Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
authorDavid S. Miller <davem@davemloft.net>
Fri, 21 Dec 2018 02:20:26 +0000 (18:20 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 21 Dec 2018 02:20:26 +0000 (18:20 -0800)
Pablo Neira Ayuso says:

====================
Netfilter updates for net-next

The following patchset contains Netfilter updates for net-next:

1) Support for destination MAC in ipset, from Stefano Brivio.

2) Disallow all-zeroes MAC address in ipset, also from Stefano.

3) Add IPSET_CMD_GET_BYNAME and IPSET_CMD_GET_BYINDEX commands,
   introduce protocol version number 7, from Jozsef Kadlecsik.
   A follow up patch to fix ip_set_byindex() is also included
   in this batch.

4) Honor CTA_MARK_MASK from ctnetlink, from Andreas Jaggi.

5) Statify nf_flow_table_iterate(), from Taehee Yoo.

6) Use nf_flow_table_iterate() to simplify garbage collection in
   nf_flow_table logic, also from Taehee Yoo.

7) Don't use _bh variants of call_rcu(), rcu_barrier() and
   synchronize_rcu_bh() in Netfilter, from Paul E. McKenney.

8) Remove NFC_* cache definition from the old caching
   infrastructure.

9) Remove layer 4 port rover in NAT helpers, use random port
   instead, from Florian Westphal.

10) Use strscpy() in ipset, from Qian Cai.

11) Remove NF_NAT_RANGE_PROTO_RANDOM_FULLY branch now that
    random port is allocated by default, from Xiaozhou Liu.

12) Ignore NF_NAT_RANGE_PROTO_RANDOM too, from Florian Westphal.

13) Limit port allocation selection routine in NAT to avoid
    softlockup splats when most ports are in use, from Florian.

14) Remove unused parameters in nf_ct_l4proto_unregister_sysctl()
    from Yafang Shao.

15) Direct call to nf_nat_l4proto_unique_tuple() instead of
    indirection, from Florian Westphal.

16) Several patches to remove all layer 4 NAT indirections,
    remove nf_nat_l4proto struct, from Florian Westphal.

17) Fix RTP/RTCP source port translation when SNAT is in place,
    from Alin Nastac.

18) Selective rule dump per chain, from Phil Sutter.

19) Revisit CLUSTERIP target, this includes a deadlock fix from
    netns path, sleep in atomic, remove bogus WARN_ON_ONCE()
    and disallow mismatching IP address and MAC address.
    Patchset from Taehee Yoo.

20) Update UDP timeout to stream after 2 seconds, from Florian.

21) Shrink UDP established timeout to 120 seconds like TCP timewait.

22) Sysctl knobs to set GRE timeouts, from Yafang Shao.

23) Move seq_print_acct() to conntrack core file, from Florian.

24) Add enum for conntrack sysctl knobs, also from Florian.

25) Place nf_conntrack_acct, nf_conntrack_helper, nf_conntrack_events
    and nf_conntrack_timestamp knobs in the core, from Florian Westphal.
    As a side effect, shrink netns_ct structure by removing obsolete
    sysctl anchors, also from Florian.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
2345 files changed:
CREDITS
Documentation/ABI/testing/sysfs-class-net-dsa
Documentation/admin-guide/kernel-parameters.txt
Documentation/admin-guide/pm/cpufreq.rst
Documentation/admin-guide/security-bugs.rst
Documentation/arm64/silicon-errata.txt
Documentation/core-api/xarray.rst
Documentation/cpu-freq/cpufreq-stats.txt
Documentation/devicetree/bindings/clock/clock-bindings.txt
Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt [deleted file]
Documentation/devicetree/bindings/input/input-reset.txt
Documentation/devicetree/bindings/media/rockchip-vpu.txt [deleted file]
Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt
Documentation/devicetree/bindings/net/broadcom-bluetooth.txt
Documentation/devicetree/bindings/net/can/fsl-flexcan.txt
Documentation/devicetree/bindings/net/can/holt_hi311x.txt
Documentation/devicetree/bindings/net/can/rcar_can.txt
Documentation/devicetree/bindings/net/can/xilinx_can.txt
Documentation/devicetree/bindings/net/dsa/dsa.txt
Documentation/devicetree/bindings/net/dsa/ksz.txt
Documentation/devicetree/bindings/net/icplus-ip101ag.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/mediatek-dwmac.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/renesas,ravb.txt
Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt
Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
Documentation/devicetree/bindings/spi/spi-uniphier.txt
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/input/event-codes.rst
Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst
Documentation/media/uapi/mediactl/media-request-ioc-queue.rst
Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst
Documentation/media/uapi/mediactl/request-api.rst
Documentation/media/uapi/mediactl/request-func-close.rst
Documentation/media/uapi/mediactl/request-func-ioctl.rst
Documentation/media/uapi/mediactl/request-func-poll.rst
Documentation/media/uapi/v4l/dev-meta.rst
Documentation/media/uapi/v4l/extended-controls.rst
Documentation/media/uapi/v4l/vidioc-g-fmt.rst
Documentation/networking/3c509.txt [deleted file]
Documentation/networking/LICENSE.qla3xxx [deleted file]
Documentation/networking/LICENSE.qlcnic [deleted file]
Documentation/networking/LICENSE.qlge [deleted file]
Documentation/networking/README.ipw2100 [deleted file]
Documentation/networking/README.ipw2200 [deleted file]
Documentation/networking/README.sb1000 [deleted file]
Documentation/networking/cs89x0.txt [deleted file]
Documentation/networking/cxgb.txt [deleted file]
Documentation/networking/de4x5.txt [deleted file]
Documentation/networking/device_drivers/3com/3c509.txt [new file with mode: 0644]
Documentation/networking/device_drivers/3com/vortex.txt [new file with mode: 0644]
Documentation/networking/device_drivers/amazon/ena.txt [new file with mode: 0644]
Documentation/networking/device_drivers/chelsio/cxgb.txt [new file with mode: 0644]
Documentation/networking/device_drivers/cirrus/cs89x0.txt [new file with mode: 0644]
Documentation/networking/device_drivers/davicom/dm9000.txt [new file with mode: 0644]
Documentation/networking/device_drivers/dec/de4x5.txt [new file with mode: 0644]
Documentation/networking/device_drivers/dec/dmfe.txt [new file with mode: 0644]
Documentation/networking/device_drivers/dlink/dl2k.txt [new file with mode: 0644]
Documentation/networking/device_drivers/freescale/dpaa.txt [new file with mode: 0644]
Documentation/networking/device_drivers/freescale/dpaa2/dpio-driver.rst [new file with mode: 0644]
Documentation/networking/device_drivers/freescale/dpaa2/ethernet-driver.rst [new file with mode: 0644]
Documentation/networking/device_drivers/freescale/dpaa2/index.rst [new file with mode: 0644]
Documentation/networking/device_drivers/freescale/dpaa2/overview.rst [new file with mode: 0644]
Documentation/networking/device_drivers/freescale/gianfar.txt [new file with mode: 0644]
Documentation/networking/device_drivers/intel/e100.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/e1000.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/e1000e.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/fm10k.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/i40e.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/iavf.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/ice.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/igb.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/igbvf.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/ipw2100.txt [new file with mode: 0644]
Documentation/networking/device_drivers/intel/ipw2200.txt [new file with mode: 0644]
Documentation/networking/device_drivers/intel/ixgb.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/ixgbe.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/ixgbevf.rst [new file with mode: 0644]
Documentation/networking/device_drivers/microsoft/netvsc.txt [new file with mode: 0644]
Documentation/networking/device_drivers/neterion/s2io.txt [new file with mode: 0644]
Documentation/networking/device_drivers/neterion/vxge.txt [new file with mode: 0644]
Documentation/networking/device_drivers/qlogic/LICENSE.qla3xxx [new file with mode: 0644]
Documentation/networking/device_drivers/qlogic/LICENSE.qlcnic [new file with mode: 0644]
Documentation/networking/device_drivers/qlogic/LICENSE.qlge [new file with mode: 0644]
Documentation/networking/device_drivers/qualcomm/rmnet.txt [new file with mode: 0644]
Documentation/networking/device_drivers/sb1000.txt [new file with mode: 0644]
Documentation/networking/device_drivers/smsc/smc9.txt [new file with mode: 0644]
Documentation/networking/device_drivers/stmicro/stmmac.txt [new file with mode: 0644]
Documentation/networking/device_drivers/ti/cpsw.txt [new file with mode: 0644]
Documentation/networking/device_drivers/ti/tlan.txt [new file with mode: 0644]
Documentation/networking/device_drivers/toshiba/spider_net.txt [new file with mode: 0644]
Documentation/networking/devlink-params.txt
Documentation/networking/dl2k.txt [deleted file]
Documentation/networking/dm9000.txt [deleted file]
Documentation/networking/dmfe.txt [deleted file]
Documentation/networking/dpaa.txt [deleted file]
Documentation/networking/dpaa2/dpio-driver.rst [deleted file]
Documentation/networking/dpaa2/ethernet-driver.rst [deleted file]
Documentation/networking/dpaa2/index.rst [deleted file]
Documentation/networking/dpaa2/overview.rst [deleted file]
Documentation/networking/e100.rst [deleted file]
Documentation/networking/e1000.rst [deleted file]
Documentation/networking/e1000e.rst [deleted file]
Documentation/networking/ena.txt [deleted file]
Documentation/networking/fm10k.rst [deleted file]
Documentation/networking/gianfar.txt [deleted file]
Documentation/networking/i40e.rst [deleted file]
Documentation/networking/iavf.rst [deleted file]
Documentation/networking/ice.rst [deleted file]
Documentation/networking/igb.rst [deleted file]
Documentation/networking/igbvf.rst [deleted file]
Documentation/networking/ip-sysctl.txt
Documentation/networking/ixgb.rst [deleted file]
Documentation/networking/ixgbe.rst [deleted file]
Documentation/networking/ixgbevf.rst [deleted file]
Documentation/networking/netdev-features.txt
Documentation/networking/netvsc.txt [deleted file]
Documentation/networking/rmnet.txt [deleted file]
Documentation/networking/rxrpc.txt
Documentation/networking/s2io.txt [deleted file]
Documentation/networking/smc9.txt [deleted file]
Documentation/networking/snmp_counter.rst
Documentation/networking/spider_net.txt [deleted file]
Documentation/networking/stmmac.txt [deleted file]
Documentation/networking/ti-cpsw.txt [deleted file]
Documentation/networking/tlan.txt [deleted file]
Documentation/networking/vortex.txt [deleted file]
Documentation/networking/vxge.txt [deleted file]
Documentation/networking/xfrm_device.txt
Documentation/userspace-api/spec_ctrl.rst
Documentation/x86/boot.txt
MAINTAINERS
Makefile
arch/alpha/kernel/setup.c
arch/alpha/mm/numa.c
arch/arc/Kconfig
arch/arc/Makefile
arch/arc/boot/dts/hsdk.dts
arch/arc/configs/axs101_defconfig
arch/arc/configs/axs103_defconfig
arch/arc/configs/axs103_smp_defconfig
arch/arc/configs/hsdk_defconfig
arch/arc/configs/nps_defconfig
arch/arc/configs/nsim_700_defconfig
arch/arc/configs/nsimosci_defconfig
arch/arc/configs/nsimosci_hs_defconfig
arch/arc/configs/nsimosci_hs_smp_defconfig
arch/arc/configs/tb10x_defconfig
arch/arc/configs/vdk_hs38_defconfig
arch/arc/configs/vdk_hs38_smp_defconfig
arch/arc/include/asm/cache.h
arch/arc/include/asm/io.h
arch/arc/kernel/setup.c
arch/arc/mm/cache.c
arch/arc/mm/fault.c
arch/arm/boot/dts/am3517-evm.dts
arch/arm/boot/dts/am3517-som.dtsi
arch/arm/boot/dts/arm-realview-pb1176.dts
arch/arm/boot/dts/arm-realview-pb11mp.dts
arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts
arch/arm/boot/dts/bcm2837-rpi-3-b.dts
arch/arm/boot/dts/imx51-zii-rdu1.dts
arch/arm/boot/dts/imx7d-nitrogen7.dts
arch/arm/boot/dts/imx7d-pico.dtsi
arch/arm/boot/dts/logicpd-som-lv.dtsi
arch/arm/boot/dts/logicpd-torpedo-37xx-devkit.dts
arch/arm/boot/dts/rk3288-veyron.dtsi
arch/arm/boot/dts/sama5d2.dtsi
arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts
arch/arm/include/asm/cputype.h
arch/arm/include/asm/proc-fns.h
arch/arm/kernel/bugs.c
arch/arm/kernel/ftrace.c
arch/arm/kernel/head-common.S
arch/arm/kernel/setup.c
arch/arm/kernel/smp.c
arch/arm/mach-davinci/da830.c
arch/arm/mach-davinci/da850.c
arch/arm/mach-davinci/devices-da8xx.c
arch/arm/mach-davinci/dm355.c
arch/arm/mach-davinci/dm365.c
arch/arm/mach-davinci/dm644x.c
arch/arm/mach-davinci/dm646x.c
arch/arm/mach-imx/cpuidle-imx6sx.c
arch/arm/mach-mmp/cputype.h
arch/arm/mach-omap1/board-ams-delta.c
arch/arm/mach-omap2/display.c
arch/arm/mach-omap2/prm44xx.c
arch/arm/mm/cache-v7.S
arch/arm/mm/cache-v7m.S
arch/arm/mm/dma-mapping.c
arch/arm/mm/proc-macros.S
arch/arm/mm/proc-v7-bugs.c
arch/arm/probes/kprobes/opt-arm.c
arch/arm/vfp/vfpmodule.c
arch/arm64/Kconfig
arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi
arch/arm64/boot/dts/marvell/armada-ap806.dtsi
arch/arm64/boot/dts/mediatek/mt7622-bananapi-bpi-r64.dts
arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts
arch/arm64/boot/dts/mediatek/mt7622.dtsi
arch/arm64/boot/dts/qcom/msm8998-mtp.dtsi
arch/arm64/boot/dts/qcom/sdm845-mtp.dts
arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts
arch/arm64/boot/dts/rockchip/rk3399-rock960.dtsi
arch/arm64/boot/dts/ti/k3-am65-wakeup.dtsi
arch/arm64/include/asm/ftrace.h
arch/arm64/include/asm/memory.h
arch/arm64/include/asm/sysreg.h
arch/arm64/include/asm/tlbflush.h
arch/arm64/kernel/cpu_errata.c
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/ftrace.c
arch/arm64/kernel/hibernate.c
arch/arm64/kernel/setup.c
arch/arm64/mm/dma-mapping.c
arch/arm64/mm/init.c
arch/arm64/net/bpf_jit_comp.c
arch/csky/include/asm/mmu_context.h
arch/ia64/include/asm/numa.h
arch/ia64/kernel/acpi.c
arch/ia64/mm/numa.c
arch/m68k/kernel/setup_mm.c
arch/m68k/mm/motorola.c
arch/microblaze/kernel/ftrace.c
arch/mips/configs/cavium_octeon_defconfig
arch/mips/include/asm/syscall.h
arch/mips/include/asm/uasm.h
arch/mips/include/uapi/asm/inst.h
arch/mips/kernel/ftrace.c
arch/mips/kernel/setup.c
arch/mips/kernel/traps.c
arch/mips/loongson64/loongson-3/numa.c
arch/mips/mm/uasm-micromips.c
arch/mips/mm/uasm-mips.c
arch/mips/mm/uasm.c
arch/mips/net/bpf_jit.c
arch/mips/net/ebpf_jit.c
arch/mips/ralink/mt7620.c
arch/mips/sgi-ip27/ip27-memory.c
arch/nds32/kernel/ftrace.c
arch/parisc/Makefile
arch/parisc/include/asm/spinlock.h
arch/parisc/kernel/ftrace.c
arch/parisc/kernel/syscall.S
arch/powerpc/boot/Makefile
arch/powerpc/boot/crt0.S
arch/powerpc/include/asm/io.h
arch/powerpc/include/asm/perf_event.h
arch/powerpc/include/asm/ppc-opcode.h
arch/powerpc/include/asm/ptrace.h
arch/powerpc/include/uapi/asm/Kbuild
arch/powerpc/include/uapi/asm/bpf_perf_event.h [new file with mode: 0644]
arch/powerpc/kernel/legacy_serial.c
arch/powerpc/kernel/msi.c
arch/powerpc/kernel/ptrace.c
arch/powerpc/kernel/setup_64.c
arch/powerpc/kernel/trace/ftrace.c
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/trace.h
arch/powerpc/kvm/trace_booke.h
arch/powerpc/kvm/trace_hv.h
arch/powerpc/kvm/trace_pr.h
arch/powerpc/mm/dump_linuxpagetables.c
arch/powerpc/mm/init_64.c
arch/powerpc/mm/numa.c
arch/powerpc/mm/slb.c
arch/powerpc/net/bpf_jit.h
arch/powerpc/net/bpf_jit_comp.c
arch/powerpc/net/bpf_jit_comp64.c
arch/powerpc/platforms/powernv/npu-dma.c
arch/powerpc/platforms/pseries/Kconfig
arch/powerpc/platforms/pseries/papr_scm.c
arch/riscv/Makefile
arch/riscv/boot/.gitignore [new file with mode: 0644]
arch/riscv/boot/Makefile [new file with mode: 0644]
arch/riscv/boot/install.sh [new file with mode: 0644]
arch/riscv/configs/defconfig
arch/riscv/include/asm/module.h
arch/riscv/include/asm/ptrace.h
arch/riscv/include/asm/uaccess.h
arch/riscv/include/asm/unistd.h
arch/riscv/include/uapi/asm/syscalls.h [deleted file]
arch/riscv/include/uapi/asm/unistd.h [new file with mode: 0644]
arch/riscv/kernel/cpu.c
arch/riscv/kernel/ftrace.c
arch/riscv/kernel/head.S
arch/riscv/kernel/module.c
arch/riscv/kernel/vmlinux.lds.S
arch/riscv/lib/Makefile
arch/s390/kernel/ftrace.c
arch/s390/kernel/perf_cpum_cf.c
arch/s390/mm/pgalloc.c
arch/s390/net/bpf_jit_comp.c
arch/sh/include/asm/io.h
arch/sh/kernel/ftrace.c
arch/sparc/kernel/ftrace.c
arch/sparc/kernel/iommu.c
arch/sparc/kernel/signal32.c
arch/sparc/kernel/signal_32.c
arch/sparc/kernel/signal_64.c
arch/sparc/net/bpf_jit_comp_32.c
arch/sparc/net/bpf_jit_comp_64.c
arch/x86/Kconfig
arch/x86/Makefile
arch/x86/boot/compressed/eboot.c
arch/x86/boot/header.S
arch/x86/entry/entry_64.S
arch/x86/entry/vdso/Makefile
arch/x86/events/core.c
arch/x86/events/intel/core.c
arch/x86/events/intel/uncore.h
arch/x86/events/intel/uncore_snb.c
arch/x86/events/perf_event.h
arch/x86/include/asm/bootparam_utils.h
arch/x86/include/asm/fpu/internal.h
arch/x86/include/asm/kvm_host.h
arch/x86/include/asm/msr-index.h
arch/x86/include/asm/nospec-branch.h
arch/x86/include/asm/spec-ctrl.h
arch/x86/include/asm/switch_to.h
arch/x86/include/asm/thread_info.h
arch/x86/include/asm/tlbflush.h
arch/x86/include/asm/x86_init.h
arch/x86/include/uapi/asm/bootparam.h
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/cpu/bugs.c
arch/x86/kernel/cpu/mcheck/mce_amd.c
arch/x86/kernel/fpu/signal.c
arch/x86/kernel/ftrace.c
arch/x86/kernel/head32.c
arch/x86/kernel/head64.c
arch/x86/kernel/kprobes/opt.c
arch/x86/kernel/process.c
arch/x86/kernel/process.h [new file with mode: 0644]
arch/x86/kernel/process_32.c
arch/x86/kernel/process_64.c
arch/x86/kernel/setup.c
arch/x86/kvm/lapic.c
arch/x86/kvm/mmu.c
arch/x86/kvm/svm.c
arch/x86/kvm/vmx.c
arch/x86/kvm/x86.c
arch/x86/mm/tlb.c
arch/x86/net/bpf_jit_comp.c
arch/x86/platform/efi/early_printk.c
arch/x86/xen/enlighten.c
arch/x86/xen/multicalls.c
arch/x86/xen/setup.c
arch/x86/xen/spinlock.c
arch/xtensa/include/asm/processor.h
arch/xtensa/kernel/asm-offsets.c
arch/xtensa/kernel/head.S
arch/xtensa/kernel/process.c
arch/xtensa/kernel/ptrace.c
block/bfq-iosched.c
block/bfq-iosched.h
block/bfq-wf2q.c
block/bio.c
block/blk-core.c
block/blk-lib.c
block/blk-merge.c
block/blk-mq.c
block/blk-zoned.c
block/bounce.c
crypto/Kconfig
crypto/cbc.c
crypto/cfb.c
crypto/crypto_user_base.c
crypto/crypto_user_stat.c
crypto/pcbc.c
crypto/simd.c
drivers/acpi/Kconfig
drivers/acpi/acpi_platform.c
drivers/acpi/acpica/exserial.c
drivers/acpi/arm64/iort.c
drivers/acpi/nfit/core.c
drivers/android/binder.c
drivers/android/binder_alloc.c
drivers/android/binder_alloc.h
drivers/ata/libata-core.c
drivers/atm/firestream.c
drivers/atm/fore200e.c
drivers/base/devres.c
drivers/block/floppy.c
drivers/bluetooth/btbcm.c
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_bcm.c
drivers/bluetooth/hci_h5.c
drivers/bluetooth/hci_intel.c
drivers/bluetooth/hci_serdev.c
drivers/clk/mmp/clk.c
drivers/clk/mvebu/cp110-system-controller.c
drivers/clk/qcom/common.c
drivers/clk/qcom/gcc-qcs404.c
drivers/clk/zynqmp/clkc.c
drivers/cpufreq/imx6q-cpufreq.c
drivers/cpufreq/ti-cpufreq.c
drivers/cpuidle/cpuidle-arm.c
drivers/crypto/chelsio/chcr_ipsec.c
drivers/crypto/chelsio/chtls/chtls.h
drivers/crypto/chelsio/chtls/chtls_cm.c
drivers/crypto/chelsio/chtls/chtls_io.c
drivers/crypto/chelsio/chtls/chtls_main.c
drivers/crypto/hisilicon/sec/sec_algs.c
drivers/dma-buf/udmabuf.c
drivers/dma/at_hdmac.c
drivers/dma/dw/core.c
drivers/dma/imx-sdma.c
drivers/dma/ti/cppi41.c
drivers/firmware/efi/arm-init.c
drivers/firmware/efi/arm-runtime.c
drivers/firmware/efi/efi.c
drivers/firmware/efi/libstub/arm-stub.c
drivers/firmware/efi/libstub/fdt.c
drivers/firmware/efi/memmap.c
drivers/firmware/efi/runtime-wrappers.c
drivers/fsi/Kconfig
drivers/fsi/fsi-scom.c
drivers/gnss/serial.c
drivers/gnss/sirf.c
drivers/gpio/gpio-davinci.c
drivers/gpio/gpio-mockup.c
drivers/gpio/gpio-pxa.c
drivers/gpio/gpiolib.c
drivers/gpu/drm/amd/amdgpu/amdgpu.h
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c
drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c
drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
drivers/gpu/drm/amd/amdgpu/mmhub_v1_0.c
drivers/gpu/drm/amd/amdgpu/soc15.c
drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c
drivers/gpu/drm/amd/amdgpu/vega10_ih.c
drivers/gpu/drm/amd/amdkfd/kfd_device.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c
drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c
drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c
drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
drivers/gpu/drm/amd/powerplay/hwmgr/smu_helper.c
drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c
drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c
drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
drivers/gpu/drm/amd/powerplay/inc/smu7_ppsmc.h
drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c
drivers/gpu/drm/amd/powerplay/smumgr/smumgr.c
drivers/gpu/drm/ast/ast_drv.c
drivers/gpu/drm/ast/ast_fb.c
drivers/gpu/drm/ast/ast_main.c
drivers/gpu/drm/ast/ast_mode.c
drivers/gpu/drm/bridge/ti-sn65dsi86.c
drivers/gpu/drm/drm_auth.c
drivers/gpu/drm/drm_dp_mst_topology.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_fourcc.c
drivers/gpu/drm/drm_internal.h
drivers/gpu/drm/drm_lease.c
drivers/gpu/drm/drm_sysfs.c
drivers/gpu/drm/i915/gvt/aperture_gm.c
drivers/gpu/drm/i915/gvt/fb_decoder.c
drivers/gpu/drm/i915/gvt/gtt.c
drivers/gpu/drm/i915/gvt/mmio_context.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_gpu_error.c
drivers/gpu/drm/i915/i915_gpu_error.h
drivers/gpu/drm/i915/intel_device_info.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp_mst.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_engine_cs.c
drivers/gpu/drm/i915/intel_hotplug.c
drivers/gpu/drm/i915/intel_lrc.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/i915/intel_ringbuffer.h
drivers/gpu/drm/i915/intel_runtime_pm.c
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/i915/intel_workarounds.c
drivers/gpu/drm/i915/intel_workarounds.h
drivers/gpu/drm/mediatek/mtk_dsi.c
drivers/gpu/drm/meson/meson_crtc.c
drivers/gpu/drm/meson/meson_dw_hdmi.c
drivers/gpu/drm/meson/meson_venc.c
drivers/gpu/drm/meson/meson_viu.c
drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c
drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c
drivers/gpu/drm/msm/hdmi/hdmi.c
drivers/gpu/drm/msm/hdmi/hdmi.h
drivers/gpu/drm/msm/hdmi/hdmi_connector.c
drivers/gpu/drm/msm/msm_atomic.c
drivers/gpu/drm/msm/msm_debugfs.c
drivers/gpu/drm/msm/msm_drv.c
drivers/gpu/drm/msm/msm_gem_submit.c
drivers/gpu/drm/msm/msm_gpu.c
drivers/gpu/drm/msm/msm_iommu.c
drivers/gpu/drm/msm/msm_rd.c
drivers/gpu/drm/nouveau/dispnv50/disp.c
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/omapdrm/displays/panel-dpi.c
drivers/gpu/drm/omapdrm/dss/dsi.c
drivers/gpu/drm/omapdrm/dss/dss.c
drivers/gpu/drm/omapdrm/dss/hdmi4.c
drivers/gpu/drm/omapdrm/dss/hdmi5.c
drivers/gpu/drm/omapdrm/dss/omapdss.h
drivers/gpu/drm/omapdrm/dss/venc.c
drivers/gpu/drm/omapdrm/omap_crtc.c
drivers/gpu/drm/omapdrm/omap_encoder.c
drivers/gpu/drm/rcar-du/rcar_du_group.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.c
drivers/gpu/drm/ttm/ttm_bo_util.c
drivers/gpu/drm/vc4/vc4_kms.c
drivers/gpu/drm/vc4/vc4_plane.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
drivers/gpu/drm/vmwgfx/vmwgfx_validation.h
drivers/hid/hid-hyperv.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-ite.c
drivers/hid/hid-logitech-hidpp.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-quirks.c
drivers/hid/hid-sensor-custom.c
drivers/hid/hid-sensor-hub.c
drivers/hid/hid-steam.c
drivers/hid/i2c-hid/i2c-hid-core.c
drivers/hid/uhid.c
drivers/hv/Kconfig
drivers/hv/channel.c
drivers/hv/channel_mgmt.c
drivers/hv/connection.c
drivers/hv/hv_kvp.c
drivers/hv/hyperv_vmbus.h
drivers/hv/vmbus_drv.c
drivers/hwmon/ina2xx.c
drivers/hwmon/mlxreg-fan.c
drivers/hwmon/raspberrypi-hwmon.c
drivers/hwmon/w83795.c
drivers/i2c/busses/i2c-axxia.c
drivers/i2c/busses/i2c-nvidia-gpu.c
drivers/i2c/busses/i2c-rcar.c
drivers/i2c/busses/i2c-scmi.c
drivers/i2c/busses/i2c-uniphier-f.c
drivers/i2c/busses/i2c-uniphier.c
drivers/ide/ide-proc.c
drivers/ide/pmac.c
drivers/iio/accel/hid-sensor-accel-3d.c
drivers/iio/gyro/hid-sensor-gyro-3d.c
drivers/iio/humidity/hid-sensor-humidity.c
drivers/iio/light/hid-sensor-als.c
drivers/iio/light/hid-sensor-prox.c
drivers/iio/magnetometer/hid-sensor-magn-3d.c
drivers/iio/magnetometer/st_magn_buffer.c
drivers/iio/orientation/hid-sensor-incl-3d.c
drivers/iio/pressure/hid-sensor-press.c
drivers/iio/temperature/hid-sensor-temperature.c
drivers/infiniband/core/roce_gid_mgmt.c
drivers/infiniband/core/umem_odp.c
drivers/infiniband/hw/bnxt_re/main.c
drivers/infiniband/hw/cxgb4/cm.c
drivers/infiniband/hw/hfi1/chip.c
drivers/infiniband/hw/hfi1/hfi.h
drivers/infiniband/hw/hfi1/qp.c
drivers/infiniband/hw/hfi1/verbs.c
drivers/infiniband/hw/hns/hns_roce_hw_v2.c
drivers/infiniband/hw/mlx4/cq.c
drivers/infiniband/hw/mlx5/Makefile
drivers/infiniband/hw/mlx5/cq.c
drivers/infiniband/hw/mlx5/devx.c
drivers/infiniband/hw/mlx5/ib_rep.c
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/mlx5/mlx5_ib.h
drivers/infiniband/hw/mlx5/odp.c
drivers/infiniband/hw/mlx5/qp.c
drivers/infiniband/hw/mlx5/srq.c
drivers/infiniband/hw/mlx5/srq.h [new file with mode: 0644]
drivers/infiniband/hw/mlx5/srq_cmd.c [new file with mode: 0644]
drivers/infiniband/hw/nes/nes_mgt.c
drivers/infiniband/sw/rdmavt/ah.c
drivers/infiniband/sw/rdmavt/ah.h
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/iser/iser_verbs.c
drivers/input/joystick/xpad.c
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/cros_ec_keyb.c
drivers/input/keyboard/matrix_keypad.c
drivers/input/keyboard/omap4-keypad.c
drivers/input/mouse/elan_i2c_core.c
drivers/input/mouse/synaptics.c
drivers/input/serio/hyperv-keyboard.c
drivers/input/touchscreen/migor_ts.c
drivers/input/touchscreen/st1232.c
drivers/iommu/amd_iommu_init.c
drivers/iommu/intel-iommu.c
drivers/iommu/intel-svm.c
drivers/iommu/ipmmu-vmsa.c
drivers/isdn/hisax/hfc_pci.c
drivers/md/dm-cache-metadata.c
drivers/md/dm-thin.c
drivers/md/dm-zoned-target.c
drivers/md/dm.c
drivers/media/Kconfig
drivers/media/cec/cec-adap.c
drivers/media/common/videobuf2/videobuf2-core.c
drivers/media/common/videobuf2/videobuf2-v4l2.c
drivers/media/dvb-frontends/dvb-pll.c
drivers/media/i2c/tc358743.c
drivers/media/media-device.c
drivers/media/media-request.c
drivers/media/pci/intel/ipu3/ipu3-cio2.c
drivers/media/platform/omap3isp/isp.c
drivers/media/platform/vicodec/vicodec-core.c
drivers/media/platform/vim2m.c
drivers/media/platform/vivid/vivid-sdr-cap.c
drivers/media/platform/vivid/vivid-vbi-cap.c
drivers/media/platform/vivid/vivid-vbi-out.c
drivers/media/platform/vivid/vivid-vid-cap.c
drivers/media/platform/vivid/vivid-vid-out.c
drivers/media/platform/vsp1/vsp1_lif.c
drivers/media/rc/bpf-lirc.c
drivers/media/usb/gspca/gspca.c
drivers/media/v4l2-core/v4l2-ctrls.c
drivers/media/v4l2-core/v4l2-event.c
drivers/media/v4l2-core/v4l2-mem2mem.c
drivers/mfd/cros_ec_dev.c
drivers/misc/atmel-ssc.c
drivers/misc/mic/scif/scif_rma.c
drivers/misc/mic/vop/vop_main.c
drivers/misc/sgi-gru/grukdump.c
drivers/mmc/core/block.c
drivers/mmc/core/mmc.c
drivers/mmc/host/omap.c
drivers/mmc/host/omap_hsmmc.c
drivers/mmc/host/sdhci-omap.c
drivers/mmc/host/sdhci-pci-core.c
drivers/mmc/host/sdhci-tegra.c
drivers/mmc/host/sdhci.c
drivers/mtd/nand/bbt.c
drivers/mtd/nand/raw/atmel/nand-controller.c
drivers/mtd/nand/raw/qcom_nandc.c
drivers/mtd/spi-nor/cadence-quadspi.c
drivers/mtd/spi-nor/spi-nor.c
drivers/net/Kconfig
drivers/net/bonding/bond_3ad.c
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_debugfs.c
drivers/net/bonding/bond_main.c
drivers/net/can/Kconfig
drivers/net/can/dev.c
drivers/net/can/flexcan.c
drivers/net/can/rcar/Kconfig
drivers/net/can/rcar/Makefile
drivers/net/can/rcar/rcar_can.c
drivers/net/can/rcar/rcar_canfd.c
drivers/net/can/rx-offload.c
drivers/net/can/sja1000/Kconfig
drivers/net/can/sja1000/plx_pci.c
drivers/net/can/spi/hi311x.c
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c
drivers/net/can/usb/ucan.c
drivers/net/can/xilinx_can.c
drivers/net/dsa/microchip/Kconfig
drivers/net/dsa/microchip/Makefile
drivers/net/dsa/microchip/ksz9477.c [new file with mode: 0644]
drivers/net/dsa/microchip/ksz9477_reg.h [new file with mode: 0644]
drivers/net/dsa/microchip/ksz9477_spi.c [new file with mode: 0644]
drivers/net/dsa/microchip/ksz_9477_reg.h [deleted file]
drivers/net/dsa/microchip/ksz_common.c
drivers/net/dsa/microchip/ksz_common.h [new file with mode: 0644]
drivers/net/dsa/microchip/ksz_priv.h
drivers/net/dsa/microchip/ksz_spi.c [deleted file]
drivers/net/dsa/microchip/ksz_spi.h [new file with mode: 0644]
drivers/net/dsa/mv88e6060.c
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/ethernet/3com/3c59x.c
drivers/net/ethernet/3com/Kconfig
drivers/net/ethernet/amazon/ena/ena_netdev.c
drivers/net/ethernet/amazon/ena/ena_netdev.h
drivers/net/ethernet/amd/au1000_eth.c
drivers/net/ethernet/amd/sunlance.c
drivers/net/ethernet/apm/xgene/xgene_enet_main.c
drivers/net/ethernet/aquantia/atlantic/Makefile
drivers/net/ethernet/aquantia/atlantic/aq_cfg.h
drivers/net/ethernet/aquantia/atlantic/aq_common.h
drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
drivers/net/ethernet/aquantia/atlantic/aq_filters.c [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/aq_filters.h [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/aq_hw.h
drivers/net/ethernet/aquantia/atlantic/aq_main.c
drivers/net/ethernet/aquantia/atlantic/aq_nic.c
drivers/net/ethernet/aquantia/atlantic/aq_nic.h
drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/broadcom/bnxt/bnxt.h
drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c
drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h
drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h
drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
drivers/net/ethernet/broadcom/genet/bcmgenet.c
drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/cadence/macb_main.c
drivers/net/ethernet/cadence/macb_ptp.c
drivers/net/ethernet/cavium/common/cavium_ptp.c
drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c
drivers/net/ethernet/cavium/thunder/nic_main.c
drivers/net/ethernet/cavium/thunder/nicvf_main.c
drivers/net/ethernet/cavium/thunder/nicvf_queues.c
drivers/net/ethernet/chelsio/Kconfig
drivers/net/ethernet/chelsio/cxgb4/Makefile
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/l2t.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
drivers/net/ethernet/cirrus/Kconfig
drivers/net/ethernet/cisco/enic/enic_ethtool.c
drivers/net/ethernet/cortina/gemini.c
drivers/net/ethernet/dec/tulip/Kconfig
drivers/net/ethernet/dlink/dl2k.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/faraday/ftmac100.c
drivers/net/ethernet/freescale/dpaa/dpaa_eth.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/dpaa2/dpaa2-ethtool.c
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/freescale/fman/fman.c
drivers/net/ethernet/freescale/fsl_pq_mdio.c
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/ucc_geth.c
drivers/net/ethernet/hisilicon/Kconfig
drivers/net/ethernet/hisilicon/hip04_eth.c
drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
drivers/net/ethernet/hisilicon/hns/hns_enet.c
drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
drivers/net/ethernet/hisilicon/hns3/Makefile
drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h
drivers/net/ethernet/hisilicon/hns3/hnae3.h
drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c [new file with mode: 0644]
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/Makefile
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 [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.h
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_tm.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h
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/huawei/hinic/hinic_hw_dev.h
drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c
drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h
drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h
drivers/net/ethernet/huawei/hinic/hinic_main.c
drivers/net/ethernet/huawei/hinic/hinic_port.c
drivers/net/ethernet/huawei/hinic/hinic_port.h
drivers/net/ethernet/huawei/hinic/hinic_rx.c
drivers/net/ethernet/huawei/hinic/hinic_rx.h
drivers/net/ethernet/ibm/emac/emac.h
drivers/net/ethernet/ibm/ibmvnic.c
drivers/net/ethernet/ibm/ibmvnic.h
drivers/net/ethernet/intel/Kconfig
drivers/net/ethernet/intel/e100.c
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/fm10k/fm10k_main.c
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_adminq.c
drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
drivers/net/ethernet/intel/i40e/i40e_common.c
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_ptp.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_txrx_common.h
drivers/net/ethernet/intel/i40e/i40e_type.h
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
drivers/net/ethernet/intel/i40e/i40e_xsk.c
drivers/net/ethernet/intel/iavf/iavf_txrx.c
drivers/net/ethernet/intel/ice/ice.h
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
drivers/net/ethernet/intel/ice/ice_common.c
drivers/net/ethernet/intel/ice/ice_controlq.c
drivers/net/ethernet/intel/ice/ice_ethtool.c
drivers/net/ethernet/intel/ice/ice_hw_autogen.h
drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
drivers/net/ethernet/intel/ice/ice_lib.c
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_sched.c
drivers/net/ethernet/intel/ice/ice_sched.h
drivers/net/ethernet/intel/ice/ice_sriov.c
drivers/net/ethernet/intel/ice/ice_switch.c
drivers/net/ethernet/intel/ice/ice_txrx.c
drivers/net/ethernet/intel/ice/ice_type.h
drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c
drivers/net/ethernet/intel/ice/ice_virtchnl_pf.h
drivers/net/ethernet/intel/igb/e1000_defines.h
drivers/net/ethernet/intel/igb/e1000_i210.c
drivers/net/ethernet/intel/igb/igb.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igbvf/netdev.c
drivers/net/ethernet/intel/igc/igc.h
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
drivers/net/ethernet/intel/ixgbevf/ipsec.c
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/lantiq_xrx200.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
drivers/net/ethernet/marvell/octeontx2/af/cgx.c
drivers/net/ethernet/marvell/octeontx2/af/cgx.h
drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h
drivers/net/ethernet/marvell/octeontx2/af/common.h
drivers/net/ethernet/marvell/octeontx2/af/mbox.c
drivers/net/ethernet/marvell/octeontx2/af/mbox.h
drivers/net/ethernet/marvell/octeontx2/af/npc.h
drivers/net/ethernet/marvell/octeontx2/af/rvu.c
drivers/net/ethernet/marvell/octeontx2/af/rvu.h
drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
drivers/net/ethernet/marvell/skge.c
drivers/net/ethernet/marvell/sky2.c
drivers/net/ethernet/mellanox/mlx4/Kconfig
drivers/net/ethernet/mellanox/mlx4/alloc.c
drivers/net/ethernet/mellanox/mlx4/cq.c
drivers/net/ethernet/mellanox/mlx4/en_cq.c
drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
drivers/net/ethernet/mellanox/mlx4/en_netdev.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/en_tx.c
drivers/net/ethernet/mellanox/mlx4/fw.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx4/mlx4.h
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/mellanox/mlx4/mr.c
drivers/net/ethernet/mellanox/mlx5/core/Makefile
drivers/net/ethernet/mellanox/mlx5/core/cmd.c
drivers/net/ethernet/mellanox/mlx5/core/cq.c
drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
drivers/net/ethernet/mellanox/mlx5/core/dev.c
drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c
drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.h
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en/port.c
drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_fs_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_rx.c
drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
drivers/net/ethernet/mellanox/mlx5/core/en_tc.h
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 [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c
drivers/net/ethernet/mellanox/mlx5/core/fpga/core.h
drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c
drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c
drivers/net/ethernet/mellanox/mlx5/core/health.c
drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
drivers/net/ethernet/mellanox/mlx5/core/lag.c
drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h
drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/mellanox/mlx5/core/port.c
drivers/net/ethernet/mellanox/mlx5/core/qp.c
drivers/net/ethernet/mellanox/mlx5/core/sriov.c
drivers/net/ethernet/mellanox/mlx5/core/srq.c [deleted file]
drivers/net/ethernet/mellanox/mlx5/core/transobj.c
drivers/net/ethernet/mellanox/mlx5/core/vport.c
drivers/net/ethernet/mellanox/mlx5/core/wq.c
drivers/net/ethernet/mellanox/mlx5/core/wq.h
drivers/net/ethernet/mellanox/mlxsw/Kconfig
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_acl_flex_keys.c
drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h
drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
drivers/net/ethernet/mellanox/mlxsw/pci.c
drivers/net/ethernet/mellanox/mlxsw/reg.h
drivers/net/ethernet/mellanox/mlxsw/resources.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/spectrum2_mr_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 [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_ctcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.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_fid.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
drivers/net/ethernet/mellanox/mlxsw/trap.h
drivers/net/ethernet/microchip/lan743x_main.c
drivers/net/ethernet/microchip/lan743x_main.h
drivers/net/ethernet/mscc/ocelot.c
drivers/net/ethernet/mscc/ocelot.h
drivers/net/ethernet/mscc/ocelot_board.c
drivers/net/ethernet/neterion/Kconfig
drivers/net/ethernet/neterion/vxge/vxge-config.c
drivers/net/ethernet/neterion/vxge/vxge-traffic.c
drivers/net/ethernet/netronome/nfp/Makefile
drivers/net/ethernet/netronome/nfp/abm/cls.c [new file with mode: 0644]
drivers/net/ethernet/netronome/nfp/abm/ctrl.c
drivers/net/ethernet/netronome/nfp/abm/main.c
drivers/net/ethernet/netronome/nfp/abm/main.h
drivers/net/ethernet/netronome/nfp/abm/qdisc.c
drivers/net/ethernet/netronome/nfp/bpf/jit.c
drivers/net/ethernet/netronome/nfp/bpf/main.c
drivers/net/ethernet/netronome/nfp/bpf/main.h
drivers/net/ethernet/netronome/nfp/bpf/offload.c
drivers/net/ethernet/netronome/nfp/bpf/verifier.c
drivers/net/ethernet/netronome/nfp/flower/offload.c
drivers/net/ethernet/netronome/nfp/nfp_app.c
drivers/net/ethernet/netronome/nfp/nfp_net.h
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
drivers/net/ethernet/netronome/nfp/nfp_net_repr.h
drivers/net/ethernet/nuvoton/w90p910_ether.c
drivers/net/ethernet/nxp/lpc_eth.c
drivers/net/ethernet/qlogic/qed/qed.h
drivers/net/ethernet/qlogic/qed/qed_dcbx.c
drivers/net/ethernet/qlogic/qed/qed_debug.c
drivers/net/ethernet/qlogic/qed/qed_dev.c
drivers/net/ethernet/qlogic/qed/qed_dev_api.h
drivers/net/ethernet/qlogic/qed/qed_hsi.h
drivers/net/ethernet/qlogic/qed/qed_int.c
drivers/net/ethernet/qlogic/qed/qed_int.h
drivers/net/ethernet/qlogic/qed/qed_ll2.c
drivers/net/ethernet/qlogic/qed/qed_ll2.h
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_rdma.c
drivers/net/ethernet/qlogic/qed/qed_rdma.h
drivers/net/ethernet/qlogic/qed/qed_reg_addr.h
drivers/net/ethernet/qlogic/qed/qed_sp.h
drivers/net/ethernet/qlogic/qed/qed_spq.c
drivers/net/ethernet/qlogic/qede/qede.h
drivers/net/ethernet/qlogic/qede/qede_ethtool.c
drivers/net/ethernet/qlogic/qede/qede_fp.c
drivers/net/ethernet/qlogic/qede/qede_main.c
drivers/net/ethernet/qlogic/qede/qede_ptp.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
drivers/net/ethernet/qualcomm/qca_debug.c
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
drivers/net/ethernet/realtek/8139cp.c
drivers/net/ethernet/realtek/r8169.c
drivers/net/ethernet/renesas/ravb.h
drivers/net/ethernet/renesas/ravb_main.c
drivers/net/ethernet/rocker/rocker_main.c
drivers/net/ethernet/sfc/ethtool.c
drivers/net/ethernet/sfc/falcon/ethtool.c
drivers/net/ethernet/smsc/Kconfig
drivers/net/ethernet/socionext/netsec.c
drivers/net/ethernet/socionext/sni_ave.c
drivers/net/ethernet/stmicro/stmmac/Kconfig
drivers/net/ethernet/stmicro/stmmac/Makefile
drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c [new file with mode: 0644]
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/sun/sunhme.c
drivers/net/ethernet/ti/Kconfig
drivers/net/ethernet/ti/cpmac.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpts.c
drivers/net/ethernet/ti/cpts.h
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/ethernet/ti/netcp_ethss.c
drivers/net/ethernet/ti/tlan.c
drivers/net/ethernet/via/via-velocity.c
drivers/net/fjes/fjes_debugfs.c
drivers/net/geneve.c
drivers/net/hamradio/6pack.c
drivers/net/hamradio/mkiss.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/ieee802154/at86rf230.c
drivers/net/ieee802154/ca8210.c
drivers/net/ieee802154/mac802154_hwsim.c
drivers/net/ipvlan/ipvlan_main.c
drivers/net/macvlan.c
drivers/net/net_failover.c
drivers/net/netdevsim/bpf.c
drivers/net/netdevsim/ipsec.c
drivers/net/phy/fixed_phy.c
drivers/net/phy/icplus.c
drivers/net/phy/marvell.c
drivers/net/phy/marvell10g.c
drivers/net/phy/mdio-gpio.c
drivers/net/phy/mscc.c
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
drivers/net/phy/sfp-bus.c
drivers/net/phy/vitesse.c
drivers/net/ppp/ppp_async.c
drivers/net/ppp/ppp_generic.c
drivers/net/ppp/ppp_synctty.c
drivers/net/ppp/pptp.c
drivers/net/rionet.c
drivers/net/tap.c
drivers/net/team/team.c
drivers/net/tun.c
drivers/net/usb/Kconfig
drivers/net/usb/Makefile
drivers/net/usb/aqc111.c [new file with mode: 0644]
drivers/net/usb/aqc111.h [new file with mode: 0644]
drivers/net/usb/cdc_ether.c
drivers/net/usb/hso.c
drivers/net/usb/ipheth.c
drivers/net/usb/lan78xx.c
drivers/net/usb/qmi_wwan.c
drivers/net/usb/r8152.c
drivers/net/usb/smsc95xx.c
drivers/net/virtio_net.c
drivers/net/vrf.c
drivers/net/vxlan.c
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/ath/ath10k/Kconfig
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/coredump.c
drivers/net/wireless/ath/ath10k/coredump.h
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/debugfs_sta.c
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/hw.c
drivers/net/wireless/ath/ath10k/hw.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/qmi.c
drivers/net/wireless/ath/ath10k/rx_desc.h
drivers/net/wireless/ath/ath10k/snoc.c
drivers/net/wireless/ath/ath10k/snoc.h
drivers/net/wireless/ath/ath10k/thermal.c
drivers/net/wireless/ath/ath10k/wmi-ops.h
drivers/net/wireless/ath/ath10k/wmi-tlv.c
drivers/net/wireless/ath/ath10k/wmi-tlv.h
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath10k/wow.c
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/ar5008_phy.c
drivers/net/wireless/ath/ath9k/ar9002_phy.c
drivers/net/wireless/ath/ath9k/ar9003_mci.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/dynack.c
drivers/net/wireless/ath/ath9k/dynack.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/carl9170/rx.c
drivers/net/wireless/ath/carl9170/tx.c
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/txrx_edma.c
drivers/net/wireless/ath/wil6210/txrx_edma.h
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/broadcom/b43/Kconfig
drivers/net/wireless/broadcom/b43/phy_common.c
drivers/net/wireless/broadcom/b43/phy_common.h
drivers/net/wireless/broadcom/b43/phy_lp.c
drivers/net/wireless/broadcom/b43/phy_n.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c [new file with mode: 0644]
drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h
drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c
drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
drivers/net/wireless/cisco/airo.c
drivers/net/wireless/intel/ipw2x00/Kconfig
drivers/net/wireless/intel/ipw2x00/ipw2100.c
drivers/net/wireless/intel/ipw2x00/ipw2200.c
drivers/net/wireless/intel/iwlegacy/3945-rs.c
drivers/net/wireless/intel/iwlegacy/4965-mac.c
drivers/net/wireless/intel/iwlegacy/common.c
drivers/net/wireless/intel/iwlwifi/Kconfig
drivers/net/wireless/intel/iwlwifi/Makefile
drivers/net/wireless/intel/iwlwifi/cfg/1000.c
drivers/net/wireless/intel/iwlwifi/cfg/2000.c
drivers/net/wireless/intel/iwlwifi/cfg/22000.c
drivers/net/wireless/intel/iwlwifi/cfg/6000.c
drivers/net/wireless/intel/iwlwifi/cfg/7000.c
drivers/net/wireless/intel/iwlwifi/cfg/8000.c
drivers/net/wireless/intel/iwlwifi/cfg/9000.c
drivers/net/wireless/intel/iwlwifi/dvm/main.c
drivers/net/wireless/intel/iwlwifi/fw/acpi.h
drivers/net/wireless/intel/iwlwifi/fw/api/config.h
drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h [new file with mode: 0644]
drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
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/img.h
drivers/net/wireless/intel/iwlwifi/fw/runtime.h
drivers/net/wireless/intel/iwlwifi/iwl-config.h
drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c [new file with mode: 0644]
drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h [new file with mode: 0644]
drivers/net/wireless/intel/iwlwifi/iwl-drv.c
drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c
drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.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/iwl-trans.h
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.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/rs-fw.c
drivers/net/wireless/intel/iwlwifi/mvm/rx.c
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
drivers/net/wireless/intel/iwlwifi/mvm/scan.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.h
drivers/net/wireless/intel/iwlwifi/mvm/tx.c
drivers/net/wireless/intel/iwlwifi/mvm/utils.c
drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c
drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c
drivers/net/wireless/intel/iwlwifi/pcie/drv.c
drivers/net/wireless/intel/iwlwifi/pcie/internal.h
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/intersil/hostap/hostap_main.c
drivers/net/wireless/intersil/orinoco/orinoco_usb.c
drivers/net/wireless/intersil/prism54/isl_38xx.c
drivers/net/wireless/intersil/prism54/isl_ioctl.c
drivers/net/wireless/intersil/prism54/islpci_dev.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mac80211_hwsim.h
drivers/net/wireless/marvell/libertas/if_spi.c
drivers/net/wireless/marvell/mwifiex/11n.c
drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
drivers/net/wireless/marvell/mwifiex/cfg80211.c
drivers/net/wireless/marvell/mwifiex/debugfs.c
drivers/net/wireless/marvell/mwifiex/ie.c
drivers/net/wireless/marvell/mwifiex/scan.c
drivers/net/wireless/marvell/mwifiex/sta_rx.c
drivers/net/wireless/marvell/mwifiex/uap_txrx.c
drivers/net/wireless/mediatek/mt76/Kconfig
drivers/net/wireless/mediatek/mt76/Makefile
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/Makefile
drivers/net/wireless/mediatek/mt76/mt76x0/debugfs.c [deleted file]
drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c
drivers/net/wireless/mediatek/mt76/mt76x0/init.c
drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h
drivers/net/wireless/mediatek/mt76/mt76x0/initvals_phy.h
drivers/net/wireless/mediatek/mt76/mt76x0/mac.c [deleted file]
drivers/net/wireless/mediatek/mt76/mt76x0/main.c
drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h
drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c
drivers/net/wireless/mediatek/mt76/mt76x0/phy.c
drivers/net/wireless/mediatek/mt76/mt76x0/phy.h
drivers/net/wireless/mediatek/mt76/mt76x0/trace.c [deleted file]
drivers/net/wireless/mediatek/mt76/mt76x0/trace.h [deleted file]
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 [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h
drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c
drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h
drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c
drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h
drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c
drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c
drivers/net/wireless/mediatek/mt76/mt76x02_util.c
drivers/net/wireless/mediatek/mt76/mt76x2/Makefile
drivers/net/wireless/mediatek/mt76/mt76x2/debugfs.c [deleted file]
drivers/net/wireless/mediatek/mt76/mt76x2/dfs.h [deleted file]
drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c
drivers/net/wireless/mediatek/mt76/mt76x2/init.c
drivers/net/wireless/mediatek/mt76/mt76x2/mac.h
drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c
drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h
drivers/net/wireless/mediatek/mt76/mt76x2/pci_dfs.c [deleted file]
drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
drivers/net/wireless/mediatek/mt76/mt76x2/pci_mac.c [deleted file]
drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c
drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c
drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c
drivers/net/wireless/mediatek/mt76/mt76x2/pci_tx.c [deleted file]
drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c
drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c
drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c
drivers/net/wireless/mediatek/mt76/tx.c
drivers/net/wireless/mediatek/mt76/usb.c
drivers/net/wireless/mediatek/mt76/usb_trace.h
drivers/net/wireless/quantenna/qtnfmac/Kconfig
drivers/net/wireless/quantenna/qtnfmac/Makefile
drivers/net/wireless/quantenna/qtnfmac/commands.c
drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c [new file with mode: 0644]
drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_ipc.h [new file with mode: 0644]
drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_regs.h [new file with mode: 0644]
drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h
drivers/net/wireless/quantenna/qtnfmac/util.c
drivers/net/wireless/quantenna/qtnfmac/util.h
drivers/net/wireless/ralink/rt2x00/rt2400pci.c
drivers/net/wireless/ralink/rt2x00/rt2500pci.c
drivers/net/wireless/ralink/rt2x00/rt2800lib.c
drivers/net/wireless/ralink/rt2x00/rt61pci.c
drivers/net/wireless/ray_cs.c
drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
drivers/net/wireless/realtek/rtlwifi/base.c
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c
drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/phy.c
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/table.c
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/table.h
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c
drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
drivers/net/wireless/st/cw1200/debug.c
drivers/net/wireless/st/cw1200/scan.c
drivers/net/wireless/st/cw1200/sta.c
drivers/net/wireless/ti/wlcore/sdio.c
drivers/net/wireless/ti/wlcore/vendor_cmd.c
drivers/net/wireless/virt_wifi.c [new file with mode: 0644]
drivers/net/wireless/zydas/zd1201.c
drivers/net/xen-netback/xenbus.c
drivers/net/xen-netfront.c
drivers/nvdimm/nd-core.h
drivers/nvdimm/pfn_devs.c
drivers/nvdimm/region_devs.c
drivers/nvme/host/core.c
drivers/nvme/host/fc.c
drivers/nvme/host/nvme.h
drivers/nvme/host/rdma.c
drivers/nvme/target/rdma.c
drivers/nvmem/core.c
drivers/of/of_net.c
drivers/opp/of.c
drivers/opp/ti-opp-supply.c
drivers/pci/controller/dwc/pci-imx6.c
drivers/pci/controller/dwc/pci-layerscape.c
drivers/pci/controller/dwc/pcie-designware-ep.c
drivers/pci/pci-acpi.c
drivers/pci/pci.c
drivers/pci/pcie/aer.c
drivers/pci/pcie/aspm.c
drivers/phy/qualcomm/phy-qcom-qusb2.c
drivers/phy/socionext/Kconfig
drivers/pinctrl/meson/pinctrl-meson-gxbb.c
drivers/pinctrl/meson/pinctrl-meson-gxl.c
drivers/pinctrl/meson/pinctrl-meson.c
drivers/pinctrl/meson/pinctrl-meson8.c
drivers/pinctrl/meson/pinctrl-meson8b.c
drivers/pinctrl/qcom/pinctrl-sdm660.c
drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c
drivers/ptp/ptp_clock.c
drivers/remoteproc/remoteproc_virtio.c
drivers/rtc/hctosys.c
drivers/rtc/rtc-cmos.c
drivers/rtc/rtc-hid-sensor-time.c
drivers/rtc/rtc-pcf2127.c
drivers/s390/cio/vfio_ccw_cp.c
drivers/s390/cio/vfio_ccw_drv.c
drivers/s390/crypto/ap_bus.c
drivers/s390/crypto/ap_bus.h
drivers/s390/crypto/ap_queue.c
drivers/s390/crypto/zcrypt_cex2a.c
drivers/s390/crypto/zcrypt_cex2c.c
drivers/s390/crypto/zcrypt_cex4.c
drivers/s390/net/ism_drv.c
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
drivers/s390/virtio/virtio_ccw.c
drivers/sbus/char/display7seg.c
drivers/sbus/char/envctrl.c
drivers/scsi/Kconfig
drivers/scsi/NCR5380.c
drivers/scsi/bnx2fc/bnx2fc_fcoe.c
drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
drivers/scsi/libiscsi.c
drivers/scsi/lpfc/lpfc_debugfs.c
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_sli.c
drivers/scsi/myrb.c
drivers/scsi/myrs.c
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/scsi_lib.c
drivers/scsi/storvsc_drv.c
drivers/scsi/ufs/ufs-hisi.c
drivers/scsi/ufs/ufs_quirks.h
drivers/scsi/ufs/ufshcd.c
drivers/scsi/vmw_pvscsi.c
drivers/slimbus/qcom-ngd-ctrl.c
drivers/slimbus/slimbus.h
drivers/soc/fsl/dpio/dpio-service.c
drivers/soc/fsl/dpio/qbman-portal.c
drivers/soc/fsl/dpio/qbman-portal.h
drivers/soc/fsl/qbman/qman.c
drivers/spi/spi-mt65xx.c
drivers/spi/spi-omap2-mcspi.c
drivers/staging/comedi/comedi.h
drivers/staging/comedi/drivers/ni_mio_common.c
drivers/staging/fsl-dpaa2/ethsw/ethsw.c
drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
drivers/staging/media/sunxi/cedrus/Kconfig
drivers/staging/media/sunxi/cedrus/TODO
drivers/staging/media/sunxi/cedrus/cedrus.c
drivers/staging/media/sunxi/cedrus/cedrus_hw.c
drivers/staging/most/core.c
drivers/staging/mt7621-dma/mtk-hsdma.c
drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c
drivers/staging/rtl8712/mlme_linux.c
drivers/staging/rtl8712/rtl871x_mlme.c
drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
drivers/staging/rtl8723bs/os_dep/ioctl_linux.c
drivers/staging/unisys/visornic/visornic_main.c
drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
drivers/target/iscsi/cxgbit/cxgbit_cm.c
drivers/target/target_core_transport.c
drivers/thermal/armada_thermal.c
drivers/thermal/broadcom/bcm2835_thermal.c
drivers/thermal/broadcom/brcmstb_thermal.c
drivers/thermal/hisi_thermal.c
drivers/thermal/st/stm_thermal.c
drivers/thunderbolt/switch.c
drivers/tty/serial/8250/8250_mtk.c
drivers/tty/serial/8250/8250_port.c
drivers/tty/serial/kgdboc.c
drivers/tty/serial/suncore.c
drivers/tty/tty_io.c
drivers/tty/tty_port.c
drivers/uio/uio.c
drivers/uio/uio_hv_generic.c
drivers/usb/class/cdc-acm.c
drivers/usb/core/hub.c
drivers/usb/core/quirks.c
drivers/usb/core/usb.c
drivers/usb/dwc2/pci.c
drivers/usb/dwc3/core.c
drivers/usb/dwc3/dwc3-pci.c
drivers/usb/dwc3/gadget.c
drivers/usb/gadget/function/f_fs.c
drivers/usb/gadget/function/u_ether.c
drivers/usb/gadget/udc/omap_udc.c
drivers/usb/host/hwa-hc.c
drivers/usb/host/xhci-histb.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-mtk.c
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci-tegra.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/misc/appledisplay.c
drivers/usb/serial/console.c
drivers/usb/serial/option.c
drivers/usb/storage/unusual_realtek.h
drivers/vhost/net.c
drivers/vhost/vhost.c
drivers/vhost/vsock.c
drivers/video/backlight/pwm_bl.c
drivers/virtio/virtio_ring.c
drivers/xen/balloon.c
drivers/xen/pvcalls-front.c
drivers/xen/xlate_mmu.c
fs/afs/dir.c
fs/afs/fs_probe.c
fs/afs/inode.c
fs/afs/internal.h
fs/afs/misc.c
fs/afs/rotate.c
fs/afs/rxrpc.c
fs/afs/vl_probe.c
fs/afs/vl_rotate.c
fs/aio.c
fs/btrfs/disk-io.c
fs/btrfs/file.c
fs/btrfs/qgroup.c
fs/btrfs/relocation.c
fs/btrfs/send.c
fs/btrfs/super.c
fs/btrfs/tree-checker.c
fs/cachefiles/namei.c
fs/cachefiles/rdwr.c
fs/cachefiles/xattr.c
fs/ceph/super.c
fs/ceph/super.h
fs/cifs/Kconfig
fs/cifs/dir.c
fs/cifs/file.c
fs/dax.c
fs/direct-io.c
fs/exportfs/expfs.c
fs/ext2/super.c
fs/ext2/xattr.c
fs/fscache/object.c
fs/fuse/dev.c
fs/fuse/dir.c
fs/fuse/file.c
fs/fuse/fuse_i.h
fs/fuse/inode.c
fs/gfs2/bmap.c
fs/gfs2/rgrp.c
fs/hfs/btree.c
fs/hfsplus/btree.c
fs/inode.c
fs/iomap.c
fs/namespace.c
fs/nfs/callback_proc.c
fs/nfs/delegation.c
fs/nfs/direct.c
fs/nfs/flexfilelayout/flexfilelayout.c
fs/nfs/flexfilelayout/flexfilelayout.h
fs/nfs/flexfilelayout/flexfilelayoutdev.c
fs/nfs/nfs42proc.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4state.c
fs/nfsd/nfs4proc.c
fs/nilfs2/btnode.c
fs/notify/fanotify/fanotify.c
fs/notify/fsnotify.c
fs/ocfs2/aops.c
fs/ocfs2/cluster/masklog.h
fs/ocfs2/export.c
fs/ocfs2/move_extents.c
fs/overlayfs/dir.c
fs/overlayfs/export.c
fs/overlayfs/inode.c
fs/pstore/ram.c
fs/read_write.c
fs/splice.c
fs/sysv/inode.c
fs/udf/super.c
fs/udf/unicode.c
fs/userfaultfd.c
fs/xfs/libxfs/xfs_bmap.c
fs/xfs/libxfs/xfs_btree.c
fs/xfs/libxfs/xfs_ialloc_btree.c
fs/xfs/xfs_bmap_util.c
fs/xfs/xfs_bmap_util.h
fs/xfs/xfs_buf_item.c
fs/xfs/xfs_file.c
fs/xfs/xfs_qm_bhv.c
fs/xfs/xfs_reflink.c
fs/xfs/xfs_trace.h
include/asm-generic/fixmap.h
include/linux/avf/virtchnl.h
include/linux/bpf.h
include/linux/bpf_verifier.h
include/linux/btf.h
include/linux/can/dev.h
include/linux/can/rx-offload.h
include/linux/cordic.h
include/linux/dax.h
include/linux/dma-direct.h
include/linux/efi.h
include/linux/etherdevice.h
include/linux/filter.h
include/linux/fscache-cache.h
include/linux/ftrace.h
include/linux/gfp.h
include/linux/hid-sensor-hub.h
include/linux/hid.h
include/linux/hyperv.h
include/linux/ieee80211.h
include/linux/if_bridge.h
include/linux/if_vlan.h
include/linux/indirect_call_wrapper.h [new file with mode: 0644]
include/linux/linkmode.h
include/linux/mempolicy.h
include/linux/mii.h
include/linux/mlx4/device.h
include/linux/mlx5/cq.h
include/linux/mlx5/device.h
include/linux/mlx5/driver.h
include/linux/mlx5/eq.h [new file with mode: 0644]
include/linux/mlx5/fs.h
include/linux/mlx5/mlx5_ifc.h
include/linux/mlx5/port.h
include/linux/mlx5/qp.h
include/linux/mlx5/srq.h [deleted file]
include/linux/mlx5/transobj.h
include/linux/mm_types.h
include/linux/mmc/sdio_ids.h
include/linux/mmzone.h
include/linux/mod_devicetable.h
include/linux/module.h
include/linux/net_dim.h
include/linux/netdevice.h
include/linux/netfilter/nf_conntrack_proto_gre.h
include/linux/netfilter/nfnetlink.h
include/linux/netfilter_bridge.h
include/linux/netlink.h
include/linux/objagg.h [new file with mode: 0644]
include/linux/of_net.h
include/linux/phy.h
include/linux/phy_fixed.h
include/linux/platform_data/gpio-davinci.h
include/linux/platform_data/mdio-gpio.h [new file with mode: 0644]
include/linux/psi.h
include/linux/pstore.h
include/linux/ptrace.h
include/linux/qed/qed_if.h
include/linux/rhashtable.h
include/linux/sched.h
include/linux/sched/smt.h [new file with mode: 0644]
include/linux/sfp.h
include/linux/skbuff.h
include/linux/skmsg.h
include/linux/socket.h
include/linux/sunrpc/xdr.h
include/linux/t10-pi.h
include/linux/tcp.h
include/linux/trace_events.h
include/linux/tracehook.h
include/linux/tracepoint.h
include/linux/tty.h
include/linux/usb.h
include/linux/usb/quirks.h
include/linux/xarray.h
include/media/media-request.h
include/media/mpeg2-ctrls.h [new file with mode: 0644]
include/media/v4l2-ctrls.h
include/media/v4l2-mem2mem.h
include/media/videobuf2-core.h
include/net/act_api.h
include/net/af_rxrpc.h
include/net/cfg80211.h
include/net/devlink.h
include/net/dsa.h
include/net/flow.h
include/net/flow_dissector.h
include/net/gen_stats.h
include/net/gre.h
include/net/inet_common.h
include/net/ip_tunnels.h
include/net/l3mdev.h
include/net/mac80211.h
include/net/neighbour.h
include/net/netfilter/br_netfilter.h
include/net/netfilter/ipv4/nf_nat_masquerade.h
include/net/netfilter/ipv6/nf_nat_masquerade.h
include/net/netns/xfrm.h
include/net/pkt_cls.h
include/net/sctp/constants.h
include/net/sctp/sctp.h
include/net/sctp/sm.h
include/net/sctp/structs.h
include/net/sctp/ulpevent.h
include/net/seg6.h
include/net/sock.h
include/net/switchdev.h
include/net/tcp.h
include/net/tls.h
include/net/udp_tunnel.h
include/net/vxlan.h
include/net/xfrm.h
include/soc/fsl/dpaa2-io.h
include/soc/fsl/qman.h
include/sound/pcm_params.h
include/sound/soc.h
include/trace/events/kyber.h
include/trace/events/net.h
include/trace/events/objagg.h [new file with mode: 0644]
include/trace/events/rxrpc.h
include/trace/events/sched.h
include/uapi/asm-generic/Kbuild.asm
include/uapi/asm-generic/unistd.h
include/uapi/linux/blkzoned.h
include/uapi/linux/bpf.h
include/uapi/linux/btf.h
include/uapi/linux/devlink.h
include/uapi/linux/ethtool.h
include/uapi/linux/if_bridge.h
include/uapi/linux/if_link.h
include/uapi/linux/if_tun.h
include/uapi/linux/if_tunnel.h
include/uapi/linux/in.h
include/uapi/linux/input-event-codes.h
include/uapi/linux/ncsi.h
include/uapi/linux/neighbour.h
include/uapi/linux/net_namespace.h
include/uapi/linux/net_tstamp.h
include/uapi/linux/netlink.h
include/uapi/linux/nl80211.h
include/uapi/linux/pkt_cls.h
include/uapi/linux/pkt_sched.h
include/uapi/linux/prctl.h
include/uapi/linux/sctp.h
include/uapi/linux/snmp.h
include/uapi/linux/tcp.h
include/uapi/linux/v4l2-controls.h
include/uapi/linux/videodev2.h
include/uapi/linux/virtio_config.h
include/uapi/linux/virtio_ring.h
include/xen/balloon.h
init/Kconfig
init/initramfs.c
kernel/bpf/arraymap.c
kernel/bpf/btf.c
kernel/bpf/core.c
kernel/bpf/cpumap.c
kernel/bpf/hashtab.c
kernel/bpf/local_storage.c
kernel/bpf/lpm_trie.c
kernel/bpf/offload.c
kernel/bpf/queue_stack_maps.c
kernel/bpf/syscall.c
kernel/bpf/verifier.c
kernel/cpu.c
kernel/debug/kdb/kdb_bt.c
kernel/debug/kdb/kdb_io.c
kernel/debug/kdb/kdb_keyboard.c
kernel/debug/kdb/kdb_main.c
kernel/debug/kdb/kdb_private.h
kernel/debug/kdb/kdb_support.c
kernel/dma/direct.c
kernel/dma/swiotlb.c
kernel/events/uprobes.c
kernel/kcov.c
kernel/module.c
kernel/ptrace.c
kernel/sched/core.c
kernel/sched/fair.c
kernel/sched/psi.c
kernel/sched/sched.h
kernel/sched/stats.h
kernel/stackleak.c
kernel/trace/bpf_trace.c
kernel/trace/ftrace.c
kernel/trace/trace.h
kernel/trace/trace_events_filter.c
kernel/trace/trace_events_trigger.c
kernel/trace/trace_functions_graph.c
kernel/trace/trace_irqsoff.c
kernel/trace/trace_sched_wakeup.c
lib/Kconfig
lib/Kconfig.debug
lib/Makefile
lib/cordic.c
lib/debugobjects.c
lib/iov_iter.c
lib/objagg.c [new file with mode: 0644]
lib/radix-tree.c
lib/rhashtable.c
lib/test_bpf.c
lib/test_firmware.c
lib/test_hexdump.c
lib/test_kmod.c
lib/test_objagg.c [new file with mode: 0644]
lib/test_rhashtable.c
lib/test_xarray.c
lib/ubsan.c
lib/xarray.c
mm/gup.c
mm/huge_memory.c
mm/hugetlb.c
mm/khugepaged.c
mm/memblock.c
mm/memory-failure.c
mm/mempolicy.c
mm/page_alloc.c
mm/rmap.c
mm/shmem.c
mm/sparse.c
mm/swapfile.c
mm/truncate.c
mm/userfaultfd.c
mm/vmstat.c
mm/z3fold.c
net/6lowpan/debugfs.c
net/8021q/vlan.c
net/8021q/vlan_core.c
net/Kconfig
net/batman-adv/Kconfig
net/batman-adv/bat_iv_ogm.c
net/batman-adv/bat_v.c
net/batman-adv/bat_v_elp.c
net/batman-adv/bridge_loop_avoidance.c
net/batman-adv/debugfs.c
net/batman-adv/distributed-arp-table.c
net/batman-adv/fragmentation.c
net/batman-adv/gateway_client.c
net/batman-adv/hard-interface.c
net/batman-adv/hash.c
net/batman-adv/hash.h
net/batman-adv/log.c
net/batman-adv/main.c
net/batman-adv/main.h
net/batman-adv/multicast.c
net/batman-adv/netlink.c
net/batman-adv/trace.c
net/batman-adv/trace.h
net/batman-adv/translation-table.c
net/batman-adv/types.h
net/bluetooth/6lowpan.c
net/bluetooth/hci_event.c
net/bluetooth/hci_request.c
net/bluetooth/l2cap_core.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c
net/bluetooth/sco.c
net/bpf/test_run.c
net/bridge/br.c
net/bridge/br_device.c
net/bridge/br_fdb.c
net/bridge/br_if.c
net/bridge/br_input.c
net/bridge/br_mdb.c
net/bridge/br_multicast.c
net/bridge/br_netfilter_hooks.c
net/bridge/br_netfilter_ipv6.c
net/bridge/br_netlink.c
net/bridge/br_private.h
net/bridge/br_switchdev.c
net/bridge/br_sysfs_br.c
net/bridge/br_sysfs_if.c
net/bridge/br_vlan.c
net/can/raw.c
net/ceph/messenger.c
net/core/datagram.c
net/core/dev.c
net/core/dev_addr_lists.c
net/core/dev_ioctl.c
net/core/devlink.c
net/core/filter.c
net/core/flow_dissector.c
net/core/gro_cells.c
net/core/neighbour.c
net/core/net-sysfs.c
net/core/net_namespace.c
net/core/netpoll.c
net/core/rtnetlink.c
net/core/skbuff.c
net/core/skmsg.c
net/core/sock.c
net/core/sock_reuseport.c
net/core/stream.c
net/core/sysctl_net_core.c
net/dccp/proto.c
net/decnet/af_decnet.c
net/dsa/Kconfig
net/dsa/dsa.c
net/dsa/dsa_priv.h
net/dsa/master.c
net/dsa/port.c
net/dsa/slave.c
net/dsa/tag_brcm.c
net/dsa/tag_dsa.c
net/dsa/tag_edsa.c
net/dsa/tag_gswip.c
net/dsa/tag_ksz.c
net/dsa/tag_lan9303.c
net/dsa/tag_mtk.c
net/dsa/tag_qca.c
net/dsa/tag_trailer.c
net/ethernet/eth.c
net/ieee802154/nl-phy.c
net/ipv4/af_inet.c
net/ipv4/devinet.c
net/ipv4/esp4.c
net/ipv4/esp4_offload.c
net/ipv4/fou.c
net/ipv4/inet_hashtables.c
net/ipv4/ip_forward.c
net/ipv4/ip_fragment.c
net/ipv4/ip_gre.c
net/ipv4/ip_input.c
net/ipv4/ip_output.c
net/ipv4/ip_tunnel_core.c
net/ipv4/ipconfig.c
net/ipv4/ipmr.c
net/ipv4/netfilter/ipt_MASQUERADE.c
net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
net/ipv4/netfilter/nf_reject_ipv4.c
net/ipv4/netfilter/nft_masq_ipv4.c
net/ipv4/proc.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/tcp.c
net/ipv4/tcp_bpf.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_offload.c
net/ipv4/tcp_output.c
net/ipv4/tcp_timer.c
net/ipv4/udp.c
net/ipv4/udp_offload.c
net/ipv4/udp_tunnel.c
net/ipv6/addrconf.c
net/ipv6/esp6.c
net/ipv6/esp6_offload.c
net/ipv6/inet6_hashtables.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_input.c
net/ipv6/ip6_offload.c
net/ipv6/ip6_output.c
net/ipv6/ip6_udp_tunnel.c
net/ipv6/ip6mr.c
net/ipv6/netfilter.c
net/ipv6/netfilter/ip6t_MASQUERADE.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/netfilter/nf_nat_masquerade_ipv6.c
net/ipv6/netfilter/nf_reject_ipv6.c
net/ipv6/netfilter/nft_masq_ipv6.c
net/ipv6/raw.c
net/ipv6/reassembly.c
net/ipv6/route.c
net/ipv6/seg6_iptunnel.c
net/ipv6/tcp_ipv6.c
net/ipv6/tcpv6_offload.c
net/ipv6/udp.c
net/ipv6/udp_offload.c
net/ipv6/xfrm6_input.c
net/ipv6/xfrm6_policy.c
net/ipv6/xfrm6_tunnel.c
net/key/af_key.c
net/l2tp/l2tp_core.c
net/l3mdev/l3mdev.c
net/mac80211/Kconfig
net/mac80211/cfg.c
net/mac80211/debugfs_netdev.c
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/ncsi/internal.h
net/ncsi/ncsi-aen.c
net/ncsi/ncsi-manage.c
net/ncsi/ncsi-netlink.c
net/ncsi/ncsi-pkt.h
net/ncsi/ncsi-rsp.c
net/netfilter/ipset/ip_set_list_set.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/nf_conncount.c
net/netfilter/nf_conntrack_proto_gre.c
net/netfilter/nf_conntrack_seqadj.c
net/netfilter/nf_log_common.c
net/netfilter/nf_nat_core.c
net/netfilter/nf_queue.c
net/netfilter/nf_tables_api.c
net/netfilter/nf_tables_core.c
net/netfilter/nfnetlink_cttimeout.c
net/netfilter/nfnetlink_queue.c
net/netfilter/nft_compat.c
net/netfilter/nft_flow_offload.c
net/netfilter/nft_meta.c
net/netfilter/nft_xfrm.c
net/netfilter/xt_RATEEST.c
net/netfilter/xt_hashlimit.c
net/netfilter/xt_physdev.c
net/netfilter/xt_policy.c
net/netlink/af_netlink.c
net/openvswitch/conntrack.c
net/openvswitch/vport-geneve.c
net/openvswitch/vport-gre.c
net/openvswitch/vport-vxlan.c
net/packet/af_packet.c
net/rds/message.c
net/rds/rdma.c
net/rds/rds.h
net/rds/send.c
net/rfkill/rfkill-gpio.c
net/rxrpc/af_rxrpc.c
net/sched/act_api.c
net/sched/act_pedit.c
net/sched/act_police.c
net/sched/act_tunnel_key.c
net/sched/cls_api.c
net/sched/cls_bpf.c
net/sched/cls_flower.c
net/sched/cls_matchall.c
net/sched/cls_u32.c
net/sched/sch_api.c
net/sched/sch_etf.c
net/sched/sch_fq.c
net/sched/sch_gred.c
net/sched/sch_mq.c
net/sched/sch_netem.c
net/sched/sch_red.c
net/sctp/associola.c
net/sctp/bind_addr.c
net/sctp/chunk.c
net/sctp/input.c
net/sctp/ipv6.c
net/sctp/output.c
net/sctp/primitive.c
net/sctp/sm_make_chunk.c
net/sctp/sm_sideeffect.c
net/sctp/sm_statetable.c
net/sctp/socket.c
net/sctp/stream.c
net/sctp/stream_interleave.c
net/sctp/ulpqueue.c
net/smc/af_smc.c
net/smc/smc.h
net/smc/smc_cdc.c
net/smc/smc_cdc.h
net/smc/smc_clc.c
net/smc/smc_clc.h
net/smc/smc_core.c
net/smc/smc_core.h
net/smc/smc_ism.c
net/smc/smc_ism.h
net/smc/smc_llc.c
net/smc/smc_llc.h
net/smc/smc_wr.c
net/socket.c
net/sunrpc/auth_generic.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/clnt.c
net/sunrpc/socklib.c
net/sunrpc/xdr.c
net/sunrpc/xprt.c
net/sunrpc/xprtsock.c
net/switchdev/switchdev.c
net/tipc/Makefile
net/tipc/bearer.c
net/tipc/bearer.h
net/tipc/discover.c
net/tipc/link.c
net/tipc/link.h
net/tipc/net.c
net/tipc/net.h
net/tipc/node.c
net/tipc/node.h
net/tipc/socket.c
net/tipc/socket.h
net/tipc/sysctl.c
net/tipc/trace.c [new file with mode: 0644]
net/tipc/trace.h [new file with mode: 0644]
net/tipc/udp_media.c
net/tls/tls_main.c
net/tls/tls_sw.c
net/vmw_vsock/af_vsock.c
net/vmw_vsock/vmci_transport.c
net/wireless/Makefile
net/wireless/chan.c
net/wireless/core.c
net/wireless/core.h
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/pmsr.c [new file with mode: 0644]
net/wireless/rdev-ops.h
net/wireless/scan.c
net/wireless/sme.c
net/wireless/trace.h
net/wireless/util.c
net/x25/af_x25.c
net/x25/x25_in.c
net/xdp/xsk.c
net/xfrm/Kconfig
net/xfrm/xfrm_device.c
net/xfrm/xfrm_input.c
net/xfrm/xfrm_interface.c
net/xfrm/xfrm_output.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_state.c
net/xfrm/xfrm_user.c
samples/bpf/Makefile
samples/bpf/bpf_load.c
samples/bpf/xdp1_user.c
scripts/Makefile.build
scripts/checkstack.pl
scripts/faddr2line
scripts/gcc-plugins/stackleak_plugin.c
scripts/spdxcheck.py
scripts/unifdef.c
security/integrity/digsig_asymmetric.c
security/integrity/ima/ima_policy.c
security/keys/keyctl_pkey.c
security/keys/trusted.c
security/selinux/hooks.c
security/selinux/nlmsgtab.c
security/selinux/ss/mls.c
security/selinux/xfrm.c
sound/core/control.c
sound/core/oss/pcm_oss.c
sound/core/oss/pcm_plugin.c
sound/core/pcm_native.c
sound/firewire/fireface/ff-protocol-ff400.c
sound/isa/wss/wss_lib.c
sound/pci/ac97/ac97_codec.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_ca0132.c
sound/pci/hda/patch_realtek.c
sound/soc/codecs/hdac_hdmi.c
sound/soc/codecs/pcm186x.h
sound/soc/codecs/pcm3060.c
sound/soc/codecs/wm_adsp.c
sound/soc/intel/Kconfig
sound/soc/intel/boards/Kconfig
sound/soc/intel/boards/cht_bsw_max98090_ti.c
sound/soc/intel/skylake/skl.c
sound/soc/omap/omap-abe-twl6040.c
sound/soc/omap/omap-dmic.c
sound/soc/omap/omap-mcbsp.c
sound/soc/omap/omap-mcpdm.c
sound/soc/qcom/common.c
sound/soc/qcom/qdsp6/q6afe-dai.c
sound/soc/qcom/qdsp6/q6afe.c
sound/soc/qcom/qdsp6/q6asm-dai.c
sound/soc/qcom/qdsp6/q6routing.c
sound/soc/rockchip/rockchip_pcm.c
sound/soc/sh/rcar/ssi.c
sound/soc/soc-acpi.c
sound/soc/soc-core.c
sound/soc/stm/stm32_sai_sub.c
sound/soc/sunxi/Kconfig
sound/soc/sunxi/sun8i-codec.c
sound/sparc/cs4231.c
sound/usb/card.c
sound/usb/quirks-table.h
sound/usb/quirks.c
tools/arch/x86/include/asm/cpufeatures.h
tools/bpf/bpftool/Documentation/bpftool-cgroup.rst
tools/bpf/bpftool/Documentation/bpftool-map.rst
tools/bpf/bpftool/Documentation/bpftool-net.rst
tools/bpf/bpftool/Documentation/bpftool-perf.rst
tools/bpf/bpftool/Documentation/bpftool-prog.rst
tools/bpf/bpftool/Documentation/bpftool.rst
tools/bpf/bpftool/Makefile
tools/bpf/bpftool/bash-completion/bpftool
tools/bpf/bpftool/btf_dumper.c
tools/bpf/bpftool/cfg.c
tools/bpf/bpftool/cfg.h
tools/bpf/bpftool/cgroup.c
tools/bpf/bpftool/common.c
tools/bpf/bpftool/jit_disasm.c
tools/bpf/bpftool/json_writer.c
tools/bpf/bpftool/json_writer.h
tools/bpf/bpftool/main.c
tools/bpf/bpftool/main.h
tools/bpf/bpftool/map.c
tools/bpf/bpftool/map_perf_ring.c
tools/bpf/bpftool/net.c
tools/bpf/bpftool/netlink_dumper.c
tools/bpf/bpftool/netlink_dumper.h
tools/bpf/bpftool/perf.c
tools/bpf/bpftool/prog.c
tools/bpf/bpftool/tracelog.c [new file with mode: 0644]
tools/bpf/bpftool/xlated_dumper.c
tools/bpf/bpftool/xlated_dumper.h
tools/build/Makefile.feature
tools/build/feature/Makefile
tools/build/feature/test-all.c
tools/build/feature/test-get_current_dir_name.c [new file with mode: 0644]
tools/include/uapi/asm-generic/ioctls.h
tools/include/uapi/drm/i915_drm.h
tools/include/uapi/linux/bpf.h
tools/include/uapi/linux/btf.h
tools/include/uapi/linux/netlink.h
tools/include/uapi/linux/pkt_cls.h [new file with mode: 0644]
tools/include/uapi/linux/prctl.h
tools/include/uapi/linux/tc_act/tc_bpf.h [new file with mode: 0644]
tools/lib/bpf/Build
tools/lib/bpf/Makefile
tools/lib/bpf/README.rst [new file with mode: 0644]
tools/lib/bpf/bpf.c
tools/lib/bpf/bpf.h
tools/lib/bpf/bpf_prog_linfo.c [new file with mode: 0644]
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 [new file with mode: 0644]
tools/lib/bpf/libbpf_errno.c
tools/lib/bpf/test_libbpf.cpp [new file with mode: 0644]
tools/objtool/elf.c
tools/perf/Makefile.config
tools/perf/tests/attr/base-record
tools/perf/trace/beauty/ioctl.c
tools/perf/util/Build
tools/perf/util/evsel.c
tools/perf/util/get_current_dir_name.c [new file with mode: 0644]
tools/perf/util/namespaces.c
tools/perf/util/namespaces.h
tools/perf/util/util.h
tools/power/cpupower/Makefile
tools/power/cpupower/bench/Makefile
tools/power/cpupower/debug/x86_64/Makefile
tools/power/cpupower/lib/cpufreq.c
tools/power/cpupower/lib/cpuidle.c
tools/power/cpupower/lib/cpupower.c
tools/power/cpupower/lib/cpupower_intern.h
tools/testing/nvdimm/test/nfit.c
tools/testing/radix-tree/Makefile
tools/testing/radix-tree/main.c
tools/testing/radix-tree/regression.h
tools/testing/radix-tree/regression4.c [new file with mode: 0644]
tools/testing/selftests/Makefile
tools/testing/selftests/bpf/.gitignore
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/bpf_flow.c
tools/testing/selftests/bpf/bpf_helpers.h
tools/testing/selftests/bpf/config
tools/testing/selftests/bpf/connect4_prog.c
tools/testing/selftests/bpf/connect6_prog.c
tools/testing/selftests/bpf/netcnt_prog.c
tools/testing/selftests/bpf/test_align.c
tools/testing/selftests/bpf/test_btf.c
tools/testing/selftests/bpf/test_btf_haskv.c
tools/testing/selftests/bpf/test_btf_nokv.c
tools/testing/selftests/bpf/test_flow_dissector.sh
tools/testing/selftests/bpf/test_libbpf.sh
tools/testing/selftests/bpf/test_lirc_mode2.sh
tools/testing/selftests/bpf/test_lirc_mode2_kern.c
tools/testing/selftests/bpf/test_lirc_mode2_user.c
tools/testing/selftests/bpf/test_map_in_map.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_maps.c
tools/testing/selftests/bpf/test_netcnt.c
tools/testing/selftests/bpf/test_progs.c
tools/testing/selftests/bpf/test_sk_lookup_kern.c
tools/testing/selftests/bpf/test_sock_addr.c
tools/testing/selftests/bpf/test_sockmap.c
tools/testing/selftests/bpf/test_sockmap_kern.h
tools/testing/selftests/bpf/test_tcpnotify.h [new file with mode: 0644]
tools/testing/selftests/bpf/test_tcpnotify_kern.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_tcpnotify_user.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_verifier.c
tools/testing/selftests/drivers/net/mlxsw/extack.sh [new file with mode: 0755]
tools/testing/selftests/drivers/net/mlxsw/one_armed_router.sh [new file with mode: 0755]
tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh [new file with mode: 0755]
tools/testing/selftests/drivers/net/mlxsw/spectrum-2/tc_flower.sh
tools/testing/selftests/drivers/net/mlxsw/vxlan.sh [new file with mode: 0755]
tools/testing/selftests/drivers/net/mlxsw/vxlan_flooding.sh [new file with mode: 0755]
tools/testing/selftests/net/.gitignore
tools/testing/selftests/net/Makefile
tools/testing/selftests/net/config
tools/testing/selftests/net/forwarding/lib.sh
tools/testing/selftests/net/forwarding/router_multicast.sh [new file with mode: 0755]
tools/testing/selftests/net/forwarding/router_vid_1.sh [new file with mode: 0755]
tools/testing/selftests/net/forwarding/vxlan_bridge_1d.sh [new file with mode: 0755]
tools/testing/selftests/net/forwarding/vxlan_bridge_1d_port_8472.sh [new file with mode: 0755]
tools/testing/selftests/net/forwarding/vxlan_bridge_1q.sh [new file with mode: 0755]
tools/testing/selftests/net/forwarding/vxlan_bridge_1q_port_8472.sh [new file with mode: 0755]
tools/testing/selftests/net/msg_zerocopy.c
tools/testing/selftests/net/msg_zerocopy.sh
tools/testing/selftests/net/reuseport_addr_any.c [new file with mode: 0644]
tools/testing/selftests/net/reuseport_addr_any.sh [new file with mode: 0755]
tools/testing/selftests/net/rtnetlink.sh
tools/testing/selftests/net/run_afpackettests
tools/testing/selftests/net/test_vxlan_fdb_changelink.sh [new file with mode: 0755]
tools/testing/selftests/net/test_vxlan_under_vrf.sh [new file with mode: 0755]
tools/testing/selftests/net/txring_overwrite.c [new file with mode: 0644]
tools/testing/selftests/net/udpgro.sh
tools/testing/selftests/net/udpgso_bench.sh
tools/testing/selftests/net/xfrm_policy.sh [new file with mode: 0755]
tools/testing/selftests/netfilter/Makefile [new file with mode: 0644]
tools/testing/selftests/netfilter/config [new file with mode: 0644]
tools/testing/selftests/netfilter/nft_trans_stress.sh [new file with mode: 0755]
tools/testing/selftests/networking/timestamping/Makefile
tools/testing/selftests/networking/timestamping/config [new file with mode: 0644]
tools/testing/selftests/networking/timestamping/txtimestamp.c
tools/testing/selftests/networking/timestamping/txtimestamp.sh [new file with mode: 0755]
tools/testing/selftests/powerpc/mm/wild_bctr.c
tools/testing/selftests/proc/proc-self-map-files-002.c
tools/testing/selftests/seccomp/seccomp_bpf.c
tools/testing/selftests/tc-testing/.gitignore
tools/testing/selftests/tc-testing/TdcPlugin.py
tools/testing/selftests/tc-testing/TdcResults.py [new file with mode: 0644]
tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py
tools/testing/selftests/tc-testing/tdc.py
tools/testing/selftests/tc-testing/tdc_config.py
tools/virtio/linux/kernel.h
virt/kvm/coalesced_mmio.c

diff --git a/CREDITS b/CREDITS
index 5befd2d..7d397ee 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -2138,6 +2138,10 @@ E: paul@laufernet.com
 D: Soundblaster driver fixes, ISAPnP quirk
 S: California, USA
 
+N: Jarkko Lavinen
+E: jarkko.lavinen@nokia.com
+D: OMAP MMC support
+
 N: Jonathan Layes
 D: ARPD support
 
@@ -2200,6 +2204,10 @@ S: Post Office Box 371
 S: North Little Rock, Arkansas 72115
 S: USA
 
+N: Christopher Li
+E: sparse@chrisli.org
+D: Sparse maintainer 2009 - 2018
+
 N: Stephan Linz
 E: linz@mazet.de
 E: Stephan.Linz@gmx.de
@@ -2533,6 +2541,10 @@ S: Ormond
 S: Victoria 3163
 S: Australia
 
+N: Eric Miao
+E: eric.y.miao@gmail.com
+D: MMP support
+
 N: Pauline Middelink
 E: middelin@polyware.nl
 D: General low-level bug fixes, /proc fixes, identd support
@@ -4107,6 +4119,10 @@ S: 1507 145th Place SE #B5
 S: Bellevue, Washington 98007
 S: USA
 
+N: Haojian Zhuang
+E: haojian.zhuang@gmail.com
+D: MMP support
+
 N: Richard Zidlicky
 E: rz@linux-m68k.org, rdzidlic@geocities.com
 W: http://www.geocities.com/rdzidlic
index f240221..985d84c 100644 (file)
@@ -1,4 +1,4 @@
-What:          /sys/class/net/<iface>/tagging
+What:          /sys/class/net/<iface>/dsa/tagging
 Date:          August 2018
 KernelVersion: 4.20
 Contact:       netdev@vger.kernel.org
index 81d1d5a..aefd358 100644 (file)
                        causing system reset or hang due to sending
                        INIT from AP to BSP.
 
-       disable_counter_freezing [HW]
+       perf_v4_pmi=    [X86,INTEL]
+                       Format: <bool>
                        Disable Intel PMU counter freezing feature.
                        The feature only exists starting from
                        Arch Perfmon v4 (Skylake and newer).
                        before loading.
                        See Documentation/blockdev/ramdisk.txt.
 
+       psi=            [KNL] Enable or disable pressure stall information
+                       tracking.
+                       Format: <bool>
+
        psmouse.proto=  [HW,MOUSE] Highest PS2 mouse protocol extension to
                        probe for; one of (bare|imps|exps|lifebook|any).
        psmouse.rate=   [HW,MOUSE] Set desired mouse report rate, in reports
 
        spectre_v2=     [X86] Control mitigation of Spectre variant 2
                        (indirect branch speculation) vulnerability.
+                       The default operation protects the kernel from
+                       user space attacks.
 
-                       on   - unconditionally enable
-                       off  - unconditionally disable
+                       on   - unconditionally enable, implies
+                              spectre_v2_user=on
+                       off  - unconditionally disable, implies
+                              spectre_v2_user=off
                        auto - kernel detects whether your CPU model is
                               vulnerable
 
                        CONFIG_RETPOLINE configuration option, and the
                        compiler with which the kernel was built.
 
+                       Selecting 'on' will also enable the mitigation
+                       against user space to user space task attacks.
+
+                       Selecting 'off' will disable both the kernel and
+                       the user space protections.
+
                        Specific mitigations can also be selected manually:
 
                        retpoline         - replace indirect branches
                        Not specifying this option is equivalent to
                        spectre_v2=auto.
 
+       spectre_v2_user=
+                       [X86] Control mitigation of Spectre variant 2
+                       (indirect branch speculation) vulnerability between
+                       user space tasks
+
+                       on      - Unconditionally enable mitigations. Is
+                                 enforced by spectre_v2=on
+
+                       off     - Unconditionally disable mitigations. Is
+                                 enforced by spectre_v2=off
+
+                       prctl   - Indirect branch speculation is enabled,
+                                 but mitigation can be enabled via prctl
+                                 per thread.  The mitigation control state
+                                 is inherited on fork.
+
+                       prctl,ibpb
+                               - Like "prctl" above, but only STIBP is
+                                 controlled per thread. IBPB is issued
+                                 always when switching between different user
+                                 space processes.
+
+                       seccomp
+                               - Same as "prctl" above, but all seccomp
+                                 threads will enable the mitigation unless
+                                 they explicitly opt out.
+
+                       seccomp,ibpb
+                               - Like "seccomp" above, but only STIBP is
+                                 controlled per thread. IBPB is issued
+                                 always when switching between different
+                                 user space processes.
+
+                       auto    - Kernel selects the mitigation depending on
+                                 the available CPU features and vulnerability.
+
+                       Default mitigation:
+                       If CONFIG_SECCOMP=y then "seccomp", otherwise "prctl"
+
+                       Not specifying this option is equivalent to
+                       spectre_v2_user=auto.
+
        spec_store_bypass_disable=
                        [HW] Control Speculative Store Bypass (SSB) Disable mitigation
                        (Speculative Store Bypass vulnerability)
                                        prevent spurious wakeup);
                                n = USB_QUIRK_DELAY_CTRL_MSG (Device needs a
                                        pause after every control message);
+                               o = USB_QUIRK_HUB_SLOW_RESET (Hub needs extra
+                                       delay after resetting its port);
                        Example: quirks=0781:5580:bk,0a5c:5834:gij
 
        usbhid.mousepoll=
index 47153e6..7eca902 100644 (file)
@@ -150,7 +150,7 @@ data structures necessary to handle the given policy and, possibly, to add
 a governor ``sysfs`` interface to it.  Next, the governor is started by
 invoking its ``->start()`` callback.
 
-That callback it expected to register per-CPU utilization update callbacks for
+That callback is expected to register per-CPU utilization update callbacks for
 all of the online CPUs belonging to the given policy with the CPU scheduler.
 The utilization update callbacks will be invoked by the CPU scheduler on
 important events, like task enqueue and dequeue, on every iteration of the
index 164bf71..30187d4 100644 (file)
@@ -32,16 +32,17 @@ Disclosure and embargoed information
 The security list is not a disclosure channel.  For that, see Coordination
 below.
 
-Once a robust fix has been developed, our preference is to release the
-fix in a timely fashion, treating it no differently than any of the other
-thousands of changes and fixes the Linux kernel project releases every
-month.
-
-However, at the request of the reporter, we will postpone releasing the
-fix for up to 5 business days after the date of the report or after the
-embargo has lifted; whichever comes first.  The only exception to that
-rule is if the bug is publicly known, in which case the preference is to
-release the fix as soon as it's available.
+Once a robust fix has been developed, the release process starts.  Fixes
+for publicly known bugs are released immediately.
+
+Although our preference is to release fixes for publicly undisclosed bugs
+as soon as they become available, this may be postponed at the request of
+the reporter or an affected party for up to 7 calendar days from the start
+of the release process, with an exceptional extension to 14 calendar days
+if it is agreed that the criticality of the bug requires more time.  The
+only valid reason for deferring the publication of a fix is to accommodate
+the logistics of QA and large scale rollouts which require release
+coordination.
 
 Whilst embargoed information may be shared with trusted individuals in
 order to develop a fix, such information will not be published alongside
index 76ccded..8f95776 100644 (file)
@@ -57,6 +57,7 @@ stable kernels.
 | ARM            | Cortex-A73      | #858921         | ARM64_ERRATUM_858921        |
 | ARM            | Cortex-A55      | #1024718        | ARM64_ERRATUM_1024718       |
 | ARM            | Cortex-A76      | #1188873        | ARM64_ERRATUM_1188873       |
+| ARM            | Cortex-A76      | #1286807        | ARM64_ERRATUM_1286807       |
 | ARM            | MMU-500         | #841119,#826419 | N/A                         |
 |                |                 |                 |                             |
 | Cavium         | ThunderX ITS    | #22375, #24313  | CAVIUM_ERRATUM_22375        |
index a4e7051..6a6d67a 100644 (file)
@@ -74,7 +74,8 @@ using :c:func:`xa_load`.  xa_store will overwrite any entry with the
 new entry and return the previous entry stored at that index.  You can
 use :c:func:`xa_erase` instead of calling :c:func:`xa_store` with a
 ``NULL`` entry.  There is no difference between an entry that has never
-been stored to and one that has most recently had ``NULL`` stored to it.
+been stored to, one that has been erased and one that has most recently
+had ``NULL`` stored to it.
 
 You can conditionally replace an entry at an index by using
 :c:func:`xa_cmpxchg`.  Like :c:func:`cmpxchg`, it will only succeed if
@@ -105,23 +106,44 @@ may result in the entry being marked at some, but not all of the other
 indices.  Storing into one index may result in the entry retrieved by
 some, but not all of the other indices changing.
 
+Sometimes you need to ensure that a subsequent call to :c:func:`xa_store`
+will not need to allocate memory.  The :c:func:`xa_reserve` function
+will store a reserved entry at the indicated index.  Users of the normal
+API will see this entry as containing ``NULL``.  If you do not need to
+use the reserved entry, you can call :c:func:`xa_release` to remove the
+unused entry.  If another user has stored to the entry in the meantime,
+:c:func:`xa_release` will do nothing; if instead you want the entry to
+become ``NULL``, you should use :c:func:`xa_erase`.
+
+If all entries in the array are ``NULL``, the :c:func:`xa_empty` function
+will return ``true``.
+
 Finally, you can remove all entries from an XArray by calling
 :c:func:`xa_destroy`.  If the XArray entries are pointers, you may wish
 to free the entries first.  You can do this by iterating over all present
 entries in the XArray using the :c:func:`xa_for_each` iterator.
 
-ID assignment
--------------
+Allocating XArrays
+------------------
+
+If you use :c:func:`DEFINE_XARRAY_ALLOC` to define the XArray, or
+initialise it by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`,
+the XArray changes to track whether entries are in use or not.
 
 You can call :c:func:`xa_alloc` to store the entry at any unused index
 in the XArray.  If you need to modify the array from interrupt context,
 you can use :c:func:`xa_alloc_bh` or :c:func:`xa_alloc_irq` to disable
-interrupts while allocating the ID.  Unlike :c:func:`xa_store`, allocating
-a ``NULL`` pointer does not delete an entry.  Instead it reserves an
-entry like :c:func:`xa_reserve` and you can release it using either
-:c:func:`xa_erase` or :c:func:`xa_release`.  To use ID assignment, the
-XArray must be defined with :c:func:`DEFINE_XARRAY_ALLOC`, or initialised
-by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`,
+interrupts while allocating the ID.
+
+Using :c:func:`xa_store`, :c:func:`xa_cmpxchg` or :c:func:`xa_insert`
+will mark the entry as being allocated.  Unlike a normal XArray, storing
+``NULL`` will mark the entry as being in use, like :c:func:`xa_reserve`.
+To free an entry, use :c:func:`xa_erase` (or :c:func:`xa_release` if
+you only want to free the entry if it's ``NULL``).
+
+You cannot use ``XA_MARK_0`` with an allocating XArray as this mark
+is used to track whether an entry is free or not.  The other marks are
+available for your use.
 
 Memory allocation
 -----------------
@@ -158,15 +180,22 @@ Takes RCU read lock:
 
 Takes xa_lock internally:
  * :c:func:`xa_store`
+ * :c:func:`xa_store_bh`
+ * :c:func:`xa_store_irq`
  * :c:func:`xa_insert`
  * :c:func:`xa_erase`
  * :c:func:`xa_erase_bh`
  * :c:func:`xa_erase_irq`
  * :c:func:`xa_cmpxchg`
+ * :c:func:`xa_cmpxchg_bh`
+ * :c:func:`xa_cmpxchg_irq`
  * :c:func:`xa_store_range`
  * :c:func:`xa_alloc`
  * :c:func:`xa_alloc_bh`
  * :c:func:`xa_alloc_irq`
+ * :c:func:`xa_reserve`
+ * :c:func:`xa_reserve_bh`
+ * :c:func:`xa_reserve_irq`
  * :c:func:`xa_destroy`
  * :c:func:`xa_set_mark`
  * :c:func:`xa_clear_mark`
@@ -177,6 +206,7 @@ Assumes xa_lock held on entry:
  * :c:func:`__xa_erase`
  * :c:func:`__xa_cmpxchg`
  * :c:func:`__xa_alloc`
+ * :c:func:`__xa_reserve`
  * :c:func:`__xa_set_mark`
  * :c:func:`__xa_clear_mark`
 
@@ -234,7 +264,9 @@ Sharing the XArray with interrupt context is also possible, either
 using :c:func:`xa_lock_irqsave` in both the interrupt handler and process
 context, or :c:func:`xa_lock_irq` in process context and :c:func:`xa_lock`
 in the interrupt handler.  Some of the more common patterns have helper
-functions such as :c:func:`xa_erase_bh` and :c:func:`xa_erase_irq`.
+functions such as :c:func:`xa_store_bh`, :c:func:`xa_store_irq`,
+:c:func:`xa_erase_bh`, :c:func:`xa_erase_irq`, :c:func:`xa_cmpxchg_bh`
+and :c:func:`xa_cmpxchg_irq`.
 
 Sometimes you need to protect access to the XArray with a mutex because
 that lock sits above another mutex in the locking hierarchy.  That does
@@ -322,7 +354,8 @@ to :c:func:`xas_retry`, and retry the operation if it returns ``true``.
      - :c:func:`xa_is_zero`
      - Zero entries appear as ``NULL`` through the Normal API, but occupy
        an entry in the XArray which can be used to reserve the index for
-       future use.
+       future use.  This is used by allocating XArrays for allocated entries
+       which are ``NULL``.
 
 Other internal entries may be added in the future.  As far as possible, they
 will be handled by :c:func:`xas_retry`.
index a873855..14378ce 100644 (file)
@@ -86,9 +86,11 @@ transitions.
 This will give a fine grained information about all the CPU frequency
 transitions. The cat output here is a two dimensional matrix, where an entry
 <i,j> (row i, column j) represents the count of number of transitions from 
-Freq_i to Freq_j. Freq_i is in descending order with increasing rows and 
-Freq_j is in descending order with increasing columns. The output here also 
-contains the actual freq values for each row and column for better readability.
+Freq_i to Freq_j. Freq_i rows and Freq_j columns follow the sorting order in
+which the driver has provided the frequency table initially to the cpufreq core
+and so can be sorted (ascending or descending) or unsorted.  The output here
+also contains the actual freq values for each row and column for better
+readability.
 
 If the transition table is bigger than PAGE_SIZE, reading this will
 return an -EFBIG error.
index 2ec489e..b646bbc 100644 (file)
@@ -168,3 +168,19 @@ a shared clock is forbidden.
 
 Configuration of common clocks, which affect multiple consumer devices can
 be similarly specified in the clock provider node.
+
+==Protected clocks==
+
+Some platforms or firmwares may not fully expose all the clocks to the OS, such
+as in situations where those clks are used by drivers running in ARM secure
+execution levels. Such a configuration can be specified in device tree with the
+protected-clocks property in the form of a clock specifier list. This property should
+only be specified in the node that is providing the clocks being protected:
+
+   clock-controller@a000f000 {
+        compatible = "vendor,clk95;
+        reg = <0xa000f000 0x1000>
+        #clocks-cells = <1>;
+        ...
+        protected-clocks = <UART3_CLK>, <SPI5_CLK>;
+   };
diff --git a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt
deleted file mode 100644 (file)
index 2aa06ac..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-Generic ARM big LITTLE cpufreq driver's DT glue
------------------------------------------------
-
-This is DT specific glue layer for generic cpufreq driver for big LITTLE
-systems.
-
-Both required and optional properties listed below must be defined
-under node /cpus/cpu@x. Where x is the first cpu inside a cluster.
-
-FIXME: Cpus should boot in the order specified in DT and all cpus for a cluster
-must be present contiguously. Generic DT driver will check only node 'x' for
-cpu:x.
-
-Required properties:
-- operating-points: Refer to Documentation/devicetree/bindings/opp/opp.txt
-  for details
-
-Optional properties:
-- clock-latency: Specify the possible maximum transition latency for clock,
-  in unit of nanoseconds.
-
-Examples:
-
-cpus {
-       #address-cells = <1>;
-       #size-cells = <0>;
-
-       cpu@0 {
-               compatible = "arm,cortex-a15";
-               reg = <0>;
-               next-level-cache = <&L2>;
-               operating-points = <
-                       /* kHz    uV */
-                       792000  1100000
-                       396000  950000
-                       198000  850000
-               >;
-               clock-latency = <61036>; /* two CLK32 periods */
-       };
-
-       cpu@1 {
-               compatible = "arm,cortex-a15";
-               reg = <1>;
-               next-level-cache = <&L2>;
-       };
-
-       cpu@100 {
-               compatible = "arm,cortex-a7";
-               reg = <100>;
-               next-level-cache = <&L2>;
-               operating-points = <
-                       /* kHz    uV */
-                       792000  950000
-                       396000  750000
-                       198000  450000
-               >;
-               clock-latency = <61036>; /* two CLK32 periods */
-       };
-
-       cpu@101 {
-               compatible = "arm,cortex-a7";
-               reg = <101>;
-               next-level-cache = <&L2>;
-       };
-};
index 2bb2626..1ca6cc5 100644 (file)
@@ -12,7 +12,7 @@ The /chosen node should contain a 'linux,sysrq-reset-seq' child node to define
 a set of keys.
 
 Required property:
-sysrq-reset-seq: array of Linux keycodes, one keycode per cell.
+keyset: array of Linux keycodes, one keycode per cell.
 
 Optional property:
 timeout-ms: duration keys must be pressed together in milliseconds before
diff --git a/Documentation/devicetree/bindings/media/rockchip-vpu.txt b/Documentation/devicetree/bindings/media/rockchip-vpu.txt
deleted file mode 100644 (file)
index 35dc464..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-device-tree bindings for rockchip VPU codec
-
-Rockchip (Video Processing Unit) present in various Rockchip platforms,
-such as RK3288 and RK3399.
-
-Required properties:
-- compatible: value should be one of the following
-               "rockchip,rk3288-vpu";
-               "rockchip,rk3399-vpu";
-- interrupts: encoding and decoding interrupt specifiers
-- interrupt-names: should be "vepu" and "vdpu"
-- clocks: phandle to VPU aclk, hclk clocks
-- clock-names: should be "aclk" and "hclk"
-- power-domains: phandle to power domain node
-- iommus: phandle to a iommu node
-
-Example:
-SoC-specific DT entry:
-       vpu: video-codec@ff9a0000 {
-               compatible = "rockchip,rk3288-vpu";
-               reg = <0x0 0xff9a0000 0x0 0x800>;
-               interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
-                            <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
-               interrupt-names = "vepu", "vdpu";
-               clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
-               clock-names = "aclk", "hclk";
-               power-domains = <&power RK3288_PD_VIDEO>;
-               iommus = <&vpu_mmu>;
-       };
index 01fdc33..bb7e896 100644 (file)
@@ -10,7 +10,7 @@ such as network interfaces, crypto accelerator instances, L2 switches,
 etc.
 
 For an overview of the DPAA2 architecture and fsl-mc bus see:
-Documentation/networking/dpaa2/overview.rst
+Documentation/networking/device_drivers/freescale/dpaa2/overview.rst
 
 As described in the above overview, all DPAA2 objects in a DPRC share the
 same hardware "isolation context" and a 10-bit value called an ICID
index 4194ff7..c26f4e1 100644 (file)
@@ -10,6 +10,8 @@ device the slave device is attached to.
 Required properties:
 
  - compatible: should contain one of the following:
+   * "brcm,bcm20702a1"
+   * "brcm,bcm4330-bt"
    * "brcm,bcm43438-bt"
 
 Optional properties:
@@ -18,8 +20,13 @@ Optional properties:
  - shutdown-gpios: GPIO specifier, used to enable the BT module
  - device-wakeup-gpios: GPIO specifier, used to wakeup the controller
  - host-wakeup-gpios: GPIO specifier, used to wakeup the host processor
- - clocks: clock specifier if external clock provided to the controller
- - clock-names: should be "extclk"
+ - clocks: 1 or 2 clocks as defined in clock-names below, in that order
+ - clock-names: names for clock inputs, matching the clocks given
+   - "extclk": deprecated, replaced by "txco"
+   - "txco": external reference clock (not a standalone crystal)
+   - "lpo": external low power 32.768 kHz clock
+ - vbat-supply: phandle to regulator supply for VBAT
+ - vddio-supply: phandle to regulator supply for VDDIO
 
 
 Example:
index bfc0c43..bc77477 100644 (file)
@@ -24,6 +24,14 @@ Optional properties:
               if this property is present then controller is assumed to be big
               endian.
 
+- fsl,stop-mode: register bits of stop mode control, the format is
+                <&gpr req_gpr req_bit ack_gpr ack_bit>.
+                gpr is the phandle to general purpose register node.
+                req_gpr is the gpr register offset of CAN stop request.
+                req_bit is the bit offset of CAN stop request.
+                ack_gpr is the gpr register offset of CAN stop acknowledge.
+                ack_bit is the bit offset of CAN stop acknowledge.
+
 Example:
 
        can@1c000 {
index 903a78d..3a9926f 100644 (file)
@@ -17,7 +17,7 @@ Example:
                reg = <1>;
                clocks = <&clk32m>;
                interrupt-parent = <&gpio4>;
-               interrupts = <13 IRQ_TYPE_EDGE_RISING>;
+               interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
                vdd-supply = <&reg5v0>;
                xceiver-supply = <&reg5v0>;
        };
index cc43728..9936b9e 100644 (file)
@@ -5,6 +5,7 @@ Required properties:
 - compatible: "renesas,can-r8a7743" if CAN controller is a part of R8A7743 SoC.
              "renesas,can-r8a7744" if CAN controller is a part of R8A7744 SoC.
              "renesas,can-r8a7745" if CAN controller is a part of R8A7745 SoC.
+             "renesas,can-r8a774a1" if CAN controller is a part of R8A774A1 SoC.
              "renesas,can-r8a7778" if CAN controller is a part of R8A7778 SoC.
              "renesas,can-r8a7779" if CAN controller is a part of R8A7779 SoC.
              "renesas,can-r8a7790" if CAN controller is a part of R8A7790 SoC.
@@ -14,26 +15,32 @@ Required properties:
              "renesas,can-r8a7794" if CAN controller is a part of R8A7794 SoC.
              "renesas,can-r8a7795" if CAN controller is a part of R8A7795 SoC.
              "renesas,can-r8a7796" if CAN controller is a part of R8A7796 SoC.
+             "renesas,can-r8a77965" if CAN controller is a part of R8A77965 SoC.
              "renesas,rcar-gen1-can" for a generic R-Car Gen1 compatible device.
              "renesas,rcar-gen2-can" for a generic R-Car Gen2 or RZ/G1
              compatible device.
-             "renesas,rcar-gen3-can" for a generic R-Car Gen3 compatible device.
+             "renesas,rcar-gen3-can" for a generic R-Car Gen3 or RZ/G2
+             compatible device.
              When compatible with the generic version, nodes must list the
              SoC-specific version corresponding to the platform first
              followed by the generic version.
 
 - reg: physical base address and size of the R-Car CAN register map.
 - interrupts: interrupt specifier for the sole interrupt.
-- clocks: phandles and clock specifiers for 3 CAN clock inputs.
-- clock-names: 3 clock input name strings: "clkp1", "clkp2", "can_clk".
+- clocks: phandles and clock specifiers for 2 CAN clock inputs for RZ/G2
+         devices.
+         phandles and clock specifiers for 3 CAN clock inputs for every other
+         SoC.
+- clock-names: 2 clock input name strings for RZ/G2: "clkp1", "can_clk".
+              3 clock input name strings for every other SoC: "clkp1", "clkp2",
+              "can_clk".
 - pinctrl-0: pin control group to be used for this controller.
 - pinctrl-names: must be "default".
 
-Required properties for "renesas,can-r8a7795" and "renesas,can-r8a7796"
-compatible:
-In R8A7795 and R8A7796 SoCs, "clkp2" can be CANFD clock. This is a div6 clock
-and can be used by both CAN and CAN FD controller at the same time. It needs to
-be scaled to maximum frequency if any of these controllers use it. This is done
+Required properties for R8A7795, R8A7796 and R8A77965:
+For the denoted SoCs, "clkp2" can be CANFD clock. This is a div6 clock and can
+be used by both CAN and CAN FD controller at the same time. It needs to be
+scaled to maximum frequency if any of these controllers use it. This is done
 using the below properties:
 
 - assigned-clocks: phandle of clkp2(CANFD) clock.
@@ -42,8 +49,9 @@ using the below properties:
 Optional properties:
 - renesas,can-clock-select: R-Car CAN Clock Source Select. Valid values are:
                            <0x0> (default) : Peripheral clock (clkp1)
-                           <0x1> : Peripheral clock (clkp2)
-                           <0x3> : Externally input clock
+                           <0x1> : Peripheral clock (clkp2) (not supported by
+                                   RZ/G2 devices)
+                           <0x3> : External input clock
 
 Example
 -------
index 060e2d4..100cc40 100644 (file)
@@ -6,6 +6,7 @@ Required properties:
                          - "xlnx,zynq-can-1.0" for Zynq CAN controllers
                          - "xlnx,axi-can-1.00.a" for Axi CAN controllers
                          - "xlnx,canfd-1.0" for CAN FD controllers
+                         - "xlnx,canfd-2.0" for CAN FD 2.0 controllers
 - reg                  : Physical base address and size of the controller
                          registers map.
 - interrupts           : Property with a value describing the interrupt
index 3ceeb8d..35694c0 100644 (file)
@@ -7,7 +7,7 @@ limitations.
 Current Binding
 ---------------
 
-Switches are true Linux devices and can be probes by any means. Once
+Switches are true Linux devices and can be probed by any means. Once
 probed, they register to the DSA framework, passing a node
 pointer. This node is expected to fulfil the following binding, and
 may contain additional properties as required by the device it is
index ac145b8..0f407fb 100644 (file)
@@ -8,6 +8,10 @@ Required properties:
   - "microchip,ksz9477"
   - "microchip,ksz9897"
 
+Optional properties:
+
+- reset-gpios          : Should be a gpio specifier for a reset line
+
 See Documentation/devicetree/bindings/net/dsa/dsa.txt for a list of additional
 required and optional properties.
 
diff --git a/Documentation/devicetree/bindings/net/icplus-ip101ag.txt b/Documentation/devicetree/bindings/net/icplus-ip101ag.txt
new file mode 100644 (file)
index 0000000..a784592
--- /dev/null
@@ -0,0 +1,19 @@
+IC Plus Corp. IP101A / IP101G Ethernet PHYs
+
+There are different models of the IP101G Ethernet PHY:
+- IP101GR (32-pin QFN package)
+- IP101G (die only, no package)
+- IP101GA (48-pin LQFP package)
+
+There are different models of the IP101A Ethernet PHY (which is the
+predecessor of the IP101G):
+- IP101A (48-pin LQFP package)
+- IP101AH (48-pin LQFP package)
+
+Optional properties for the IP101GR (32-pin QFN package):
+
+- icplus,select-rx-error:
+  pin 21 ("RXER/INTR_32") will output the receive error status.
+  interrupts are not routed outside the PHY in this mode.
+- icplus,select-interrupt:
+  pin 21 ("RXER/INTR_32") will output the interrupt signal.
diff --git a/Documentation/devicetree/bindings/net/mediatek-dwmac.txt b/Documentation/devicetree/bindings/net/mediatek-dwmac.txt
new file mode 100644 (file)
index 0000000..8a08621
--- /dev/null
@@ -0,0 +1,78 @@
+MediaTek DWMAC glue layer controller
+
+This file documents platform glue layer for stmmac.
+Please see stmmac.txt for the other unchanged properties.
+
+The device node has following properties.
+
+Required properties:
+- compatible:  Should be "mediatek,mt2712-gmac" for MT2712 SoC
+- reg:  Address and length of the register set for the device
+- interrupts:  Should contain the MAC interrupts
+- interrupt-names: Should contain a list of interrupt names corresponding to
+       the interrupts in the interrupts property, if available.
+       Should be "macirq" for the main MAC IRQ
+- clocks: Must contain a phandle for each entry in clock-names.
+- clock-names: The name of the clock listed in the clocks property. These are
+       "axi", "apb", "mac_main", "ptp_ref" for MT2712 SoC
+- mac-address: See ethernet.txt in the same directory
+- phy-mode: See ethernet.txt in the same directory
+- mediatek,pericfg: A phandle to the syscon node that control ethernet
+       interface and timing delay.
+
+Optional properties:
+- mediatek,tx-delay-ps: TX clock delay macro value. Default is 0.
+       It should be defined for RGMII/MII interface.
+- mediatek,rx-delay-ps: RX clock delay macro value. Default is 0.
+       It should be defined for RGMII/MII/RMII interface.
+Both delay properties need to be a multiple of 170 for RGMII interface,
+or will round down. Range 0~31*170.
+Both delay properties need to be a multiple of 550 for MII/RMII interface,
+or will round down. Range 0~31*550.
+
+- mediatek,rmii-rxc: boolean property, if present indicates that the RMII
+       reference clock, which is from external PHYs, is connected to RXC pin
+       on MT2712 SoC.
+       Otherwise, is connected to TXC pin.
+- mediatek,txc-inverse: boolean property, if present indicates that
+       1. tx clock will be inversed in MII/RGMII case,
+       2. tx clock inside MAC will be inversed relative to reference clock
+          which is from external PHYs in RMII case, and it rarely happen.
+- mediatek,rxc-inverse: boolean property, if present indicates that
+       1. rx clock will be inversed in MII/RGMII case.
+       2. reference clock will be inversed when arrived at MAC in RMII case.
+- assigned-clocks: mac_main and ptp_ref clocks
+- assigned-clock-parents: parent clocks of the assigned clocks
+
+Example:
+       eth: ethernet@1101c000 {
+               compatible = "mediatek,mt2712-gmac";
+               reg = <0 0x1101c000 0 0x1300>;
+               interrupts = <GIC_SPI 237 IRQ_TYPE_LEVEL_LOW>;
+               interrupt-names = "macirq";
+               phy-mode ="rgmii";
+               mac-address = [00 55 7b b5 7d f7];
+               clock-names = "axi",
+                             "apb",
+                             "mac_main",
+                             "ptp_ref",
+                             "ptp_top";
+               clocks = <&pericfg CLK_PERI_GMAC>,
+                        <&pericfg CLK_PERI_GMAC_PCLK>,
+                        <&topckgen CLK_TOP_ETHER_125M_SEL>,
+                        <&topckgen CLK_TOP_ETHER_50M_SEL>;
+               assigned-clocks = <&topckgen CLK_TOP_ETHER_125M_SEL>,
+                                 <&topckgen CLK_TOP_ETHER_50M_SEL>;
+               assigned-clock-parents = <&topckgen CLK_TOP_ETHERPLL_125M>,
+                                        <&topckgen CLK_TOP_APLL1_D3>;
+               mediatek,pericfg = <&pericfg>;
+               mediatek,tx-delay-ps = <1530>;
+               mediatek,rx-delay-ps = <1530>;
+               mediatek,rmii-rxc;
+               mediatek,txc-inverse;
+               mediatek,rxc-inverse;
+               snps,txpbl = <32>;
+               snps,rxpbl = <32>;
+               snps,reset-gpio = <&pio 87 GPIO_ACTIVE_LOW>;
+               snps,reset-active-low;
+       };
index 3530256..7ad3621 100644 (file)
@@ -18,6 +18,7 @@ Required properties:
                R-Car Gen2 and RZ/G1 devices.
 
       - "renesas,etheravb-r8a774a1" for the R8A774A1 SoC.
+      - "renesas,etheravb-r8a774c0" for the R8A774C0 SoC.
       - "renesas,etheravb-r8a7795" for the R8A7795 SoC.
       - "renesas,etheravb-r8a7796" for the R8A7796 SoC.
       - "renesas,etheravb-r8a77965" for the R8A77965 SoC.
index 2196d1a..ae661e6 100644 (file)
@@ -21,10 +21,22 @@ can be provided per device.
 
 SNOC based devices (i.e. wcn3990) uses compatible string "qcom,wcn3990-wifi".
 
-Optional properties:
 - reg: Address and length of the register set for the device.
 - reg-names: Must include the list of following reg names,
             "membase"
+- interrupts: reference to the list of 17 interrupt numbers for "qcom,ipq4019-wifi"
+             compatible target.
+             reference to the list of 12 interrupt numbers for "qcom,wcn3990-wifi"
+             compatible target.
+             Must contain interrupt-names property per entry for
+             "qcom,ath10k", "qcom,ipq4019-wifi" compatible targets.
+
+- interrupt-names: Must include the entries for MSI interrupt
+                  names ("msi0" to "msi15") and legacy interrupt
+                  name ("legacy") for "qcom,ath10k", "qcom,ipq4019-wifi"
+                  compatible targets.
+
+Optional properties:
 - resets: Must contain an entry for each entry in reset-names.
           See ../reset/reseti.txt for details.
 - reset-names: Must include the list of following reset names,
@@ -37,12 +49,9 @@ Optional properties:
 - clocks: List of clock specifiers, must contain an entry for each required
           entry in clock-names.
 - clock-names: Should contain the clock names "wifi_wcss_cmd", "wifi_wcss_ref",
-               "wifi_wcss_rtc".
-- interrupts: List of interrupt lines. Must contain an entry
-             for each entry in the interrupt-names property.
-- interrupt-names: Must include the entries for MSI interrupt
-                  names ("msi0" to "msi15") and legacy interrupt
-                  name ("legacy"),
+              "wifi_wcss_rtc" for "qcom,ipq4019-wifi" compatible target and
+              "cxo_ref_clk_pin" for "qcom,wcn3990-wifi"
+              compatible target.
 - qcom,msi_addr: MSI interrupt address.
 - qcom,msi_base: Base value to add before writing MSI data into
                MSI address register.
@@ -55,14 +64,25 @@ Optional properties:
 - qcom,ath10k-pre-calibration-data : pre calibration data as an array,
                                     the length can vary between hw versions.
 - <supply-name>-supply: handle to the regulator device tree node
-                          optional "supply-name" is "vdd-0.8-cx-mx".
+                          optional "supply-name" are "vdd-0.8-cx-mx",
+                          "vdd-1.8-xo", "vdd-1.3-rfa" and "vdd-3.3-ch0".
 - memory-region:
        Usage: optional
        Value type: <phandle>
        Definition: reference to the reserved-memory for the msa region
                    used by the wifi firmware running in Q6.
+- iommus:
+       Usage: optional
+       Value type: <prop-encoded-array>
+       Definition: A list of phandle and IOMMU specifier pairs.
+- ext-fem-name:
+       Usage: Optional
+       Value type: string
+       Definition: Name of external front end module used. Some valid FEM names
+                   for example: "microsemi-lx5586", "sky85703-11"
+                   and "sky85803" etc.
 
-Example (to supply the calibration data alone):
+Example (to supply PCI based wifi block details):
 
 In this example, the node is defined as child node of the PCI controller.
 
@@ -74,10 +94,10 @@ pci {
                #address-cells = <3>;
                device_type = "pci";
 
-               ath10k@0,0 {
+               wifi@0,0 {
                        reg = <0 0 0 0 0>;
-                       device_type = "pci";
                        qcom,ath10k-calibration-data = [ 01 02 03 ... ];
+                       ext-fem-name = "microsemi-lx5586";
                };
        };
 };
@@ -138,21 +158,25 @@ wifi@18000000 {
                compatible = "qcom,wcn3990-wifi";
                reg = <0x18800000 0x800000>;
                reg-names = "membase";
-               clocks = <&clock_gcc clk_aggre2_noc_clk>;
-               clock-names = "smmu_aggre2_noc_clk"
+               clocks = <&clock_gcc clk_rf_clk2_pin>;
+               clock-names = "cxo_ref_clk_pin";
                interrupts =
-                          <0 130 0 /* CE0 */ >,
-                          <0 131 0 /* CE1 */ >,
-                          <0 132 0 /* CE2 */ >,
-                          <0 133 0 /* CE3 */ >,
-                          <0 134 0 /* CE4 */ >,
-                          <0 135 0 /* CE5 */ >,
-                          <0 136 0 /* CE6 */ >,
-                          <0 137 0 /* CE7 */ >,
-                          <0 138 0 /* CE8 */ >,
-                          <0 139 0 /* CE9 */ >,
-                          <0 140 0 /* CE10 */ >,
-                          <0 141 0 /* CE11 */ >;
+                       <GIC_SPI 414 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 415 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 417 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 418 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 419 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 420 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 421 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 423 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 424 IRQ_TYPE_LEVEL_HIGH>,
+                       <GIC_SPI 425 IRQ_TYPE_LEVEL_HIGH>;
                vdd-0.8-cx-mx-supply = <&pm8998_l5>;
+               vdd-1.8-xo-supply = <&vreg_l7a_1p8>;
+               vdd-1.3-rfa-supply = <&vreg_l17a_1p3>;
+               vdd-3.3-ch0-supply = <&vreg_l25a_3p3>;
                memory-region = <&wifi_msa_mem>;
+               iommus = <&apps_smmu 0x0040 0x1>;
 };
index adf20b2..fbc198d 100644 (file)
@@ -40,24 +40,36 @@ Required properties:
                "ref" for 19.2 MHz ref clk,
                "com_aux" for phy common block aux clock,
                "ref_aux" for phy reference aux clock,
+
+               For "qcom,ipq8074-qmp-pcie-phy": no clocks are listed.
                For "qcom,msm8996-qmp-pcie-phy" must contain:
                        "aux", "cfg_ahb", "ref".
                For "qcom,msm8996-qmp-usb3-phy" must contain:
                        "aux", "cfg_ahb", "ref".
-               For "qcom,qmp-v3-usb3-phy" must contain:
+               For "qcom,sdm845-qmp-usb3-phy" must contain:
+                       "aux", "cfg_ahb", "ref", "com_aux".
+               For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
                        "aux", "cfg_ahb", "ref", "com_aux".
+               For "qcom,sdm845-qmp-ufs-phy" must contain:
+                       "ref", "ref_aux".
 
  - resets: a list of phandles and reset controller specifier pairs,
           one for each entry in reset-names.
  - reset-names: "phy" for reset of phy block,
                "common" for phy common block reset,
-               "cfg" for phy's ahb cfg block reset (Optional).
+               "cfg" for phy's ahb cfg block reset.
+
+               For "qcom,ipq8074-qmp-pcie-phy" must contain:
+                       "phy", "common".
                For "qcom,msm8996-qmp-pcie-phy" must contain:
-                "phy", "common", "cfg".
+                       "phy", "common", "cfg".
                For "qcom,msm8996-qmp-usb3-phy" must contain
-                "phy", "common".
-               For "qcom,ipq8074-qmp-pcie-phy" must contain:
-                "phy", "common".
+                       "phy", "common".
+               For "qcom,sdm845-qmp-usb3-phy" must contain:
+                       "phy", "common".
+               For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
+                       "phy", "common".
+               For "qcom,sdm845-qmp-ufs-phy": no resets are listed.
 
  - vdda-phy-supply: Phandle to a regulator supply to PHY core block.
  - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
@@ -79,9 +91,10 @@ Required properties for child node:
 
  - #phy-cells: must be 0
 
+Required properties child node of pcie and usb3 qmp phys:
  - clocks: a list of phandles and clock-specifier pairs,
           one for each entry in clock-names.
- - clock-names: Must contain following for pcie and usb qmp phys:
+ - clock-names: Must contain following:
                 "pipe<lane-number>" for pipe clock specific to each lane.
  - clock-output-names: Name of the PHY clock that will be the parent for
                       the above pipe clock.
@@ -91,9 +104,11 @@ Required properties for child node:
                        (or)
                  "pcie20_phy1_pipe_clk"
 
+Required properties for child node of PHYs with lane reset, AKA:
+       "qcom,msm8996-qmp-pcie-phy"
  - resets: a list of phandles and reset controller specifier pairs,
           one for each entry in reset-names.
- - reset-names: Must contain following for pcie qmp phys:
+ - reset-names: Must contain following:
                 "lane<lane-number>" for reset specific to each lane.
 
 Example:
index 504a4ec..b04e66a 100644 (file)
@@ -5,18 +5,20 @@ UniPhier SoCs have SCSSI which supports SPI single channel.
 Required properties:
  - compatible: should be "socionext,uniphier-scssi"
  - reg: address and length of the spi master registers
- - #address-cells: must be <1>, see spi-bus.txt
- - #size-cells: must be <0>, see spi-bus.txt
- - clocks: A phandle to the clock for the device.
- - resets: A phandle to the reset control for the device.
+ - interrupts: a single interrupt specifier
+ - pinctrl-names: should be "default"
+ - pinctrl-0: pin control state for the default mode
+ - clocks: a phandle to the clock for the device
+ - resets: a phandle to the reset control for the device
 
 Example:
 
 spi0: spi@54006000 {
        compatible = "socionext,uniphier-scssi";
        reg = <0x54006000 0x100>;
-       #address-cells = <1>;
-       #size-cells = <0>;
+       interrupts = <0 39 4>;
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_spi0>;
        clocks = <&peri_clk 11>;
        resets = <&peri_rst 11>;
 };
index 4b1a2a8..cc6b2c0 100644 (file)
@@ -170,6 +170,7 @@ holtek      Holtek Semiconductor, Inc.
 hwacom HwaCom Systems Inc.
 i2se   I2SE GmbH
 ibm    International Business Machines (IBM)
+icplus IC Plus Corp.
 idt    Integrated Device Technologies, Inc.
 ifi    Ingenieurburo Fur Ic-Technologie (I/F/I)
 ilitek ILI Technology Corporation (ILITEK)
index cef220c..a8c0873 100644 (file)
@@ -190,16 +190,7 @@ A few EV_REL codes have special meanings:
 * REL_WHEEL, REL_HWHEEL:
 
   - These codes are used for vertical and horizontal scroll wheels,
-    respectively. The value is the number of "notches" moved on the wheel, the
-    physical size of which varies by device. For high-resolution wheels (which
-    report multiple events for each notch of movement, or do not have notches)
-    this may be an approximation based on the high-resolution scroll events.
-
-* REL_WHEEL_HI_RES:
-
-  - If a vertical scroll wheel supports high-resolution scrolling, this code
-    will be emitted in addition to REL_WHEEL. The value is the (approximate)
-    distance travelled by the user's finger, in microns.
+    respectively.
 
 EV_ABS
 ------
index 0f8b318..de131f0 100644 (file)
@@ -1,4 +1,28 @@
-.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
+.. This file is dual-licensed: you can use it either under the terms
+.. of the GPL or the GFDL 1.1+ license, at your option. Note that this
+.. dual licensing only applies to this file, and not this project as a
+.. whole.
+..
+.. a) This file 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 file 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.
+..
+.. Or, alternatively,
+..
+.. b) Permission is granted to copy, distribute and/or modify this
+..    document under the terms of the GNU Free Documentation License,
+..    Version 1.1 or any later version published by the Free Software
+..    Foundation, with no Invariant Sections, no Front-Cover Texts
+..    and no Back-Cover Texts. A copy of the license is included at
+..    Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
 
 .. _media_ioc_request_alloc:
 
index 6dd2d7f..5d26043 100644 (file)
@@ -1,4 +1,28 @@
-.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
+.. This file is dual-licensed: you can use it either under the terms
+.. of the GPL or the GFDL 1.1+ license, at your option. Note that this
+.. dual licensing only applies to this file, and not this project as a
+.. whole.
+..
+.. a) This file 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 file 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.
+..
+.. Or, alternatively,
+..
+.. b) Permission is granted to copy, distribute and/or modify this
+..    document under the terms of the GNU Free Documentation License,
+..    Version 1.1 or any later version published by the Free Software
+..    Foundation, with no Invariant Sections, no Front-Cover Texts
+..    and no Back-Cover Texts. A copy of the license is included at
+..    Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
 
 .. _media_request_ioc_queue:
 
index febe888..ec61960 100644 (file)
@@ -1,4 +1,28 @@
-.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
+.. This file is dual-licensed: you can use it either under the terms
+.. of the GPL or the GFDL 1.1+ license, at your option. Note that this
+.. dual licensing only applies to this file, and not this project as a
+.. whole.
+..
+.. a) This file 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 file 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.
+..
+.. Or, alternatively,
+..
+.. b) Permission is granted to copy, distribute and/or modify this
+..    document under the terms of the GNU Free Documentation License,
+..    Version 1.1 or any later version published by the Free Software
+..    Foundation, with no Invariant Sections, no Front-Cover Texts
+..    and no Back-Cover Texts. A copy of the license is included at
+..    Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
 
 .. _media_request_ioc_reinit:
 
index 5f4a230..945113d 100644 (file)
@@ -1,4 +1,28 @@
-.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
+.. This file is dual-licensed: you can use it either under the terms
+.. of the GPL or the GFDL 1.1+ license, at your option. Note that this
+.. dual licensing only applies to this file, and not this project as a
+.. whole.
+..
+.. a) This file 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 file 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.
+..
+.. Or, alternatively,
+..
+.. b) Permission is granted to copy, distribute and/or modify this
+..    document under the terms of the GNU Free Documentation License,
+..    Version 1.1 or any later version published by the Free Software
+..    Foundation, with no Invariant Sections, no Front-Cover Texts
+..    and no Back-Cover Texts. A copy of the license is included at
+..    Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
 
 .. _media-request-api:
 
index 098d7f2..dcf3f35 100644 (file)
@@ -1,4 +1,28 @@
-.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
+.. This file is dual-licensed: you can use it either under the terms
+.. of the GPL or the GFDL 1.1+ license, at your option. Note that this
+.. dual licensing only applies to this file, and not this project as a
+.. whole.
+..
+.. a) This file 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 file 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.
+..
+.. Or, alternatively,
+..
+.. b) Permission is granted to copy, distribute and/or modify this
+..    document under the terms of the GNU Free Documentation License,
+..    Version 1.1 or any later version published by the Free Software
+..    Foundation, with no Invariant Sections, no Front-Cover Texts
+..    and no Back-Cover Texts. A copy of the license is included at
+..    Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
 
 .. _request-func-close:
 
index ff7b072..11a22f8 100644 (file)
@@ -1,4 +1,28 @@
-.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
+.. This file is dual-licensed: you can use it either under the terms
+.. of the GPL or the GFDL 1.1+ license, at your option. Note that this
+.. dual licensing only applies to this file, and not this project as a
+.. whole.
+..
+.. a) This file 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 file 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.
+..
+.. Or, alternatively,
+..
+.. b) Permission is granted to copy, distribute and/or modify this
+..    document under the terms of the GNU Free Documentation License,
+..    Version 1.1 or any later version published by the Free Software
+..    Foundation, with no Invariant Sections, no Front-Cover Texts
+..    and no Back-Cover Texts. A copy of the license is included at
+..    Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
 
 .. _request-func-ioctl:
 
index 8519125..2609fd5 100644 (file)
@@ -1,4 +1,28 @@
-.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
+.. This file is dual-licensed: you can use it either under the terms
+.. of the GPL or the GFDL 1.1+ license, at your option. Note that this
+.. dual licensing only applies to this file, and not this project as a
+.. whole.
+..
+.. a) This file 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 file 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.
+..
+.. Or, alternatively,
+..
+.. b) Permission is granted to copy, distribute and/or modify this
+..    document under the terms of the GNU Free Documentation License,
+..    Version 1.1 or any later version published by the Free Software
+..    Foundation, with no Invariant Sections, no Front-Cover Texts
+..    and no Back-Cover Texts. A copy of the license is included at
+..    Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections
 
 .. _request-func-poll:
 
index f7ac8d0..b65dc07 100644 (file)
@@ -40,7 +40,7 @@ To use the :ref:`format` ioctls applications set the ``type`` field of the
 the desired operation. Both drivers and applications must set the remainder of
 the :c:type:`v4l2_format` structure to 0.
 
-.. _v4l2-meta-format:
+.. c:type:: v4l2_meta_format
 
 .. tabularcolumns:: |p{1.4cm}|p{2.2cm}|p{13.9cm}|
 
index 65a1d87..027358b 100644 (file)
@@ -1505,6 +1505,11 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type -
     configuring a stateless hardware decoding pipeline for MPEG-2.
     The bitstream parameters are defined according to :ref:`mpeg2part2`.
 
+    .. note::
+
+       This compound control is not yet part of the public kernel API and
+       it is expected to change.
+
 .. c:type:: v4l2_ctrl_mpeg2_slice_params
 
 .. cssclass:: longtable
@@ -1625,6 +1630,11 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type -
     Specifies quantization matrices (as extracted from the bitstream) for the
     associated MPEG-2 slice data.
 
+    .. note::
+
+       This compound control is not yet part of the public kernel API and
+       it is expected to change.
+
 .. c:type:: v4l2_ctrl_mpeg2_quantization
 
 .. cssclass:: longtable
index 3ead350..9ea494a 100644 (file)
@@ -132,6 +132,11 @@ The format as returned by :ref:`VIDIOC_TRY_FMT <VIDIOC_G_FMT>` must be identical
       - ``sdr``
       - Definition of a data format, see :ref:`pixfmt`, used by SDR
        capture and output devices.
+    * -
+      - struct :c:type:`v4l2_meta_format`
+      - ``meta``
+      - Definition of a metadata format, see :ref:`meta-formats`, used by
+       metadata capture devices.
     * -
       - __u8
       - ``raw_data``\ [200]
diff --git a/Documentation/networking/3c509.txt b/Documentation/networking/3c509.txt
deleted file mode 100644 (file)
index fbf722e..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-Linux and the 3Com EtherLink III Series Ethercards (driver v1.18c and higher)
-----------------------------------------------------------------------------
-
-This file contains the instructions and caveats for v1.18c and higher versions
-of the 3c509 driver. You should not use the driver without reading this file.
-
-release 1.0
-28 February 2002
-Current maintainer (corrections to):
-  David Ruggiero <jdr@farfalle.com>
-
-----------------------------------------------------------------------------
-
-(0) Introduction
-
-The following are notes and information on using the 3Com EtherLink III series
-ethercards in Linux. These cards are commonly known by the most widely-used
-card's 3Com model number, 3c509. They are all 10mb/s ISA-bus cards and shouldn't
-be (but sometimes are) confused with the similarly-numbered PCI-bus "3c905"
-(aka "Vortex" or "Boomerang") series.  Kernel support for the 3c509 family is
-provided by the module 3c509.c, which has code to support all of the following
-models:
-
-  3c509 (original ISA card)
-  3c509B (later revision of the ISA card; supports full-duplex)
-  3c589 (PCMCIA)
-  3c589B (later revision of the 3c589; supports full-duplex)
-  3c579 (EISA)
-
-Large portions of this documentation were heavily borrowed from the guide
-written the original author of the 3c509 driver, Donald Becker. The master
-copy of that document, which contains notes on older versions of the driver,
-currently resides on Scyld web server: http://www.scyld.com/.
-
-
-(1) Special Driver Features
-
-Overriding card settings
-
-The driver allows boot- or load-time overriding of the card's detected IOADDR,
-IRQ, and transceiver settings, although this capability shouldn't generally be
-needed except to enable full-duplex mode (see below). An example of the syntax
-for LILO parameters for doing this:
-
-    ether=10,0x310,3,0x3c509,eth0 
-
-This configures the first found 3c509 card for IRQ 10, base I/O 0x310, and
-transceiver type 3 (10base2). The flag "0x3c509" must be set to avoid conflicts
-with other card types when overriding the I/O address. When the driver is
-loaded as a module, only the IRQ may be overridden. For example,
-setting two cards to IRQ10 and IRQ11 is done by using the irq module
-option:
-
-   options 3c509 irq=10,11
-
-
-(2) Full-duplex mode
-
-The v1.18c driver added support for the 3c509B's full-duplex capabilities.
-In order to enable and successfully use full-duplex mode, three conditions
-must be met: 
-
-(a) You must have a Etherlink III card model whose hardware supports full-
-duplex operations. Currently, the only members of the 3c509 family that are
-positively known to support full-duplex are the 3c509B (ISA bus) and 3c589B
-(PCMCIA) cards. Cards without the "B" model designation do *not* support
-full-duplex mode; these include the original 3c509 (no "B"), the original
-3c589, the 3c529 (MCA bus), and the 3c579 (EISA bus).
-
-(b) You must be using your card's 10baseT transceiver (i.e., the RJ-45
-connector), not its AUI (thick-net) or 10base2 (thin-net/coax) interfaces.
-AUI and 10base2 network cabling is physically incapable of full-duplex
-operation.
-
-(c) Most importantly, your 3c509B must be connected to a link partner that is
-itself full-duplex capable. This is almost certainly one of two things: a full-
-duplex-capable  Ethernet switch (*not* a hub), or a full-duplex-capable NIC on
-another system that's connected directly to the 3c509B via a crossover cable.
-
-Full-duplex mode can be enabled using 'ethtool'.
-/////Extremely important caution concerning full-duplex mode/////
-Understand that the 3c509B's hardware's full-duplex support is much more
-limited than that provide by more modern network interface cards. Although
-at the physical layer of the network it fully supports full-duplex operation,
-the card was designed before the current Ethernet auto-negotiation (N-way)
-spec was written. This means that the 3c509B family ***cannot and will not
-auto-negotiate a full-duplex connection with its link partner under any
-circumstances, no matter how it is initialized***. If the full-duplex mode
-of the 3c509B is enabled, its link partner will very likely need to be
-independently _forced_ into full-duplex mode as well; otherwise various nasty
-failures will occur - at the very least, you'll see massive numbers of packet
-collisions. This is one of very rare circumstances where disabling auto-
-negotiation and forcing the duplex mode of a network interface card or switch
-would ever be necessary or desirable.
-
-
-(3) Available Transceiver Types
-
-For versions of the driver v1.18c and above, the available transceiver types are:
-0  transceiver type from EEPROM config (normally 10baseT); force half-duplex
-1  AUI (thick-net / DB15 connector)
-2  (undefined)
-3  10base2 (thin-net == coax / BNC connector)
-4  10baseT (RJ-45 connector); force half-duplex mode
-8  transceiver type and duplex mode taken from card's EEPROM config settings
-12 10baseT (RJ-45 connector); force full-duplex mode
-
-Prior to driver version 1.18c, only transceiver codes 0-4 were supported. Note
-that the new transceiver codes 8 and 12 are the *only* ones that will enable
-full-duplex mode, no matter what the card's detected EEPROM settings might be.
-This insured that merely upgrading the driver from an earlier version would
-never automatically enable full-duplex mode in an existing installation;
-it must always be explicitly enabled via one of these code in order to be
-activated.
-
-The transceiver type can be changed using 'ethtool'.
-  
-
-(4a) Interpretation of error messages and common problems
-
-Error Messages
-
-eth0: Infinite loop in interrupt, status 2011. 
-These are "mostly harmless" message indicating that the driver had too much
-work during that interrupt cycle. With a status of 0x2011 you are receiving
-packets faster than they can be removed from the card. This should be rare
-or impossible in normal operation. Possible causes of this error report are:
-   - a "green" mode enabled that slows the processor down when there is no
-     keyboard activity. 
-
-   - some other device or device driver hogging the bus or disabling interrupts.
-     Check /proc/interrupts for excessive interrupt counts. The timer tick
-     interrupt should always be incrementing faster than the others. 
-
-No received packets 
-If a 3c509, 3c562 or 3c589 can successfully transmit packets, but never
-receives packets (as reported by /proc/net/dev or 'ifconfig') you likely
-have an interrupt line problem. Check /proc/interrupts to verify that the
-card is actually generating interrupts. If the interrupt count is not
-increasing you likely have a physical conflict with two devices trying to
-use the same ISA IRQ line. The common conflict is with a sound card on IRQ10
-or IRQ5, and the easiest solution is to move the 3c509 to a different
-interrupt line. If the device is receiving packets but 'ping' doesn't work,
-you have a routing problem.
-
-Tx Carrier Errors Reported in /proc/net/dev 
-If an EtherLink III appears to transmit packets, but the "Tx carrier errors"
-field in /proc/net/dev increments as quickly as the Tx packet count, you
-likely have an unterminated network or the incorrect media transceiver selected. 
-
-3c509B card is not detected on machines with an ISA PnP BIOS. 
-While the updated driver works with most PnP BIOS programs, it does not work
-with all. This can be fixed by disabling PnP support using the 3Com-supplied
-setup program. 
-
-3c509 card is not detected on overclocked machines 
-Increase the delay time in id_read_eeprom() from the current value, 500,
-to an absurdly high value, such as 5000. 
-
-
-(4b) Decoding Status and Error Messages
-
-The bits in the main status register are: 
-
-value  description
-0x01   Interrupt latch
-0x02   Tx overrun, or Rx underrun
-0x04   Tx complete
-0x08   Tx FIFO room available
-0x10   A complete Rx packet has arrived
-0x20   A Rx packet has started to arrive
-0x40   The driver has requested an interrupt
-0x80   Statistics counter nearly full
-
-The bits in the transmit (Tx) status word are: 
-
-value  description
-0x02   Out-of-window collision.
-0x04   Status stack overflow (normally impossible).
-0x08   16 collisions.
-0x10   Tx underrun (not enough PCI bus bandwidth).
-0x20   Tx jabber.
-0x40   Tx interrupt requested.
-0x80   Status is valid (this should always be set).
-
-
-When a transmit error occurs the driver produces a status message such as 
-
-   eth0: Transmit error, Tx status register 82
-
-The two values typically seen here are:
-
-0x82 
-Out of window collision. This typically occurs when some other Ethernet
-host is incorrectly set to full duplex on a half duplex network. 
-
-0x88 
-16 collisions. This typically occurs when the network is exceptionally busy
-or when another host doesn't correctly back off after a collision. If this
-error is mixed with 0x82 errors it is the result of a host incorrectly set
-to full duplex (see above).
-
-Both of these errors are the result of network problems that should be
-corrected. They do not represent driver malfunction.
-
-
-(5) Revision history (this file)
-
-28Feb02 v1.0  DR   New; major portions based on Becker original 3c509 docs
-
diff --git a/Documentation/networking/LICENSE.qla3xxx b/Documentation/networking/LICENSE.qla3xxx
deleted file mode 100644 (file)
index 2f2077e..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-Copyright (c)  2003-2006 QLogic Corporation
-QLogic Linux Networking HBA Driver
-
-This program includes a device driver for Linux 2.6 that may be
-distributed with QLogic hardware specific firmware binary file.
-You may modify and redistribute the device driver code under the
-GNU General Public License as published by the Free Software
-Foundation (version 2 or a later version).
-
-You may redistribute the hardware specific firmware binary file
-under the following terms:
-
-       1. Redistribution of source code (only if applicable),
-          must retain the above copyright notice, this list of
-          conditions and the following disclaimer.
-
-       2. Redistribution 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.
-
-       3. The name of QLogic Corporation may not be used to
-          endorse or promote products derived from this software
-          without specific prior written permission
-
-REGARDLESS OF WHAT LICENSING MECHANISM IS USED OR APPLICABLE,
-THIS PROGRAM IS PROVIDED BY QLOGIC CORPORATION "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 AUTHOR
-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.
-
-USER ACKNOWLEDGES AND AGREES THAT USE OF THIS PROGRAM WILL NOT
-CREATE OR GIVE GROUNDS FOR A LICENSE BY IMPLICATION, ESTOPPEL, OR
-OTHERWISE IN ANY INTELLECTUAL PROPERTY RIGHTS (PATENT, COPYRIGHT,
-TRADE SECRET, MASK WORK, OR OTHER PROPRIETARY RIGHT) EMBODIED IN
-ANY OTHER QLOGIC HARDWARE OR SOFTWARE EITHER SOLELY OR IN
-COMBINATION WITH THIS PROGRAM.
-
diff --git a/Documentation/networking/LICENSE.qlcnic b/Documentation/networking/LICENSE.qlcnic
deleted file mode 100644 (file)
index 2ae3b64..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-Copyright (c) 2009-2013 QLogic Corporation
-QLogic Linux qlcnic NIC Driver
-
-You may modify and redistribute the device driver code under the
-GNU General Public License (a copy of which is attached hereto as
-Exhibit A) published by the Free Software Foundation (version 2).
-
-
-EXHIBIT A
-
-                   GNU GENERAL PUBLIC LICENSE
-                      Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                           Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                   GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                           NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
diff --git a/Documentation/networking/LICENSE.qlge b/Documentation/networking/LICENSE.qlge
deleted file mode 100644 (file)
index ce64e4d..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-Copyright (c) 2003-2011 QLogic Corporation
-QLogic Linux qlge NIC Driver
-
-You may modify and redistribute the device driver code under the
-GNU General Public License (a copy of which is attached hereto as
-Exhibit A) published by the Free Software Foundation (version 2).
-
-
-EXHIBIT A
-
-                   GNU GENERAL PUBLIC LICENSE
-                      Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                           Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                   GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                           NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
diff --git a/Documentation/networking/README.ipw2100 b/Documentation/networking/README.ipw2100
deleted file mode 100644 (file)
index 6f85e1d..0000000
+++ /dev/null
@@ -1,293 +0,0 @@
-
-Intel(R) PRO/Wireless 2100 Driver for Linux in support of:
-
-Intel(R) PRO/Wireless 2100 Network Connection
-
-Copyright (C) 2003-2006, Intel Corporation
-
-README.ipw2100
-
-Version: git-1.1.5
-Date   : January 25, 2006
-
-Index
------------------------------------------------
-0. IMPORTANT INFORMATION BEFORE USING THIS DRIVER
-1. Introduction
-2. Release git-1.1.5 Current Features
-3. Command Line Parameters
-4. Sysfs Helper Files
-5. Radio Kill Switch
-6. Dynamic Firmware
-7. Power Management
-8. Support
-9. License
-
-
-0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
------------------------------------------------
-
-Important Notice FOR ALL USERS OR DISTRIBUTORS!!!!
-
-Intel wireless LAN adapters are engineered, manufactured, tested, and
-quality checked to ensure that they meet all necessary local and
-governmental regulatory agency requirements for the regions that they
-are designated and/or marked to ship into. Since wireless LANs are
-generally unlicensed devices that share spectrum with radars,
-satellites, and other licensed and unlicensed devices, it is sometimes
-necessary to dynamically detect, avoid, and limit usage to avoid
-interference with these devices. In many instances Intel is required to
-provide test data to prove regional and local compliance to regional and
-governmental regulations before certification or approval to use the
-product is granted. Intel's wireless LAN's EEPROM, firmware, and
-software driver are designed to carefully control parameters that affect
-radio operation and to ensure electromagnetic compliance (EMC). These
-parameters include, without limitation, RF power, spectrum usage,
-channel scanning, and human exposure.
-
-For these reasons Intel cannot permit any manipulation by third parties
-of the software provided in binary format with the wireless WLAN
-adapters (e.g., the EEPROM and firmware). Furthermore, if you use any
-patches, utilities, or code with the Intel wireless LAN adapters that
-have been manipulated by an unauthorized party (i.e., patches,
-utilities, or code (including open source code modifications) which have
-not been validated by Intel), (i) you will be solely responsible for
-ensuring the regulatory compliance of the products, (ii) Intel will bear
-no liability, under any theory of liability for any issues associated
-with the modified products, including without limitation, claims under
-the warranty and/or issues arising from regulatory non-compliance, and
-(iii) Intel will not provide or be required to assist in providing
-support to any third parties for such modified products.
-
-Note: Many regulatory agencies consider Wireless LAN adapters to be
-modules, and accordingly, condition system-level regulatory approval
-upon receipt and review of test data documenting that the antennas and
-system configuration do not cause the EMC and radio operation to be
-non-compliant.
-
-The drivers available for download from SourceForge are provided as a
-part of a development project.  Conformance to local regulatory
-requirements is the responsibility of the individual developer.  As
-such, if you are interested in deploying or shipping a driver as part of
-solution intended to be used for purposes other than development, please
-obtain a tested driver from Intel Customer Support at:
-
-http://www.intel.com/support/wireless/sb/CS-006408.htm
-
-1. Introduction
------------------------------------------------
-
-This document provides a brief overview of the features supported by the 
-IPW2100 driver project.  The main project website, where the latest 
-development version of the driver can be found, is:
-
-       http://ipw2100.sourceforge.net
-
-There you can find the not only the latest releases, but also information about
-potential fixes and patches, as well as links to the development mailing list
-for the driver project.
-
-
-2. Release git-1.1.5 Current Supported Features
------------------------------------------------
-- Managed (BSS) and Ad-Hoc (IBSS)
-- WEP (shared key and open)
-- Wireless Tools support 
-- 802.1x (tested with XSupplicant 1.0.1)
-
-Enabled (but not supported) features:
-- Monitor/RFMon mode
-- WPA/WPA2
-
-The distinction between officially supported and enabled is a reflection
-on the amount of validation and interoperability testing that has been
-performed on a given feature.
-
-
-3. Command Line Parameters
------------------------------------------------
-
-If the driver is built as a module, the following optional parameters are used
-by entering them on the command line with the modprobe command using this
-syntax:
-
-       modprobe ipw2100 [<option>=<VAL1><,VAL2>...]
-
-For example, to disable the radio on driver loading, enter:
-
-       modprobe ipw2100 disable=1
-
-The ipw2100 driver supports the following module parameters:
-
-Name           Value           Example:
-debug          0x0-0xffffffff  debug=1024
-mode           0,1,2           mode=1   /* AdHoc */
-channel                int             channel=3 /* Only valid in AdHoc or Monitor */
-associate      boolean         associate=0 /* Do NOT auto associate */
-disable                boolean         disable=1 /* Do not power the HW */
-
-
-4. Sysfs Helper Files
----------------------------     
------------------------------------------------
-
-There are several ways to control the behavior of the driver.  Many of the 
-general capabilities are exposed through the Wireless Tools (iwconfig).  There
-are a few capabilities that are exposed through entries in the Linux Sysfs.
-
-
------ Driver Level ------
-For the driver level files, look in /sys/bus/pci/drivers/ipw2100/
-
-  debug_level  
-       
-       This controls the same global as the 'debug' module parameter.  For 
-        information on the various debugging levels available, run the 'dvals'
-       script found in the driver source directory.
-
-       NOTE:  'debug_level' is only enabled if CONFIG_IPW2100_DEBUG is turn
-              on.
-
------ Device Level ------
-For the device level files look in
-       
-       /sys/bus/pci/drivers/ipw2100/{PCI-ID}/
-
-For example:
-       /sys/bus/pci/drivers/ipw2100/0000:02:01.0
-
-For the device level files, see /sys/bus/pci/drivers/ipw2100:
-
-  rf_kill
-       read - 
-       0 = RF kill not enabled (radio on)
-       1 = SW based RF kill active (radio off)
-       2 = HW based RF kill active (radio off)
-       3 = Both HW and SW RF kill active (radio off)
-       write -
-       0 = If SW based RF kill active, turn the radio back on
-       1 = If radio is on, activate SW based RF kill
-
-       NOTE: If you enable the SW based RF kill and then toggle the HW
-       based RF kill from ON -> OFF -> ON, the radio will NOT come back on
-
-
-5. Radio Kill Switch
------------------------------------------------
-Most laptops provide the ability for the user to physically disable the radio.
-Some vendors have implemented this as a physical switch that requires no
-software to turn the radio off and on.  On other laptops, however, the switch
-is controlled through a button being pressed and a software driver then making
-calls to turn the radio off and on.  This is referred to as a "software based
-RF kill switch"
-
-See the Sysfs helper file 'rf_kill' for determining the state of the RF switch
-on your system.
-
-
-6. Dynamic Firmware
------------------------------------------------
-As the firmware is licensed under a restricted use license, it can not be 
-included within the kernel sources.  To enable the IPW2100 you will need a 
-firmware image to load into the wireless NIC's processors.
-
-You can obtain these images from <http://ipw2100.sf.net/firmware.php>.
-
-See INSTALL for instructions on installing the firmware.
-
-
-7. Power Management
------------------------------------------------
-The IPW2100 supports the configuration of the Power Save Protocol 
-through a private wireless extension interface.  The IPW2100 supports 
-the following different modes:
-
-       off     No power management.  Radio is always on.
-       on      Automatic power management
-       1-5     Different levels of power management.  The higher the 
-               number the greater the power savings, but with an impact to 
-               packet latencies. 
-
-Power management works by powering down the radio after a certain 
-interval of time has passed where no packets are passed through the 
-radio.  Once powered down, the radio remains in that state for a given 
-period of time.  For higher power savings, the interval between last 
-packet processed to sleep is shorter and the sleep period is longer.
-
-When the radio is asleep, the access point sending data to the station 
-must buffer packets at the AP until the station wakes up and requests 
-any buffered packets.  If you have an AP that does not correctly support 
-the PSP protocol you may experience packet loss or very poor performance 
-while power management is enabled.  If this is the case, you will need 
-to try and find a firmware update for your AP, or disable power 
-management (via `iwconfig eth1 power off`)
-
-To configure the power level on the IPW2100 you use a combination of 
-iwconfig and iwpriv.  iwconfig is used to turn power management on, off, 
-and set it to auto.
-
-       iwconfig eth1 power off    Disables radio power down
-       iwconfig eth1 power on     Enables radio power management to 
-                                  last set level (defaults to AUTO)
-       iwpriv eth1 set_power 0    Sets power level to AUTO and enables 
-                                  power management if not previously 
-                                  enabled.
-       iwpriv eth1 set_power 1-5  Set the power level as specified, 
-                                  enabling power management if not 
-                                  previously enabled.
-
-You can view the current power level setting via:
-       
-       iwpriv eth1 get_power
-
-It will return the current period or timeout that is configured as a string
-in the form of xxxx/yyyy (z) where xxxx is the timeout interval (amount of
-time after packet processing), yyyy is the period to sleep (amount of time to 
-wait before powering the radio and querying the access point for buffered
-packets), and z is the 'power level'.  If power management is turned off the
-xxxx/yyyy will be replaced with 'off' -- the level reported will be the active
-level if `iwconfig eth1 power on` is invoked.
-
-
-8. Support
------------------------------------------------
-
-For general development information and support,
-go to:
-       
-    http://ipw2100.sf.net/
-
-The ipw2100 1.1.0 driver and firmware can be downloaded from:  
-
-    http://support.intel.com
-
-For installation support on the ipw2100 1.1.0 driver on Linux kernels 
-2.6.8 or greater, email support is available from:  
-
-    http://supportmail.intel.com
-
-9. License
------------------------------------------------
-
-  Copyright(c) 2003 - 2006 Intel Corporation. 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 (version 2) 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.
-  
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, write to the Free Software Foundation, Inc., 59 
-  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-  
-  The full GNU General Public License is included in this distribution in the
-  file called LICENSE.
-  
-  License Contact Information:
-  James P. Ketrenos <ipw2100-admin@linux.intel.com>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
diff --git a/Documentation/networking/README.ipw2200 b/Documentation/networking/README.ipw2200
deleted file mode 100644 (file)
index b7658be..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-
-Intel(R) PRO/Wireless 2915ABG Driver for Linux in support of:
-
-Intel(R) PRO/Wireless 2200BG Network Connection
-Intel(R) PRO/Wireless 2915ABG Network Connection
-
-Note: The Intel(R) PRO/Wireless 2915ABG Driver for Linux and Intel(R)
-PRO/Wireless 2200BG Driver for Linux is a unified driver that works on
-both hardware adapters listed above. In this document the Intel(R)
-PRO/Wireless 2915ABG Driver for Linux will be used to reference the
-unified driver.
-
-Copyright (C) 2004-2006, Intel Corporation
-
-README.ipw2200
-
-Version: 1.1.2
-Date   : March 30, 2006
-
-
-Index
------------------------------------------------
-0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
-1.   Introduction
-1.1. Overview of features
-1.2. Module parameters
-1.3. Wireless Extension Private Methods
-1.4. Sysfs Helper Files
-1.5. Supported channels
-2.   Ad-Hoc Networking
-3.   Interacting with Wireless Tools
-3.1. iwconfig mode
-3.2. iwconfig sens
-4.   About the Version Numbers
-5.   Firmware installation
-6.   Support
-7.   License
-
-
-0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
------------------------------------------------
-
-Important Notice FOR ALL USERS OR DISTRIBUTORS!!!! 
-
-Intel wireless LAN adapters are engineered, manufactured, tested, and
-quality checked to ensure that they meet all necessary local and
-governmental regulatory agency requirements for the regions that they
-are designated and/or marked to ship into. Since wireless LANs are
-generally unlicensed devices that share spectrum with radars,
-satellites, and other licensed and unlicensed devices, it is sometimes
-necessary to dynamically detect, avoid, and limit usage to avoid
-interference with these devices. In many instances Intel is required to
-provide test data to prove regional and local compliance to regional and
-governmental regulations before certification or approval to use the
-product is granted. Intel's wireless LAN's EEPROM, firmware, and
-software driver are designed to carefully control parameters that affect
-radio operation and to ensure electromagnetic compliance (EMC). These
-parameters include, without limitation, RF power, spectrum usage,
-channel scanning, and human exposure. 
-
-For these reasons Intel cannot permit any manipulation by third parties
-of the software provided in binary format with the wireless WLAN
-adapters (e.g., the EEPROM and firmware). Furthermore, if you use any
-patches, utilities, or code with the Intel wireless LAN adapters that
-have been manipulated by an unauthorized party (i.e., patches,
-utilities, or code (including open source code modifications) which have
-not been validated by Intel), (i) you will be solely responsible for
-ensuring the regulatory compliance of the products, (ii) Intel will bear
-no liability, under any theory of liability for any issues associated
-with the modified products, including without limitation, claims under
-the warranty and/or issues arising from regulatory non-compliance, and
-(iii) Intel will not provide or be required to assist in providing
-support to any third parties for such modified products.  
-
-Note: Many regulatory agencies consider Wireless LAN adapters to be
-modules, and accordingly, condition system-level regulatory approval
-upon receipt and review of test data documenting that the antennas and
-system configuration do not cause the EMC and radio operation to be
-non-compliant.
-
-The drivers available for download from SourceForge are provided as a 
-part of a development project.  Conformance to local regulatory 
-requirements is the responsibility of the individual developer.  As 
-such, if you are interested in deploying or shipping a driver as part of 
-solution intended to be used for purposes other than development, please 
-obtain a tested driver from Intel Customer Support at:
-
-http://support.intel.com
-
-
-1.   Introduction
------------------------------------------------
-The following sections attempt to provide a brief introduction to using 
-the Intel(R) PRO/Wireless 2915ABG Driver for Linux.
-
-This document is not meant to be a comprehensive manual on 
-understanding or using wireless technologies, but should be sufficient 
-to get you moving without wires on Linux.
-
-For information on building and installing the driver, see the INSTALL
-file.
-
-
-1.1. Overview of Features
------------------------------------------------
-The current release (1.1.2) supports the following features:
-
-+ BSS mode (Infrastructure, Managed)
-+ IBSS mode (Ad-Hoc)
-+ WEP (OPEN and SHARED KEY mode)
-+ 802.1x EAP via wpa_supplicant and xsupplicant
-+ Wireless Extension support 
-+ Full B and G rate support (2200 and 2915)
-+ Full A rate support (2915 only)
-+ Transmit power control
-+ S state support (ACPI suspend/resume)
-
-The following features are currently enabled, but not officially
-supported:
-
-+ WPA
-+ long/short preamble support
-+ Monitor mode (aka RFMon)
-
-The distinction between officially supported and enabled is a reflection 
-on the amount of validation and interoperability testing that has been
-performed on a given feature. 
-
-
-
-1.2. Command Line Parameters
------------------------------------------------
-
-Like many modules used in the Linux kernel, the Intel(R) PRO/Wireless
-2915ABG Driver for Linux allows configuration options to be provided 
-as module parameters.  The most common way to specify a module parameter 
-is via the command line.  
-
-The general form is:
-
-% modprobe ipw2200 parameter=value
-
-Where the supported parameter are:
-
-  associate
-       Set to 0 to disable the auto scan-and-associate functionality of the
-       driver.  If disabled, the driver will not attempt to scan 
-       for and associate to a network until it has been configured with 
-       one or more properties for the target network, for example configuring 
-       the network SSID.  Default is 0 (do not auto-associate)
-       
-       Example: % modprobe ipw2200 associate=0
-
-  auto_create
-       Set to 0 to disable the auto creation of an Ad-Hoc network 
-       matching the channel and network name parameters provided.  
-       Default is 1.
-
-  channel
-       channel number for association.  The normal method for setting
-        the channel would be to use the standard wireless tools
-        (i.e. `iwconfig eth1 channel 10`), but it is useful sometimes
-       to set this while debugging.  Channel 0 means 'ANY'
-
-  debug
-       If using a debug build, this is used to control the amount of debug
-       info is logged.  See the 'dvals' and 'load' script for more info on
-       how to use this (the dvals and load scripts are provided as part 
-       of the ipw2200 development snapshot releases available from the 
-       SourceForge project at http://ipw2200.sf.net)
-  
-  led
-       Can be used to turn on experimental LED code.
-       0 = Off, 1 = On.  Default is 1.
-
-  mode
-       Can be used to set the default mode of the adapter.  
-       0 = Managed, 1 = Ad-Hoc, 2 = Monitor
-
-
-1.3. Wireless Extension Private Methods
------------------------------------------------
-
-As an interface designed to handle generic hardware, there are certain 
-capabilities not exposed through the normal Wireless Tool interface.  As 
-such, a provision is provided for a driver to declare custom, or 
-private, methods.  The Intel(R) PRO/Wireless 2915ABG Driver for Linux 
-defines several of these to configure various settings.
-
-The general form of using the private wireless methods is:
-
-       % iwpriv $IFNAME method parameters
-
-Where $IFNAME is the interface name the device is registered with 
-(typically eth1, customized via one of the various network interface
-name managers, such as ifrename)
-
-The supported private methods are:
-
-  get_mode
-       Can be used to report out which IEEE mode the driver is 
-       configured to support.  Example:
-       
-       % iwpriv eth1 get_mode
-       eth1    get_mode:802.11bg (6)
-
-  set_mode
-       Can be used to configure which IEEE mode the driver will 
-       support.  
-
-       Usage:
-       % iwpriv eth1 set_mode {mode}
-       Where {mode} is a number in the range 1-7:
-       1       802.11a (2915 only)
-       2       802.11b
-       3       802.11ab (2915 only)
-       4       802.11g 
-       5       802.11ag (2915 only)
-       6       802.11bg
-       7       802.11abg (2915 only)
-
-  get_preamble
-       Can be used to report configuration of preamble length.
-
-  set_preamble
-       Can be used to set the configuration of preamble length:
-
-       Usage:
-       % iwpriv eth1 set_preamble {mode}
-       Where {mode} is one of:
-       1       Long preamble only
-       0       Auto (long or short based on connection)
-       
-
-1.4. Sysfs Helper Files:
------------------------------------------------
-
-The Linux kernel provides a pseudo file system that can be used to 
-access various components of the operating system.  The Intel(R)
-PRO/Wireless 2915ABG Driver for Linux exposes several configuration
-parameters through this mechanism.
-
-An entry in the sysfs can support reading and/or writing.  You can 
-typically query the contents of a sysfs entry through the use of cat, 
-and can set the contents via echo.  For example:
-
-% cat /sys/bus/pci/drivers/ipw2200/debug_level
-
-Will report the current debug level of the driver's logging subsystem 
-(only available if CONFIG_IPW2200_DEBUG was configured when the driver
-was built).
-
-You can set the debug level via:
-
-% echo $VALUE > /sys/bus/pci/drivers/ipw2200/debug_level
-
-Where $VALUE would be a number in the case of this sysfs entry.  The 
-input to sysfs files does not have to be a number.  For example, the 
-firmware loader used by hotplug utilizes sysfs entries for transferring 
-the firmware image from user space into the driver.
-
-The Intel(R) PRO/Wireless 2915ABG Driver for Linux exposes sysfs entries 
-at two levels -- driver level, which apply to all instances of the driver 
-(in the event that there are more than one device installed) and device 
-level, which applies only to the single specific instance.
-
-
-1.4.1 Driver Level Sysfs Helper Files
------------------------------------------------
-
-For the driver level files, look in /sys/bus/pci/drivers/ipw2200/
-
-  debug_level  
-       
-       This controls the same global as the 'debug' module parameter
-
-
-
-1.4.2 Device Level Sysfs Helper Files
------------------------------------------------
-
-For the device level files, look in
-       
-       /sys/bus/pci/drivers/ipw2200/{PCI-ID}/
-
-For example:
-       /sys/bus/pci/drivers/ipw2200/0000:02:01.0
-
-For the device level files, see /sys/bus/pci/drivers/ipw2200:
-
-  rf_kill
-       read - 
-       0 = RF kill not enabled (radio on)
-       1 = SW based RF kill active (radio off)
-       2 = HW based RF kill active (radio off)
-       3 = Both HW and SW RF kill active (radio off)
-       write -
-       0 = If SW based RF kill active, turn the radio back on
-       1 = If radio is on, activate SW based RF kill
-
-       NOTE: If you enable the SW based RF kill and then toggle the HW
-       based RF kill from ON -> OFF -> ON, the radio will NOT come back on
-       
-  ucode 
-       read-only access to the ucode version number
-
-  led
-       read -
-       0 = LED code disabled
-       1 = LED code enabled
-       write -
-       0 = Disable LED code
-       1 = Enable LED code
-
-       NOTE: The LED code has been reported to hang some systems when 
-       running ifconfig and is therefore disabled by default.
-
-
-1.5. Supported channels
------------------------------------------------
-
-Upon loading the Intel(R) PRO/Wireless 2915ABG Driver for Linux, a
-message stating the detected geography code and the number of 802.11
-channels supported by the card will be displayed in the log.
-
-The geography code corresponds to a regulatory domain as shown in the
-table below.
-
-                                         Supported channels
-Code   Geography                       802.11bg        802.11a
-
----    Restricted                      11               0
-ZZF    Custom US/Canada                11               8
-ZZD    Rest of World                   13               0
-ZZA    Custom USA & Europe & High      11              13
-ZZB    Custom NA & Europe              11              13
-ZZC    Custom Japan                    11               4
-ZZM    Custom                          11               0
-ZZE    Europe                          13              19
-ZZJ    Custom Japan                    14               4
-ZZR    Rest of World                   14               0
-ZZH    High Band                       13               4
-ZZG    Custom Europe                   13               4
-ZZK    Europe                          13              24
-ZZL    Europe                          11              13
-
-
-2.   Ad-Hoc Networking
------------------------------------------------
-
-When using a device in an Ad-Hoc network, it is useful to understand the 
-sequence and requirements for the driver to be able to create, join, or 
-merge networks.
-
-The following attempts to provide enough information so that you can 
-have a consistent experience while using the driver as a member of an 
-Ad-Hoc network.
-
-2.1. Joining an Ad-Hoc Network
------------------------------------------------
-
-The easiest way to get onto an Ad-Hoc network is to join one that 
-already exists.
-
-2.2. Creating an Ad-Hoc Network
------------------------------------------------
-
-An Ad-Hoc networks is created using the syntax of the Wireless tool.
-
-For Example:
-iwconfig eth1 mode ad-hoc essid testing channel 2
-
-2.3. Merging Ad-Hoc Networks
------------------------------------------------
-
-
-3.  Interaction with Wireless Tools
------------------------------------------------
-
-3.1 iwconfig mode
------------------------------------------------
-
-When configuring the mode of the adapter, all run-time configured parameters
-are reset to the value used when the module was loaded.  This includes
-channels, rates, ESSID, etc.
-
-3.2 iwconfig sens
------------------------------------------------
-
-The 'iwconfig ethX sens XX' command will not set the signal sensitivity
-threshold, as described in iwconfig documentation, but rather the number
-of consecutive missed beacons that will trigger handover, i.e. roaming
-to another access point. At the same time, it will set the disassociation
-threshold to 3 times the given value.
-
-
-4.   About the Version Numbers
------------------------------------------------
-
-Due to the nature of open source development projects, there are 
-frequently changes being incorporated that have not gone through 
-a complete validation process.  These changes are incorporated into 
-development snapshot releases.
-
-Releases are numbered with a three level scheme: 
-
-       major.minor.development
-
-Any version where the 'development' portion is 0 (for example
-1.0.0, 1.1.0, etc.) indicates a stable version that will be made 
-available for kernel inclusion.
-
-Any version where the 'development' portion is not a 0 (for
-example 1.0.1, 1.1.5, etc.) indicates a development version that is
-being made available for testing and cutting edge users.  The stability 
-and functionality of the development releases are not know.  We make
-efforts to try and keep all snapshots reasonably stable, but due to the
-frequency of their release, and the desire to get those releases 
-available as quickly as possible, unknown anomalies should be expected.
-
-The major version number will be incremented when significant changes
-are made to the driver.  Currently, there are no major changes planned.
-
-5.  Firmware installation
-----------------------------------------------
-
-The driver requires a firmware image, download it and extract the
-files under /lib/firmware (or wherever your hotplug's firmware.agent
-will look for firmware files)
-
-The firmware can be downloaded from the following URL:
-
-    http://ipw2200.sf.net/
-
-
-6.  Support
------------------------------------------------
-
-For direct support of the 1.0.0 version, you can contact 
-http://supportmail.intel.com, or you can use the open source project
-support.
-
-For general information and support, go to:
-       
-    http://ipw2200.sf.net/
-
-
-7.  License
------------------------------------------------
-
-  Copyright(c) 2003 - 2006 Intel Corporation. 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 version 2 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.
-  
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, write to the Free Software Foundation, Inc., 59 
-  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-  
-  The full GNU General Public License is included in this distribution in the
-  file called LICENSE.
-  
-  Contact Information:
-  James P. Ketrenos <ipw2100-admin@linux.intel.com>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
diff --git a/Documentation/networking/README.sb1000 b/Documentation/networking/README.sb1000
deleted file mode 100644 (file)
index f92c2aa..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-sb1000 is a module network device driver for the General Instrument (also known
-as NextLevel) SURFboard1000 internal cable modem board.  This is an ISA card
-which is used by a number of cable TV companies to provide cable modem access.
-It's a one-way downstream-only cable modem, meaning that your upstream net link
-is provided by your regular phone modem.
-
-This driver was written by Franco Venturi <fventuri@mediaone.net>.  He deserves
-a great deal of thanks for this wonderful piece of code!
-
------------------------------------------------------------------------------
-
-Support for this device is now a part of the standard Linux kernel.  The
-driver source code file is drivers/net/sb1000.c.  In addition to this
-you will need:
-
-1.) The "cmconfig" program.  This is a utility which supplements "ifconfig"
-to configure the cable modem and network interface (usually called "cm0");
-and
-
-2.) Several PPP scripts which live in /etc/ppp to make connecting via your
-cable modem easy.
-
-   These utilities can be obtained from:
-
-      http://www.jacksonville.net/~fventuri/
-
-   in Franco's original source code distribution .tar.gz file.  Support for
-   the sb1000 driver can be found at:
-
-      http://web.archive.org/web/*/http://home.adelphia.net/~siglercm/sb1000.html
-      http://web.archive.org/web/*/http://linuxpower.cx/~cable/
-
-   along with these utilities.
-
-3.) The standard isapnp tools.  These are necessary to configure your SB1000
-card at boot time (or afterwards by hand) since it's a PnP card.
-
-   If you don't have these installed as a standard part of your Linux
-   distribution, you can find them at:
-
-      http://www.roestock.demon.co.uk/isapnptools/
-
-   or check your Linux distribution binary CD or their web site.  For help with
-   isapnp, pnpdump, or /etc/isapnp.conf, go to:
-
-      http://www.roestock.demon.co.uk/isapnptools/isapnpfaq.html
-
------------------------------------------------------------------------------
-
-To make the SB1000 card work, follow these steps:
-
-1.) Run `make config', or `make menuconfig', or `make xconfig', whichever
-you prefer, in the top kernel tree directory to set up your kernel
-configuration.  Make sure to say "Y" to "Prompt for development drivers"
-and to say "M" to the sb1000 driver.  Also say "Y" or "M" to all the standard
-networking questions to get TCP/IP and PPP networking support.
-
-2.) *BEFORE* you build the kernel, edit drivers/net/sb1000.c.  Make sure
-to redefine the value of READ_DATA_PORT to match the I/O address used
-by isapnp to access your PnP cards.  This is the value of READPORT in
-/etc/isapnp.conf or given by the output of pnpdump.
-
-3.) Build and install the kernel and modules as usual.
-
-4.) Boot your new kernel following the usual procedures.
-
-5.) Set up to configure the new SB1000 PnP card by capturing the output
-of "pnpdump" to a file and editing this file to set the correct I/O ports,
-IRQ, and DMA settings for all your PnP cards.  Make sure none of the settings
-conflict with one another.  Then test this configuration by running the
-"isapnp" command with your new config file as the input.  Check for
-errors and fix as necessary.  (As an aside, I use I/O ports 0x110 and
-0x310 and IRQ 11 for my SB1000 card and these work well for me.  YMMV.)
-Then save the finished config file as /etc/isapnp.conf for proper configuration
-on subsequent reboots.
-
-6.) Download the original file sb1000-1.1.2.tar.gz from Franco's site or one of
-the others referenced above.  As root, unpack it into a temporary directory and
-do a `make cmconfig' and then `install -c cmconfig /usr/local/sbin'.  Don't do
-`make install' because it expects to find all the utilities built and ready for
-installation, not just cmconfig.
-
-7.) As root, copy all the files under the ppp/ subdirectory in Franco's
-tar file into /etc/ppp, being careful not to overwrite any files that are
-already in there.  Then modify ppp@gi-on to set the correct login name,
-phone number, and frequency for the cable modem.  Also edit pap-secrets
-to specify your login name and password and any site-specific information
-you need.
-
-8.) Be sure to modify /etc/ppp/firewall to use ipchains instead of
-the older ipfwadm commands from the 2.0.x kernels.  There's a neat utility to
-convert ipfwadm commands to ipchains commands:
-
-   http://users.dhp.com/~whisper/ipfwadm2ipchains/
-
-You may also wish to modify the firewall script to implement a different
-firewalling scheme.
-
-9.) Start the PPP connection via the script /etc/ppp/ppp@gi-on.  You must be
-root to do this.  It's better to use a utility like sudo to execute
-frequently used commands like this with root permissions if possible.  If you
-connect successfully the cable modem interface will come up and you'll see a
-driver message like this at the console:
-
-         cm0: sb1000 at (0x110,0x310), csn 1, S/N 0x2a0d16d8, IRQ 11.
-         sb1000.c:v1.1.2 6/01/98 (fventuri@mediaone.net)
-
-The "ifconfig" command should show two new interfaces, ppp0 and cm0.
-The command "cmconfig cm0" will give you information about the cable modem
-interface.
-
-10.) Try pinging a site via `ping -c 5 www.yahoo.com', for example.  You should
-see packets received.
-
-11.) If you can't get site names (like www.yahoo.com) to resolve into
-IP addresses (like 204.71.200.67), be sure your /etc/resolv.conf file
-has no syntax errors and has the right nameserver IP addresses in it.
-If this doesn't help, try something like `ping -c 5 204.71.200.67' to
-see if the networking is running but the DNS resolution is where the
-problem lies.
-
-12.) If you still have problems, go to the support web sites mentioned above
-and read the information and documentation there.
-
------------------------------------------------------------------------------
-
-Common problems:
-
-1.) Packets go out on the ppp0 interface but don't come back on the cm0
-interface.  It looks like I'm connected but I can't even ping any
-numerical IP addresses.  (This happens predominantly on Debian systems due
-to a default boot-time configuration script.)
-
-Solution -- As root `echo 0 > /proc/sys/net/ipv4/conf/cm0/rp_filter' so it
-can share the same IP address as the ppp0 interface.  Note that this
-command should probably be added to the /etc/ppp/cablemodem script
-*right*between* the "/sbin/ifconfig" and "/sbin/cmconfig" commands.
-You may need to do this to /proc/sys/net/ipv4/conf/ppp0/rp_filter as well.
-If you do this to /proc/sys/net/ipv4/conf/default/rp_filter on each reboot
-(in rc.local or some such) then any interfaces can share the same IP
-addresses.
-
-2.) I get "unresolved symbol" error messages on executing `insmod sb1000.o'.
-
-Solution -- You probably have a non-matching kernel source tree and
-/usr/include/linux and /usr/include/asm header files.  Make sure you
-install the correct versions of the header files in these two directories.
-Then rebuild and reinstall the kernel.
-
-3.) When isapnp runs it reports an error, and my SB1000 card isn't working.
-
-Solution -- There's a problem with later versions of isapnp using the "(CHECK)"
-option in the lines that allocate the two I/O addresses for the SB1000 card.
-This first popped up on RH 6.0.  Delete "(CHECK)" for the SB1000 I/O addresses.
-Make sure they don't conflict with any other pieces of hardware first!  Then
-rerun isapnp and go from there.
-
-4.) I can't execute the /etc/ppp/ppp@gi-on file.
-
-Solution -- As root do `chmod ug+x /etc/ppp/ppp@gi-on'.
-
-5.) The firewall script isn't working (with 2.2.x and higher kernels).
-
-Solution -- Use the ipfwadm2ipchains script referenced above to convert the
-/etc/ppp/firewall script from the deprecated ipfwadm commands to ipchains.
-
-6.) I'm getting *tons* of firewall deny messages in the /var/kern.log,
-/var/messages, and/or /var/syslog files, and they're filling up my /var
-partition!!!
-
-Solution -- First, tell your ISP that you're receiving DoS (Denial of Service)
-and/or portscanning (UDP connection attempts) attacks!  Look over the deny
-messages to figure out what the attack is and where it's coming from.  Next,
-edit /etc/ppp/cablemodem and make sure the ",nobroadcast" option is turned on
-to the "cmconfig" command (uncomment that line).  If you're not receiving these
-denied packets on your broadcast interface (IP address xxx.yyy.zzz.255
-typically), then someone is attacking your machine in particular.  Be careful
-out there....
-
-7.) Everything seems to work fine but my computer locks up after a while
-(and typically during a lengthy download through the cable modem)!
-
-Solution -- You may need to add a short delay in the driver to 'slow down' the
-SURFboard because your PC might not be able to keep up with the transfer rate
-of the SB1000. To do this, it's probably best to download Franco's
-sb1000-1.1.2.tar.gz archive and build and install sb1000.o manually.  You'll
-want to edit the 'Makefile' and look for the 'SB1000_DELAY'
-define.  Uncomment those 'CFLAGS' lines (and comment out the default ones)
-and try setting the delay to something like 60 microseconds with:
-'-DSB1000_DELAY=60'.  Then do `make' and as root `make install' and try
-it out.  If it still doesn't work or you like playing with the driver, you may
-try other numbers.  Remember though that the higher the delay, the slower the
-driver (which slows down the rest of the PC too when it is actively
-used). Thanks to Ed Daiga for this tip!
-
------------------------------------------------------------------------------
-
-Credits:  This README came from Franco Venturi's original README file which is
-still supplied with his driver .tar.gz archive.  I and all other sb1000 users
-owe Franco a tremendous "Thank you!"  Additional thanks goes to Carl Patten
-and Ralph Bonnell who are now managing the Linux SB1000 web site, and to
-the SB1000 users who reported and helped debug the common problems listed
-above.
-
-
-                                       Clemmitt Sigler
-                                       csigler@vt.edu
diff --git a/Documentation/networking/cs89x0.txt b/Documentation/networking/cs89x0.txt
deleted file mode 100644 (file)
index 0e19018..0000000
+++ /dev/null
@@ -1,624 +0,0 @@
-
-NOTE
-----
-
-This document was contributed by Cirrus Logic for kernel 2.2.5.  This version
-has been updated for 2.3.48 by Andrew Morton.
-
-Cirrus make a copy of this driver available at their website, as
-described below.  In general, you should use the driver version which
-comes with your Linux distribution.
-
-
-
-CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
-Linux Network Interface Driver ver. 2.00 <kernel 2.3.48>
-===============================================================================
-
-TABLE OF CONTENTS
-
-1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
-    1.1 Product Overview 
-    1.2 Driver Description
-       1.2.1 Driver Name
-       1.2.2 File in the Driver Package
-    1.3 System Requirements
-    1.4 Licensing Information
-
-2.0 ADAPTER INSTALLATION and CONFIGURATION
-    2.1 CS8900-based Adapter Configuration
-    2.2 CS8920-based Adapter Configuration 
-
-3.0 LOADING THE DRIVER AS A MODULE
-
-4.0 COMPILING THE DRIVER
-    4.1 Compiling the Driver as a Loadable Module
-    4.2 Compiling the driver to support memory mode
-    4.3 Compiling the driver to support Rx DMA 
-
-5.0 TESTING AND TROUBLESHOOTING
-    5.1 Known Defects and Limitations
-    5.2 Testing the Adapter
-        5.2.1 Diagnostic Self-Test
-        5.2.2 Diagnostic Network Test
-    5.3 Using the Adapter's LEDs
-    5.4 Resolving I/O Conflicts
-
-6.0 TECHNICAL SUPPORT
-    6.1 Contacting Cirrus Logic's Technical Support
-    6.2 Information Required Before Contacting Technical Support
-    6.3 Obtaining the Latest Driver Version
-    6.4 Current maintainer
-    6.5 Kernel boot parameters
-
-
-1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
-===============================================================================
-
-
-1.1 PRODUCT OVERVIEW
-
-The CS8900-based ISA Ethernet Adapters from Cirrus Logic follow 
-IEEE 802.3 standards and support half or full-duplex operation in ISA bus 
-computers on 10 Mbps Ethernet networks.  The adapters are designed for operation 
-in 16-bit ISA or EISA bus expansion slots and are available in 
-10BaseT-only or 3-media configurations (10BaseT, 10Base2, and AUI for 10Base-5 
-or fiber networks).  
-
-CS8920-based adapters are similar to the CS8900-based adapter with additional 
-features for Plug and Play (PnP) support and Wakeup Frame recognition.  As 
-such, the configuration procedures differ somewhat between the two types of 
-adapters.  Refer to the "Adapter Configuration" section for details on 
-configuring both types of adapters.
-
-
-1.2 DRIVER DESCRIPTION
-
-The CS8900/CS8920 Ethernet Adapter driver for Linux supports the Linux
-v2.3.48 or greater kernel.  It can be compiled directly into the kernel
-or loaded at run-time as a device driver module.
-
-1.2.1 Driver Name: cs89x0
-
-1.2.2 Files in the Driver Archive:
-
-The files in the driver at Cirrus' website include:
-
-  readme.txt         - this file
-  build              - batch file to compile cs89x0.c.
-  cs89x0.c           - driver C code
-  cs89x0.h           - driver header file
-  cs89x0.o           - pre-compiled module (for v2.2.5 kernel)
-  config/Config.in   - sample file to include cs89x0 driver in the kernel.
-  config/Makefile    - sample file to include cs89x0 driver in the kernel.
-  config/Space.c     - sample file to include cs89x0 driver in the kernel.
-
-
-
-1.3 SYSTEM REQUIREMENTS
-
-The following hardware is required:
-
-   * Cirrus Logic LAN (CS8900/20-based) Ethernet ISA Adapter   
-
-   * IBM or IBM-compatible PC with:
-     * An 80386 or higher processor
-     * 16 bytes of contiguous IO space available between 210h - 370h
-     * One available IRQ (5,10,11,or 12 for the CS8900, 3-7,9-15 for CS8920).
-
-   * Appropriate cable (and connector for AUI, 10BASE-2) for your network
-     topology.
-
-The following software is required:
-
-* LINUX kernel version 2.3.48 or higher
-
-   * CS8900/20 Setup Utility (DOS-based)
-
-   * LINUX kernel sources for your kernel (if compiling into kernel)
-
-   * GNU Toolkit (gcc and make) v2.6 or above (if compiling into kernel 
-     or a module)   
-
-
-
-1.4 LICENSING INFORMATION
-
-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, version 1.
-
-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.
-
-For a full copy of the GNU General Public License, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-
-
-2.0 ADAPTER INSTALLATION and CONFIGURATION
-===============================================================================
-
-Both the CS8900 and CS8920-based adapters can be configured using parameters 
-stored in an on-board EEPROM. You must use the DOS-based CS8900/20 Setup 
-Utility if you want to change the adapter's configuration in EEPROM.  
-
-When loading the driver as a module, you can specify many of the adapter's 
-configuration parameters on the command-line to override the EEPROM's settings 
-or for interface configuration when an EEPROM is not used. (CS8920-based 
-adapters must use an EEPROM.) See Section 3.0 LOADING THE DRIVER AS A MODULE.
-
-Since the CS8900/20 Setup Utility is a DOS-based application, you must install 
-and configure the adapter in a DOS-based system using the CS8900/20 Setup 
-Utility before installation in the target LINUX system.  (Not required if 
-installing a CS8900-based adapter and the default configuration is acceptable.)
-     
-
-2.1 CS8900-BASED ADAPTER CONFIGURATION
-
-CS8900-based adapters shipped from Cirrus Logic have been configured 
-with the following "default" settings:
-
-  Operation Mode:      Memory Mode
-  IRQ:                 10
-  Base I/O Address:    300
-  Memory Base Address: D0000
-  Optimization:               DOS Client
-  Transmission Mode:   Half-duplex
-  BootProm:            None
-  Media Type:         Autodetect (3-media cards) or 
-                       10BASE-T (10BASE-T only adapter)
-
-You should only change the default configuration settings if conflicts with 
-another adapter exists. To change the adapter's configuration, run the 
-CS8900/20 Setup Utility. 
-
-
-2.2 CS8920-BASED ADAPTER CONFIGURATION
-
-CS8920-based adapters are shipped from Cirrus Logic configured as Plug
-and Play (PnP) enabled.  However, since the cs89x0 driver does NOT
-support PnP, you must install the CS8920 adapter in a DOS-based PC and
-run the CS8900/20 Setup Utility to disable PnP and configure the
-adapter before installation in the target Linux system.  Failure to do
-this will leave the adapter inactive and the driver will be unable to
-communicate with the adapter.  
-
-
-        **************************************************************** 
-        *                    CS8920-BASED ADAPTERS:                    *
-        *                                                              * 
-        * CS8920-BASED ADAPTERS ARE PLUG and PLAY ENABLED BY DEFAULT.  * 
-        * THE CS89X0 DRIVER DOES NOT SUPPORT PnP. THEREFORE, YOU MUST  *
-        * RUN THE CS8900/20 SETUP UTILITY TO DISABLE PnP SUPPORT AND   *
-        * TO ACTIVATE THE ADAPTER.                                     *
-        ****************************************************************
-
-
-
-
-3.0 LOADING THE DRIVER AS A MODULE
-===============================================================================
-
-If the driver is compiled as a loadable module, you can load the driver module
-with the 'modprobe' command.  Many of the adapter's configuration parameters can 
-be specified as command-line arguments to the load command.  This facility 
-provides a means to override the EEPROM's settings or for interface 
-configuration when an EEPROM is not used.
-
-Example:
-
-    insmod cs89x0.o io=0x200 irq=0xA media=aui
-
-This example loads the module and configures the adapter to use an IO port base
-address of 200h, interrupt 10, and use the AUI media connection.  The following
-configuration options are available on the command line:
-
-* io=###               - specify IO address (200h-360h)
-* irq=##               - specify interrupt level
-* use_dma=1            - Enable DMA
-* dma=#                - specify dma channel (Driver is compiled to support
-                         Rx DMA only)
-* dmasize=# (16 or 64) - DMA size 16K or 64K.  Default value is set to 16.
-* media=rj45           - specify media type
-   or media=bnc
-   or media=aui
-   or media=auto
-* duplex=full          - specify forced half/full/autonegotiate duplex
-   or duplex=half
-   or duplex=auto
-* debug=#              - debug level (only available if the driver was compiled
-                         for debugging)
-
-NOTES:
-
-a) If an EEPROM is present, any specified command-line parameter
-   will override the corresponding configuration value stored in
-   EEPROM.
-
-b) The "io" parameter must be specified on the command-line.  
-
-c) The driver's hardware probe routine is designed to avoid
-   writing to I/O space until it knows that there is a cs89x0
-   card at the written addresses.  This could cause problems
-   with device probing.  To avoid this behaviour, add one
-   to the `io=' module parameter.  This doesn't actually change
-   the I/O address, but it is a flag to tell the driver
-   to partially initialise the hardware before trying to
-   identify the card.  This could be dangerous if you are
-   not sure that there is a cs89x0 card at the provided address.
-
-   For example, to scan for an adapter located at IO base 0x300,
-   specify an IO address of 0x301.  
-
-d) The "duplex=auto" parameter is only supported for the CS8920.
-
-e) The minimum command-line configuration required if an EEPROM is
-   not present is:
-
-   io 
-   irq 
-   media type (no autodetect)
-
-f) The following additional parameters are CS89XX defaults (values
-   used with no EEPROM or command-line argument).
-
-   * DMA Burst = enabled
-   * IOCHRDY Enabled = enabled
-   * UseSA = enabled
-   * CS8900 defaults to half-duplex if not specified on command-line
-   * CS8920 defaults to autoneg if not specified on command-line
-   * Use reset defaults for other config parameters
-   * dma_mode = 0
-
-g) You can use ifconfig to set the adapter's Ethernet address.
-
-h) Many Linux distributions use the 'modprobe' command to load
-   modules.  This program uses the '/etc/conf.modules' file to
-   determine configuration information which is passed to a driver
-   module when it is loaded.  All the configuration options which are
-   described above may be placed within /etc/conf.modules.
-
-   For example:
-
-   > cat /etc/conf.modules
-   ...
-   alias eth0 cs89x0
-   options cs89x0 io=0x0200 dma=5 use_dma=1
-   ...
-
-   In this example we are telling the module system that the
-   ethernet driver for this machine should use the cs89x0 driver.  We
-   are asking 'modprobe' to pass the 'io', 'dma' and 'use_dma'
-   arguments to the driver when it is loaded.
-
-i) Cirrus recommend that the cs89x0 use the ISA DMA channels 5, 6 or
-   7.  You will probably find that other DMA channels will not work.
-
-j) The cs89x0 supports DMA for receiving only.  DMA mode is
-   significantly more efficient.  Flooding a 400 MHz Celeron machine
-   with large ping packets consumes 82% of its CPU capacity in non-DMA
-   mode.  With DMA this is reduced to 45%.
-
-k) If your Linux kernel was compiled with inbuilt plug-and-play
-   support you will be able to find information about the cs89x0 card
-   with the command
-
-   cat /proc/isapnp
-
-l) If during DMA operation you find erratic behavior or network data
-   corruption you should use your PC's BIOS to slow the EISA bus clock.
-
-m) If the cs89x0 driver is compiled directly into the kernel
-   (non-modular) then its I/O address is automatically determined by
-   ISA bus probing.  The IRQ number, media options, etc are determined
-   from the card's EEPROM.
-
-n) If the cs89x0 driver is compiled directly into the kernel, DMA
-   mode may be selected by providing the kernel with a boot option
-   'cs89x0_dma=N' where 'N' is the desired DMA channel number (5, 6 or 7).
-
-   Kernel boot options may be provided on the LILO command line:
-
-       LILO boot: linux cs89x0_dma=5
-
-   or they may be placed in /etc/lilo.conf:
-
-       image=/boot/bzImage-2.3.48
-         append="cs89x0_dma=5"
-         label=linux
-         root=/dev/hda5
-         read-only
-
-   The DMA Rx buffer size is hardwired to 16 kbytes in this mode.
-   (64k mode is not available).
-
-
-4.0 COMPILING THE DRIVER
-===============================================================================
-
-The cs89x0 driver can be compiled directly into the kernel or compiled into
-a loadable device driver module.
-
-
-4.1 COMPILING THE DRIVER AS A LOADABLE MODULE
-
-To compile the driver into a loadable module, use the following command 
-(single command line, without quotes):
-
-"gcc -D__KERNEL__ -I/usr/src/linux/include -I/usr/src/linux/net/inet -Wall 
--Wstrict-prototypes -O2 -fomit-frame-pointer -DMODULE -DCONFIG_MODVERSIONS 
--c cs89x0.c"
-
-4.2 COMPILING THE DRIVER TO SUPPORT MEMORY MODE
-
-Support for memory mode was not carried over into the 2.3 series kernels.
-
-4.3 COMPILING THE DRIVER TO SUPPORT Rx DMA
-
-The compile-time optionality for DMA was removed in the 2.3 kernel
-series.  DMA support is now unconditionally part of the driver.  It is
-enabled by the 'use_dma=1' module option.
-
-
-5.0 TESTING AND TROUBLESHOOTING
-===============================================================================
-
-5.1 KNOWN DEFECTS and LIMITATIONS
-
-Refer to the RELEASE.TXT file distributed as part of this archive for a list of 
-known defects, driver limitations, and work arounds.
-
-
-5.2 TESTING THE ADAPTER
-
-Once the adapter has been installed and configured, the diagnostic option of 
-the CS8900/20 Setup Utility can be used to test the functionality of the 
-adapter and its network connection.  Use the diagnostics 'Self Test' option to
-test the functionality of the adapter with the hardware configuration you have
-assigned. You can use the diagnostics 'Network Test' to test the ability of the
-adapter to communicate across the Ethernet with another PC equipped with a 
-CS8900/20-based adapter card (it must also be running the CS8900/20 Setup 
-Utility).
-
-         NOTE: The Setup Utility's diagnostics are designed to run in a
-         DOS-only operating system environment.  DO NOT run the diagnostics 
-         from a DOS or command prompt session under Windows 95, Windows NT, 
-         OS/2, or other operating system.
-
-To run the diagnostics tests on the CS8900/20 adapter:
-
-   1.) Boot DOS on the PC and start the CS8900/20 Setup Utility.
-
-   2.) The adapter's current configuration is displayed.  Hit the ENTER key to
-       get to the main menu.
-
-   4.) Select 'Diagnostics' (ALT-G) from the main menu.  
-       * Select 'Self-Test' to test the adapter's basic functionality.
-       * Select 'Network Test' to test the network connection and cabling.
-
-
-5.2.1 DIAGNOSTIC SELF-TEST
-
-The diagnostic self-test checks the adapter's basic functionality as well as 
-its ability to communicate across the ISA bus based on the system resources 
-assigned during hardware configuration.  The following tests are performed:
-
-   * IO Register Read/Write Test
-     The IO Register Read/Write test insures that the CS8900/20 can be 
-     accessed in IO mode, and that the IO base address is correct.
-
-   * Shared Memory Test
-     The Shared Memory test insures the CS8900/20 can be accessed in memory 
-     mode and that the range of memory addresses assigned does not conflict 
-     with other devices in the system.
-
-   * Interrupt Test
-     The Interrupt test insures there are no conflicts with the assigned IRQ
-     signal.
-
-   * EEPROM Test
-     The EEPROM test insures the EEPROM can be read.
-
-   * Chip RAM Test
-     The Chip RAM test insures the 4K of memory internal to the CS8900/20 is
-     working properly.
-
-   * Internal Loop-back Test
-     The Internal Loop Back test insures the adapter's transmitter and 
-     receiver are operating properly.  If this test fails, make sure the 
-     adapter's cable is connected to the network (check for LED activity for 
-     example).
-
-   * Boot PROM Test
-     The Boot PROM  test insures the Boot PROM is present, and can be read.
-     Failure indicates the Boot PROM  was not successfully read due to a
-     hardware problem or due to a conflicts on the Boot PROM address
-     assignment. (Test only applies if the adapter is configured to use the
-     Boot PROM option.)
-
-Failure of a test item indicates a possible system resource conflict with 
-another device on the ISA bus.  In this case, you should use the Manual Setup 
-option to reconfigure the adapter by selecting a different value for the system
-resource that failed.
-
-
-5.2.2 DIAGNOSTIC NETWORK TEST
-
-The Diagnostic Network Test verifies a working network connection by 
-transferring data between two CS8900/20 adapters installed in different PCs 
-on the same network. (Note: the diagnostic network test should not be run 
-between two nodes across a router.) 
-
-This test requires that each of the two PCs have a CS8900/20-based adapter
-installed and have the CS8900/20 Setup Utility running.  The first PC is 
-configured as a Responder and the other PC is configured as an Initiator.  
-Once the Initiator is started, it sends data frames to the Responder which 
-returns the frames to the Initiator.
-
-The total number of frames received and transmitted are displayed on the 
-Initiator's display, along with a count of the number of frames received and 
-transmitted OK or in error.  The test can be terminated anytime by the user at 
-either PC.
-
-To setup the Diagnostic Network Test:
-
-    1.) Select a PC with a CS8900/20-based adapter and a known working network
-        connection to act as the Responder.  Run the CS8900/20 Setup Utility 
-        and select 'Diagnostics -> Network Test -> Responder' from the main 
-        menu.  Hit ENTER to start the Responder.
-
-    2.) Return to the PC with the CS8900/20-based adapter you want to test and
-        start the CS8900/20 Setup Utility. 
-
-    3.) From the main menu, Select 'Diagnostic -> Network Test -> Initiator'.
-        Hit ENTER to start the test.
-You may stop the test on the Initiator at any time while allowing the Responder
-to continue running.  In this manner, you can move to additional PCs and test 
-them by starting the Initiator on another PC without having to stop/start the 
-Responder.
-
-
-5.3 USING THE ADAPTER'S LEDs
-
-The 2 and 3-media adapters have two LEDs visible on the back end of the board 
-located near the 10Base-T connector.  
-
-Link Integrity LED: A "steady" ON of the green LED indicates a valid 10Base-T 
-connection.  (Only applies to 10Base-T.  The green LED has no significance for
-a 10Base-2 or AUI connection.)
-
-TX/RX LED: The yellow LED lights briefly each time the adapter transmits or 
-receives data. (The yellow LED will appear to "flicker" on a typical network.)
-
-
-5.4 RESOLVING I/O CONFLICTS
-
-An IO conflict occurs when two or more adapter use the same ISA resource (IO 
-address, memory address or IRQ).  You can usually detect an IO conflict in one 
-of four ways after installing and or configuring the CS8900/20-based adapter:
-
-    1.) The system does not boot properly (or at all).
-
-    2.) The driver cannot communicate with the adapter, reporting an "Adapter
-        not found" error message.
-
-    3.) You cannot connect to the network or the driver will not load.
-
-    4.) If you have configured the adapter to run in memory mode but the driver
-        reports it is using IO mode when loading, this is an indication of a
-        memory address conflict.
-
-If an IO conflict occurs, run the CS8900/20 Setup Utility and perform a 
-diagnostic self-test.  Normally, the ISA resource in conflict will fail the 
-self-test.  If so, reconfigure the adapter selecting another choice for the 
-resource in conflict.  Run the diagnostics again to check for further IO 
-conflicts.
-
-In some cases, such as when the PC will not boot, it may be necessary to remove
-the adapter and reconfigure it by installing it in another PC to run the 
-CS8900/20 Setup Utility.  Once reinstalled in the target system, run the 
-diagnostics self-test to ensure the new configuration is free of conflicts 
-before loading the driver again.
-
-When manually configuring the adapter, keep in mind the typical ISA system 
-resource usage as indicated in the tables below.
-
-I/O Address            Device                        IRQ      Device
------------            --------                      ---      --------
- 200-20F               Game I/O adapter               3       COM2, Bus Mouse
- 230-23F               Bus Mouse                      4       COM1
- 270-27F               LPT3: third parallel port      5       LPT2
- 2F0-2FF               COM2: second serial port       6       Floppy Disk controller
- 320-32F               Fixed disk controller          7       LPT1
-                                              8       Real-time Clock
-                                                 9       EGA/VGA display adapter    
-                                                12       Mouse (PS/2)                              
-Memory Address  Device                          13       Math Coprocessor
---------------  ---------------------           14       Hard Disk controller
-A000-BFFF      EGA Graphics Adapter
-A000-C7FF      VGA Graphics Adapter
-B000-BFFF      Mono Graphics Adapter
-B800-BFFF      Color Graphics Adapter
-E000-FFFF      AT BIOS
-
-
-
-
-6.0 TECHNICAL SUPPORT
-===============================================================================
-
-6.1 CONTACTING CIRRUS LOGIC'S TECHNICAL SUPPORT
-
-Cirrus Logic's CS89XX Technical Application Support can be reached at:
-
-Telephone  :(800) 888-5016 (from inside U.S. and Canada)
-           :(512) 442-7555 (from outside the U.S. and Canada)
-Fax        :(512) 912-3871
-Email      :ethernet@crystal.cirrus.com
-WWW        :http://www.cirrus.com
-
-
-6.2 INFORMATION REQUIRED BEFORE CONTACTING TECHNICAL SUPPORT
-
-Before contacting Cirrus Logic for technical support, be prepared to provide as 
-Much of the following information as possible. 
-
-1.) Adapter type (CRD8900, CDB8900, CDB8920, etc.)
-
-2.) Adapter configuration
-
-    * IO Base, Memory Base, IO or memory mode enabled, IRQ, DMA channel
-    * Plug and Play enabled/disabled (CS8920-based adapters only)
-    * Configured for media auto-detect or specific media type (which type).    
-
-3.) PC System's Configuration
-
-    * Plug and Play system (yes/no)
-    * BIOS (make and version)
-    * System make and model
-    * CPU (type and speed)
-    * System RAM
-    * SCSI Adapter
-
-4.) Software
-
-    * CS89XX driver and version
-    * Your network operating system and version
-    * Your system's OS version 
-    * Version of all protocol support files
-
-5.) Any Error Message displayed.
-
-
-
-6.3 OBTAINING THE LATEST DRIVER VERSION
-
-You can obtain the latest CS89XX drivers and support software from Cirrus Logic's 
-Web site.  You can also contact Cirrus Logic's Technical Support (email:
-ethernet@crystal.cirrus.com) and request that you be registered for automatic 
-software-update notification.
-
-Cirrus Logic maintains a web page at http://www.cirrus.com with the
-latest drivers and technical publications.
-
-
-6.4 Current maintainer
-
-In February 2000 the maintenance of this driver was assumed by Andrew
-Morton.
-
-6.5 Kernel module parameters
-
-For use in embedded environments with no cs89x0 EEPROM, the kernel boot
-parameter `cs89x0_media=' has been implemented.  Usage is:
-
-       cs89x0_media=rj45    or
-       cs89x0_media=aui     or
-       cs89x0_media=bnc
-
diff --git a/Documentation/networking/cxgb.txt b/Documentation/networking/cxgb.txt
deleted file mode 100644 (file)
index 20a8876..0000000
+++ /dev/null
@@ -1,352 +0,0 @@
-                 Chelsio N210 10Gb Ethernet Network Controller
-
-                         Driver Release Notes for Linux
-
-                                 Version 2.1.1
-
-                                 June 20, 2005
-
-CONTENTS
-========
- INTRODUCTION
- FEATURES
- PERFORMANCE
- DRIVER MESSAGES
- KNOWN ISSUES
- SUPPORT
-
-
-INTRODUCTION
-============
-
- This document describes the Linux driver for Chelsio 10Gb Ethernet Network
- Controller. This driver supports the Chelsio N210 NIC and is backward
- compatible with the Chelsio N110 model 10Gb NICs.
-
-
-FEATURES
-========
-
- Adaptive Interrupts (adaptive-rx)
- ---------------------------------
-
-  This feature provides an adaptive algorithm that adjusts the interrupt
-  coalescing parameters, allowing the driver to dynamically adapt the latency
-  settings to achieve the highest performance during various types of network
-  load.
-
-  The interface used to control this feature is ethtool. Please see the
-  ethtool manpage for additional usage information.
-
-  By default, adaptive-rx is disabled.
-  To enable adaptive-rx:
-
-      ethtool -C <interface> adaptive-rx on
-
-  To disable adaptive-rx, use ethtool:
-
-      ethtool -C <interface> adaptive-rx off
-
-  After disabling adaptive-rx, the timer latency value will be set to 50us.
-  You may set the timer latency after disabling adaptive-rx:
-
-      ethtool -C <interface> rx-usecs <microseconds>
-
-  An example to set the timer latency value to 100us on eth0:
-
-      ethtool -C eth0 rx-usecs 100
-
-  You may also provide a timer latency value while disabling adaptive-rx:
-
-      ethtool -C <interface> adaptive-rx off rx-usecs <microseconds>
-
-  If adaptive-rx is disabled and a timer latency value is specified, the timer
-  will be set to the specified value until changed by the user or until
-  adaptive-rx is enabled.
-
-  To view the status of the adaptive-rx and timer latency values:
-
-      ethtool -c <interface>
-
-
- TCP Segmentation Offloading (TSO) Support
- -----------------------------------------
-
-  This feature, also known as "large send", enables a system's protocol stack
-  to offload portions of outbound TCP processing to a network interface card
-  thereby reducing system CPU utilization and enhancing performance.
-
-  The interface used to control this feature is ethtool version 1.8 or higher.
-  Please see the ethtool manpage for additional usage information.
-
-  By default, TSO is enabled.
-  To disable TSO:
-
-      ethtool -K <interface> tso off
-
-  To enable TSO:
-
-      ethtool -K <interface> tso on
-
-  To view the status of TSO:
-
-      ethtool -k <interface>
-
-
-PERFORMANCE
-===========
-
- The following information is provided as an example of how to change system
- parameters for "performance tuning" an what value to use. You may or may not
- want to change these system parameters, depending on your server/workstation
- application. Doing so is not warranted in any way by Chelsio Communications,
- and is done at "YOUR OWN RISK". Chelsio will not be held responsible for loss
- of data or damage to equipment.
-
- Your distribution may have a different way of doing things, or you may prefer
- a different method. These commands are shown only to provide an example of
- what to do and are by no means definitive.
-
- Making any of the following system changes will only last until you reboot
- your system. You may want to write a script that runs at boot-up which
- includes the optimal settings for your system.
-
-  Setting PCI Latency Timer:
-      setpci -d 1425:* 0x0c.l=0x0000F800
-
-  Disabling TCP timestamp:
-      sysctl -w net.ipv4.tcp_timestamps=0
-
-  Disabling SACK:
-      sysctl -w net.ipv4.tcp_sack=0
-
-  Setting large number of incoming connection requests:
-      sysctl -w net.ipv4.tcp_max_syn_backlog=3000
-
-  Setting maximum receive socket buffer size:
-      sysctl -w net.core.rmem_max=1024000
-
-  Setting maximum send socket buffer size:
-      sysctl -w net.core.wmem_max=1024000
-
-  Set smp_affinity (on a multiprocessor system) to a single CPU:
-      echo 1 > /proc/irq/<interrupt_number>/smp_affinity
-
-  Setting default receive socket buffer size:
-      sysctl -w net.core.rmem_default=524287
-
-  Setting default send socket buffer size:
-      sysctl -w net.core.wmem_default=524287
-
-  Setting maximum option memory buffers:
-      sysctl -w net.core.optmem_max=524287
-
-  Setting maximum backlog (# of unprocessed packets before kernel drops):
-      sysctl -w net.core.netdev_max_backlog=300000
-
-  Setting TCP read buffers (min/default/max):
-      sysctl -w net.ipv4.tcp_rmem="10000000 10000000 10000000"
-
-  Setting TCP write buffers (min/pressure/max):
-      sysctl -w net.ipv4.tcp_wmem="10000000 10000000 10000000"
-
-  Setting TCP buffer space (min/pressure/max):
-      sysctl -w net.ipv4.tcp_mem="10000000 10000000 10000000"
-
-  TCP window size for single connections:
-   The receive buffer (RX_WINDOW) size must be at least as large as the
-   Bandwidth-Delay Product of the communication link between the sender and
-   receiver. Due to the variations of RTT, you may want to increase the buffer
-   size up to 2 times the Bandwidth-Delay Product. Reference page 289 of
-   "TCP/IP Illustrated, Volume 1, The Protocols" by W. Richard Stevens.
-   At 10Gb speeds, use the following formula:
-       RX_WINDOW >= 1.25MBytes * RTT(in milliseconds)
-       Example for RTT with 100us: RX_WINDOW = (1,250,000 * 0.1) = 125,000
-   RX_WINDOW sizes of 256KB - 512KB should be sufficient.
-   Setting the min, max, and default receive buffer (RX_WINDOW) size:
-       sysctl -w net.ipv4.tcp_rmem="<min> <default> <max>"
-
-  TCP window size for multiple connections:
-   The receive buffer (RX_WINDOW) size may be calculated the same as single
-   connections, but should be divided by the number of connections. The
-   smaller window prevents congestion and facilitates better pacing,
-   especially if/when MAC level flow control does not work well or when it is
-   not supported on the machine. Experimentation may be necessary to attain
-   the correct value. This method is provided as a starting point for the
-   correct receive buffer size.
-   Setting the min, max, and default receive buffer (RX_WINDOW) size is
-   performed in the same manner as single connection.
-
-
-DRIVER MESSAGES
-===============
-
- The following messages are the most common messages logged by syslog. These
- may be found in /var/log/messages.
-
-  Driver up:
-     Chelsio Network Driver - version 2.1.1
-
-  NIC detected:
-     eth#: Chelsio N210 1x10GBaseX NIC (rev #), PCIX 133MHz/64-bit
-
-  Link up:
-     eth#: link is up at 10 Gbps, full duplex
-
-  Link down:
-     eth#: link is down
-
-
-KNOWN ISSUES
-============
-
- These issues have been identified during testing. The following information
- is provided as a workaround to the problem. In some cases, this problem is
- inherent to Linux or to a particular Linux Distribution and/or hardware
- platform.
-
-  1. Large number of TCP retransmits on a multiprocessor (SMP) system.
-
-      On a system with multiple CPUs, the interrupt (IRQ) for the network
-      controller may be bound to more than one CPU. This will cause TCP
-      retransmits if the packet data were to be split across different CPUs
-      and re-assembled in a different order than expected.
-
-      To eliminate the TCP retransmits, set smp_affinity on the particular
-      interrupt to a single CPU. You can locate the interrupt (IRQ) used on
-      the N110/N210 by using ifconfig:
-          ifconfig <dev_name> | grep Interrupt
-      Set the smp_affinity to a single CPU:
-          echo 1 > /proc/irq/<interrupt_number>/smp_affinity
-
-      It is highly suggested that you do not run the irqbalance daemon on your
-      system, as this will change any smp_affinity setting you have applied.
-      The irqbalance daemon runs on a 10 second interval and binds interrupts
-      to the least loaded CPU determined by the daemon. To disable this daemon:
-          chkconfig --level 2345 irqbalance off
-
-      By default, some Linux distributions enable the kernel feature,
-      irqbalance, which performs the same function as the daemon. To disable
-      this feature, add the following line to your bootloader:
-          noirqbalance
-
-          Example using the Grub bootloader:
-              title Red Hat Enterprise Linux AS (2.4.21-27.ELsmp)
-              root (hd0,0)
-              kernel /vmlinuz-2.4.21-27.ELsmp ro root=/dev/hda3 noirqbalance
-              initrd /initrd-2.4.21-27.ELsmp.img
-
-  2. After running insmod, the driver is loaded and the incorrect network
-     interface is brought up without running ifup.
-
-      When using 2.4.x kernels, including RHEL kernels, the Linux kernel
-      invokes a script named "hotplug". This script is primarily used to
-      automatically bring up USB devices when they are plugged in, however,
-      the script also attempts to automatically bring up a network interface
-      after loading the kernel module. The hotplug script does this by scanning
-      the ifcfg-eth# config files in /etc/sysconfig/network-scripts, looking
-      for HWADDR=<mac_address>.
-
-      If the hotplug script does not find the HWADDRR within any of the
-      ifcfg-eth# files, it will bring up the device with the next available
-      interface name. If this interface is already configured for a different
-      network card, your new interface will have incorrect IP address and
-      network settings.
-
-      To solve this issue, you can add the HWADDR=<mac_address> key to the
-      interface config file of your network controller.
-
-      To disable this "hotplug" feature, you may add the driver (module name)
-      to the "blacklist" file located in /etc/hotplug. It has been noted that
-      this does not work for network devices because the net.agent script
-      does not use the blacklist file. Simply remove, or rename, the net.agent
-      script located in /etc/hotplug to disable this feature.
-
-  3. Transport Protocol (TP) hangs when running heavy multi-connection traffic
-     on an AMD Opteron system with HyperTransport PCI-X Tunnel chipset.
-
-      If your AMD Opteron system uses the AMD-8131 HyperTransport PCI-X Tunnel
-      chipset, you may experience the "133-Mhz Mode Split Completion Data
-      Corruption" bug identified by AMD while using a 133Mhz PCI-X card on the
-      bus PCI-X bus.
-
-      AMD states, "Under highly specific conditions, the AMD-8131 PCI-X Tunnel
-      can provide stale data via split completion cycles to a PCI-X card that
-      is operating at 133 Mhz", causing data corruption.
-
-      AMD's provides three workarounds for this problem, however, Chelsio
-      recommends the first option for best performance with this bug:
-
-        For 133Mhz secondary bus operation, limit the transaction length and
-        the number of outstanding transactions, via BIOS configuration
-        programming of the PCI-X card, to the following:
-
-           Data Length (bytes): 1k
-           Total allowed outstanding transactions: 2
-
-      Please refer to AMD 8131-HT/PCI-X Errata 26310 Rev 3.08 August 2004,
-      section 56, "133-MHz Mode Split Completion Data Corruption" for more
-      details with this bug and workarounds suggested by AMD.
-
-      It may be possible to work outside AMD's recommended PCI-X settings, try
-      increasing the Data Length to 2k bytes for increased performance. If you
-      have issues with these settings, please revert to the "safe" settings
-      and duplicate the problem before submitting a bug or asking for support.
-
-      NOTE: The default setting on most systems is 8 outstanding transactions
-            and 2k bytes data length.
-
-  4. On multiprocessor systems, it has been noted that an application which
-     is handling 10Gb networking can switch between CPUs causing degraded
-     and/or unstable performance.
-
-      If running on an SMP system and taking performance measurements, it
-      is suggested you either run the latest netperf-2.4.0+ or use a binding
-      tool such as Tim Hockin's procstate utilities (runon)
-      <http://www.hockin.org/~thockin/procstate/>.
-
-      Binding netserver and netperf (or other applications) to particular
-      CPUs will have a significant difference in performance measurements.
-      You may need to experiment which CPU to bind the application to in
-      order to achieve the best performance for your system.
-
-      If you are developing an application designed for 10Gb networking,
-      please keep in mind you may want to look at kernel functions
-      sched_setaffinity & sched_getaffinity to bind your application.
-
-      If you are just running user-space applications such as ftp, telnet,
-      etc., you may want to try the runon tool provided by Tim Hockin's
-      procstate utility. You could also try binding the interface to a
-      particular CPU: runon 0 ifup eth0
-
-
-SUPPORT
-=======
-
- If you have problems with the software or hardware, please contact our
- customer support team via email at support@chelsio.com or check our website
- at http://www.chelsio.com
-
-===============================================================================
-
- Chelsio Communications
- 370 San Aleso Ave.
- Suite 100
- Sunnyvale, CA 94085
- http://www.chelsio.com
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License, version 2, as
-published by the Free Software Foundation.
-
-You should have received a copy of the GNU General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
- Copyright (c) 2003-2005 Chelsio Communications. All rights reserved.
-
-===============================================================================
diff --git a/Documentation/networking/de4x5.txt b/Documentation/networking/de4x5.txt
deleted file mode 100644 (file)
index c8e4ca9..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-    Originally,   this  driver  was    written  for the  Digital   Equipment
-    Corporation series of EtherWORKS Ethernet cards:
-
-        DE425 TP/COAX EISA
-       DE434 TP PCI
-       DE435 TP/COAX/AUI PCI
-       DE450 TP/COAX/AUI PCI
-       DE500 10/100 PCI Fasternet
-
-    but it  will  now attempt  to  support all  cards which   conform to the
-    Digital Semiconductor   SROM   Specification.    The  driver   currently
-    recognises the following chips:
-
-        DC21040  (no SROM) 
-       DC21041[A]  
-       DC21140[A] 
-       DC21142 
-       DC21143 
-
-    So far the driver is known to work with the following cards:
-
-        KINGSTON
-       Linksys
-       ZNYX342
-       SMC8432
-       SMC9332 (w/new SROM)
-       ZNYX31[45]
-       ZNYX346 10/100 4 port (can act as a 10/100 bridge!) 
-
-    The driver has been tested on a relatively busy network using the DE425,
-    DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
-    16M of data to a DECstation 5000/200 as follows:
-
-                TCP           UDP
-             TX     RX     TX     RX
-    DE425   1030k  997k   1170k  1128k
-    DE434   1063k  995k   1170k  1125k
-    DE435   1063k  995k   1170k  1125k
-    DE500   1063k  998k   1170k  1125k  in 10Mb/s mode
-
-    All  values are typical (in   kBytes/sec) from a  sample  of 4 for  each
-    measurement. Their error is +/-20k on a quiet (private) network and also
-    depend on what load the CPU has.
-
-    =========================================================================
-
-    The ability to load this  driver as a loadable  module has been included
-    and used extensively  during the driver development  (to save those long
-    reboot sequences).  Loadable module support  under PCI and EISA has been
-    achieved by letting the driver autoprobe as if it were compiled into the
-    kernel. Do make sure  you're not sharing  interrupts with anything  that
-    cannot accommodate  interrupt  sharing!
-
-    To utilise this ability, you have to do 8 things:
-
-    0) have a copy of the loadable modules code installed on your system.
-    1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
-    temporary directory.
-    2) for fixed  autoprobes (not  recommended),  edit the source code  near
-    line 5594 to reflect the I/O address  you're using, or assign these when
-    loading by:
-
-                   insmod de4x5 io=0xghh           where g = bus number
-                                                       hh = device number   
-
-       NB: autoprobing for modules is now supported by default. You may just
-           use:
-
-                   insmod de4x5
-
-           to load all available boards. For a specific board, still use
-          the 'io=?' above.
-    3) compile  de4x5.c, but include -DMODULE in  the command line to ensure
-    that the correct bits are compiled (see end of source code).
-    4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a
-    kernel with the de4x5 configuration turned off and reboot.
-    5) insmod de4x5 [io=0xghh]
-    6) run the net startup bits for your new eth?? interface(s) manually 
-    (usually /etc/rc.inet[12] at boot time). 
-    7) enjoy!
-
-    To unload a module, turn off the associated interface(s) 
-    'ifconfig eth?? down' then 'rmmod de4x5'.
-
-    Automedia detection is included so that in  principle you can disconnect
-    from, e.g.  TP, reconnect  to BNC  and  things will still work  (after a
-    pause whilst the   driver figures out   where its media went).  My tests
-    using ping showed that it appears to work....
-
-    By  default,  the driver will  now   autodetect any  DECchip based card.
-    Should you have a need to restrict the driver to DIGITAL only cards, you
-    can compile with a  DEC_ONLY define, or if  loading as a module, use the
-    'dec_only=1'  parameter. 
-
-    I've changed the timing routines to  use the kernel timer and scheduling
-    functions  so that the  hangs  and other assorted problems that occurred
-    while autosensing the  media  should be gone.  A  bonus  for the DC21040
-    auto  media sense algorithm is  that it can now  use one that is more in
-    line with the  rest (the DC21040  chip doesn't  have a hardware  timer).
-    The downside is the 1 'jiffies' (10ms) resolution.
-
-    IEEE 802.3u MII interface code has  been added in anticipation that some
-    products may use it in the future.
-
-    The SMC9332 card  has a non-compliant SROM  which needs fixing -  I have
-    patched this  driver to detect it  because the SROM format used complies
-    to a previous DEC-STD format.
-
-    I have removed the buffer copies needed for receive on Intels.  I cannot
-    remove them for   Alphas since  the  Tulip hardware   only does longword
-    aligned  DMA transfers  and  the  Alphas get   alignment traps with  non
-    longword aligned data copies (which makes them really slow). No comment.
-
-    I  have added SROM decoding  routines to make this  driver work with any
-    card that  supports the Digital  Semiconductor SROM spec. This will help
-    all  cards running the dc2114x  series chips in particular.  Cards using
-    the dc2104x  chips should run correctly with  the basic  driver.  I'm in
-    debt to <mjacob@feral.com> for the  testing and feedback that helped get
-    this feature working.  So far we have  tested KINGSTON, SMC8432, SMC9332
-    (with the latest SROM complying  with the SROM spec  V3: their first was
-    broken), ZNYX342  and  LinkSys. ZNYX314 (dual  21041  MAC) and  ZNYX 315
-    (quad 21041 MAC)  cards also  appear  to work despite their  incorrectly
-    wired IRQs.
-
-    I have added a temporary fix for interrupt problems when some SCSI cards
-    share the same interrupt as the DECchip based  cards. The problem occurs
-    because  the SCSI card wants to  grab the interrupt  as a fast interrupt
-    (runs the   service routine with interrupts turned   off) vs.  this card
-    which really needs to run the service routine with interrupts turned on.
-    This driver will  now   add the interrupt service   routine  as  a  fast
-    interrupt if it   is bounced from the   slow interrupt.  THIS IS NOT   A
-    RECOMMENDED WAY TO RUN THE DRIVER  and has been done  for a limited time
-    until  people   sort  out their  compatibility    issues and the  kernel
-    interrupt  service code  is  fixed.   YOU  SHOULD SEPARATE OUT  THE FAST
-    INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
-    run on the same interrupt. PCMCIA/CardBus is another can of worms...
-
-    Finally, I think  I have really  fixed  the module  loading problem with
-    more than one DECchip based  card.  As a  side effect, I don't mess with
-    the  device structure any  more which means that  if more than 1 card in
-    2.0.x is    installed (4  in   2.1.x),  the  user   will have   to  edit
-    linux/drivers/net/Space.c  to make room for  them. Hence, module loading
-    is  the preferred way to use   this driver, since  it  doesn't have this
-    limitation.
-
-    Where SROM media  detection is used and  full duplex is specified in the
-    SROM,  the feature is  ignored unless  lp->params.fdx  is set at compile
-    time  OR during  a   module load  (insmod  de4x5   args='eth??:fdx' [see
-    below]).  This is because there  is no way  to automatically detect full
-    duplex   links  except through   autonegotiation.    When I  include the
-    autonegotiation feature in  the SROM autoconf  code, this detection will
-    occur automatically for that case.
-
-    Command line  arguments are  now allowed, similar to  passing  arguments
-    through LILO. This will allow a per adapter board set  up of full duplex
-    and media. The only lexical constraints are:  the board name (dev->name)
-    appears in  the list before its parameters.  The list of parameters ends
-    either at the end of the parameter list or with another board name.  The
-    following parameters are allowed:
-
-            fdx        for full duplex
-           autosense  to set the media/speed; with the following 
-                      sub-parameters:
-                      TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO
-
-    Case sensitivity is important  for  the sub-parameters. They *must*   be
-    upper case. Examples:
-
-        insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
-
-    For a compiled in driver, in linux/drivers/net/CONFIG, place e.g.
-       DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' 
-
-    Yes,  I know full duplex  isn't permissible on BNC  or AUI; they're just
-    examples. By default, full duplex is turned  off and AUTO is the default
-    autosense setting. In  reality, I expect only the  full duplex option to
-    be used. Note the use of single quotes in the two examples above and the
-    lack of commas to separate items.
diff --git a/Documentation/networking/device_drivers/3com/3c509.txt b/Documentation/networking/device_drivers/3com/3c509.txt
new file mode 100644 (file)
index 0000000..fbf722e
--- /dev/null
@@ -0,0 +1,213 @@
+Linux and the 3Com EtherLink III Series Ethercards (driver v1.18c and higher)
+----------------------------------------------------------------------------
+
+This file contains the instructions and caveats for v1.18c and higher versions
+of the 3c509 driver. You should not use the driver without reading this file.
+
+release 1.0
+28 February 2002
+Current maintainer (corrections to):
+  David Ruggiero <jdr@farfalle.com>
+
+----------------------------------------------------------------------------
+
+(0) Introduction
+
+The following are notes and information on using the 3Com EtherLink III series
+ethercards in Linux. These cards are commonly known by the most widely-used
+card's 3Com model number, 3c509. They are all 10mb/s ISA-bus cards and shouldn't
+be (but sometimes are) confused with the similarly-numbered PCI-bus "3c905"
+(aka "Vortex" or "Boomerang") series.  Kernel support for the 3c509 family is
+provided by the module 3c509.c, which has code to support all of the following
+models:
+
+  3c509 (original ISA card)
+  3c509B (later revision of the ISA card; supports full-duplex)
+  3c589 (PCMCIA)
+  3c589B (later revision of the 3c589; supports full-duplex)
+  3c579 (EISA)
+
+Large portions of this documentation were heavily borrowed from the guide
+written the original author of the 3c509 driver, Donald Becker. The master
+copy of that document, which contains notes on older versions of the driver,
+currently resides on Scyld web server: http://www.scyld.com/.
+
+
+(1) Special Driver Features
+
+Overriding card settings
+
+The driver allows boot- or load-time overriding of the card's detected IOADDR,
+IRQ, and transceiver settings, although this capability shouldn't generally be
+needed except to enable full-duplex mode (see below). An example of the syntax
+for LILO parameters for doing this:
+
+    ether=10,0x310,3,0x3c509,eth0 
+
+This configures the first found 3c509 card for IRQ 10, base I/O 0x310, and
+transceiver type 3 (10base2). The flag "0x3c509" must be set to avoid conflicts
+with other card types when overriding the I/O address. When the driver is
+loaded as a module, only the IRQ may be overridden. For example,
+setting two cards to IRQ10 and IRQ11 is done by using the irq module
+option:
+
+   options 3c509 irq=10,11
+
+
+(2) Full-duplex mode
+
+The v1.18c driver added support for the 3c509B's full-duplex capabilities.
+In order to enable and successfully use full-duplex mode, three conditions
+must be met: 
+
+(a) You must have a Etherlink III card model whose hardware supports full-
+duplex operations. Currently, the only members of the 3c509 family that are
+positively known to support full-duplex are the 3c509B (ISA bus) and 3c589B
+(PCMCIA) cards. Cards without the "B" model designation do *not* support
+full-duplex mode; these include the original 3c509 (no "B"), the original
+3c589, the 3c529 (MCA bus), and the 3c579 (EISA bus).
+
+(b) You must be using your card's 10baseT transceiver (i.e., the RJ-45
+connector), not its AUI (thick-net) or 10base2 (thin-net/coax) interfaces.
+AUI and 10base2 network cabling is physically incapable of full-duplex
+operation.
+
+(c) Most importantly, your 3c509B must be connected to a link partner that is
+itself full-duplex capable. This is almost certainly one of two things: a full-
+duplex-capable  Ethernet switch (*not* a hub), or a full-duplex-capable NIC on
+another system that's connected directly to the 3c509B via a crossover cable.
+
+Full-duplex mode can be enabled using 'ethtool'.
+/////Extremely important caution concerning full-duplex mode/////
+Understand that the 3c509B's hardware's full-duplex support is much more
+limited than that provide by more modern network interface cards. Although
+at the physical layer of the network it fully supports full-duplex operation,
+the card was designed before the current Ethernet auto-negotiation (N-way)
+spec was written. This means that the 3c509B family ***cannot and will not
+auto-negotiate a full-duplex connection with its link partner under any
+circumstances, no matter how it is initialized***. If the full-duplex mode
+of the 3c509B is enabled, its link partner will very likely need to be
+independently _forced_ into full-duplex mode as well; otherwise various nasty
+failures will occur - at the very least, you'll see massive numbers of packet
+collisions. This is one of very rare circumstances where disabling auto-
+negotiation and forcing the duplex mode of a network interface card or switch
+would ever be necessary or desirable.
+
+
+(3) Available Transceiver Types
+
+For versions of the driver v1.18c and above, the available transceiver types are:
+0  transceiver type from EEPROM config (normally 10baseT); force half-duplex
+1  AUI (thick-net / DB15 connector)
+2  (undefined)
+3  10base2 (thin-net == coax / BNC connector)
+4  10baseT (RJ-45 connector); force half-duplex mode
+8  transceiver type and duplex mode taken from card's EEPROM config settings
+12 10baseT (RJ-45 connector); force full-duplex mode
+
+Prior to driver version 1.18c, only transceiver codes 0-4 were supported. Note
+that the new transceiver codes 8 and 12 are the *only* ones that will enable
+full-duplex mode, no matter what the card's detected EEPROM settings might be.
+This insured that merely upgrading the driver from an earlier version would
+never automatically enable full-duplex mode in an existing installation;
+it must always be explicitly enabled via one of these code in order to be
+activated.
+
+The transceiver type can be changed using 'ethtool'.
+  
+
+(4a) Interpretation of error messages and common problems
+
+Error Messages
+
+eth0: Infinite loop in interrupt, status 2011. 
+These are "mostly harmless" message indicating that the driver had too much
+work during that interrupt cycle. With a status of 0x2011 you are receiving
+packets faster than they can be removed from the card. This should be rare
+or impossible in normal operation. Possible causes of this error report are:
+   - a "green" mode enabled that slows the processor down when there is no
+     keyboard activity. 
+
+   - some other device or device driver hogging the bus or disabling interrupts.
+     Check /proc/interrupts for excessive interrupt counts. The timer tick
+     interrupt should always be incrementing faster than the others. 
+
+No received packets 
+If a 3c509, 3c562 or 3c589 can successfully transmit packets, but never
+receives packets (as reported by /proc/net/dev or 'ifconfig') you likely
+have an interrupt line problem. Check /proc/interrupts to verify that the
+card is actually generating interrupts. If the interrupt count is not
+increasing you likely have a physical conflict with two devices trying to
+use the same ISA IRQ line. The common conflict is with a sound card on IRQ10
+or IRQ5, and the easiest solution is to move the 3c509 to a different
+interrupt line. If the device is receiving packets but 'ping' doesn't work,
+you have a routing problem.
+
+Tx Carrier Errors Reported in /proc/net/dev 
+If an EtherLink III appears to transmit packets, but the "Tx carrier errors"
+field in /proc/net/dev increments as quickly as the Tx packet count, you
+likely have an unterminated network or the incorrect media transceiver selected. 
+
+3c509B card is not detected on machines with an ISA PnP BIOS. 
+While the updated driver works with most PnP BIOS programs, it does not work
+with all. This can be fixed by disabling PnP support using the 3Com-supplied
+setup program. 
+
+3c509 card is not detected on overclocked machines 
+Increase the delay time in id_read_eeprom() from the current value, 500,
+to an absurdly high value, such as 5000. 
+
+
+(4b) Decoding Status and Error Messages
+
+The bits in the main status register are: 
+
+value  description
+0x01   Interrupt latch
+0x02   Tx overrun, or Rx underrun
+0x04   Tx complete
+0x08   Tx FIFO room available
+0x10   A complete Rx packet has arrived
+0x20   A Rx packet has started to arrive
+0x40   The driver has requested an interrupt
+0x80   Statistics counter nearly full
+
+The bits in the transmit (Tx) status word are: 
+
+value  description
+0x02   Out-of-window collision.
+0x04   Status stack overflow (normally impossible).
+0x08   16 collisions.
+0x10   Tx underrun (not enough PCI bus bandwidth).
+0x20   Tx jabber.
+0x40   Tx interrupt requested.
+0x80   Status is valid (this should always be set).
+
+
+When a transmit error occurs the driver produces a status message such as 
+
+   eth0: Transmit error, Tx status register 82
+
+The two values typically seen here are:
+
+0x82 
+Out of window collision. This typically occurs when some other Ethernet
+host is incorrectly set to full duplex on a half duplex network. 
+
+0x88 
+16 collisions. This typically occurs when the network is exceptionally busy
+or when another host doesn't correctly back off after a collision. If this
+error is mixed with 0x82 errors it is the result of a host incorrectly set
+to full duplex (see above).
+
+Both of these errors are the result of network problems that should be
+corrected. They do not represent driver malfunction.
+
+
+(5) Revision history (this file)
+
+28Feb02 v1.0  DR   New; major portions based on Becker original 3c509 docs
+
diff --git a/Documentation/networking/device_drivers/3com/vortex.txt b/Documentation/networking/device_drivers/3com/vortex.txt
new file mode 100644 (file)
index 0000000..587f3fc
--- /dev/null
@@ -0,0 +1,448 @@
+Documentation/networking/device_drivers/3com/vortex.txt
+Andrew Morton
+30 April 2000
+
+
+This document describes the usage and errata of the 3Com "Vortex" device
+driver for Linux, 3c59x.c.
+
+The driver was written by Donald Becker <becker@scyld.com>
+
+Don is no longer the prime maintainer of this version of the driver. 
+Please report problems to one or more of:
+
+  Andrew Morton
+  Netdev mailing list <netdev@vger.kernel.org>
+  Linux kernel mailing list <linux-kernel@vger.kernel.org>
+
+Please note the 'Reporting and Diagnosing Problems' section at the end
+of this file.
+
+
+Since kernel 2.3.99-pre6, this driver incorporates the support for the
+3c575-series Cardbus cards which used to be handled by 3c575_cb.c.
+
+This driver supports the following hardware:
+
+       3c590 Vortex 10Mbps
+       3c592 EISA 10Mbps Demon/Vortex
+       3c597 EISA Fast Demon/Vortex
+       3c595 Vortex 100baseTx
+       3c595 Vortex 100baseT4
+       3c595 Vortex 100base-MII
+       3c900 Boomerang 10baseT
+       3c900 Boomerang 10Mbps Combo
+       3c900 Cyclone 10Mbps TPO
+       3c900 Cyclone 10Mbps Combo
+       3c900 Cyclone 10Mbps TPC
+       3c900B-FL Cyclone 10base-FL
+       3c905 Boomerang 100baseTx
+       3c905 Boomerang 100baseT4
+       3c905B Cyclone 100baseTx
+       3c905B Cyclone 10/100/BNC
+       3c905B-FX Cyclone 100baseFx
+       3c905C Tornado
+       3c920B-EMB-WNM (ATI Radeon 9100 IGP)
+       3c980 Cyclone
+       3c980C Python-T
+       3cSOHO100-TX Hurricane
+       3c555 Laptop Hurricane
+       3c556 Laptop Tornado
+       3c556B Laptop Hurricane
+       3c575 [Megahertz] 10/100 LAN  CardBus
+       3c575 Boomerang CardBus
+       3CCFE575BT Cyclone CardBus
+       3CCFE575CT Tornado CardBus
+       3CCFE656 Cyclone CardBus
+       3CCFEM656B Cyclone+Winmodem CardBus
+       3CXFEM656C Tornado+Winmodem CardBus
+       3c450 HomePNA Tornado
+       3c920 Tornado
+       3c982 Hydra Dual Port A
+       3c982 Hydra Dual Port B
+       3c905B-T4
+       3c920B-EMB-WNM Tornado
+
+Module parameters
+=================
+
+There are several parameters which may be provided to the driver when
+its module is loaded.  These are usually placed in /etc/modprobe.d/*.conf
+configuration files.  Example:
+
+options 3c59x debug=3 rx_copybreak=300
+
+If you are using the PCMCIA tools (cardmgr) then the options may be
+placed in /etc/pcmcia/config.opts:
+
+module "3c59x" opts "debug=3 rx_copybreak=300"
+
+
+The supported parameters are:
+
+debug=N
+
+  Where N is a number from 0 to 7.  Anything above 3 produces a lot
+  of output in your system logs.  debug=1 is default.
+
+options=N1,N2,N3,...
+
+  Each number in the list provides an option to the corresponding
+  network card.  So if you have two 3c905's and you wish to provide
+  them with option 0x204 you would use:
+
+    options=0x204,0x204
+
+  The individual options are composed of a number of bitfields which
+  have the following meanings:
+
+  Possible media type settings
+       0       10baseT
+       1       10Mbs AUI
+       2       undefined
+       3       10base2 (BNC)
+       4       100base-TX
+       5       100base-FX
+       6       MII (Media Independent Interface)
+       7       Use default setting from EEPROM
+       8       Autonegotiate
+       9       External MII
+       10      Use default setting from EEPROM
+
+  When generating a value for the 'options' setting, the above media
+  selection values may be OR'ed (or added to) the following:
+
+  0x8000  Set driver debugging level to 7
+  0x4000  Set driver debugging level to 2
+  0x0400  Enable Wake-on-LAN
+  0x0200  Force full duplex mode.
+  0x0010  Bus-master enable bit (Old Vortex cards only)
+
+  For example:
+
+    insmod 3c59x options=0x204
+
+  will force full-duplex 100base-TX, rather than allowing the usual
+  autonegotiation.
+
+global_options=N
+
+  Sets the `options' parameter for all 3c59x NICs in the machine. 
+  Entries in the `options' array above will override any setting of
+  this.
+
+full_duplex=N1,N2,N3...
+
+  Similar to bit 9 of 'options'.  Forces the corresponding card into
+  full-duplex mode.  Please use this in preference to the `options'
+  parameter.
+
+  In fact, please don't use this at all! You're better off getting
+  autonegotiation working properly.
+
+global_full_duplex=N1
+
+  Sets full duplex mode for all 3c59x NICs in the machine.  Entries
+  in the `full_duplex' array above will override any setting of this.
+
+flow_ctrl=N1,N2,N3...
+
+  Use 802.3x MAC-layer flow control.  The 3com cards only support the
+  PAUSE command, which means that they will stop sending packets for a
+  short period if they receive a PAUSE frame from the link partner. 
+
+  The driver only allows flow control on a link which is operating in
+  full duplex mode.
+
+  This feature does not appear to work on the 3c905 - only 3c905B and
+  3c905C have been tested.
+
+  The 3com cards appear to only respond to PAUSE frames which are
+  sent to the reserved destination address of 01:80:c2:00:00:01.  They
+  do not honour PAUSE frames which are sent to the station MAC address.
+
+rx_copybreak=M
+
+  The driver preallocates 32 full-sized (1536 byte) network buffers
+  for receiving.  When a packet arrives, the driver has to decide
+  whether to leave the packet in its full-sized buffer, or to allocate
+  a smaller buffer and copy the packet across into it.
+
+  This is a speed/space tradeoff.
+
+  The value of rx_copybreak is used to decide when to make the copy. 
+  If the packet size is less than rx_copybreak, the packet is copied. 
+  The default value for rx_copybreak is 200 bytes.
+
+max_interrupt_work=N
+
+  The driver's interrupt service routine can handle many receive and
+  transmit packets in a single invocation.  It does this in a loop. 
+  The value of max_interrupt_work governs how many times the interrupt
+  service routine will loop.  The default value is 32 loops.  If this
+  is exceeded the interrupt service routine gives up and generates a
+  warning message "eth0: Too much work in interrupt".
+
+hw_checksums=N1,N2,N3,...
+
+  Recent 3com NICs are able to generate IPv4, TCP and UDP checksums
+  in hardware.  Linux has used the Rx checksumming for a long time. 
+  The "zero copy" patch which is planned for the 2.4 kernel series
+  allows you to make use of the NIC's DMA scatter/gather and transmit
+  checksumming as well.
+
+  The driver is set up so that, when the zerocopy patch is applied,
+  all Tornado and Cyclone devices will use S/G and Tx checksums.
+
+  This module parameter has been provided so you can override this
+  decision.  If you think that Tx checksums are causing a problem, you
+  may disable the feature with `hw_checksums=0'.
+
+  If you think your NIC should be performing Tx checksumming and the
+  driver isn't enabling it, you can force the use of hardware Tx
+  checksumming with `hw_checksums=1'.
+
+  The driver drops a message in the logfiles to indicate whether or
+  not it is using hardware scatter/gather and hardware Tx checksums.
+
+  Scatter/gather and hardware checksums provide considerable
+  performance improvement for the sendfile() system call, but a small
+  decrease in throughput for send().  There is no effect upon receive
+  efficiency.
+
+compaq_ioaddr=N
+compaq_irq=N
+compaq_device_id=N
+
+  "Variables to work-around the Compaq PCI BIOS32 problem"....
+
+watchdog=N
+
+  Sets the time duration (in milliseconds) after which the kernel
+  decides that the transmitter has become stuck and needs to be reset. 
+  This is mainly for debugging purposes, although it may be advantageous
+  to increase this value on LANs which have very high collision rates.
+  The default value is 5000 (5.0 seconds).
+
+enable_wol=N1,N2,N3,...
+
+  Enable Wake-on-LAN support for the relevant interface.  Donald
+  Becker's `ether-wake' application may be used to wake suspended
+  machines.
+
+  Also enables the NIC's power management support.
+
+global_enable_wol=N
+
+  Sets enable_wol mode for all 3c59x NICs in the machine.  Entries in
+  the `enable_wol' array above will override any setting of this.
+
+Media selection
+---------------
+
+A number of the older NICs such as the 3c590 and 3c900 series have
+10base2 and AUI interfaces.
+
+Prior to January, 2001 this driver would autoeselect the 10base2 or AUI
+port if it didn't detect activity on the 10baseT port.  It would then
+get stuck on the 10base2 port and a driver reload was necessary to
+switch back to 10baseT.  This behaviour could not be prevented with a
+module option override.
+
+Later (current) versions of the driver _do_ support locking of the
+media type.  So if you load the driver module with
+
+       modprobe 3c59x options=0
+
+it will permanently select the 10baseT port.  Automatic selection of
+other media types does not occur.
+
+
+Transmit error, Tx status register 82
+-------------------------------------
+
+This is a common error which is almost always caused by another host on
+the same network being in full-duplex mode, while this host is in
+half-duplex mode.  You need to find that other host and make it run in
+half-duplex mode or fix this host to run in full-duplex mode.
+
+As a last resort, you can force the 3c59x driver into full-duplex mode
+with
+
+       options 3c59x full_duplex=1
+
+but this has to be viewed as a workaround for broken network gear and
+should only really be used for equipment which cannot autonegotiate.
+
+
+Additional resources
+--------------------
+
+Details of the device driver implementation are at the top of the source file.
+
+Additional documentation is available at Don Becker's Linux Drivers site:
+
+     http://www.scyld.com/vortex.html
+
+Donald Becker's driver development site:
+
+     http://www.scyld.com/network.html
+
+Donald's vortex-diag program is useful for inspecting the NIC's state:
+
+     http://www.scyld.com/ethercard_diag.html
+
+Donald's mii-diag program may be used for inspecting and manipulating
+the NIC's Media Independent Interface subsystem:
+
+     http://www.scyld.com/ethercard_diag.html#mii-diag
+
+Donald's wake-on-LAN page:
+
+     http://www.scyld.com/wakeonlan.html
+
+3Com's DOS-based application for setting up the NICs EEPROMs:
+
+       ftp://ftp.3com.com/pub/nic/3c90x/3c90xx2.exe
+
+
+Autonegotiation notes
+---------------------
+
+  The driver uses a one-minute heartbeat for adapting to changes in
+  the external LAN environment if link is up and 5 seconds if link is down.
+  This means that when, for example, a machine is unplugged from a hubbed
+  10baseT LAN plugged into a  switched 100baseT LAN, the throughput
+  will be quite dreadful for up to sixty seconds.  Be patient.
+
+  Cisco interoperability note from Walter Wong <wcw+@CMU.EDU>:
+
+  On a side note, adding HAS_NWAY seems to share a problem with the
+  Cisco 6509 switch.  Specifically, you need to change the spanning
+  tree parameter for the port the machine is plugged into to 'portfast'
+  mode.  Otherwise, the negotiation fails.  This has been an issue
+  we've noticed for a while but haven't had the time to track down.
+
+  Cisco switches    (Jeff Busch <jbusch@deja.com>)
+
+    My "standard config" for ports to which PC's/servers connect directly:
+
+        interface FastEthernet0/N
+        description machinename
+        load-interval 30
+        spanning-tree portfast
+
+    If autonegotiation is a problem, you may need to specify "speed
+    100" and "duplex full" as well (or "speed 10" and "duplex half").
+
+    WARNING: DO NOT hook up hubs/switches/bridges to these
+    specially-configured ports! The switch will become very confused.
+
+
+Reporting and diagnosing problems
+---------------------------------
+
+Maintainers find that accurate and complete problem reports are
+invaluable in resolving driver problems.  We are frequently not able to
+reproduce problems and must rely on your patience and efforts to get to
+the bottom of the problem.
+
+If you believe you have a driver problem here are some of the
+steps you should take:
+
+- Is it really a driver problem?
+
+   Eliminate some variables: try different cards, different
+   computers, different cables, different ports on the switch/hub,
+   different versions of the kernel or of the driver, etc.
+
+- OK, it's a driver problem.
+
+   You need to generate a report.  Typically this is an email to the
+   maintainer and/or netdev@vger.kernel.org.  The maintainer's
+   email address will be in the driver source or in the MAINTAINERS file.
+
+- The contents of your report will vary a lot depending upon the
+  problem.  If it's a kernel crash then you should refer to the
+  admin-guide/reporting-bugs.rst file.
+
+  But for most problems it is useful to provide the following:
+
+   o Kernel version, driver version
+
+   o A copy of the banner message which the driver generates when
+     it is initialised.  For example:
+
+     eth0: 3Com PCI 3c905C Tornado at 0xa400,  00:50:da:6a:88:f0, IRQ 19
+     8K byte-wide RAM 5:3 Rx:Tx split, autoselect/Autonegotiate interface.
+     MII transceiver found at address 24, status 782d.
+     Enabling bus-master transmits and whole-frame receives.
+
+     NOTE: You must provide the `debug=2' modprobe option to generate
+     a full detection message.  Please do this:
+
+       modprobe 3c59x debug=2
+
+   o If it is a PCI device, the relevant output from 'lspci -vx', eg:
+
+     00:09.0 Ethernet controller: 3Com Corporation 3c905C-TX [Fast Etherlink] (rev 74)
+             Subsystem: 3Com Corporation: Unknown device 9200
+             Flags: bus master, medium devsel, latency 32, IRQ 19
+             I/O ports at a400 [size=128]
+             Memory at db000000 (32-bit, non-prefetchable) [size=128]
+             Expansion ROM at <unassigned> [disabled] [size=128K]
+             Capabilities: [dc] Power Management version 2
+     00: b7 10 00 92 07 00 10 02 74 00 00 02 08 20 00 00
+     10: 01 a4 00 00 00 00 00 db 00 00 00 00 00 00 00 00
+     20: 00 00 00 00 00 00 00 00 00 00 00 00 b7 10 00 10
+     30: 00 00 00 00 dc 00 00 00 00 00 00 00 05 01 0a 0a
+
+   o A description of the environment: 10baseT? 100baseT?
+     full/half duplex? switched or hubbed?
+
+   o Any additional module parameters which you may be providing to the driver.
+
+   o Any kernel logs which are produced.  The more the merrier. 
+     If this is a large file and you are sending your report to a
+     mailing list, mention that you have the logfile, but don't send
+     it.  If you're reporting direct to the maintainer then just send
+     it.
+
+     To ensure that all kernel logs are available, add the
+     following line to /etc/syslog.conf:
+
+         kern.* /var/log/messages
+
+     Then restart syslogd with:
+
+         /etc/rc.d/init.d/syslog restart
+
+     (The above may vary, depending upon which Linux distribution you use).
+
+    o If your problem is reproducible then that's great.  Try the
+      following:
+
+      1) Increase the debug level.  Usually this is done via:
+
+         a) modprobe driver debug=7
+         b) In /etc/modprobe.d/driver.conf:
+            options driver debug=7
+
+      2) Recreate the problem with the higher debug level,
+         send all logs to the maintainer.
+
+      3) Download you card's diagnostic tool from Donald
+         Becker's website <http://www.scyld.com/ethercard_diag.html>.
+         Download mii-diag.c as well.  Build these.
+
+         a) Run 'vortex-diag -aaee' and 'mii-diag -v' when the card is
+            working correctly.  Save the output.
+
+         b) Run the above commands when the card is malfunctioning.  Send
+            both sets of output.
+
+Finally, please be patient and be prepared to do some work.  You may
+end up working on this problem for a week or more as the maintainer
+asks more questions, asks for more tests, asks for patches to be
+applied, etc.  At the end of it all, the problem may even remain
+unresolved.
diff --git a/Documentation/networking/device_drivers/amazon/ena.txt b/Documentation/networking/device_drivers/amazon/ena.txt
new file mode 100644 (file)
index 0000000..2b4b6f5
--- /dev/null
@@ -0,0 +1,305 @@
+Linux kernel driver for Elastic Network Adapter (ENA) family:
+=============================================================
+
+Overview:
+=========
+ENA is a networking interface designed to make good use of modern CPU
+features and system architectures.
+
+The ENA device exposes a lightweight management interface with a
+minimal set of memory mapped registers and extendable command set
+through an Admin Queue.
+
+The driver supports a range of ENA devices, is link-speed independent
+(i.e., the same driver is used for 10GbE, 25GbE, 40GbE, etc.), and has
+a negotiated and extendable feature set.
+
+Some ENA devices support SR-IOV. This driver is used for both the
+SR-IOV Physical Function (PF) and Virtual Function (VF) devices.
+
+ENA devices enable high speed and low overhead network traffic
+processing by providing multiple Tx/Rx queue pairs (the maximum number
+is advertised by the device via the Admin Queue), a dedicated MSI-X
+interrupt vector per Tx/Rx queue pair, adaptive interrupt moderation,
+and CPU cacheline optimized data placement.
+
+The ENA driver supports industry standard TCP/IP offload features such
+as checksum offload and TCP transmit segmentation offload (TSO).
+Receive-side scaling (RSS) is supported for multi-core scaling.
+
+The ENA driver and its corresponding devices implement health
+monitoring mechanisms such as watchdog, enabling the device and driver
+to recover in a manner transparent to the application, as well as
+debug logs.
+
+Some of the ENA devices support a working mode called Low-latency
+Queue (LLQ), which saves several more microseconds.
+
+Supported PCI vendor ID/device IDs:
+===================================
+1d0f:0ec2 - ENA PF
+1d0f:1ec2 - ENA PF with LLQ support
+1d0f:ec20 - ENA VF
+1d0f:ec21 - ENA VF with LLQ support
+
+ENA Source Code Directory Structure:
+====================================
+ena_com.[ch]      - Management communication layer. This layer is
+                    responsible for the handling all the management
+                    (admin) communication between the device and the
+                    driver.
+ena_eth_com.[ch]  - Tx/Rx data path.
+ena_admin_defs.h  - Definition of ENA management interface.
+ena_eth_io_defs.h - Definition of ENA data path interface.
+ena_common_defs.h - Common definitions for ena_com layer.
+ena_regs_defs.h   - Definition of ENA PCI memory-mapped (MMIO) registers.
+ena_netdev.[ch]   - Main Linux kernel driver.
+ena_syfsfs.[ch]   - Sysfs files.
+ena_ethtool.c     - ethtool callbacks.
+ena_pci_id_tbl.h  - Supported device IDs.
+
+Management Interface:
+=====================
+ENA management interface is exposed by means of:
+- PCIe Configuration Space
+- Device Registers
+- Admin Queue (AQ) and Admin Completion Queue (ACQ)
+- Asynchronous Event Notification Queue (AENQ)
+
+ENA device MMIO Registers are accessed only during driver
+initialization and are not involved in further normal device
+operation.
+
+AQ is used for submitting management commands, and the
+results/responses are reported asynchronously through ACQ.
+
+ENA introduces a very small set of management commands with room for
+vendor-specific extensions. Most of the management operations are
+framed in a generic Get/Set feature command.
+
+The following admin queue commands are supported:
+- Create I/O submission queue
+- Create I/O completion queue
+- Destroy I/O submission queue
+- Destroy I/O completion queue
+- Get feature
+- Set feature
+- Configure AENQ
+- Get statistics
+
+Refer to ena_admin_defs.h for the list of supported Get/Set Feature
+properties.
+
+The Asynchronous Event Notification Queue (AENQ) is a uni-directional
+queue used by the ENA device to send to the driver events that cannot
+be reported using ACQ. AENQ events are subdivided into groups. Each
+group may have multiple syndromes, as shown below
+
+The events are:
+       Group                   Syndrome
+       Link state change       - X -
+       Fatal error             - X -
+       Notification            Suspend traffic
+       Notification            Resume traffic
+       Keep-Alive              - X -
+
+ACQ and AENQ share the same MSI-X vector.
+
+Keep-Alive is a special mechanism that allows monitoring of the
+device's health. The driver maintains a watchdog (WD) handler which,
+if fired, logs the current state and statistics then resets and
+restarts the ENA device and driver. A Keep-Alive event is delivered by
+the device every second. The driver re-arms the WD upon reception of a
+Keep-Alive event. A missed Keep-Alive event causes the WD handler to
+fire.
+
+Data Path Interface:
+====================
+I/O operations are based on Tx and Rx Submission Queues (Tx SQ and Rx
+SQ correspondingly). Each SQ has a completion queue (CQ) associated
+with it.
+
+The SQs and CQs are implemented as descriptor rings in contiguous
+physical memory.
+
+The ENA driver supports two Queue Operation modes for Tx SQs:
+- Regular mode
+  * In this mode the Tx SQs reside in the host's memory. The ENA
+    device fetches the ENA Tx descriptors and packet data from host
+    memory.
+- Low Latency Queue (LLQ) mode or "push-mode".
+  * In this mode the driver pushes the transmit descriptors and the
+    first 128 bytes of the packet directly to the ENA device memory
+    space. The rest of the packet payload is fetched by the
+    device. For this operation mode, the driver uses a dedicated PCI
+    device memory BAR, which is mapped with write-combine capability.
+
+The Rx SQs support only the regular mode.
+
+Note: Not all ENA devices support LLQ, and this feature is negotiated
+      with the device upon initialization. If the ENA device does not
+      support LLQ mode, the driver falls back to the regular mode.
+
+The driver supports multi-queue for both Tx and Rx. This has various
+benefits:
+- Reduced CPU/thread/process contention on a given Ethernet interface.
+- Cache miss rate on completion is reduced, particularly for data
+  cache lines that hold the sk_buff structures.
+- Increased process-level parallelism when handling received packets.
+- Increased data cache hit rate, by steering kernel processing of
+  packets to the CPU, where the application thread consuming the
+  packet is running.
+- In hardware interrupt re-direction.
+
+Interrupt Modes:
+================
+The driver assigns a single MSI-X vector per queue pair (for both Tx
+and Rx directions). The driver assigns an additional dedicated MSI-X vector
+for management (for ACQ and AENQ).
+
+Management interrupt registration is performed when the Linux kernel
+probes the adapter, and it is de-registered when the adapter is
+removed. I/O queue interrupt registration is performed when the Linux
+interface of the adapter is opened, and it is de-registered when the
+interface is closed.
+
+The management interrupt is named:
+   ena-mgmnt@pci:<PCI domain:bus:slot.function>
+and for each queue pair, an interrupt is named:
+   <interface name>-Tx-Rx-<queue index>
+
+The ENA device operates in auto-mask and auto-clear interrupt
+modes. That is, once MSI-X is delivered to the host, its Cause bit is
+automatically cleared and the interrupt is masked. The interrupt is
+unmasked by the driver after NAPI processing is complete.
+
+Interrupt Moderation:
+=====================
+ENA driver and device can operate in conventional or adaptive interrupt
+moderation mode.
+
+In conventional mode the driver instructs device to postpone interrupt
+posting according to static interrupt delay value. The interrupt delay
+value can be configured through ethtool(8). The following ethtool
+parameters are supported by the driver: tx-usecs, rx-usecs
+
+In adaptive interrupt moderation mode the interrupt delay value is
+updated by the driver dynamically and adjusted every NAPI cycle
+according to the traffic nature.
+
+By default ENA driver applies adaptive coalescing on Rx traffic and
+conventional coalescing on Tx traffic.
+
+Adaptive coalescing can be switched on/off through ethtool(8)
+adaptive_rx on|off parameter.
+
+The driver chooses interrupt delay value according to the number of
+bytes and packets received between interrupt unmasking and interrupt
+posting. The driver uses interrupt delay table that subdivides the
+range of received bytes/packets into 5 levels and assigns interrupt
+delay value to each level.
+
+The user can enable/disable adaptive moderation, modify the interrupt
+delay table and restore its default values through sysfs.
+
+The rx_copybreak is initialized by default to ENA_DEFAULT_RX_COPYBREAK
+and can be configured by the ETHTOOL_STUNABLE command of the
+SIOCETHTOOL ioctl.
+
+SKB:
+The driver-allocated SKB for frames received from Rx handling using
+NAPI context. The allocation method depends on the size of the packet.
+If the frame length is larger than rx_copybreak, napi_get_frags()
+is used, otherwise netdev_alloc_skb_ip_align() is used, the buffer
+content is copied (by CPU) to the SKB, and the buffer is recycled.
+
+Statistics:
+===========
+The user can obtain ENA device and driver statistics using ethtool.
+The driver can collect regular or extended statistics (including
+per-queue stats) from the device.
+
+In addition the driver logs the stats to syslog upon device reset.
+
+MTU:
+====
+The driver supports an arbitrarily large MTU with a maximum that is
+negotiated with the device. The driver configures MTU using the
+SetFeature command (ENA_ADMIN_MTU property). The user can change MTU
+via ip(8) and similar legacy tools.
+
+Stateless Offloads:
+===================
+The ENA driver supports:
+- TSO over IPv4/IPv6
+- TSO with ECN
+- IPv4 header checksum offload
+- TCP/UDP over IPv4/IPv6 checksum offloads
+
+RSS:
+====
+- The ENA device supports RSS that allows flexible Rx traffic
+  steering.
+- Toeplitz and CRC32 hash functions are supported.
+- Different combinations of L2/L3/L4 fields can be configured as
+  inputs for hash functions.
+- The driver configures RSS settings using the AQ SetFeature command
+  (ENA_ADMIN_RSS_HASH_FUNCTION, ENA_ADMIN_RSS_HASH_INPUT and
+  ENA_ADMIN_RSS_REDIRECTION_TABLE_CONFIG properties).
+- If the NETIF_F_RXHASH flag is set, the 32-bit result of the hash
+  function delivered in the Rx CQ descriptor is set in the received
+  SKB.
+- The user can provide a hash key, hash function, and configure the
+  indirection table through ethtool(8).
+
+DATA PATH:
+==========
+Tx:
+---
+end_start_xmit() is called by the stack. This function does the following:
+- Maps data buffers (skb->data and frags).
+- Populates ena_buf for the push buffer (if the driver and device are
+  in push mode.)
+- Prepares ENA bufs for the remaining frags.
+- Allocates a new request ID from the empty req_id ring. The request
+  ID is the index of the packet in the Tx info. This is used for
+  out-of-order TX completions.
+- Adds the packet to the proper place in the Tx ring.
+- Calls ena_com_prepare_tx(), an ENA communication layer that converts
+  the ena_bufs to ENA descriptors (and adds meta ENA descriptors as
+  needed.)
+  * This function also copies the ENA descriptors and the push buffer
+    to the Device memory space (if in push mode.)
+- Writes doorbell to the ENA device.
+- When the ENA device finishes sending the packet, a completion
+  interrupt is raised.
+- The interrupt handler schedules NAPI.
+- The ena_clean_tx_irq() function is called. This function handles the
+  completion descriptors generated by the ENA, with a single
+  completion descriptor per completed packet.
+  * req_id is retrieved from the completion descriptor. The tx_info of
+    the packet is retrieved via the req_id. The data buffers are
+    unmapped and req_id is returned to the empty req_id ring.
+  * The function stops when the completion descriptors are completed or
+    the budget is reached.
+
+Rx:
+---
+- When a packet is received from the ENA device.
+- The interrupt handler schedules NAPI.
+- The ena_clean_rx_irq() function is called. This function calls
+  ena_rx_pkt(), an ENA communication layer function, which returns the
+  number of descriptors used for a new unhandled packet, and zero if
+  no new packet is found.
+- Then it calls the ena_clean_rx_irq() function.
+- ena_eth_rx_skb() checks packet length:
+  * If the packet is small (len < rx_copybreak), the driver allocates
+    a SKB for the new packet, and copies the packet payload into the
+    SKB data buffer.
+    - In this way the original data buffer is not passed to the stack
+      and is reused for future Rx packets.
+  * Otherwise the function unmaps the Rx buffer, then allocates the
+    new SKB structure and hooks the Rx buffer to the SKB frags.
+- The new SKB is updated with the necessary information (protocol,
+  checksum hw verify result, etc.), and then passed to the network
+  stack, using the NAPI interface function napi_gro_receive().
diff --git a/Documentation/networking/device_drivers/chelsio/cxgb.txt b/Documentation/networking/device_drivers/chelsio/cxgb.txt
new file mode 100644 (file)
index 0000000..20a8876
--- /dev/null
@@ -0,0 +1,352 @@
+                 Chelsio N210 10Gb Ethernet Network Controller
+
+                         Driver Release Notes for Linux
+
+                                 Version 2.1.1
+
+                                 June 20, 2005
+
+CONTENTS
+========
+ INTRODUCTION
+ FEATURES
+ PERFORMANCE
+ DRIVER MESSAGES
+ KNOWN ISSUES
+ SUPPORT
+
+
+INTRODUCTION
+============
+
+ This document describes the Linux driver for Chelsio 10Gb Ethernet Network
+ Controller. This driver supports the Chelsio N210 NIC and is backward
+ compatible with the Chelsio N110 model 10Gb NICs.
+
+
+FEATURES
+========
+
+ Adaptive Interrupts (adaptive-rx)
+ ---------------------------------
+
+  This feature provides an adaptive algorithm that adjusts the interrupt
+  coalescing parameters, allowing the driver to dynamically adapt the latency
+  settings to achieve the highest performance during various types of network
+  load.
+
+  The interface used to control this feature is ethtool. Please see the
+  ethtool manpage for additional usage information.
+
+  By default, adaptive-rx is disabled.
+  To enable adaptive-rx:
+
+      ethtool -C <interface> adaptive-rx on
+
+  To disable adaptive-rx, use ethtool:
+
+      ethtool -C <interface> adaptive-rx off
+
+  After disabling adaptive-rx, the timer latency value will be set to 50us.
+  You may set the timer latency after disabling adaptive-rx:
+
+      ethtool -C <interface> rx-usecs <microseconds>
+
+  An example to set the timer latency value to 100us on eth0:
+
+      ethtool -C eth0 rx-usecs 100
+
+  You may also provide a timer latency value while disabling adaptive-rx:
+
+      ethtool -C <interface> adaptive-rx off rx-usecs <microseconds>
+
+  If adaptive-rx is disabled and a timer latency value is specified, the timer
+  will be set to the specified value until changed by the user or until
+  adaptive-rx is enabled.
+
+  To view the status of the adaptive-rx and timer latency values:
+
+      ethtool -c <interface>
+
+
+ TCP Segmentation Offloading (TSO) Support
+ -----------------------------------------
+
+  This feature, also known as "large send", enables a system's protocol stack
+  to offload portions of outbound TCP processing to a network interface card
+  thereby reducing system CPU utilization and enhancing performance.
+
+  The interface used to control this feature is ethtool version 1.8 or higher.
+  Please see the ethtool manpage for additional usage information.
+
+  By default, TSO is enabled.
+  To disable TSO:
+
+      ethtool -K <interface> tso off
+
+  To enable TSO:
+
+      ethtool -K <interface> tso on
+
+  To view the status of TSO:
+
+      ethtool -k <interface>
+
+
+PERFORMANCE
+===========
+
+ The following information is provided as an example of how to change system
+ parameters for "performance tuning" an what value to use. You may or may not
+ want to change these system parameters, depending on your server/workstation
+ application. Doing so is not warranted in any way by Chelsio Communications,
+ and is done at "YOUR OWN RISK". Chelsio will not be held responsible for loss
+ of data or damage to equipment.
+
+ Your distribution may have a different way of doing things, or you may prefer
+ a different method. These commands are shown only to provide an example of
+ what to do and are by no means definitive.
+
+ Making any of the following system changes will only last until you reboot
+ your system. You may want to write a script that runs at boot-up which
+ includes the optimal settings for your system.
+
+  Setting PCI Latency Timer:
+      setpci -d 1425:* 0x0c.l=0x0000F800
+
+  Disabling TCP timestamp:
+      sysctl -w net.ipv4.tcp_timestamps=0
+
+  Disabling SACK:
+      sysctl -w net.ipv4.tcp_sack=0
+
+  Setting large number of incoming connection requests:
+      sysctl -w net.ipv4.tcp_max_syn_backlog=3000
+
+  Setting maximum receive socket buffer size:
+      sysctl -w net.core.rmem_max=1024000
+
+  Setting maximum send socket buffer size:
+      sysctl -w net.core.wmem_max=1024000
+
+  Set smp_affinity (on a multiprocessor system) to a single CPU:
+      echo 1 > /proc/irq/<interrupt_number>/smp_affinity
+
+  Setting default receive socket buffer size:
+      sysctl -w net.core.rmem_default=524287
+
+  Setting default send socket buffer size:
+      sysctl -w net.core.wmem_default=524287
+
+  Setting maximum option memory buffers:
+      sysctl -w net.core.optmem_max=524287
+
+  Setting maximum backlog (# of unprocessed packets before kernel drops):
+      sysctl -w net.core.netdev_max_backlog=300000
+
+  Setting TCP read buffers (min/default/max):
+      sysctl -w net.ipv4.tcp_rmem="10000000 10000000 10000000"
+
+  Setting TCP write buffers (min/pressure/max):
+      sysctl -w net.ipv4.tcp_wmem="10000000 10000000 10000000"
+
+  Setting TCP buffer space (min/pressure/max):
+      sysctl -w net.ipv4.tcp_mem="10000000 10000000 10000000"
+
+  TCP window size for single connections:
+   The receive buffer (RX_WINDOW) size must be at least as large as the
+   Bandwidth-Delay Product of the communication link between the sender and
+   receiver. Due to the variations of RTT, you may want to increase the buffer
+   size up to 2 times the Bandwidth-Delay Product. Reference page 289 of
+   "TCP/IP Illustrated, Volume 1, The Protocols" by W. Richard Stevens.
+   At 10Gb speeds, use the following formula:
+       RX_WINDOW >= 1.25MBytes * RTT(in milliseconds)
+       Example for RTT with 100us: RX_WINDOW = (1,250,000 * 0.1) = 125,000
+   RX_WINDOW sizes of 256KB - 512KB should be sufficient.
+   Setting the min, max, and default receive buffer (RX_WINDOW) size:
+       sysctl -w net.ipv4.tcp_rmem="<min> <default> <max>"
+
+  TCP window size for multiple connections:
+   The receive buffer (RX_WINDOW) size may be calculated the same as single
+   connections, but should be divided by the number of connections. The
+   smaller window prevents congestion and facilitates better pacing,
+   especially if/when MAC level flow control does not work well or when it is
+   not supported on the machine. Experimentation may be necessary to attain
+   the correct value. This method is provided as a starting point for the
+   correct receive buffer size.
+   Setting the min, max, and default receive buffer (RX_WINDOW) size is
+   performed in the same manner as single connection.
+
+
+DRIVER MESSAGES
+===============
+
+ The following messages are the most common messages logged by syslog. These
+ may be found in /var/log/messages.
+
+  Driver up:
+     Chelsio Network Driver - version 2.1.1
+
+  NIC detected:
+     eth#: Chelsio N210 1x10GBaseX NIC (rev #), PCIX 133MHz/64-bit
+
+  Link up:
+     eth#: link is up at 10 Gbps, full duplex
+
+  Link down:
+     eth#: link is down
+
+
+KNOWN ISSUES
+============
+
+ These issues have been identified during testing. The following information
+ is provided as a workaround to the problem. In some cases, this problem is
+ inherent to Linux or to a particular Linux Distribution and/or hardware
+ platform.
+
+  1. Large number of TCP retransmits on a multiprocessor (SMP) system.
+
+      On a system with multiple CPUs, the interrupt (IRQ) for the network
+      controller may be bound to more than one CPU. This will cause TCP
+      retransmits if the packet data were to be split across different CPUs
+      and re-assembled in a different order than expected.
+
+      To eliminate the TCP retransmits, set smp_affinity on the particular
+      interrupt to a single CPU. You can locate the interrupt (IRQ) used on
+      the N110/N210 by using ifconfig:
+          ifconfig <dev_name> | grep Interrupt
+      Set the smp_affinity to a single CPU:
+          echo 1 > /proc/irq/<interrupt_number>/smp_affinity
+
+      It is highly suggested that you do not run the irqbalance daemon on your
+      system, as this will change any smp_affinity setting you have applied.
+      The irqbalance daemon runs on a 10 second interval and binds interrupts
+      to the least loaded CPU determined by the daemon. To disable this daemon:
+          chkconfig --level 2345 irqbalance off
+
+      By default, some Linux distributions enable the kernel feature,
+      irqbalance, which performs the same function as the daemon. To disable
+      this feature, add the following line to your bootloader:
+          noirqbalance
+
+          Example using the Grub bootloader:
+              title Red Hat Enterprise Linux AS (2.4.21-27.ELsmp)
+              root (hd0,0)
+              kernel /vmlinuz-2.4.21-27.ELsmp ro root=/dev/hda3 noirqbalance
+              initrd /initrd-2.4.21-27.ELsmp.img
+
+  2. After running insmod, the driver is loaded and the incorrect network
+     interface is brought up without running ifup.
+
+      When using 2.4.x kernels, including RHEL kernels, the Linux kernel
+      invokes a script named "hotplug". This script is primarily used to
+      automatically bring up USB devices when they are plugged in, however,
+      the script also attempts to automatically bring up a network interface
+      after loading the kernel module. The hotplug script does this by scanning
+      the ifcfg-eth# config files in /etc/sysconfig/network-scripts, looking
+      for HWADDR=<mac_address>.
+
+      If the hotplug script does not find the HWADDRR within any of the
+      ifcfg-eth# files, it will bring up the device with the next available
+      interface name. If this interface is already configured for a different
+      network card, your new interface will have incorrect IP address and
+      network settings.
+
+      To solve this issue, you can add the HWADDR=<mac_address> key to the
+      interface config file of your network controller.
+
+      To disable this "hotplug" feature, you may add the driver (module name)
+      to the "blacklist" file located in /etc/hotplug. It has been noted that
+      this does not work for network devices because the net.agent script
+      does not use the blacklist file. Simply remove, or rename, the net.agent
+      script located in /etc/hotplug to disable this feature.
+
+  3. Transport Protocol (TP) hangs when running heavy multi-connection traffic
+     on an AMD Opteron system with HyperTransport PCI-X Tunnel chipset.
+
+      If your AMD Opteron system uses the AMD-8131 HyperTransport PCI-X Tunnel
+      chipset, you may experience the "133-Mhz Mode Split Completion Data
+      Corruption" bug identified by AMD while using a 133Mhz PCI-X card on the
+      bus PCI-X bus.
+
+      AMD states, "Under highly specific conditions, the AMD-8131 PCI-X Tunnel
+      can provide stale data via split completion cycles to a PCI-X card that
+      is operating at 133 Mhz", causing data corruption.
+
+      AMD's provides three workarounds for this problem, however, Chelsio
+      recommends the first option for best performance with this bug:
+
+        For 133Mhz secondary bus operation, limit the transaction length and
+        the number of outstanding transactions, via BIOS configuration
+        programming of the PCI-X card, to the following:
+
+           Data Length (bytes): 1k
+           Total allowed outstanding transactions: 2
+
+      Please refer to AMD 8131-HT/PCI-X Errata 26310 Rev 3.08 August 2004,
+      section 56, "133-MHz Mode Split Completion Data Corruption" for more
+      details with this bug and workarounds suggested by AMD.
+
+      It may be possible to work outside AMD's recommended PCI-X settings, try
+      increasing the Data Length to 2k bytes for increased performance. If you
+      have issues with these settings, please revert to the "safe" settings
+      and duplicate the problem before submitting a bug or asking for support.
+
+      NOTE: The default setting on most systems is 8 outstanding transactions
+            and 2k bytes data length.
+
+  4. On multiprocessor systems, it has been noted that an application which
+     is handling 10Gb networking can switch between CPUs causing degraded
+     and/or unstable performance.
+
+      If running on an SMP system and taking performance measurements, it
+      is suggested you either run the latest netperf-2.4.0+ or use a binding
+      tool such as Tim Hockin's procstate utilities (runon)
+      <http://www.hockin.org/~thockin/procstate/>.
+
+      Binding netserver and netperf (or other applications) to particular
+      CPUs will have a significant difference in performance measurements.
+      You may need to experiment which CPU to bind the application to in
+      order to achieve the best performance for your system.
+
+      If you are developing an application designed for 10Gb networking,
+      please keep in mind you may want to look at kernel functions
+      sched_setaffinity & sched_getaffinity to bind your application.
+
+      If you are just running user-space applications such as ftp, telnet,
+      etc., you may want to try the runon tool provided by Tim Hockin's
+      procstate utility. You could also try binding the interface to a
+      particular CPU: runon 0 ifup eth0
+
+
+SUPPORT
+=======
+
+ If you have problems with the software or hardware, please contact our
+ customer support team via email at support@chelsio.com or check our website
+ at http://www.chelsio.com
+
+===============================================================================
+
+ Chelsio Communications
+ 370 San Aleso Ave.
+ Suite 100
+ Sunnyvale, CA 94085
+ http://www.chelsio.com
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License, version 2, as
+published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ Copyright (c) 2003-2005 Chelsio Communications. All rights reserved.
+
+===============================================================================
diff --git a/Documentation/networking/device_drivers/cirrus/cs89x0.txt b/Documentation/networking/device_drivers/cirrus/cs89x0.txt
new file mode 100644 (file)
index 0000000..0e19018
--- /dev/null
@@ -0,0 +1,624 @@
+
+NOTE
+----
+
+This document was contributed by Cirrus Logic for kernel 2.2.5.  This version
+has been updated for 2.3.48 by Andrew Morton.
+
+Cirrus make a copy of this driver available at their website, as
+described below.  In general, you should use the driver version which
+comes with your Linux distribution.
+
+
+
+CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
+Linux Network Interface Driver ver. 2.00 <kernel 2.3.48>
+===============================================================================
+
+TABLE OF CONTENTS
+
+1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
+    1.1 Product Overview 
+    1.2 Driver Description
+       1.2.1 Driver Name
+       1.2.2 File in the Driver Package
+    1.3 System Requirements
+    1.4 Licensing Information
+
+2.0 ADAPTER INSTALLATION and CONFIGURATION
+    2.1 CS8900-based Adapter Configuration
+    2.2 CS8920-based Adapter Configuration 
+
+3.0 LOADING THE DRIVER AS A MODULE
+
+4.0 COMPILING THE DRIVER
+    4.1 Compiling the Driver as a Loadable Module
+    4.2 Compiling the driver to support memory mode
+    4.3 Compiling the driver to support Rx DMA 
+
+5.0 TESTING AND TROUBLESHOOTING
+    5.1 Known Defects and Limitations
+    5.2 Testing the Adapter
+        5.2.1 Diagnostic Self-Test
+        5.2.2 Diagnostic Network Test
+    5.3 Using the Adapter's LEDs
+    5.4 Resolving I/O Conflicts
+
+6.0 TECHNICAL SUPPORT
+    6.1 Contacting Cirrus Logic's Technical Support
+    6.2 Information Required Before Contacting Technical Support
+    6.3 Obtaining the Latest Driver Version
+    6.4 Current maintainer
+    6.5 Kernel boot parameters
+
+
+1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
+===============================================================================
+
+
+1.1 PRODUCT OVERVIEW
+
+The CS8900-based ISA Ethernet Adapters from Cirrus Logic follow 
+IEEE 802.3 standards and support half or full-duplex operation in ISA bus 
+computers on 10 Mbps Ethernet networks.  The adapters are designed for operation 
+in 16-bit ISA or EISA bus expansion slots and are available in 
+10BaseT-only or 3-media configurations (10BaseT, 10Base2, and AUI for 10Base-5 
+or fiber networks).  
+
+CS8920-based adapters are similar to the CS8900-based adapter with additional 
+features for Plug and Play (PnP) support and Wakeup Frame recognition.  As 
+such, the configuration procedures differ somewhat between the two types of 
+adapters.  Refer to the "Adapter Configuration" section for details on 
+configuring both types of adapters.
+
+
+1.2 DRIVER DESCRIPTION
+
+The CS8900/CS8920 Ethernet Adapter driver for Linux supports the Linux
+v2.3.48 or greater kernel.  It can be compiled directly into the kernel
+or loaded at run-time as a device driver module.
+
+1.2.1 Driver Name: cs89x0
+
+1.2.2 Files in the Driver Archive:
+
+The files in the driver at Cirrus' website include:
+
+  readme.txt         - this file
+  build              - batch file to compile cs89x0.c.
+  cs89x0.c           - driver C code
+  cs89x0.h           - driver header file
+  cs89x0.o           - pre-compiled module (for v2.2.5 kernel)
+  config/Config.in   - sample file to include cs89x0 driver in the kernel.
+  config/Makefile    - sample file to include cs89x0 driver in the kernel.
+  config/Space.c     - sample file to include cs89x0 driver in the kernel.
+
+
+
+1.3 SYSTEM REQUIREMENTS
+
+The following hardware is required:
+
+   * Cirrus Logic LAN (CS8900/20-based) Ethernet ISA Adapter   
+
+   * IBM or IBM-compatible PC with:
+     * An 80386 or higher processor
+     * 16 bytes of contiguous IO space available between 210h - 370h
+     * One available IRQ (5,10,11,or 12 for the CS8900, 3-7,9-15 for CS8920).
+
+   * Appropriate cable (and connector for AUI, 10BASE-2) for your network
+     topology.
+
+The following software is required:
+
+* LINUX kernel version 2.3.48 or higher
+
+   * CS8900/20 Setup Utility (DOS-based)
+
+   * LINUX kernel sources for your kernel (if compiling into kernel)
+
+   * GNU Toolkit (gcc and make) v2.6 or above (if compiling into kernel 
+     or a module)   
+
+
+
+1.4 LICENSING INFORMATION
+
+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, version 1.
+
+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.
+
+For a full copy of the GNU General Public License, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+
+2.0 ADAPTER INSTALLATION and CONFIGURATION
+===============================================================================
+
+Both the CS8900 and CS8920-based adapters can be configured using parameters 
+stored in an on-board EEPROM. You must use the DOS-based CS8900/20 Setup 
+Utility if you want to change the adapter's configuration in EEPROM.  
+
+When loading the driver as a module, you can specify many of the adapter's 
+configuration parameters on the command-line to override the EEPROM's settings 
+or for interface configuration when an EEPROM is not used. (CS8920-based 
+adapters must use an EEPROM.) See Section 3.0 LOADING THE DRIVER AS A MODULE.
+
+Since the CS8900/20 Setup Utility is a DOS-based application, you must install 
+and configure the adapter in a DOS-based system using the CS8900/20 Setup 
+Utility before installation in the target LINUX system.  (Not required if 
+installing a CS8900-based adapter and the default configuration is acceptable.)
+     
+
+2.1 CS8900-BASED ADAPTER CONFIGURATION
+
+CS8900-based adapters shipped from Cirrus Logic have been configured 
+with the following "default" settings:
+
+  Operation Mode:      Memory Mode
+  IRQ:                 10
+  Base I/O Address:    300
+  Memory Base Address: D0000
+  Optimization:               DOS Client
+  Transmission Mode:   Half-duplex
+  BootProm:            None
+  Media Type:         Autodetect (3-media cards) or 
+                       10BASE-T (10BASE-T only adapter)
+
+You should only change the default configuration settings if conflicts with 
+another adapter exists. To change the adapter's configuration, run the 
+CS8900/20 Setup Utility. 
+
+
+2.2 CS8920-BASED ADAPTER CONFIGURATION
+
+CS8920-based adapters are shipped from Cirrus Logic configured as Plug
+and Play (PnP) enabled.  However, since the cs89x0 driver does NOT
+support PnP, you must install the CS8920 adapter in a DOS-based PC and
+run the CS8900/20 Setup Utility to disable PnP and configure the
+adapter before installation in the target Linux system.  Failure to do
+this will leave the adapter inactive and the driver will be unable to
+communicate with the adapter.  
+
+
+        **************************************************************** 
+        *                    CS8920-BASED ADAPTERS:                    *
+        *                                                              * 
+        * CS8920-BASED ADAPTERS ARE PLUG and PLAY ENABLED BY DEFAULT.  * 
+        * THE CS89X0 DRIVER DOES NOT SUPPORT PnP. THEREFORE, YOU MUST  *
+        * RUN THE CS8900/20 SETUP UTILITY TO DISABLE PnP SUPPORT AND   *
+        * TO ACTIVATE THE ADAPTER.                                     *
+        ****************************************************************
+
+
+
+
+3.0 LOADING THE DRIVER AS A MODULE
+===============================================================================
+
+If the driver is compiled as a loadable module, you can load the driver module
+with the 'modprobe' command.  Many of the adapter's configuration parameters can 
+be specified as command-line arguments to the load command.  This facility 
+provides a means to override the EEPROM's settings or for interface 
+configuration when an EEPROM is not used.
+
+Example:
+
+    insmod cs89x0.o io=0x200 irq=0xA media=aui
+
+This example loads the module and configures the adapter to use an IO port base
+address of 200h, interrupt 10, and use the AUI media connection.  The following
+configuration options are available on the command line:
+
+* io=###               - specify IO address (200h-360h)
+* irq=##               - specify interrupt level
+* use_dma=1            - Enable DMA
+* dma=#                - specify dma channel (Driver is compiled to support
+                         Rx DMA only)
+* dmasize=# (16 or 64) - DMA size 16K or 64K.  Default value is set to 16.
+* media=rj45           - specify media type
+   or media=bnc
+   or media=aui
+   or media=auto
+* duplex=full          - specify forced half/full/autonegotiate duplex
+   or duplex=half
+   or duplex=auto
+* debug=#              - debug level (only available if the driver was compiled
+                         for debugging)
+
+NOTES:
+
+a) If an EEPROM is present, any specified command-line parameter
+   will override the corresponding configuration value stored in
+   EEPROM.
+
+b) The "io" parameter must be specified on the command-line.  
+
+c) The driver's hardware probe routine is designed to avoid
+   writing to I/O space until it knows that there is a cs89x0
+   card at the written addresses.  This could cause problems
+   with device probing.  To avoid this behaviour, add one
+   to the `io=' module parameter.  This doesn't actually change
+   the I/O address, but it is a flag to tell the driver
+   to partially initialise the hardware before trying to
+   identify the card.  This could be dangerous if you are
+   not sure that there is a cs89x0 card at the provided address.
+
+   For example, to scan for an adapter located at IO base 0x300,
+   specify an IO address of 0x301.  
+
+d) The "duplex=auto" parameter is only supported for the CS8920.
+
+e) The minimum command-line configuration required if an EEPROM is
+   not present is:
+
+   io 
+   irq 
+   media type (no autodetect)
+
+f) The following additional parameters are CS89XX defaults (values
+   used with no EEPROM or command-line argument).
+
+   * DMA Burst = enabled
+   * IOCHRDY Enabled = enabled
+   * UseSA = enabled
+   * CS8900 defaults to half-duplex if not specified on command-line
+   * CS8920 defaults to autoneg if not specified on command-line
+   * Use reset defaults for other config parameters
+   * dma_mode = 0
+
+g) You can use ifconfig to set the adapter's Ethernet address.
+
+h) Many Linux distributions use the 'modprobe' command to load
+   modules.  This program uses the '/etc/conf.modules' file to
+   determine configuration information which is passed to a driver
+   module when it is loaded.  All the configuration options which are
+   described above may be placed within /etc/conf.modules.
+
+   For example:
+
+   > cat /etc/conf.modules
+   ...
+   alias eth0 cs89x0
+   options cs89x0 io=0x0200 dma=5 use_dma=1
+   ...
+
+   In this example we are telling the module system that the
+   ethernet driver for this machine should use the cs89x0 driver.  We
+   are asking 'modprobe' to pass the 'io', 'dma' and 'use_dma'
+   arguments to the driver when it is loaded.
+
+i) Cirrus recommend that the cs89x0 use the ISA DMA channels 5, 6 or
+   7.  You will probably find that other DMA channels will not work.
+
+j) The cs89x0 supports DMA for receiving only.  DMA mode is
+   significantly more efficient.  Flooding a 400 MHz Celeron machine
+   with large ping packets consumes 82% of its CPU capacity in non-DMA
+   mode.  With DMA this is reduced to 45%.
+
+k) If your Linux kernel was compiled with inbuilt plug-and-play
+   support you will be able to find information about the cs89x0 card
+   with the command
+
+   cat /proc/isapnp
+
+l) If during DMA operation you find erratic behavior or network data
+   corruption you should use your PC's BIOS to slow the EISA bus clock.
+
+m) If the cs89x0 driver is compiled directly into the kernel
+   (non-modular) then its I/O address is automatically determined by
+   ISA bus probing.  The IRQ number, media options, etc are determined
+   from the card's EEPROM.
+
+n) If the cs89x0 driver is compiled directly into the kernel, DMA
+   mode may be selected by providing the kernel with a boot option
+   'cs89x0_dma=N' where 'N' is the desired DMA channel number (5, 6 or 7).
+
+   Kernel boot options may be provided on the LILO command line:
+
+       LILO boot: linux cs89x0_dma=5
+
+   or they may be placed in /etc/lilo.conf:
+
+       image=/boot/bzImage-2.3.48
+         append="cs89x0_dma=5"
+         label=linux
+         root=/dev/hda5
+         read-only
+
+   The DMA Rx buffer size is hardwired to 16 kbytes in this mode.
+   (64k mode is not available).
+
+
+4.0 COMPILING THE DRIVER
+===============================================================================
+
+The cs89x0 driver can be compiled directly into the kernel or compiled into
+a loadable device driver module.
+
+
+4.1 COMPILING THE DRIVER AS A LOADABLE MODULE
+
+To compile the driver into a loadable module, use the following command 
+(single command line, without quotes):
+
+"gcc -D__KERNEL__ -I/usr/src/linux/include -I/usr/src/linux/net/inet -Wall 
+-Wstrict-prototypes -O2 -fomit-frame-pointer -DMODULE -DCONFIG_MODVERSIONS 
+-c cs89x0.c"
+
+4.2 COMPILING THE DRIVER TO SUPPORT MEMORY MODE
+
+Support for memory mode was not carried over into the 2.3 series kernels.
+
+4.3 COMPILING THE DRIVER TO SUPPORT Rx DMA
+
+The compile-time optionality for DMA was removed in the 2.3 kernel
+series.  DMA support is now unconditionally part of the driver.  It is
+enabled by the 'use_dma=1' module option.
+
+
+5.0 TESTING AND TROUBLESHOOTING
+===============================================================================
+
+5.1 KNOWN DEFECTS and LIMITATIONS
+
+Refer to the RELEASE.TXT file distributed as part of this archive for a list of 
+known defects, driver limitations, and work arounds.
+
+
+5.2 TESTING THE ADAPTER
+
+Once the adapter has been installed and configured, the diagnostic option of 
+the CS8900/20 Setup Utility can be used to test the functionality of the 
+adapter and its network connection.  Use the diagnostics 'Self Test' option to
+test the functionality of the adapter with the hardware configuration you have
+assigned. You can use the diagnostics 'Network Test' to test the ability of the
+adapter to communicate across the Ethernet with another PC equipped with a 
+CS8900/20-based adapter card (it must also be running the CS8900/20 Setup 
+Utility).
+
+         NOTE: The Setup Utility's diagnostics are designed to run in a
+         DOS-only operating system environment.  DO NOT run the diagnostics 
+         from a DOS or command prompt session under Windows 95, Windows NT, 
+         OS/2, or other operating system.
+
+To run the diagnostics tests on the CS8900/20 adapter:
+
+   1.) Boot DOS on the PC and start the CS8900/20 Setup Utility.
+
+   2.) The adapter's current configuration is displayed.  Hit the ENTER key to
+       get to the main menu.
+
+   4.) Select 'Diagnostics' (ALT-G) from the main menu.  
+       * Select 'Self-Test' to test the adapter's basic functionality.
+       * Select 'Network Test' to test the network connection and cabling.
+
+
+5.2.1 DIAGNOSTIC SELF-TEST
+
+The diagnostic self-test checks the adapter's basic functionality as well as 
+its ability to communicate across the ISA bus based on the system resources 
+assigned during hardware configuration.  The following tests are performed:
+
+   * IO Register Read/Write Test
+     The IO Register Read/Write test insures that the CS8900/20 can be 
+     accessed in IO mode, and that the IO base address is correct.
+
+   * Shared Memory Test
+     The Shared Memory test insures the CS8900/20 can be accessed in memory 
+     mode and that the range of memory addresses assigned does not conflict 
+     with other devices in the system.
+
+   * Interrupt Test
+     The Interrupt test insures there are no conflicts with the assigned IRQ
+     signal.
+
+   * EEPROM Test
+     The EEPROM test insures the EEPROM can be read.
+
+   * Chip RAM Test
+     The Chip RAM test insures the 4K of memory internal to the CS8900/20 is
+     working properly.
+
+   * Internal Loop-back Test
+     The Internal Loop Back test insures the adapter's transmitter and 
+     receiver are operating properly.  If this test fails, make sure the 
+     adapter's cable is connected to the network (check for LED activity for 
+     example).
+
+   * Boot PROM Test
+     The Boot PROM  test insures the Boot PROM is present, and can be read.
+     Failure indicates the Boot PROM  was not successfully read due to a
+     hardware problem or due to a conflicts on the Boot PROM address
+     assignment. (Test only applies if the adapter is configured to use the
+     Boot PROM option.)
+
+Failure of a test item indicates a possible system resource conflict with 
+another device on the ISA bus.  In this case, you should use the Manual Setup 
+option to reconfigure the adapter by selecting a different value for the system
+resource that failed.
+
+
+5.2.2 DIAGNOSTIC NETWORK TEST
+
+The Diagnostic Network Test verifies a working network connection by 
+transferring data between two CS8900/20 adapters installed in different PCs 
+on the same network. (Note: the diagnostic network test should not be run 
+between two nodes across a router.) 
+
+This test requires that each of the two PCs have a CS8900/20-based adapter
+installed and have the CS8900/20 Setup Utility running.  The first PC is 
+configured as a Responder and the other PC is configured as an Initiator.  
+Once the Initiator is started, it sends data frames to the Responder which 
+returns the frames to the Initiator.
+
+The total number of frames received and transmitted are displayed on the 
+Initiator's display, along with a count of the number of frames received and 
+transmitted OK or in error.  The test can be terminated anytime by the user at 
+either PC.
+
+To setup the Diagnostic Network Test:
+
+    1.) Select a PC with a CS8900/20-based adapter and a known working network
+        connection to act as the Responder.  Run the CS8900/20 Setup Utility 
+        and select 'Diagnostics -> Network Test -> Responder' from the main 
+        menu.  Hit ENTER to start the Responder.
+
+    2.) Return to the PC with the CS8900/20-based adapter you want to test and
+        start the CS8900/20 Setup Utility. 
+
+    3.) From the main menu, Select 'Diagnostic -> Network Test -> Initiator'.
+        Hit ENTER to start the test.
+You may stop the test on the Initiator at any time while allowing the Responder
+to continue running.  In this manner, you can move to additional PCs and test 
+them by starting the Initiator on another PC without having to stop/start the 
+Responder.
+
+
+5.3 USING THE ADAPTER'S LEDs
+
+The 2 and 3-media adapters have two LEDs visible on the back end of the board 
+located near the 10Base-T connector.  
+
+Link Integrity LED: A "steady" ON of the green LED indicates a valid 10Base-T 
+connection.  (Only applies to 10Base-T.  The green LED has no significance for
+a 10Base-2 or AUI connection.)
+
+TX/RX LED: The yellow LED lights briefly each time the adapter transmits or 
+receives data. (The yellow LED will appear to "flicker" on a typical network.)
+
+
+5.4 RESOLVING I/O CONFLICTS
+
+An IO conflict occurs when two or more adapter use the same ISA resource (IO 
+address, memory address or IRQ).  You can usually detect an IO conflict in one 
+of four ways after installing and or configuring the CS8900/20-based adapter:
+
+    1.) The system does not boot properly (or at all).
+
+    2.) The driver cannot communicate with the adapter, reporting an "Adapter
+        not found" error message.
+
+    3.) You cannot connect to the network or the driver will not load.
+
+    4.) If you have configured the adapter to run in memory mode but the driver
+        reports it is using IO mode when loading, this is an indication of a
+        memory address conflict.
+
+If an IO conflict occurs, run the CS8900/20 Setup Utility and perform a 
+diagnostic self-test.  Normally, the ISA resource in conflict will fail the 
+self-test.  If so, reconfigure the adapter selecting another choice for the 
+resource in conflict.  Run the diagnostics again to check for further IO 
+conflicts.
+
+In some cases, such as when the PC will not boot, it may be necessary to remove
+the adapter and reconfigure it by installing it in another PC to run the 
+CS8900/20 Setup Utility.  Once reinstalled in the target system, run the 
+diagnostics self-test to ensure the new configuration is free of conflicts 
+before loading the driver again.
+
+When manually configuring the adapter, keep in mind the typical ISA system 
+resource usage as indicated in the tables below.
+
+I/O Address            Device                        IRQ      Device
+-----------            --------                      ---      --------
+ 200-20F               Game I/O adapter               3       COM2, Bus Mouse
+ 230-23F               Bus Mouse                      4       COM1
+ 270-27F               LPT3: third parallel port      5       LPT2
+ 2F0-2FF               COM2: second serial port       6       Floppy Disk controller
+ 320-32F               Fixed disk controller          7       LPT1
+                                              8       Real-time Clock
+                                                 9       EGA/VGA display adapter    
+                                                12       Mouse (PS/2)                              
+Memory Address  Device                          13       Math Coprocessor
+--------------  ---------------------           14       Hard Disk controller
+A000-BFFF      EGA Graphics Adapter
+A000-C7FF      VGA Graphics Adapter
+B000-BFFF      Mono Graphics Adapter
+B800-BFFF      Color Graphics Adapter
+E000-FFFF      AT BIOS
+
+
+
+
+6.0 TECHNICAL SUPPORT
+===============================================================================
+
+6.1 CONTACTING CIRRUS LOGIC'S TECHNICAL SUPPORT
+
+Cirrus Logic's CS89XX Technical Application Support can be reached at:
+
+Telephone  :(800) 888-5016 (from inside U.S. and Canada)
+           :(512) 442-7555 (from outside the U.S. and Canada)
+Fax        :(512) 912-3871
+Email      :ethernet@crystal.cirrus.com
+WWW        :http://www.cirrus.com
+
+
+6.2 INFORMATION REQUIRED BEFORE CONTACTING TECHNICAL SUPPORT
+
+Before contacting Cirrus Logic for technical support, be prepared to provide as 
+Much of the following information as possible. 
+
+1.) Adapter type (CRD8900, CDB8900, CDB8920, etc.)
+
+2.) Adapter configuration
+
+    * IO Base, Memory Base, IO or memory mode enabled, IRQ, DMA channel
+    * Plug and Play enabled/disabled (CS8920-based adapters only)
+    * Configured for media auto-detect or specific media type (which type).    
+
+3.) PC System's Configuration
+
+    * Plug and Play system (yes/no)
+    * BIOS (make and version)
+    * System make and model
+    * CPU (type and speed)
+    * System RAM
+    * SCSI Adapter
+
+4.) Software
+
+    * CS89XX driver and version
+    * Your network operating system and version
+    * Your system's OS version 
+    * Version of all protocol support files
+
+5.) Any Error Message displayed.
+
+
+
+6.3 OBTAINING THE LATEST DRIVER VERSION
+
+You can obtain the latest CS89XX drivers and support software from Cirrus Logic's 
+Web site.  You can also contact Cirrus Logic's Technical Support (email:
+ethernet@crystal.cirrus.com) and request that you be registered for automatic 
+software-update notification.
+
+Cirrus Logic maintains a web page at http://www.cirrus.com with the
+latest drivers and technical publications.
+
+
+6.4 Current maintainer
+
+In February 2000 the maintenance of this driver was assumed by Andrew
+Morton.
+
+6.5 Kernel module parameters
+
+For use in embedded environments with no cs89x0 EEPROM, the kernel boot
+parameter `cs89x0_media=' has been implemented.  Usage is:
+
+       cs89x0_media=rj45    or
+       cs89x0_media=aui     or
+       cs89x0_media=bnc
+
diff --git a/Documentation/networking/device_drivers/davicom/dm9000.txt b/Documentation/networking/device_drivers/davicom/dm9000.txt
new file mode 100644 (file)
index 0000000..5552e2e
--- /dev/null
@@ -0,0 +1,167 @@
+DM9000 Network driver
+=====================
+
+Copyright 2008 Simtec Electronics,
+         Ben Dooks <ben@simtec.co.uk> <ben-linux@fluff.org>
+
+
+Introduction
+------------
+
+This file describes how to use the DM9000 platform-device based network driver
+that is contained in the files drivers/net/dm9000.c and drivers/net/dm9000.h.
+
+The driver supports three DM9000 variants, the DM9000E which is the first chip
+supported as well as the newer DM9000A and DM9000B devices. It is currently
+maintained and tested by Ben Dooks, who should be CC: to any patches for this
+driver.
+
+
+Defining the platform device
+----------------------------
+
+The minimum set of resources attached to the platform device are as follows:
+
+    1) The physical address of the address register
+    2) The physical address of the data register
+    3) The IRQ line the device's interrupt pin is connected to.
+
+These resources should be specified in that order, as the ordering of the
+two address regions is important (the driver expects these to be address
+and then data).
+
+An example from arch/arm/mach-s3c2410/mach-bast.c is:
+
+static struct resource bast_dm9k_resource[] = {
+       [0] = {
+               .start = S3C2410_CS5 + BAST_PA_DM9000,
+               .end   = S3C2410_CS5 + BAST_PA_DM9000 + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start = S3C2410_CS5 + BAST_PA_DM9000 + 0x40,
+               .end   = S3C2410_CS5 + BAST_PA_DM9000 + 0x40 + 0x3f,
+               .flags = IORESOURCE_MEM,
+       },
+       [2] = {
+               .start = IRQ_DM9000,
+               .end   = IRQ_DM9000,
+               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
+       }
+};
+
+static struct platform_device bast_device_dm9k = {
+       .name           = "dm9000",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(bast_dm9k_resource),
+       .resource       = bast_dm9k_resource,
+};
+
+Note the setting of the IRQ trigger flag in bast_dm9k_resource[2].flags,
+as this will generate a warning if it is not present. The trigger from
+the flags field will be passed to request_irq() when registering the IRQ
+handler to ensure that the IRQ is setup correctly.
+
+This shows a typical platform device, without the optional configuration
+platform data supplied. The next example uses the same resources, but adds
+the optional platform data to pass extra configuration data:
+
+static struct dm9000_plat_data bast_dm9k_platdata = {
+       .flags          = DM9000_PLATF_16BITONLY,
+};
+
+static struct platform_device bast_device_dm9k = {
+       .name           = "dm9000",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(bast_dm9k_resource),
+       .resource       = bast_dm9k_resource,
+       .dev            = {
+               .platform_data = &bast_dm9k_platdata,
+       }
+};
+
+The platform data is defined in include/linux/dm9000.h and described below.
+
+
+Platform data
+-------------
+
+Extra platform data for the DM9000 can describe the IO bus width to the
+device, whether or not an external PHY is attached to the device and
+the availability of an external configuration EEPROM.
+
+The flags for the platform data .flags field are as follows:
+
+DM9000_PLATF_8BITONLY
+
+       The IO should be done with 8bit operations.
+
+DM9000_PLATF_16BITONLY
+
+       The IO should be done with 16bit operations.
+
+DM9000_PLATF_32BITONLY
+
+       The IO should be done with 32bit operations.
+
+DM9000_PLATF_EXT_PHY
+
+       The chip is connected to an external PHY.
+
+DM9000_PLATF_NO_EEPROM
+
+       This can be used to signify that the board does not have an
+       EEPROM, or that the EEPROM should be hidden from the user.
+
+DM9000_PLATF_SIMPLE_PHY
+
+       Switch to using the simpler PHY polling method which does not
+       try and read the MII PHY state regularly. This is only available
+       when using the internal PHY. See the section on link state polling
+       for more information.
+
+       The config symbol DM9000_FORCE_SIMPLE_PHY_POLL, Kconfig entry
+       "Force simple NSR based PHY polling" allows this flag to be
+       forced on at build time.
+
+
+PHY Link state polling
+----------------------
+
+The driver keeps track of the link state and informs the network core
+about link (carrier) availability. This is managed by several methods
+depending on the version of the chip and on which PHY is being used.
+
+For the internal PHY, the original (and currently default) method is
+to read the MII state, either when the status changes if we have the
+necessary interrupt support in the chip or every two seconds via a
+periodic timer.
+
+To reduce the overhead for the internal PHY, there is now the option
+of using the DM9000_FORCE_SIMPLE_PHY_POLL config, or DM9000_PLATF_SIMPLE_PHY
+platform data option to read the summary information without the
+expensive MII accesses. This method is faster, but does not print
+as much information.
+
+When using an external PHY, the driver currently has to poll the MII
+link status as there is no method for getting an interrupt on link change.
+
+
+DM9000A / DM9000B
+-----------------
+
+These chips are functionally similar to the DM9000E and are supported easily
+by the same driver. The features are:
+
+   1) Interrupt on internal PHY state change. This means that the periodic
+      polling of the PHY status may be disabled on these devices when using
+      the internal PHY.
+
+   2) TCP/UDP checksum offloading, which the driver does not currently support.
+
+
+ethtool
+-------
+
+The driver supports the ethtool interface for access to the driver
+state information, the PHY state and the EEPROM.
diff --git a/Documentation/networking/device_drivers/dec/de4x5.txt b/Documentation/networking/device_drivers/dec/de4x5.txt
new file mode 100644 (file)
index 0000000..c8e4ca9
--- /dev/null
@@ -0,0 +1,178 @@
+    Originally,   this  driver  was    written  for the  Digital   Equipment
+    Corporation series of EtherWORKS Ethernet cards:
+
+        DE425 TP/COAX EISA
+       DE434 TP PCI
+       DE435 TP/COAX/AUI PCI
+       DE450 TP/COAX/AUI PCI
+       DE500 10/100 PCI Fasternet
+
+    but it  will  now attempt  to  support all  cards which   conform to the
+    Digital Semiconductor   SROM   Specification.    The  driver   currently
+    recognises the following chips:
+
+        DC21040  (no SROM) 
+       DC21041[A]  
+       DC21140[A] 
+       DC21142 
+       DC21143 
+
+    So far the driver is known to work with the following cards:
+
+        KINGSTON
+       Linksys
+       ZNYX342
+       SMC8432
+       SMC9332 (w/new SROM)
+       ZNYX31[45]
+       ZNYX346 10/100 4 port (can act as a 10/100 bridge!) 
+
+    The driver has been tested on a relatively busy network using the DE425,
+    DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
+    16M of data to a DECstation 5000/200 as follows:
+
+                TCP           UDP
+             TX     RX     TX     RX
+    DE425   1030k  997k   1170k  1128k
+    DE434   1063k  995k   1170k  1125k
+    DE435   1063k  995k   1170k  1125k
+    DE500   1063k  998k   1170k  1125k  in 10Mb/s mode
+
+    All  values are typical (in   kBytes/sec) from a  sample  of 4 for  each
+    measurement. Their error is +/-20k on a quiet (private) network and also
+    depend on what load the CPU has.
+
+    =========================================================================
+
+    The ability to load this  driver as a loadable  module has been included
+    and used extensively  during the driver development  (to save those long
+    reboot sequences).  Loadable module support  under PCI and EISA has been
+    achieved by letting the driver autoprobe as if it were compiled into the
+    kernel. Do make sure  you're not sharing  interrupts with anything  that
+    cannot accommodate  interrupt  sharing!
+
+    To utilise this ability, you have to do 8 things:
+
+    0) have a copy of the loadable modules code installed on your system.
+    1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
+    temporary directory.
+    2) for fixed  autoprobes (not  recommended),  edit the source code  near
+    line 5594 to reflect the I/O address  you're using, or assign these when
+    loading by:
+
+                   insmod de4x5 io=0xghh           where g = bus number
+                                                       hh = device number   
+
+       NB: autoprobing for modules is now supported by default. You may just
+           use:
+
+                   insmod de4x5
+
+           to load all available boards. For a specific board, still use
+          the 'io=?' above.
+    3) compile  de4x5.c, but include -DMODULE in  the command line to ensure
+    that the correct bits are compiled (see end of source code).
+    4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a
+    kernel with the de4x5 configuration turned off and reboot.
+    5) insmod de4x5 [io=0xghh]
+    6) run the net startup bits for your new eth?? interface(s) manually 
+    (usually /etc/rc.inet[12] at boot time). 
+    7) enjoy!
+
+    To unload a module, turn off the associated interface(s) 
+    'ifconfig eth?? down' then 'rmmod de4x5'.
+
+    Automedia detection is included so that in  principle you can disconnect
+    from, e.g.  TP, reconnect  to BNC  and  things will still work  (after a
+    pause whilst the   driver figures out   where its media went).  My tests
+    using ping showed that it appears to work....
+
+    By  default,  the driver will  now   autodetect any  DECchip based card.
+    Should you have a need to restrict the driver to DIGITAL only cards, you
+    can compile with a  DEC_ONLY define, or if  loading as a module, use the
+    'dec_only=1'  parameter. 
+
+    I've changed the timing routines to  use the kernel timer and scheduling
+    functions  so that the  hangs  and other assorted problems that occurred
+    while autosensing the  media  should be gone.  A  bonus  for the DC21040
+    auto  media sense algorithm is  that it can now  use one that is more in
+    line with the  rest (the DC21040  chip doesn't  have a hardware  timer).
+    The downside is the 1 'jiffies' (10ms) resolution.
+
+    IEEE 802.3u MII interface code has  been added in anticipation that some
+    products may use it in the future.
+
+    The SMC9332 card  has a non-compliant SROM  which needs fixing -  I have
+    patched this  driver to detect it  because the SROM format used complies
+    to a previous DEC-STD format.
+
+    I have removed the buffer copies needed for receive on Intels.  I cannot
+    remove them for   Alphas since  the  Tulip hardware   only does longword
+    aligned  DMA transfers  and  the  Alphas get   alignment traps with  non
+    longword aligned data copies (which makes them really slow). No comment.
+
+    I  have added SROM decoding  routines to make this  driver work with any
+    card that  supports the Digital  Semiconductor SROM spec. This will help
+    all  cards running the dc2114x  series chips in particular.  Cards using
+    the dc2104x  chips should run correctly with  the basic  driver.  I'm in
+    debt to <mjacob@feral.com> for the  testing and feedback that helped get
+    this feature working.  So far we have  tested KINGSTON, SMC8432, SMC9332
+    (with the latest SROM complying  with the SROM spec  V3: their first was
+    broken), ZNYX342  and  LinkSys. ZNYX314 (dual  21041  MAC) and  ZNYX 315
+    (quad 21041 MAC)  cards also  appear  to work despite their  incorrectly
+    wired IRQs.
+
+    I have added a temporary fix for interrupt problems when some SCSI cards
+    share the same interrupt as the DECchip based  cards. The problem occurs
+    because  the SCSI card wants to  grab the interrupt  as a fast interrupt
+    (runs the   service routine with interrupts turned   off) vs.  this card
+    which really needs to run the service routine with interrupts turned on.
+    This driver will  now   add the interrupt service   routine  as  a  fast
+    interrupt if it   is bounced from the   slow interrupt.  THIS IS NOT   A
+    RECOMMENDED WAY TO RUN THE DRIVER  and has been done  for a limited time
+    until  people   sort  out their  compatibility    issues and the  kernel
+    interrupt  service code  is  fixed.   YOU  SHOULD SEPARATE OUT  THE FAST
+    INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
+    run on the same interrupt. PCMCIA/CardBus is another can of worms...
+
+    Finally, I think  I have really  fixed  the module  loading problem with
+    more than one DECchip based  card.  As a  side effect, I don't mess with
+    the  device structure any  more which means that  if more than 1 card in
+    2.0.x is    installed (4  in   2.1.x),  the  user   will have   to  edit
+    linux/drivers/net/Space.c  to make room for  them. Hence, module loading
+    is  the preferred way to use   this driver, since  it  doesn't have this
+    limitation.
+
+    Where SROM media  detection is used and  full duplex is specified in the
+    SROM,  the feature is  ignored unless  lp->params.fdx  is set at compile
+    time  OR during  a   module load  (insmod  de4x5   args='eth??:fdx' [see
+    below]).  This is because there  is no way  to automatically detect full
+    duplex   links  except through   autonegotiation.    When I  include the
+    autonegotiation feature in  the SROM autoconf  code, this detection will
+    occur automatically for that case.
+
+    Command line  arguments are  now allowed, similar to  passing  arguments
+    through LILO. This will allow a per adapter board set  up of full duplex
+    and media. The only lexical constraints are:  the board name (dev->name)
+    appears in  the list before its parameters.  The list of parameters ends
+    either at the end of the parameter list or with another board name.  The
+    following parameters are allowed:
+
+            fdx        for full duplex
+           autosense  to set the media/speed; with the following 
+                      sub-parameters:
+                      TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO
+
+    Case sensitivity is important  for  the sub-parameters. They *must*   be
+    upper case. Examples:
+
+        insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
+
+    For a compiled in driver, in linux/drivers/net/CONFIG, place e.g.
+       DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' 
+
+    Yes,  I know full duplex  isn't permissible on BNC  or AUI; they're just
+    examples. By default, full duplex is turned  off and AUTO is the default
+    autosense setting. In  reality, I expect only the  full duplex option to
+    be used. Note the use of single quotes in the two examples above and the
+    lack of commas to separate items.
diff --git a/Documentation/networking/device_drivers/dec/dmfe.txt b/Documentation/networking/device_drivers/dec/dmfe.txt
new file mode 100644 (file)
index 0000000..25320bf
--- /dev/null
@@ -0,0 +1,66 @@
+Note: This driver doesn't have a maintainer.
+
+Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux.
+
+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.
+
+
+This driver provides kernel support for Davicom DM9102(A)/DM9132/DM9801 ethernet cards ( CNET
+10/100 ethernet cards uses Davicom chipset too, so this driver supports CNET cards too ).If you
+didn't compile this driver as a module, it will automatically load itself on boot and print a
+line similar to :
+
+       dmfe: Davicom DM9xxx net driver, version 1.36.4 (2002-01-17)
+
+If you compiled this driver as a module, you have to load it on boot.You can load it with command :
+
+       insmod dmfe
+
+This way it will autodetect the device mode.This is the suggested way to load the module.Or you can pass
+a mode= setting to module while loading, like :
+
+       insmod dmfe mode=0 # Force 10M Half Duplex
+       insmod dmfe mode=1 # Force 100M Half Duplex
+       insmod dmfe mode=4 # Force 10M Full Duplex
+       insmod dmfe mode=5 # Force 100M Full Duplex
+
+Next you should configure your network interface with a command similar to :
+
+       ifconfig eth0 172.22.3.18
+                      ^^^^^^^^^^^
+                    Your IP Address
+
+Then you may have to modify the default routing table with command :
+
+       route add default eth0
+
+
+Now your ethernet card should be up and running.
+
+
+TODO:
+
+Implement pci_driver::suspend() and pci_driver::resume() power management methods.
+Check on 64 bit boxes.
+Check and fix on big endian boxes.
+Test and make sure PCI latency is now correct for all cases.
+
+
+Authors:
+
+Sten Wang <sten_wang@davicom.com.tw >   : Original Author
+
+Contributors:
+
+Marcelo Tosatti <marcelo@conectiva.com.br>
+Alan Cox <alan@lxorguk.ukuu.org.uk>
+Jeff Garzik <jgarzik@pobox.com>
+Vojtech Pavlik <vojtech@suse.cz>
diff --git a/Documentation/networking/device_drivers/dlink/dl2k.txt b/Documentation/networking/device_drivers/dlink/dl2k.txt
new file mode 100644 (file)
index 0000000..cba74f7
--- /dev/null
@@ -0,0 +1,282 @@
+
+    D-Link DL2000-based Gigabit Ethernet Adapter Installation
+    for Linux
+    May 23, 2002
+
+Contents
+========
+ - Compatibility List
+ - Quick Install
+ - Compiling the Driver
+ - Installing the Driver
+ - Option parameter
+ - Configuration Script Sample
+ - Troubleshooting
+
+
+Compatibility List
+=================
+Adapter Support:
+
+D-Link DGE-550T Gigabit Ethernet Adapter.
+D-Link DGE-550SX Gigabit Ethernet Adapter.
+D-Link DL2000-based Gigabit Ethernet Adapter.
+
+
+The driver support Linux kernel 2.4.7 later. We had tested it
+on the environments below.
+
+ . Red Hat v6.2 (update kernel to 2.4.7)
+ . Red Hat v7.0 (update kernel to 2.4.7)
+ . Red Hat v7.1 (kernel 2.4.7)
+ . Red Hat v7.2 (kernel 2.4.7-10)
+
+
+Quick Install
+=============
+Install linux driver as following command:
+
+1. make all
+2. insmod dl2k.ko
+3. ifconfig eth0 up 10.xxx.xxx.xxx netmask 255.0.0.0
+                   ^^^^^^^^^^^^^^^\        ^^^^^^^^\
+                                   IP               NETMASK
+Now eth0 should active, you can test it by "ping" or get more information by
+"ifconfig". If tested ok, continue the next step.
+
+4. cp dl2k.ko /lib/modules/`uname -r`/kernel/drivers/net
+5. Add the following line to /etc/modprobe.d/dl2k.conf:
+       alias eth0 dl2k
+6. Run depmod to updated module indexes.
+7. Run "netconfig" or "netconf" to create configuration script ifcfg-eth0
+   located at /etc/sysconfig/network-scripts or create it manually.
+   [see - Configuration Script Sample]
+8. Driver will automatically load and configure at next boot time.
+
+Compiling the Driver
+====================
+  In Linux, NIC drivers are most commonly configured as loadable modules.
+The approach of building a monolithic kernel has become obsolete. The driver
+can be compiled as part of a monolithic kernel, but is strongly discouraged.
+The remainder of this section assumes the driver is built as a loadable module.
+In the Linux environment, it is a good idea to rebuild the driver from the
+source instead of relying on a precompiled version. This approach provides
+better reliability since a precompiled driver might depend on libraries or
+kernel features that are not present in a given Linux installation.
+
+The 3 files necessary to build Linux device driver are dl2k.c, dl2k.h and
+Makefile. To compile, the Linux installation must include the gcc compiler,
+the kernel source, and the kernel headers. The Linux driver supports Linux
+Kernels 2.4.7. Copy the files to a directory and enter the following command
+to compile and link the driver:
+
+CD-ROM drive
+------------
+
+[root@XXX /] mkdir cdrom
+[root@XXX /] mount -r -t iso9660 -o conv=auto /dev/cdrom /cdrom
+[root@XXX /] cd root
+[root@XXX /root] mkdir dl2k
+[root@XXX /root] cd dl2k
+[root@XXX dl2k] cp /cdrom/linux/dl2k.tgz /root/dl2k
+[root@XXX dl2k] tar xfvz dl2k.tgz
+[root@XXX dl2k] make all
+
+Floppy disc drive
+-----------------
+
+[root@XXX /] cd root
+[root@XXX /root] mkdir dl2k
+[root@XXX /root] cd dl2k
+[root@XXX dl2k] mcopy a:/linux/dl2k.tgz /root/dl2k
+[root@XXX dl2k] tar xfvz dl2k.tgz
+[root@XXX dl2k] make all
+
+Installing the Driver
+=====================
+
+  Manual Installation
+  -------------------
+  Once the driver has been compiled, it must be loaded, enabled, and bound
+  to a protocol stack in order to establish network connectivity. To load a
+  module enter the command:
+
+  insmod dl2k.o
+
+  or
+
+  insmod dl2k.o <optional parameter>   ; add parameter
+
+  ===============================================================
+   example: insmod dl2k.o media=100mbps_hd
+   or      insmod dl2k.o media=3
+   or      insmod dl2k.o media=3,2     ; for 2 cards
+  ===============================================================
+
+  Please reference the list of the command line parameters supported by
+  the Linux device driver below.
+
+  The insmod command only loads the driver and gives it a name of the form
+  eth0, eth1, etc. To bring the NIC into an operational state,
+  it is necessary to issue the following command:
+
+  ifconfig eth0 up
+
+  Finally, to bind the driver to the active protocol (e.g., TCP/IP with
+  Linux), enter the following command:
+
+  ifup eth0
+
+  Note that this is meaningful only if the system can find a configuration
+  script that contains the necessary network information. A sample will be
+  given in the next paragraph.
+
+  The commands to unload a driver are as follows:
+
+  ifdown eth0
+  ifconfig eth0 down
+  rmmod dl2k.o
+
+  The following are the commands to list the currently loaded modules and
+  to see the current network configuration.
+
+  lsmod
+  ifconfig
+
+
+  Automated Installation
+  ----------------------
+  This section describes how to install the driver such that it is
+  automatically loaded and configured at boot time. The following description
+  is based on a Red Hat 6.0/7.0 distribution, but it can easily be ported to
+  other distributions as well.
+
+  Red Hat v6.x/v7.x
+  -----------------
+  1. Copy dl2k.o to the network modules directory, typically
+     /lib/modules/2.x.x-xx/net or /lib/modules/2.x.x/kernel/drivers/net.
+  2. Locate the boot module configuration file, most commonly in the
+     /etc/modprobe.d/ directory. Add the following lines:
+
+     alias ethx dl2k
+     options dl2k <optional parameters>
+
+     where ethx will be eth0 if the NIC is the only ethernet adapter, eth1 if
+     one other ethernet adapter is installed, etc. Refer to the table in the
+     previous section for the list of optional parameters.
+  3. Locate the network configuration scripts, normally the
+     /etc/sysconfig/network-scripts directory, and create a configuration
+     script named ifcfg-ethx that contains network information.
+  4. Note that for most Linux distributions, Red Hat included, a configuration
+     utility with a graphical user interface is provided to perform steps 2
+     and 3 above.
+
+
+Parameter Description
+=====================
+You can install this driver without any additional parameter. However, if you
+are going to have extensive functions then it is necessary to set extra
+parameter. Below is a list of the command line parameters supported by the
+Linux device
+driver.
+
+mtu=packet_size                        - Specifies the maximum packet size. default
+                                 is 1500.
+
+media=media_type               - Specifies the media type the NIC operates at.
+                                 autosense     Autosensing active media.
+                                 10mbps_hd     10Mbps half duplex.
+                                 10mbps_fd     10Mbps full duplex.
+                                 100mbps_hd    100Mbps half duplex.
+                                 100mbps_fd    100Mbps full duplex.
+                                 1000mbps_fd   1000Mbps full duplex.
+                                 1000mbps_hd   1000Mbps half duplex.
+                                 0             Autosensing active media.
+                                 1             10Mbps half duplex.
+                                 2             10Mbps full duplex.
+                                 3             100Mbps half duplex.
+                                 4             100Mbps full duplex.
+                                 5             1000Mbps half duplex.
+                                 6             1000Mbps full duplex.
+
+                                 By default, the NIC operates at autosense.
+                                 1000mbps_fd and 1000mbps_hd types are only
+                                 available for fiber adapter.
+
+vlan=n                         - Specifies the VLAN ID. If vlan=0, the
+                                 Virtual Local Area Network (VLAN) function is
+                                 disable.
+
+jumbo=[0|1]                    - Specifies the jumbo frame support. If jumbo=1,
+                                 the NIC accept jumbo frames. By default, this
+                                 function is disabled.
+                                 Jumbo frame usually improve the performance
+                                 int gigabit.
+                                 This feature need jumbo frame compatible 
+                                 remote.
+                                 
+rx_coalesce=m                  - Number of rx frame handled each interrupt.
+rx_timeout=n                   - Rx DMA wait time for an interrupt. 
+                                 If set rx_coalesce > 0, hardware only assert 
+                                 an interrupt for m frames. Hardware won't 
+                                 assert rx interrupt until m frames received or
+                                 reach timeout of n * 640 nano seconds. 
+                                 Set proper rx_coalesce and rx_timeout can 
+                                 reduce congestion collapse and overload which
+                                 has been a bottleneck for high speed network.
+                                 
+                                 For example, rx_coalesce=10 rx_timeout=800.
+                                 that is, hardware assert only 1 interrupt 
+                                 for 10 frames received or timeout of 512 us. 
+
+tx_coalesce=n                  - Number of tx frame handled each interrupt.
+                                 Set n > 1 can reduce the interrupts 
+                                 congestion usually lower performance of
+                                 high speed network card. Default is 16.
+                                 
+tx_flow=[1|0]                  - Specifies the Tx flow control. If tx_flow=0, 
+                                 the Tx flow control disable else driver
+                                 autodetect.
+rx_flow=[1|0]                  - Specifies the Rx flow control. If rx_flow=0, 
+                                 the Rx flow control enable else driver
+                                 autodetect.
+
+
+Configuration Script Sample
+===========================
+Here is a sample of a simple configuration script:
+
+DEVICE=eth0
+USERCTL=no
+ONBOOT=yes
+POOTPROTO=none
+BROADCAST=207.200.5.255
+NETWORK=207.200.5.0
+NETMASK=255.255.255.0
+IPADDR=207.200.5.2
+
+
+Troubleshooting
+===============
+Q1. Source files contain ^ M behind every line.
+       Make sure all files are Unix file format (no LF). Try the following
+    shell command to convert files.
+
+       cat dl2k.c | col -b > dl2k.tmp
+       mv dl2k.tmp dl2k.c
+
+       OR
+
+       cat dl2k.c | tr -d "\r" > dl2k.tmp
+       mv dl2k.tmp dl2k.c
+
+Q2: Could not find header files (*.h) ?
+       To compile the driver, you need kernel header files. After
+    installing the kernel source, the header files are usually located in
+    /usr/src/linux/include, which is the default include directory configured
+    in Makefile. For some distributions, there is a copy of header files in
+    /usr/src/include/linux and /usr/src/include/asm, that you can change the
+    INCLUDEDIR in Makefile to /usr/include without installing kernel source.
+       Note that RH 7.0 didn't provide correct header files in /usr/include,
+    including those files will make a wrong version driver.
+
diff --git a/Documentation/networking/device_drivers/freescale/dpaa.txt b/Documentation/networking/device_drivers/freescale/dpaa.txt
new file mode 100644 (file)
index 0000000..f88194f
--- /dev/null
@@ -0,0 +1,260 @@
+The QorIQ DPAA Ethernet Driver
+==============================
+
+Authors:
+Madalin Bucur <madalin.bucur@nxp.com>
+Camelia Groza <camelia.groza@nxp.com>
+
+Contents
+========
+
+       - DPAA Ethernet Overview
+       - DPAA Ethernet Supported SoCs
+       - Configuring DPAA Ethernet in your kernel
+       - DPAA Ethernet Frame Processing
+       - DPAA Ethernet Features
+       - DPAA IRQ Affinity and Receive Side Scaling
+       - Debugging
+
+DPAA Ethernet Overview
+======================
+
+DPAA stands for Data Path Acceleration Architecture and it is a
+set of networking acceleration IPs that are available on several
+generations of SoCs, both on PowerPC and ARM64.
+
+The Freescale DPAA architecture consists of a series of hardware blocks
+that support Ethernet connectivity. The Ethernet driver depends upon the
+following drivers in the Linux kernel:
+
+ - Peripheral Access Memory Unit (PAMU) (* needed only for PPC platforms)
+    drivers/iommu/fsl_*
+ - Frame Manager (FMan)
+    drivers/net/ethernet/freescale/fman
+ - Queue Manager (QMan), Buffer Manager (BMan)
+    drivers/soc/fsl/qbman
+
+A simplified view of the dpaa_eth interfaces mapped to FMan MACs:
+
+  dpaa_eth       /eth0\     ...       /ethN\
+  driver        |      |             |      |
+  -------------   ----   -----------   ----   -------------
+       -Ports  / Tx  Rx \    ...    / Tx  Rx \
+  FMan        |          |         |          |
+       -MACs  |   MAC0   |         |   MACN   |
+             /   dtsec0   \  ...  /   dtsecN   \ (or tgec)
+            /              \     /              \(or memac)
+  ---------  --------------  ---  --------------  ---------
+      FMan, FMan Port, FMan SP, FMan MURAM drivers
+  ---------------------------------------------------------
+      FMan HW blocks: MURAM, MACs, Ports, SP
+  ---------------------------------------------------------
+
+The dpaa_eth relation to the QMan, BMan and FMan:
+              ________________________________
+  dpaa_eth   /            eth0                \
+  driver    /                                  \
+  ---------   -^-   -^-   -^-   ---    ---------
+  QMan driver / \   / \   / \  \   /  | BMan    |
+             |Rx | |Rx | |Tx | |Tx |  | driver  |
+  ---------  |Dfl| |Err| |Cnf| |FQs|  |         |
+  QMan HW    |FQ | |FQ | |FQs| |   |  |         |
+             /   \ /   \ /   \  \ /   |         |
+  ---------   ---   ---   ---   -v-    ---------
+            |        FMan QMI         |         |
+            | FMan HW       FMan BMI  | BMan HW |
+              -----------------------   --------
+
+where the acronyms used above (and in the code) are:
+DPAA = Data Path Acceleration Architecture
+FMan = DPAA Frame Manager
+QMan = DPAA Queue Manager
+BMan = DPAA Buffers Manager
+QMI = QMan interface in FMan
+BMI = BMan interface in FMan
+FMan SP = FMan Storage Profiles
+MURAM = Multi-user RAM in FMan
+FQ = QMan Frame Queue
+Rx Dfl FQ = default reception FQ
+Rx Err FQ = Rx error frames FQ
+Tx Cnf FQ = Tx confirmation FQs
+Tx FQs = transmission frame queues
+dtsec = datapath three speed Ethernet controller (10/100/1000 Mbps)
+tgec = ten gigabit Ethernet controller (10 Gbps)
+memac = multirate Ethernet MAC (10/100/1000/10000)
+
+DPAA Ethernet Supported SoCs
+============================
+
+The DPAA drivers enable the Ethernet controllers present on the following SoCs:
+
+# PPC
+P1023
+P2041
+P3041
+P4080
+P5020
+P5040
+T1023
+T1024
+T1040
+T1042
+T2080
+T4240
+B4860
+
+# ARM
+LS1043A
+LS1046A
+
+Configuring DPAA Ethernet in your kernel
+========================================
+
+To enable the DPAA Ethernet driver, the following Kconfig options are required:
+
+# common for arch/arm64 and arch/powerpc platforms
+CONFIG_FSL_DPAA=y
+CONFIG_FSL_FMAN=y
+CONFIG_FSL_DPAA_ETH=y
+CONFIG_FSL_XGMAC_MDIO=y
+
+# for arch/powerpc only
+CONFIG_FSL_PAMU=y
+
+# common options needed for the PHYs used on the RDBs
+CONFIG_VITESSE_PHY=y
+CONFIG_REALTEK_PHY=y
+CONFIG_AQUANTIA_PHY=y
+
+DPAA Ethernet Frame Processing
+==============================
+
+On Rx, buffers for the incoming frames are retrieved from one of the three
+existing buffers pools. The driver initializes and seeds these, each with
+buffers of different sizes: 1KB, 2KB and 4KB.
+
+On Tx, all transmitted frames are returned to the driver through Tx
+confirmation frame queues. The driver is then responsible for freeing the
+buffers. In order to do this properly, a backpointer is added to the buffer
+before transmission that points to the skb. When the buffer returns to the
+driver on a confirmation FQ, the skb can be correctly consumed.
+
+DPAA Ethernet Features
+======================
+
+Currently the DPAA Ethernet driver enables the basic features required for
+a Linux Ethernet driver. The support for advanced features will be added
+gradually.
+
+The driver has Rx and Tx checksum offloading for UDP and TCP. Currently the Rx
+checksum offload feature is enabled by default and cannot be controlled through
+ethtool. Also, rx-flow-hash and rx-hashing was added. The addition of RSS
+provides a big performance boost for the forwarding scenarios, allowing
+different traffic flows received by one interface to be processed by different
+CPUs in parallel.
+
+The driver has support for multiple prioritized Tx traffic classes. Priorities
+range from 0 (lowest) to 3 (highest). These are mapped to HW workqueues with
+strict priority levels. Each traffic class contains NR_CPU TX queues. By
+default, only one traffic class is enabled and the lowest priority Tx queues
+are used. Higher priority traffic classes can be enabled with the mqprio
+qdisc. For example, all four traffic classes are enabled on an interface with
+the following command. Furthermore, skb priority levels are mapped to traffic
+classes as follows:
+
+       * priorities 0 to 3 - traffic class 0 (low priority)
+       * priorities 4 to 7 - traffic class 1 (medium-low priority)
+       * priorities 8 to 11 - traffic class 2 (medium-high priority)
+       * priorities 12 to 15 - traffic class 3 (high priority)
+
+tc qdisc add dev <int> root handle 1: \
+        mqprio num_tc 4 map 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 hw 1
+
+DPAA IRQ Affinity and Receive Side Scaling
+==========================================
+
+Traffic coming on the DPAA Rx queues or on the DPAA Tx confirmation
+queues is seen by the CPU as ingress traffic on a certain portal.
+The DPAA QMan portal interrupts are affined each to a certain CPU.
+The same portal interrupt services all the QMan portal consumers.
+
+By default the DPAA Ethernet driver enables RSS, making use of the
+DPAA FMan Parser and Keygen blocks to distribute traffic on 128
+hardware frame queues using a hash on IP v4/v6 source and destination
+and L4 source and destination ports, in present in the received frame.
+When RSS is disabled, all traffic received by a certain interface is
+received on the default Rx frame queue. The default DPAA Rx frame
+queues are configured to put the received traffic into a pool channel
+that allows any available CPU portal to dequeue the ingress traffic.
+The default frame queues have the HOLDACTIVE option set, ensuring that
+traffic bursts from a certain queue are serviced by the same CPU.
+This ensures a very low rate of frame reordering. A drawback of this
+is that only one CPU at a time can service the traffic received by a
+certain interface when RSS is not enabled.
+
+To implement RSS, the DPAA Ethernet driver allocates an extra set of
+128 Rx frame queues that are configured to dedicated channels, in a
+round-robin manner. The mapping of the frame queues to CPUs is now
+hardcoded, there is no indirection table to move traffic for a certain
+FQ (hash result) to another CPU. The ingress traffic arriving on one
+of these frame queues will arrive at the same portal and will always
+be processed by the same CPU. This ensures intra-flow order preservation
+and workload distribution for multiple traffic flows.
+
+RSS can be turned off for a certain interface using ethtool, i.e.
+
+       # ethtool -N fm1-mac9 rx-flow-hash tcp4 ""
+
+To turn it back on, one needs to set rx-flow-hash for tcp4/6 or udp4/6:
+
+       # ethtool -N fm1-mac9 rx-flow-hash udp4 sfdn
+
+There is no independent control for individual protocols, any command
+run for one of tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6 is
+going to control the rx-flow-hashing for all protocols on that interface.
+
+Besides using the FMan Keygen computed hash for spreading traffic on the
+128 Rx FQs, the DPAA Ethernet driver also sets the skb hash value when
+the NETIF_F_RXHASH feature is on (active by default). This can be turned
+on or off through ethtool, i.e.:
+
+       # ethtool -K fm1-mac9 rx-hashing off
+       # ethtool -k fm1-mac9 | grep hash
+       receive-hashing: off
+       # ethtool -K fm1-mac9 rx-hashing on
+       Actual changes:
+       receive-hashing: on
+       # ethtool -k fm1-mac9 | grep hash
+       receive-hashing: on
+
+Please note that Rx hashing depends upon the rx-flow-hashing being on
+for that interface - turning off rx-flow-hashing will also disable the
+rx-hashing (without ethtool reporting it as off as that depends on the
+NETIF_F_RXHASH feature flag).
+
+Debugging
+=========
+
+The following statistics are exported for each interface through ethtool:
+
+       - interrupt count per CPU
+       - Rx packets count per CPU
+       - Tx packets count per CPU
+       - Tx confirmed packets count per CPU
+       - Tx S/G frames count per CPU
+       - Tx error count per CPU
+       - Rx error count per CPU
+       - Rx error count per type
+       - congestion related statistics:
+               - congestion status
+               - time spent in congestion
+               - number of time the device entered congestion
+               - dropped packets count per cause
+
+The driver also exports the following information in sysfs:
+
+       - the FQ IDs for each FQ type
+       /sys/devices/platform/dpaa-ethernet.0/net/<int>/fqids
+
+       - the IDs of the buffer pools in use
+       /sys/devices/platform/dpaa-ethernet.0/net/<int>/bpids
diff --git a/Documentation/networking/device_drivers/freescale/dpaa2/dpio-driver.rst b/Documentation/networking/device_drivers/freescale/dpaa2/dpio-driver.rst
new file mode 100644 (file)
index 0000000..a188466
--- /dev/null
@@ -0,0 +1,158 @@
+.. include:: <isonum.txt>
+
+DPAA2 DPIO (Data Path I/O) Overview
+===================================
+
+:Copyright: |copy| 2016-2018 NXP
+
+This document provides an overview of the Freescale DPAA2 DPIO
+drivers
+
+Introduction
+============
+
+A DPAA2 DPIO (Data Path I/O) is a hardware object that provides
+interfaces to enqueue and dequeue frames to/from network interfaces
+and other accelerators.  A DPIO also provides hardware buffer
+pool management for network interfaces.
+
+This document provides an overview the Linux DPIO driver, its
+subcomponents, and its APIs.
+
+See Documentation/networking/device_drivers/freescale/dpaa2/overview.rst for
+a general overview of DPAA2 and the general DPAA2 driver architecture in Linux.
+
+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
+     frames for their respective objects
+  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
+
+The Linux DPIO driver consists of 3 primary components--
+   DPIO object driver-- fsl-mc driver that manages the DPIO object
+
+   DPIO service-- provides APIs to other Linux drivers for services
+
+   QBman portal interface-- sends portal commands, gets responses
+::
+
+          fsl-mc          other
+           bus           drivers
+            |               |
+        +---+----+   +------+-----+
+        |DPIO obj|   |DPIO service|
+        | driver |---|  (DPIO)    |
+        +--------+   +------+-----+
+                            |
+                     +------+-----+
+                     |    QBman   |
+                     | portal i/f |
+                     +------------+
+                            |
+                         hardware
+
+
+The diagram below shows how the DPIO driver components fit with the other
+DPAA2 Linux driver components::
+                                                   +------------+
+                                                   | OS Network |
+                                                   |   Stack    |
+                 +------------+                    +------------+
+                 | Allocator  |. . . . . . .       |  Ethernet  |
+                 |(DPMCP,DPBP)|                    |   (DPNI)   |
+                 +-.----------+                    +---+---+----+
+                  .          .                         ^   |
+                 .            .           <data avail, |   |<enqueue,
+                .              .           tx confirm> |   | dequeue>
+    +-------------+             .                      |   |
+    | DPRC driver |              .    +--------+ +------------+
+    |   (DPRC)    |               . . |DPIO obj| |DPIO service|
+    +----------+--+                   | driver |-|  (DPIO)    |
+               |                      +--------+ +------+-----+
+               |<dev add/remove>                 +------|-----+
+               |                                 |   QBman    |
+          +----+--------------+                  | portal i/f |
+          |   MC-bus driver   |                  +------------+
+          |                   |                     |
+          | /soc/fsl-mc       |                     |
+          +-------------------+                     |
+                                                    |
+ =========================================|=========|========================
+                                        +-+--DPIO---|-----------+
+                                        |           |           |
+                                        |        QBman Portal   |
+                                        +-----------------------+
+
+ ============================================================================
+
+
+DPIO Object Driver (dpio-driver.c)
+----------------------------------
+
+   The dpio-driver component registers with the fsl-mc bus to handle objects of
+   type "dpio".  The implementation of probe() handles basic initialization
+   of the DPIO including mapping of the DPIO regions (the QBman SW portal)
+   and initializing interrupts and registering irq handlers.  The dpio-driver
+   registers the probed DPIO with dpio-service.
+
+DPIO service  (dpio-service.c, dpaa2-io.h)
+------------------------------------------
+
+   The dpio service component provides queuing, notification, and buffers
+   management services to DPAA2 drivers, such as the Ethernet driver.  A system
+   will typically allocate 1 DPIO object per CPU to allow queuing operations
+   to happen simultaneously across all CPUs.
+
+   Notification handling
+      dpaa2_io_service_register()
+
+      dpaa2_io_service_deregister()
+
+      dpaa2_io_service_rearm()
+
+   Queuing
+      dpaa2_io_service_pull_fq()
+
+      dpaa2_io_service_pull_channel()
+
+      dpaa2_io_service_enqueue_fq()
+
+      dpaa2_io_service_enqueue_qd()
+
+      dpaa2_io_store_create()
+
+      dpaa2_io_store_destroy()
+
+      dpaa2_io_store_next()
+
+   Buffer pool management
+      dpaa2_io_service_release()
+
+      dpaa2_io_service_acquire()
+
+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
+
+   The qbman-portal APIs are not public to other drivers, and are
+   only used by dpio-service.
+
+Other (dpaa2-fd.h, dpaa2-global.h)
+----------------------------------
+
+   Frame descriptor and scatter-gather definitions and the APIs used to
+   manipulate them are defined in dpaa2-fd.h.
+
+   Dequeue result struct and parsing APIs are defined in dpaa2-global.h.
diff --git a/Documentation/networking/device_drivers/freescale/dpaa2/ethernet-driver.rst b/Documentation/networking/device_drivers/freescale/dpaa2/ethernet-driver.rst
new file mode 100644 (file)
index 0000000..cb4c9a0
--- /dev/null
@@ -0,0 +1,185 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+===============================
+DPAA2 Ethernet driver
+===============================
+
+:Copyright: |copy| 2017-2018 NXP
+
+This file provides documentation for the Freescale DPAA2 Ethernet driver.
+
+Supported Platforms
+===================
+This driver provides networking support for Freescale DPAA2 SoCs, e.g.
+LS2080A, LS2088A, LS1088A.
+
+
+Architecture Overview
+=====================
+Unlike regular NICs, in the DPAA2 architecture there is no single hardware block
+representing network interfaces; instead, several separate hardware resources
+concur to provide the networking functionality:
+
+- network interfaces
+- queues, channels
+- buffer pools
+- MAC/PHY
+
+All hardware resources are allocated and configured through the Management
+Complex (MC) portals. MC abstracts most of these resources as DPAA2 objects
+and exposes ABIs through which they can be configured and controlled. A few
+hardware resources, like queues, do not have a corresponding MC object and
+are treated as internal resources of other objects.
+
+For a more detailed description of the DPAA2 architecture and its object
+abstractions see *Documentation/networking/device_drivers/freescale/dpaa2/overview.rst*.
+
+Each Linux net device is built on top of a Datapath Network Interface (DPNI)
+object and uses Buffer Pools (DPBPs), I/O Portals (DPIOs) and Concentrators
+(DPCONs).
+
+Configuration interface::
+
+                 -----------------------
+                | DPAA2 Ethernet Driver |
+                 -----------------------
+                     .      .      .
+                     .      .      .
+             . . . . .      .      . . . . . .
+             .              .                .
+             .              .                .
+         ----------     ----------      -----------
+        | DPBP API |   | DPNI API |    | DPCON API |
+         ----------     ----------      -----------
+             .              .                .             software
+    =======  .  ==========  .  ============  .  ===================
+             .              .                .             hardware
+         ------------------------------------------
+        |            MC hardware portals           |
+         ------------------------------------------
+             .              .                .
+             .              .                .
+          ------         ------            -------
+         | DPBP |       | DPNI |          | DPCON |
+          ------         ------            -------
+
+The DPNIs are network interfaces without a direct one-on-one mapping to PHYs.
+DPBPs represent hardware buffer pools. Packet I/O is performed in the context
+of DPCON objects, using DPIO portals for managing and communicating with the
+hardware resources.
+
+Datapath (I/O) interface::
+
+         -----------------------------------------------
+        |           DPAA2 Ethernet Driver               |
+         -----------------------------------------------
+          |          ^        ^         |            |
+          |          |        |         |            |
+   enqueue|   dequeue|   data |  dequeue|       seed |
+    (Tx)  | (Rx, TxC)|  avail.|  request|     buffers|
+          |          |  notify|         |            |
+          |          |        |         |            |
+          V          |        |         V            V
+         -----------------------------------------------
+        |                 DPIO Driver                   |
+         -----------------------------------------------
+          |          |        |         |            |          software
+          |          |        |         |            |  ================
+          |          |        |         |            |          hardware
+         -----------------------------------------------
+        |               I/O hardware portals            |
+         -----------------------------------------------
+          |          ^        ^         |            |
+          |          |        |         |            |
+          |          |        |         V            |
+          V          |    ================           V
+        ----------------------           |      -------------
+ queues  ----------------------          |     | Buffer pool |
+          ----------------------         |      -------------
+                   =======================
+                                Channel
+
+Datapath I/O (DPIO) portals provide enqueue and dequeue services, data
+availability notifications and buffer pool management. DPIOs are shared between
+all DPAA2 objects (and implicitly all DPAA2 kernel drivers) that work with data
+frames, but must be affine to the CPUs for the purpose of traffic distribution.
+
+Frames are transmitted and received through hardware frame queues, which can be
+grouped in channels for the purpose of hardware scheduling. The Ethernet driver
+enqueues TX frames on egress queues and after transmission is complete a TX
+confirmation frame is sent back to the CPU.
+
+When frames are available on ingress queues, a data availability notification
+is sent to the CPU; notifications are raised per channel, so even if multiple
+queues in the same channel have available frames, only one notification is sent.
+After a channel fires a notification, is must be explicitly rearmed.
+
+Each network interface can have multiple Rx, Tx and confirmation queues affined
+to CPUs, and one channel (DPCON) for each CPU that services at least one queue.
+DPCONs are used to distribute ingress traffic to different CPUs via the cores'
+affine DPIOs.
+
+The role of hardware buffer pools is storage of ingress frame data. Each network
+interface has a privately owned buffer pool which it seeds with kernel allocated
+buffers.
+
+
+DPNIs are decoupled from PHYs; a DPNI can be connected to a PHY through a DPMAC
+object or to another DPNI through an internal link, but the connection is
+managed by MC and completely transparent to the Ethernet driver.
+
+::
+
+     ---------     ---------     ---------
+    | eth if1 |   | eth if2 |   | eth ifn |
+     ---------     ---------     ---------
+          .           .          .
+          .           .          .
+          .           .          .
+         ---------------------------
+        |   DPAA2 Ethernet Driver   |
+         ---------------------------
+          .           .          .
+          .           .          .
+          .           .          .
+       ------      ------      ------            -------
+      | DPNI |    | DPNI |    | DPNI |          | DPMAC |----+
+       ------      ------      ------            -------     |
+         |           |           |                  |        |
+         |           |           |                  |      -----
+          ===========             ==================      | PHY |
+                                                           -----
+
+Creating a Network Interface
+============================
+A net device is created for each DPNI object probed on the MC bus. Each DPNI has
+a number of properties which determine the network interface configuration
+options and associated hardware resources.
+
+DPNI objects (and the other DPAA2 objects needed for a network interface) can be
+added to a container on the MC bus in one of two ways: statically, through a
+Datapath Layout Binary file (DPL) that is parsed by MC at boot time; or created
+dynamically at runtime, via the DPAA2 objects APIs.
+
+
+Features & Offloads
+===================
+Hardware checksum offloading is supported for TCP and UDP over IPv4/6 frames.
+The checksum offloads can be independently configured on RX and TX through
+ethtool.
+
+Hardware offload of unicast and multicast MAC filtering is supported on the
+ingress path and permanently enabled.
+
+Scatter-gather frames are supported on both RX and TX paths. On TX, SG support
+is configurable via ethtool; on RX it is always enabled.
+
+The DPAA2 hardware can process jumbo Ethernet frames of up to 10K bytes.
+
+The Ethernet driver defines a static flow hashing scheme that distributes
+traffic based on a 5-tuple key: src IP, dst IP, IP proto, L4 src port,
+L4 dst port. No user configuration is supported for now.
+
+Hardware specific statistics for the network interface as well as some
+non-standard driver stats can be consulted through ethtool -S option.
diff --git a/Documentation/networking/device_drivers/freescale/dpaa2/index.rst b/Documentation/networking/device_drivers/freescale/dpaa2/index.rst
new file mode 100644 (file)
index 0000000..67bd87f
--- /dev/null
@@ -0,0 +1,10 @@
+===================
+DPAA2 Documentation
+===================
+
+.. toctree::
+   :maxdepth: 1
+
+   overview
+   dpio-driver
+   ethernet-driver
diff --git a/Documentation/networking/device_drivers/freescale/dpaa2/overview.rst b/Documentation/networking/device_drivers/freescale/dpaa2/overview.rst
new file mode 100644 (file)
index 0000000..d638b5a
--- /dev/null
@@ -0,0 +1,405 @@
+.. include:: <isonum.txt>
+
+=========================================================
+DPAA2 (Data Path Acceleration Architecture Gen2) Overview
+=========================================================
+
+:Copyright: |copy| 2015 Freescale Semiconductor Inc.
+:Copyright: |copy| 2018 NXP
+
+This document provides an overview of the Freescale DPAA2 architecture
+and how it is integrated into the Linux kernel.
+
+Introduction
+============
+
+DPAA2 is a hardware architecture designed for high-speeed network
+packet processing.  DPAA2 consists of sophisticated mechanisms for
+processing Ethernet packets, queue management, buffer management,
+autonomous L2 switching, virtual Ethernet bridging, and accelerator
+(e.g. crypto) sharing.
+
+A DPAA2 hardware component called the Management Complex (or MC) manages the
+DPAA2 hardware resources.  The MC provides an object-based abstraction for
+software drivers to use the DPAA2 hardware.
+The MC uses DPAA2 hardware resources such as queues, buffer pools, and
+network ports to create functional objects/devices such as network
+interfaces, an L2 switch, or accelerator instances.
+The MC provides memory-mapped I/O command interfaces (MC portals)
+which DPAA2 software drivers use to operate on DPAA2 objects.
+
+The diagram below shows an overview of the DPAA2 resource management
+architecture::
+
+       +--------------------------------------+
+       |                  OS                  |
+       |                        DPAA2 drivers |
+       |                             |        |
+       +-----------------------------|--------+
+                                     |
+                                     | (create,discover,connect
+                                     |  config,use,destroy)
+                                     |
+                        DPAA2        |
+       +------------------------| mc portal |-+
+       |                             |        |
+       |   +- - - - - - - - - - - - -V- - -+  |
+       |   |                               |  |
+       |   |   Management Complex (MC)     |  |
+       |   |                               |  |
+       |   +- - - - - - - - - - - - - - - -+  |
+       |                                      |
+       | Hardware                  Hardware   |
+       | Resources                 Objects    |
+       | ---------                 -------    |
+       | -queues                   -DPRC      |
+       | -buffer pools             -DPMCP     |
+       | -Eth MACs/ports           -DPIO      |
+       | -network interface        -DPNI      |
+       |  profiles                 -DPMAC     |
+       | -queue portals            -DPBP      |
+       | -MC portals                ...       |
+       |  ...                                 |
+       |                                      |
+       +--------------------------------------+
+
+
+The MC mediates operations such as create, discover,
+connect, configuration, and destroy.  Fast-path operations
+on data, such as packet transmit/receive, are not mediated by
+the MC and are done directly using memory mapped regions in
+DPIO objects.
+
+Overview of DPAA2 Objects
+=========================
+
+The section provides a brief overview of some key DPAA2 objects.
+A simple scenario is described illustrating the objects involved
+in creating a network interfaces.
+
+DPRC (Datapath Resource Container)
+----------------------------------
+
+A DPRC is a container object that holds all the other
+types of DPAA2 objects.  In the example diagram below there
+are 8 objects of 5 types (DPMCP, DPIO, DPBP, DPNI, and DPMAC)
+in the container.
+
+::
+
+       +---------------------------------------------------------+
+       | DPRC                                                    |
+       |                                                         |
+       |  +-------+  +-------+  +-------+  +-------+  +-------+  |
+       |  | DPMCP |  | DPIO  |  | DPBP  |  | DPNI  |  | DPMAC |  |
+       |  +-------+  +-------+  +-------+  +---+---+  +---+---+  |
+       |  | DPMCP |  | DPIO  |                                   |
+       |  +-------+  +-------+                                   |
+       |  | DPMCP |                                              |
+       |  +-------+                                              |
+       |                                                         |
+       +---------------------------------------------------------+
+
+From the point of view of an OS, a DPRC behaves similar to a plug and
+play bus, like PCI.  DPRC commands can be used to enumerate the contents
+of the DPRC, discover the hardware objects present (including mappable
+regions and interrupts).
+
+::
+
+       DPRC.1 (bus)
+          |
+          +--+--------+-------+-------+-------+
+             |        |       |       |       |
+           DPMCP.1  DPIO.1  DPBP.1  DPNI.1  DPMAC.1
+           DPMCP.2  DPIO.2
+           DPMCP.3
+
+Hardware objects can be created and destroyed dynamically, providing
+the ability to hot plug/unplug objects in and out of the DPRC.
+
+A DPRC has a mappable MMIO region (an MC portal) that can be used
+to send MC commands.  It has an interrupt for status events (like
+hotplug).
+All objects in a container share the same hardware "isolation context".
+This means that with respect to an IOMMU the isolation granularity
+is at the DPRC (container) level, not at the individual object
+level.
+
+DPRCs can be defined statically and populated with objects
+via a config file passed to the MC when firmware starts it.
+
+DPAA2 Objects for an Ethernet Network Interface
+-----------------------------------------------
+
+A typical Ethernet NIC is monolithic-- the NIC device contains TX/RX
+queuing mechanisms, configuration mechanisms, buffer management,
+physical ports, and interrupts.  DPAA2 uses a more granular approach
+utilizing multiple hardware objects.  Each object provides specialized
+functions. Groups of these objects are used by software to provide
+Ethernet network interface functionality.  This approach provides
+efficient use of finite hardware resources, flexibility, and
+performance advantages.
+
+The diagram below shows the objects needed for a simple
+network interface configuration on a system with 2 CPUs.
+
+::
+
+       +---+---+ +---+---+
+          CPU0     CPU1
+       +---+---+ +---+---+
+           |         |
+       +---+---+ +---+---+
+          DPIO     DPIO
+       +---+---+ +---+---+
+           \     /
+            \   /
+             \ /
+          +---+---+
+             DPNI  --- DPBP,DPMCP
+          +---+---+
+              |
+              |
+          +---+---+
+            DPMAC
+          +---+---+
+              |
+          port/PHY
+
+Below the objects are described.  For each object a brief description
+is provided along with a summary of the kinds of operations the object
+supports and a summary of key resources of the object (MMIO regions
+and IRQs).
+
+DPMAC (Datapath Ethernet MAC)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Represents an Ethernet MAC, a hardware device that connects to an Ethernet
+PHY and allows physical transmission and reception of Ethernet frames.
+
+- MMIO regions: none
+- IRQs: DPNI link change
+- commands: set link up/down, link config, get stats,
+  IRQ config, enable, reset
+
+DPNI (Datapath Network Interface)
+Contains TX/RX queues, network interface configuration, and RX buffer pool
+configuration mechanisms.  The TX/RX queues are in memory and are identified
+by queue number.
+
+- MMIO regions: none
+- IRQs: link state
+- commands: port config, offload config, queue config,
+  parse/classify config, IRQ config, enable, reset
+
+DPIO (Datapath I/O)
+~~~~~~~~~~~~~~~~~~~
+Provides interfaces to enqueue and dequeue
+packets and do hardware buffer pool management operations.  The DPAA2
+architecture separates the mechanism to access queues (the DPIO object)
+from the queues themselves.  The DPIO provides an MMIO interface to
+enqueue/dequeue packets.  To enqueue something a descriptor is written
+to the DPIO MMIO region, which includes the target queue number.
+There will typically be one DPIO assigned to each CPU.  This allows all
+CPUs to simultaneously perform enqueue/dequeued operations.  DPIOs are
+expected to be shared by different DPAA2 drivers.
+
+- MMIO regions: queue operations, buffer management
+- IRQs: data availability, congestion notification, buffer
+  pool depletion
+- commands: IRQ config, enable, reset
+
+DPBP (Datapath Buffer Pool)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Represents a hardware buffer pool.
+
+- MMIO regions: none
+- IRQs: none
+- commands: enable, reset
+
+DPMCP (Datapath MC Portal)
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+Provides an MC command portal.
+Used by drivers to send commands to the MC to manage
+objects.
+
+- MMIO regions: MC command portal
+- IRQs: command completion
+- commands: IRQ config, enable, reset
+
+Object Connections
+==================
+Some objects have explicit relationships that must
+be configured:
+
+- DPNI <--> DPMAC
+- DPNI <--> DPNI
+- DPNI <--> L2-switch-port
+
+    A DPNI must be connected to something such as a DPMAC,
+    another DPNI, or L2 switch port.  The DPNI connection
+    is made via a DPRC command.
+
+::
+
+              +-------+  +-------+
+              | DPNI  |  | DPMAC |
+              +---+---+  +---+---+
+                  |          |
+                  +==========+
+
+- DPNI <--> DPBP
+
+    A network interface requires a 'buffer pool' (DPBP
+    object) which provides a list of pointers to memory
+    where received Ethernet data is to be copied.  The
+    Ethernet driver configures the DPBPs associated with
+    the network interface.
+
+Interrupts
+==========
+All interrupts generated by DPAA2 objects are message
+interrupts.  At the hardware level message interrupts
+generated by devices will normally have 3 components--
+1) a non-spoofable 'device-id' expressed on the hardware
+bus, 2) an address, 3) a data value.
+
+In the case of DPAA2 devices/objects, all objects in the
+same container/DPRC share the same 'device-id'.
+For ARM-based SoC this is the same as the stream ID.
+
+
+DPAA2 Linux Drivers Overview
+============================
+
+This section provides an overview of the Linux kernel drivers for
+DPAA2-- 1) the bus driver and associated "DPAA2 infrastructure"
+drivers and 2) functional object drivers (such as Ethernet).
+
+As described previously, a DPRC is a container that holds the other
+types of DPAA2 objects.  It is functionally similar to a plug-and-play
+bus controller.
+Each object in the DPRC is a Linux "device" and is bound to a driver.
+The diagram below shows the Linux drivers involved in a networking
+scenario and the objects bound to each driver.  A brief description
+of each driver follows.
+
+::
+
+                                            +------------+
+                                            | OS Network |
+                                            |   Stack    |
+                +------------+              +------------+
+                | Allocator  |. . . . . . . |  Ethernet  |
+                |(DPMCP,DPBP)|              |   (DPNI)   |
+                +-.----------+              +---+---+----+
+                 .          .                   ^   |
+                .            .     <data avail, |   | <enqueue,
+               .              .     tx confirm> |   | dequeue>
+       +-------------+         .                |   |
+       | DPRC driver |          .           +---+---V----+     +---------+
+       |   (DPRC)    |           . . . . . .| DPIO driver|     |   MAC   |
+       +----------+--+                      |  (DPIO)    |     | (DPMAC) |
+                  |                         +------+-----+     +-----+---+
+                  |<dev add/remove>                |                 |
+                  |                                |                 |
+         +--------+----------+                     |              +--+---+
+         |   MC-bus driver   |                     |              | PHY  |
+         |                   |                     |              |driver|
+         |   /bus/fsl-mc     |                     |              +--+---+
+         +-------------------+                     |                 |
+                                                   |                 |
+       ========================= HARDWARE =========|=================|======
+                                                 DPIO                |
+                                                   |                 |
+                                                 DPNI---DPBP         |
+                                                   |                 |
+                                                 DPMAC               |
+                                                   |                 |
+                                                  PHY ---------------+
+       ============================================|========================
+
+A brief description of each driver is provided below.
+
+MC-bus driver
+-------------
+The MC-bus driver is a platform driver and is probed from a
+node in the device tree (compatible "fsl,qoriq-mc") passed in by boot
+firmware.  It is responsible for bootstrapping the DPAA2 kernel
+infrastructure.
+Key functions include:
+
+- registering a new bus type named "fsl-mc" with the kernel,
+  and implementing bus call-backs (e.g. match/uevent/dev_groups)
+- implementing APIs for DPAA2 driver registration and for device
+  add/remove
+- creates an MSI IRQ domain
+- doing a 'device add' to expose the 'root' DPRC, in turn triggering
+  a bind of the root DPRC to the DPRC driver
+
+The binding for the MC-bus device-tree node can be consulted at
+*Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt*.
+The sysfs bind/unbind interfaces for the MC-bus can be consulted at
+*Documentation/ABI/testing/sysfs-bus-fsl-mc*.
+
+DPRC driver
+-----------
+The DPRC driver is bound to DPRC objects and does runtime management
+of a bus instance.  It performs the initial bus scan of the DPRC
+and handles interrupts for container events such as hot plug by
+re-scanning the DPRC.
+
+Allocator
+---------
+Certain objects such as DPMCP and DPBP are generic and fungible,
+and are intended to be used by other drivers.  For example,
+the DPAA2 Ethernet driver needs:
+
+- DPMCPs to send MC commands, to configure network interfaces
+- DPBPs for network buffer pools
+
+The allocator driver registers for these allocatable object types
+and those objects are bound to the allocator when the bus is probed.
+The allocator maintains a pool of objects that are available for
+allocation by other DPAA2 drivers.
+
+DPIO driver
+-----------
+The DPIO driver is bound to DPIO objects and provides services that allow
+other drivers such as the Ethernet driver to enqueue and dequeue data for
+their respective objects.
+Key services include:
+
+- data availability notifications
+- hardware queuing operations (enqueue and dequeue of data)
+- hardware buffer pool management
+
+To transmit a packet the Ethernet driver puts data on a queue and
+invokes a DPIO API.  For receive, the Ethernet driver registers
+a data availability notification callback.  To dequeue a packet
+a DPIO API is used.
+There is typically one DPIO object per physical CPU for optimum
+performance, allowing different CPUs to simultaneously enqueue
+and dequeue data.
+
+The DPIO driver operates on behalf of all DPAA2 drivers
+active in the kernel--  Ethernet, crypto, compression,
+etc.
+
+Ethernet driver
+---------------
+The Ethernet driver is bound to a DPNI and implements the kernel
+interfaces needed to connect the DPAA2 network interface to
+the network stack.
+Each DPNI corresponds to a Linux network interface.
+
+MAC driver
+----------
+An Ethernet PHY is an off-chip, board specific component and is managed
+by the appropriate PHY driver via an mdio bus.  The MAC driver
+plays a role of being a proxy between the PHY driver and the
+MC.  It does this proxy via the MC commands to a DPMAC object.
+If the PHY driver signals a link change, the MAC driver notifies
+the MC via a DPMAC command.  If a network interface is brought
+up or down, the MC notifies the DPMAC driver via an interrupt and
+the driver can take appropriate action.
diff --git a/Documentation/networking/device_drivers/freescale/gianfar.txt b/Documentation/networking/device_drivers/freescale/gianfar.txt
new file mode 100644 (file)
index 0000000..ba1daea
--- /dev/null
@@ -0,0 +1,42 @@
+The Gianfar Ethernet Driver
+
+Author: Andy Fleming <afleming@freescale.com>
+Updated: 2005-07-28
+
+
+CHECKSUM OFFLOADING
+
+The eTSEC controller (first included in parts from late 2005 like
+the 8548) has the ability to perform TCP, UDP, and IP checksums
+in hardware.  The Linux kernel only offloads the TCP and UDP
+checksums (and always performs the pseudo header checksums), so
+the driver only supports checksumming for TCP/IP and UDP/IP
+packets.  Use ethtool to enable or disable this feature for RX
+and TX.
+
+VLAN
+
+In order to use VLAN, please consult Linux documentation on
+configuring VLANs.  The gianfar driver supports hardware insertion and
+extraction of VLAN headers, but not filtering.  Filtering will be
+done by the kernel.
+
+MULTICASTING
+
+The gianfar driver supports using the group hash table on the
+TSEC (and the extended hash table on the eTSEC) for multicast
+filtering.  On the eTSEC, the exact-match MAC registers are used
+before the hash tables.  See Linux documentation on how to join
+multicast groups.
+
+PADDING
+
+The gianfar driver supports padding received frames with 2 bytes
+to align the IP header to a 16-byte boundary, when supported by
+hardware.
+
+ETHTOOL
+
+The gianfar driver supports the use of ethtool for many
+configuration options.  You must run ethtool only on currently
+open interfaces.  See ethtool documentation for details.
diff --git a/Documentation/networking/device_drivers/intel/e100.rst b/Documentation/networking/device_drivers/intel/e100.rst
new file mode 100644 (file)
index 0000000..5e2839b
--- /dev/null
@@ -0,0 +1,187 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Base Driver for the Intel(R) PRO/100 Family of Adapters
+==============================================================
+
+June 1, 2018
+
+Contents
+========
+
+- In This Release
+- Identifying Your Adapter
+- Building and Installation
+- Driver Configuration Parameters
+- Additional Configurations
+- Known Issues
+- Support
+
+
+In This Release
+===============
+
+This file describes the Linux* Base Driver for the Intel(R) PRO/100 Family of
+Adapters. This driver includes support for Itanium(R)2-based systems.
+
+For questions related to hardware requirements, refer to the documentation
+supplied with your Intel PRO/100 adapter.
+
+The following features are now available in supported kernels:
+ - Native VLANs
+ - Channel Bonding (teaming)
+ - SNMP
+
+Channel Bonding documentation can be found in the Linux kernel source:
+/Documentation/networking/bonding.txt
+
+
+Identifying Your Adapter
+========================
+
+For information on how to identify your adapter, and for the latest Intel
+network drivers, refer to the Intel Support website:
+http://www.intel.com/support
+
+Driver Configuration Parameters
+===============================
+
+The default value for each parameter is generally the recommended setting,
+unless otherwise noted.
+
+Rx Descriptors:
+   Number of receive descriptors. A receive descriptor is a data
+   structure that describes a receive buffer and its attributes to the network
+   controller. The data in the descriptor is used by the controller to write
+   data from the controller to host memory. In the 3.x.x driver the valid range
+   for this parameter is 64-256. The default value is 256. This parameter can be
+   changed using the command::
+
+     ethtool -G eth? rx n
+
+   Where n is the number of desired Rx descriptors.
+
+Tx Descriptors:
+   Number of transmit descriptors. A transmit descriptor is a data
+   structure that describes a transmit buffer and its attributes to the network
+   controller. The data in the descriptor is used by the controller to read
+   data from the host memory to the controller. In the 3.x.x driver the valid
+   range for this parameter is 64-256. The default value is 128. This parameter
+   can be changed using the command::
+
+     ethtool -G eth? tx n
+
+   Where n is the number of desired Tx descriptors.
+
+Speed/Duplex:
+   The driver auto-negotiates the link speed and duplex settings by
+   default. The ethtool utility can be used as follows to force speed/duplex.::
+
+     ethtool -s eth?  autoneg off speed {10|100} duplex {full|half}
+
+   NOTE: setting the speed/duplex to incorrect values will cause the link to
+   fail.
+
+Event Log Message Level:
+   The driver uses the message level flag to log events
+   to syslog. The message level can be set at driver load time. It can also be
+   set using the command::
+
+     ethtool -s eth? msglvl n
+
+
+Additional Configurations
+=========================
+
+Configuring the Driver on Different Distributions
+-------------------------------------------------
+
+Configuring a network driver to load properly when the system is started
+is distribution dependent.  Typically, the configuration process involves
+adding an alias line to `/etc/modprobe.d/*.conf` as well as editing other
+system startup scripts and/or configuration files.  Many popular Linux
+distributions ship with tools to make these changes for you.  To learn
+the proper way to configure a network device for your system, refer to
+your distribution documentation.  If during this process you are asked
+for the driver or module name, the name for the Linux Base Driver for
+the Intel PRO/100 Family of Adapters is e100.
+
+As an example, if you install the e100 driver for two PRO/100 adapters
+(eth0 and eth1), add the following to a configuration file in
+/etc/modprobe.d/::
+
+       alias eth0 e100
+       alias eth1 e100
+
+Viewing Link Messages
+---------------------
+
+In order to see link messages and other Intel driver information on your
+console, you must set the dmesg level up to six.  This can be done by
+entering the following on the command line before loading the e100
+driver::
+
+       dmesg -n 6
+
+If you wish to see all messages issued by the driver, including debug
+messages, set the dmesg level to eight.
+
+NOTE: This setting is not saved across reboots.
+
+ethtool
+-------
+
+The driver utilizes the ethtool interface for driver configuration and
+diagnostics, as well as displaying statistical information.  The ethtool
+version 1.6 or later is required for this functionality.
+
+The latest release of ethtool can be found from
+https://www.kernel.org/pub/software/network/ethtool/
+
+Enabling Wake on LAN* (WoL)
+---------------------------
+WoL is provided through the ethtool* utility.  For instructions on
+enabling WoL with ethtool, refer to the ethtool man page.  WoL will be
+enabled on the system during the next shut down or reboot.  For this
+driver version, in order to enable WoL, the e100 driver must be loaded
+when shutting down or rebooting the system.
+
+NAPI
+----
+
+NAPI (Rx polling mode) is supported in the e100 driver.
+
+See https://wiki.linuxfoundation.org/networking/napi for more
+information on NAPI.
+
+Multiple Interfaces on Same Ethernet Broadcast Network
+------------------------------------------------------
+
+Due to the default ARP behavior on Linux, it is not possible to have one
+system on two IP networks in the same Ethernet broadcast domain
+(non-partitioned switch) behave as expected.  All Ethernet interfaces
+will respond to IP traffic for any IP address assigned to the system.
+This results in unbalanced receive traffic.
+
+If you have multiple interfaces in a server, either turn on ARP
+filtering by
+
+(1) entering::
+
+       echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
+
+    (this only works if your kernel's version is higher than 2.4.5), or
+
+(2) installing the interfaces in separate broadcast domains (either
+    in different switches or in a switch partitioned to VLANs).
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+http://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+http://sourceforge.net/projects/e1000
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/device_drivers/intel/e1000.rst b/Documentation/networking/device_drivers/intel/e1000.rst
new file mode 100644 (file)
index 0000000..6379d4d
--- /dev/null
@@ -0,0 +1,462 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Base Driver for Intel(R) Ethernet Network Connection
+===========================================================
+
+Intel Gigabit Linux driver.
+Copyright(c) 1999 - 2013 Intel Corporation.
+
+Contents
+========
+
+- Identifying Your Adapter
+- Command Line Parameters
+- Speed and Duplex Configuration
+- Additional Configurations
+- Support
+
+Identifying Your Adapter
+========================
+
+For more information on how to identify your adapter, go to the Adapter &
+Driver ID Guide at:
+
+    http://support.intel.com/support/go/network/adapter/idguide.htm
+
+For the latest Intel network drivers for Linux, refer to the following
+website.  In the search field, enter your adapter name or type, or use the
+networking link on the left to search for your adapter:
+
+    http://support.intel.com/support/go/network/adapter/home.htm
+
+Command Line Parameters
+=======================
+
+The default value for each parameter is generally the recommended setting,
+unless otherwise noted.
+
+NOTES:
+       For more information about the AutoNeg, Duplex, and Speed
+        parameters, see the "Speed and Duplex Configuration" section in
+        this document.
+
+        For more information about the InterruptThrottleRate,
+        RxIntDelay, TxIntDelay, RxAbsIntDelay, and TxAbsIntDelay
+        parameters, see the application note at:
+        http://www.intel.com/design/network/applnots/ap450.htm
+
+AutoNeg
+-------
+
+(Supported only on adapters with copper connections)
+
+:Valid Range:   0x01-0x0F, 0x20-0x2F
+:Default Value: 0x2F
+
+This parameter is a bit-mask that specifies the speed and duplex settings
+advertised by the adapter.  When this parameter is used, the Speed and
+Duplex parameters must not be specified.
+
+NOTE:
+       Refer to the Speed and Duplex section of this readme for more
+       information on the AutoNeg parameter.
+
+Duplex
+------
+
+(Supported only on adapters with copper connections)
+
+:Valid Range:   0-2 (0=auto-negotiate, 1=half, 2=full)
+:Default Value: 0
+
+This defines the direction in which data is allowed to flow.  Can be
+either one or two-directional.  If both Duplex and the link partner are
+set to auto-negotiate, the board auto-detects the correct duplex.  If the
+link partner is forced (either full or half), Duplex defaults to half-
+duplex.
+
+FlowControl
+-----------
+
+:Valid Range:   0-3 (0=none, 1=Rx only, 2=Tx only, 3=Rx&Tx)
+:Default Value: Reads flow control settings from the EEPROM
+
+This parameter controls the automatic generation(Tx) and response(Rx)
+to Ethernet PAUSE frames.
+
+InterruptThrottleRate
+---------------------
+
+(not supported on Intel(R) 82542, 82543 or 82544-based adapters)
+
+:Valid Range:
+   0,1,3,4,100-100000 (0=off, 1=dynamic, 3=dynamic conservative,
+   4=simplified balancing)
+:Default Value: 3
+
+The driver can limit the amount of interrupts per second that the adapter
+will generate for incoming packets. It does this by writing a value to the
+adapter that is based on the maximum amount of interrupts that the adapter
+will generate per second.
+
+Setting InterruptThrottleRate to a value greater or equal to 100
+will program the adapter to send out a maximum of that many interrupts
+per second, even if more packets have come in. This reduces interrupt
+load on the system and can lower CPU utilization under heavy load,
+but will increase latency as packets are not processed as quickly.
+
+The default behaviour of the driver previously assumed a static
+InterruptThrottleRate value of 8000, providing a good fallback value for
+all traffic types,but lacking in small packet performance and latency.
+The hardware can handle many more small packets per second however, and
+for this reason an adaptive interrupt moderation algorithm was implemented.
+
+Since 7.3.x, the driver has two adaptive modes (setting 1 or 3) in which
+it dynamically adjusts the InterruptThrottleRate value based on the traffic
+that it receives. After determining the type of incoming traffic in the last
+timeframe, it will adjust the InterruptThrottleRate to an appropriate value
+for that traffic.
+
+The algorithm classifies the incoming traffic every interval into
+classes.  Once the class is determined, the InterruptThrottleRate value is
+adjusted to suit that traffic type the best. There are three classes defined:
+"Bulk traffic", for large amounts of packets of normal size; "Low latency",
+for small amounts of traffic and/or a significant percentage of small
+packets; and "Lowest latency", for almost completely small packets or
+minimal traffic.
+
+In dynamic conservative mode, the InterruptThrottleRate value is set to 4000
+for traffic that falls in class "Bulk traffic". If traffic falls in the "Low
+latency" or "Lowest latency" class, the InterruptThrottleRate is increased
+stepwise to 20000. This default mode is suitable for most applications.
+
+For situations where low latency is vital such as cluster or
+grid computing, the algorithm can reduce latency even more when
+InterruptThrottleRate is set to mode 1. In this mode, which operates
+the same as mode 3, the InterruptThrottleRate will be increased stepwise to
+70000 for traffic in class "Lowest latency".
+
+In simplified mode the interrupt rate is based on the ratio of TX and
+RX traffic.  If the bytes per second rate is approximately equal, the
+interrupt rate will drop as low as 2000 interrupts per second.  If the
+traffic is mostly transmit or mostly receive, the interrupt rate could
+be as high as 8000.
+
+Setting InterruptThrottleRate to 0 turns off any interrupt moderation
+and may improve small packet latency, but is generally not suitable
+for bulk throughput traffic.
+
+NOTE:
+       InterruptThrottleRate takes precedence over the TxAbsIntDelay and
+       RxAbsIntDelay parameters.  In other words, minimizing the receive
+       and/or transmit absolute delays does not force the controller to
+       generate more interrupts than what the Interrupt Throttle Rate
+       allows.
+
+CAUTION:
+          If you are using the Intel(R) PRO/1000 CT Network Connection
+          (controller 82547), setting InterruptThrottleRate to a value
+          greater than 75,000, may hang (stop transmitting) adapters
+          under certain network conditions.  If this occurs a NETDEV
+          WATCHDOG message is logged in the system event log.  In
+          addition, the controller is automatically reset, restoring
+          the network connection.  To eliminate the potential for the
+          hang, ensure that InterruptThrottleRate is set no greater
+          than 75,000 and is not set to 0.
+
+NOTE:
+       When e1000 is loaded with default settings and multiple adapters
+       are in use simultaneously, the CPU utilization may increase non-
+       linearly.  In order to limit the CPU utilization without impacting
+       the overall throughput, we recommend that you load the driver as
+       follows::
+
+           modprobe e1000 InterruptThrottleRate=3000,3000,3000
+
+       This sets the InterruptThrottleRate to 3000 interrupts/sec for
+       the first, second, and third instances of the driver.  The range
+       of 2000 to 3000 interrupts per second works on a majority of
+       systems and is a good starting point, but the optimal value will
+       be platform-specific.  If CPU utilization is not a concern, use
+       RX_POLLING (NAPI) and default driver settings.
+
+RxDescriptors
+-------------
+
+:Valid Range:
+ - 48-256 for 82542 and 82543-based adapters
+ - 48-4096 for all other supported adapters
+:Default Value: 256
+
+This value specifies the number of receive buffer descriptors allocated
+by the driver.  Increasing this value allows the driver to buffer more
+incoming packets, at the expense of increased system memory utilization.
+
+Each descriptor is 16 bytes.  A receive buffer is also allocated for each
+descriptor and can be either 2048, 4096, 8192, or 16384 bytes, depending
+on the MTU setting. The maximum MTU size is 16110.
+
+NOTE:
+       MTU designates the frame size.  It only needs to be set for Jumbo
+       Frames.  Depending on the available system resources, the request
+       for a higher number of receive descriptors may be denied.  In this
+       case, use a lower number.
+
+RxIntDelay
+----------
+
+:Valid Range:   0-65535 (0=off)
+:Default Value: 0
+
+This value delays the generation of receive interrupts in units of 1.024
+microseconds.  Receive interrupt reduction can improve CPU efficiency if
+properly tuned for specific network traffic.  Increasing this value adds
+extra latency to frame reception and can end up decreasing the throughput
+of TCP traffic.  If the system is reporting dropped receives, this value
+may be set too high, causing the driver to run out of available receive
+descriptors.
+
+CAUTION:
+          When setting RxIntDelay to a value other than 0, adapters may
+          hang (stop transmitting) under certain network conditions.  If
+          this occurs a NETDEV WATCHDOG message is logged in the system
+          event log.  In addition, the controller is automatically reset,
+          restoring the network connection.  To eliminate the potential
+          for the hang ensure that RxIntDelay is set to 0.
+
+RxAbsIntDelay
+-------------
+
+(This parameter is supported only on 82540, 82545 and later adapters.)
+
+:Valid Range:   0-65535 (0=off)
+:Default Value: 128
+
+This value, in units of 1.024 microseconds, limits the delay in which a
+receive interrupt is generated.  Useful only if RxIntDelay is non-zero,
+this value ensures that an interrupt is generated after the initial
+packet is received within the set amount of time.  Proper tuning,
+along with RxIntDelay, may improve traffic throughput in specific network
+conditions.
+
+Speed
+-----
+
+(This parameter is supported only on adapters with copper connections.)
+
+:Valid Settings: 0, 10, 100, 1000
+:Default Value:  0 (auto-negotiate at all supported speeds)
+
+Speed forces the line speed to the specified value in megabits per second
+(Mbps).  If this parameter is not specified or is set to 0 and the link
+partner is set to auto-negotiate, the board will auto-detect the correct
+speed.  Duplex should also be set when Speed is set to either 10 or 100.
+
+TxDescriptors
+-------------
+
+:Valid Range:
+  - 48-256 for 82542 and 82543-based adapters
+  - 48-4096 for all other supported adapters
+:Default Value: 256
+
+This value is the number of transmit descriptors allocated by the driver.
+Increasing this value allows the driver to queue more transmits.  Each
+descriptor is 16 bytes.
+
+NOTE:
+       Depending on the available system resources, the request for a
+       higher number of transmit descriptors may be denied.  In this case,
+       use a lower number.
+
+TxIntDelay
+----------
+
+:Valid Range:   0-65535 (0=off)
+:Default Value: 8
+
+This value delays the generation of transmit interrupts in units of
+1.024 microseconds.  Transmit interrupt reduction can improve CPU
+efficiency if properly tuned for specific network traffic.  If the
+system is reporting dropped transmits, this value may be set too high
+causing the driver to run out of available transmit descriptors.
+
+TxAbsIntDelay
+-------------
+
+(This parameter is supported only on 82540, 82545 and later adapters.)
+
+:Valid Range:   0-65535 (0=off)
+:Default Value: 32
+
+This value, in units of 1.024 microseconds, limits the delay in which a
+transmit interrupt is generated.  Useful only if TxIntDelay is non-zero,
+this value ensures that an interrupt is generated after the initial
+packet is sent on the wire within the set amount of time.  Proper tuning,
+along with TxIntDelay, may improve traffic throughput in specific
+network conditions.
+
+XsumRX
+------
+
+(This parameter is NOT supported on the 82542-based adapter.)
+
+:Valid Range:   0-1
+:Default Value: 1
+
+A value of '1' indicates that the driver should enable IP checksum
+offload for received packets (both UDP and TCP) to the adapter hardware.
+
+Copybreak
+---------
+
+:Valid Range:   0-xxxxxxx (0=off)
+:Default Value: 256
+:Usage: modprobe e1000.ko copybreak=128
+
+Driver copies all packets below or equaling this size to a fresh RX
+buffer before handing it up the stack.
+
+This parameter is different than other parameters, in that it is a
+single (not 1,1,1 etc.) parameter applied to all driver instances and
+it is also available during runtime at
+/sys/module/e1000/parameters/copybreak
+
+SmartPowerDownEnable
+--------------------
+
+:Valid Range: 0-1
+:Default Value:  0 (disabled)
+
+Allows PHY to turn off in lower power states. The user can turn off
+this parameter in supported chipsets.
+
+Speed and Duplex Configuration
+==============================
+
+Three keywords are used to control the speed and duplex configuration.
+These keywords are Speed, Duplex, and AutoNeg.
+
+If the board uses a fiber interface, these keywords are ignored, and the
+fiber interface board only links at 1000 Mbps full-duplex.
+
+For copper-based boards, the keywords interact as follows:
+
+- The default operation is auto-negotiate.  The board advertises all
+  supported speed and duplex combinations, and it links at the highest
+  common speed and duplex mode IF the link partner is set to auto-negotiate.
+
+- If Speed = 1000, limited auto-negotiation is enabled and only 1000 Mbps
+  is advertised (The 1000BaseT spec requires auto-negotiation.)
+
+- If Speed = 10 or 100, then both Speed and Duplex should be set.  Auto-
+  negotiation is disabled, and the AutoNeg parameter is ignored.  Partner
+  SHOULD also be forced.
+
+The AutoNeg parameter is used when more control is required over the
+auto-negotiation process.  It should be used when you wish to control which
+speed and duplex combinations are advertised during the auto-negotiation
+process.
+
+The parameter may be specified as either a decimal or hexadecimal value as
+determined by the bitmap below.
+
+============== ====== ====== ======= ======= ====== ====== ======= ======
+Bit position   7      6      5       4       3      2      1       0
+Decimal Value  128    64     32      16      8      4      2       1
+Hex value      80     40     20      10      8      4      2       1
+Speed (Mbps)   N/A    N/A    1000    N/A     100    100    10      10
+Duplex                       Full            Full   Half   Full    Half
+============== ====== ====== ======= ======= ====== ====== ======= ======
+
+Some examples of using AutoNeg::
+
+  modprobe e1000 AutoNeg=0x01 (Restricts autonegotiation to 10 Half)
+  modprobe e1000 AutoNeg=1 (Same as above)
+  modprobe e1000 AutoNeg=0x02 (Restricts autonegotiation to 10 Full)
+  modprobe e1000 AutoNeg=0x03 (Restricts autonegotiation to 10 Half or 10 Full)
+  modprobe e1000 AutoNeg=0x04 (Restricts autonegotiation to 100 Half)
+  modprobe e1000 AutoNeg=0x05 (Restricts autonegotiation to 10 Half or 100
+  Half)
+  modprobe e1000 AutoNeg=0x020 (Restricts autonegotiation to 1000 Full)
+  modprobe e1000 AutoNeg=32 (Same as above)
+
+Note that when this parameter is used, Speed and Duplex must not be specified.
+
+If the link partner is forced to a specific speed and duplex, then this
+parameter should not be used.  Instead, use the Speed and Duplex parameters
+previously mentioned to force the adapter to the same speed and duplex.
+
+Additional Configurations
+=========================
+
+Jumbo Frames
+------------
+
+  Jumbo Frames support is enabled by changing the MTU to a value larger than
+  the default of 1500.  Use the ifconfig command to increase the MTU size.
+  For example::
+
+       ifconfig eth<x> mtu 9000 up
+
+  This setting is not saved across reboots.  It can be made permanent if
+  you add::
+
+       MTU=9000
+
+  to the file /etc/sysconfig/network-scripts/ifcfg-eth<x>.  This example
+  applies to the Red Hat distributions; other distributions may store this
+  setting in a different location.
+
+Notes:
+  Degradation in throughput performance may be observed in some Jumbo frames
+  environments. If this is observed, increasing the application's socket buffer
+  size and/or increasing the /proc/sys/net/ipv4/tcp_*mem entry values may help.
+  See the specific application manual and /usr/src/linux*/Documentation/
+  networking/ip-sysctl.txt for more details.
+
+  - The maximum MTU setting for Jumbo Frames is 16110.  This value coincides
+    with the maximum Jumbo Frames size of 16128.
+
+  - Using Jumbo frames at 10 or 100 Mbps is not supported and may result in
+    poor performance or loss of link.
+
+  - Adapters based on the Intel(R) 82542 and 82573V/E controller do not
+    support Jumbo Frames. These correspond to the following product names::
+
+     Intel(R) PRO/1000 Gigabit Server Adapter
+     Intel(R) PRO/1000 PM Network Connection
+
+ethtool
+-------
+
+  The driver utilizes the ethtool interface for driver configuration and
+  diagnostics, as well as displaying statistical information.  The ethtool
+  version 1.6 or later is required for this functionality.
+
+  The latest release of ethtool can be found from
+  https://www.kernel.org/pub/software/network/ethtool/
+
+Enabling Wake on LAN* (WoL)
+---------------------------
+
+  WoL is configured through the ethtool* utility.
+
+  WoL will be enabled on the system during the next shut down or reboot.
+  For this driver version, in order to enable WoL, the e1000 driver must be
+  loaded when shutting down or rebooting the system.
+
+Support
+=======
+
+For general information, go to the Intel support website at:
+
+    http://support.intel.com
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+    http://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on the supported
+kernel with a supported adapter, email the specific information related
+to the issue to e1000-devel@lists.sf.net
diff --git a/Documentation/networking/device_drivers/intel/e1000e.rst b/Documentation/networking/device_drivers/intel/e1000e.rst
new file mode 100644 (file)
index 0000000..33554e5
--- /dev/null
@@ -0,0 +1,382 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Driver for Intel(R) Ethernet Network Connection
+======================================================
+
+Intel Gigabit Linux driver.
+Copyright(c) 2008-2018 Intel Corporation.
+
+Contents
+========
+
+- Identifying Your Adapter
+- Command Line Parameters
+- Additional Configurations
+- Support
+
+
+Identifying Your Adapter
+========================
+For information on how to identify your adapter, and for the latest Intel
+network drivers, refer to the Intel Support website:
+https://www.intel.com/support
+
+
+Command Line Parameters
+=======================
+If the driver is built as a module, the following optional parameters are used
+by entering them on the command line with the modprobe command using this
+syntax::
+
+    modprobe e1000e [<option>=<VAL1>,<VAL2>,...]
+
+There needs to be a <VAL#> for each network port in the system supported by
+this driver. The values will be applied to each instance, in function order.
+For example::
+
+    modprobe e1000e InterruptThrottleRate=16000,16000
+
+In this case, there are two network ports supported by e1000e in the system.
+The default value for each parameter is generally the recommended setting,
+unless otherwise noted.
+
+NOTE: A descriptor describes a data buffer and attributes related to the data
+buffer. This information is accessed by the hardware.
+
+InterruptThrottleRate
+---------------------
+:Valid Range: 0,1,3,4,100-100000
+:Default Value: 3
+
+Interrupt Throttle Rate controls the number of interrupts each interrupt
+vector can generate per second. Increasing ITR lowers latency at the cost of
+increased CPU utilization, though it may help throughput in some circumstances.
+
+Setting InterruptThrottleRate to a value greater or equal to 100
+will program the adapter to send out a maximum of that many interrupts
+per second, even if more packets have come in. This reduces interrupt
+load on the system and can lower CPU utilization under heavy load,
+but will increase latency as packets are not processed as quickly.
+
+The default behaviour of the driver previously assumed a static
+InterruptThrottleRate value of 8000, providing a good fallback value for
+all traffic types, but lacking in small packet performance and latency.
+The hardware can handle many more small packets per second however, and
+for this reason an adaptive interrupt moderation algorithm was implemented.
+
+The driver has two adaptive modes (setting 1 or 3) in which
+it dynamically adjusts the InterruptThrottleRate value based on the traffic
+that it receives. After determining the type of incoming traffic in the last
+timeframe, it will adjust the InterruptThrottleRate to an appropriate value
+for that traffic.
+
+The algorithm classifies the incoming traffic every interval into
+classes.  Once the class is determined, the InterruptThrottleRate value is
+adjusted to suit that traffic type the best. There are three classes defined:
+"Bulk traffic", for large amounts of packets of normal size; "Low latency",
+for small amounts of traffic and/or a significant percentage of small
+packets; and "Lowest latency", for almost completely small packets or
+minimal traffic.
+
+ - 0: Off
+      Turns off any interrupt moderation and may improve small packet latency.
+      However, this is generally not suitable for bulk throughput traffic due
+      to the increased CPU utilization of the higher interrupt rate.
+ - 1: Dynamic mode
+      This mode attempts to moderate interrupts per vector while maintaining
+      very low latency. This can sometimes cause extra CPU utilization. If
+      planning on deploying e1000e in a latency sensitive environment, this
+      parameter should be considered.
+ - 3: Dynamic Conservative mode (default)
+      In dynamic conservative mode, the InterruptThrottleRate value is set to
+      4000 for traffic that falls in class "Bulk traffic". If traffic falls in
+      the "Low latency" or "Lowest latency" class, the InterruptThrottleRate is
+      increased stepwise to 20000. This default mode is suitable for most
+      applications.
+ - 4: Simplified Balancing mode
+      In simplified mode the interrupt rate is based on the ratio of TX and
+      RX traffic.  If the bytes per second rate is approximately equal, the
+      interrupt rate will drop as low as 2000 interrupts per second.  If the
+      traffic is mostly transmit or mostly receive, the interrupt rate could
+      be as high as 8000.
+ - 100-100000:
+      Setting InterruptThrottleRate to a value greater or equal to 100
+      will program the adapter to send at most that many interrupts per second,
+      even if more packets have come in. This reduces interrupt load on the
+      system and can lower CPU utilization under heavy load, but will increase
+      latency as packets are not processed as quickly.
+
+NOTE: InterruptThrottleRate takes precedence over the TxAbsIntDelay and
+RxAbsIntDelay parameters. In other words, minimizing the receive and/or
+transmit absolute delays does not force the controller to generate more
+interrupts than what the Interrupt Throttle Rate allows.
+
+RxIntDelay
+----------
+:Valid Range: 0-65535 (0=off)
+:Default Value: 0
+
+This value delays the generation of receive interrupts in units of 1.024
+microseconds. Receive interrupt reduction can improve CPU efficiency if
+properly tuned for specific network traffic. Increasing this value adds extra
+latency to frame reception and can end up decreasing the throughput of TCP
+traffic. If the system is reporting dropped receives, this value may be set
+too high, causing the driver to run out of available receive descriptors.
+
+CAUTION: When setting RxIntDelay to a value other than 0, adapters may hang
+(stop transmitting) under certain network conditions. If this occurs a NETDEV
+WATCHDOG message is logged in the system event log. In addition, the
+controller is automatically reset, restoring the network connection. To
+eliminate the potential for the hang ensure that RxIntDelay is set to 0.
+
+RxAbsIntDelay
+-------------
+:Valid Range: 0-65535 (0=off)
+:Default Value: 8
+
+This value, in units of 1.024 microseconds, limits the delay in which a
+receive interrupt is generated. This value ensures that an interrupt is
+generated after the initial packet is received within the set amount of time,
+which is useful only if RxIntDelay is non-zero. Proper tuning, along with
+RxIntDelay, may improve traffic throughput in specific network conditions.
+
+TxIntDelay
+----------
+:Valid Range: 0-65535 (0=off)
+:Default Value: 8
+
+This value delays the generation of transmit interrupts in units of 1.024
+microseconds. Transmit interrupt reduction can improve CPU efficiency if
+properly tuned for specific network traffic. If the system is reporting
+dropped transmits, this value may be set too high causing the driver to run
+out of available transmit descriptors.
+
+TxAbsIntDelay
+-------------
+:Valid Range: 0-65535 (0=off)
+:Default Value: 32
+
+This value, in units of 1.024 microseconds, limits the delay in which a
+transmit interrupt is generated. It is useful only if TxIntDelay is non-zero.
+It ensures that an interrupt is generated after the initial Packet is sent on
+the wire within the set amount of time. Proper tuning, along with TxIntDelay,
+may improve traffic throughput in specific network conditions.
+
+copybreak
+---------
+:Valid Range: 0-xxxxxxx (0=off)
+:Default Value: 256
+
+The driver copies all packets below or equaling this size to a fresh receive
+buffer before handing it up the stack.
+This parameter differs from other parameters because it is a single (not 1,1,1
+etc.) parameter applied to all driver instances and it is also available
+during runtime at /sys/module/e1000e/parameters/copybreak.
+
+To use copybreak, type::
+
+    modprobe e1000e.ko copybreak=128
+
+SmartPowerDownEnable
+--------------------
+:Valid Range: 0,1
+:Default Value: 0 (disabled)
+
+Allows the PHY to turn off in lower power states. The user can turn off this
+parameter in supported chipsets.
+
+KumeranLockLoss
+---------------
+:Valid Range: 0,1
+:Default Value: 1 (enabled)
+
+This workaround skips resetting the PHY at shutdown for the initial silicon
+releases of ICH8 systems.
+
+IntMode
+-------
+:Valid Range: 0-2
+:Default Value: 0
+
+   +-------+----------------+
+   | Value | Interrupt Mode |
+   +=======+================+
+   |   0   |     Legacy     |
+   +-------+----------------+
+   |   1   |       MSI      |
+   +-------+----------------+
+   |   2   |      MSI-X     |
+   +-------+----------------+
+
+IntMode allows load time control over the type of interrupt registered for by
+the driver. MSI-X is required for multiple queue support, and some kernels and
+combinations of kernel .config options will force a lower level of interrupt
+support.
+
+This command will show different values for each type of interrupt::
+
+  cat /proc/interrupts
+
+CrcStripping
+------------
+:Valid Range: 0,1
+:Default Value: 1 (enabled)
+
+Strip the CRC from received packets before sending up the network stack. If
+you have a machine with a BMC enabled but cannot receive IPMI traffic after
+loading or enabling the driver, try disabling this feature.
+
+WriteProtectNVM
+---------------
+:Valid Range: 0,1
+:Default Value: 1 (enabled)
+
+If set to 1, configure the hardware to ignore all write/erase cycles to the
+GbE region in the ICHx NVM (in order to prevent accidental corruption of the
+NVM). This feature can be disabled by setting the parameter to 0 during initial
+driver load.
+
+NOTE: The machine must be power cycled (full off/on) when enabling NVM writes
+via setting the parameter to zero. Once the NVM has been locked (via the
+parameter at 1 when the driver loads) it cannot be unlocked except via power
+cycle.
+
+Debug
+-----
+:Valid Range: 0-16 (0=none,...,16=all)
+:Default Value: 0
+
+This parameter adjusts the level of debug messages displayed in the system logs.
+
+
+Additional Features and Configurations
+======================================
+
+Jumbo Frames
+------------
+Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
+to a value larger than the default value of 1500.
+
+Use the ifconfig command to increase the MTU size. For example, enter the
+following where <x> is the interface number::
+
+    ifconfig eth<x> mtu 9000 up
+
+Alternatively, you can use the ip command as follows::
+
+    ip link set mtu 9000 dev eth<x>
+    ip link set up dev eth<x>
+
+This setting is not saved across reboots. The setting change can be made
+permanent by adding 'MTU=9000' to the file:
+
+- For RHEL: /etc/sysconfig/network-scripts/ifcfg-eth<x>
+- For SLES: /etc/sysconfig/network/<config_file>
+
+NOTE: The maximum MTU setting for Jumbo Frames is 8996. This value coincides
+with the maximum Jumbo Frames size of 9018 bytes.
+
+NOTE: Using Jumbo frames at 10 or 100 Mbps is not supported and may result in
+poor performance or loss of link.
+
+NOTE: The following adapters limit Jumbo Frames sized packets to a maximum of
+4088 bytes:
+
+  - Intel(R) 82578DM Gigabit Network Connection
+  - Intel(R) 82577LM Gigabit Network Connection
+
+The following adapters do not support Jumbo Frames:
+
+  - Intel(R) PRO/1000 Gigabit Server Adapter
+  - Intel(R) PRO/1000 PM Network Connection
+  - Intel(R) 82562G 10/100 Network Connection
+  - Intel(R) 82562G-2 10/100 Network Connection
+  - Intel(R) 82562GT 10/100 Network Connection
+  - Intel(R) 82562GT-2 10/100 Network Connection
+  - Intel(R) 82562V 10/100 Network Connection
+  - Intel(R) 82562V-2 10/100 Network Connection
+  - Intel(R) 82566DC Gigabit Network Connection
+  - Intel(R) 82566DC-2 Gigabit Network Connection
+  - Intel(R) 82566DM Gigabit Network Connection
+  - Intel(R) 82566MC Gigabit Network Connection
+  - Intel(R) 82566MM Gigabit Network Connection
+  - Intel(R) 82567V-3 Gigabit Network Connection
+  - Intel(R) 82577LC Gigabit Network Connection
+  - Intel(R) 82578DC Gigabit Network Connection
+
+NOTE: Jumbo Frames cannot be configured on an 82579-based Network device if
+MACSec is enabled on the system.
+
+
+ethtool
+-------
+The driver utilizes the ethtool interface for driver configuration and
+diagnostics, as well as displaying statistical information. The latest ethtool
+version is required for this functionality. Download it at:
+
+https://www.kernel.org/pub/software/network/ethtool/
+
+NOTE: When validating enable/disable tests on some parts (for example, 82578),
+it is necessary to add a few seconds between tests when working with ethtool.
+
+
+Speed and Duplex Configuration
+------------------------------
+In addressing speed and duplex configuration issues, you need to distinguish
+between copper-based adapters and fiber-based adapters.
+
+In the default mode, an Intel(R) Ethernet Network Adapter using copper
+connections will attempt to auto-negotiate with its link partner to determine
+the best setting. If the adapter cannot establish link with the link partner
+using auto-negotiation, you may need to manually configure the adapter and link
+partner to identical settings to establish link and pass packets. This should
+only be needed when attempting to link with an older switch that does not
+support auto-negotiation or one that has been forced to a specific speed or
+duplex mode. Your link partner must match the setting you choose. 1 Gbps speeds
+and higher cannot be forced. Use the autonegotiation advertising setting to
+manually set devices for 1 Gbps and higher.
+
+Speed, duplex, and autonegotiation advertising are configured through the
+ethtool* utility.
+
+Caution: Only experienced network administrators should force speed and duplex
+or change autonegotiation advertising manually. The settings at the switch must
+always match the adapter settings. Adapter performance may suffer or your
+adapter may not operate if you configure the adapter differently from your
+switch.
+
+An Intel(R) Ethernet Network Adapter using fiber-based connections, however,
+will not attempt to auto-negotiate with its link partner since those adapters
+operate only in full duplex and only at their native speed.
+
+
+Enabling Wake on LAN* (WoL)
+---------------------------
+WoL is configured through the ethtool* utility.
+
+WoL will be enabled on the system during the next shut down or reboot. For
+this driver version, in order to enable WoL, the e1000e driver must be loaded
+prior to shutting down or suspending the system.
+
+NOTE: Wake on LAN is only supported on port A for the following devices:
+- Intel(R) PRO/1000 PT Dual Port Network Connection
+- Intel(R) PRO/1000 PT Dual Port Server Connection
+- Intel(R) PRO/1000 PT Dual Port Server Adapter
+- Intel(R) PRO/1000 PF Dual Port Server Adapter
+- Intel(R) PRO/1000 PT Quad Port Server Adapter
+- Intel(R) Gigabit PT Quad Port Server ExpressModule
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/device_drivers/intel/fm10k.rst b/Documentation/networking/device_drivers/intel/fm10k.rst
new file mode 100644 (file)
index 0000000..bf5e594
--- /dev/null
@@ -0,0 +1,141 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Base Driver for Intel(R) Ethernet Multi-host Controller
+==============================================================
+
+August 20, 2018
+Copyright(c) 2015-2018 Intel Corporation.
+
+Contents
+========
+- Identifying Your Adapter
+- Additional Configurations
+- Performance Tuning
+- Known Issues
+- Support
+
+Identifying Your Adapter
+========================
+The driver in this release is compatible with devices based on the Intel(R)
+Ethernet Multi-host Controller.
+
+For information on how to identify your adapter, and for the latest Intel
+network drivers, refer to the Intel Support website:
+http://www.intel.com/support
+
+
+Flow Control
+------------
+The Intel(R) Ethernet Switch Host Interface Driver does not support Flow
+Control. It will not send pause frames. This may result in dropped frames.
+
+
+Virtual Functions (VFs)
+-----------------------
+Use sysfs to enable VFs.
+Valid Range: 0-64
+
+For example::
+
+    echo $num_vf_enabled > /sys/class/net/$dev/device/sriov_numvfs //enable VFs
+    echo 0 > /sys/class/net/$dev/device/sriov_numvfs //disable VFs
+
+NOTE: Neither the device nor the driver control how VFs are mapped into config
+space. Bus layout will vary by operating system. On operating systems that
+support it, you can check sysfs to find the mapping.
+
+NOTE: When SR-IOV mode is enabled, hardware VLAN filtering and VLAN tag
+stripping/insertion will remain enabled. Please remove the old VLAN filter
+before the new VLAN filter is added. For example::
+
+    ip link set eth0 vf 0 vlan 100     // set vlan 100 for VF 0
+    ip link set eth0 vf 0 vlan 0       // Delete vlan 100
+    ip link set eth0 vf 0 vlan 200     // set a new vlan 200 for VF 0
+
+
+Additional Features and Configurations
+======================================
+
+Jumbo Frames
+------------
+Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
+to a value larger than the default value of 1500.
+
+Use the ifconfig command to increase the MTU size. For example, enter the
+following where <x> is the interface number::
+
+    ifconfig eth<x> mtu 9000 up
+
+Alternatively, you can use the ip command as follows::
+
+    ip link set mtu 9000 dev eth<x>
+    ip link set up dev eth<x>
+
+This setting is not saved across reboots. The setting change can be made
+permanent by adding 'MTU=9000' to the file:
+
+- For RHEL: /etc/sysconfig/network-scripts/ifcfg-eth<x>
+- For SLES: /etc/sysconfig/network/<config_file>
+
+NOTE: The maximum MTU setting for Jumbo Frames is 15342. This value coincides
+with the maximum Jumbo Frames size of 15364 bytes.
+
+NOTE: This driver will attempt to use multiple page sized buffers to receive
+each jumbo packet. This should help to avoid buffer starvation issues when
+allocating receive packets.
+
+
+Generic Receive Offload, aka GRO
+--------------------------------
+The driver supports the in-kernel software implementation of GRO. GRO has
+shown that by coalescing Rx traffic into larger chunks of data, CPU
+utilization can be significantly reduced when under large Rx load. GRO is an
+evolution of the previously-used LRO interface. GRO is able to coalesce
+other protocols besides TCP. It's also safe to use with configurations that
+are problematic for LRO, namely bridging and iSCSI.
+
+
+
+Supported ethtool Commands and Options for Filtering
+----------------------------------------------------
+-n --show-nfc
+  Retrieves the receive network flow classification configurations.
+
+rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6
+  Retrieves the hash options for the specified network traffic type.
+
+-N --config-nfc
+  Configures the receive network flow classification.
+
+rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r
+  Configures the hash options for the specified network traffic type.
+
+- udp4: UDP over IPv4
+- udp6: UDP over IPv6
+- f Hash on bytes 0 and 1 of the Layer 4 header of the rx packet.
+- n Hash on bytes 2 and 3 of the Layer 4 header of the rx packet.
+
+
+Known Issues/Troubleshooting
+============================
+
+Enabling SR-IOV in a 64-bit Microsoft* Windows Server* 2012/R2 guest OS under Linux KVM
+---------------------------------------------------------------------------------------
+KVM Hypervisor/VMM supports direct assignment of a PCIe device to a VM. This
+includes traditional PCIe devices, as well as SR-IOV-capable devices based on
+the Intel Ethernet Controller XL710.
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/device_drivers/intel/i40e.rst b/Documentation/networking/device_drivers/intel/i40e.rst
new file mode 100644 (file)
index 0000000..0cc16c5
--- /dev/null
@@ -0,0 +1,770 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Base Driver for the Intel(R) Ethernet Controller 700 Series
+==================================================================
+
+Intel 40 Gigabit Linux driver.
+Copyright(c) 1999-2018 Intel Corporation.
+
+Contents
+========
+
+- Overview
+- Identifying Your Adapter
+- Intel(R) Ethernet Flow Director
+- Additional Configurations
+- Known Issues
+- Support
+
+
+Driver information can be obtained using ethtool, lspci, and ifconfig.
+Instructions on updating ethtool can be found in the section Additional
+Configurations later in this document.
+
+For questions related to hardware requirements, refer to the documentation
+supplied with your Intel adapter. All hardware requirements listed apply to use
+with Linux.
+
+
+Identifying Your Adapter
+========================
+The driver is compatible with devices based on the following:
+
+ * Intel(R) Ethernet Controller X710
+ * Intel(R) Ethernet Controller XL710
+ * Intel(R) Ethernet Network Connection X722
+ * Intel(R) Ethernet Controller XXV710
+
+For the best performance, make sure the latest NVM/FW is installed on your
+device.
+
+For information on how to identify your adapter, and for the latest NVM/FW
+images and Intel network drivers, refer to the Intel Support website:
+https://www.intel.com/support
+
+SFP+ and QSFP+ Devices
+----------------------
+For information about supported media, refer to this document:
+https://www.intel.com/content/dam/www/public/us/en/documents/release-notes/xl710-ethernet-controller-feature-matrix.pdf
+
+NOTE: Some adapters based on the Intel(R) Ethernet Controller 700 Series only
+support Intel Ethernet Optics modules. On these adapters, other modules are not
+supported and will not function.  In all cases Intel recommends using Intel
+Ethernet Optics; other modules may function but are not validated by Intel.
+Contact Intel for supported media types.
+
+NOTE: For connections based on Intel(R) Ethernet Controller 700 Series, support
+is dependent on your system board. Please see your vendor for details.
+
+NOTE: In systems that do not have adequate airflow to cool the adapter and
+optical modules, you must use high temperature optical modules.
+
+Virtual Functions (VFs)
+-----------------------
+Use sysfs to enable VFs. For example::
+
+  #echo $num_vf_enabled > /sys/class/net/$dev/device/sriov_numvfs #enable VFs
+  #echo 0 > /sys/class/net/$dev/device/sriov_numvfs #disable VFs
+
+For example, the following instructions will configure PF eth0 and the first VF
+on VLAN 10::
+
+  $ ip link set dev eth0 vf 0 vlan 10
+
+VLAN Tag Packet Steering
+------------------------
+Allows you to send all packets with a specific VLAN tag to a particular SR-IOV
+virtual function (VF). Further, this feature allows you to designate a
+particular VF as trusted, and allows that trusted VF to request selective
+promiscuous mode on the Physical Function (PF).
+
+To set a VF as trusted or untrusted, enter the following command in the
+Hypervisor::
+
+  # ip link set dev eth0 vf 1 trust [on|off]
+
+Once the VF is designated as trusted, use the following commands in the VM to
+set the VF to promiscuous mode.
+
+::
+
+  For promiscuous all:
+  #ip link set eth2 promisc on
+  Where eth2 is a VF interface in the VM
+
+  For promiscuous Multicast:
+  #ip link set eth2 allmulticast on
+  Where eth2 is a VF interface in the VM
+
+NOTE: By default, the ethtool priv-flag vf-true-promisc-support is set to
+"off",meaning that promiscuous mode for the VF will be limited. To set the
+promiscuous mode for the VF to true promiscuous and allow the VF to see all
+ingress traffic, use the following command::
+
+  #ethtool -set-priv-flags p261p1 vf-true-promisc-support on
+
+The vf-true-promisc-support priv-flag does not enable promiscuous mode; rather,
+it designates which type of promiscuous mode (limited or true) you will get
+when you enable promiscuous mode using the ip link commands above. Note that
+this is a global setting that affects the entire device. However,the
+vf-true-promisc-support priv-flag is only exposed to the first PF of the
+device. The PF remains in limited promiscuous mode (unless it is in MFP mode)
+regardless of the vf-true-promisc-support setting.
+
+Now add a VLAN interface on the VF interface::
+
+  #ip link add link eth2 name eth2.100 type vlan id 100
+
+Note that the order in which you set the VF to promiscuous mode and add the
+VLAN interface does not matter (you can do either first). The end result in
+this example is that the VF will get all traffic that is tagged with VLAN 100.
+
+Intel(R) Ethernet Flow Director
+-------------------------------
+The Intel Ethernet Flow Director performs the following tasks:
+
+- Directs receive packets according to their flows to different queues.
+- Enables tight control on routing a flow in the platform.
+- Matches flows and CPU cores for flow affinity.
+- Supports multiple parameters for flexible flow classification and load
+  balancing (in SFP mode only).
+
+NOTE: The Linux i40e driver supports the following flow types: IPv4, TCPv4, and
+UDPv4. For a given flow type, it supports valid combinations of IP addresses
+(source or destination) and UDP/TCP ports (source and destination). For
+example, you can supply only a source IP address, a source IP address and a
+destination port, or any combination of one or more of these four parameters.
+
+NOTE: The Linux i40e driver allows you to filter traffic based on a
+user-defined flexible two-byte pattern and offset by using the ethtool user-def
+and mask fields. Only L3 and L4 flow types are supported for user-defined
+flexible filters. For a given flow type, you must clear all Intel Ethernet Flow
+Director filters before changing the input set (for that flow type).
+
+To enable or disable the Intel Ethernet Flow Director::
+
+  # ethtool -K ethX ntuple <on|off>
+
+When disabling ntuple filters, all the user programmed filters are flushed from
+the driver cache and hardware. All needed filters must be re-added when ntuple
+is re-enabled.
+
+To add a filter that directs packet to queue 2, use -U or -N switch::
+
+  # ethtool -N ethX flow-type tcp4 src-ip 192.168.10.1 dst-ip \
+  192.168.10.2 src-port 2000 dst-port 2001 action 2 [loc 1]
+
+To set a filter using only the source and destination IP address::
+
+  # ethtool -N ethX flow-type tcp4 src-ip 192.168.10.1 dst-ip \
+  192.168.10.2 action 2 [loc 1]
+
+To see the list of filters currently present::
+
+  # ethtool <-u|-n> ethX
+
+Application Targeted Routing (ATR) Perfect Filters
+--------------------------------------------------
+ATR is enabled by default when the kernel is in multiple transmit queue mode.
+An ATR Intel Ethernet Flow Director filter rule is added when a TCP-IP flow
+starts and is deleted when the flow ends. When a TCP-IP Intel Ethernet Flow
+Director rule is added from ethtool (Sideband filter), ATR is turned off by the
+driver. To re-enable ATR, the sideband can be disabled with the ethtool -K
+option. For example::
+
+  ethtool â€“K [adapter] ntuple [off|on]
+
+If sideband is re-enabled after ATR is re-enabled, ATR remains enabled until a
+TCP-IP flow is added. When all TCP-IP sideband rules are deleted, ATR is
+automatically re-enabled.
+
+Packets that match the ATR rules are counted in fdir_atr_match stats in
+ethtool, which also can be used to verify whether ATR rules still exist.
+
+Sideband Perfect Filters
+------------------------
+Sideband Perfect Filters are used to direct traffic that matches specified
+characteristics. They are enabled through ethtool's ntuple interface. To add a
+new filter use the following command::
+
+  ethtool -U <device> flow-type <type> src-ip <ip> dst-ip <ip> src-port <port> \
+  dst-port <port> action <queue>
+
+Where:
+  <device> - the ethernet device to program
+  <type> - can be ip4, tcp4, udp4, or sctp4
+  <ip> - the ip address to match on
+  <port> - the port number to match on
+  <queue> - the queue to direct traffic towards (-1 discards matching traffic)
+
+Use the following command to display all of the active filters::
+
+  ethtool -u <device>
+
+Use the following command to delete a filter::
+
+  ethtool -U <device> delete <N>
+
+Where <N> is the filter id displayed when printing all the active filters, and
+may also have been specified using "loc <N>" when adding the filter.
+
+The following example matches TCP traffic sent from 192.168.0.1, port 5300,
+directed to 192.168.0.5, port 80, and sends it to queue 7::
+
+  ethtool -U enp130s0 flow-type tcp4 src-ip 192.168.0.1 dst-ip 192.168.0.5 \
+  src-port 5300 dst-port 80 action 7
+
+For each flow-type, the programmed filters must all have the same matching
+input set. For example, issuing the following two commands is acceptable::
+
+  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.1 src-port 5300 action 7
+  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.5 src-port 55 action 10
+
+Issuing the next two commands, however, is not acceptable, since the first
+specifies src-ip and the second specifies dst-ip::
+
+  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.1 src-port 5300 action 7
+  ethtool -U enp130s0 flow-type ip4 dst-ip 192.168.0.5 src-port 55 action 10
+
+The second command will fail with an error. You may program multiple filters
+with the same fields, using different values, but, on one device, you may not
+program two tcp4 filters with different matching fields.
+
+Matching on a sub-portion of a field is not supported by the i40e driver, thus
+partial mask fields are not supported.
+
+The driver also supports matching user-defined data within the packet payload.
+This flexible data is specified using the "user-def" field of the ethtool
+command in the following way:
+
++----------------------------+--------------------------+
+| 31    28    24    20    16 | 15    12    8    4    0  |
++----------------------------+--------------------------+
+| offset into packet payload | 2 bytes of flexible data |
++----------------------------+--------------------------+
+
+For example,
+
+::
+
+  ... user-def 0x4FFFF ...
+
+tells the filter to look 4 bytes into the payload and match that value against
+0xFFFF. The offset is based on the beginning of the payload, and not the
+beginning of the packet. Thus
+
+::
+
+  flow-type tcp4 ... user-def 0x8BEAF ...
+
+would match TCP/IPv4 packets which have the value 0xBEAF 8 bytes into the
+TCP/IPv4 payload.
+
+Note that ICMP headers are parsed as 4 bytes of header and 4 bytes of payload.
+Thus to match the first byte of the payload, you must actually add 4 bytes to
+the offset. Also note that ip4 filters match both ICMP frames as well as raw
+(unknown) ip4 frames, where the payload will be the L3 payload of the IP4 frame.
+
+The maximum offset is 64. The hardware will only read up to 64 bytes of data
+from the payload. The offset must be even because the flexible data is 2 bytes
+long and must be aligned to byte 0 of the packet payload.
+
+The user-defined flexible offset is also considered part of the input set and
+cannot be programmed separately for multiple filters of the same type. However,
+the flexible data is not part of the input set and multiple filters may use the
+same offset but match against different data.
+
+To create filters that direct traffic to a specific Virtual Function, use the
+"action" parameter. Specify the action as a 64 bit value, where the lower 32
+bits represents the queue number, while the next 8 bits represent which VF.
+Note that 0 is the PF, so the VF identifier is offset by 1. For example::
+
+  ... action 0x800000002 ...
+
+specifies to direct traffic to Virtual Function 7 (8 minus 1) into queue 2 of
+that VF.
+
+Note that these filters will not break internal routing rules, and will not
+route traffic that otherwise would not have been sent to the specified Virtual
+Function.
+
+Setting the link-down-on-close Private Flag
+-------------------------------------------
+When the link-down-on-close private flag is set to "on", the port's link will
+go down when the interface is brought down using the ifconfig ethX down command.
+
+Use ethtool to view and set link-down-on-close, as follows::
+
+  ethtool --show-priv-flags ethX
+  ethtool --set-priv-flags ethX link-down-on-close [on|off]
+
+Viewing Link Messages
+---------------------
+Link messages will not be displayed to the console if the distribution is
+restricting system messages. In order to see network driver link messages on
+your console, set dmesg to eight by entering the following::
+
+  dmesg -n 8
+
+NOTE: This setting is not saved across reboots.
+
+Jumbo Frames
+------------
+Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
+to a value larger than the default value of 1500.
+
+Use the ifconfig command to increase the MTU size. For example, enter the
+following where <x> is the interface number::
+
+  ifconfig eth<x> mtu 9000 up
+
+Alternatively, you can use the ip command as follows::
+
+  ip link set mtu 9000 dev eth<x>
+  ip link set up dev eth<x>
+
+This setting is not saved across reboots. The setting change can be made
+permanent by adding 'MTU=9000' to the file::
+
+  /etc/sysconfig/network-scripts/ifcfg-eth<x> // for RHEL
+  /etc/sysconfig/network/<config_file> // for SLES
+
+NOTE: The maximum MTU setting for Jumbo Frames is 9702. This value coincides
+with the maximum Jumbo Frames size of 9728 bytes.
+
+NOTE: This driver will attempt to use multiple page sized buffers to receive
+each jumbo packet. This should help to avoid buffer starvation issues when
+allocating receive packets.
+
+ethtool
+-------
+The driver utilizes the ethtool interface for driver configuration and
+diagnostics, as well as displaying statistical information. The latest ethtool
+version is required for this functionality. Download it at:
+https://www.kernel.org/pub/software/network/ethtool/
+
+Supported ethtool Commands and Options for Filtering
+----------------------------------------------------
+-n --show-nfc
+  Retrieves the receive network flow classification configurations.
+
+rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6
+  Retrieves the hash options for the specified network traffic type.
+
+-N --config-nfc
+  Configures the receive network flow classification.
+
+rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r...
+  Configures the hash options for the specified network traffic type.
+
+udp4 UDP over IPv4
+udp6 UDP over IPv6
+
+f Hash on bytes 0 and 1 of the Layer 4 header of the Rx packet.
+n Hash on bytes 2 and 3 of the Layer 4 header of the Rx packet.
+
+Speed and Duplex Configuration
+------------------------------
+In addressing speed and duplex configuration issues, you need to distinguish
+between copper-based adapters and fiber-based adapters.
+
+In the default mode, an Intel(R) Ethernet Network Adapter using copper
+connections will attempt to auto-negotiate with its link partner to determine
+the best setting. If the adapter cannot establish link with the link partner
+using auto-negotiation, you may need to manually configure the adapter and link
+partner to identical settings to establish link and pass packets. This should
+only be needed when attempting to link with an older switch that does not
+support auto-negotiation or one that has been forced to a specific speed or
+duplex mode. Your link partner must match the setting you choose. 1 Gbps speeds
+and higher cannot be forced. Use the autonegotiation advertising setting to
+manually set devices for 1 Gbps and higher.
+
+NOTE: You cannot set the speed for devices based on the Intel(R) Ethernet
+Network Adapter XXV710 based devices.
+
+Speed, duplex, and autonegotiation advertising are configured through the
+ethtool* utility.
+
+Caution: Only experienced network administrators should force speed and duplex
+or change autonegotiation advertising manually. The settings at the switch must
+always match the adapter settings. Adapter performance may suffer or your
+adapter may not operate if you configure the adapter differently from your
+switch.
+
+An Intel(R) Ethernet Network Adapter using fiber-based connections, however,
+will not attempt to auto-negotiate with its link partner since those adapters
+operate only in full duplex and only at their native speed.
+
+NAPI
+----
+NAPI (Rx polling mode) is supported in the i40e driver.
+For more information on NAPI, see
+https://wiki.linuxfoundation.org/networking/napi
+
+Flow Control
+------------
+Ethernet Flow Control (IEEE 802.3x) can be configured with ethtool to enable
+receiving and transmitting pause frames for i40e. When transmit is enabled,
+pause frames are generated when the receive packet buffer crosses a predefined
+threshold. When receive is enabled, the transmit unit will halt for the time
+delay specified when a pause frame is received.
+
+NOTE: You must have a flow control capable link partner.
+
+Flow Control is on by default.
+
+Use ethtool to change the flow control settings.
+
+To enable or disable Rx or Tx Flow Control::
+
+  ethtool -A eth? rx <on|off> tx <on|off>
+
+Note: This command only enables or disables Flow Control if auto-negotiation is
+disabled. If auto-negotiation is enabled, this command changes the parameters
+used for auto-negotiation with the link partner.
+
+To enable or disable auto-negotiation::
+
+  ethtool -s eth? autoneg <on|off>
+
+Note: Flow Control auto-negotiation is part of link auto-negotiation. Depending
+on your device, you may not be able to change the auto-negotiation setting.
+
+RSS Hash Flow
+-------------
+Allows you to set the hash bytes per flow type and any combination of one or
+more options for Receive Side Scaling (RSS) hash byte configuration.
+
+::
+
+  # ethtool -N <dev> rx-flow-hash <type> <option>
+
+Where <type> is:
+  tcp4 signifying TCP over IPv4
+  udp4 signifying UDP over IPv4
+  tcp6 signifying TCP over IPv6
+  udp6 signifying UDP over IPv6
+And <option> is one or more of:
+  s    Hash on the IP source address of the Rx packet.
+  d    Hash on the IP destination address of the Rx packet.
+  f    Hash on bytes 0 and 1 of the Layer 4 header of the Rx packet.
+  n    Hash on bytes 2 and 3 of the Layer 4 header of the Rx packet.
+
+MAC and VLAN anti-spoofing feature
+----------------------------------
+When a malicious driver attempts to send a spoofed packet, it is dropped by the
+hardware and not transmitted.
+NOTE: This feature can be disabled for a specific Virtual Function (VF)::
+
+  ip link set <pf dev> vf <vf id> spoofchk {off|on}
+
+IEEE 1588 Precision Time Protocol (PTP) Hardware Clock (PHC)
+------------------------------------------------------------
+Precision Time Protocol (PTP) is used to synchronize clocks in a computer
+network. PTP support varies among Intel devices that support this driver. Use
+"ethtool -T <netdev name>" to get a definitive list of PTP capabilities
+supported by the device.
+
+IEEE 802.1ad (QinQ) Support
+---------------------------
+The IEEE 802.1ad standard, informally known as QinQ, allows for multiple VLAN
+IDs within a single Ethernet frame. VLAN IDs are sometimes referred to as
+"tags," and multiple VLAN IDs are thus referred to as a "tag stack." Tag stacks
+allow L2 tunneling and the ability to segregate traffic within a particular
+VLAN ID, among other uses.
+
+The following are examples of how to configure 802.1ad (QinQ)::
+
+  ip link add link eth0 eth0.24 type vlan proto 802.1ad id 24
+  ip link add link eth0.24 eth0.24.371 type vlan proto 802.1Q id 371
+
+Where "24" and "371" are example VLAN IDs.
+
+NOTES:
+  Receive checksum offloads, cloud filters, and VLAN acceleration are not
+  supported for 802.1ad (QinQ) packets.
+
+VXLAN and GENEVE Overlay HW Offloading
+--------------------------------------
+Virtual Extensible LAN (VXLAN) allows you to extend an L2 network over an L3
+network, which may be useful in a virtualized or cloud environment. Some
+Intel(R) Ethernet Network devices perform VXLAN processing, offloading it from
+the operating system. This reduces CPU utilization.
+
+VXLAN offloading is controlled by the Tx and Rx checksum offload options
+provided by ethtool. That is, if Tx checksum offload is enabled, and the
+adapter has the capability, VXLAN offloading is also enabled.
+
+Support for VXLAN and GENEVE HW offloading is dependent on kernel support of
+the HW offloading features.
+
+Multiple Functions per Port
+---------------------------
+Some adapters based on the Intel Ethernet Controller X710/XL710 support
+multiple functions on a single physical port. Configure these functions through
+the System Setup/BIOS.
+
+Minimum TX Bandwidth is the guaranteed minimum data transmission bandwidth, as
+a percentage of the full physical port link speed, that the partition will
+receive. The bandwidth the partition is awarded will never fall below the level
+you specify.
+
+The range for the minimum bandwidth values is:
+1 to ((100 minus # of partitions on the physical port) plus 1)
+For example, if a physical port has 4 partitions, the range would be:
+1 to ((100 - 4) + 1 = 97)
+
+The Maximum Bandwidth percentage represents the maximum transmit bandwidth
+allocated to the partition as a percentage of the full physical port link
+speed. The accepted range of values is 1-100. The value is used as a limiter,
+should you chose that any one particular function not be able to consume 100%
+of a port's bandwidth (should it be available). The sum of all the values for
+Maximum Bandwidth is not restricted, because no more than 100% of a port's
+bandwidth can ever be used.
+
+NOTE: X710/XXV710 devices fail to enable Max VFs (64) when Multiple Functions
+per Port (MFP) and SR-IOV are enabled. An error from i40e is logged that says
+"add vsi failed for VF N, aq_err 16". To workaround the issue, enable less than
+64 virtual functions (VFs).
+
+Data Center Bridging (DCB)
+--------------------------
+DCB is a configuration Quality of Service implementation in hardware. It uses
+the VLAN priority tag (802.1p) to filter traffic. That means that there are 8
+different priorities that traffic can be filtered into. It also enables
+priority flow control (802.1Qbb) which can limit or eliminate the number of
+dropped packets during network stress. Bandwidth can be allocated to each of
+these priorities, which is enforced at the hardware level (802.1Qaz).
+
+Adapter firmware implements LLDP and DCBX protocol agents as per 802.1AB and
+802.1Qaz respectively. The firmware based DCBX agent runs in willing mode only
+and can accept settings from a DCBX capable peer. Software configuration of
+DCBX parameters via dcbtool/lldptool are not supported.
+
+NOTE: Firmware LLDP can be disabled by setting the private flag disable-fw-lldp.
+
+The i40e driver implements the DCB netlink interface layer to allow user-space
+to communicate with the driver and query DCB configuration for the port.
+
+NOTE:
+The kernel assumes that TC0 is available, and will disable Priority Flow
+Control (PFC) on the device if TC0 is not available. To fix this, ensure TC0 is
+enabled when setting up DCB on your switch.
+
+Interrupt Rate Limiting
+-----------------------
+:Valid Range: 0-235 (0=no limit)
+
+The Intel(R) Ethernet Controller XL710 family supports an interrupt rate
+limiting mechanism. The user can control, via ethtool, the number of
+microseconds between interrupts.
+
+Syntax::
+
+  # ethtool -C ethX rx-usecs-high N
+
+The range of 0-235 microseconds provides an effective range of 4,310 to 250,000
+interrupts per second. The value of rx-usecs-high can be set independently of
+rx-usecs and tx-usecs in the same ethtool command, and is also independent of
+the adaptive interrupt moderation algorithm. The underlying hardware supports
+granularity in 4-microsecond intervals, so adjacent values may result in the
+same interrupt rate.
+
+One possible use case is the following::
+
+  # ethtool -C ethX adaptive-rx off adaptive-tx off rx-usecs-high 20 rx-usecs \
+    5 tx-usecs 5
+
+The above command would disable adaptive interrupt moderation, and allow a
+maximum of 5 microseconds before indicating a receive or transmit was complete.
+However, instead of resulting in as many as 200,000 interrupts per second, it
+limits total interrupts per second to 50,000 via the rx-usecs-high parameter.
+
+Performance Optimization
+========================
+Driver defaults are meant to fit a wide variety of workloads, but if further
+optimization is required we recommend experimenting with the following settings.
+
+NOTE: For better performance when processing small (64B) frame sizes, try
+enabling Hyper threading in the BIOS in order to increase the number of logical
+cores in the system and subsequently increase the number of queues available to
+the adapter.
+
+Virtualized Environments
+------------------------
+1. Disable XPS on both ends by using the included virt_perf_default script
+or by running the following command as root::
+
+  for file in `ls /sys/class/net/<ethX>/queues/tx-*/xps_cpus`;
+  do echo 0 > $file; done
+
+2. Using the appropriate mechanism (vcpupin) in the vm, pin the cpu's to
+individual lcpu's, making sure to use a set of cpu's included in the
+device's local_cpulist: /sys/class/net/<ethX>/device/local_cpulist.
+
+3. Configure as many Rx/Tx queues in the VM as available. Do not rely on
+the default setting of 1.
+
+
+Non-virtualized Environments
+----------------------------
+Pin the adapter's IRQs to specific cores by disabling the irqbalance service
+and using the included set_irq_affinity script. Please see the script's help
+text for further options.
+
+- The following settings will distribute the IRQs across all the cores evenly::
+
+  # scripts/set_irq_affinity -x all <interface1> , [ <interface2>, ... ]
+
+- The following settings will distribute the IRQs across all the cores that are
+  local to the adapter (same NUMA node)::
+
+  # scripts/set_irq_affinity -x local <interface1> ,[ <interface2>, ... ]
+
+For very CPU intensive workloads, we recommend pinning the IRQs to all cores.
+
+For IP Forwarding: Disable Adaptive ITR and lower Rx and Tx interrupts per
+queue using ethtool.
+
+- Setting rx-usecs and tx-usecs to 125 will limit interrupts to about 8000
+  interrupts per second per queue.
+
+::
+
+  # ethtool -C <interface> adaptive-rx off adaptive-tx off rx-usecs 125 \
+    tx-usecs 125
+
+For lower CPU utilization: Disable Adaptive ITR and lower Rx and Tx interrupts
+per queue using ethtool.
+
+- Setting rx-usecs and tx-usecs to 250 will limit interrupts to about 4000
+  interrupts per second per queue.
+
+::
+
+  # ethtool -C <interface> adaptive-rx off adaptive-tx off rx-usecs 250 \
+    tx-usecs 250
+
+For lower latency: Disable Adaptive ITR and ITR by setting Rx and Tx to 0 using
+ethtool.
+
+::
+
+  # ethtool -C <interface> adaptive-rx off adaptive-tx off rx-usecs 0 \
+    tx-usecs 0
+
+Application Device Queues (ADq)
+-------------------------------
+Application Device Queues (ADq) allows you to dedicate one or more queues to a
+specific application. This can reduce latency for the specified application,
+and allow Tx traffic to be rate limited per application. Follow the steps below
+to set ADq.
+
+1. Create traffic classes (TCs). Maximum of 8 TCs can be created per interface.
+The shaper bw_rlimit parameter is optional.
+
+Example: Sets up two tcs, tc0 and tc1, with 16 queues each and max tx rate set
+to 1Gbit for tc0 and 3Gbit for tc1.
+
+::
+
+  # tc qdisc add dev <interface> root mqprio num_tc 2 map 0 0 0 0 1 1 1 1
+  queues 16@0 16@16 hw 1 mode channel shaper bw_rlimit min_rate 1Gbit 2Gbit
+  max_rate 1Gbit 3Gbit
+
+map: priority mapping for up to 16 priorities to tcs (e.g. map 0 0 0 0 1 1 1 1
+sets priorities 0-3 to use tc0 and 4-7 to use tc1)
+
+queues: for each tc, <num queues>@<offset> (e.g. queues 16@0 16@16 assigns
+16 queues to tc0 at offset 0 and 16 queues to tc1 at offset 16. Max total
+number of queues for all tcs is 64 or number of cores, whichever is lower.)
+
+hw 1 mode channel: â€˜channel’ with â€˜hw’ set to 1 is a new new hardware
+offload mode in mqprio that makes full use of the mqprio options, the
+TCs, the queue configurations, and the QoS parameters.
+
+shaper bw_rlimit: for each tc, sets minimum and maximum bandwidth rates.
+Totals must be equal or less than port speed.
+
+For example: min_rate 1Gbit 3Gbit: Verify bandwidth limit using network
+monitoring tools such as ifstat or sar â€“n DEV [interval] [number of samples]
+
+2. Enable HW TC offload on interface::
+
+    # ethtool -K <interface> hw-tc-offload on
+
+3. Apply TCs to ingress (RX) flow of interface::
+
+    # tc qdisc add dev <interface> ingress
+
+NOTES:
+ - Run all tc commands from the iproute2 <pathtoiproute2>/tc/ directory.
+ - ADq is not compatible with cloud filters.
+ - Setting up channels via ethtool (ethtool -L) is not supported when the
+   TCs are configured using mqprio.
+ - You must have iproute2 latest version
+ - NVM version 6.01 or later is required.
+ - ADq cannot be enabled when any the following features are enabled: Data
+   Center Bridging (DCB), Multiple Functions per Port (MFP), or Sideband
+   Filters.
+ - If another driver (for example, DPDK) has set cloud filters, you cannot
+   enable ADq.
+ - Tunnel filters are not supported in ADq. If encapsulated packets do
+   arrive in non-tunnel mode, filtering will be done on the inner headers.
+   For example, for VXLAN traffic in non-tunnel mode, PCTYPE is identified
+   as a VXLAN encapsulated packet, outer headers are ignored. Therefore,
+   inner headers are matched.
+ - If a TC filter on a PF matches traffic over a VF (on the PF), that
+   traffic will be routed to the appropriate queue of the PF, and will
+   not be passed on the VF. Such traffic will end up getting dropped higher
+   up in the TCP/IP stack as it does not match PF address data.
+ - If traffic matches multiple TC filters that point to different TCs,
+   that traffic will be duplicated and sent to all matching TC queues.
+   The hardware switch mirrors the packet to a VSI list when multiple
+   filters are matched.
+
+
+Known Issues/Troubleshooting
+============================
+
+NOTE: 1 Gb devices based on the Intel(R) Ethernet Network Connection X722 do
+not support the following features:
+
+  * Data Center Bridging (DCB)
+  * QOS
+  * VMQ
+  * SR-IOV
+  * Task Encapsulation offload (VXLAN, NVGRE)
+  * Energy Efficient Ethernet (EEE)
+  * Auto-media detect
+
+Unexpected Issues when the device driver and DPDK share a device
+----------------------------------------------------------------
+Unexpected issues may result when an i40e device is in multi driver mode and
+the kernel driver and DPDK driver are sharing the device. This is because
+access to the global NIC resources is not synchronized between multiple
+drivers. Any change to the global NIC configuration (writing to a global
+register, setting global configuration by AQ, or changing switch modes) will
+affect all ports and drivers on the device. Loading DPDK with the
+"multi-driver" module parameter may mitigate some of the issues.
+
+TC0 must be enabled when setting up DCB on a switch
+---------------------------------------------------
+The kernel assumes that TC0 is available, and will disable Priority Flow
+Control (PFC) on the device if TC0 is not available. To fix this, ensure TC0 is
+enabled when setting up DCB on your switch.
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/device_drivers/intel/iavf.rst b/Documentation/networking/device_drivers/intel/iavf.rst
new file mode 100644 (file)
index 0000000..f8b42b6
--- /dev/null
@@ -0,0 +1,281 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Base Driver for Intel(R) Ethernet Adaptive Virtual Function
+==================================================================
+
+Intel Ethernet Adaptive Virtual Function Linux driver.
+Copyright(c) 2013-2018 Intel Corporation.
+
+Contents
+========
+
+- Identifying Your Adapter
+- Additional Configurations
+- Known Issues/Troubleshooting
+- Support
+
+This file describes the iavf Linux* Base Driver. This driver was formerly
+called i40evf.
+
+The iavf driver supports the below mentioned virtual function devices and
+can only be activated on kernels running the i40e or newer Physical Function
+(PF) driver compiled with CONFIG_PCI_IOV.  The iavf driver requires
+CONFIG_PCI_MSI to be enabled.
+
+The guest OS loading the iavf driver must support MSI-X interrupts.
+
+Identifying Your Adapter
+========================
+The driver in this kernel is compatible with devices based on the following:
+ * Intel(R) XL710 X710 Virtual Function
+ * Intel(R) X722 Virtual Function
+ * Intel(R) XXV710 Virtual Function
+ * Intel(R) Ethernet Adaptive Virtual Function
+
+For the best performance, make sure the latest NVM/FW is installed on your
+device.
+
+For information on how to identify your adapter, and for the latest NVM/FW
+images and Intel network drivers, refer to the Intel Support website:
+http://www.intel.com/support
+
+
+Additional Features and Configurations
+======================================
+
+Viewing Link Messages
+---------------------
+Link messages will not be displayed to the console if the distribution is
+restricting system messages. In order to see network driver link messages on
+your console, set dmesg to eight by entering the following::
+
+  dmesg -n 8
+
+NOTE: This setting is not saved across reboots.
+
+ethtool
+-------
+The driver utilizes the ethtool interface for driver configuration and
+diagnostics, as well as displaying statistical information. The latest ethtool
+version is required for this functionality. Download it at:
+https://www.kernel.org/pub/software/network/ethtool/
+
+Setting VLAN Tag Stripping
+--------------------------
+If you have applications that require Virtual Functions (VFs) to receive
+packets with VLAN tags, you can disable VLAN tag stripping for the VF. The
+Physical Function (PF) processes requests issued from the VF to enable or
+disable VLAN tag stripping. Note that if the PF has assigned a VLAN to a VF,
+then requests from that VF to set VLAN tag stripping will be ignored.
+
+To enable/disable VLAN tag stripping for a VF, issue the following command
+from inside the VM in which you are running the VF::
+
+  ethtool -K <if_name> rxvlan on/off
+
+or alternatively::
+
+  ethtool --offload <if_name> rxvlan on/off
+
+Adaptive Virtual Function
+-------------------------
+Adaptive Virtual Function (AVF) allows the virtual function driver, or VF, to
+adapt to changing feature sets of the physical function driver (PF) with which
+it is associated. This allows system administrators to update a PF without
+having to update all the VFs associated with it. All AVFs have a single common
+device ID and branding string.
+
+AVFs have a minimum set of features known as "base mode," but may provide
+additional features depending on what features are available in the PF with
+which the AVF is associated. The following are base mode features:
+
+- 4 Queue Pairs (QP) and associated Configuration Status Registers (CSRs)
+  for Tx/Rx.
+- i40e descriptors and ring format.
+- Descriptor write-back completion.
+- 1 control queue, with i40e descriptors, CSRs and ring format.
+- 5 MSI-X interrupt vectors and corresponding i40e CSRs.
+- 1 Interrupt Throttle Rate (ITR) index.
+- 1 Virtual Station Interface (VSI) per VF.
+- 1 Traffic Class (TC), TC0
+- Receive Side Scaling (RSS) with 64 entry indirection table and key,
+  configured through the PF.
+- 1 unicast MAC address reserved per VF.
+- 16 MAC address filters for each VF.
+- Stateless offloads - non-tunneled checksums.
+- AVF device ID.
+- HW mailbox is used for VF to PF communications (including on Windows).
+
+IEEE 802.1ad (QinQ) Support
+---------------------------
+The IEEE 802.1ad standard, informally known as QinQ, allows for multiple VLAN
+IDs within a single Ethernet frame. VLAN IDs are sometimes referred to as
+"tags," and multiple VLAN IDs are thus referred to as a "tag stack." Tag stacks
+allow L2 tunneling and the ability to segregate traffic within a particular
+VLAN ID, among other uses.
+
+The following are examples of how to configure 802.1ad (QinQ)::
+
+  ip link add link eth0 eth0.24 type vlan proto 802.1ad id 24
+  ip link add link eth0.24 eth0.24.371 type vlan proto 802.1Q id 371
+
+Where "24" and "371" are example VLAN IDs.
+
+NOTES:
+  Receive checksum offloads, cloud filters, and VLAN acceleration are not
+  supported for 802.1ad (QinQ) packets.
+
+Application Device Queues (ADq)
+-------------------------------
+Application Device Queues (ADq) allows you to dedicate one or more queues to a
+specific application. This can reduce latency for the specified application,
+and allow Tx traffic to be rate limited per application. Follow the steps below
+to set ADq.
+
+1. Create traffic classes (TCs). Maximum of 8 TCs can be created per interface.
+The shaper bw_rlimit parameter is optional.
+
+Example: Sets up two tcs, tc0 and tc1, with 16 queues each and max tx rate set
+to 1Gbit for tc0 and 3Gbit for tc1.
+
+::
+
+  # tc qdisc add dev <interface> root mqprio num_tc 2 map 0 0 0 0 1 1 1 1
+  queues 16@0 16@16 hw 1 mode channel shaper bw_rlimit min_rate 1Gbit 2Gbit
+  max_rate 1Gbit 3Gbit
+
+map: priority mapping for up to 16 priorities to tcs (e.g. map 0 0 0 0 1 1 1 1
+sets priorities 0-3 to use tc0 and 4-7 to use tc1)
+
+queues: for each tc, <num queues>@<offset> (e.g. queues 16@0 16@16 assigns
+16 queues to tc0 at offset 0 and 16 queues to tc1 at offset 16. Max total
+number of queues for all tcs is 64 or number of cores, whichever is lower.)
+
+hw 1 mode channel: â€˜channel’ with â€˜hw’ set to 1 is a new new hardware
+offload mode in mqprio that makes full use of the mqprio options, the
+TCs, the queue configurations, and the QoS parameters.
+
+shaper bw_rlimit: for each tc, sets minimum and maximum bandwidth rates.
+Totals must be equal or less than port speed.
+
+For example: min_rate 1Gbit 3Gbit: Verify bandwidth limit using network
+monitoring tools such as ifstat or sar â€“n DEV [interval] [number of samples]
+
+2. Enable HW TC offload on interface::
+
+    # ethtool -K <interface> hw-tc-offload on
+
+3. Apply TCs to ingress (RX) flow of interface::
+
+    # tc qdisc add dev <interface> ingress
+
+NOTES:
+ - Run all tc commands from the iproute2 <pathtoiproute2>/tc/ directory.
+ - ADq is not compatible with cloud filters.
+ - Setting up channels via ethtool (ethtool -L) is not supported when the TCs
+   are configured using mqprio.
+ - You must have iproute2 latest version
+ - NVM version 6.01 or later is required.
+ - ADq cannot be enabled when any the following features are enabled: Data
+   Center Bridging (DCB), Multiple Functions per Port (MFP), or Sideband Filters.
+ - If another driver (for example, DPDK) has set cloud filters, you cannot
+   enable ADq.
+ - Tunnel filters are not supported in ADq. If encapsulated packets do arrive
+   in non-tunnel mode, filtering will be done on the inner headers.  For example,
+   for VXLAN traffic in non-tunnel mode, PCTYPE is identified as a VXLAN
+   encapsulated packet, outer headers are ignored. Therefore, inner headers are
+   matched.
+ - If a TC filter on a PF matches traffic over a VF (on the PF), that traffic
+   will be routed to the appropriate queue of the PF, and will not be passed on
+   the VF. Such traffic will end up getting dropped higher up in the TCP/IP
+   stack as it does not match PF address data.
+ - If traffic matches multiple TC filters that point to different TCs, that
+   traffic will be duplicated and sent to all matching TC queues.  The hardware
+   switch mirrors the packet to a VSI list when multiple filters are matched.
+
+
+Known Issues/Troubleshooting
+============================
+
+Traffic Is Not Being Passed Between VM and Client
+-------------------------------------------------
+You may not be able to pass traffic between a client system and a
+Virtual Machine (VM) running on a separate host if the Virtual Function
+(VF, or Virtual NIC) is not in trusted mode and spoof checking is enabled
+on the VF. Note that this situation can occur in any combination of client,
+host, and guest operating system. For information on how to set the VF to
+trusted mode, refer to the section "VLAN Tag Packet Steering" in this
+readme document. For information on setting spoof checking, refer to the
+section "MAC and VLAN anti-spoofing feature" in this readme document.
+
+Do not unload port driver if VF with active VM is bound to it
+-------------------------------------------------------------
+Do not unload a port's driver if a Virtual Function (VF) with an active Virtual
+Machine (VM) is bound to it. Doing so will cause the port to appear to hang.
+Once the VM shuts down, or otherwise releases the VF, the command will complete.
+
+Virtual machine does not get link
+---------------------------------
+If the virtual machine has more than one virtual port assigned to it, and those
+virtual ports are bound to different physical ports, you may not get link on
+all of the virtual ports. The following command may work around the issue::
+
+  ethtool -r <PF>
+
+Where <PF> is the PF interface in the host, for example: p5p1. You may need to
+run the command more than once to get link on all virtual ports.
+
+MAC address of Virtual Function changes unexpectedly
+----------------------------------------------------
+If a Virtual Function's MAC address is not assigned in the host, then the VF
+(virtual function) driver will use a random MAC address. This random MAC
+address may change each time the VF driver is reloaded. You can assign a static
+MAC address in the host machine. This static MAC address will survive
+a VF driver reload.
+
+Driver Buffer Overflow Fix
+--------------------------
+The fix to resolve CVE-2016-8105, referenced in Intel SA-00069
+https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00069.html
+is included in this and future versions of the driver.
+
+Multiple Interfaces on Same Ethernet Broadcast Network
+------------------------------------------------------
+Due to the default ARP behavior on Linux, it is not possible to have one system
+on two IP networks in the same Ethernet broadcast domain (non-partitioned
+switch) behave as expected. All Ethernet interfaces will respond to IP traffic
+for any IP address assigned to the system. This results in unbalanced receive
+traffic.
+
+If you have multiple interfaces in a server, either turn on ARP filtering by
+entering::
+
+  echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
+
+NOTE: This setting is not saved across reboots. The configuration change can be
+made permanent by adding the following line to the file /etc/sysctl.conf::
+
+  net.ipv4.conf.all.arp_filter = 1
+
+Another alternative is to install the interfaces in separate broadcast domains
+(either in different switches or in a switch partitioned to VLANs).
+
+Rx Page Allocation Errors
+-------------------------
+'Page allocation failure. order:0' errors may occur under stress.
+This is caused by the way the Linux kernel reports this stressed condition.
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://support.intel.com
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on the supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net
diff --git a/Documentation/networking/device_drivers/intel/ice.rst b/Documentation/networking/device_drivers/intel/ice.rst
new file mode 100644 (file)
index 0000000..4d118b8
--- /dev/null
@@ -0,0 +1,45 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Base Driver for the Intel(R) Ethernet Connection E800 Series
+===================================================================
+
+Intel ice Linux driver.
+Copyright(c) 2018 Intel Corporation.
+
+Contents
+========
+
+- Enabling the driver
+- Support
+
+The driver in this release supports Intel's E800 Series of products. For
+more information, visit Intel's support page at https://support.intel.com.
+
+Enabling the driver
+===================
+The driver is enabled via the standard kernel configuration system,
+using the make command::
+
+  make oldconfig/menuconfig/etc.
+
+The driver is located in the menu structure at:
+
+  -> Device Drivers
+    -> Network device support (NETDEVICES [=y])
+      -> Ethernet driver support
+        -> Intel devices
+          -> Intel(R) Ethernet Connection E800 Series Support
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/device_drivers/intel/igb.rst b/Documentation/networking/device_drivers/intel/igb.rst
new file mode 100644 (file)
index 0000000..e87a4a7
--- /dev/null
@@ -0,0 +1,212 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Base Driver for Intel(R) Ethernet Network Connection
+===========================================================
+
+Intel Gigabit Linux driver.
+Copyright(c) 1999-2018 Intel Corporation.
+
+Contents
+========
+
+- Identifying Your Adapter
+- Command Line Parameters
+- Additional Configurations
+- Support
+
+
+Identifying Your Adapter
+========================
+For information on how to identify your adapter, and for the latest Intel
+network drivers, refer to the Intel Support website:
+http://www.intel.com/support
+
+
+Command Line Parameters
+========================
+If the driver is built as a module, the following optional parameters are used
+by entering them on the command line with the modprobe command using this
+syntax::
+
+    modprobe igb [<option>=<VAL1>,<VAL2>,...]
+
+There needs to be a <VAL#> for each network port in the system supported by
+this driver. The values will be applied to each instance, in function order.
+For example::
+
+    modprobe igb max_vfs=2,4
+
+In this case, there are two network ports supported by igb in the system.
+
+NOTE: A descriptor describes a data buffer and attributes related to the data
+buffer. This information is accessed by the hardware.
+
+max_vfs
+-------
+:Valid Range: 0-7
+
+This parameter adds support for SR-IOV. It causes the driver to spawn up to
+max_vfs worth of virtual functions.  If the value is greater than 0 it will
+also force the VMDq parameter to be 1 or more.
+
+The parameters for the driver are referenced by position. Thus, if you have a
+dual port adapter, or more than one adapter in your system, and want N virtual
+functions per port, you must specify a number for each port with each parameter
+separated by a comma. For example::
+
+    modprobe igb max_vfs=4
+
+This will spawn 4 VFs on the first port.
+
+::
+
+    modprobe igb max_vfs=2,4
+
+This will spawn 2 VFs on the first port and 4 VFs on the second port.
+
+NOTE: Caution must be used in loading the driver with these parameters.
+Depending on your system configuration, number of slots, etc., it is impossible
+to predict in all cases where the positions would be on the command line.
+
+NOTE: Neither the device nor the driver control how VFs are mapped into config
+space. Bus layout will vary by operating system. On operating systems that
+support it, you can check sysfs to find the mapping.
+
+NOTE: When either SR-IOV mode or VMDq mode is enabled, hardware VLAN filtering
+and VLAN tag stripping/insertion will remain enabled. Please remove the old
+VLAN filter before the new VLAN filter is added. For example::
+
+    ip link set eth0 vf 0 vlan 100     // set vlan 100 for VF 0
+    ip link set eth0 vf 0 vlan 0       // Delete vlan 100
+    ip link set eth0 vf 0 vlan 200     // set a new vlan 200 for VF 0
+
+Debug
+-----
+:Valid Range: 0-16 (0=none,...,16=all)
+:Default Value: 0
+
+This parameter adjusts the level debug messages displayed in the system logs.
+
+
+Additional Features and Configurations
+======================================
+
+Jumbo Frames
+------------
+Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
+to a value larger than the default value of 1500.
+
+Use the ifconfig command to increase the MTU size. For example, enter the
+following where <x> is the interface number::
+
+    ifconfig eth<x> mtu 9000 up
+
+Alternatively, you can use the ip command as follows::
+
+    ip link set mtu 9000 dev eth<x>
+    ip link set up dev eth<x>
+
+This setting is not saved across reboots. The setting change can be made
+permanent by adding 'MTU=9000' to the file:
+
+- For RHEL: /etc/sysconfig/network-scripts/ifcfg-eth<x>
+- For SLES: /etc/sysconfig/network/<config_file>
+
+NOTE: The maximum MTU setting for Jumbo Frames is 9216. This value coincides
+with the maximum Jumbo Frames size of 9234 bytes.
+
+NOTE: Using Jumbo frames at 10 or 100 Mbps is not supported and may result in
+poor performance or loss of link.
+
+
+ethtool
+-------
+The driver utilizes the ethtool interface for driver configuration and
+diagnostics, as well as displaying statistical information. The latest ethtool
+version is required for this functionality. Download it at:
+
+https://www.kernel.org/pub/software/network/ethtool/
+
+
+Enabling Wake on LAN* (WoL)
+---------------------------
+WoL is configured through the ethtool* utility.
+
+WoL will be enabled on the system during the next shut down or reboot. For
+this driver version, in order to enable WoL, the igb driver must be loaded
+prior to shutting down or suspending the system.
+
+NOTE: Wake on LAN is only supported on port A of multi-port devices.  Also
+Wake On LAN is not supported for the following device:
+- Intel(R) Gigabit VT Quad Port Server Adapter
+
+
+Multiqueue
+----------
+In this mode, a separate MSI-X vector is allocated for each queue and one for
+"other" interrupts such as link status change and errors. All interrupts are
+throttled via interrupt moderation. Interrupt moderation must be used to avoid
+interrupt storms while the driver is processing one interrupt. The moderation
+value should be at least as large as the expected time for the driver to
+process an interrupt. Multiqueue is off by default.
+
+REQUIREMENTS: MSI-X support is required for Multiqueue. If MSI-X is not found,
+the system will fallback to MSI or to Legacy interrupts. This driver supports
+receive multiqueue on all kernels that support MSI-X.
+
+NOTE: On some kernels a reboot is required to switch between single queue mode
+and multiqueue mode or vice-versa.
+
+
+MAC and VLAN anti-spoofing feature
+----------------------------------
+When a malicious driver attempts to send a spoofed packet, it is dropped by the
+hardware and not transmitted.
+
+An interrupt is sent to the PF driver notifying it of the spoof attempt. When a
+spoofed packet is detected, the PF driver will send the following message to
+the system log (displayed by the "dmesg" command):
+Spoof event(s) detected on VF(n), where n = the VF that attempted to do the
+spoofing
+
+
+Setting MAC Address, VLAN and Rate Limit Using IProute2 Tool
+------------------------------------------------------------
+You can set a MAC address of a Virtual Function (VF), a default VLAN and the
+rate limit using the IProute2 tool. Download the latest version of the
+IProute2 tool from Sourceforge if your version does not have all the features
+you require.
+
+Credit Based Shaper (Qav Mode)
+------------------------------
+When enabling the CBS qdisc in the hardware offload mode, traffic shaping using
+the CBS (described in the IEEE 802.1Q-2018 Section 8.6.8.2 and discussed in the
+Annex L) algorithm will run in the i210 controller, so it's more accurate and
+uses less CPU.
+
+When using offloaded CBS, and the traffic rate obeys the configured rate
+(doesn't go above it), CBS should have little to no effect in the latency.
+
+The offloaded version of the algorithm has some limits, caused by how the idle
+slope is expressed in the adapter's registers. It can only represent idle slopes
+in 16.38431 kbps units, which means that if a idle slope of 2576kbps is
+requested, the controller will be configured to use a idle slope of ~2589 kbps,
+because the driver rounds the value up. For more details, see the comments on
+:c:func:`igb_config_tx_modes()`.
+
+NOTE: This feature is exclusive to i210 models.
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/device_drivers/intel/igbvf.rst b/Documentation/networking/device_drivers/intel/igbvf.rst
new file mode 100644 (file)
index 0000000..a8a9ffa
--- /dev/null
@@ -0,0 +1,64 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Base Virtual Function Driver for Intel(R) 1G Ethernet
+============================================================
+
+Intel Gigabit Virtual Function Linux driver.
+Copyright(c) 1999-2018 Intel Corporation.
+
+Contents
+========
+- Identifying Your Adapter
+- Additional Configurations
+- Support
+
+This driver supports Intel 82576-based virtual function devices-based virtual
+function devices that can only be activated on kernels that support SR-IOV.
+
+SR-IOV requires the correct platform and OS support.
+
+The guest OS loading this driver must support MSI-X interrupts.
+
+For questions related to hardware requirements, refer to the documentation
+supplied with your Intel adapter. All hardware requirements listed apply to use
+with Linux.
+
+Driver information can be obtained using ethtool, lspci, and ifconfig.
+Instructions on updating ethtool can be found in the section Additional
+Configurations later in this document.
+
+NOTE: There is a limit of a total of 32 shared VLANs to 1 or more VFs.
+
+
+Identifying Your Adapter
+========================
+For information on how to identify your adapter, and for the latest Intel
+network drivers, refer to the Intel Support website:
+http://www.intel.com/support
+
+
+Additional Features and Configurations
+======================================
+
+ethtool
+-------
+The driver utilizes the ethtool interface for driver configuration and
+diagnostics, as well as displaying statistical information. The latest ethtool
+version is required for this functionality. Download it at:
+
+https://www.kernel.org/pub/software/network/ethtool/
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/device_drivers/intel/ipw2100.txt b/Documentation/networking/device_drivers/intel/ipw2100.txt
new file mode 100644 (file)
index 0000000..6f85e1d
--- /dev/null
@@ -0,0 +1,293 @@
+
+Intel(R) PRO/Wireless 2100 Driver for Linux in support of:
+
+Intel(R) PRO/Wireless 2100 Network Connection
+
+Copyright (C) 2003-2006, Intel Corporation
+
+README.ipw2100
+
+Version: git-1.1.5
+Date   : January 25, 2006
+
+Index
+-----------------------------------------------
+0. IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+1. Introduction
+2. Release git-1.1.5 Current Features
+3. Command Line Parameters
+4. Sysfs Helper Files
+5. Radio Kill Switch
+6. Dynamic Firmware
+7. Power Management
+8. Support
+9. License
+
+
+0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+-----------------------------------------------
+
+Important Notice FOR ALL USERS OR DISTRIBUTORS!!!!
+
+Intel wireless LAN adapters are engineered, manufactured, tested, and
+quality checked to ensure that they meet all necessary local and
+governmental regulatory agency requirements for the regions that they
+are designated and/or marked to ship into. Since wireless LANs are
+generally unlicensed devices that share spectrum with radars,
+satellites, and other licensed and unlicensed devices, it is sometimes
+necessary to dynamically detect, avoid, and limit usage to avoid
+interference with these devices. In many instances Intel is required to
+provide test data to prove regional and local compliance to regional and
+governmental regulations before certification or approval to use the
+product is granted. Intel's wireless LAN's EEPROM, firmware, and
+software driver are designed to carefully control parameters that affect
+radio operation and to ensure electromagnetic compliance (EMC). These
+parameters include, without limitation, RF power, spectrum usage,
+channel scanning, and human exposure.
+
+For these reasons Intel cannot permit any manipulation by third parties
+of the software provided in binary format with the wireless WLAN
+adapters (e.g., the EEPROM and firmware). Furthermore, if you use any
+patches, utilities, or code with the Intel wireless LAN adapters that
+have been manipulated by an unauthorized party (i.e., patches,
+utilities, or code (including open source code modifications) which have
+not been validated by Intel), (i) you will be solely responsible for
+ensuring the regulatory compliance of the products, (ii) Intel will bear
+no liability, under any theory of liability for any issues associated
+with the modified products, including without limitation, claims under
+the warranty and/or issues arising from regulatory non-compliance, and
+(iii) Intel will not provide or be required to assist in providing
+support to any third parties for such modified products.
+
+Note: Many regulatory agencies consider Wireless LAN adapters to be
+modules, and accordingly, condition system-level regulatory approval
+upon receipt and review of test data documenting that the antennas and
+system configuration do not cause the EMC and radio operation to be
+non-compliant.
+
+The drivers available for download from SourceForge are provided as a
+part of a development project.  Conformance to local regulatory
+requirements is the responsibility of the individual developer.  As
+such, if you are interested in deploying or shipping a driver as part of
+solution intended to be used for purposes other than development, please
+obtain a tested driver from Intel Customer Support at:
+
+http://www.intel.com/support/wireless/sb/CS-006408.htm
+
+1. Introduction
+-----------------------------------------------
+
+This document provides a brief overview of the features supported by the 
+IPW2100 driver project.  The main project website, where the latest 
+development version of the driver can be found, is:
+
+       http://ipw2100.sourceforge.net
+
+There you can find the not only the latest releases, but also information about
+potential fixes and patches, as well as links to the development mailing list
+for the driver project.
+
+
+2. Release git-1.1.5 Current Supported Features
+-----------------------------------------------
+- Managed (BSS) and Ad-Hoc (IBSS)
+- WEP (shared key and open)
+- Wireless Tools support 
+- 802.1x (tested with XSupplicant 1.0.1)
+
+Enabled (but not supported) features:
+- Monitor/RFMon mode
+- WPA/WPA2
+
+The distinction between officially supported and enabled is a reflection
+on the amount of validation and interoperability testing that has been
+performed on a given feature.
+
+
+3. Command Line Parameters
+-----------------------------------------------
+
+If the driver is built as a module, the following optional parameters are used
+by entering them on the command line with the modprobe command using this
+syntax:
+
+       modprobe ipw2100 [<option>=<VAL1><,VAL2>...]
+
+For example, to disable the radio on driver loading, enter:
+
+       modprobe ipw2100 disable=1
+
+The ipw2100 driver supports the following module parameters:
+
+Name           Value           Example:
+debug          0x0-0xffffffff  debug=1024
+mode           0,1,2           mode=1   /* AdHoc */
+channel                int             channel=3 /* Only valid in AdHoc or Monitor */
+associate      boolean         associate=0 /* Do NOT auto associate */
+disable                boolean         disable=1 /* Do not power the HW */
+
+
+4. Sysfs Helper Files
+---------------------------     
+-----------------------------------------------
+
+There are several ways to control the behavior of the driver.  Many of the 
+general capabilities are exposed through the Wireless Tools (iwconfig).  There
+are a few capabilities that are exposed through entries in the Linux Sysfs.
+
+
+----- Driver Level ------
+For the driver level files, look in /sys/bus/pci/drivers/ipw2100/
+
+  debug_level  
+       
+       This controls the same global as the 'debug' module parameter.  For 
+        information on the various debugging levels available, run the 'dvals'
+       script found in the driver source directory.
+
+       NOTE:  'debug_level' is only enabled if CONFIG_IPW2100_DEBUG is turn
+              on.
+
+----- Device Level ------
+For the device level files look in
+       
+       /sys/bus/pci/drivers/ipw2100/{PCI-ID}/
+
+For example:
+       /sys/bus/pci/drivers/ipw2100/0000:02:01.0
+
+For the device level files, see /sys/bus/pci/drivers/ipw2100:
+
+  rf_kill
+       read - 
+       0 = RF kill not enabled (radio on)
+       1 = SW based RF kill active (radio off)
+       2 = HW based RF kill active (radio off)
+       3 = Both HW and SW RF kill active (radio off)
+       write -
+       0 = If SW based RF kill active, turn the radio back on
+       1 = If radio is on, activate SW based RF kill
+
+       NOTE: If you enable the SW based RF kill and then toggle the HW
+       based RF kill from ON -> OFF -> ON, the radio will NOT come back on
+
+
+5. Radio Kill Switch
+-----------------------------------------------
+Most laptops provide the ability for the user to physically disable the radio.
+Some vendors have implemented this as a physical switch that requires no
+software to turn the radio off and on.  On other laptops, however, the switch
+is controlled through a button being pressed and a software driver then making
+calls to turn the radio off and on.  This is referred to as a "software based
+RF kill switch"
+
+See the Sysfs helper file 'rf_kill' for determining the state of the RF switch
+on your system.
+
+
+6. Dynamic Firmware
+-----------------------------------------------
+As the firmware is licensed under a restricted use license, it can not be 
+included within the kernel sources.  To enable the IPW2100 you will need a 
+firmware image to load into the wireless NIC's processors.
+
+You can obtain these images from <http://ipw2100.sf.net/firmware.php>.
+
+See INSTALL for instructions on installing the firmware.
+
+
+7. Power Management
+-----------------------------------------------
+The IPW2100 supports the configuration of the Power Save Protocol 
+through a private wireless extension interface.  The IPW2100 supports 
+the following different modes:
+
+       off     No power management.  Radio is always on.
+       on      Automatic power management
+       1-5     Different levels of power management.  The higher the 
+               number the greater the power savings, but with an impact to 
+               packet latencies. 
+
+Power management works by powering down the radio after a certain 
+interval of time has passed where no packets are passed through the 
+radio.  Once powered down, the radio remains in that state for a given 
+period of time.  For higher power savings, the interval between last 
+packet processed to sleep is shorter and the sleep period is longer.
+
+When the radio is asleep, the access point sending data to the station 
+must buffer packets at the AP until the station wakes up and requests 
+any buffered packets.  If you have an AP that does not correctly support 
+the PSP protocol you may experience packet loss or very poor performance 
+while power management is enabled.  If this is the case, you will need 
+to try and find a firmware update for your AP, or disable power 
+management (via `iwconfig eth1 power off`)
+
+To configure the power level on the IPW2100 you use a combination of 
+iwconfig and iwpriv.  iwconfig is used to turn power management on, off, 
+and set it to auto.
+
+       iwconfig eth1 power off    Disables radio power down
+       iwconfig eth1 power on     Enables radio power management to 
+                                  last set level (defaults to AUTO)
+       iwpriv eth1 set_power 0    Sets power level to AUTO and enables 
+                                  power management if not previously 
+                                  enabled.
+       iwpriv eth1 set_power 1-5  Set the power level as specified, 
+                                  enabling power management if not 
+                                  previously enabled.
+
+You can view the current power level setting via:
+       
+       iwpriv eth1 get_power
+
+It will return the current period or timeout that is configured as a string
+in the form of xxxx/yyyy (z) where xxxx is the timeout interval (amount of
+time after packet processing), yyyy is the period to sleep (amount of time to 
+wait before powering the radio and querying the access point for buffered
+packets), and z is the 'power level'.  If power management is turned off the
+xxxx/yyyy will be replaced with 'off' -- the level reported will be the active
+level if `iwconfig eth1 power on` is invoked.
+
+
+8. Support
+-----------------------------------------------
+
+For general development information and support,
+go to:
+       
+    http://ipw2100.sf.net/
+
+The ipw2100 1.1.0 driver and firmware can be downloaded from:  
+
+    http://support.intel.com
+
+For installation support on the ipw2100 1.1.0 driver on Linux kernels 
+2.6.8 or greater, email support is available from:  
+
+    http://supportmail.intel.com
+
+9. License
+-----------------------------------------------
+
+  Copyright(c) 2003 - 2006 Intel Corporation. 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 (version 2) 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.
+  
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59 
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+  
+  License Contact Information:
+  James P. Ketrenos <ipw2100-admin@linux.intel.com>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
diff --git a/Documentation/networking/device_drivers/intel/ipw2200.txt b/Documentation/networking/device_drivers/intel/ipw2200.txt
new file mode 100644 (file)
index 0000000..b7658be
--- /dev/null
@@ -0,0 +1,472 @@
+
+Intel(R) PRO/Wireless 2915ABG Driver for Linux in support of:
+
+Intel(R) PRO/Wireless 2200BG Network Connection
+Intel(R) PRO/Wireless 2915ABG Network Connection
+
+Note: The Intel(R) PRO/Wireless 2915ABG Driver for Linux and Intel(R)
+PRO/Wireless 2200BG Driver for Linux is a unified driver that works on
+both hardware adapters listed above. In this document the Intel(R)
+PRO/Wireless 2915ABG Driver for Linux will be used to reference the
+unified driver.
+
+Copyright (C) 2004-2006, Intel Corporation
+
+README.ipw2200
+
+Version: 1.1.2
+Date   : March 30, 2006
+
+
+Index
+-----------------------------------------------
+0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+1.   Introduction
+1.1. Overview of features
+1.2. Module parameters
+1.3. Wireless Extension Private Methods
+1.4. Sysfs Helper Files
+1.5. Supported channels
+2.   Ad-Hoc Networking
+3.   Interacting with Wireless Tools
+3.1. iwconfig mode
+3.2. iwconfig sens
+4.   About the Version Numbers
+5.   Firmware installation
+6.   Support
+7.   License
+
+
+0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+-----------------------------------------------
+
+Important Notice FOR ALL USERS OR DISTRIBUTORS!!!! 
+
+Intel wireless LAN adapters are engineered, manufactured, tested, and
+quality checked to ensure that they meet all necessary local and
+governmental regulatory agency requirements for the regions that they
+are designated and/or marked to ship into. Since wireless LANs are
+generally unlicensed devices that share spectrum with radars,
+satellites, and other licensed and unlicensed devices, it is sometimes
+necessary to dynamically detect, avoid, and limit usage to avoid
+interference with these devices. In many instances Intel is required to
+provide test data to prove regional and local compliance to regional and
+governmental regulations before certification or approval to use the
+product is granted. Intel's wireless LAN's EEPROM, firmware, and
+software driver are designed to carefully control parameters that affect
+radio operation and to ensure electromagnetic compliance (EMC). These
+parameters include, without limitation, RF power, spectrum usage,
+channel scanning, and human exposure. 
+
+For these reasons Intel cannot permit any manipulation by third parties
+of the software provided in binary format with the wireless WLAN
+adapters (e.g., the EEPROM and firmware). Furthermore, if you use any
+patches, utilities, or code with the Intel wireless LAN adapters that
+have been manipulated by an unauthorized party (i.e., patches,
+utilities, or code (including open source code modifications) which have
+not been validated by Intel), (i) you will be solely responsible for
+ensuring the regulatory compliance of the products, (ii) Intel will bear
+no liability, under any theory of liability for any issues associated
+with the modified products, including without limitation, claims under
+the warranty and/or issues arising from regulatory non-compliance, and
+(iii) Intel will not provide or be required to assist in providing
+support to any third parties for such modified products.  
+
+Note: Many regulatory agencies consider Wireless LAN adapters to be
+modules, and accordingly, condition system-level regulatory approval
+upon receipt and review of test data documenting that the antennas and
+system configuration do not cause the EMC and radio operation to be
+non-compliant.
+
+The drivers available for download from SourceForge are provided as a 
+part of a development project.  Conformance to local regulatory 
+requirements is the responsibility of the individual developer.  As 
+such, if you are interested in deploying or shipping a driver as part of 
+solution intended to be used for purposes other than development, please 
+obtain a tested driver from Intel Customer Support at:
+
+http://support.intel.com
+
+
+1.   Introduction
+-----------------------------------------------
+The following sections attempt to provide a brief introduction to using 
+the Intel(R) PRO/Wireless 2915ABG Driver for Linux.
+
+This document is not meant to be a comprehensive manual on 
+understanding or using wireless technologies, but should be sufficient 
+to get you moving without wires on Linux.
+
+For information on building and installing the driver, see the INSTALL
+file.
+
+
+1.1. Overview of Features
+-----------------------------------------------
+The current release (1.1.2) supports the following features:
+
++ BSS mode (Infrastructure, Managed)
++ IBSS mode (Ad-Hoc)
++ WEP (OPEN and SHARED KEY mode)
++ 802.1x EAP via wpa_supplicant and xsupplicant
++ Wireless Extension support 
++ Full B and G rate support (2200 and 2915)
++ Full A rate support (2915 only)
++ Transmit power control
++ S state support (ACPI suspend/resume)
+
+The following features are currently enabled, but not officially
+supported:
+
++ WPA
++ long/short preamble support
++ Monitor mode (aka RFMon)
+
+The distinction between officially supported and enabled is a reflection 
+on the amount of validation and interoperability testing that has been
+performed on a given feature. 
+
+
+
+1.2. Command Line Parameters
+-----------------------------------------------
+
+Like many modules used in the Linux kernel, the Intel(R) PRO/Wireless
+2915ABG Driver for Linux allows configuration options to be provided 
+as module parameters.  The most common way to specify a module parameter 
+is via the command line.  
+
+The general form is:
+
+% modprobe ipw2200 parameter=value
+
+Where the supported parameter are:
+
+  associate
+       Set to 0 to disable the auto scan-and-associate functionality of the
+       driver.  If disabled, the driver will not attempt to scan 
+       for and associate to a network until it has been configured with 
+       one or more properties for the target network, for example configuring 
+       the network SSID.  Default is 0 (do not auto-associate)
+       
+       Example: % modprobe ipw2200 associate=0
+
+  auto_create
+       Set to 0 to disable the auto creation of an Ad-Hoc network 
+       matching the channel and network name parameters provided.  
+       Default is 1.
+
+  channel
+       channel number for association.  The normal method for setting
+        the channel would be to use the standard wireless tools
+        (i.e. `iwconfig eth1 channel 10`), but it is useful sometimes
+       to set this while debugging.  Channel 0 means 'ANY'
+
+  debug
+       If using a debug build, this is used to control the amount of debug
+       info is logged.  See the 'dvals' and 'load' script for more info on
+       how to use this (the dvals and load scripts are provided as part 
+       of the ipw2200 development snapshot releases available from the 
+       SourceForge project at http://ipw2200.sf.net)
+  
+  led
+       Can be used to turn on experimental LED code.
+       0 = Off, 1 = On.  Default is 1.
+
+  mode
+       Can be used to set the default mode of the adapter.  
+       0 = Managed, 1 = Ad-Hoc, 2 = Monitor
+
+
+1.3. Wireless Extension Private Methods
+-----------------------------------------------
+
+As an interface designed to handle generic hardware, there are certain 
+capabilities not exposed through the normal Wireless Tool interface.  As 
+such, a provision is provided for a driver to declare custom, or 
+private, methods.  The Intel(R) PRO/Wireless 2915ABG Driver for Linux 
+defines several of these to configure various settings.
+
+The general form of using the private wireless methods is:
+
+       % iwpriv $IFNAME method parameters
+
+Where $IFNAME is the interface name the device is registered with 
+(typically eth1, customized via one of the various network interface
+name managers, such as ifrename)
+
+The supported private methods are:
+
+  get_mode
+       Can be used to report out which IEEE mode the driver is 
+       configured to support.  Example:
+       
+       % iwpriv eth1 get_mode
+       eth1    get_mode:802.11bg (6)
+
+  set_mode
+       Can be used to configure which IEEE mode the driver will 
+       support.  
+
+       Usage:
+       % iwpriv eth1 set_mode {mode}
+       Where {mode} is a number in the range 1-7:
+       1       802.11a (2915 only)
+       2       802.11b
+       3       802.11ab (2915 only)
+       4       802.11g 
+       5       802.11ag (2915 only)
+       6       802.11bg
+       7       802.11abg (2915 only)
+
+  get_preamble
+       Can be used to report configuration of preamble length.
+
+  set_preamble
+       Can be used to set the configuration of preamble length:
+
+       Usage:
+       % iwpriv eth1 set_preamble {mode}
+       Where {mode} is one of:
+       1       Long preamble only
+       0       Auto (long or short based on connection)
+       
+
+1.4. Sysfs Helper Files:
+-----------------------------------------------
+
+The Linux kernel provides a pseudo file system that can be used to 
+access various components of the operating system.  The Intel(R)
+PRO/Wireless 2915ABG Driver for Linux exposes several configuration
+parameters through this mechanism.
+
+An entry in the sysfs can support reading and/or writing.  You can 
+typically query the contents of a sysfs entry through the use of cat, 
+and can set the contents via echo.  For example:
+
+% cat /sys/bus/pci/drivers/ipw2200/debug_level
+
+Will report the current debug level of the driver's logging subsystem 
+(only available if CONFIG_IPW2200_DEBUG was configured when the driver
+was built).
+
+You can set the debug level via:
+
+% echo $VALUE > /sys/bus/pci/drivers/ipw2200/debug_level
+
+Where $VALUE would be a number in the case of this sysfs entry.  The 
+input to sysfs files does not have to be a number.  For example, the 
+firmware loader used by hotplug utilizes sysfs entries for transferring 
+the firmware image from user space into the driver.
+
+The Intel(R) PRO/Wireless 2915ABG Driver for Linux exposes sysfs entries 
+at two levels -- driver level, which apply to all instances of the driver 
+(in the event that there are more than one device installed) and device 
+level, which applies only to the single specific instance.
+
+
+1.4.1 Driver Level Sysfs Helper Files
+-----------------------------------------------
+
+For the driver level files, look in /sys/bus/pci/drivers/ipw2200/
+
+  debug_level  
+       
+       This controls the same global as the 'debug' module parameter
+
+
+
+1.4.2 Device Level Sysfs Helper Files
+-----------------------------------------------
+
+For the device level files, look in
+       
+       /sys/bus/pci/drivers/ipw2200/{PCI-ID}/
+
+For example:
+       /sys/bus/pci/drivers/ipw2200/0000:02:01.0
+
+For the device level files, see /sys/bus/pci/drivers/ipw2200:
+
+  rf_kill
+       read - 
+       0 = RF kill not enabled (radio on)
+       1 = SW based RF kill active (radio off)
+       2 = HW based RF kill active (radio off)
+       3 = Both HW and SW RF kill active (radio off)
+       write -
+       0 = If SW based RF kill active, turn the radio back on
+       1 = If radio is on, activate SW based RF kill
+
+       NOTE: If you enable the SW based RF kill and then toggle the HW
+       based RF kill from ON -> OFF -> ON, the radio will NOT come back on
+       
+  ucode 
+       read-only access to the ucode version number
+
+  led
+       read -
+       0 = LED code disabled
+       1 = LED code enabled
+       write -
+       0 = Disable LED code
+       1 = Enable LED code
+
+       NOTE: The LED code has been reported to hang some systems when 
+       running ifconfig and is therefore disabled by default.
+
+
+1.5. Supported channels
+-----------------------------------------------
+
+Upon loading the Intel(R) PRO/Wireless 2915ABG Driver for Linux, a
+message stating the detected geography code and the number of 802.11
+channels supported by the card will be displayed in the log.
+
+The geography code corresponds to a regulatory domain as shown in the
+table below.
+
+                                         Supported channels
+Code   Geography                       802.11bg        802.11a
+
+---    Restricted                      11               0
+ZZF    Custom US/Canada                11               8
+ZZD    Rest of World                   13               0
+ZZA    Custom USA & Europe & High      11              13
+ZZB    Custom NA & Europe              11              13
+ZZC    Custom Japan                    11               4
+ZZM    Custom                          11               0
+ZZE    Europe                          13              19
+ZZJ    Custom Japan                    14               4
+ZZR    Rest of World                   14               0
+ZZH    High Band                       13               4
+ZZG    Custom Europe                   13               4
+ZZK    Europe                          13              24
+ZZL    Europe                          11              13
+
+
+2.   Ad-Hoc Networking
+-----------------------------------------------
+
+When using a device in an Ad-Hoc network, it is useful to understand the 
+sequence and requirements for the driver to be able to create, join, or 
+merge networks.
+
+The following attempts to provide enough information so that you can 
+have a consistent experience while using the driver as a member of an 
+Ad-Hoc network.
+
+2.1. Joining an Ad-Hoc Network
+-----------------------------------------------
+
+The easiest way to get onto an Ad-Hoc network is to join one that 
+already exists.
+
+2.2. Creating an Ad-Hoc Network
+-----------------------------------------------
+
+An Ad-Hoc networks is created using the syntax of the Wireless tool.
+
+For Example:
+iwconfig eth1 mode ad-hoc essid testing channel 2
+
+2.3. Merging Ad-Hoc Networks
+-----------------------------------------------
+
+
+3.  Interaction with Wireless Tools
+-----------------------------------------------
+
+3.1 iwconfig mode
+-----------------------------------------------
+
+When configuring the mode of the adapter, all run-time configured parameters
+are reset to the value used when the module was loaded.  This includes
+channels, rates, ESSID, etc.
+
+3.2 iwconfig sens
+-----------------------------------------------
+
+The 'iwconfig ethX sens XX' command will not set the signal sensitivity
+threshold, as described in iwconfig documentation, but rather the number
+of consecutive missed beacons that will trigger handover, i.e. roaming
+to another access point. At the same time, it will set the disassociation
+threshold to 3 times the given value.
+
+
+4.   About the Version Numbers
+-----------------------------------------------
+
+Due to the nature of open source development projects, there are 
+frequently changes being incorporated that have not gone through 
+a complete validation process.  These changes are incorporated into 
+development snapshot releases.
+
+Releases are numbered with a three level scheme: 
+
+       major.minor.development
+
+Any version where the 'development' portion is 0 (for example
+1.0.0, 1.1.0, etc.) indicates a stable version that will be made 
+available for kernel inclusion.
+
+Any version where the 'development' portion is not a 0 (for
+example 1.0.1, 1.1.5, etc.) indicates a development version that is
+being made available for testing and cutting edge users.  The stability 
+and functionality of the development releases are not know.  We make
+efforts to try and keep all snapshots reasonably stable, but due to the
+frequency of their release, and the desire to get those releases 
+available as quickly as possible, unknown anomalies should be expected.
+
+The major version number will be incremented when significant changes
+are made to the driver.  Currently, there are no major changes planned.
+
+5.  Firmware installation
+----------------------------------------------
+
+The driver requires a firmware image, download it and extract the
+files under /lib/firmware (or wherever your hotplug's firmware.agent
+will look for firmware files)
+
+The firmware can be downloaded from the following URL:
+
+    http://ipw2200.sf.net/
+
+
+6.  Support
+-----------------------------------------------
+
+For direct support of the 1.0.0 version, you can contact 
+http://supportmail.intel.com, or you can use the open source project
+support.
+
+For general information and support, go to:
+       
+    http://ipw2200.sf.net/
+
+
+7.  License
+-----------------------------------------------
+
+  Copyright(c) 2003 - 2006 Intel Corporation. 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 version 2 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.
+  
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59 
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+  
+  Contact Information:
+  James P. Ketrenos <ipw2100-admin@linux.intel.com>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
diff --git a/Documentation/networking/device_drivers/intel/ixgb.rst b/Documentation/networking/device_drivers/intel/ixgb.rst
new file mode 100644 (file)
index 0000000..8bd80e2
--- /dev/null
@@ -0,0 +1,467 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux Base Driver for 10 Gigabit Intel(R) Ethernet Network Connection
+=====================================================================
+
+October 1, 2018
+
+
+Contents
+========
+
+- In This Release
+- Identifying Your Adapter
+- Command Line Parameters
+- Improving Performance
+- Additional Configurations
+- Known Issues/Troubleshooting
+- Support
+
+
+
+In This Release
+===============
+
+This file describes the ixgb Linux Base Driver for the 10 Gigabit Intel(R)
+Network Connection.  This driver includes support for Itanium(R)2-based
+systems.
+
+For questions related to hardware requirements, refer to the documentation
+supplied with your 10 Gigabit adapter.  All hardware requirements listed apply
+to use with Linux.
+
+The following features are available in this kernel:
+ - Native VLANs
+ - Channel Bonding (teaming)
+ - SNMP
+
+Channel Bonding documentation can be found in the Linux kernel source:
+/Documentation/networking/bonding.txt
+
+The driver information previously displayed in the /proc filesystem is not
+supported in this release.  Alternatively, you can use ethtool (version 1.6
+or later), lspci, and iproute2 to obtain the same information.
+
+Instructions on updating ethtool can be found in the section "Additional
+Configurations" later in this document.
+
+
+Identifying Your Adapter
+========================
+
+The following Intel network adapters are compatible with the drivers in this
+release:
+
++------------+------------------------------+----------------------------------+
+| Controller | Adapter Name                 | Physical Layer                   |
++============+==============================+==================================+
+| 82597EX    | Intel(R) PRO/10GbE LR/SR/CX4 | - 10G Base-LR (fiber)            |
+|            | Server Adapters              | - 10G Base-SR (fiber)            |
+|            |                              | - 10G Base-CX4 (copper)          |
++------------+------------------------------+----------------------------------+
+
+For more information on how to identify your adapter, go to the Adapter &
+Driver ID Guide at:
+
+    https://support.intel.com
+
+
+Command Line Parameters
+=======================
+
+If the driver is built as a module, the  following optional parameters are
+used by entering them on the command line with the modprobe command using
+this syntax::
+
+    modprobe ixgb [<option>=<VAL1>,<VAL2>,...]
+
+For example, with two 10GbE PCI adapters, entering::
+
+    modprobe ixgb TxDescriptors=80,128
+
+loads the ixgb driver with 80 TX resources for the first adapter and 128 TX
+resources for the second adapter.
+
+The default value for each parameter is generally the recommended setting,
+unless otherwise noted.
+
+Copybreak
+---------
+:Valid Range: 0-XXXX
+:Default Value: 256
+
+    This is the maximum size of packet that is copied to a new buffer on
+    receive.
+
+Debug
+-----
+:Valid Range: 0-16 (0=none,...,16=all)
+:Default Value: 0
+
+    This parameter adjusts the level of debug messages displayed in the
+    system logs.
+
+FlowControl
+-----------
+:Valid Range: 0-3 (0=none, 1=Rx only, 2=Tx only, 3=Rx&Tx)
+:Default Value: 1 if no EEPROM, otherwise read from EEPROM
+
+    This parameter controls the automatic generation(Tx) and response(Rx) to
+    Ethernet PAUSE frames.  There are hardware bugs associated with enabling
+    Tx flow control so beware.
+
+RxDescriptors
+-------------
+:Valid Range: 64-4096
+:Default Value: 1024
+
+    This value is the number of receive descriptors allocated by the driver.
+    Increasing this value allows the driver to buffer more incoming packets.
+    Each descriptor is 16 bytes.  A receive buffer is also allocated for
+    each descriptor and can be either 2048, 4056, 8192, or 16384 bytes,
+    depending on the MTU setting.  When the MTU size is 1500 or less, the
+    receive buffer size is 2048 bytes. When the MTU is greater than 1500 the
+    receive buffer size will be either 4056, 8192, or 16384 bytes.  The
+    maximum MTU size is 16114.
+
+TxDescriptors
+-------------
+:Valid Range: 64-4096
+:Default Value: 256
+
+    This value is the number of transmit descriptors allocated by the driver.
+    Increasing this value allows the driver to queue more transmits.  Each
+    descriptor is 16 bytes.
+
+RxIntDelay
+----------
+:Valid Range: 0-65535 (0=off)
+:Default Value: 72
+
+    This value delays the generation of receive interrupts in units of
+    0.8192 microseconds.  Receive interrupt reduction can improve CPU
+    efficiency if properly tuned for specific network traffic.  Increasing
+    this value adds extra latency to frame reception and can end up
+    decreasing the throughput of TCP traffic.  If the system is reporting
+    dropped receives, this value may be set too high, causing the driver to
+    run out of available receive descriptors.
+
+TxIntDelay
+----------
+:Valid Range: 0-65535 (0=off)
+:Default Value: 32
+
+    This value delays the generation of transmit interrupts in units of
+    0.8192 microseconds.  Transmit interrupt reduction can improve CPU
+    efficiency if properly tuned for specific network traffic.  Increasing
+    this value adds extra latency to frame transmission and can end up
+    decreasing the throughput of TCP traffic.  If this value is set too high,
+    it will cause the driver to run out of available transmit descriptors.
+
+XsumRX
+------
+:Valid Range: 0-1
+:Default Value: 1
+
+    A value of '1' indicates that the driver should enable IP checksum
+    offload for received packets (both UDP and TCP) to the adapter hardware.
+
+RxFCHighThresh
+--------------
+:Valid Range: 1,536-262,136 (0x600 - 0x3FFF8, 8 byte granularity)
+:Default Value: 196,608 (0x30000)
+
+    Receive Flow control high threshold (when we send a pause frame)
+
+RxFCLowThresh
+-------------
+:Valid Range: 64-262,136 (0x40 - 0x3FFF8, 8 byte granularity)
+:Default Value: 163,840 (0x28000)
+
+    Receive Flow control low threshold (when we send a resume frame)
+
+FCReqTimeout
+------------
+:Valid Range: 1-65535
+:Default Value: 65535
+
+    Flow control request timeout (how long to pause the link partner's tx)
+
+IntDelayEnable
+--------------
+:Value Range: 0,1
+:Default Value: 1
+
+    Interrupt Delay, 0 disables transmit interrupt delay and 1 enables it.
+
+
+Improving Performance
+=====================
+
+With the 10 Gigabit server adapters, the default Linux configuration will
+very likely limit the total available throughput artificially.  There is a set
+of configuration changes that, when applied together, will increase the ability
+of Linux to transmit and receive data.  The following enhancements were
+originally acquired from settings published at http://www.spec.org/web99/ for
+various submitted results using Linux.
+
+NOTE:
+  These changes are only suggestions, and serve as a starting point for
+  tuning your network performance.
+
+The changes are made in three major ways, listed in order of greatest effect:
+
+- Use ip link to modify the mtu (maximum transmission unit) and the txqueuelen
+  parameter.
+- Use sysctl to modify /proc parameters (essentially kernel tuning)
+- Use setpci to modify the MMRBC field in PCI-X configuration space to increase
+  transmit burst lengths on the bus.
+
+NOTE:
+  setpci modifies the adapter's configuration registers to allow it to read
+  up to 4k bytes at a time (for transmits).  However, for some systems the
+  behavior after modifying this register may be undefined (possibly errors of
+  some kind).  A power-cycle, hard reset or explicitly setting the e6 register
+  back to 22 (setpci -d 8086:1a48 e6.b=22) may be required to get back to a
+  stable configuration.
+
+- COPY these lines and paste them into ixgb_perf.sh:
+
+::
+
+  #!/bin/bash
+  echo "configuring network performance , edit this file to change the interface
+  or device ID of 10GbE card"
+  # set mmrbc to 4k reads, modify only Intel 10GbE device IDs
+  # replace 1a48 with appropriate 10GbE device's ID installed on the system,
+  # if needed.
+  setpci -d 8086:1a48 e6.b=2e
+  # set the MTU (max transmission unit) - it requires your switch and clients
+  # to change as well.
+  # set the txqueuelen
+  # your ixgb adapter should be loaded as eth1 for this to work, change if needed
+  ip li set dev eth1 mtu 9000 txqueuelen 1000 up
+  # call the sysctl utility to modify /proc/sys entries
+  sysctl -p ./sysctl_ixgb.conf
+
+- COPY these lines and paste them into sysctl_ixgb.conf:
+
+::
+
+  # some of the defaults may be different for your kernel
+  # call this file with sysctl -p <this file>
+  # these are just suggested values that worked well to increase throughput in
+  # several network benchmark tests, your mileage may vary
+
+  ### IPV4 specific settings
+  # turn TCP timestamp support off, default 1, reduces CPU use
+  net.ipv4.tcp_timestamps = 0
+  # turn SACK support off, default on
+  # on systems with a VERY fast bus -> memory interface this is the big gainer
+  net.ipv4.tcp_sack = 0
+  # set min/default/max TCP read buffer, default 4096 87380 174760
+  net.ipv4.tcp_rmem = 10000000 10000000 10000000
+  # set min/pressure/max TCP write buffer, default 4096 16384 131072
+  net.ipv4.tcp_wmem = 10000000 10000000 10000000
+  # set min/pressure/max TCP buffer space, default 31744 32256 32768
+  net.ipv4.tcp_mem = 10000000 10000000 10000000
+
+  ### CORE settings (mostly for socket and UDP effect)
+  # set maximum receive socket buffer size, default 131071
+  net.core.rmem_max = 524287
+  # set maximum send socket buffer size, default 131071
+  net.core.wmem_max = 524287
+  # set default receive socket buffer size, default 65535
+  net.core.rmem_default = 524287
+  # set default send socket buffer size, default 65535
+  net.core.wmem_default = 524287
+  # set maximum amount of option memory buffers, default 10240
+  net.core.optmem_max = 524287
+  # set number of unprocessed input packets before kernel starts dropping them; default 300
+  net.core.netdev_max_backlog = 300000
+
+Edit the ixgb_perf.sh script if necessary to change eth1 to whatever interface
+your ixgb driver is using and/or replace '1a48' with appropriate 10GbE device's
+ID installed on the system.
+
+NOTE:
+  Unless these scripts are added to the boot process, these changes will
+  only last only until the next system reboot.
+
+
+Resolving Slow UDP Traffic
+--------------------------
+If your server does not seem to be able to receive UDP traffic as fast as it
+can receive TCP traffic, it could be because Linux, by default, does not set
+the network stack buffers as large as they need to be to support high UDP
+transfer rates.  One way to alleviate this problem is to allow more memory to
+be used by the IP stack to store incoming data.
+
+For instance, use the commands::
+
+    sysctl -w net.core.rmem_max=262143
+
+and::
+
+    sysctl -w net.core.rmem_default=262143
+
+to increase the read buffer memory max and default to 262143 (256k - 1) from
+defaults of max=131071 (128k - 1) and default=65535 (64k - 1).  These variables
+will increase the amount of memory used by the network stack for receives, and
+can be increased significantly more if necessary for your application.
+
+
+Additional Configurations
+=========================
+
+Configuring the Driver on Different Distributions
+-------------------------------------------------
+Configuring a network driver to load properly when the system is started is
+distribution dependent. Typically, the configuration process involves adding
+an alias line to /etc/modprobe.conf as well as editing other system startup
+scripts and/or configuration files.  Many popular Linux distributions ship
+with tools to make these changes for you.  To learn the proper way to
+configure a network device for your system, refer to your distribution
+documentation.  If during this process you are asked for the driver or module
+name, the name for the Linux Base Driver for the Intel 10GbE Family of
+Adapters is ixgb.
+
+Viewing Link Messages
+---------------------
+Link messages will not be displayed to the console if the distribution is
+restricting system messages. In order to see network driver link messages on
+your console, set dmesg to eight by entering the following::
+
+    dmesg -n 8
+
+NOTE: This setting is not saved across reboots.
+
+Jumbo Frames
+------------
+The driver supports Jumbo Frames for all adapters. Jumbo Frames support is
+enabled by changing the MTU to a value larger than the default of 1500.
+The maximum value for the MTU is 16114.  Use the ip command to
+increase the MTU size.  For example::
+
+    ip li set dev ethx mtu 9000
+
+The maximum MTU setting for Jumbo Frames is 16114.  This value coincides
+with the maximum Jumbo Frames size of 16128.
+
+Ethtool
+-------
+The driver utilizes the ethtool interface for driver configuration and
+diagnostics, as well as displaying statistical information.  The ethtool
+version 1.6 or later is required for this functionality.
+
+The latest release of ethtool can be found from
+https://www.kernel.org/pub/software/network/ethtool/
+
+NOTE:
+  The ethtool version 1.6 only supports a limited set of ethtool options.
+  Support for a more complete ethtool feature set can be enabled by
+  upgrading to the latest version.
+
+NAPI
+----
+NAPI (Rx polling mode) is supported in the ixgb driver.
+
+See https://wiki.linuxfoundation.org/networking/napi for more information on
+NAPI.
+
+
+Known Issues/Troubleshooting
+============================
+
+NOTE:
+  After installing the driver, if your Intel Network Connection is not
+  working, verify in the "In This Release" section of the readme that you have
+  installed the correct driver.
+
+Cable Interoperability Issue with Fujitsu XENPAK Module in SmartBits Chassis
+----------------------------------------------------------------------------
+Excessive CRC errors may be observed if the Intel(R) PRO/10GbE CX4
+Server adapter is connected to a Fujitsu XENPAK CX4 module in a SmartBits
+chassis using 15 m/24AWG cable assemblies manufactured by Fujitsu or Leoni.
+The CRC errors may be received either by the Intel(R) PRO/10GbE CX4
+Server adapter or the SmartBits. If this situation occurs using a different
+cable assembly may resolve the issue.
+
+Cable Interoperability Issues with HP Procurve 3400cl Switch Port
+-----------------------------------------------------------------
+Excessive CRC errors may be observed if the Intel(R) PRO/10GbE CX4 Server
+adapter is connected to an HP Procurve 3400cl switch port using short cables
+(1 m or shorter). If this situation occurs, using a longer cable may resolve
+the issue.
+
+Excessive CRC errors may be observed using Fujitsu 24AWG cable assemblies that
+Are 10 m or longer or where using a Leoni 15 m/24AWG cable assembly. The CRC
+errors may be received either by the CX4 Server adapter or at the switch. If
+this situation occurs, using a different cable assembly may resolve the issue.
+
+Jumbo Frames System Requirement
+-------------------------------
+Memory allocation failures have been observed on Linux systems with 64 MB
+of RAM or less that are running Jumbo Frames.  If you are using Jumbo
+Frames, your system may require more than the advertised minimum
+requirement of 64 MB of system memory.
+
+Performance Degradation with Jumbo Frames
+-----------------------------------------
+Degradation in throughput performance may be observed in some Jumbo frames
+environments.  If this is observed, increasing the application's socket buffer
+size and/or increasing the /proc/sys/net/ipv4/tcp_*mem entry values may help.
+See the specific application manual and /usr/src/linux*/Documentation/
+networking/ip-sysctl.txt for more details.
+
+Allocating Rx Buffers when Using Jumbo Frames
+---------------------------------------------
+Allocating Rx buffers when using Jumbo Frames on 2.6.x kernels may fail if
+the available memory is heavily fragmented. This issue may be seen with PCI-X
+adapters or with packet split disabled. This can be reduced or eliminated
+by changing the amount of available memory for receive buffer allocation, by
+increasing /proc/sys/vm/min_free_kbytes.
+
+Multiple Interfaces on Same Ethernet Broadcast Network
+------------------------------------------------------
+Due to the default ARP behavior on Linux, it is not possible to have
+one system on two IP networks in the same Ethernet broadcast domain
+(non-partitioned switch) behave as expected.  All Ethernet interfaces
+will respond to IP traffic for any IP address assigned to the system.
+This results in unbalanced receive traffic.
+
+If you have multiple interfaces in a server, do either of the following:
+
+  - Turn on ARP filtering by entering::
+
+      echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
+
+  - Install the interfaces in separate broadcast domains - either in
+    different switches or in a switch partitioned to VLANs.
+
+UDP Stress Test Dropped Packet Issue
+--------------------------------------
+Under small packets UDP stress test with 10GbE driver, the Linux system
+may drop UDP packets due to the fullness of socket buffers. You may want
+to change the driver's Flow Control variables to the minimum value for
+controlling packet reception.
+
+Tx Hangs Possible Under Stress
+------------------------------
+Under stress conditions, if TX hangs occur, turning off TSO
+"ethtool -K eth0 tso off" may resolve the problem.
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net
diff --git a/Documentation/networking/device_drivers/intel/ixgbe.rst b/Documentation/networking/device_drivers/intel/ixgbe.rst
new file mode 100644 (file)
index 0000000..86d887a
--- /dev/null
@@ -0,0 +1,540 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Base Driver for the Intel(R) Ethernet 10 Gigabit PCI Express Adapters
+=============================================================================
+
+Intel 10 Gigabit Linux driver.
+Copyright(c) 1999-2018 Intel Corporation.
+
+Contents
+========
+
+- Identifying Your Adapter
+- Command Line Parameters
+- Additional Configurations
+- Known Issues
+- Support
+
+Identifying Your Adapter
+========================
+The driver is compatible with devices based on the following:
+
+ * Intel(R) Ethernet Controller 82598
+ * Intel(R) Ethernet Controller 82599
+ * Intel(R) Ethernet Controller X520
+ * Intel(R) Ethernet Controller X540
+ * Intel(R) Ethernet Controller x550
+ * Intel(R) Ethernet Controller X552
+ * Intel(R) Ethernet Controller X553
+
+For information on how to identify your adapter, and for the latest Intel
+network drivers, refer to the Intel Support website:
+https://www.intel.com/support
+
+SFP+ Devices with Pluggable Optics
+----------------------------------
+
+82599-BASED ADAPTERS
+~~~~~~~~~~~~~~~~~~~~
+NOTES:
+- If your 82599-based Intel(R) Network Adapter came with Intel optics or is an
+Intel(R) Ethernet Server Adapter X520-2, then it only supports Intel optics
+and/or the direct attach cables listed below.
+- When 82599-based SFP+ devices are connected back to back, they should be set
+to the same Speed setting via ethtool. Results may vary if you mix speed
+settings.
+
++---------------+---------------------------------------+------------------+
+| Supplier      | Type                                  | Part Numbers     |
++===============+=======================================+==================+
+| SR Modules                                                               |
++---------------+---------------------------------------+------------------+
+| Intel         | DUAL RATE 1G/10G SFP+ SR (bailed)     | FTLX8571D3BCV-IT |
++---------------+---------------------------------------+------------------+
+| Intel         | DUAL RATE 1G/10G SFP+ SR (bailed)     | AFBR-703SDZ-IN2  |
++---------------+---------------------------------------+------------------+
+| Intel         | DUAL RATE 1G/10G SFP+ SR (bailed)     | AFBR-703SDDZ-IN1 |
++---------------+---------------------------------------+------------------+
+| LR Modules                                                               |
++---------------+---------------------------------------+------------------+
+| Intel         | DUAL RATE 1G/10G SFP+ LR (bailed)     | FTLX1471D3BCV-IT |
++---------------+---------------------------------------+------------------+
+| Intel         | DUAL RATE 1G/10G SFP+ LR (bailed)     | AFCT-701SDZ-IN2  |
++---------------+---------------------------------------+------------------+
+| Intel         | DUAL RATE 1G/10G SFP+ LR (bailed)     | AFCT-701SDDZ-IN1 |
++---------------+---------------------------------------+------------------+
+
+The following is a list of 3rd party SFP+ modules that have received some
+testing. Not all modules are applicable to all devices.
+
++---------------+---------------------------------------+------------------+
+| Supplier      | Type                                  | Part Numbers     |
++===============+=======================================+==================+
+| Finisar       | SFP+ SR bailed, 10g single rate       | FTLX8571D3BCL    |
++---------------+---------------------------------------+------------------+
+| Avago         | SFP+ SR bailed, 10g single rate       | AFBR-700SDZ      |
++---------------+---------------------------------------+------------------+
+| Finisar       | SFP+ LR bailed, 10g single rate       | FTLX1471D3BCL    |
++---------------+---------------------------------------+------------------+
+| Finisar       | DUAL RATE 1G/10G SFP+ SR (No Bail)    | FTLX8571D3QCV-IT |
++---------------+---------------------------------------+------------------+
+| Avago         | DUAL RATE 1G/10G SFP+ SR (No Bail)    | AFBR-703SDZ-IN1  |
++---------------+---------------------------------------+------------------+
+| Finisar       | DUAL RATE 1G/10G SFP+ LR (No Bail)    | FTLX1471D3QCV-IT |
++---------------+---------------------------------------+------------------+
+| Avago         | DUAL RATE 1G/10G SFP+ LR (No Bail)    | AFCT-701SDZ-IN1  |
++---------------+---------------------------------------+------------------+
+| Finisar       | 1000BASE-T SFP                        | FCLF8522P2BTL    |
++---------------+---------------------------------------+------------------+
+| Avago         | 1000BASE-T                            | ABCU-5710RZ      |
++---------------+---------------------------------------+------------------+
+| HP            | 1000BASE-SX SFP                       | 453153-001       |
++---------------+---------------------------------------+------------------+
+
+82599-based adapters support all passive and active limiting direct attach
+cables that comply with SFF-8431 v4.1 and SFF-8472 v10.4 specifications.
+
+Laser turns off for SFP+ when ifconfig ethX down
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"ifconfig ethX down" turns off the laser for 82599-based SFP+ fiber adapters.
+"ifconfig ethX up" turns on the laser.
+Alternatively, you can use "ip link set [down/up] dev ethX" to turn the
+laser off and on.
+
+
+82599-based QSFP+ Adapters
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+NOTES:
+- If your 82599-based Intel(R) Network Adapter came with Intel optics, it only
+supports Intel optics.
+- 82599-based QSFP+ adapters only support 4x10 Gbps connections.  1x40 Gbps
+connections are not supported. QSFP+ link partners must be configured for
+4x10 Gbps.
+- 82599-based QSFP+ adapters do not support automatic link speed detection.
+The link speed must be configured to either 10 Gbps or 1 Gbps to match the link
+partners speed capabilities. Incorrect speed configurations will result in
+failure to link.
+- Intel(R) Ethernet Converged Network Adapter X520-Q1 only supports the optics
+and direct attach cables listed below.
+
++---------------+---------------------------------------+------------------+
+| Supplier      | Type                                  | Part Numbers     |
++===============+=======================================+==================+
+| Intel         | DUAL RATE 1G/10G QSFP+ SRL (bailed)   | E10GQSFPSR       |
++---------------+---------------------------------------+------------------+
+
+82599-based QSFP+ adapters support all passive and active limiting QSFP+
+direct attach cables that comply with SFF-8436 v4.1 specifications.
+
+82598-BASED ADAPTERS
+~~~~~~~~~~~~~~~~~~~~
+NOTES:
+- Intel(r) Ethernet Network Adapters that support removable optical modules
+only support their original module type (for example, the Intel(R) 10 Gigabit
+SR Dual Port Express Module only supports SR optical modules). If you plug in
+a different type of module, the driver will not load.
+- Hot Swapping/hot plugging optical modules is not supported.
+- Only single speed, 10 gigabit modules are supported.
+- LAN on Motherboard (LOMs) may support DA, SR, or LR modules. Other module
+types are not supported. Please see your system documentation for details.
+
+The following is a list of SFP+ modules and direct attach cables that have
+received some testing. Not all modules are applicable to all devices.
+
++---------------+---------------------------------------+------------------+
+| Supplier      | Type                                  | Part Numbers     |
++===============+=======================================+==================+
+| Finisar       | SFP+ SR bailed, 10g single rate       | FTLX8571D3BCL    |
++---------------+---------------------------------------+------------------+
+| Avago         | SFP+ SR bailed, 10g single rate       | AFBR-700SDZ      |
++---------------+---------------------------------------+------------------+
+| Finisar       | SFP+ LR bailed, 10g single rate       | FTLX1471D3BCL    |
++---------------+---------------------------------------+------------------+
+
+82598-based adapters support all passive direct attach cables that comply with
+SFF-8431 v4.1 and SFF-8472 v10.4 specifications. Active direct attach cables
+are not supported.
+
+Third party optic modules and cables referred to above are listed only for the
+purpose of highlighting third party specifications and potential
+compatibility, and are not recommendations or endorsements or sponsorship of
+any third party's product by Intel. Intel is not endorsing or promoting
+products made by any third party and the third party reference is provided
+only to share information regarding certain optic modules and cables with the
+above specifications. There may be other manufacturers or suppliers, producing
+or supplying optic modules and cables with similar or matching descriptions.
+Customers must use their own discretion and diligence to purchase optic
+modules and cables from any third party of their choice. Customers are solely
+responsible for assessing the suitability of the product and/or devices and
+for the selection of the vendor for purchasing any product. THE OPTIC MODULES
+AND CABLES REFERRED TO ABOVE ARE NOT WARRANTED OR SUPPORTED BY INTEL. INTEL
+ASSUMES NO LIABILITY WHATSOEVER, AND INTEL DISCLAIMS ANY EXPRESS OR IMPLIED
+WARRANTY, RELATING TO SALE AND/OR USE OF SUCH THIRD PARTY PRODUCTS OR
+SELECTION OF VENDOR BY CUSTOMERS.
+
+Command Line Parameters
+=======================
+
+max_vfs
+-------
+:Valid Range: 1-63
+
+This parameter adds support for SR-IOV. It causes the driver to spawn up to
+max_vfs worth of virtual functions.
+If the value is greater than 0 it will also force the VMDq parameter to be 1 or
+more.
+
+NOTE: This parameter is only used on kernel 3.7.x and below. On kernel 3.8.x
+and above, use sysfs to enable VFs. Also, for Red Hat distributions, this
+parameter is only used on version 6.6 and older. For version 6.7 and newer, use
+sysfs. For example::
+
+  #echo $num_vf_enabled > /sys/class/net/$dev/device/sriov_numvfs // enable VFs
+  #echo 0 > /sys/class/net/$dev/device/sriov_numvfs               //disable VFs
+
+The parameters for the driver are referenced by position. Thus, if you have a
+dual port adapter, or more than one adapter in your system, and want N virtual
+functions per port, you must specify a number for each port with each parameter
+separated by a comma. For example::
+
+  modprobe ixgbe max_vfs=4
+
+This will spawn 4 VFs on the first port.
+
+::
+
+  modprobe ixgbe max_vfs=2,4
+
+This will spawn 2 VFs on the first port and 4 VFs on the second port.
+
+NOTE: Caution must be used in loading the driver with these parameters.
+Depending on your system configuration, number of slots, etc., it is impossible
+to predict in all cases where the positions would be on the command line.
+
+NOTE: Neither the device nor the driver control how VFs are mapped into config
+space. Bus layout will vary by operating system. On operating systems that
+support it, you can check sysfs to find the mapping.
+
+NOTE: When either SR-IOV mode or VMDq mode is enabled, hardware VLAN filtering
+and VLAN tag stripping/insertion will remain enabled. Please remove the old
+VLAN filter before the new VLAN filter is added. For example,
+
+::
+
+  ip link set eth0 vf 0 vlan 100 // set VLAN 100 for VF 0
+  ip link set eth0 vf 0 vlan 0   // Delete VLAN 100
+  ip link set eth0 vf 0 vlan 200 // set a new VLAN 200 for VF 0
+
+With kernel 3.6, the driver supports the simultaneous usage of max_vfs and DCB
+features, subject to the constraints described below. Prior to kernel 3.6, the
+driver did not support the simultaneous operation of max_vfs greater than 0 and
+the DCB features (multiple traffic classes utilizing Priority Flow Control and
+Extended Transmission Selection).
+
+When DCB is enabled, network traffic is transmitted and received through
+multiple traffic classes (packet buffers in the NIC). The traffic is associated
+with a specific class based on priority, which has a value of 0 through 7 used
+in the VLAN tag. When SR-IOV is not enabled, each traffic class is associated
+with a set of receive/transmit descriptor queue pairs. The number of queue
+pairs for a given traffic class depends on the hardware configuration. When
+SR-IOV is enabled, the descriptor queue pairs are grouped into pools. The
+Physical Function (PF) and each Virtual Function (VF) is allocated a pool of
+receive/transmit descriptor queue pairs. When multiple traffic classes are
+configured (for example, DCB is enabled), each pool contains a queue pair from
+each traffic class. When a single traffic class is configured in the hardware,
+the pools contain multiple queue pairs from the single traffic class.
+
+The number of VFs that can be allocated depends on the number of traffic
+classes that can be enabled. The configurable number of traffic classes for
+each enabled VF is as follows:
+0 - 15 VFs = Up to 8 traffic classes, depending on device support
+16 - 31 VFs = Up to 4 traffic classes
+32 - 63 VFs = 1 traffic class
+
+When VFs are configured, the PF is allocated one pool as well. The PF supports
+the DCB features with the constraint that each traffic class will only use a
+single queue pair. When zero VFs are configured, the PF can support multiple
+queue pairs per traffic class.
+
+allow_unsupported_sfp
+---------------------
+:Valid Range: 0,1
+:Default Value: 0 (disabled)
+
+This parameter allows unsupported and untested SFP+ modules on 82599-based
+adapters, as long as the type of module is known to the driver.
+
+debug
+-----
+:Valid Range: 0-16 (0=none,...,16=all)
+:Default Value: 0
+
+This parameter adjusts the level of debug messages displayed in the system
+logs.
+
+
+Additional Features and Configurations
+======================================
+
+Flow Control
+------------
+Ethernet Flow Control (IEEE 802.3x) can be configured with ethtool to enable
+receiving and transmitting pause frames for ixgbe. When transmit is enabled,
+pause frames are generated when the receive packet buffer crosses a predefined
+threshold. When receive is enabled, the transmit unit will halt for the time
+delay specified when a pause frame is received.
+
+NOTE: You must have a flow control capable link partner.
+
+Flow Control is enabled by default.
+
+Use ethtool to change the flow control settings. To enable or disable Rx or
+Tx Flow Control::
+
+  ethtool -A eth? rx <on|off> tx <on|off>
+
+Note: This command only enables or disables Flow Control if auto-negotiation is
+disabled. If auto-negotiation is enabled, this command changes the parameters
+used for auto-negotiation with the link partner.
+
+To enable or disable auto-negotiation::
+
+  ethtool -s eth? autoneg <on|off>
+
+Note: Flow Control auto-negotiation is part of link auto-negotiation. Depending
+on your device, you may not be able to change the auto-negotiation setting.
+
+NOTE: For 82598 backplane cards entering 1 gigabit mode, flow control default
+behavior is changed to off. Flow control in 1 gigabit mode on these devices can
+lead to transmit hangs.
+
+Intel(R) Ethernet Flow Director
+-------------------------------
+The Intel Ethernet Flow Director performs the following tasks:
+
+- Directs receive packets according to their flows to different queues.
+- Enables tight control on routing a flow in the platform.
+- Matches flows and CPU cores for flow affinity.
+- Supports multiple parameters for flexible flow classification and load
+  balancing (in SFP mode only).
+
+NOTE: Intel Ethernet Flow Director masking works in the opposite manner from
+subnet masking. In the following command::
+
+  #ethtool -N eth11 flow-type ip4 src-ip 172.4.1.2 m 255.0.0.0 dst-ip \
+  172.21.1.1 m 255.128.0.0 action 31
+
+The src-ip value that is written to the filter will be 0.4.1.2, not 172.0.0.0
+as might be expected. Similarly, the dst-ip value written to the filter will be
+0.21.1.1, not 172.0.0.0.
+
+To enable or disable the Intel Ethernet Flow Director::
+
+  # ethtool -K ethX ntuple <on|off>
+
+When disabling ntuple filters, all the user programmed filters are flushed from
+the driver cache and hardware. All needed filters must be re-added when ntuple
+is re-enabled.
+
+To add a filter that directs packet to queue 2, use -U or -N switch::
+
+  # ethtool -N ethX flow-type tcp4 src-ip 192.168.10.1 dst-ip \
+  192.168.10.2 src-port 2000 dst-port 2001 action 2 [loc 1]
+
+To see the list of filters currently present::
+
+  # ethtool <-u|-n> ethX
+
+Sideband Perfect Filters
+------------------------
+Sideband Perfect Filters are used to direct traffic that matches specified
+characteristics. They are enabled through ethtool's ntuple interface. To add a
+new filter use the following command::
+
+  ethtool -U <device> flow-type <type> src-ip <ip> dst-ip <ip> src-port <port> \
+  dst-port <port> action <queue>
+
+Where:
+  <device> - the ethernet device to program
+  <type> - can be ip4, tcp4, udp4, or sctp4
+  <ip> - the IP address to match on
+  <port> - the port number to match on
+  <queue> - the queue to direct traffic towards (-1 discards the matched traffic)
+
+Use the following command to delete a filter::
+
+  ethtool -U <device> delete <N>
+
+Where <N> is the filter id displayed when printing all the active filters, and
+may also have been specified using "loc <N>" when adding the filter.
+
+The following example matches TCP traffic sent from 192.168.0.1, port 5300,
+directed to 192.168.0.5, port 80, and sends it to queue 7::
+
+  ethtool -U enp130s0 flow-type tcp4 src-ip 192.168.0.1 dst-ip 192.168.0.5 \
+  src-port 5300 dst-port 80 action 7
+
+For each flow-type, the programmed filters must all have the same matching
+input set. For example, issuing the following two commands is acceptable::
+
+  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.1 src-port 5300 action 7
+  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.5 src-port 55 action 10
+
+Issuing the next two commands, however, is not acceptable, since the first
+specifies src-ip and the second specifies dst-ip::
+
+  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.1 src-port 5300 action 7
+  ethtool -U enp130s0 flow-type ip4 dst-ip 192.168.0.5 src-port 55 action 10
+
+The second command will fail with an error. You may program multiple filters
+with the same fields, using different values, but, on one device, you may not
+program two TCP4 filters with different matching fields.
+
+Matching on a sub-portion of a field is not supported by the ixgbe driver, thus
+partial mask fields are not supported.
+
+To create filters that direct traffic to a specific Virtual Function, use the
+"user-def" parameter. Specify the user-def as a 64 bit value, where the lower 32
+bits represents the queue number, while the next 8 bits represent which VF.
+Note that 0 is the PF, so the VF identifier is offset by 1. For example::
+
+  ... user-def 0x800000002 ...
+
+specifies to direct traffic to Virtual Function 7 (8 minus 1) into queue 2 of
+that VF.
+
+Note that these filters will not break internal routing rules, and will not
+route traffic that otherwise would not have been sent to the specified Virtual
+Function.
+
+Jumbo Frames
+------------
+Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
+to a value larger than the default value of 1500.
+
+Use the ifconfig command to increase the MTU size. For example, enter the
+following where <x> is the interface number::
+
+  ifconfig eth<x> mtu 9000 up
+
+Alternatively, you can use the ip command as follows::
+
+  ip link set mtu 9000 dev eth<x>
+  ip link set up dev eth<x>
+
+This setting is not saved across reboots. The setting change can be made
+permanent by adding 'MTU=9000' to the file::
+
+  /etc/sysconfig/network-scripts/ifcfg-eth<x> // for RHEL
+  /etc/sysconfig/network/<config_file> // for SLES
+
+NOTE: The maximum MTU setting for Jumbo Frames is 9710. This value coincides
+with the maximum Jumbo Frames size of 9728 bytes.
+
+NOTE: This driver will attempt to use multiple page sized buffers to receive
+each jumbo packet. This should help to avoid buffer starvation issues when
+allocating receive packets.
+
+NOTE: For 82599-based network connections, if you are enabling jumbo frames in
+a virtual function (VF), jumbo frames must first be enabled in the physical
+function (PF). The VF MTU setting cannot be larger than the PF MTU.
+
+Generic Receive Offload, aka GRO
+--------------------------------
+The driver supports the in-kernel software implementation of GRO. GRO has
+shown that by coalescing Rx traffic into larger chunks of data, CPU
+utilization can be significantly reduced when under large Rx load. GRO is an
+evolution of the previously-used LRO interface. GRO is able to coalesce
+other protocols besides TCP. It's also safe to use with configurations that
+are problematic for LRO, namely bridging and iSCSI.
+
+Data Center Bridging (DCB)
+--------------------------
+NOTE:
+The kernel assumes that TC0 is available, and will disable Priority Flow
+Control (PFC) on the device if TC0 is not available. To fix this, ensure TC0 is
+enabled when setting up DCB on your switch.
+
+DCB is a configuration Quality of Service implementation in hardware. It uses
+the VLAN priority tag (802.1p) to filter traffic. That means that there are 8
+different priorities that traffic can be filtered into. It also enables
+priority flow control (802.1Qbb) which can limit or eliminate the number of
+dropped packets during network stress. Bandwidth can be allocated to each of
+these priorities, which is enforced at the hardware level (802.1Qaz).
+
+Adapter firmware implements LLDP and DCBX protocol agents as per 802.1AB and
+802.1Qaz respectively. The firmware based DCBX agent runs in willing mode only
+and can accept settings from a DCBX capable peer. Software configuration of
+DCBX parameters via dcbtool/lldptool are not supported.
+
+The ixgbe driver implements the DCB netlink interface layer to allow user-space
+to communicate with the driver and query DCB configuration for the port.
+
+ethtool
+-------
+The driver utilizes the ethtool interface for driver configuration and
+diagnostics, as well as displaying statistical information. The latest ethtool
+version is required for this functionality. Download it at:
+https://www.kernel.org/pub/software/network/ethtool/
+
+FCoE
+----
+The ixgbe driver supports Fiber Channel over Ethernet (FCoE) and Data Center
+Bridging (DCB). This code has no default effect on the regular driver
+operation. Configuring DCB and FCoE is outside the scope of this README. Refer
+to http://www.open-fcoe.org/ for FCoE project information and contact
+ixgbe-eedc@lists.sourceforge.net for DCB information.
+
+MAC and VLAN anti-spoofing feature
+----------------------------------
+When a malicious driver attempts to send a spoofed packet, it is dropped by the
+hardware and not transmitted.
+
+An interrupt is sent to the PF driver notifying it of the spoof attempt. When a
+spoofed packet is detected, the PF driver will send the following message to
+the system log (displayed by the "dmesg" command)::
+
+  ixgbe ethX: ixgbe_spoof_check: n spoofed packets detected
+
+where "x" is the PF interface number; and "n" is number of spoofed packets.
+NOTE: This feature can be disabled for a specific Virtual Function (VF)::
+
+  ip link set <pf dev> vf <vf id> spoofchk {off|on}
+
+IPsec Offload
+-------------
+The ixgbe driver supports IPsec Hardware Offload.  When creating Security
+Associations with "ip xfrm ..." the 'offload' tag option can be used to
+register the IPsec SA with the driver in order to get higher throughput in
+the secure communications.
+
+The offload is also supported for ixgbe's VFs, but the VF must be set as
+'trusted' and the support must be enabled with::
+
+  ethtool --set-priv-flags eth<x> vf-ipsec on
+  ip link set eth<x> vf <y> trust on
+
+
+Known Issues/Troubleshooting
+============================
+
+Enabling SR-IOV in a 64-bit Microsoft* Windows Server* 2012/R2 guest OS
+-----------------------------------------------------------------------
+Linux KVM Hypervisor/VMM supports direct assignment of a PCIe device to a VM.
+This includes traditional PCIe devices, as well as SR-IOV-capable devices based
+on the Intel Ethernet Controller XL710.
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/device_drivers/intel/ixgbevf.rst b/Documentation/networking/device_drivers/intel/ixgbevf.rst
new file mode 100644 (file)
index 0000000..56cde63
--- /dev/null
@@ -0,0 +1,66 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Linux* Base Virtual Function Driver for Intel(R) 10G Ethernet
+=============================================================
+
+Intel 10 Gigabit Virtual Function Linux driver.
+Copyright(c) 1999-2018 Intel Corporation.
+
+Contents
+========
+
+- Identifying Your Adapter
+- Known Issues
+- Support
+
+This driver supports 82599, X540, X550, and X552-based virtual function devices
+that can only be activated on kernels that support SR-IOV.
+
+For questions related to hardware requirements, refer to the documentation
+supplied with your Intel adapter. All hardware requirements listed apply to use
+with Linux.
+
+
+Identifying Your Adapter
+========================
+The driver is compatible with devices based on the following:
+
+  * Intel(R) Ethernet Controller 82598
+  * Intel(R) Ethernet Controller 82599
+  * Intel(R) Ethernet Controller X520
+  * Intel(R) Ethernet Controller X540
+  * Intel(R) Ethernet Controller x550
+  * Intel(R) Ethernet Controller X552
+  * Intel(R) Ethernet Controller X553
+
+For information on how to identify your adapter, and for the latest Intel
+network drivers, refer to the Intel Support website:
+https://www.intel.com/support
+
+Known Issues/Troubleshooting
+============================
+
+SR-IOV requires the correct platform and OS support.
+
+The guest OS loading this driver must support MSI-X interrupts.
+
+This driver is only supported as a loadable module at this time. Intel is not
+supplying patches against the kernel source to allow for static linking of the
+drivers.
+
+VLANs: There is a limit of a total of 64 shared VLANs to 1 or more VFs.
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/device_drivers/microsoft/netvsc.txt b/Documentation/networking/device_drivers/microsoft/netvsc.txt
new file mode 100644 (file)
index 0000000..3bfa635
--- /dev/null
@@ -0,0 +1,84 @@
+Hyper-V network driver
+======================
+
+Compatibility
+=============
+
+This driver is compatible with Windows Server 2012 R2, 2016 and
+Windows 10.
+
+Features
+========
+
+  Checksum offload
+  ----------------
+  The netvsc driver supports checksum offload as long as the
+  Hyper-V host version does. Windows Server 2016 and Azure
+  support checksum offload for TCP and UDP for both IPv4 and
+  IPv6. Windows Server 2012 only supports checksum offload for TCP.
+
+  Receive Side Scaling
+  --------------------
+  Hyper-V supports receive side scaling. For TCP & UDP, packets can
+  be distributed among available queues based on IP address and port
+  number.
+
+  For TCP & UDP, we can switch hash level between L3 and L4 by ethtool
+  command. TCP/UDP over IPv4 and v6 can be set differently. The default
+  hash level is L4. We currently only allow switching TX hash level
+  from within the guests.
+
+  On Azure, fragmented UDP packets have high loss rate with L4
+  hashing. Using L3 hashing is recommended in this case.
+
+  For example, for UDP over IPv4 on eth0:
+  To include UDP port numbers in hashing:
+        ethtool -N eth0 rx-flow-hash udp4 sdfn
+  To exclude UDP port numbers in hashing:
+        ethtool -N eth0 rx-flow-hash udp4 sd
+  To show UDP hash level:
+        ethtool -n eth0 rx-flow-hash udp4
+
+  Generic Receive Offload, aka GRO
+  --------------------------------
+  The driver supports GRO and it is enabled by default. GRO coalesces
+  like packets and significantly reduces CPU usage under heavy Rx
+  load.
+
+  Large Receive Offload (LRO), or Receive Side Coalescing (RSC)
+  -------------------------------------------------------------
+  The driver supports LRO/RSC in the vSwitch feature. It reduces the per packet
+  processing overhead by coalescing multiple TCP segments when possible. The
+  feature is enabled by default on VMs running on Windows Server 2019 and
+  later. It may be changed by ethtool command:
+       ethtool -K eth0 lro on
+       ethtool -K eth0 lro off
+
+  SR-IOV support
+  --------------
+  Hyper-V supports SR-IOV as a hardware acceleration option. If SR-IOV
+  is enabled in both the vSwitch and the guest configuration, then the
+  Virtual Function (VF) device is passed to the guest as a PCI
+  device. In this case, both a synthetic (netvsc) and VF device are
+  visible in the guest OS and both NIC's have the same MAC address.
+
+  The VF is enslaved by netvsc device.  The netvsc driver will transparently
+  switch the data path to the VF when it is available and up.
+  Network state (addresses, firewall, etc) should be applied only to the
+  netvsc device; the slave device should not be accessed directly in
+  most cases.  The exceptions are if some special queue discipline or
+  flow direction is desired, these should be applied directly to the
+  VF slave device.
+
+  Receive Buffer
+  --------------
+  Packets are received into a receive area which is created when device
+  is probed. The receive area is broken into MTU sized chunks and each may
+  contain one or more packets. The number of receive sections may be changed
+  via ethtool Rx ring parameters.
+
+  There is a similar send buffer which is used to aggregate packets for sending.
+  The send area is broken into chunks of 6144 bytes, each of section may
+  contain one or more packets. The send buffer is an optimization, the driver
+  will use slower method to handle very large packets or if the send buffer
+  area is exhausted.
diff --git a/Documentation/networking/device_drivers/neterion/s2io.txt b/Documentation/networking/device_drivers/neterion/s2io.txt
new file mode 100644 (file)
index 0000000..0362a42
--- /dev/null
@@ -0,0 +1,141 @@
+Release notes for Neterion's (Formerly S2io) Xframe I/II PCI-X 10GbE driver.
+
+Contents
+=======
+- 1.  Introduction
+- 2.  Identifying the adapter/interface
+- 3.  Features supported
+- 4.  Command line parameters
+- 5.  Performance suggestions
+- 6.  Available Downloads 
+
+
+1.     Introduction:
+This Linux driver supports Neterion's Xframe I PCI-X 1.0 and
+Xframe II PCI-X 2.0 adapters. It supports several features 
+such as jumbo frames, MSI/MSI-X, checksum offloads, TSO, UFO and so on.
+See below for complete list of features.
+All features are supported for both IPv4 and IPv6.
+
+2.     Identifying the adapter/interface:
+a. Insert the adapter(s) in your system.
+b. Build and load driver 
+# insmod s2io.ko
+c. View log messages
+# dmesg | tail -40
+You will see messages similar to:
+eth3: Neterion Xframe I 10GbE adapter (rev 3), Version 2.0.9.1, Intr type INTA
+eth4: Neterion Xframe II 10GbE adapter (rev 2), Version 2.0.9.1, Intr type INTA
+eth4: Device is on 64 bit 133MHz PCIX(M1) bus
+
+The above messages identify the adapter type(Xframe I/II), adapter revision,
+driver version, interface name(eth3, eth4), Interrupt type(INTA, MSI, MSI-X).
+In case of Xframe II, the PCI/PCI-X bus width and frequency are displayed
+as well.
+
+To associate an interface with a physical adapter use "ethtool -p <ethX>".
+The corresponding adapter's LED will blink multiple times.
+
+3.     Features supported:
+a. Jumbo frames. Xframe I/II supports MTU up to 9600 bytes,
+modifiable using ip command.
+
+b. Offloads. Supports checksum offload(TCP/UDP/IP) on transmit
+and receive, TSO.
+
+c. Multi-buffer receive mode. Scattering of packet across multiple
+buffers. Currently driver supports 2-buffer mode which yields
+significant performance improvement on certain platforms(SGI Altix,
+IBM xSeries).
+
+d. MSI/MSI-X. Can be enabled on platforms which support this feature
+(IA64, Xeon) resulting in noticeable performance improvement(up to 7%
+on certain platforms).
+
+e. Statistics. Comprehensive MAC-level and software statistics displayed
+using "ethtool -S" option.
+
+f. Multi-FIFO/Ring. Supports up to 8 transmit queues and receive rings,
+with multiple steering options.
+
+4.  Command line parameters
+a. tx_fifo_num
+Number of transmit queues
+Valid range: 1-8
+Default: 1
+
+b. rx_ring_num
+Number of receive rings
+Valid range: 1-8
+Default: 1
+
+c. tx_fifo_len
+Size of each transmit queue
+Valid range: Total length of all queues should not exceed 8192
+Default: 4096
+
+d. rx_ring_sz 
+Size of each receive ring(in 4K blocks)
+Valid range: Limited by memory on system
+Default: 30 
+
+e. intr_type
+Specifies interrupt type. Possible values 0(INTA), 2(MSI-X)
+Valid values: 0, 2
+Default: 2
+
+5.  Performance suggestions
+General:
+a. Set MTU to maximum(9000 for switch setup, 9600 in back-to-back configuration)
+b. Set TCP windows size to optimal value. 
+For instance, for MTU=1500 a value of 210K has been observed to result in 
+good performance.
+# sysctl -w net.ipv4.tcp_rmem="210000 210000 210000"
+# sysctl -w net.ipv4.tcp_wmem="210000 210000 210000"
+For MTU=9000, TCP window size of 10 MB is recommended.
+# sysctl -w net.ipv4.tcp_rmem="10000000 10000000 10000000"
+# sysctl -w net.ipv4.tcp_wmem="10000000 10000000 10000000"
+
+Transmit performance:
+a. By default, the driver respects BIOS settings for PCI bus parameters. 
+However, you may want to experiment with PCI bus parameters 
+max-split-transactions(MOST) and MMRBC (use setpci command). 
+A MOST value of 2 has been found optimal for Opterons and 3 for Itanium.  
+It could be different for your hardware.  
+Set MMRBC to 4K**.
+
+For example you can set 
+For opteron
+#setpci -d 17d5:* 62=1d 
+For Itanium
+#setpci -d 17d5:* 62=3d 
+
+For detailed description of the PCI registers, please see Xframe User Guide.
+
+b. Ensure Transmit Checksum offload is enabled. Use ethtool to set/verify this 
+parameter.
+c. Turn on TSO(using "ethtool -K")
+# ethtool -K <ethX> tso on
+
+Receive performance:
+a. By default, the driver respects BIOS settings for PCI bus parameters. 
+However, you may want to set PCI latency timer to 248.
+#setpci -d 17d5:* LATENCY_TIMER=f8
+For detailed description of the PCI registers, please see Xframe User Guide.
+b. Use 2-buffer mode. This results in large performance boost on
+certain platforms(eg. SGI Altix, IBM xSeries).
+c. Ensure Receive Checksum offload is enabled. Use "ethtool -K ethX" command to 
+set/verify this option.
+d. Enable NAPI feature(in kernel configuration Device Drivers ---> Network 
+device support --->  Ethernet (10000 Mbit) ---> S2IO 10Gbe Xframe NIC) to 
+bring down CPU utilization.
+
+** For AMD opteron platforms with 8131 chipset, MMRBC=1 and MOST=1 are 
+recommended as safe parameters.
+For more information, please review the AMD8131 errata at
+http://vip.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/
+26310_AMD-8131_HyperTransport_PCI-X_Tunnel_Revision_Guide_rev_3_18.pdf
+
+6. Support
+For further support please contact either your 10GbE Xframe NIC vendor (IBM, 
+HP, SGI etc.)
diff --git a/Documentation/networking/device_drivers/neterion/vxge.txt b/Documentation/networking/device_drivers/neterion/vxge.txt
new file mode 100644 (file)
index 0000000..abfec24
--- /dev/null
@@ -0,0 +1,93 @@
+Neterion's (Formerly S2io) X3100 Series 10GbE PCIe Server Adapter Linux driver
+==============================================================================
+
+Contents
+--------
+
+1) Introduction
+2) Features supported
+3) Configurable driver parameters
+4) Troubleshooting
+
+1) Introduction:
+----------------
+This Linux driver supports all Neterion's X3100 series 10 GbE PCIe I/O
+Virtualized Server adapters.
+The X3100 series supports four modes of operation, configurable via
+firmware -
+       Single function mode
+       Multi function mode
+       SRIOV mode
+       MRIOV mode
+The functions share a 10GbE link and the pci-e bus, but hardly anything else
+inside the ASIC. Features like independent hw reset, statistics, bandwidth/
+priority allocation and guarantees, GRO, TSO, interrupt moderation etc are
+supported independently on each function.
+
+(See below for a complete list of features supported for both IPv4 and IPv6)
+
+2) Features supported:
+----------------------
+
+i)   Single function mode (up to 17 queues)
+
+ii)  Multi function mode (up to 17 functions)
+
+iii) PCI-SIG's I/O Virtualization
+       - Single Root mode: v1.0 (up to 17 functions)
+       - Multi-Root mode: v1.0 (up to 17 functions)
+
+iv)  Jumbo frames
+       X3100 Series supports MTU up to 9600 bytes, modifiable using
+       ip command.
+
+v)   Offloads supported: (Enabled by default)
+       Checksum offload (TCP/UDP/IP) on transmit and receive paths
+       TCP Segmentation Offload (TSO) on transmit path
+       Generic Receive Offload (GRO) on receive path
+
+vi)  MSI-X: (Enabled by default)
+       Resulting in noticeable performance improvement (up to 7% on certain
+       platforms).
+
+vii) NAPI: (Enabled by default)
+       For better Rx interrupt moderation.
+
+viii)RTH (Receive Traffic Hash): (Enabled by default)
+       Receive side steering for better scaling.
+
+ix)  Statistics
+       Comprehensive MAC-level and software statistics displayed using
+       "ethtool -S" option.
+
+x)   Multiple hardware queues: (Enabled by default)
+       Up to 17 hardware based transmit and receive data channels, with
+       multiple steering options (transmit multiqueue enabled by default).
+
+3) Configurable driver parameters:
+----------------------------------
+
+i)  max_config_dev
+       Specifies maximum device functions to be enabled.
+       Valid range: 1-8
+
+ii) max_config_port
+       Specifies number of ports to be enabled.
+       Valid range: 1,2
+       Default: 1
+
+iii)max_config_vpath
+       Specifies maximum VPATH(s) configured for each device function.
+       Valid range: 1-17
+
+iv) vlan_tag_strip
+       Enables/disables vlan tag stripping from all received tagged frames that
+       are not replicated at the internal L2 switch.
+       Valid range: 0,1 (disabled, enabled respectively)
+       Default: 1
+
+v)  addr_learn_en
+       Enable learning the mac address of the guest OS interface in
+       virtualization environment.
+       Valid range: 0,1 (disabled, enabled respectively)
+       Default: 0
diff --git a/Documentation/networking/device_drivers/qlogic/LICENSE.qla3xxx b/Documentation/networking/device_drivers/qlogic/LICENSE.qla3xxx
new file mode 100644 (file)
index 0000000..2f2077e
--- /dev/null
@@ -0,0 +1,46 @@
+Copyright (c)  2003-2006 QLogic Corporation
+QLogic Linux Networking HBA Driver
+
+This program includes a device driver for Linux 2.6 that may be
+distributed with QLogic hardware specific firmware binary file.
+You may modify and redistribute the device driver code under the
+GNU General Public License as published by the Free Software
+Foundation (version 2 or a later version).
+
+You may redistribute the hardware specific firmware binary file
+under the following terms:
+
+       1. Redistribution of source code (only if applicable),
+          must retain the above copyright notice, this list of
+          conditions and the following disclaimer.
+
+       2. Redistribution 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.
+
+       3. The name of QLogic Corporation may not be used to
+          endorse or promote products derived from this software
+          without specific prior written permission
+
+REGARDLESS OF WHAT LICENSING MECHANISM IS USED OR APPLICABLE,
+THIS PROGRAM IS PROVIDED BY QLOGIC CORPORATION "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 AUTHOR
+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.
+
+USER ACKNOWLEDGES AND AGREES THAT USE OF THIS PROGRAM WILL NOT
+CREATE OR GIVE GROUNDS FOR A LICENSE BY IMPLICATION, ESTOPPEL, OR
+OTHERWISE IN ANY INTELLECTUAL PROPERTY RIGHTS (PATENT, COPYRIGHT,
+TRADE SECRET, MASK WORK, OR OTHER PROPRIETARY RIGHT) EMBODIED IN
+ANY OTHER QLOGIC HARDWARE OR SOFTWARE EITHER SOLELY OR IN
+COMBINATION WITH THIS PROGRAM.
+
diff --git a/Documentation/networking/device_drivers/qlogic/LICENSE.qlcnic b/Documentation/networking/device_drivers/qlogic/LICENSE.qlcnic
new file mode 100644 (file)
index 0000000..2ae3b64
--- /dev/null
@@ -0,0 +1,288 @@
+Copyright (c) 2009-2013 QLogic Corporation
+QLogic Linux qlcnic NIC Driver
+
+You may modify and redistribute the device driver code under the
+GNU General Public License (a copy of which is attached hereto as
+Exhibit A) published by the Free Software Foundation (version 2).
+
+
+EXHIBIT A
+
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/Documentation/networking/device_drivers/qlogic/LICENSE.qlge b/Documentation/networking/device_drivers/qlogic/LICENSE.qlge
new file mode 100644 (file)
index 0000000..ce64e4d
--- /dev/null
@@ -0,0 +1,288 @@
+Copyright (c) 2003-2011 QLogic Corporation
+QLogic Linux qlge NIC Driver
+
+You may modify and redistribute the device driver code under the
+GNU General Public License (a copy of which is attached hereto as
+Exhibit A) published by the Free Software Foundation (version 2).
+
+
+EXHIBIT A
+
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/Documentation/networking/device_drivers/qualcomm/rmnet.txt b/Documentation/networking/device_drivers/qualcomm/rmnet.txt
new file mode 100644 (file)
index 0000000..6b341ea
--- /dev/null
@@ -0,0 +1,82 @@
+1. Introduction
+
+rmnet driver is used for supporting the Multiplexing and aggregation
+Protocol (MAP). This protocol is used by all recent chipsets using Qualcomm
+Technologies, Inc. modems.
+
+This driver can be used to register onto any physical network device in
+IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator.
+
+Multiplexing allows for creation of logical netdevices (rmnet devices) to
+handle multiple private data networks (PDN) like a default internet, tethering,
+multimedia messaging service (MMS) or IP media subsystem (IMS). Hardware sends
+packets with MAP headers to rmnet. Based on the multiplexer id, rmnet
+routes to the appropriate PDN after removing the MAP header.
+
+Aggregation is required to achieve high data rates. This involves hardware
+sending aggregated bunch of MAP frames. rmnet driver will de-aggregate
+these MAP frames and send them to appropriate PDN's.
+
+2. Packet format
+
+a. MAP packet (data / control)
+
+MAP header has the same endianness of the IP packet.
+
+Packet format -
+
+Bit             0             1           2-7      8 - 15           16 - 31
+Function   Command / Data   Reserved     Pad   Multiplexer ID    Payload length
+Bit            32 - x
+Function     Raw  Bytes
+
+Command (1)/ Data (0) bit value is to indicate if the packet is a MAP command
+or data packet. Control packet is used for transport level flow control. Data
+packets are standard IP packets.
+
+Reserved bits are usually zeroed out and to be ignored by receiver.
+
+Padding is number of bytes to be added for 4 byte alignment if required by
+hardware.
+
+Multiplexer ID is to indicate the PDN on which data has to be sent.
+
+Payload length includes the padding length but does not include MAP header
+length.
+
+b. MAP packet (command specific)
+
+Bit             0             1           2-7      8 - 15           16 - 31
+Function   Command         Reserved     Pad   Multiplexer ID    Payload length
+Bit          32 - 39        40 - 45    46 - 47       48 - 63
+Function   Command name    Reserved   Command Type   Reserved
+Bit          64 - 95
+Function   Transaction ID
+Bit          96 - 127
+Function   Command data
+
+Command 1 indicates disabling flow while 2 is enabling flow
+
+Command types -
+0 for MAP command request
+1 is to acknowledge the receipt of a command
+2 is for unsupported commands
+3 is for error during processing of commands
+
+c. Aggregation
+
+Aggregation is multiple MAP packets (can be data or command) delivered to
+rmnet in a single linear skb. rmnet will process the individual
+packets and either ACK the MAP command or deliver the IP packet to the
+network stack as needed
+
+MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding....
+MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad...
+
+3. Userspace configuration
+
+rmnet userspace configuration is done through netlink library librmnetctl
+and command line utility rmnetcli. Utility is hosted in codeaurora forum git.
+The driver uses rtnl_link_ops for communication.
+
+https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl
diff --git a/Documentation/networking/device_drivers/sb1000.txt b/Documentation/networking/device_drivers/sb1000.txt
new file mode 100644 (file)
index 0000000..f92c2aa
--- /dev/null
@@ -0,0 +1,207 @@
+sb1000 is a module network device driver for the General Instrument (also known
+as NextLevel) SURFboard1000 internal cable modem board.  This is an ISA card
+which is used by a number of cable TV companies to provide cable modem access.
+It's a one-way downstream-only cable modem, meaning that your upstream net link
+is provided by your regular phone modem.
+
+This driver was written by Franco Venturi <fventuri@mediaone.net>.  He deserves
+a great deal of thanks for this wonderful piece of code!
+
+-----------------------------------------------------------------------------
+
+Support for this device is now a part of the standard Linux kernel.  The
+driver source code file is drivers/net/sb1000.c.  In addition to this
+you will need:
+
+1.) The "cmconfig" program.  This is a utility which supplements "ifconfig"
+to configure the cable modem and network interface (usually called "cm0");
+and
+
+2.) Several PPP scripts which live in /etc/ppp to make connecting via your
+cable modem easy.
+
+   These utilities can be obtained from:
+
+      http://www.jacksonville.net/~fventuri/
+
+   in Franco's original source code distribution .tar.gz file.  Support for
+   the sb1000 driver can be found at:
+
+      http://web.archive.org/web/*/http://home.adelphia.net/~siglercm/sb1000.html
+      http://web.archive.org/web/*/http://linuxpower.cx/~cable/
+
+   along with these utilities.
+
+3.) The standard isapnp tools.  These are necessary to configure your SB1000
+card at boot time (or afterwards by hand) since it's a PnP card.
+
+   If you don't have these installed as a standard part of your Linux
+   distribution, you can find them at:
+
+      http://www.roestock.demon.co.uk/isapnptools/
+
+   or check your Linux distribution binary CD or their web site.  For help with
+   isapnp, pnpdump, or /etc/isapnp.conf, go to:
+
+      http://www.roestock.demon.co.uk/isapnptools/isapnpfaq.html
+
+-----------------------------------------------------------------------------
+
+To make the SB1000 card work, follow these steps:
+
+1.) Run `make config', or `make menuconfig', or `make xconfig', whichever
+you prefer, in the top kernel tree directory to set up your kernel
+configuration.  Make sure to say "Y" to "Prompt for development drivers"
+and to say "M" to the sb1000 driver.  Also say "Y" or "M" to all the standard
+networking questions to get TCP/IP and PPP networking support.
+
+2.) *BEFORE* you build the kernel, edit drivers/net/sb1000.c.  Make sure
+to redefine the value of READ_DATA_PORT to match the I/O address used
+by isapnp to access your PnP cards.  This is the value of READPORT in
+/etc/isapnp.conf or given by the output of pnpdump.
+
+3.) Build and install the kernel and modules as usual.
+
+4.) Boot your new kernel following the usual procedures.
+
+5.) Set up to configure the new SB1000 PnP card by capturing the output
+of "pnpdump" to a file and editing this file to set the correct I/O ports,
+IRQ, and DMA settings for all your PnP cards.  Make sure none of the settings
+conflict with one another.  Then test this configuration by running the
+"isapnp" command with your new config file as the input.  Check for
+errors and fix as necessary.  (As an aside, I use I/O ports 0x110 and
+0x310 and IRQ 11 for my SB1000 card and these work well for me.  YMMV.)
+Then save the finished config file as /etc/isapnp.conf for proper configuration
+on subsequent reboots.
+
+6.) Download the original file sb1000-1.1.2.tar.gz from Franco's site or one of
+the others referenced above.  As root, unpack it into a temporary directory and
+do a `make cmconfig' and then `install -c cmconfig /usr/local/sbin'.  Don't do
+`make install' because it expects to find all the utilities built and ready for
+installation, not just cmconfig.
+
+7.) As root, copy all the files under the ppp/ subdirectory in Franco's
+tar file into /etc/ppp, being careful not to overwrite any files that are
+already in there.  Then modify ppp@gi-on to set the correct login name,
+phone number, and frequency for the cable modem.  Also edit pap-secrets
+to specify your login name and password and any site-specific information
+you need.
+
+8.) Be sure to modify /etc/ppp/firewall to use ipchains instead of
+the older ipfwadm commands from the 2.0.x kernels.  There's a neat utility to
+convert ipfwadm commands to ipchains commands:
+
+   http://users.dhp.com/~whisper/ipfwadm2ipchains/
+
+You may also wish to modify the firewall script to implement a different
+firewalling scheme.
+
+9.) Start the PPP connection via the script /etc/ppp/ppp@gi-on.  You must be
+root to do this.  It's better to use a utility like sudo to execute
+frequently used commands like this with root permissions if possible.  If you
+connect successfully the cable modem interface will come up and you'll see a
+driver message like this at the console:
+
+         cm0: sb1000 at (0x110,0x310), csn 1, S/N 0x2a0d16d8, IRQ 11.
+         sb1000.c:v1.1.2 6/01/98 (fventuri@mediaone.net)
+
+The "ifconfig" command should show two new interfaces, ppp0 and cm0.
+The command "cmconfig cm0" will give you information about the cable modem
+interface.
+
+10.) Try pinging a site via `ping -c 5 www.yahoo.com', for example.  You should
+see packets received.
+
+11.) If you can't get site names (like www.yahoo.com) to resolve into
+IP addresses (like 204.71.200.67), be sure your /etc/resolv.conf file
+has no syntax errors and has the right nameserver IP addresses in it.
+If this doesn't help, try something like `ping -c 5 204.71.200.67' to
+see if the networking is running but the DNS resolution is where the
+problem lies.
+
+12.) If you still have problems, go to the support web sites mentioned above
+and read the information and documentation there.
+
+-----------------------------------------------------------------------------
+
+Common problems:
+
+1.) Packets go out on the ppp0 interface but don't come back on the cm0
+interface.  It looks like I'm connected but I can't even ping any
+numerical IP addresses.  (This happens predominantly on Debian systems due
+to a default boot-time configuration script.)
+
+Solution -- As root `echo 0 > /proc/sys/net/ipv4/conf/cm0/rp_filter' so it
+can share the same IP address as the ppp0 interface.  Note that this
+command should probably be added to the /etc/ppp/cablemodem script
+*right*between* the "/sbin/ifconfig" and "/sbin/cmconfig" commands.
+You may need to do this to /proc/sys/net/ipv4/conf/ppp0/rp_filter as well.
+If you do this to /proc/sys/net/ipv4/conf/default/rp_filter on each reboot
+(in rc.local or some such) then any interfaces can share the same IP
+addresses.
+
+2.) I get "unresolved symbol" error messages on executing `insmod sb1000.o'.
+
+Solution -- You probably have a non-matching kernel source tree and
+/usr/include/linux and /usr/include/asm header files.  Make sure you
+install the correct versions of the header files in these two directories.
+Then rebuild and reinstall the kernel.
+
+3.) When isapnp runs it reports an error, and my SB1000 card isn't working.
+
+Solution -- There's a problem with later versions of isapnp using the "(CHECK)"
+option in the lines that allocate the two I/O addresses for the SB1000 card.
+This first popped up on RH 6.0.  Delete "(CHECK)" for the SB1000 I/O addresses.
+Make sure they don't conflict with any other pieces of hardware first!  Then
+rerun isapnp and go from there.
+
+4.) I can't execute the /etc/ppp/ppp@gi-on file.
+
+Solution -- As root do `chmod ug+x /etc/ppp/ppp@gi-on'.
+
+5.) The firewall script isn't working (with 2.2.x and higher kernels).
+
+Solution -- Use the ipfwadm2ipchains script referenced above to convert the
+/etc/ppp/firewall script from the deprecated ipfwadm commands to ipchains.
+
+6.) I'm getting *tons* of firewall deny messages in the /var/kern.log,
+/var/messages, and/or /var/syslog files, and they're filling up my /var
+partition!!!
+
+Solution -- First, tell your ISP that you're receiving DoS (Denial of Service)
+and/or portscanning (UDP connection attempts) attacks!  Look over the deny
+messages to figure out what the attack is and where it's coming from.  Next,
+edit /etc/ppp/cablemodem and make sure the ",nobroadcast" option is turned on
+to the "cmconfig" command (uncomment that line).  If you're not receiving these
+denied packets on your broadcast interface (IP address xxx.yyy.zzz.255
+typically), then someone is attacking your machine in particular.  Be careful
+out there....
+
+7.) Everything seems to work fine but my computer locks up after a while
+(and typically during a lengthy download through the cable modem)!
+
+Solution -- You may need to add a short delay in the driver to 'slow down' the
+SURFboard because your PC might not be able to keep up with the transfer rate
+of the SB1000. To do this, it's probably best to download Franco's
+sb1000-1.1.2.tar.gz archive and build and install sb1000.o manually.  You'll
+want to edit the 'Makefile' and look for the 'SB1000_DELAY'
+define.  Uncomment those 'CFLAGS' lines (and comment out the default ones)
+and try setting the delay to something like 60 microseconds with:
+'-DSB1000_DELAY=60'.  Then do `make' and as root `make install' and try
+it out.  If it still doesn't work or you like playing with the driver, you may
+try other numbers.  Remember though that the higher the delay, the slower the
+driver (which slows down the rest of the PC too when it is actively
+used). Thanks to Ed Daiga for this tip!
+
+-----------------------------------------------------------------------------
+
+Credits:  This README came from Franco Venturi's original README file which is
+still supplied with his driver .tar.gz archive.  I and all other sb1000 users
+owe Franco a tremendous "Thank you!"  Additional thanks goes to Carl Patten
+and Ralph Bonnell who are now managing the Linux SB1000 web site, and to
+the SB1000 users who reported and helped debug the common problems listed
+above.
+
+
+                                       Clemmitt Sigler
+                                       csigler@vt.edu
diff --git a/Documentation/networking/device_drivers/smsc/smc9.txt b/Documentation/networking/device_drivers/smsc/smc9.txt
new file mode 100644 (file)
index 0000000..d1e1507
--- /dev/null
@@ -0,0 +1,42 @@
+
+SMC 9xxxx Driver 
+Revision 0.12 
+3/5/96
+Copyright 1996  Erik Stahlman 
+Released under terms of the GNU General Public License. 
+
+This file contains the instructions and caveats for my SMC9xxx driver.  You
+should not be using the driver without reading this file.  
+
+Things to note about installation:
+
+  1. The driver should work on all kernels from 1.2.13 until 1.3.71.
+       (A kernel patch is supplied for 1.3.71 )
+
+  2. If you include this into the kernel, you might need to change some
+       options, such as for forcing IRQ.   
+
+  3.  To compile as a module, run 'make' .   
+       Make will give you the appropriate options for various kernel support.
+  4.  Loading the driver as a module :
+
+       use:   insmod smc9194.o 
+       optional parameters:
+               io=xxxx    : your base address
+               irq=xx     : your irq 
+               ifport=x   :    0 for whatever is default
+                               1 for twisted pair
+                               2 for AUI  ( or BNC on some cards )
+
+How to obtain the latest version? 
+       
+FTP:  
+       ftp://fenris.campus.vt.edu/smc9/smc9-12.tar.gz
+       ftp://sfbox.vt.edu/filebox/F/fenris/smc9/smc9-12.tar.gz 
+   
+
+Contacting me:
+    erik@mail.vt.edu
diff --git a/Documentation/networking/device_drivers/stmicro/stmmac.txt b/Documentation/networking/device_drivers/stmicro/stmmac.txt
new file mode 100644 (file)
index 0000000..2bb0707
--- /dev/null
@@ -0,0 +1,401 @@
+       STMicroelectronics 10/100/1000 Synopsys Ethernet driver
+
+Copyright (C) 2007-2015  STMicroelectronics Ltd
+Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+
+This is the driver for the MAC 10/100/1000 on-chip Ethernet controllers
+(Synopsys IP blocks).
+
+Currently this network device driver is for all STi embedded MAC/GMAC
+(i.e. 7xxx/5xxx SoCs), SPEAr (arm), Loongson1B (mips) and XLINX XC2V3000
+FF1152AMT0221 D1215994A VIRTEX FPGA board.
+
+DWC Ether MAC 10/100/1000 Universal version 3.70a (and older) and DWC Ether
+MAC 10/100 Universal version 4.0 have been used for developing this driver.
+
+This driver supports both the platform bus and PCI.
+
+Please, for more information also visit: www.stlinux.com
+
+1) Kernel Configuration
+The kernel configuration option is STMMAC_ETH:
+ Device Drivers ---> Network device support ---> Ethernet (1000 Mbit) --->
+ STMicroelectronics 10/100/1000 Ethernet driver (STMMAC_ETH)
+
+CONFIG_STMMAC_PLATFORM: is to enable the platform driver.
+CONFIG_STMMAC_PCI: is to enable the pci driver.
+
+2) Driver parameters list:
+       debug: message level (0: no output, 16: all);
+       phyaddr: to manually provide the physical address to the PHY device;
+       buf_sz: DMA buffer size;
+       tc: control the HW FIFO threshold;
+       watchdog: transmit timeout (in milliseconds);
+       flow_ctrl: Flow control ability [on/off];
+       pause: Flow Control Pause Time;
+       eee_timer: tx EEE timer;
+       chain_mode: select chain mode instead of ring.
+
+3) Command line options
+Driver parameters can be also passed in command line by using:
+       stmmaceth=watchdog:100,chain_mode=1
+
+4) Driver information and notes
+
+4.1) Transmit process
+The xmit method is invoked when the kernel needs to transmit a packet; it sets
+the descriptors in the ring and informs the DMA engine, that there is a packet
+ready to be transmitted.
+By default, the driver sets the NETIF_F_SG bit in the features field of the
+net_device structure, enabling the scatter-gather feature. This is true on
+chips and configurations where the checksum can be done in hardware.
+Once the controller has finished transmitting the packet, timer will be
+scheduled to release the transmit resources.
+
+4.2) Receive process
+When one or more packets are received, an interrupt happens. The interrupts
+are not queued, so the driver has to scan all the descriptors in the ring during
+the receive process.
+This is based on NAPI, so the interrupt handler signals only if there is work
+to be done, and it exits.
+Then the poll method will be scheduled at some future point.
+The incoming packets are stored, by the DMA, in a list of pre-allocated socket
+buffers in order to avoid the memcpy (zero-copy).
+
+4.3) Interrupt mitigation
+The driver is able to mitigate the number of its DMA interrupts
+using NAPI for the reception on chips older than the 3.50.
+New chips have an HW RX-Watchdog used for this mitigation.
+Mitigation parameters can be tuned by ethtool.
+
+4.4) WOL
+Wake up on Lan feature through Magic and Unicast frames are supported for the
+GMAC core.
+
+4.5) DMA descriptors
+Driver handles both normal and alternate descriptors. The latter has been only
+tested on DWC Ether MAC 10/100/1000 Universal version 3.41a and later.
+
+STMMAC supports DMA descriptor to operate both in dual buffer (RING)
+and linked-list(CHAINED) mode. In RING each descriptor points to two
+data buffer pointers whereas in CHAINED mode they point to only one data
+buffer pointer. RING mode is the default.
+
+In CHAINED mode each descriptor will have pointer to next descriptor in
+the list, hence creating the explicit chaining in the descriptor itself,
+whereas such explicit chaining is not possible in RING mode.
+
+4.5.1) Extended descriptors
+The extended descriptors give us information about the Ethernet payload
+when it is carrying PTP packets or TCP/UDP/ICMP over IP.
+These are not available on GMAC Synopsys chips older than the 3.50.
+At probe time the driver will decide if these can be actually used.
+This support also is mandatory for PTPv2 because the extra descriptors
+are used for saving the hardware timestamps and Extended Status.
+
+4.6) Ethtool support
+Ethtool is supported.
+
+For example, driver statistics (including RMON), internal errors can be taken
+using:
+  # ethtool -S ethX
+command
+
+4.7) Jumbo and Segmentation Offloading
+Jumbo frames are supported and tested for the GMAC.
+The GSO has been also added but it's performed in software.
+LRO is not supported.
+
+4.8) Physical
+The driver is compatible with Physical Abstraction Layer to be connected with
+PHY and GPHY devices.
+
+4.9) Platform information
+Several information can be passed through the platform and device-tree.
+
+struct plat_stmmacenet_data {
+       char *phy_bus_name;
+       int bus_id;
+       int phy_addr;
+       int interface;
+       struct stmmac_mdio_bus_data *mdio_bus_data;
+       struct stmmac_dma_cfg *dma_cfg;
+       int clk_csr;
+       int has_gmac;
+       int enh_desc;
+       int tx_coe;
+       int rx_coe;
+       int bugged_jumbo;
+       int pmt;
+       int force_sf_dma_mode;
+       int force_thresh_dma_mode;
+       int riwt_off;
+       int max_speed;
+       int maxmtu;
+       void (*fix_mac_speed)(void *priv, unsigned int speed);
+       void (*bus_setup)(void __iomem *ioaddr);
+       int (*init)(struct platform_device *pdev, void *priv);
+       void (*exit)(struct platform_device *pdev, void *priv);
+       void *bsp_priv;
+       int has_gmac4;
+       bool tso_en;
+};
+
+Where:
+ o phy_bus_name: phy bus name to attach to the stmmac.
+ o bus_id: bus identifier.
+ o phy_addr: the physical address can be passed from the platform.
+           If it is set to -1 the driver will automatically
+           detect it at run-time by probing all the 32 addresses.
+ o interface: PHY device's interface.
+ o mdio_bus_data: specific platform fields for the MDIO bus.
+ o dma_cfg: internal DMA parameters
+   o pbl: the Programmable Burst Length is maximum number of beats to
+       be transferred in one DMA transaction.
+       GMAC also enables the 4xPBL by default. (8xPBL for GMAC 3.50 and newer)
+   o txpbl/rxpbl: GMAC and newer supports independent DMA pbl for tx/rx.
+   o pblx8: Enable 8xPBL (4xPBL for core rev < 3.50). Enabled by default.
+   o fixed_burst/mixed_burst/aal
+ o clk_csr: fixed CSR Clock range selection.
+ o has_gmac: uses the GMAC core.
+ o enh_desc: if sets the MAC will use the enhanced descriptor structure.
+ o tx_coe: core is able to perform the tx csum in HW.
+ o rx_coe: the supports three check sum offloading engine types:
+          type_1, type_2 (full csum) and no RX coe.
+ o bugged_jumbo: some HWs are not able to perform the csum in HW for
+               over-sized frames due to limited buffer sizes.
+               Setting this flag the csum will be done in SW on
+               JUMBO frames.
+ o pmt: core has the embedded power module (optional).
+ o force_sf_dma_mode: force DMA to use the Store and Forward mode
+                    instead of the Threshold.
+ o force_thresh_dma_mode: force DMA to use the Threshold mode other than
+                    the Store and Forward mode.
+ o riwt_off: force to disable the RX watchdog feature and switch to NAPI mode.
+ o fix_mac_speed: this callback is used for modifying some syscfg registers
+                (on ST SoCs) according to the link speed negotiated by the
+                physical layer .
+ o bus_setup: perform HW setup of the bus. For example, on some ST platforms
+            this field is used to configure the AMBA  bridge to generate more
+            efficient STBus traffic.
+ o init/exit: callbacks used for calling a custom initialization;
+            this is sometime necessary on some platforms (e.g. ST boxes)
+            where the HW needs to have set some PIO lines or system cfg
+            registers.  init/exit callbacks should not use or modify
+            platform data.
+ o bsp_priv: another private pointer.
+ o has_gmac4: uses GMAC4 core.
+ o tso_en: Enables TSO (TCP Segmentation Offload) feature.
+
+For MDIO bus The we have:
+
+ struct stmmac_mdio_bus_data {
+       int (*phy_reset)(void *priv);
+       unsigned int phy_mask;
+       int *irqs;
+       int probed_phy_irq;
+ };
+
+Where:
+ o phy_reset: hook to reset the phy device attached to the bus.
+ o phy_mask: phy mask passed when register the MDIO bus within the driver.
+ o irqs: list of IRQs, one per PHY.
+ o probed_phy_irq: if irqs is NULL, use this for probed PHY.
+
+For DMA engine we have the following internal fields that should be
+tuned according to the HW capabilities.
+
+struct stmmac_dma_cfg {
+       int pbl;
+       int txpbl;
+       int rxpbl;
+       bool pblx8;
+       int fixed_burst;
+       int mixed_burst;
+       bool aal;
+};
+
+Where:
+ o pbl: Programmable Burst Length (tx and rx)
+ o txpbl: Transmit Programmable Burst Length. Only for GMAC and newer.
+        If set, DMA tx will use this value rather than pbl.
+ o rxpbl: Receive Programmable Burst Length. Only for GMAC and newer.
+        If set, DMA rx will use this value rather than pbl.
+ o pblx8: Enable 8xPBL (4xPBL for core rev < 3.50). Enabled by default.
+ o fixed_burst: program the DMA to use the fixed burst mode
+ o mixed_burst: program the DMA to use the mixed burst mode
+ o aal: Address-Aligned Beats
+
+---
+
+Below an example how the structures above are using on ST platforms.
+
+ static struct plat_stmmacenet_data stxYYY_ethernet_platform_data = {
+       .has_gmac = 0,
+       .enh_desc = 0,
+       .fix_mac_speed = stxYYY_ethernet_fix_mac_speed,
+                               |
+                               |-> to write an internal syscfg
+                               |   on this platform when the
+                               |   link speed changes from 10 to
+                               |   100 and viceversa
+       .init = &stmmac_claim_resource,
+                               |
+                               |-> On ST SoC this calls own "PAD"
+                               |   manager framework to claim
+                               |   all the resources necessary
+                               |   (GPIO ...). The .custom_cfg field
+                               |   is used to pass a custom config.
+};
+
+Below the usage of the stmmac_mdio_bus_data: on this SoC, in fact,
+there are two MAC cores: one MAC is for MDIO Bus/PHY emulation
+with fixed_link support.
+
+static struct stmmac_mdio_bus_data stmmac1_mdio_bus = {
+       .phy_reset = phy_reset;
+               |
+               |-> function to provide the phy_reset on this board
+       .phy_mask = 0,
+};
+
+static struct fixed_phy_status stmmac0_fixed_phy_status = {
+       .link = 1,
+       .speed = 100,
+       .duplex = 1,
+};
+
+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);
+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).
+
+Note that, starting from new chips, where it is available the HW capability
+register, many configurations are discovered at run-time for example to
+understand if EEE, HW csum, PTP, enhanced descriptor etc are actually
+available. As strategy adopted in this driver, the information from the HW
+capability register can replace what has been passed from the platform.
+
+4.10) Device-tree support.
+
+Please see the following document:
+       Documentation/devicetree/bindings/net/stmmac.txt
+
+4.11) This is a summary of the content of some relevant files:
+ o stmmac_main.c: implements the main network device driver;
+ o stmmac_mdio.c: provides MDIO functions;
+ o stmmac_pci: this is the PCI driver;
+ o stmmac_platform.c: this the platform driver (OF supported);
+ o stmmac_ethtool.c: implements the ethtool support;
+ o stmmac.h: private driver structure;
+ o common.h: common definitions and VFTs;
+ o mmc_core.c/mmc.h: Management MAC Counters;
+ o stmmac_hwtstamp.c: HW timestamp support for PTP;
+ o stmmac_ptp.c: PTP 1588 clock;
+ o stmmac_pcs.h: Physical Coding Sublayer common implementation;
+ o dwmac-<XXX>.c: these are for the platform glue-logic file; e.g. dwmac-sti.c
+   for STMicroelectronics SoCs.
+
+- GMAC 3.x
+ o descs.h: descriptor structure definitions;
+ o dwmac1000_core.c: dwmac GiGa core functions;
+ o dwmac1000_dma.c: dma functions for the GMAC chip;
+ o dwmac1000.h: specific header file for the dwmac GiGa;
+ o dwmac100_core: dwmac 100 core code;
+ o dwmac100_dma.c: dma functions for the dwmac 100 chip;
+ o dwmac1000.h: specific header file for the MAC;
+ o dwmac_lib.c: generic DMA functions;
+ o enh_desc.c: functions for handling enhanced descriptors;
+ o norm_desc.c: functions for handling normal descriptors;
+ o chain_mode.c/ring_mode.c:: functions to manage RING/CHAINED modes;
+
+- GMAC4.x generation
+ o dwmac4_core.c: dwmac GMAC4.x core functions;
+ o dwmac4_desc.c: functions for handling GMAC4.x descriptors;
+ o dwmac4_descs.h: descriptor definitions;
+ o dwmac4_dma.c: dma functions for the GMAC4.x chip;
+ o dwmac4_dma.h: dma definitions for the GMAC4.x chip;
+ o dwmac4.h: core definitions for the GMAC4.x chip;
+ o dwmac4_lib.c: generic GMAC4.x functions;
+
+4.12) TSO support (GMAC4.x)
+
+TSO (Tcp Segmentation Offload) feature is supported by GMAC 4.x chip family.
+When a packet is sent through TCP protocol, the TCP stack ensures that
+the SKB provided to the low level driver (stmmac in our case) matches with
+the maximum frame len (IP header + TCP header + payload <= 1500 bytes (for
+MTU set to 1500)). It means that if an application using TCP want to send a
+packet which will have a length (after adding headers) > 1514 the packet
+will be split in several TCP packets: The data payload is split and headers
+(TCP/IP ..) are added. It is done by software.
+
+When TSO is enabled, the TCP stack doesn't care about the maximum frame
+length and provide SKB packet to stmmac as it is. The GMAC IP will have to
+perform the segmentation by it self to match with maximum frame length.
+
+This feature can be enabled in device tree through "snps,tso" entry.
+
+5) Debug Information
+
+The driver exports many information i.e. internal statistics,
+debug information, MAC and DMA registers etc.
+
+These can be read in several ways depending on the
+type of the information actually needed.
+
+For example a user can be use the ethtool support
+to get statistics: e.g. using: ethtool -S ethX
+(that shows the Management counters (MMC) if supported)
+or sees the MAC/DMA registers: e.g. using: ethtool -d ethX
+
+Compiling the Kernel with CONFIG_DEBUG_FS the driver will export the following
+debugfs entries:
+
+/sys/kernel/debug/stmmaceth/descriptors_status
+  To show the DMA TX/RX descriptor rings
+
+Developer can also use the "debug" module parameter to get further debug
+information (please see: NETIF Msg Level).
+
+6) Energy Efficient Ethernet
+
+Energy Efficient Ethernet(EEE) enables IEEE 802.3 MAC sublayer along
+with a family of Physical layer to operate in the Low power Idle(LPI)
+mode. The EEE mode supports the IEEE 802.3 MAC operation at 100Mbps,
+1000Mbps & 10Gbps.
+
+The LPI mode allows power saving by switching off parts of the
+communication device functionality when there is no data to be
+transmitted & received. The system on both the side of the link can
+disable some functionalities & save power during the period of low-link
+utilization. The MAC controls whether the system should enter or exit
+the LPI mode & communicate this to PHY.
+
+As soon as the interface is opened, the driver verifies if the EEE can
+be supported. This is done by looking at both the DMA HW capability
+register and the PHY devices MCD registers.
+To enter in Tx LPI mode the driver needs to have a software timer
+that enable and disable the LPI mode when there is nothing to be
+transmitted.
+
+7) Precision Time Protocol (PTP)
+The driver supports the IEEE 1588-2002, Precision Time Protocol (PTP),
+which enables precise synchronization of clocks in measurement and
+control systems implemented with technologies such as network
+communication.
+
+In addition to the basic timestamp features mentioned in IEEE 1588-2002
+Timestamps, new GMAC cores support the advanced timestamp features.
+IEEE 1588-2008 that can be enabled when configure the Kernel.
+
+8) SGMII/RGMII support
+New GMAC devices provide own way to manage RGMII/SGMII.
+This information is available at run-time by looking at the
+HW capability register. This means that the stmmac can manage
+auto-negotiation and link status w/o using the PHYLIB stuff.
+In fact, the HW provides a subset of extended registers to
+restart the ANE, verify Full/Half duplex mode and Speed.
+Thanks to these registers, it is possible to look at the
+Auto-negotiated Link Parter Ability.
diff --git a/Documentation/networking/device_drivers/ti/cpsw.txt b/Documentation/networking/device_drivers/ti/cpsw.txt
new file mode 100644 (file)
index 0000000..d4d4c07
--- /dev/null
@@ -0,0 +1,541 @@
+* Texas Instruments CPSW ethernet driver
+
+Multiqueue & CBS & MQPRIO
+=====================================================================
+=====================================================================
+
+The cpsw has 3 CBS shapers for each external ports. This document
+describes MQPRIO and CBS Qdisc offload configuration for cpsw driver
+based on examples. It potentially can be used in audio video bridging
+(AVB) and time sensitive networking (TSN).
+
+The following examples were tested on AM572x EVM and BBB boards.
+
+Test setup
+==========
+
+Under consideration two examples with AM572x EVM running cpsw driver
+in dual_emac mode.
+
+Several prerequisites:
+- TX queues must be rated starting from txq0 that has highest priority
+- Traffic classes are used starting from 0, that has highest priority
+- CBS shapers should be used with rated queues
+- The bandwidth for CBS shapers has to be set a little bit more then
+  potential incoming rate, thus, rate of all incoming tx queues has
+  to be a little less
+- Real rates can differ, due to discreetness
+- Map skb-priority to txq is not enough, also skb-priority to l2 prio
+  map has to be created with ip or vconfig tool
+- Any l2/socket prio (0 - 7) for classes can be used, but for
+  simplicity default values are used: 3 and 2
+- only 2 classes tested: A and B, but checked and can work with more,
+  maximum allowed 4, but only for 3 rate can be set.
+
+Test setup for examples
+=======================
+                                    +-------------------------------+
+                                    |--+                            |
+                                    |  |      Workstation0          |
+                                    |E |  MAC 18:03:73:66:87:42     |
++-----------------------------+  +--|t |                            |
+|                    | 1  | E |  |  |h |./tsn_listener -d \         |
+|  Target board:     | 0  | t |--+  |0 | 18:03:73:66:87:42 -i eth0 \|
+|  AM572x EVM        | 0  | h |     |  | -s 1500                    |
+|                    | 0  | 0 |     |--+                            |
+|  Only 2 classes:   |Mb  +---|     +-------------------------------+
+|  class A, class B  |        |
+|                    |    +---|     +-------------------------------+
+|                    | 1  | E |     |--+                            |
+|                    | 0  | t |     |  |      Workstation1          |
+|                    | 0  | h |--+  |E |  MAC 20:cf:30:85:7d:fd     |
+|                    |Mb  | 1 |  +--|t |                            |
++-----------------------------+     |h |./tsn_listener -d \         |
+                                    |0 | 20:cf:30:85:7d:fd -i eth0 \|
+                                    |  | -s 1500                    |
+                                    |--+                            |
+                                    +-------------------------------+
+
+*********************************************************************
+*********************************************************************
+*********************************************************************
+Example 1: One port tx AVB configuration scheme for target board
+----------------------------------------------------------------------
+(prints and scheme for AM572x evm, applicable for single port boards)
+
+tc - traffic class
+txq - transmit queue
+p - priority
+f - fifo (cpsw fifo)
+S - shaper configured
+
++------------------------------------------------------------------+ u
+| +---------------+  +---------------+  +------+ +------+          | s
+| |               |  |               |  |      | |      |          | e
+| | App 1         |  | App 2         |  | Apps | | Apps |          | r
+| | Class A       |  | Class B       |  | Rest | | Rest |          |
+| | Eth0          |  | Eth0          |  | Eth0 | | Eth1 |          | s
+| | VLAN100       |  | VLAN100       |  |   |  | |   |  |          | p
+| | 40 Mb/s       |  | 20 Mb/s       |  |   |  | |   |  |          | a
+| | SO_PRIORITY=3 |  | SO_PRIORITY=2 |  |   |  | |   |  |          | c
+| |   |           |  |   |           |  |   |  | |   |  |          | e
+| +---|-----------+  +---|-----------+  +---|--+ +---|--+          |
++-----|------------------|------------------|--------|-------------+
+    +-+     +------------+                  |        |
+    |       |             +-----------------+     +--+
+    |       |             |                       |
++---|-------|-------------|-----------------------|----------------+
+| +----+ +----+ +----+ +----+                   +----+             |
+| | p3 | | p2 | | p1 | | p0 |                   | p0 |             | k
+| \    / \    / \    / \    /                   \    /             | e
+|  \  /   \  /   \  /   \  /                     \  /              | r
+|   \/     \/     \/     \/                       \/               | n
+|    |     |             |                        |                | e
+|    |     |       +-----+                        |                | l
+|    |     |       |                              |                |
+| +----+ +----+ +----+                          +----+             | s
+| |tc0 | |tc1 | |tc2 |                          |tc0 |             | p
+| \    / \    / \    /                          \    /             | a
+|  \  /   \  /   \  /                            \  /              | c
+|   \/     \/     \/                              \/               | e
+|   |      |       +-----+                        |                |
+|   |      |       |     |                        |                |
+|   |      |       |     |                        |                |
+|   |      |       |     |                        |                |
+| +----+ +----+ +----+ +----+                   +----+             |
+| |txq0| |txq1| |txq2| |txq3|                   |txq4|             |
+| \    / \    / \    / \    /                   \    /             |
+|  \  /   \  /   \  /   \  /                     \  /              |
+|   \/     \/     \/     \/                       \/               |
+| +-|------|------|------|--+                  +--|--------------+ |
+| | |      |      |      |  | Eth0.100         |  |     Eth1     | |
++---|------|------|------|------------------------|----------------+
+    |      |      |      |                        |
+    p      p      p      p                        |
+    3      2      0-1, 4-7  <- L2 priority        |
+    |      |      |      |                        |
+    |      |      |      |                        |
++---|------|------|------|------------------------|----------------+
+|   |      |      |      |             |----------+                |
+| +----+ +----+ +----+ +----+       +----+                         |
+| |dma7| |dma6| |dma5| |dma4|       |dma3|                         |
+| \    / \    / \    / \    /       \    /                         | c
+|  \S /   \S /   \  /   \  /         \  /                          | p
+|   \/     \/     \/     \/           \/                           | s
+|   |      |      | +-----            |                            | w
+|   |      |      | |                 |                            |
+|   |      |      | |                 |                            | d
+| +----+ +----+ +----+p            p+----+                         | r
+| |    | |    | |    |o            o|    |                         | i
+| | f3 | | f2 | | f0 |r            r| f0 |                         | v
+| |tc0 | |tc1 | |tc2 |t            t|tc0 |                         | e
+| \CBS / \CBS / \CBS /1            2\CBS /                         | r
+|  \S /   \S /   \  /                \  /                          |
+|   \/     \/     \/                  \/                           |
++------------------------------------------------------------------+
+========================================Eth==========================>
+
+1)
+// Add 4 tx queues, for interface Eth0, and 1 tx queue for Eth1
+$ ethtool -L eth0 rx 1 tx 5
+rx unmodified, ignoring
+
+2)
+// Check if num of queues is set correctly:
+$ ethtool -l eth0
+Channel parameters for eth0:
+Pre-set maximums:
+RX:             8
+TX:             8
+Other:          0
+Combined:       0
+Current hardware settings:
+RX:             1
+TX:             5
+Other:          0
+Combined:       0
+
+3)
+// TX queues must be rated starting from 0, so set bws for tx0 and tx1
+// Set rates 40 and 20 Mb/s appropriately.
+// Pay attention, real speed can differ a bit due to discreetness.
+// Leave last 2 tx queues not rated.
+$ echo 40 > /sys/class/net/eth0/queues/tx-0/tx_maxrate
+$ echo 20 > /sys/class/net/eth0/queues/tx-1/tx_maxrate
+
+4)
+// Check maximum rate of tx (cpdma) queues:
+$ cat /sys/class/net/eth0/queues/tx-*/tx_maxrate
+40
+20
+0
+0
+0
+
+5)
+// Map skb->priority to traffic class:
+// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
+// Map traffic class to transmit queue:
+// tc0 -> txq0, tc1 -> txq1, tc2 -> (txq2, txq3)
+$ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \
+map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@2 hw 1
+
+5a)
+// As two interface sharing same set of tx queues, assign all traffic
+// coming to interface Eth1 to separate queue in order to not mix it
+// with traffic from interface Eth0, so use separate txq to send
+// packets to Eth1, so all prio -> tc0 and tc0 -> txq4
+// Here hw 0, so here still default configuration for eth1 in hw
+$ tc qdisc replace dev eth1 handle 100: parent root mqprio num_tc 1 \
+map 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 queues 1@4 hw 0
+
+6)
+// Check classes settings
+$ tc -g class show dev eth0
++---(100:ffe2) mqprio
+|    +---(100:3) mqprio
+|    +---(100:4) mqprio
+|
++---(100:ffe1) mqprio
+|    +---(100:2) mqprio
+|
++---(100:ffe0) mqprio
+     +---(100:1) mqprio
+
+$ tc -g class show dev eth1
++---(100:ffe0) mqprio
+     +---(100:5) mqprio
+
+7)
+// Set rate for class A - 41 Mbit (tc0, txq0) using CBS Qdisc
+// Set it +1 Mb for reserve (important!)
+// here only idle slope is important, others arg are ignored
+// Pay attention, real speed can differ a bit due to discreetness
+$ tc qdisc add dev eth0 parent 100:1 cbs locredit -1438 \
+hicredit 62 sendslope -959000 idleslope 41000 offload 1
+net eth0: set FIFO3 bw = 50
+
+8)
+// Set rate for class B - 21 Mbit (tc1, txq1) using CBS Qdisc:
+// Set it +1 Mb for reserve (important!)
+$ tc qdisc add dev eth0 parent 100:2 cbs locredit -1468 \
+hicredit 65 sendslope -979000 idleslope 21000 offload 1
+net eth0: set FIFO2 bw = 30
+
+9)
+// Create vlan 100 to map sk->priority to vlan qos
+$ ip link add link eth0 name eth0.100 type vlan id 100
+8021q: 802.1Q VLAN Support v1.8
+8021q: adding VLAN 0 to HW filter on device eth0
+8021q: adding VLAN 0 to HW filter on device eth1
+net eth0: Adding vlanid 100 to vlan filter
+
+10)
+// Map skb->priority to L2 prio, 1 to 1
+$ ip link set eth0.100 type vlan \
+egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+11)
+// Check egress map for vlan 100
+$ cat /proc/net/vlan/eth0.100
+[...]
+INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
+EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+12)
+// Run your appropriate tools with socket option "SO_PRIORITY"
+// to 3 for class A and/or to 2 for class B
+// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
+./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500&
+./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500&
+
+13)
+// run your listener on workstation (should be in same vlan)
+// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
+./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39000 kbps
+
+14)
+// Restore default configuration if needed
+$ ip link del eth0.100
+$ tc qdisc del dev eth1 root
+$ tc qdisc del dev eth0 root
+net eth0: Prev FIFO2 is shaped
+net eth0: set FIFO3 bw = 0
+net eth0: set FIFO2 bw = 0
+$ ethtool -L eth0 rx 1 tx 1
+
+*********************************************************************
+*********************************************************************
+*********************************************************************
+Example 2: Two port tx AVB configuration scheme for target board
+----------------------------------------------------------------------
+(prints and scheme for AM572x evm, for dual emac boards only)
+
++------------------------------------------------------------------+ u
+| +----------+  +----------+  +------+  +----------+  +----------+ | s
+| |          |  |          |  |      |  |          |  |          | | e
+| | App 1    |  | App 2    |  | Apps |  | App 3    |  | App 4    | | r
+| | Class A  |  | Class B  |  | Rest |  | Class B  |  | Class A  | |
+| | Eth0     |  | Eth0     |  |   |  |  | Eth1     |  | Eth1     | | s
+| | VLAN100  |  | VLAN100  |  |   |  |  | VLAN100  |  | VLAN100  | | p
+| | 40 Mb/s  |  | 20 Mb/s  |  |   |  |  | 10 Mb/s  |  | 30 Mb/s  | | a
+| | SO_PRI=3 |  | SO_PRI=2 |  |   |  |  | SO_PRI=3 |  | SO_PRI=2 | | c
+| |   |      |  |   |      |  |   |  |  |   |      |  |   |      | | e
+| +---|------+  +---|------+  +---|--+  +---|------+  +---|------+ |
++-----|-------------|-------------|---------|-------------|--------+
+    +-+     +-------+             |         +----------+  +----+
+    |       |             +-------+------+             |       |
+    |       |             |              |             |       |
++---|-------|-------------|--------------|-------------|-------|---+
+| +----+ +----+ +----+ +----+          +----+ +----+ +----+ +----+ |
+| | p3 | | p2 | | p1 | | p0 |          | p0 | | p1 | | p2 | | p3 | | k
+| \    / \    / \    / \    /          \    / \    / \    / \    / | e
+|  \  /   \  /   \  /   \  /            \  /   \  /   \  /   \  /  | r
+|   \/     \/     \/     \/              \/     \/     \/     \/   | n
+|   |      |             |                |             |      |   | e
+|   |      |        +----+                +----+        |      |   | l
+|   |      |        |                          |        |      |   |
+| +----+ +----+ +----+                        +----+ +----+ +----+ | s
+| |tc0 | |tc1 | |tc2 |                        |tc2 | |tc1 | |tc0 | | p
+| \    / \    / \    /                        \    / \    / \    / | a
+|  \  /   \  /   \  /                          \  /   \  /   \  /  | c
+|   \/     \/     \/                            \/     \/     \/   | e
+|   |      |       +-----+                +-----+      |       |   |
+|   |      |       |     |                |     |      |       |   |
+|   |      |       |     |                |     |      |       |   |
+|   |      |       |     |    E      E    |     |      |       |   |
+| +----+ +----+ +----+ +----+ t      t +----+ +----+ +----+ +----+ |
+| |txq0| |txq1| |txq4| |txq5| h      h |txq6| |txq7| |txq3| |txq2| |
+| \    / \    / \    / \    / 0      1 \    / \    / \    / \    / |
+|  \  /   \  /   \  /   \  /  .      .  \  /   \  /   \  /   \  /  |
+|   \/     \/     \/     \/   1      1   \/     \/     \/     \/   |
+| +-|------|------|------|--+ 0      0 +-|------|------|------|--+ |
+| | |      |      |      |  | 0      0 | |      |      |      |  | |
++---|------|------|------|---------------|------|------|------|----+
+    |      |      |      |               |      |      |      |
+    p      p      p      p               p      p      p      p
+    3      2      0-1, 4-7   <-L2 pri->  0-1, 4-7      2      3
+    |      |      |      |               |      |      |      |
+    |      |      |      |               |      |      |      |
++---|------|------|------|---------------|------|------|------|----+
+|   |      |      |      |               |      |      |      |    |
+| +----+ +----+ +----+ +----+          +----+ +----+ +----+ +----+ |
+| |dma7| |dma6| |dma3| |dma2|          |dma1| |dma0| |dma4| |dma5| |
+| \    / \    / \    / \    /          \    / \    / \    / \    / | c
+|  \S /   \S /   \  /   \  /            \  /   \  /   \S /   \S /  | p
+|   \/     \/     \/     \/              \/     \/     \/     \/   | s
+|   |      |      | +-----                |      |      |      |   | w
+|   |      |      | |                     +----+ |      |      |   |
+|   |      |      | |                          | |      |      |   | d
+| +----+ +----+ +----+p                      p+----+ +----+ +----+ | r
+| |    | |    | |    |o                      o|    | |    | |    | | i
+| | f3 | | f2 | | f0 |r        CPSW          r| f3 | | f2 | | f0 | | v
+| |tc0 | |tc1 | |tc2 |t                      t|tc0 | |tc1 | |tc2 | | e
+| \CBS / \CBS / \CBS /1                      2\CBS / \CBS / \CBS / | r
+|  \S /   \S /   \  /                          \S /   \S /   \  /  |
+|   \/     \/     \/                            \/     \/     \/   |
++------------------------------------------------------------------+
+========================================Eth==========================>
+
+1)
+// Add 8 tx queues, for interface Eth0, but they are common, so are accessed
+// by two interfaces Eth0 and Eth1.
+$ ethtool -L eth1 rx 1 tx 8
+rx unmodified, ignoring
+
+2)
+// Check if num of queues is set correctly:
+$ ethtool -l eth0
+Channel parameters for eth0:
+Pre-set maximums:
+RX:             8
+TX:             8
+Other:          0
+Combined:       0
+Current hardware settings:
+RX:             1
+TX:             8
+Other:          0
+Combined:       0
+
+3)
+// TX queues must be rated starting from 0, so set bws for tx0 and tx1 for Eth0
+// and for tx2 and tx3 for Eth1. That is, rates 40 and 20 Mb/s appropriately
+// for Eth0 and 30 and 10 Mb/s for Eth1.
+// Real speed can differ a bit due to discreetness
+// Leave last 4 tx queues as not rated
+$ echo 40 > /sys/class/net/eth0/queues/tx-0/tx_maxrate
+$ echo 20 > /sys/class/net/eth0/queues/tx-1/tx_maxrate
+$ echo 30 > /sys/class/net/eth1/queues/tx-2/tx_maxrate
+$ echo 10 > /sys/class/net/eth1/queues/tx-3/tx_maxrate
+
+4)
+// Check maximum rate of tx (cpdma) queues:
+$ cat /sys/class/net/eth0/queues/tx-*/tx_maxrate
+40
+20
+30
+10
+0
+0
+0
+0
+
+5)
+// Map skb->priority to traffic class for Eth0:
+// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
+// Map traffic class to transmit queue:
+// tc0 -> txq0, tc1 -> txq1, tc2 -> (txq4, txq5)
+$ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \
+map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@4 hw 1
+
+6)
+// Check classes settings
+$ tc -g class show dev eth0
++---(100:ffe2) mqprio
+|    +---(100:5) mqprio
+|    +---(100:6) mqprio
+|
++---(100:ffe1) mqprio
+|    +---(100:2) mqprio
+|
++---(100:ffe0) mqprio
+     +---(100:1) mqprio
+
+7)
+// Set rate for class A - 41 Mbit (tc0, txq0) using CBS Qdisc for Eth0
+// here only idle slope is important, others ignored
+// Real speed can differ a bit due to discreetness
+$ tc qdisc add dev eth0 parent 100:1 cbs locredit -1470 \
+hicredit 62 sendslope -959000 idleslope 41000 offload 1
+net eth0: set FIFO3 bw = 50
+
+8)
+// Set rate for class B - 21 Mbit (tc1, txq1) using CBS Qdisc for Eth0
+$ tc qdisc add dev eth0 parent 100:2 cbs locredit -1470 \
+hicredit 65 sendslope -979000 idleslope 21000 offload 1
+net eth0: set FIFO2 bw = 30
+
+9)
+// Create vlan 100 to map sk->priority to vlan qos for Eth0
+$ ip link add link eth0 name eth0.100 type vlan id 100
+net eth0: Adding vlanid 100 to vlan filter
+
+10)
+// Map skb->priority to L2 prio for Eth0.100, one to one
+$ ip link set eth0.100 type vlan \
+egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+11)
+// Check egress map for vlan 100
+$ cat /proc/net/vlan/eth0.100
+[...]
+INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
+EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+12)
+// Map skb->priority to traffic class for Eth1:
+// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
+// Map traffic class to transmit queue:
+// tc0 -> txq2, tc1 -> txq3, tc2 -> (txq6, txq7)
+$ tc qdisc replace dev eth1 handle 100: parent root mqprio num_tc 3 \
+map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@2 1@3 2@6 hw 1
+
+13)
+// Check classes settings
+$ tc -g class show dev eth1
++---(100:ffe2) mqprio
+|    +---(100:7) mqprio
+|    +---(100:8) mqprio
+|
++---(100:ffe1) mqprio
+|    +---(100:4) mqprio
+|
++---(100:ffe0) mqprio
+     +---(100:3) mqprio
+
+14)
+// Set rate for class A - 31 Mbit (tc0, txq2) using CBS Qdisc for Eth1
+// here only idle slope is important, others ignored, but calculated
+// for interface speed - 100Mb for eth1 port.
+// Set it +1 Mb for reserve (important!)
+$ tc qdisc add dev eth1 parent 100:3 cbs locredit -1035 \
+hicredit 465 sendslope -69000 idleslope 31000 offload 1
+net eth1: set FIFO3 bw = 31
+
+15)
+// Set rate for class B - 11 Mbit (tc1, txq3) using CBS Qdisc for Eth1
+// Set it +1 Mb for reserve (important!)
+$ tc qdisc add dev eth1 parent 100:4 cbs locredit -1335 \
+hicredit 405 sendslope -89000 idleslope 11000 offload 1
+net eth1: set FIFO2 bw = 11
+
+16)
+// Create vlan 100 to map sk->priority to vlan qos for Eth1
+$ ip link add link eth1 name eth1.100 type vlan id 100
+net eth1: Adding vlanid 100 to vlan filter
+
+17)
+// Map skb->priority to L2 prio for Eth1.100, one to one
+$ ip link set eth1.100 type vlan \
+egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+18)
+// Check egress map for vlan 100
+$ cat /proc/net/vlan/eth1.100
+[...]
+INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
+EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+19)
+// Run appropriate tools with socket option "SO_PRIORITY" to 3
+// for class A and to 2 for class B. For both interfaces
+./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500&
+./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500&
+./tsn_talker -d 20:cf:30:85:7d:fd -i eth1.100 -p2 -s 1500&
+./tsn_talker -d 20:cf:30:85:7d:fd -i eth1.100 -p3 -s 1500&
+
+20)
+// run your listener on workstation (should be in same vlan)
+// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
+./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39012 kbps
+Receiving data rate: 39000 kbps
+
+21)
+// Restore default configuration if needed
+$ ip link del eth1.100
+$ ip link del eth0.100
+$ tc qdisc del dev eth1 root
+net eth1: Prev FIFO2 is shaped
+net eth1: set FIFO3 bw = 0
+net eth1: set FIFO2 bw = 0
+$ tc qdisc del dev eth0 root
+net eth0: Prev FIFO2 is shaped
+net eth0: set FIFO3 bw = 0
+net eth0: set FIFO2 bw = 0
+$ ethtool -L eth0 rx 1 tx 1
diff --git a/Documentation/networking/device_drivers/ti/tlan.txt b/Documentation/networking/device_drivers/ti/tlan.txt
new file mode 100644 (file)
index 0000000..34550df
--- /dev/null
@@ -0,0 +1,117 @@
+(C) 1997-1998 Caldera, Inc.
+(C) 1998 James Banks
+(C) 1999-2001 Torben Mathiasen <tmm@image.dk, torben.mathiasen@compaq.com>
+
+For driver information/updates visit http://www.compaq.com
+
+
+TLAN driver for Linux, version 1.14a
+README
+
+
+I.  Supported Devices.
+
+    Only PCI devices will work with this driver.
+
+    Supported:
+    Vendor ID  Device ID       Name
+    0e11       ae32            Compaq Netelligent 10/100 TX PCI UTP
+    0e11       ae34            Compaq Netelligent 10 T PCI UTP
+    0e11       ae35            Compaq Integrated NetFlex 3/P
+    0e11       ae40            Compaq Netelligent Dual 10/100 TX PCI UTP
+    0e11       ae43            Compaq Netelligent Integrated 10/100 TX UTP
+    0e11       b011            Compaq Netelligent 10/100 TX Embedded UTP
+    0e11       b012            Compaq Netelligent 10 T/2 PCI UTP/Coax
+    0e11       b030            Compaq Netelligent 10/100 TX UTP
+    0e11       f130            Compaq NetFlex 3/P
+    0e11       f150            Compaq NetFlex 3/P
+    108d       0012            Olicom OC-2325  
+    108d       0013            Olicom OC-2183
+    108d       0014            Olicom OC-2326  
+
+
+    Caveats:
+    
+    I am not sure if 100BaseTX daughterboards (for those cards which
+    support such things) will work.  I haven't had any solid evidence
+    either way.
+
+    However, if a card supports 100BaseTx without requiring an add
+    on daughterboard, it should work with 100BaseTx.
+
+    The "Netelligent 10 T/2 PCI UTP/Coax" (b012) device is untested,
+    but I do not expect any problems.
+    
+
+II.   Driver Options
+       1. You can append debug=x to the end of the insmod line to get
+           debug messages, where x is a bit field where the bits mean
+          the following:
+          
+          0x01         Turn on general debugging messages.
+          0x02         Turn on receive debugging messages.
+          0x04         Turn on transmit debugging messages.
+          0x08         Turn on list debugging messages.
+
+       2. You can append aui=1 to the end of the insmod line to cause
+           the adapter to use the AUI interface instead of the 10 Base T
+           interface.  This is also what to do if you want to use the BNC
+          connector on a TLAN based device.  (Setting this option on a
+          device that does not have an AUI/BNC connector will probably
+          cause it to not function correctly.)
+
+       3. You can set duplex=1 to force half duplex, and duplex=2 to
+          force full duplex.
+
+       4. You can set speed=10 to force 10Mbs operation, and speed=100
+          to force 100Mbs operation. (I'm not sure what will happen
+          if a card which only supports 10Mbs is forced into 100Mbs
+          mode.)
+
+       5. You have to use speed=X duplex=Y together now. If you just
+          do "insmod tlan.o speed=100" the driver will do Auto-Neg.
+          To force a 10Mbps Half-Duplex link do "insmod tlan.o speed=10 
+          duplex=1".
+
+       6. If the driver is built into the kernel, you can use the 3rd
+          and 4th parameters to set aui and debug respectively.  For
+          example:
+
+          ether=0,0,0x1,0x7,eth0
+
+          This sets aui to 0x1 and debug to 0x7, assuming eth0 is a
+          supported TLAN device.
+
+          The bits in the third byte are assigned as follows:
+
+               0x01 = aui
+               0x02 = use half duplex
+               0x04 = use full duplex
+               0x08 = use 10BaseT
+               0x10 = use 100BaseTx
+
+          You also need to set both speed and duplex settings when forcing
+          speeds with kernel-parameters. 
+          ether=0,0,0x12,0,eth0 will force link to 100Mbps Half-Duplex.
+
+       7. If you have more than one tlan adapter in your system, you can
+          use the above options on a per adapter basis. To force a 100Mbit/HD
+          link with your eth1 adapter use:
+          
+          insmod tlan speed=0,100 duplex=0,1
+
+          Now eth0 will use auto-neg and eth1 will be forced to 100Mbit/HD.
+          Note that the tlan driver supports a maximum of 8 adapters.
+
+
+III.  Things to try if you have problems.
+       1. Make sure your card's PCI id is among those listed in
+          section I, above.
+       2. Make sure routing is correct.
+       3. Try forcing different speed/duplex settings
+
+
+There is also a tlan mailing list which you can join by sending "subscribe tlan"
+in the body of an email to majordomo@vuser.vu.union.edu.
+There is also a tlan website at http://www.compaq.com
+
diff --git a/Documentation/networking/device_drivers/toshiba/spider_net.txt b/Documentation/networking/device_drivers/toshiba/spider_net.txt
new file mode 100644 (file)
index 0000000..b0b75f8
--- /dev/null
@@ -0,0 +1,204 @@
+
+            The Spidernet Device Driver
+            ===========================
+
+Written by Linas Vepstas <linas@austin.ibm.com>
+
+Version of 7 June 2007
+
+Abstract
+========
+This document sketches the structure of portions of the spidernet
+device driver in the Linux kernel tree. The spidernet is a gigabit
+ethernet device built into the Toshiba southbridge commonly used
+in the SONY Playstation 3 and the IBM QS20 Cell blade.
+
+The Structure of the RX Ring.
+=============================
+The receive (RX) ring is a circular linked list of RX descriptors,
+together with three pointers into the ring that are used to manage its
+contents.
+
+The elements of the ring are called "descriptors" or "descrs"; they
+describe the received data. This includes a pointer to a buffer
+containing the received data, the buffer size, and various status bits.
+
+There are three primary states that a descriptor can be in: "empty",
+"full" and "not-in-use".  An "empty" or "ready" descriptor is ready
+to receive data from the hardware. A "full" descriptor has data in it,
+and is waiting to be emptied and processed by the OS. A "not-in-use"
+descriptor is neither empty or full; it is simply not ready. It may
+not even have a data buffer in it, or is otherwise unusable.
+
+During normal operation, on device startup, the OS (specifically, the
+spidernet device driver) allocates a set of RX descriptors and RX
+buffers. These are all marked "empty", ready to receive data. This
+ring is handed off to the hardware, which sequentially fills in the
+buffers, and marks them "full". The OS follows up, taking the full
+buffers, processing them, and re-marking them empty.
+
+This filling and emptying is managed by three pointers, the "head"
+and "tail" pointers, managed by the OS, and a hardware current
+descriptor pointer (GDACTDPA). The GDACTDPA points at the descr
+currently being filled. When this descr is filled, the hardware
+marks it full, and advances the GDACTDPA by one.  Thus, when there is
+flowing RX traffic, every descr behind it should be marked "full",
+and everything in front of it should be "empty".  If the hardware
+discovers that the current descr is not empty, it will signal an
+interrupt, and halt processing.
+
+The tail pointer tails or trails the hardware pointer. When the
+hardware is ahead, the tail pointer will be pointing at a "full"
+descr. The OS will process this descr, and then mark it "not-in-use",
+and advance the tail pointer.  Thus, when there is flowing RX traffic,
+all of the descrs in front of the tail pointer should be "full", and
+all of those behind it should be "not-in-use". When RX traffic is not
+flowing, then the tail pointer can catch up to the hardware pointer.
+The OS will then note that the current tail is "empty", and halt
+processing.
+
+The head pointer (somewhat mis-named) follows after the tail pointer.
+When traffic is flowing, then the head pointer will be pointing at
+a "not-in-use" descr. The OS will perform various housekeeping duties
+on this descr. This includes allocating a new data buffer and
+dma-mapping it so as to make it visible to the hardware. The OS will
+then mark the descr as "empty", ready to receive data. Thus, when there
+is flowing RX traffic, everything in front of the head pointer should
+be "not-in-use", and everything behind it should be "empty". If no
+RX traffic is flowing, then the head pointer can catch up to the tail
+pointer, at which point the OS will notice that the head descr is
+"empty", and it will halt processing.
+
+Thus, in an idle system, the GDACTDPA, tail and head pointers will
+all be pointing at the same descr, which should be "empty". All of the
+other descrs in the ring should be "empty" as well.
+
+The show_rx_chain() routine will print out the locations of the
+GDACTDPA, tail and head pointers. It will also summarize the contents
+of the ring, starting at the tail pointer, and listing the status
+of the descrs that follow.
+
+A typical example of the output, for a nearly idle system, might be
+
+net eth1: Total number of descrs=256
+net eth1: Chain tail located at descr=20
+net eth1: Chain head is at 20
+net eth1: HW curr desc (GDACTDPA) is at 21
+net eth1: Have 1 descrs with stat=x40800101
+net eth1: HW next desc (GDACNEXTDA) is at 22
+net eth1: Last 255 descrs with stat=xa0800000
+
+In the above, the hardware has filled in one descr, number 20. Both
+head and tail are pointing at 20, because it has not yet been emptied.
+Meanwhile, hw is pointing at 21, which is free.
+
+The "Have nnn decrs" refers to the descr starting at the tail: in this
+case, nnn=1 descr, starting at descr 20. The "Last nnn descrs" refers
+to all of the rest of the descrs, from the last status change. The "nnn"
+is a count of how many descrs have exactly the same status.
+
+The status x4... corresponds to "full" and status xa... corresponds
+to "empty". The actual value printed is RXCOMST_A.
+
+In the device driver source code, a different set of names are
+used for these same concepts, so that
+
+"empty" == SPIDER_NET_DESCR_CARDOWNED == 0xa
+"full"  == SPIDER_NET_DESCR_FRAME_END == 0x4
+"not in use" == SPIDER_NET_DESCR_NOT_IN_USE == 0xf
+
+
+The RX RAM full bug/feature
+===========================
+
+As long as the OS can empty out the RX buffers at a rate faster than
+the hardware can fill them, there is no problem. If, for some reason,
+the OS fails to empty the RX ring fast enough, the hardware GDACTDPA
+pointer will catch up to the head, notice the not-empty condition,
+ad stop. However, RX packets may still continue arriving on the wire.
+The spidernet chip can save some limited number of these in local RAM.
+When this local ram fills up, the spider chip will issue an interrupt
+indicating this (GHIINT0STS will show ERRINT, and the GRMFLLINT bit
+will be set in GHIINT1STS).  When the RX ram full condition occurs,
+a certain bug/feature is triggered that has to be specially handled.
+This section describes the special handling for this condition.
+
+When the OS finally has a chance to run, it will empty out the RX ring.
+In particular, it will clear the descriptor on which the hardware had
+stopped. However, once the hardware has decided that a certain
+descriptor is invalid, it will not restart at that descriptor; instead
+it will restart at the next descr. This potentially will lead to a
+deadlock condition, as the tail pointer will be pointing at this descr,
+which, from the OS point of view, is empty; the OS will be waiting for
+this descr to be filled. However, the hardware has skipped this descr,
+and is filling the next descrs. Since the OS doesn't see this, there
+is a potential deadlock, with the OS waiting for one descr to fill,
+while the hardware is waiting for a different set of descrs to become
+empty.
+
+A call to show_rx_chain() at this point indicates the nature of the
+problem. A typical print when the network is hung shows the following:
+
+net eth1: Spider RX RAM full, incoming packets might be discarded!
+net eth1: Total number of descrs=256
+net eth1: Chain tail located at descr=255
+net eth1: Chain head is at 255
+net eth1: HW curr desc (GDACTDPA) is at 0
+net eth1: Have 1 descrs with stat=xa0800000
+net eth1: HW next desc (GDACNEXTDA) is at 1
+net eth1: Have 127 descrs with stat=x40800101
+net eth1: Have 1 descrs with stat=x40800001
+net eth1: Have 126 descrs with stat=x40800101
+net eth1: Last 1 descrs with stat=xa0800000
+
+Both the tail and head pointers are pointing at descr 255, which is
+marked xa... which is "empty". Thus, from the OS point of view, there
+is nothing to be done. In particular, there is the implicit assumption
+that everything in front of the "empty" descr must surely also be empty,
+as explained in the last section. The OS is waiting for descr 255 to
+become non-empty, which, in this case, will never happen.
+
+The HW pointer is at descr 0. This descr is marked 0x4.. or "full".
+Since its already full, the hardware can do nothing more, and thus has
+halted processing. Notice that descrs 0 through 254 are all marked
+"full", while descr 254 and 255 are empty. (The "Last 1 descrs" is
+descr 254, since tail was at 255.) Thus, the system is deadlocked,
+and there can be no forward progress; the OS thinks there's nothing
+to do, and the hardware has nowhere to put incoming data.
+
+This bug/feature is worked around with the spider_net_resync_head_ptr()
+routine. When the driver receives RX interrupts, but an examination
+of the RX chain seems to show it is empty, then it is probable that
+the hardware has skipped a descr or two (sometimes dozens under heavy
+network conditions). The spider_net_resync_head_ptr() subroutine will
+search the ring for the next full descr, and the driver will resume
+operations there.  Since this will leave "holes" in the ring, there
+is also a spider_net_resync_tail_ptr() that will skip over such holes.
+
+As of this writing, the spider_net_resync() strategy seems to work very
+well, even under heavy network loads.
+
+
+The TX ring
+===========
+The TX ring uses a low-watermark interrupt scheme to make sure that
+the TX queue is appropriately serviced for large packet sizes.
+
+For packet sizes greater than about 1KBytes, the kernel can fill
+the TX ring quicker than the device can drain it. Once the ring
+is full, the netdev is stopped. When there is room in the ring,
+the netdev needs to be reawakened, so that more TX packets are placed
+in the ring. The hardware can empty the ring about four times per jiffy,
+so its not appropriate to wait for the poll routine to refill, since
+the poll routine runs only once per jiffy.  The low-watermark mechanism
+marks a descr about 1/4th of the way from the bottom of the queue, so
+that an interrupt is generated when the descr is processed. This
+interrupt wakes up the netdev, which can then refill the queue.
+For large packets, this mechanism generates a relatively small number
+of interrupts, about 1K/sec. For smaller packets, this will drop to zero
+interrupts, as the hardware can empty the queue faster than the kernel
+can fill it.
+
+
+ ======= END OF DOCUMENT ========
+
index ae444ff..2d26434 100644 (file)
@@ -40,3 +40,12 @@ msix_vec_per_pf_min  [DEVICE, GENERIC]
                        for the device initialization. Value is same across all
                        physical functions (PFs) in the device.
                        Type: u32
+
+fw_load_policy         [DEVICE, GENERIC]
+                       Controls the device's firmware loading policy.
+                       Valid values:
+                       * DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER (0)
+                         Load firmware version preferred by the driver.
+                       * DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH (1)
+                         Load firmware currently stored in flash.
+                       Type: u8
diff --git a/Documentation/networking/dl2k.txt b/Documentation/networking/dl2k.txt
deleted file mode 100644 (file)
index cba74f7..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-
-    D-Link DL2000-based Gigabit Ethernet Adapter Installation
-    for Linux
-    May 23, 2002
-
-Contents
-========
- - Compatibility List
- - Quick Install
- - Compiling the Driver
- - Installing the Driver
- - Option parameter
- - Configuration Script Sample
- - Troubleshooting
-
-
-Compatibility List
-=================
-Adapter Support:
-
-D-Link DGE-550T Gigabit Ethernet Adapter.
-D-Link DGE-550SX Gigabit Ethernet Adapter.
-D-Link DL2000-based Gigabit Ethernet Adapter.
-
-
-The driver support Linux kernel 2.4.7 later. We had tested it
-on the environments below.
-
- . Red Hat v6.2 (update kernel to 2.4.7)
- . Red Hat v7.0 (update kernel to 2.4.7)
- . Red Hat v7.1 (kernel 2.4.7)
- . Red Hat v7.2 (kernel 2.4.7-10)
-
-
-Quick Install
-=============
-Install linux driver as following command:
-
-1. make all
-2. insmod dl2k.ko
-3. ifconfig eth0 up 10.xxx.xxx.xxx netmask 255.0.0.0
-                   ^^^^^^^^^^^^^^^\        ^^^^^^^^\
-                                   IP               NETMASK
-Now eth0 should active, you can test it by "ping" or get more information by
-"ifconfig". If tested ok, continue the next step.
-
-4. cp dl2k.ko /lib/modules/`uname -r`/kernel/drivers/net
-5. Add the following line to /etc/modprobe.d/dl2k.conf:
-       alias eth0 dl2k
-6. Run depmod to updated module indexes.
-7. Run "netconfig" or "netconf" to create configuration script ifcfg-eth0
-   located at /etc/sysconfig/network-scripts or create it manually.
-   [see - Configuration Script Sample]
-8. Driver will automatically load and configure at next boot time.
-
-Compiling the Driver
-====================
-  In Linux, NIC drivers are most commonly configured as loadable modules.
-The approach of building a monolithic kernel has become obsolete. The driver
-can be compiled as part of a monolithic kernel, but is strongly discouraged.
-The remainder of this section assumes the driver is built as a loadable module.
-In the Linux environment, it is a good idea to rebuild the driver from the
-source instead of relying on a precompiled version. This approach provides
-better reliability since a precompiled driver might depend on libraries or
-kernel features that are not present in a given Linux installation.
-
-The 3 files necessary to build Linux device driver are dl2k.c, dl2k.h and
-Makefile. To compile, the Linux installation must include the gcc compiler,
-the kernel source, and the kernel headers. The Linux driver supports Linux
-Kernels 2.4.7. Copy the files to a directory and enter the following command
-to compile and link the driver:
-
-CD-ROM drive
-------------
-
-[root@XXX /] mkdir cdrom
-[root@XXX /] mount -r -t iso9660 -o conv=auto /dev/cdrom /cdrom
-[root@XXX /] cd root
-[root@XXX /root] mkdir dl2k
-[root@XXX /root] cd dl2k
-[root@XXX dl2k] cp /cdrom/linux/dl2k.tgz /root/dl2k
-[root@XXX dl2k] tar xfvz dl2k.tgz
-[root@XXX dl2k] make all
-
-Floppy disc drive
------------------
-
-[root@XXX /] cd root
-[root@XXX /root] mkdir dl2k
-[root@XXX /root] cd dl2k
-[root@XXX dl2k] mcopy a:/linux/dl2k.tgz /root/dl2k
-[root@XXX dl2k] tar xfvz dl2k.tgz
-[root@XXX dl2k] make all
-
-Installing the Driver
-=====================
-
-  Manual Installation
-  -------------------
-  Once the driver has been compiled, it must be loaded, enabled, and bound
-  to a protocol stack in order to establish network connectivity. To load a
-  module enter the command:
-
-  insmod dl2k.o
-
-  or
-
-  insmod dl2k.o <optional parameter>   ; add parameter
-
-  ===============================================================
-   example: insmod dl2k.o media=100mbps_hd
-   or      insmod dl2k.o media=3
-   or      insmod dl2k.o media=3,2     ; for 2 cards
-  ===============================================================
-
-  Please reference the list of the command line parameters supported by
-  the Linux device driver below.
-
-  The insmod command only loads the driver and gives it a name of the form
-  eth0, eth1, etc. To bring the NIC into an operational state,
-  it is necessary to issue the following command:
-
-  ifconfig eth0 up
-
-  Finally, to bind the driver to the active protocol (e.g., TCP/IP with
-  Linux), enter the following command:
-
-  ifup eth0
-
-  Note that this is meaningful only if the system can find a configuration
-  script that contains the necessary network information. A sample will be
-  given in the next paragraph.
-
-  The commands to unload a driver are as follows:
-
-  ifdown eth0
-  ifconfig eth0 down
-  rmmod dl2k.o
-
-  The following are the commands to list the currently loaded modules and
-  to see the current network configuration.
-
-  lsmod
-  ifconfig
-
-
-  Automated Installation
-  ----------------------
-  This section describes how to install the driver such that it is
-  automatically loaded and configured at boot time. The following description
-  is based on a Red Hat 6.0/7.0 distribution, but it can easily be ported to
-  other distributions as well.
-
-  Red Hat v6.x/v7.x
-  -----------------
-  1. Copy dl2k.o to the network modules directory, typically
-     /lib/modules/2.x.x-xx/net or /lib/modules/2.x.x/kernel/drivers/net.
-  2. Locate the boot module configuration file, most commonly in the
-     /etc/modprobe.d/ directory. Add the following lines:
-
-     alias ethx dl2k
-     options dl2k <optional parameters>
-
-     where ethx will be eth0 if the NIC is the only ethernet adapter, eth1 if
-     one other ethernet adapter is installed, etc. Refer to the table in the
-     previous section for the list of optional parameters.
-  3. Locate the network configuration scripts, normally the
-     /etc/sysconfig/network-scripts directory, and create a configuration
-     script named ifcfg-ethx that contains network information.
-  4. Note that for most Linux distributions, Red Hat included, a configuration
-     utility with a graphical user interface is provided to perform steps 2
-     and 3 above.
-
-
-Parameter Description
-=====================
-You can install this driver without any additional parameter. However, if you
-are going to have extensive functions then it is necessary to set extra
-parameter. Below is a list of the command line parameters supported by the
-Linux device
-driver.
-
-mtu=packet_size                        - Specifies the maximum packet size. default
-                                 is 1500.
-
-media=media_type               - Specifies the media type the NIC operates at.
-                                 autosense     Autosensing active media.
-                                 10mbps_hd     10Mbps half duplex.
-                                 10mbps_fd     10Mbps full duplex.
-                                 100mbps_hd    100Mbps half duplex.
-                                 100mbps_fd    100Mbps full duplex.
-                                 1000mbps_fd   1000Mbps full duplex.
-                                 1000mbps_hd   1000Mbps half duplex.
-                                 0             Autosensing active media.
-                                 1             10Mbps half duplex.
-                                 2             10Mbps full duplex.
-                                 3             100Mbps half duplex.
-                                 4             100Mbps full duplex.
-                                 5             1000Mbps half duplex.
-                                 6             1000Mbps full duplex.
-
-                                 By default, the NIC operates at autosense.
-                                 1000mbps_fd and 1000mbps_hd types are only
-                                 available for fiber adapter.
-
-vlan=n                         - Specifies the VLAN ID. If vlan=0, the
-                                 Virtual Local Area Network (VLAN) function is
-                                 disable.
-
-jumbo=[0|1]                    - Specifies the jumbo frame support. If jumbo=1,
-                                 the NIC accept jumbo frames. By default, this
-                                 function is disabled.
-                                 Jumbo frame usually improve the performance
-                                 int gigabit.
-                                 This feature need jumbo frame compatible 
-                                 remote.
-                                 
-rx_coalesce=m                  - Number of rx frame handled each interrupt.
-rx_timeout=n                   - Rx DMA wait time for an interrupt. 
-                                 If set rx_coalesce > 0, hardware only assert 
-                                 an interrupt for m frames. Hardware won't 
-                                 assert rx interrupt until m frames received or
-                                 reach timeout of n * 640 nano seconds. 
-                                 Set proper rx_coalesce and rx_timeout can 
-                                 reduce congestion collapse and overload which
-                                 has been a bottleneck for high speed network.
-                                 
-                                 For example, rx_coalesce=10 rx_timeout=800.
-                                 that is, hardware assert only 1 interrupt 
-                                 for 10 frames received or timeout of 512 us. 
-
-tx_coalesce=n                  - Number of tx frame handled each interrupt.
-                                 Set n > 1 can reduce the interrupts 
-                                 congestion usually lower performance of
-                                 high speed network card. Default is 16.
-                                 
-tx_flow=[1|0]                  - Specifies the Tx flow control. If tx_flow=0, 
-                                 the Tx flow control disable else driver
-                                 autodetect.
-rx_flow=[1|0]                  - Specifies the Rx flow control. If rx_flow=0, 
-                                 the Rx flow control enable else driver
-                                 autodetect.
-
-
-Configuration Script Sample
-===========================
-Here is a sample of a simple configuration script:
-
-DEVICE=eth0
-USERCTL=no
-ONBOOT=yes
-POOTPROTO=none
-BROADCAST=207.200.5.255
-NETWORK=207.200.5.0
-NETMASK=255.255.255.0
-IPADDR=207.200.5.2
-
-
-Troubleshooting
-===============
-Q1. Source files contain ^ M behind every line.
-       Make sure all files are Unix file format (no LF). Try the following
-    shell command to convert files.
-
-       cat dl2k.c | col -b > dl2k.tmp
-       mv dl2k.tmp dl2k.c
-
-       OR
-
-       cat dl2k.c | tr -d "\r" > dl2k.tmp
-       mv dl2k.tmp dl2k.c
-
-Q2: Could not find header files (*.h) ?
-       To compile the driver, you need kernel header files. After
-    installing the kernel source, the header files are usually located in
-    /usr/src/linux/include, which is the default include directory configured
-    in Makefile. For some distributions, there is a copy of header files in
-    /usr/src/include/linux and /usr/src/include/asm, that you can change the
-    INCLUDEDIR in Makefile to /usr/include without installing kernel source.
-       Note that RH 7.0 didn't provide correct header files in /usr/include,
-    including those files will make a wrong version driver.
-
diff --git a/Documentation/networking/dm9000.txt b/Documentation/networking/dm9000.txt
deleted file mode 100644 (file)
index 5552e2e..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-DM9000 Network driver
-=====================
-
-Copyright 2008 Simtec Electronics,
-         Ben Dooks <ben@simtec.co.uk> <ben-linux@fluff.org>
-
-
-Introduction
-------------
-
-This file describes how to use the DM9000 platform-device based network driver
-that is contained in the files drivers/net/dm9000.c and drivers/net/dm9000.h.
-
-The driver supports three DM9000 variants, the DM9000E which is the first chip
-supported as well as the newer DM9000A and DM9000B devices. It is currently
-maintained and tested by Ben Dooks, who should be CC: to any patches for this
-driver.
-
-
-Defining the platform device
-----------------------------
-
-The minimum set of resources attached to the platform device are as follows:
-
-    1) The physical address of the address register
-    2) The physical address of the data register
-    3) The IRQ line the device's interrupt pin is connected to.
-
-These resources should be specified in that order, as the ordering of the
-two address regions is important (the driver expects these to be address
-and then data).
-
-An example from arch/arm/mach-s3c2410/mach-bast.c is:
-
-static struct resource bast_dm9k_resource[] = {
-       [0] = {
-               .start = S3C2410_CS5 + BAST_PA_DM9000,
-               .end   = S3C2410_CS5 + BAST_PA_DM9000 + 3,
-               .flags = IORESOURCE_MEM,
-       },
-       [1] = {
-               .start = S3C2410_CS5 + BAST_PA_DM9000 + 0x40,
-               .end   = S3C2410_CS5 + BAST_PA_DM9000 + 0x40 + 0x3f,
-               .flags = IORESOURCE_MEM,
-       },
-       [2] = {
-               .start = IRQ_DM9000,
-               .end   = IRQ_DM9000,
-               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
-       }
-};
-
-static struct platform_device bast_device_dm9k = {
-       .name           = "dm9000",
-       .id             = 0,
-       .num_resources  = ARRAY_SIZE(bast_dm9k_resource),
-       .resource       = bast_dm9k_resource,
-};
-
-Note the setting of the IRQ trigger flag in bast_dm9k_resource[2].flags,
-as this will generate a warning if it is not present. The trigger from
-the flags field will be passed to request_irq() when registering the IRQ
-handler to ensure that the IRQ is setup correctly.
-
-This shows a typical platform device, without the optional configuration
-platform data supplied. The next example uses the same resources, but adds
-the optional platform data to pass extra configuration data:
-
-static struct dm9000_plat_data bast_dm9k_platdata = {
-       .flags          = DM9000_PLATF_16BITONLY,
-};
-
-static struct platform_device bast_device_dm9k = {
-       .name           = "dm9000",
-       .id             = 0,
-       .num_resources  = ARRAY_SIZE(bast_dm9k_resource),
-       .resource       = bast_dm9k_resource,
-       .dev            = {
-               .platform_data = &bast_dm9k_platdata,
-       }
-};
-
-The platform data is defined in include/linux/dm9000.h and described below.
-
-
-Platform data
--------------
-
-Extra platform data for the DM9000 can describe the IO bus width to the
-device, whether or not an external PHY is attached to the device and
-the availability of an external configuration EEPROM.
-
-The flags for the platform data .flags field are as follows:
-
-DM9000_PLATF_8BITONLY
-
-       The IO should be done with 8bit operations.
-
-DM9000_PLATF_16BITONLY
-
-       The IO should be done with 16bit operations.
-
-DM9000_PLATF_32BITONLY
-
-       The IO should be done with 32bit operations.
-
-DM9000_PLATF_EXT_PHY
-
-       The chip is connected to an external PHY.
-
-DM9000_PLATF_NO_EEPROM
-
-       This can be used to signify that the board does not have an
-       EEPROM, or that the EEPROM should be hidden from the user.
-
-DM9000_PLATF_SIMPLE_PHY
-
-       Switch to using the simpler PHY polling method which does not
-       try and read the MII PHY state regularly. This is only available
-       when using the internal PHY. See the section on link state polling
-       for more information.
-
-       The config symbol DM9000_FORCE_SIMPLE_PHY_POLL, Kconfig entry
-       "Force simple NSR based PHY polling" allows this flag to be
-       forced on at build time.
-
-
-PHY Link state polling
-----------------------
-
-The driver keeps track of the link state and informs the network core
-about link (carrier) availability. This is managed by several methods
-depending on the version of the chip and on which PHY is being used.
-
-For the internal PHY, the original (and currently default) method is
-to read the MII state, either when the status changes if we have the
-necessary interrupt support in the chip or every two seconds via a
-periodic timer.
-
-To reduce the overhead for the internal PHY, there is now the option
-of using the DM9000_FORCE_SIMPLE_PHY_POLL config, or DM9000_PLATF_SIMPLE_PHY
-platform data option to read the summary information without the
-expensive MII accesses. This method is faster, but does not print
-as much information.
-
-When using an external PHY, the driver currently has to poll the MII
-link status as there is no method for getting an interrupt on link change.
-
-
-DM9000A / DM9000B
------------------
-
-These chips are functionally similar to the DM9000E and are supported easily
-by the same driver. The features are:
-
-   1) Interrupt on internal PHY state change. This means that the periodic
-      polling of the PHY status may be disabled on these devices when using
-      the internal PHY.
-
-   2) TCP/UDP checksum offloading, which the driver does not currently support.
-
-
-ethtool
--------
-
-The driver supports the ethtool interface for access to the driver
-state information, the PHY state and the EEPROM.
diff --git a/Documentation/networking/dmfe.txt b/Documentation/networking/dmfe.txt
deleted file mode 100644 (file)
index 25320bf..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-Note: This driver doesn't have a maintainer.
-
-Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux.
-
-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.
-
-
-This driver provides kernel support for Davicom DM9102(A)/DM9132/DM9801 ethernet cards ( CNET
-10/100 ethernet cards uses Davicom chipset too, so this driver supports CNET cards too ).If you
-didn't compile this driver as a module, it will automatically load itself on boot and print a
-line similar to :
-
-       dmfe: Davicom DM9xxx net driver, version 1.36.4 (2002-01-17)
-
-If you compiled this driver as a module, you have to load it on boot.You can load it with command :
-
-       insmod dmfe
-
-This way it will autodetect the device mode.This is the suggested way to load the module.Or you can pass
-a mode= setting to module while loading, like :
-
-       insmod dmfe mode=0 # Force 10M Half Duplex
-       insmod dmfe mode=1 # Force 100M Half Duplex
-       insmod dmfe mode=4 # Force 10M Full Duplex
-       insmod dmfe mode=5 # Force 100M Full Duplex
-
-Next you should configure your network interface with a command similar to :
-
-       ifconfig eth0 172.22.3.18
-                      ^^^^^^^^^^^
-                    Your IP Address
-
-Then you may have to modify the default routing table with command :
-
-       route add default eth0
-
-
-Now your ethernet card should be up and running.
-
-
-TODO:
-
-Implement pci_driver::suspend() and pci_driver::resume() power management methods.
-Check on 64 bit boxes.
-Check and fix on big endian boxes.
-Test and make sure PCI latency is now correct for all cases.
-
-
-Authors:
-
-Sten Wang <sten_wang@davicom.com.tw >   : Original Author
-
-Contributors:
-
-Marcelo Tosatti <marcelo@conectiva.com.br>
-Alan Cox <alan@lxorguk.ukuu.org.uk>
-Jeff Garzik <jgarzik@pobox.com>
-Vojtech Pavlik <vojtech@suse.cz>
diff --git a/Documentation/networking/dpaa.txt b/Documentation/networking/dpaa.txt
deleted file mode 100644 (file)
index f88194f..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-The QorIQ DPAA Ethernet Driver
-==============================
-
-Authors:
-Madalin Bucur <madalin.bucur@nxp.com>
-Camelia Groza <camelia.groza@nxp.com>
-
-Contents
-========
-
-       - DPAA Ethernet Overview
-       - DPAA Ethernet Supported SoCs
-       - Configuring DPAA Ethernet in your kernel
-       - DPAA Ethernet Frame Processing
-       - DPAA Ethernet Features
-       - DPAA IRQ Affinity and Receive Side Scaling
-       - Debugging
-
-DPAA Ethernet Overview
-======================
-
-DPAA stands for Data Path Acceleration Architecture and it is a
-set of networking acceleration IPs that are available on several
-generations of SoCs, both on PowerPC and ARM64.
-
-The Freescale DPAA architecture consists of a series of hardware blocks
-that support Ethernet connectivity. The Ethernet driver depends upon the
-following drivers in the Linux kernel:
-
- - Peripheral Access Memory Unit (PAMU) (* needed only for PPC platforms)
-    drivers/iommu/fsl_*
- - Frame Manager (FMan)
-    drivers/net/ethernet/freescale/fman
- - Queue Manager (QMan), Buffer Manager (BMan)
-    drivers/soc/fsl/qbman
-
-A simplified view of the dpaa_eth interfaces mapped to FMan MACs:
-
-  dpaa_eth       /eth0\     ...       /ethN\
-  driver        |      |             |      |
-  -------------   ----   -----------   ----   -------------
-       -Ports  / Tx  Rx \    ...    / Tx  Rx \
-  FMan        |          |         |          |
-       -MACs  |   MAC0   |         |   MACN   |
-             /   dtsec0   \  ...  /   dtsecN   \ (or tgec)
-            /              \     /              \(or memac)
-  ---------  --------------  ---  --------------  ---------
-      FMan, FMan Port, FMan SP, FMan MURAM drivers
-  ---------------------------------------------------------
-      FMan HW blocks: MURAM, MACs, Ports, SP
-  ---------------------------------------------------------
-
-The dpaa_eth relation to the QMan, BMan and FMan:
-              ________________________________
-  dpaa_eth   /            eth0                \
-  driver    /                                  \
-  ---------   -^-   -^-   -^-   ---    ---------
-  QMan driver / \   / \   / \  \   /  | BMan    |
-             |Rx | |Rx | |Tx | |Tx |  | driver  |
-  ---------  |Dfl| |Err| |Cnf| |FQs|  |         |
-  QMan HW    |FQ | |FQ | |FQs| |   |  |         |
-             /   \ /   \ /   \  \ /   |         |
-  ---------   ---   ---   ---   -v-    ---------
-            |        FMan QMI         |         |
-            | FMan HW       FMan BMI  | BMan HW |
-              -----------------------   --------
-
-where the acronyms used above (and in the code) are:
-DPAA = Data Path Acceleration Architecture
-FMan = DPAA Frame Manager
-QMan = DPAA Queue Manager
-BMan = DPAA Buffers Manager
-QMI = QMan interface in FMan
-BMI = BMan interface in FMan
-FMan SP = FMan Storage Profiles
-MURAM = Multi-user RAM in FMan
-FQ = QMan Frame Queue
-Rx Dfl FQ = default reception FQ
-Rx Err FQ = Rx error frames FQ
-Tx Cnf FQ = Tx confirmation FQs
-Tx FQs = transmission frame queues
-dtsec = datapath three speed Ethernet controller (10/100/1000 Mbps)
-tgec = ten gigabit Ethernet controller (10 Gbps)
-memac = multirate Ethernet MAC (10/100/1000/10000)
-
-DPAA Ethernet Supported SoCs
-============================
-
-The DPAA drivers enable the Ethernet controllers present on the following SoCs:
-
-# PPC
-P1023
-P2041
-P3041
-P4080
-P5020
-P5040
-T1023
-T1024
-T1040
-T1042
-T2080
-T4240
-B4860
-
-# ARM
-LS1043A
-LS1046A
-
-Configuring DPAA Ethernet in your kernel
-========================================
-
-To enable the DPAA Ethernet driver, the following Kconfig options are required:
-
-# common for arch/arm64 and arch/powerpc platforms
-CONFIG_FSL_DPAA=y
-CONFIG_FSL_FMAN=y
-CONFIG_FSL_DPAA_ETH=y
-CONFIG_FSL_XGMAC_MDIO=y
-
-# for arch/powerpc only
-CONFIG_FSL_PAMU=y
-
-# common options needed for the PHYs used on the RDBs
-CONFIG_VITESSE_PHY=y
-CONFIG_REALTEK_PHY=y
-CONFIG_AQUANTIA_PHY=y
-
-DPAA Ethernet Frame Processing
-==============================
-
-On Rx, buffers for the incoming frames are retrieved from one of the three
-existing buffers pools. The driver initializes and seeds these, each with
-buffers of different sizes: 1KB, 2KB and 4KB.
-
-On Tx, all transmitted frames are returned to the driver through Tx
-confirmation frame queues. The driver is then responsible for freeing the
-buffers. In order to do this properly, a backpointer is added to the buffer
-before transmission that points to the skb. When the buffer returns to the
-driver on a confirmation FQ, the skb can be correctly consumed.
-
-DPAA Ethernet Features
-======================
-
-Currently the DPAA Ethernet driver enables the basic features required for
-a Linux Ethernet driver. The support for advanced features will be added
-gradually.
-
-The driver has Rx and Tx checksum offloading for UDP and TCP. Currently the Rx
-checksum offload feature is enabled by default and cannot be controlled through
-ethtool. Also, rx-flow-hash and rx-hashing was added. The addition of RSS
-provides a big performance boost for the forwarding scenarios, allowing
-different traffic flows received by one interface to be processed by different
-CPUs in parallel.
-
-The driver has support for multiple prioritized Tx traffic classes. Priorities
-range from 0 (lowest) to 3 (highest). These are mapped to HW workqueues with
-strict priority levels. Each traffic class contains NR_CPU TX queues. By
-default, only one traffic class is enabled and the lowest priority Tx queues
-are used. Higher priority traffic classes can be enabled with the mqprio
-qdisc. For example, all four traffic classes are enabled on an interface with
-the following command. Furthermore, skb priority levels are mapped to traffic
-classes as follows:
-
-       * priorities 0 to 3 - traffic class 0 (low priority)
-       * priorities 4 to 7 - traffic class 1 (medium-low priority)
-       * priorities 8 to 11 - traffic class 2 (medium-high priority)
-       * priorities 12 to 15 - traffic class 3 (high priority)
-
-tc qdisc add dev <int> root handle 1: \
-        mqprio num_tc 4 map 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 hw 1
-
-DPAA IRQ Affinity and Receive Side Scaling
-==========================================
-
-Traffic coming on the DPAA Rx queues or on the DPAA Tx confirmation
-queues is seen by the CPU as ingress traffic on a certain portal.
-The DPAA QMan portal interrupts are affined each to a certain CPU.
-The same portal interrupt services all the QMan portal consumers.
-
-By default the DPAA Ethernet driver enables RSS, making use of the
-DPAA FMan Parser and Keygen blocks to distribute traffic on 128
-hardware frame queues using a hash on IP v4/v6 source and destination
-and L4 source and destination ports, in present in the received frame.
-When RSS is disabled, all traffic received by a certain interface is
-received on the default Rx frame queue. The default DPAA Rx frame
-queues are configured to put the received traffic into a pool channel
-that allows any available CPU portal to dequeue the ingress traffic.
-The default frame queues have the HOLDACTIVE option set, ensuring that
-traffic bursts from a certain queue are serviced by the same CPU.
-This ensures a very low rate of frame reordering. A drawback of this
-is that only one CPU at a time can service the traffic received by a
-certain interface when RSS is not enabled.
-
-To implement RSS, the DPAA Ethernet driver allocates an extra set of
-128 Rx frame queues that are configured to dedicated channels, in a
-round-robin manner. The mapping of the frame queues to CPUs is now
-hardcoded, there is no indirection table to move traffic for a certain
-FQ (hash result) to another CPU. The ingress traffic arriving on one
-of these frame queues will arrive at the same portal and will always
-be processed by the same CPU. This ensures intra-flow order preservation
-and workload distribution for multiple traffic flows.
-
-RSS can be turned off for a certain interface using ethtool, i.e.
-
-       # ethtool -N fm1-mac9 rx-flow-hash tcp4 ""
-
-To turn it back on, one needs to set rx-flow-hash for tcp4/6 or udp4/6:
-
-       # ethtool -N fm1-mac9 rx-flow-hash udp4 sfdn
-
-There is no independent control for individual protocols, any command
-run for one of tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6 is
-going to control the rx-flow-hashing for all protocols on that interface.
-
-Besides using the FMan Keygen computed hash for spreading traffic on the
-128 Rx FQs, the DPAA Ethernet driver also sets the skb hash value when
-the NETIF_F_RXHASH feature is on (active by default). This can be turned
-on or off through ethtool, i.e.:
-
-       # ethtool -K fm1-mac9 rx-hashing off
-       # ethtool -k fm1-mac9 | grep hash
-       receive-hashing: off
-       # ethtool -K fm1-mac9 rx-hashing on
-       Actual changes:
-       receive-hashing: on
-       # ethtool -k fm1-mac9 | grep hash
-       receive-hashing: on
-
-Please note that Rx hashing depends upon the rx-flow-hashing being on
-for that interface - turning off rx-flow-hashing will also disable the
-rx-hashing (without ethtool reporting it as off as that depends on the
-NETIF_F_RXHASH feature flag).
-
-Debugging
-=========
-
-The following statistics are exported for each interface through ethtool:
-
-       - interrupt count per CPU
-       - Rx packets count per CPU
-       - Tx packets count per CPU
-       - Tx confirmed packets count per CPU
-       - Tx S/G frames count per CPU
-       - Tx error count per CPU
-       - Rx error count per CPU
-       - Rx error count per type
-       - congestion related statistics:
-               - congestion status
-               - time spent in congestion
-               - number of time the device entered congestion
-               - dropped packets count per cause
-
-The driver also exports the following information in sysfs:
-
-       - the FQ IDs for each FQ type
-       /sys/devices/platform/dpaa-ethernet.0/net/<int>/fqids
-
-       - the IDs of the buffer pools in use
-       /sys/devices/platform/dpaa-ethernet.0/net/<int>/bpids
diff --git a/Documentation/networking/dpaa2/dpio-driver.rst b/Documentation/networking/dpaa2/dpio-driver.rst
deleted file mode 100644 (file)
index 1358810..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-.. include:: <isonum.txt>
-
-DPAA2 DPIO (Data Path I/O) Overview
-===================================
-
-:Copyright: |copy| 2016-2018 NXP
-
-This document provides an overview of the Freescale DPAA2 DPIO
-drivers
-
-Introduction
-============
-
-A DPAA2 DPIO (Data Path I/O) is a hardware object that provides
-interfaces to enqueue and dequeue frames to/from network interfaces
-and other accelerators.  A DPIO also provides hardware buffer
-pool management for network interfaces.
-
-This document provides an overview the Linux DPIO driver, its
-subcomponents, and its APIs.
-
-See Documentation/networking/dpaa2/overview.rst for a general overview of DPAA2
-and the general DPAA2 driver architecture in Linux.
-
-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
-     frames for their respective objects
-  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
-
-The Linux DPIO driver consists of 3 primary components--
-   DPIO object driver-- fsl-mc driver that manages the DPIO object
-
-   DPIO service-- provides APIs to other Linux drivers for services
-
-   QBman portal interface-- sends portal commands, gets responses
-::
-
-          fsl-mc          other
-           bus           drivers
-            |               |
-        +---+----+   +------+-----+
-        |DPIO obj|   |DPIO service|
-        | driver |---|  (DPIO)    |
-        +--------+   +------+-----+
-                            |
-                     +------+-----+
-                     |    QBman   |
-                     | portal i/f |
-                     +------------+
-                            |
-                         hardware
-
-
-The diagram below shows how the DPIO driver components fit with the other
-DPAA2 Linux driver components::
-                                                   +------------+
-                                                   | OS Network |
-                                                   |   Stack    |
-                 +------------+                    +------------+
-                 | Allocator  |. . . . . . .       |  Ethernet  |
-                 |(DPMCP,DPBP)|                    |   (DPNI)   |
-                 +-.----------+                    +---+---+----+
-                  .          .                         ^   |
-                 .            .           <data avail, |   |<enqueue,
-                .              .           tx confirm> |   | dequeue>
-    +-------------+             .                      |   |
-    | DPRC driver |              .    +--------+ +------------+
-    |   (DPRC)    |               . . |DPIO obj| |DPIO service|
-    +----------+--+                   | driver |-|  (DPIO)    |
-               |                      +--------+ +------+-----+
-               |<dev add/remove>                 +------|-----+
-               |                                 |   QBman    |
-          +----+--------------+                  | portal i/f |
-          |   MC-bus driver   |                  +------------+
-          |                   |                     |
-          | /soc/fsl-mc       |                     |
-          +-------------------+                     |
-                                                    |
- =========================================|=========|========================
-                                        +-+--DPIO---|-----------+
-                                        |           |           |
-                                        |        QBman Portal   |
-                                        +-----------------------+
-
- ============================================================================
-
-
-DPIO Object Driver (dpio-driver.c)
-----------------------------------
-
-   The dpio-driver component registers with the fsl-mc bus to handle objects of
-   type "dpio".  The implementation of probe() handles basic initialization
-   of the DPIO including mapping of the DPIO regions (the QBman SW portal)
-   and initializing interrupts and registering irq handlers.  The dpio-driver
-   registers the probed DPIO with dpio-service.
-
-DPIO service  (dpio-service.c, dpaa2-io.h)
-------------------------------------------
-
-   The dpio service component provides queuing, notification, and buffers
-   management services to DPAA2 drivers, such as the Ethernet driver.  A system
-   will typically allocate 1 DPIO object per CPU to allow queuing operations
-   to happen simultaneously across all CPUs.
-
-   Notification handling
-      dpaa2_io_service_register()
-
-      dpaa2_io_service_deregister()
-
-      dpaa2_io_service_rearm()
-
-   Queuing
-      dpaa2_io_service_pull_fq()
-
-      dpaa2_io_service_pull_channel()
-
-      dpaa2_io_service_enqueue_fq()
-
-      dpaa2_io_service_enqueue_qd()
-
-      dpaa2_io_store_create()
-
-      dpaa2_io_store_destroy()
-
-      dpaa2_io_store_next()
-
-   Buffer pool management
-      dpaa2_io_service_release()
-
-      dpaa2_io_service_acquire()
-
-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
-
-   The qbman-portal APIs are not public to other drivers, and are
-   only used by dpio-service.
-
-Other (dpaa2-fd.h, dpaa2-global.h)
-----------------------------------
-
-   Frame descriptor and scatter-gather definitions and the APIs used to
-   manipulate them are defined in dpaa2-fd.h.
-
-   Dequeue result struct and parsing APIs are defined in dpaa2-global.h.
diff --git a/Documentation/networking/dpaa2/ethernet-driver.rst b/Documentation/networking/dpaa2/ethernet-driver.rst
deleted file mode 100644 (file)
index 90ec940..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-.. include:: <isonum.txt>
-
-===============================
-DPAA2 Ethernet driver
-===============================
-
-:Copyright: |copy| 2017-2018 NXP
-
-This file provides documentation for the Freescale DPAA2 Ethernet driver.
-
-Supported Platforms
-===================
-This driver provides networking support for Freescale DPAA2 SoCs, e.g.
-LS2080A, LS2088A, LS1088A.
-
-
-Architecture Overview
-=====================
-Unlike regular NICs, in the DPAA2 architecture there is no single hardware block
-representing network interfaces; instead, several separate hardware resources
-concur to provide the networking functionality:
-
-- network interfaces
-- queues, channels
-- buffer pools
-- MAC/PHY
-
-All hardware resources are allocated and configured through the Management
-Complex (MC) portals. MC abstracts most of these resources as DPAA2 objects
-and exposes ABIs through which they can be configured and controlled. A few
-hardware resources, like queues, do not have a corresponding MC object and
-are treated as internal resources of other objects.
-
-For a more detailed description of the DPAA2 architecture and its object
-abstractions see *Documentation/networking/dpaa2/overview.rst*.
-
-Each Linux net device is built on top of a Datapath Network Interface (DPNI)
-object and uses Buffer Pools (DPBPs), I/O Portals (DPIOs) and Concentrators
-(DPCONs).
-
-Configuration interface::
-
-                 -----------------------
-                | DPAA2 Ethernet Driver |
-                 -----------------------
-                     .      .      .
-                     .      .      .
-             . . . . .      .      . . . . . .
-             .              .                .
-             .              .                .
-         ----------     ----------      -----------
-        | DPBP API |   | DPNI API |    | DPCON API |
-         ----------     ----------      -----------
-             .              .                .             software
-    =======  .  ==========  .  ============  .  ===================
-             .              .                .             hardware
-         ------------------------------------------
-        |            MC hardware portals           |
-         ------------------------------------------
-             .              .                .
-             .              .                .
-          ------         ------            -------
-         | DPBP |       | DPNI |          | DPCON |
-          ------         ------            -------
-
-The DPNIs are network interfaces without a direct one-on-one mapping to PHYs.
-DPBPs represent hardware buffer pools. Packet I/O is performed in the context
-of DPCON objects, using DPIO portals for managing and communicating with the
-hardware resources.
-
-Datapath (I/O) interface::
-
-         -----------------------------------------------
-        |           DPAA2 Ethernet Driver               |
-         -----------------------------------------------
-          |          ^        ^         |            |
-          |          |        |         |            |
-   enqueue|   dequeue|   data |  dequeue|       seed |
-    (Tx)  | (Rx, TxC)|  avail.|  request|     buffers|
-          |          |  notify|         |            |
-          |          |        |         |            |
-          V          |        |         V            V
-         -----------------------------------------------
-        |                 DPIO Driver                   |
-         -----------------------------------------------
-          |          |        |         |            |          software
-          |          |        |         |            |  ================
-          |          |        |         |            |          hardware
-         -----------------------------------------------
-        |               I/O hardware portals            |
-         -----------------------------------------------
-          |          ^        ^         |            |
-          |          |        |         |            |
-          |          |        |         V            |
-          V          |    ================           V
-        ----------------------           |      -------------
- queues  ----------------------          |     | Buffer pool |
-          ----------------------         |      -------------
-                   =======================
-                                Channel
-
-Datapath I/O (DPIO) portals provide enqueue and dequeue services, data
-availability notifications and buffer pool management. DPIOs are shared between
-all DPAA2 objects (and implicitly all DPAA2 kernel drivers) that work with data
-frames, but must be affine to the CPUs for the purpose of traffic distribution.
-
-Frames are transmitted and received through hardware frame queues, which can be
-grouped in channels for the purpose of hardware scheduling. The Ethernet driver
-enqueues TX frames on egress queues and after transmission is complete a TX
-confirmation frame is sent back to the CPU.
-
-When frames are available on ingress queues, a data availability notification
-is sent to the CPU; notifications are raised per channel, so even if multiple
-queues in the same channel have available frames, only one notification is sent.
-After a channel fires a notification, is must be explicitly rearmed.
-
-Each network interface can have multiple Rx, Tx and confirmation queues affined
-to CPUs, and one channel (DPCON) for each CPU that services at least one queue.
-DPCONs are used to distribute ingress traffic to different CPUs via the cores'
-affine DPIOs.
-
-The role of hardware buffer pools is storage of ingress frame data. Each network
-interface has a privately owned buffer pool which it seeds with kernel allocated
-buffers.
-
-
-DPNIs are decoupled from PHYs; a DPNI can be connected to a PHY through a DPMAC
-object or to another DPNI through an internal link, but the connection is
-managed by MC and completely transparent to the Ethernet driver.
-
-::
-
-     ---------     ---------     ---------
-    | eth if1 |   | eth if2 |   | eth ifn |
-     ---------     ---------     ---------
-          .           .          .
-          .           .          .
-          .           .          .
-         ---------------------------
-        |   DPAA2 Ethernet Driver   |
-         ---------------------------
-          .           .          .
-          .           .          .
-          .           .          .
-       ------      ------      ------            -------
-      | DPNI |    | DPNI |    | DPNI |          | DPMAC |----+
-       ------      ------      ------            -------     |
-         |           |           |                  |        |
-         |           |           |                  |      -----
-          ===========             ==================      | PHY |
-                                                           -----
-
-Creating a Network Interface
-============================
-A net device is created for each DPNI object probed on the MC bus. Each DPNI has
-a number of properties which determine the network interface configuration
-options and associated hardware resources.
-
-DPNI objects (and the other DPAA2 objects needed for a network interface) can be
-added to a container on the MC bus in one of two ways: statically, through a
-Datapath Layout Binary file (DPL) that is parsed by MC at boot time; or created
-dynamically at runtime, via the DPAA2 objects APIs.
-
-
-Features & Offloads
-===================
-Hardware checksum offloading is supported for TCP and UDP over IPv4/6 frames.
-The checksum offloads can be independently configured on RX and TX through
-ethtool.
-
-Hardware offload of unicast and multicast MAC filtering is supported on the
-ingress path and permanently enabled.
-
-Scatter-gather frames are supported on both RX and TX paths. On TX, SG support
-is configurable via ethtool; on RX it is always enabled.
-
-The DPAA2 hardware can process jumbo Ethernet frames of up to 10K bytes.
-
-The Ethernet driver defines a static flow hashing scheme that distributes
-traffic based on a 5-tuple key: src IP, dst IP, IP proto, L4 src port,
-L4 dst port. No user configuration is supported for now.
-
-Hardware specific statistics for the network interface as well as some
-non-standard driver stats can be consulted through ethtool -S option.
diff --git a/Documentation/networking/dpaa2/index.rst b/Documentation/networking/dpaa2/index.rst
deleted file mode 100644 (file)
index 67bd87f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-===================
-DPAA2 Documentation
-===================
-
-.. toctree::
-   :maxdepth: 1
-
-   overview
-   dpio-driver
-   ethernet-driver
diff --git a/Documentation/networking/dpaa2/overview.rst b/Documentation/networking/dpaa2/overview.rst
deleted file mode 100644 (file)
index d638b5a..0000000
+++ /dev/null
@@ -1,405 +0,0 @@
-.. include:: <isonum.txt>
-
-=========================================================
-DPAA2 (Data Path Acceleration Architecture Gen2) Overview
-=========================================================
-
-:Copyright: |copy| 2015 Freescale Semiconductor Inc.
-:Copyright: |copy| 2018 NXP
-
-This document provides an overview of the Freescale DPAA2 architecture
-and how it is integrated into the Linux kernel.
-
-Introduction
-============
-
-DPAA2 is a hardware architecture designed for high-speeed network
-packet processing.  DPAA2 consists of sophisticated mechanisms for
-processing Ethernet packets, queue management, buffer management,
-autonomous L2 switching, virtual Ethernet bridging, and accelerator
-(e.g. crypto) sharing.
-
-A DPAA2 hardware component called the Management Complex (or MC) manages the
-DPAA2 hardware resources.  The MC provides an object-based abstraction for
-software drivers to use the DPAA2 hardware.
-The MC uses DPAA2 hardware resources such as queues, buffer pools, and
-network ports to create functional objects/devices such as network
-interfaces, an L2 switch, or accelerator instances.
-The MC provides memory-mapped I/O command interfaces (MC portals)
-which DPAA2 software drivers use to operate on DPAA2 objects.
-
-The diagram below shows an overview of the DPAA2 resource management
-architecture::
-
-       +--------------------------------------+
-       |                  OS                  |
-       |                        DPAA2 drivers |
-       |                             |        |
-       +-----------------------------|--------+
-                                     |
-                                     | (create,discover,connect
-                                     |  config,use,destroy)
-                                     |
-                        DPAA2        |
-       +------------------------| mc portal |-+
-       |                             |        |
-       |   +- - - - - - - - - - - - -V- - -+  |
-       |   |                               |  |
-       |   |   Management Complex (MC)     |  |
-       |   |                               |  |
-       |   +- - - - - - - - - - - - - - - -+  |
-       |                                      |
-       | Hardware                  Hardware   |
-       | Resources                 Objects    |
-       | ---------                 -------    |
-       | -queues                   -DPRC      |
-       | -buffer pools             -DPMCP     |
-       | -Eth MACs/ports           -DPIO      |
-       | -network interface        -DPNI      |
-       |  profiles                 -DPMAC     |
-       | -queue portals            -DPBP      |
-       | -MC portals                ...       |
-       |  ...                                 |
-       |                                      |
-       +--------------------------------------+
-
-
-The MC mediates operations such as create, discover,
-connect, configuration, and destroy.  Fast-path operations
-on data, such as packet transmit/receive, are not mediated by
-the MC and are done directly using memory mapped regions in
-DPIO objects.
-
-Overview of DPAA2 Objects
-=========================
-
-The section provides a brief overview of some key DPAA2 objects.
-A simple scenario is described illustrating the objects involved
-in creating a network interfaces.
-
-DPRC (Datapath Resource Container)
-----------------------------------
-
-A DPRC is a container object that holds all the other
-types of DPAA2 objects.  In the example diagram below there
-are 8 objects of 5 types (DPMCP, DPIO, DPBP, DPNI, and DPMAC)
-in the container.
-
-::
-
-       +---------------------------------------------------------+
-       | DPRC                                                    |
-       |                                                         |
-       |  +-------+  +-------+  +-------+  +-------+  +-------+  |
-       |  | DPMCP |  | DPIO  |  | DPBP  |  | DPNI  |  | DPMAC |  |
-       |  +-------+  +-------+  +-------+  +---+---+  +---+---+  |
-       |  | DPMCP |  | DPIO  |                                   |
-       |  +-------+  +-------+                                   |
-       |  | DPMCP |                                              |
-       |  +-------+                                              |
-       |                                                         |
-       +---------------------------------------------------------+
-
-From the point of view of an OS, a DPRC behaves similar to a plug and
-play bus, like PCI.  DPRC commands can be used to enumerate the contents
-of the DPRC, discover the hardware objects present (including mappable
-regions and interrupts).
-
-::
-
-       DPRC.1 (bus)
-          |
-          +--+--------+-------+-------+-------+
-             |        |       |       |       |
-           DPMCP.1  DPIO.1  DPBP.1  DPNI.1  DPMAC.1
-           DPMCP.2  DPIO.2
-           DPMCP.3
-
-Hardware objects can be created and destroyed dynamically, providing
-the ability to hot plug/unplug objects in and out of the DPRC.
-
-A DPRC has a mappable MMIO region (an MC portal) that can be used
-to send MC commands.  It has an interrupt for status events (like
-hotplug).
-All objects in a container share the same hardware "isolation context".
-This means that with respect to an IOMMU the isolation granularity
-is at the DPRC (container) level, not at the individual object
-level.
-
-DPRCs can be defined statically and populated with objects
-via a config file passed to the MC when firmware starts it.
-
-DPAA2 Objects for an Ethernet Network Interface
------------------------------------------------
-
-A typical Ethernet NIC is monolithic-- the NIC device contains TX/RX
-queuing mechanisms, configuration mechanisms, buffer management,
-physical ports, and interrupts.  DPAA2 uses a more granular approach
-utilizing multiple hardware objects.  Each object provides specialized
-functions. Groups of these objects are used by software to provide
-Ethernet network interface functionality.  This approach provides
-efficient use of finite hardware resources, flexibility, and
-performance advantages.
-
-The diagram below shows the objects needed for a simple
-network interface configuration on a system with 2 CPUs.
-
-::
-
-       +---+---+ +---+---+
-          CPU0     CPU1
-       +---+---+ +---+---+
-           |         |
-       +---+---+ +---+---+
-          DPIO     DPIO
-       +---+---+ +---+---+
-           \     /
-            \   /
-             \ /
-          +---+---+
-             DPNI  --- DPBP,DPMCP
-          +---+---+
-              |
-              |
-          +---+---+
-            DPMAC
-          +---+---+
-              |
-          port/PHY
-
-Below the objects are described.  For each object a brief description
-is provided along with a summary of the kinds of operations the object
-supports and a summary of key resources of the object (MMIO regions
-and IRQs).
-
-DPMAC (Datapath Ethernet MAC)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Represents an Ethernet MAC, a hardware device that connects to an Ethernet
-PHY and allows physical transmission and reception of Ethernet frames.
-
-- MMIO regions: none
-- IRQs: DPNI link change
-- commands: set link up/down, link config, get stats,
-  IRQ config, enable, reset
-
-DPNI (Datapath Network Interface)
-Contains TX/RX queues, network interface configuration, and RX buffer pool
-configuration mechanisms.  The TX/RX queues are in memory and are identified
-by queue number.
-
-- MMIO regions: none
-- IRQs: link state
-- commands: port config, offload config, queue config,
-  parse/classify config, IRQ config, enable, reset
-
-DPIO (Datapath I/O)
-~~~~~~~~~~~~~~~~~~~
-Provides interfaces to enqueue and dequeue
-packets and do hardware buffer pool management operations.  The DPAA2
-architecture separates the mechanism to access queues (the DPIO object)
-from the queues themselves.  The DPIO provides an MMIO interface to
-enqueue/dequeue packets.  To enqueue something a descriptor is written
-to the DPIO MMIO region, which includes the target queue number.
-There will typically be one DPIO assigned to each CPU.  This allows all
-CPUs to simultaneously perform enqueue/dequeued operations.  DPIOs are
-expected to be shared by different DPAA2 drivers.
-
-- MMIO regions: queue operations, buffer management
-- IRQs: data availability, congestion notification, buffer
-  pool depletion
-- commands: IRQ config, enable, reset
-
-DPBP (Datapath Buffer Pool)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Represents a hardware buffer pool.
-
-- MMIO regions: none
-- IRQs: none
-- commands: enable, reset
-
-DPMCP (Datapath MC Portal)
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-Provides an MC command portal.
-Used by drivers to send commands to the MC to manage
-objects.
-
-- MMIO regions: MC command portal
-- IRQs: command completion
-- commands: IRQ config, enable, reset
-
-Object Connections
-==================
-Some objects have explicit relationships that must
-be configured:
-
-- DPNI <--> DPMAC
-- DPNI <--> DPNI
-- DPNI <--> L2-switch-port
-
-    A DPNI must be connected to something such as a DPMAC,
-    another DPNI, or L2 switch port.  The DPNI connection
-    is made via a DPRC command.
-
-::
-
-              +-------+  +-------+
-              | DPNI  |  | DPMAC |
-              +---+---+  +---+---+
-                  |          |
-                  +==========+
-
-- DPNI <--> DPBP
-
-    A network interface requires a 'buffer pool' (DPBP
-    object) which provides a list of pointers to memory
-    where received Ethernet data is to be copied.  The
-    Ethernet driver configures the DPBPs associated with
-    the network interface.
-
-Interrupts
-==========
-All interrupts generated by DPAA2 objects are message
-interrupts.  At the hardware level message interrupts
-generated by devices will normally have 3 components--
-1) a non-spoofable 'device-id' expressed on the hardware
-bus, 2) an address, 3) a data value.
-
-In the case of DPAA2 devices/objects, all objects in the
-same container/DPRC share the same 'device-id'.
-For ARM-based SoC this is the same as the stream ID.
-
-
-DPAA2 Linux Drivers Overview
-============================
-
-This section provides an overview of the Linux kernel drivers for
-DPAA2-- 1) the bus driver and associated "DPAA2 infrastructure"
-drivers and 2) functional object drivers (such as Ethernet).
-
-As described previously, a DPRC is a container that holds the other
-types of DPAA2 objects.  It is functionally similar to a plug-and-play
-bus controller.
-Each object in the DPRC is a Linux "device" and is bound to a driver.
-The diagram below shows the Linux drivers involved in a networking
-scenario and the objects bound to each driver.  A brief description
-of each driver follows.
-
-::
-
-                                            +------------+
-                                            | OS Network |
-                                            |   Stack    |
-                +------------+              +------------+
-                | Allocator  |. . . . . . . |  Ethernet  |
-                |(DPMCP,DPBP)|              |   (DPNI)   |
-                +-.----------+              +---+---+----+
-                 .          .                   ^   |
-                .            .     <data avail, |   | <enqueue,
-               .              .     tx confirm> |   | dequeue>
-       +-------------+         .                |   |
-       | DPRC driver |          .           +---+---V----+     +---------+
-       |   (DPRC)    |           . . . . . .| DPIO driver|     |   MAC   |
-       +----------+--+                      |  (DPIO)    |     | (DPMAC) |
-                  |                         +------+-----+     +-----+---+
-                  |<dev add/remove>                |                 |
-                  |                                |                 |
-         +--------+----------+                     |              +--+---+
-         |   MC-bus driver   |                     |              | PHY  |
-         |                   |                     |              |driver|
-         |   /bus/fsl-mc     |                     |              +--+---+
-         +-------------------+                     |                 |
-                                                   |                 |
-       ========================= HARDWARE =========|=================|======
-                                                 DPIO                |
-                                                   |                 |
-                                                 DPNI---DPBP         |
-                                                   |                 |
-                                                 DPMAC               |
-                                                   |                 |
-                                                  PHY ---------------+
-       ============================================|========================
-
-A brief description of each driver is provided below.
-
-MC-bus driver
--------------
-The MC-bus driver is a platform driver and is probed from a
-node in the device tree (compatible "fsl,qoriq-mc") passed in by boot
-firmware.  It is responsible for bootstrapping the DPAA2 kernel
-infrastructure.
-Key functions include:
-
-- registering a new bus type named "fsl-mc" with the kernel,
-  and implementing bus call-backs (e.g. match/uevent/dev_groups)
-- implementing APIs for DPAA2 driver registration and for device
-  add/remove
-- creates an MSI IRQ domain
-- doing a 'device add' to expose the 'root' DPRC, in turn triggering
-  a bind of the root DPRC to the DPRC driver
-
-The binding for the MC-bus device-tree node can be consulted at
-*Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt*.
-The sysfs bind/unbind interfaces for the MC-bus can be consulted at
-*Documentation/ABI/testing/sysfs-bus-fsl-mc*.
-
-DPRC driver
------------
-The DPRC driver is bound to DPRC objects and does runtime management
-of a bus instance.  It performs the initial bus scan of the DPRC
-and handles interrupts for container events such as hot plug by
-re-scanning the DPRC.
-
-Allocator
----------
-Certain objects such as DPMCP and DPBP are generic and fungible,
-and are intended to be used by other drivers.  For example,
-the DPAA2 Ethernet driver needs:
-
-- DPMCPs to send MC commands, to configure network interfaces
-- DPBPs for network buffer pools
-
-The allocator driver registers for these allocatable object types
-and those objects are bound to the allocator when the bus is probed.
-The allocator maintains a pool of objects that are available for
-allocation by other DPAA2 drivers.
-
-DPIO driver
------------
-The DPIO driver is bound to DPIO objects and provides services that allow
-other drivers such as the Ethernet driver to enqueue and dequeue data for
-their respective objects.
-Key services include:
-
-- data availability notifications
-- hardware queuing operations (enqueue and dequeue of data)
-- hardware buffer pool management
-
-To transmit a packet the Ethernet driver puts data on a queue and
-invokes a DPIO API.  For receive, the Ethernet driver registers
-a data availability notification callback.  To dequeue a packet
-a DPIO API is used.
-There is typically one DPIO object per physical CPU for optimum
-performance, allowing different CPUs to simultaneously enqueue
-and dequeue data.
-
-The DPIO driver operates on behalf of all DPAA2 drivers
-active in the kernel--  Ethernet, crypto, compression,
-etc.
-
-Ethernet driver
----------------
-The Ethernet driver is bound to a DPNI and implements the kernel
-interfaces needed to connect the DPAA2 network interface to
-the network stack.
-Each DPNI corresponds to a Linux network interface.
-
-MAC driver
-----------
-An Ethernet PHY is an off-chip, board specific component and is managed
-by the appropriate PHY driver via an mdio bus.  The MAC driver
-plays a role of being a proxy between the PHY driver and the
-MC.  It does this proxy via the MC commands to a DPMAC object.
-If the PHY driver signals a link change, the MAC driver notifies
-the MC via a DPMAC command.  If a network interface is brought
-up or down, the MC notifies the DPMAC driver via an interrupt and
-the driver can take appropriate action.
diff --git a/Documentation/networking/e100.rst b/Documentation/networking/e100.rst
deleted file mode 100644 (file)
index 5e2839b..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Base Driver for the Intel(R) PRO/100 Family of Adapters
-==============================================================
-
-June 1, 2018
-
-Contents
-========
-
-- In This Release
-- Identifying Your Adapter
-- Building and Installation
-- Driver Configuration Parameters
-- Additional Configurations
-- Known Issues
-- Support
-
-
-In This Release
-===============
-
-This file describes the Linux* Base Driver for the Intel(R) PRO/100 Family of
-Adapters. This driver includes support for Itanium(R)2-based systems.
-
-For questions related to hardware requirements, refer to the documentation
-supplied with your Intel PRO/100 adapter.
-
-The following features are now available in supported kernels:
- - Native VLANs
- - Channel Bonding (teaming)
- - SNMP
-
-Channel Bonding documentation can be found in the Linux kernel source:
-/Documentation/networking/bonding.txt
-
-
-Identifying Your Adapter
-========================
-
-For information on how to identify your adapter, and for the latest Intel
-network drivers, refer to the Intel Support website:
-http://www.intel.com/support
-
-Driver Configuration Parameters
-===============================
-
-The default value for each parameter is generally the recommended setting,
-unless otherwise noted.
-
-Rx Descriptors:
-   Number of receive descriptors. A receive descriptor is a data
-   structure that describes a receive buffer and its attributes to the network
-   controller. The data in the descriptor is used by the controller to write
-   data from the controller to host memory. In the 3.x.x driver the valid range
-   for this parameter is 64-256. The default value is 256. This parameter can be
-   changed using the command::
-
-     ethtool -G eth? rx n
-
-   Where n is the number of desired Rx descriptors.
-
-Tx Descriptors:
-   Number of transmit descriptors. A transmit descriptor is a data
-   structure that describes a transmit buffer and its attributes to the network
-   controller. The data in the descriptor is used by the controller to read
-   data from the host memory to the controller. In the 3.x.x driver the valid
-   range for this parameter is 64-256. The default value is 128. This parameter
-   can be changed using the command::
-
-     ethtool -G eth? tx n
-
-   Where n is the number of desired Tx descriptors.
-
-Speed/Duplex:
-   The driver auto-negotiates the link speed and duplex settings by
-   default. The ethtool utility can be used as follows to force speed/duplex.::
-
-     ethtool -s eth?  autoneg off speed {10|100} duplex {full|half}
-
-   NOTE: setting the speed/duplex to incorrect values will cause the link to
-   fail.
-
-Event Log Message Level:
-   The driver uses the message level flag to log events
-   to syslog. The message level can be set at driver load time. It can also be
-   set using the command::
-
-     ethtool -s eth? msglvl n
-
-
-Additional Configurations
-=========================
-
-Configuring the Driver on Different Distributions
--------------------------------------------------
-
-Configuring a network driver to load properly when the system is started
-is distribution dependent.  Typically, the configuration process involves
-adding an alias line to `/etc/modprobe.d/*.conf` as well as editing other
-system startup scripts and/or configuration files.  Many popular Linux
-distributions ship with tools to make these changes for you.  To learn
-the proper way to configure a network device for your system, refer to
-your distribution documentation.  If during this process you are asked
-for the driver or module name, the name for the Linux Base Driver for
-the Intel PRO/100 Family of Adapters is e100.
-
-As an example, if you install the e100 driver for two PRO/100 adapters
-(eth0 and eth1), add the following to a configuration file in
-/etc/modprobe.d/::
-
-       alias eth0 e100
-       alias eth1 e100
-
-Viewing Link Messages
----------------------
-
-In order to see link messages and other Intel driver information on your
-console, you must set the dmesg level up to six.  This can be done by
-entering the following on the command line before loading the e100
-driver::
-
-       dmesg -n 6
-
-If you wish to see all messages issued by the driver, including debug
-messages, set the dmesg level to eight.
-
-NOTE: This setting is not saved across reboots.
-
-ethtool
--------
-
-The driver utilizes the ethtool interface for driver configuration and
-diagnostics, as well as displaying statistical information.  The ethtool
-version 1.6 or later is required for this functionality.
-
-The latest release of ethtool can be found from
-https://www.kernel.org/pub/software/network/ethtool/
-
-Enabling Wake on LAN* (WoL)
----------------------------
-WoL is provided through the ethtool* utility.  For instructions on
-enabling WoL with ethtool, refer to the ethtool man page.  WoL will be
-enabled on the system during the next shut down or reboot.  For this
-driver version, in order to enable WoL, the e100 driver must be loaded
-when shutting down or rebooting the system.
-
-NAPI
-----
-
-NAPI (Rx polling mode) is supported in the e100 driver.
-
-See https://wiki.linuxfoundation.org/networking/napi for more
-information on NAPI.
-
-Multiple Interfaces on Same Ethernet Broadcast Network
-------------------------------------------------------
-
-Due to the default ARP behavior on Linux, it is not possible to have one
-system on two IP networks in the same Ethernet broadcast domain
-(non-partitioned switch) behave as expected.  All Ethernet interfaces
-will respond to IP traffic for any IP address assigned to the system.
-This results in unbalanced receive traffic.
-
-If you have multiple interfaces in a server, either turn on ARP
-filtering by
-
-(1) entering::
-
-       echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
-
-    (this only works if your kernel's version is higher than 2.4.5), or
-
-(2) installing the interfaces in separate broadcast domains (either
-    in different switches or in a switch partitioned to VLANs).
-
-
-Support
-=======
-For general information, go to the Intel support website at:
-http://www.intel.com/support/
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-http://sourceforge.net/projects/e1000
-If an issue is identified with the released source code on a supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/e1000.rst b/Documentation/networking/e1000.rst
deleted file mode 100644 (file)
index 6379d4d..0000000
+++ /dev/null
@@ -1,462 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Base Driver for Intel(R) Ethernet Network Connection
-===========================================================
-
-Intel Gigabit Linux driver.
-Copyright(c) 1999 - 2013 Intel Corporation.
-
-Contents
-========
-
-- Identifying Your Adapter
-- Command Line Parameters
-- Speed and Duplex Configuration
-- Additional Configurations
-- Support
-
-Identifying Your Adapter
-========================
-
-For more information on how to identify your adapter, go to the Adapter &
-Driver ID Guide at:
-
-    http://support.intel.com/support/go/network/adapter/idguide.htm
-
-For the latest Intel network drivers for Linux, refer to the following
-website.  In the search field, enter your adapter name or type, or use the
-networking link on the left to search for your adapter:
-
-    http://support.intel.com/support/go/network/adapter/home.htm
-
-Command Line Parameters
-=======================
-
-The default value for each parameter is generally the recommended setting,
-unless otherwise noted.
-
-NOTES:
-       For more information about the AutoNeg, Duplex, and Speed
-        parameters, see the "Speed and Duplex Configuration" section in
-        this document.
-
-        For more information about the InterruptThrottleRate,
-        RxIntDelay, TxIntDelay, RxAbsIntDelay, and TxAbsIntDelay
-        parameters, see the application note at:
-        http://www.intel.com/design/network/applnots/ap450.htm
-
-AutoNeg
--------
-
-(Supported only on adapters with copper connections)
-
-:Valid Range:   0x01-0x0F, 0x20-0x2F
-:Default Value: 0x2F
-
-This parameter is a bit-mask that specifies the speed and duplex settings
-advertised by the adapter.  When this parameter is used, the Speed and
-Duplex parameters must not be specified.
-
-NOTE:
-       Refer to the Speed and Duplex section of this readme for more
-       information on the AutoNeg parameter.
-
-Duplex
-------
-
-(Supported only on adapters with copper connections)
-
-:Valid Range:   0-2 (0=auto-negotiate, 1=half, 2=full)
-:Default Value: 0
-
-This defines the direction in which data is allowed to flow.  Can be
-either one or two-directional.  If both Duplex and the link partner are
-set to auto-negotiate, the board auto-detects the correct duplex.  If the
-link partner is forced (either full or half), Duplex defaults to half-
-duplex.
-
-FlowControl
------------
-
-:Valid Range:   0-3 (0=none, 1=Rx only, 2=Tx only, 3=Rx&Tx)
-:Default Value: Reads flow control settings from the EEPROM
-
-This parameter controls the automatic generation(Tx) and response(Rx)
-to Ethernet PAUSE frames.
-
-InterruptThrottleRate
----------------------
-
-(not supported on Intel(R) 82542, 82543 or 82544-based adapters)
-
-:Valid Range:
-   0,1,3,4,100-100000 (0=off, 1=dynamic, 3=dynamic conservative,
-   4=simplified balancing)
-:Default Value: 3
-
-The driver can limit the amount of interrupts per second that the adapter
-will generate for incoming packets. It does this by writing a value to the
-adapter that is based on the maximum amount of interrupts that the adapter
-will generate per second.
-
-Setting InterruptThrottleRate to a value greater or equal to 100
-will program the adapter to send out a maximum of that many interrupts
-per second, even if more packets have come in. This reduces interrupt
-load on the system and can lower CPU utilization under heavy load,
-but will increase latency as packets are not processed as quickly.
-
-The default behaviour of the driver previously assumed a static
-InterruptThrottleRate value of 8000, providing a good fallback value for
-all traffic types,but lacking in small packet performance and latency.
-The hardware can handle many more small packets per second however, and
-for this reason an adaptive interrupt moderation algorithm was implemented.
-
-Since 7.3.x, the driver has two adaptive modes (setting 1 or 3) in which
-it dynamically adjusts the InterruptThrottleRate value based on the traffic
-that it receives. After determining the type of incoming traffic in the last
-timeframe, it will adjust the InterruptThrottleRate to an appropriate value
-for that traffic.
-
-The algorithm classifies the incoming traffic every interval into
-classes.  Once the class is determined, the InterruptThrottleRate value is
-adjusted to suit that traffic type the best. There are three classes defined:
-"Bulk traffic", for large amounts of packets of normal size; "Low latency",
-for small amounts of traffic and/or a significant percentage of small
-packets; and "Lowest latency", for almost completely small packets or
-minimal traffic.
-
-In dynamic conservative mode, the InterruptThrottleRate value is set to 4000
-for traffic that falls in class "Bulk traffic". If traffic falls in the "Low
-latency" or "Lowest latency" class, the InterruptThrottleRate is increased
-stepwise to 20000. This default mode is suitable for most applications.
-
-For situations where low latency is vital such as cluster or
-grid computing, the algorithm can reduce latency even more when
-InterruptThrottleRate is set to mode 1. In this mode, which operates
-the same as mode 3, the InterruptThrottleRate will be increased stepwise to
-70000 for traffic in class "Lowest latency".
-
-In simplified mode the interrupt rate is based on the ratio of TX and
-RX traffic.  If the bytes per second rate is approximately equal, the
-interrupt rate will drop as low as 2000 interrupts per second.  If the
-traffic is mostly transmit or mostly receive, the interrupt rate could
-be as high as 8000.
-
-Setting InterruptThrottleRate to 0 turns off any interrupt moderation
-and may improve small packet latency, but is generally not suitable
-for bulk throughput traffic.
-
-NOTE:
-       InterruptThrottleRate takes precedence over the TxAbsIntDelay and
-       RxAbsIntDelay parameters.  In other words, minimizing the receive
-       and/or transmit absolute delays does not force the controller to
-       generate more interrupts than what the Interrupt Throttle Rate
-       allows.
-
-CAUTION:
-          If you are using the Intel(R) PRO/1000 CT Network Connection
-          (controller 82547), setting InterruptThrottleRate to a value
-          greater than 75,000, may hang (stop transmitting) adapters
-          under certain network conditions.  If this occurs a NETDEV
-          WATCHDOG message is logged in the system event log.  In
-          addition, the controller is automatically reset, restoring
-          the network connection.  To eliminate the potential for the
-          hang, ensure that InterruptThrottleRate is set no greater
-          than 75,000 and is not set to 0.
-
-NOTE:
-       When e1000 is loaded with default settings and multiple adapters
-       are in use simultaneously, the CPU utilization may increase non-
-       linearly.  In order to limit the CPU utilization without impacting
-       the overall throughput, we recommend that you load the driver as
-       follows::
-
-           modprobe e1000 InterruptThrottleRate=3000,3000,3000
-
-       This sets the InterruptThrottleRate to 3000 interrupts/sec for
-       the first, second, and third instances of the driver.  The range
-       of 2000 to 3000 interrupts per second works on a majority of
-       systems and is a good starting point, but the optimal value will
-       be platform-specific.  If CPU utilization is not a concern, use
-       RX_POLLING (NAPI) and default driver settings.
-
-RxDescriptors
--------------
-
-:Valid Range:
- - 48-256 for 82542 and 82543-based adapters
- - 48-4096 for all other supported adapters
-:Default Value: 256
-
-This value specifies the number of receive buffer descriptors allocated
-by the driver.  Increasing this value allows the driver to buffer more
-incoming packets, at the expense of increased system memory utilization.
-
-Each descriptor is 16 bytes.  A receive buffer is also allocated for each
-descriptor and can be either 2048, 4096, 8192, or 16384 bytes, depending
-on the MTU setting. The maximum MTU size is 16110.
-
-NOTE:
-       MTU designates the frame size.  It only needs to be set for Jumbo
-       Frames.  Depending on the available system resources, the request
-       for a higher number of receive descriptors may be denied.  In this
-       case, use a lower number.
-
-RxIntDelay
-----------
-
-:Valid Range:   0-65535 (0=off)
-:Default Value: 0
-
-This value delays the generation of receive interrupts in units of 1.024
-microseconds.  Receive interrupt reduction can improve CPU efficiency if
-properly tuned for specific network traffic.  Increasing this value adds
-extra latency to frame reception and can end up decreasing the throughput
-of TCP traffic.  If the system is reporting dropped receives, this value
-may be set too high, causing the driver to run out of available receive
-descriptors.
-
-CAUTION:
-          When setting RxIntDelay to a value other than 0, adapters may
-          hang (stop transmitting) under certain network conditions.  If
-          this occurs a NETDEV WATCHDOG message is logged in the system
-          event log.  In addition, the controller is automatically reset,
-          restoring the network connection.  To eliminate the potential
-          for the hang ensure that RxIntDelay is set to 0.
-
-RxAbsIntDelay
--------------
-
-(This parameter is supported only on 82540, 82545 and later adapters.)
-
-:Valid Range:   0-65535 (0=off)
-:Default Value: 128
-
-This value, in units of 1.024 microseconds, limits the delay in which a
-receive interrupt is generated.  Useful only if RxIntDelay is non-zero,
-this value ensures that an interrupt is generated after the initial
-packet is received within the set amount of time.  Proper tuning,
-along with RxIntDelay, may improve traffic throughput in specific network
-conditions.
-
-Speed
------
-
-(This parameter is supported only on adapters with copper connections.)
-
-:Valid Settings: 0, 10, 100, 1000
-:Default Value:  0 (auto-negotiate at all supported speeds)
-
-Speed forces the line speed to the specified value in megabits per second
-(Mbps).  If this parameter is not specified or is set to 0 and the link
-partner is set to auto-negotiate, the board will auto-detect the correct
-speed.  Duplex should also be set when Speed is set to either 10 or 100.
-
-TxDescriptors
--------------
-
-:Valid Range:
-  - 48-256 for 82542 and 82543-based adapters
-  - 48-4096 for all other supported adapters
-:Default Value: 256
-
-This value is the number of transmit descriptors allocated by the driver.
-Increasing this value allows the driver to queue more transmits.  Each
-descriptor is 16 bytes.
-
-NOTE:
-       Depending on the available system resources, the request for a
-       higher number of transmit descriptors may be denied.  In this case,
-       use a lower number.
-
-TxIntDelay
-----------
-
-:Valid Range:   0-65535 (0=off)
-:Default Value: 8
-
-This value delays the generation of transmit interrupts in units of
-1.024 microseconds.  Transmit interrupt reduction can improve CPU
-efficiency if properly tuned for specific network traffic.  If the
-system is reporting dropped transmits, this value may be set too high
-causing the driver to run out of available transmit descriptors.
-
-TxAbsIntDelay
--------------
-
-(This parameter is supported only on 82540, 82545 and later adapters.)
-
-:Valid Range:   0-65535 (0=off)
-:Default Value: 32
-
-This value, in units of 1.024 microseconds, limits the delay in which a
-transmit interrupt is generated.  Useful only if TxIntDelay is non-zero,
-this value ensures that an interrupt is generated after the initial
-packet is sent on the wire within the set amount of time.  Proper tuning,
-along with TxIntDelay, may improve traffic throughput in specific
-network conditions.
-
-XsumRX
-------
-
-(This parameter is NOT supported on the 82542-based adapter.)
-
-:Valid Range:   0-1
-:Default Value: 1
-
-A value of '1' indicates that the driver should enable IP checksum
-offload for received packets (both UDP and TCP) to the adapter hardware.
-
-Copybreak
----------
-
-:Valid Range:   0-xxxxxxx (0=off)
-:Default Value: 256
-:Usage: modprobe e1000.ko copybreak=128
-
-Driver copies all packets below or equaling this size to a fresh RX
-buffer before handing it up the stack.
-
-This parameter is different than other parameters, in that it is a
-single (not 1,1,1 etc.) parameter applied to all driver instances and
-it is also available during runtime at
-/sys/module/e1000/parameters/copybreak
-
-SmartPowerDownEnable
---------------------
-
-:Valid Range: 0-1
-:Default Value:  0 (disabled)
-
-Allows PHY to turn off in lower power states. The user can turn off
-this parameter in supported chipsets.
-
-Speed and Duplex Configuration
-==============================
-
-Three keywords are used to control the speed and duplex configuration.
-These keywords are Speed, Duplex, and AutoNeg.
-
-If the board uses a fiber interface, these keywords are ignored, and the
-fiber interface board only links at 1000 Mbps full-duplex.
-
-For copper-based boards, the keywords interact as follows:
-
-- The default operation is auto-negotiate.  The board advertises all
-  supported speed and duplex combinations, and it links at the highest
-  common speed and duplex mode IF the link partner is set to auto-negotiate.
-
-- If Speed = 1000, limited auto-negotiation is enabled and only 1000 Mbps
-  is advertised (The 1000BaseT spec requires auto-negotiation.)
-
-- If Speed = 10 or 100, then both Speed and Duplex should be set.  Auto-
-  negotiation is disabled, and the AutoNeg parameter is ignored.  Partner
-  SHOULD also be forced.
-
-The AutoNeg parameter is used when more control is required over the
-auto-negotiation process.  It should be used when you wish to control which
-speed and duplex combinations are advertised during the auto-negotiation
-process.
-
-The parameter may be specified as either a decimal or hexadecimal value as
-determined by the bitmap below.
-
-============== ====== ====== ======= ======= ====== ====== ======= ======
-Bit position   7      6      5       4       3      2      1       0
-Decimal Value  128    64     32      16      8      4      2       1
-Hex value      80     40     20      10      8      4      2       1
-Speed (Mbps)   N/A    N/A    1000    N/A     100    100    10      10
-Duplex                       Full            Full   Half   Full    Half
-============== ====== ====== ======= ======= ====== ====== ======= ======
-
-Some examples of using AutoNeg::
-
-  modprobe e1000 AutoNeg=0x01 (Restricts autonegotiation to 10 Half)
-  modprobe e1000 AutoNeg=1 (Same as above)
-  modprobe e1000 AutoNeg=0x02 (Restricts autonegotiation to 10 Full)
-  modprobe e1000 AutoNeg=0x03 (Restricts autonegotiation to 10 Half or 10 Full)
-  modprobe e1000 AutoNeg=0x04 (Restricts autonegotiation to 100 Half)
-  modprobe e1000 AutoNeg=0x05 (Restricts autonegotiation to 10 Half or 100
-  Half)
-  modprobe e1000 AutoNeg=0x020 (Restricts autonegotiation to 1000 Full)
-  modprobe e1000 AutoNeg=32 (Same as above)
-
-Note that when this parameter is used, Speed and Duplex must not be specified.
-
-If the link partner is forced to a specific speed and duplex, then this
-parameter should not be used.  Instead, use the Speed and Duplex parameters
-previously mentioned to force the adapter to the same speed and duplex.
-
-Additional Configurations
-=========================
-
-Jumbo Frames
-------------
-
-  Jumbo Frames support is enabled by changing the MTU to a value larger than
-  the default of 1500.  Use the ifconfig command to increase the MTU size.
-  For example::
-
-       ifconfig eth<x> mtu 9000 up
-
-  This setting is not saved across reboots.  It can be made permanent if
-  you add::
-
-       MTU=9000
-
-  to the file /etc/sysconfig/network-scripts/ifcfg-eth<x>.  This example
-  applies to the Red Hat distributions; other distributions may store this
-  setting in a different location.
-
-Notes:
-  Degradation in throughput performance may be observed in some Jumbo frames
-  environments. If this is observed, increasing the application's socket buffer
-  size and/or increasing the /proc/sys/net/ipv4/tcp_*mem entry values may help.
-  See the specific application manual and /usr/src/linux*/Documentation/
-  networking/ip-sysctl.txt for more details.
-
-  - The maximum MTU setting for Jumbo Frames is 16110.  This value coincides
-    with the maximum Jumbo Frames size of 16128.
-
-  - Using Jumbo frames at 10 or 100 Mbps is not supported and may result in
-    poor performance or loss of link.
-
-  - Adapters based on the Intel(R) 82542 and 82573V/E controller do not
-    support Jumbo Frames. These correspond to the following product names::
-
-     Intel(R) PRO/1000 Gigabit Server Adapter
-     Intel(R) PRO/1000 PM Network Connection
-
-ethtool
--------
-
-  The driver utilizes the ethtool interface for driver configuration and
-  diagnostics, as well as displaying statistical information.  The ethtool
-  version 1.6 or later is required for this functionality.
-
-  The latest release of ethtool can be found from
-  https://www.kernel.org/pub/software/network/ethtool/
-
-Enabling Wake on LAN* (WoL)
----------------------------
-
-  WoL is configured through the ethtool* utility.
-
-  WoL will be enabled on the system during the next shut down or reboot.
-  For this driver version, in order to enable WoL, the e1000 driver must be
-  loaded when shutting down or rebooting the system.
-
-Support
-=======
-
-For general information, go to the Intel support website at:
-
-    http://support.intel.com
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-    http://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on the supported
-kernel with a supported adapter, email the specific information related
-to the issue to e1000-devel@lists.sf.net
diff --git a/Documentation/networking/e1000e.rst b/Documentation/networking/e1000e.rst
deleted file mode 100644 (file)
index 33554e5..0000000
+++ /dev/null
@@ -1,382 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Driver for Intel(R) Ethernet Network Connection
-======================================================
-
-Intel Gigabit Linux driver.
-Copyright(c) 2008-2018 Intel Corporation.
-
-Contents
-========
-
-- Identifying Your Adapter
-- Command Line Parameters
-- Additional Configurations
-- Support
-
-
-Identifying Your Adapter
-========================
-For information on how to identify your adapter, and for the latest Intel
-network drivers, refer to the Intel Support website:
-https://www.intel.com/support
-
-
-Command Line Parameters
-=======================
-If the driver is built as a module, the following optional parameters are used
-by entering them on the command line with the modprobe command using this
-syntax::
-
-    modprobe e1000e [<option>=<VAL1>,<VAL2>,...]
-
-There needs to be a <VAL#> for each network port in the system supported by
-this driver. The values will be applied to each instance, in function order.
-For example::
-
-    modprobe e1000e InterruptThrottleRate=16000,16000
-
-In this case, there are two network ports supported by e1000e in the system.
-The default value for each parameter is generally the recommended setting,
-unless otherwise noted.
-
-NOTE: A descriptor describes a data buffer and attributes related to the data
-buffer. This information is accessed by the hardware.
-
-InterruptThrottleRate
----------------------
-:Valid Range: 0,1,3,4,100-100000
-:Default Value: 3
-
-Interrupt Throttle Rate controls the number of interrupts each interrupt
-vector can generate per second. Increasing ITR lowers latency at the cost of
-increased CPU utilization, though it may help throughput in some circumstances.
-
-Setting InterruptThrottleRate to a value greater or equal to 100
-will program the adapter to send out a maximum of that many interrupts
-per second, even if more packets have come in. This reduces interrupt
-load on the system and can lower CPU utilization under heavy load,
-but will increase latency as packets are not processed as quickly.
-
-The default behaviour of the driver previously assumed a static
-InterruptThrottleRate value of 8000, providing a good fallback value for
-all traffic types, but lacking in small packet performance and latency.
-The hardware can handle many more small packets per second however, and
-for this reason an adaptive interrupt moderation algorithm was implemented.
-
-The driver has two adaptive modes (setting 1 or 3) in which
-it dynamically adjusts the InterruptThrottleRate value based on the traffic
-that it receives. After determining the type of incoming traffic in the last
-timeframe, it will adjust the InterruptThrottleRate to an appropriate value
-for that traffic.
-
-The algorithm classifies the incoming traffic every interval into
-classes.  Once the class is determined, the InterruptThrottleRate value is
-adjusted to suit that traffic type the best. There are three classes defined:
-"Bulk traffic", for large amounts of packets of normal size; "Low latency",
-for small amounts of traffic and/or a significant percentage of small
-packets; and "Lowest latency", for almost completely small packets or
-minimal traffic.
-
- - 0: Off
-      Turns off any interrupt moderation and may improve small packet latency.
-      However, this is generally not suitable for bulk throughput traffic due
-      to the increased CPU utilization of the higher interrupt rate.
- - 1: Dynamic mode
-      This mode attempts to moderate interrupts per vector while maintaining
-      very low latency. This can sometimes cause extra CPU utilization. If
-      planning on deploying e1000e in a latency sensitive environment, this
-      parameter should be considered.
- - 3: Dynamic Conservative mode (default)
-      In dynamic conservative mode, the InterruptThrottleRate value is set to
-      4000 for traffic that falls in class "Bulk traffic". If traffic falls in
-      the "Low latency" or "Lowest latency" class, the InterruptThrottleRate is
-      increased stepwise to 20000. This default mode is suitable for most
-      applications.
- - 4: Simplified Balancing mode
-      In simplified mode the interrupt rate is based on the ratio of TX and
-      RX traffic.  If the bytes per second rate is approximately equal, the
-      interrupt rate will drop as low as 2000 interrupts per second.  If the
-      traffic is mostly transmit or mostly receive, the interrupt rate could
-      be as high as 8000.
- - 100-100000:
-      Setting InterruptThrottleRate to a value greater or equal to 100
-      will program the adapter to send at most that many interrupts per second,
-      even if more packets have come in. This reduces interrupt load on the
-      system and can lower CPU utilization under heavy load, but will increase
-      latency as packets are not processed as quickly.
-
-NOTE: InterruptThrottleRate takes precedence over the TxAbsIntDelay and
-RxAbsIntDelay parameters. In other words, minimizing the receive and/or
-transmit absolute delays does not force the controller to generate more
-interrupts than what the Interrupt Throttle Rate allows.
-
-RxIntDelay
-----------
-:Valid Range: 0-65535 (0=off)
-:Default Value: 0
-
-This value delays the generation of receive interrupts in units of 1.024
-microseconds. Receive interrupt reduction can improve CPU efficiency if
-properly tuned for specific network traffic. Increasing this value adds extra
-latency to frame reception and can end up decreasing the throughput of TCP
-traffic. If the system is reporting dropped receives, this value may be set
-too high, causing the driver to run out of available receive descriptors.
-
-CAUTION: When setting RxIntDelay to a value other than 0, adapters may hang
-(stop transmitting) under certain network conditions. If this occurs a NETDEV
-WATCHDOG message is logged in the system event log. In addition, the
-controller is automatically reset, restoring the network connection. To
-eliminate the potential for the hang ensure that RxIntDelay is set to 0.
-
-RxAbsIntDelay
--------------
-:Valid Range: 0-65535 (0=off)
-:Default Value: 8
-
-This value, in units of 1.024 microseconds, limits the delay in which a
-receive interrupt is generated. This value ensures that an interrupt is
-generated after the initial packet is received within the set amount of time,
-which is useful only if RxIntDelay is non-zero. Proper tuning, along with
-RxIntDelay, may improve traffic throughput in specific network conditions.
-
-TxIntDelay
-----------
-:Valid Range: 0-65535 (0=off)
-:Default Value: 8
-
-This value delays the generation of transmit interrupts in units of 1.024
-microseconds. Transmit interrupt reduction can improve CPU efficiency if
-properly tuned for specific network traffic. If the system is reporting
-dropped transmits, this value may be set too high causing the driver to run
-out of available transmit descriptors.
-
-TxAbsIntDelay
--------------
-:Valid Range: 0-65535 (0=off)
-:Default Value: 32
-
-This value, in units of 1.024 microseconds, limits the delay in which a
-transmit interrupt is generated. It is useful only if TxIntDelay is non-zero.
-It ensures that an interrupt is generated after the initial Packet is sent on
-the wire within the set amount of time. Proper tuning, along with TxIntDelay,
-may improve traffic throughput in specific network conditions.
-
-copybreak
----------
-:Valid Range: 0-xxxxxxx (0=off)
-:Default Value: 256
-
-The driver copies all packets below or equaling this size to a fresh receive
-buffer before handing it up the stack.
-This parameter differs from other parameters because it is a single (not 1,1,1
-etc.) parameter applied to all driver instances and it is also available
-during runtime at /sys/module/e1000e/parameters/copybreak.
-
-To use copybreak, type::
-
-    modprobe e1000e.ko copybreak=128
-
-SmartPowerDownEnable
---------------------
-:Valid Range: 0,1
-:Default Value: 0 (disabled)
-
-Allows the PHY to turn off in lower power states. The user can turn off this
-parameter in supported chipsets.
-
-KumeranLockLoss
----------------
-:Valid Range: 0,1
-:Default Value: 1 (enabled)
-
-This workaround skips resetting the PHY at shutdown for the initial silicon
-releases of ICH8 systems.
-
-IntMode
--------
-:Valid Range: 0-2
-:Default Value: 0
-
-   +-------+----------------+
-   | Value | Interrupt Mode |
-   +=======+================+
-   |   0   |     Legacy     |
-   +-------+----------------+
-   |   1   |       MSI      |
-   +-------+----------------+
-   |   2   |      MSI-X     |
-   +-------+----------------+
-
-IntMode allows load time control over the type of interrupt registered for by
-the driver. MSI-X is required for multiple queue support, and some kernels and
-combinations of kernel .config options will force a lower level of interrupt
-support.
-
-This command will show different values for each type of interrupt::
-
-  cat /proc/interrupts
-
-CrcStripping
-------------
-:Valid Range: 0,1
-:Default Value: 1 (enabled)
-
-Strip the CRC from received packets before sending up the network stack. If
-you have a machine with a BMC enabled but cannot receive IPMI traffic after
-loading or enabling the driver, try disabling this feature.
-
-WriteProtectNVM
----------------
-:Valid Range: 0,1
-:Default Value: 1 (enabled)
-
-If set to 1, configure the hardware to ignore all write/erase cycles to the
-GbE region in the ICHx NVM (in order to prevent accidental corruption of the
-NVM). This feature can be disabled by setting the parameter to 0 during initial
-driver load.
-
-NOTE: The machine must be power cycled (full off/on) when enabling NVM writes
-via setting the parameter to zero. Once the NVM has been locked (via the
-parameter at 1 when the driver loads) it cannot be unlocked except via power
-cycle.
-
-Debug
------
-:Valid Range: 0-16 (0=none,...,16=all)
-:Default Value: 0
-
-This parameter adjusts the level of debug messages displayed in the system logs.
-
-
-Additional Features and Configurations
-======================================
-
-Jumbo Frames
-------------
-Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
-to a value larger than the default value of 1500.
-
-Use the ifconfig command to increase the MTU size. For example, enter the
-following where <x> is the interface number::
-
-    ifconfig eth<x> mtu 9000 up
-
-Alternatively, you can use the ip command as follows::
-
-    ip link set mtu 9000 dev eth<x>
-    ip link set up dev eth<x>
-
-This setting is not saved across reboots. The setting change can be made
-permanent by adding 'MTU=9000' to the file:
-
-- For RHEL: /etc/sysconfig/network-scripts/ifcfg-eth<x>
-- For SLES: /etc/sysconfig/network/<config_file>
-
-NOTE: The maximum MTU setting for Jumbo Frames is 8996. This value coincides
-with the maximum Jumbo Frames size of 9018 bytes.
-
-NOTE: Using Jumbo frames at 10 or 100 Mbps is not supported and may result in
-poor performance or loss of link.
-
-NOTE: The following adapters limit Jumbo Frames sized packets to a maximum of
-4088 bytes:
-
-  - Intel(R) 82578DM Gigabit Network Connection
-  - Intel(R) 82577LM Gigabit Network Connection
-
-The following adapters do not support Jumbo Frames:
-
-  - Intel(R) PRO/1000 Gigabit Server Adapter
-  - Intel(R) PRO/1000 PM Network Connection
-  - Intel(R) 82562G 10/100 Network Connection
-  - Intel(R) 82562G-2 10/100 Network Connection
-  - Intel(R) 82562GT 10/100 Network Connection
-  - Intel(R) 82562GT-2 10/100 Network Connection
-  - Intel(R) 82562V 10/100 Network Connection
-  - Intel(R) 82562V-2 10/100 Network Connection
-  - Intel(R) 82566DC Gigabit Network Connection
-  - Intel(R) 82566DC-2 Gigabit Network Connection
-  - Intel(R) 82566DM Gigabit Network Connection
-  - Intel(R) 82566MC Gigabit Network Connection
-  - Intel(R) 82566MM Gigabit Network Connection
-  - Intel(R) 82567V-3 Gigabit Network Connection
-  - Intel(R) 82577LC Gigabit Network Connection
-  - Intel(R) 82578DC Gigabit Network Connection
-
-NOTE: Jumbo Frames cannot be configured on an 82579-based Network device if
-MACSec is enabled on the system.
-
-
-ethtool
--------
-The driver utilizes the ethtool interface for driver configuration and
-diagnostics, as well as displaying statistical information. The latest ethtool
-version is required for this functionality. Download it at:
-
-https://www.kernel.org/pub/software/network/ethtool/
-
-NOTE: When validating enable/disable tests on some parts (for example, 82578),
-it is necessary to add a few seconds between tests when working with ethtool.
-
-
-Speed and Duplex Configuration
-------------------------------
-In addressing speed and duplex configuration issues, you need to distinguish
-between copper-based adapters and fiber-based adapters.
-
-In the default mode, an Intel(R) Ethernet Network Adapter using copper
-connections will attempt to auto-negotiate with its link partner to determine
-the best setting. If the adapter cannot establish link with the link partner
-using auto-negotiation, you may need to manually configure the adapter and link
-partner to identical settings to establish link and pass packets. This should
-only be needed when attempting to link with an older switch that does not
-support auto-negotiation or one that has been forced to a specific speed or
-duplex mode. Your link partner must match the setting you choose. 1 Gbps speeds
-and higher cannot be forced. Use the autonegotiation advertising setting to
-manually set devices for 1 Gbps and higher.
-
-Speed, duplex, and autonegotiation advertising are configured through the
-ethtool* utility.
-
-Caution: Only experienced network administrators should force speed and duplex
-or change autonegotiation advertising manually. The settings at the switch must
-always match the adapter settings. Adapter performance may suffer or your
-adapter may not operate if you configure the adapter differently from your
-switch.
-
-An Intel(R) Ethernet Network Adapter using fiber-based connections, however,
-will not attempt to auto-negotiate with its link partner since those adapters
-operate only in full duplex and only at their native speed.
-
-
-Enabling Wake on LAN* (WoL)
----------------------------
-WoL is configured through the ethtool* utility.
-
-WoL will be enabled on the system during the next shut down or reboot. For
-this driver version, in order to enable WoL, the e1000e driver must be loaded
-prior to shutting down or suspending the system.
-
-NOTE: Wake on LAN is only supported on port A for the following devices:
-- Intel(R) PRO/1000 PT Dual Port Network Connection
-- Intel(R) PRO/1000 PT Dual Port Server Connection
-- Intel(R) PRO/1000 PT Dual Port Server Adapter
-- Intel(R) PRO/1000 PF Dual Port Server Adapter
-- Intel(R) PRO/1000 PT Quad Port Server Adapter
-- Intel(R) Gigabit PT Quad Port Server ExpressModule
-
-
-Support
-=======
-For general information, go to the Intel support website at:
-
-https://www.intel.com/support/
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-https://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on a supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/ena.txt b/Documentation/networking/ena.txt
deleted file mode 100644 (file)
index 2b4b6f5..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-Linux kernel driver for Elastic Network Adapter (ENA) family:
-=============================================================
-
-Overview:
-=========
-ENA is a networking interface designed to make good use of modern CPU
-features and system architectures.
-
-The ENA device exposes a lightweight management interface with a
-minimal set of memory mapped registers and extendable command set
-through an Admin Queue.
-
-The driver supports a range of ENA devices, is link-speed independent
-(i.e., the same driver is used for 10GbE, 25GbE, 40GbE, etc.), and has
-a negotiated and extendable feature set.
-
-Some ENA devices support SR-IOV. This driver is used for both the
-SR-IOV Physical Function (PF) and Virtual Function (VF) devices.
-
-ENA devices enable high speed and low overhead network traffic
-processing by providing multiple Tx/Rx queue pairs (the maximum number
-is advertised by the device via the Admin Queue), a dedicated MSI-X
-interrupt vector per Tx/Rx queue pair, adaptive interrupt moderation,
-and CPU cacheline optimized data placement.
-
-The ENA driver supports industry standard TCP/IP offload features such
-as checksum offload and TCP transmit segmentation offload (TSO).
-Receive-side scaling (RSS) is supported for multi-core scaling.
-
-The ENA driver and its corresponding devices implement health
-monitoring mechanisms such as watchdog, enabling the device and driver
-to recover in a manner transparent to the application, as well as
-debug logs.
-
-Some of the ENA devices support a working mode called Low-latency
-Queue (LLQ), which saves several more microseconds.
-
-Supported PCI vendor ID/device IDs:
-===================================
-1d0f:0ec2 - ENA PF
-1d0f:1ec2 - ENA PF with LLQ support
-1d0f:ec20 - ENA VF
-1d0f:ec21 - ENA VF with LLQ support
-
-ENA Source Code Directory Structure:
-====================================
-ena_com.[ch]      - Management communication layer. This layer is
-                    responsible for the handling all the management
-                    (admin) communication between the device and the
-                    driver.
-ena_eth_com.[ch]  - Tx/Rx data path.
-ena_admin_defs.h  - Definition of ENA management interface.
-ena_eth_io_defs.h - Definition of ENA data path interface.
-ena_common_defs.h - Common definitions for ena_com layer.
-ena_regs_defs.h   - Definition of ENA PCI memory-mapped (MMIO) registers.
-ena_netdev.[ch]   - Main Linux kernel driver.
-ena_syfsfs.[ch]   - Sysfs files.
-ena_ethtool.c     - ethtool callbacks.
-ena_pci_id_tbl.h  - Supported device IDs.
-
-Management Interface:
-=====================
-ENA management interface is exposed by means of:
-- PCIe Configuration Space
-- Device Registers
-- Admin Queue (AQ) and Admin Completion Queue (ACQ)
-- Asynchronous Event Notification Queue (AENQ)
-
-ENA device MMIO Registers are accessed only during driver
-initialization and are not involved in further normal device
-operation.
-
-AQ is used for submitting management commands, and the
-results/responses are reported asynchronously through ACQ.
-
-ENA introduces a very small set of management commands with room for
-vendor-specific extensions. Most of the management operations are
-framed in a generic Get/Set feature command.
-
-The following admin queue commands are supported:
-- Create I/O submission queue
-- Create I/O completion queue
-- Destroy I/O submission queue
-- Destroy I/O completion queue
-- Get feature
-- Set feature
-- Configure AENQ
-- Get statistics
-
-Refer to ena_admin_defs.h for the list of supported Get/Set Feature
-properties.
-
-The Asynchronous Event Notification Queue (AENQ) is a uni-directional
-queue used by the ENA device to send to the driver events that cannot
-be reported using ACQ. AENQ events are subdivided into groups. Each
-group may have multiple syndromes, as shown below
-
-The events are:
-       Group                   Syndrome
-       Link state change       - X -
-       Fatal error             - X -
-       Notification            Suspend traffic
-       Notification            Resume traffic
-       Keep-Alive              - X -
-
-ACQ and AENQ share the same MSI-X vector.
-
-Keep-Alive is a special mechanism that allows monitoring of the
-device's health. The driver maintains a watchdog (WD) handler which,
-if fired, logs the current state and statistics then resets and
-restarts the ENA device and driver. A Keep-Alive event is delivered by
-the device every second. The driver re-arms the WD upon reception of a
-Keep-Alive event. A missed Keep-Alive event causes the WD handler to
-fire.
-
-Data Path Interface:
-====================
-I/O operations are based on Tx and Rx Submission Queues (Tx SQ and Rx
-SQ correspondingly). Each SQ has a completion queue (CQ) associated
-with it.
-
-The SQs and CQs are implemented as descriptor rings in contiguous
-physical memory.
-
-The ENA driver supports two Queue Operation modes for Tx SQs:
-- Regular mode
-  * In this mode the Tx SQs reside in the host's memory. The ENA
-    device fetches the ENA Tx descriptors and packet data from host
-    memory.
-- Low Latency Queue (LLQ) mode or "push-mode".
-  * In this mode the driver pushes the transmit descriptors and the
-    first 128 bytes of the packet directly to the ENA device memory
-    space. The rest of the packet payload is fetched by the
-    device. For this operation mode, the driver uses a dedicated PCI
-    device memory BAR, which is mapped with write-combine capability.
-
-The Rx SQs support only the regular mode.
-
-Note: Not all ENA devices support LLQ, and this feature is negotiated
-      with the device upon initialization. If the ENA device does not
-      support LLQ mode, the driver falls back to the regular mode.
-
-The driver supports multi-queue for both Tx and Rx. This has various
-benefits:
-- Reduced CPU/thread/process contention on a given Ethernet interface.
-- Cache miss rate on completion is reduced, particularly for data
-  cache lines that hold the sk_buff structures.
-- Increased process-level parallelism when handling received packets.
-- Increased data cache hit rate, by steering kernel processing of
-  packets to the CPU, where the application thread consuming the
-  packet is running.
-- In hardware interrupt re-direction.
-
-Interrupt Modes:
-================
-The driver assigns a single MSI-X vector per queue pair (for both Tx
-and Rx directions). The driver assigns an additional dedicated MSI-X vector
-for management (for ACQ and AENQ).
-
-Management interrupt registration is performed when the Linux kernel
-probes the adapter, and it is de-registered when the adapter is
-removed. I/O queue interrupt registration is performed when the Linux
-interface of the adapter is opened, and it is de-registered when the
-interface is closed.
-
-The management interrupt is named:
-   ena-mgmnt@pci:<PCI domain:bus:slot.function>
-and for each queue pair, an interrupt is named:
-   <interface name>-Tx-Rx-<queue index>
-
-The ENA device operates in auto-mask and auto-clear interrupt
-modes. That is, once MSI-X is delivered to the host, its Cause bit is
-automatically cleared and the interrupt is masked. The interrupt is
-unmasked by the driver after NAPI processing is complete.
-
-Interrupt Moderation:
-=====================
-ENA driver and device can operate in conventional or adaptive interrupt
-moderation mode.
-
-In conventional mode the driver instructs device to postpone interrupt
-posting according to static interrupt delay value. The interrupt delay
-value can be configured through ethtool(8). The following ethtool
-parameters are supported by the driver: tx-usecs, rx-usecs
-
-In adaptive interrupt moderation mode the interrupt delay value is
-updated by the driver dynamically and adjusted every NAPI cycle
-according to the traffic nature.
-
-By default ENA driver applies adaptive coalescing on Rx traffic and
-conventional coalescing on Tx traffic.
-
-Adaptive coalescing can be switched on/off through ethtool(8)
-adaptive_rx on|off parameter.
-
-The driver chooses interrupt delay value according to the number of
-bytes and packets received between interrupt unmasking and interrupt
-posting. The driver uses interrupt delay table that subdivides the
-range of received bytes/packets into 5 levels and assigns interrupt
-delay value to each level.
-
-The user can enable/disable adaptive moderation, modify the interrupt
-delay table and restore its default values through sysfs.
-
-The rx_copybreak is initialized by default to ENA_DEFAULT_RX_COPYBREAK
-and can be configured by the ETHTOOL_STUNABLE command of the
-SIOCETHTOOL ioctl.
-
-SKB:
-The driver-allocated SKB for frames received from Rx handling using
-NAPI context. The allocation method depends on the size of the packet.
-If the frame length is larger than rx_copybreak, napi_get_frags()
-is used, otherwise netdev_alloc_skb_ip_align() is used, the buffer
-content is copied (by CPU) to the SKB, and the buffer is recycled.
-
-Statistics:
-===========
-The user can obtain ENA device and driver statistics using ethtool.
-The driver can collect regular or extended statistics (including
-per-queue stats) from the device.
-
-In addition the driver logs the stats to syslog upon device reset.
-
-MTU:
-====
-The driver supports an arbitrarily large MTU with a maximum that is
-negotiated with the device. The driver configures MTU using the
-SetFeature command (ENA_ADMIN_MTU property). The user can change MTU
-via ip(8) and similar legacy tools.
-
-Stateless Offloads:
-===================
-The ENA driver supports:
-- TSO over IPv4/IPv6
-- TSO with ECN
-- IPv4 header checksum offload
-- TCP/UDP over IPv4/IPv6 checksum offloads
-
-RSS:
-====
-- The ENA device supports RSS that allows flexible Rx traffic
-  steering.
-- Toeplitz and CRC32 hash functions are supported.
-- Different combinations of L2/L3/L4 fields can be configured as
-  inputs for hash functions.
-- The driver configures RSS settings using the AQ SetFeature command
-  (ENA_ADMIN_RSS_HASH_FUNCTION, ENA_ADMIN_RSS_HASH_INPUT and
-  ENA_ADMIN_RSS_REDIRECTION_TABLE_CONFIG properties).
-- If the NETIF_F_RXHASH flag is set, the 32-bit result of the hash
-  function delivered in the Rx CQ descriptor is set in the received
-  SKB.
-- The user can provide a hash key, hash function, and configure the
-  indirection table through ethtool(8).
-
-DATA PATH:
-==========
-Tx:
----
-end_start_xmit() is called by the stack. This function does the following:
-- Maps data buffers (skb->data and frags).
-- Populates ena_buf for the push buffer (if the driver and device are
-  in push mode.)
-- Prepares ENA bufs for the remaining frags.
-- Allocates a new request ID from the empty req_id ring. The request
-  ID is the index of the packet in the Tx info. This is used for
-  out-of-order TX completions.
-- Adds the packet to the proper place in the Tx ring.
-- Calls ena_com_prepare_tx(), an ENA communication layer that converts
-  the ena_bufs to ENA descriptors (and adds meta ENA descriptors as
-  needed.)
-  * This function also copies the ENA descriptors and the push buffer
-    to the Device memory space (if in push mode.)
-- Writes doorbell to the ENA device.
-- When the ENA device finishes sending the packet, a completion
-  interrupt is raised.
-- The interrupt handler schedules NAPI.
-- The ena_clean_tx_irq() function is called. This function handles the
-  completion descriptors generated by the ENA, with a single
-  completion descriptor per completed packet.
-  * req_id is retrieved from the completion descriptor. The tx_info of
-    the packet is retrieved via the req_id. The data buffers are
-    unmapped and req_id is returned to the empty req_id ring.
-  * The function stops when the completion descriptors are completed or
-    the budget is reached.
-
-Rx:
----
-- When a packet is received from the ENA device.
-- The interrupt handler schedules NAPI.
-- The ena_clean_rx_irq() function is called. This function calls
-  ena_rx_pkt(), an ENA communication layer function, which returns the
-  number of descriptors used for a new unhandled packet, and zero if
-  no new packet is found.
-- Then it calls the ena_clean_rx_irq() function.
-- ena_eth_rx_skb() checks packet length:
-  * If the packet is small (len < rx_copybreak), the driver allocates
-    a SKB for the new packet, and copies the packet payload into the
-    SKB data buffer.
-    - In this way the original data buffer is not passed to the stack
-      and is reused for future Rx packets.
-  * Otherwise the function unmaps the Rx buffer, then allocates the
-    new SKB structure and hooks the Rx buffer to the SKB frags.
-- The new SKB is updated with the necessary information (protocol,
-  checksum hw verify result, etc.), and then passed to the network
-  stack, using the NAPI interface function napi_gro_receive().
diff --git a/Documentation/networking/fm10k.rst b/Documentation/networking/fm10k.rst
deleted file mode 100644 (file)
index bf5e594..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Base Driver for Intel(R) Ethernet Multi-host Controller
-==============================================================
-
-August 20, 2018
-Copyright(c) 2015-2018 Intel Corporation.
-
-Contents
-========
-- Identifying Your Adapter
-- Additional Configurations
-- Performance Tuning
-- Known Issues
-- Support
-
-Identifying Your Adapter
-========================
-The driver in this release is compatible with devices based on the Intel(R)
-Ethernet Multi-host Controller.
-
-For information on how to identify your adapter, and for the latest Intel
-network drivers, refer to the Intel Support website:
-http://www.intel.com/support
-
-
-Flow Control
-------------
-The Intel(R) Ethernet Switch Host Interface Driver does not support Flow
-Control. It will not send pause frames. This may result in dropped frames.
-
-
-Virtual Functions (VFs)
------------------------
-Use sysfs to enable VFs.
-Valid Range: 0-64
-
-For example::
-
-    echo $num_vf_enabled > /sys/class/net/$dev/device/sriov_numvfs //enable VFs
-    echo 0 > /sys/class/net/$dev/device/sriov_numvfs //disable VFs
-
-NOTE: Neither the device nor the driver control how VFs are mapped into config
-space. Bus layout will vary by operating system. On operating systems that
-support it, you can check sysfs to find the mapping.
-
-NOTE: When SR-IOV mode is enabled, hardware VLAN filtering and VLAN tag
-stripping/insertion will remain enabled. Please remove the old VLAN filter
-before the new VLAN filter is added. For example::
-
-    ip link set eth0 vf 0 vlan 100     // set vlan 100 for VF 0
-    ip link set eth0 vf 0 vlan 0       // Delete vlan 100
-    ip link set eth0 vf 0 vlan 200     // set a new vlan 200 for VF 0
-
-
-Additional Features and Configurations
-======================================
-
-Jumbo Frames
-------------
-Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
-to a value larger than the default value of 1500.
-
-Use the ifconfig command to increase the MTU size. For example, enter the
-following where <x> is the interface number::
-
-    ifconfig eth<x> mtu 9000 up
-
-Alternatively, you can use the ip command as follows::
-
-    ip link set mtu 9000 dev eth<x>
-    ip link set up dev eth<x>
-
-This setting is not saved across reboots. The setting change can be made
-permanent by adding 'MTU=9000' to the file:
-
-- For RHEL: /etc/sysconfig/network-scripts/ifcfg-eth<x>
-- For SLES: /etc/sysconfig/network/<config_file>
-
-NOTE: The maximum MTU setting for Jumbo Frames is 15342. This value coincides
-with the maximum Jumbo Frames size of 15364 bytes.
-
-NOTE: This driver will attempt to use multiple page sized buffers to receive
-each jumbo packet. This should help to avoid buffer starvation issues when
-allocating receive packets.
-
-
-Generic Receive Offload, aka GRO
---------------------------------
-The driver supports the in-kernel software implementation of GRO. GRO has
-shown that by coalescing Rx traffic into larger chunks of data, CPU
-utilization can be significantly reduced when under large Rx load. GRO is an
-evolution of the previously-used LRO interface. GRO is able to coalesce
-other protocols besides TCP. It's also safe to use with configurations that
-are problematic for LRO, namely bridging and iSCSI.
-
-
-
-Supported ethtool Commands and Options for Filtering
-----------------------------------------------------
--n --show-nfc
-  Retrieves the receive network flow classification configurations.
-
-rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6
-  Retrieves the hash options for the specified network traffic type.
-
--N --config-nfc
-  Configures the receive network flow classification.
-
-rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r
-  Configures the hash options for the specified network traffic type.
-
-- udp4: UDP over IPv4
-- udp6: UDP over IPv6
-- f Hash on bytes 0 and 1 of the Layer 4 header of the rx packet.
-- n Hash on bytes 2 and 3 of the Layer 4 header of the rx packet.
-
-
-Known Issues/Troubleshooting
-============================
-
-Enabling SR-IOV in a 64-bit Microsoft* Windows Server* 2012/R2 guest OS under Linux KVM
----------------------------------------------------------------------------------------
-KVM Hypervisor/VMM supports direct assignment of a PCIe device to a VM. This
-includes traditional PCIe devices, as well as SR-IOV-capable devices based on
-the Intel Ethernet Controller XL710.
-
-
-Support
-=======
-For general information, go to the Intel support website at:
-
-https://www.intel.com/support/
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-https://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on a supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/gianfar.txt b/Documentation/networking/gianfar.txt
deleted file mode 100644 (file)
index ba1daea..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-The Gianfar Ethernet Driver
-
-Author: Andy Fleming <afleming@freescale.com>
-Updated: 2005-07-28
-
-
-CHECKSUM OFFLOADING
-
-The eTSEC controller (first included in parts from late 2005 like
-the 8548) has the ability to perform TCP, UDP, and IP checksums
-in hardware.  The Linux kernel only offloads the TCP and UDP
-checksums (and always performs the pseudo header checksums), so
-the driver only supports checksumming for TCP/IP and UDP/IP
-packets.  Use ethtool to enable or disable this feature for RX
-and TX.
-
-VLAN
-
-In order to use VLAN, please consult Linux documentation on
-configuring VLANs.  The gianfar driver supports hardware insertion and
-extraction of VLAN headers, but not filtering.  Filtering will be
-done by the kernel.
-
-MULTICASTING
-
-The gianfar driver supports using the group hash table on the
-TSEC (and the extended hash table on the eTSEC) for multicast
-filtering.  On the eTSEC, the exact-match MAC registers are used
-before the hash tables.  See Linux documentation on how to join
-multicast groups.
-
-PADDING
-
-The gianfar driver supports padding received frames with 2 bytes
-to align the IP header to a 16-byte boundary, when supported by
-hardware.
-
-ETHTOOL
-
-The gianfar driver supports the use of ethtool for many
-configuration options.  You must run ethtool only on currently
-open interfaces.  See ethtool documentation for details.
diff --git a/Documentation/networking/i40e.rst b/Documentation/networking/i40e.rst
deleted file mode 100644 (file)
index 0cc16c5..0000000
+++ /dev/null
@@ -1,770 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Base Driver for the Intel(R) Ethernet Controller 700 Series
-==================================================================
-
-Intel 40 Gigabit Linux driver.
-Copyright(c) 1999-2018 Intel Corporation.
-
-Contents
-========
-
-- Overview
-- Identifying Your Adapter
-- Intel(R) Ethernet Flow Director
-- Additional Configurations
-- Known Issues
-- Support
-
-
-Driver information can be obtained using ethtool, lspci, and ifconfig.
-Instructions on updating ethtool can be found in the section Additional
-Configurations later in this document.
-
-For questions related to hardware requirements, refer to the documentation
-supplied with your Intel adapter. All hardware requirements listed apply to use
-with Linux.
-
-
-Identifying Your Adapter
-========================
-The driver is compatible with devices based on the following:
-
- * Intel(R) Ethernet Controller X710
- * Intel(R) Ethernet Controller XL710
- * Intel(R) Ethernet Network Connection X722
- * Intel(R) Ethernet Controller XXV710
-
-For the best performance, make sure the latest NVM/FW is installed on your
-device.
-
-For information on how to identify your adapter, and for the latest NVM/FW
-images and Intel network drivers, refer to the Intel Support website:
-https://www.intel.com/support
-
-SFP+ and QSFP+ Devices
-----------------------
-For information about supported media, refer to this document:
-https://www.intel.com/content/dam/www/public/us/en/documents/release-notes/xl710-ethernet-controller-feature-matrix.pdf
-
-NOTE: Some adapters based on the Intel(R) Ethernet Controller 700 Series only
-support Intel Ethernet Optics modules. On these adapters, other modules are not
-supported and will not function.  In all cases Intel recommends using Intel
-Ethernet Optics; other modules may function but are not validated by Intel.
-Contact Intel for supported media types.
-
-NOTE: For connections based on Intel(R) Ethernet Controller 700 Series, support
-is dependent on your system board. Please see your vendor for details.
-
-NOTE: In systems that do not have adequate airflow to cool the adapter and
-optical modules, you must use high temperature optical modules.
-
-Virtual Functions (VFs)
------------------------
-Use sysfs to enable VFs. For example::
-
-  #echo $num_vf_enabled > /sys/class/net/$dev/device/sriov_numvfs #enable VFs
-  #echo 0 > /sys/class/net/$dev/device/sriov_numvfs #disable VFs
-
-For example, the following instructions will configure PF eth0 and the first VF
-on VLAN 10::
-
-  $ ip link set dev eth0 vf 0 vlan 10
-
-VLAN Tag Packet Steering
-------------------------
-Allows you to send all packets with a specific VLAN tag to a particular SR-IOV
-virtual function (VF). Further, this feature allows you to designate a
-particular VF as trusted, and allows that trusted VF to request selective
-promiscuous mode on the Physical Function (PF).
-
-To set a VF as trusted or untrusted, enter the following command in the
-Hypervisor::
-
-  # ip link set dev eth0 vf 1 trust [on|off]
-
-Once the VF is designated as trusted, use the following commands in the VM to
-set the VF to promiscuous mode.
-
-::
-
-  For promiscuous all:
-  #ip link set eth2 promisc on
-  Where eth2 is a VF interface in the VM
-
-  For promiscuous Multicast:
-  #ip link set eth2 allmulticast on
-  Where eth2 is a VF interface in the VM
-
-NOTE: By default, the ethtool priv-flag vf-true-promisc-support is set to
-"off",meaning that promiscuous mode for the VF will be limited. To set the
-promiscuous mode for the VF to true promiscuous and allow the VF to see all
-ingress traffic, use the following command::
-
-  #ethtool -set-priv-flags p261p1 vf-true-promisc-support on
-
-The vf-true-promisc-support priv-flag does not enable promiscuous mode; rather,
-it designates which type of promiscuous mode (limited or true) you will get
-when you enable promiscuous mode using the ip link commands above. Note that
-this is a global setting that affects the entire device. However,the
-vf-true-promisc-support priv-flag is only exposed to the first PF of the
-device. The PF remains in limited promiscuous mode (unless it is in MFP mode)
-regardless of the vf-true-promisc-support setting.
-
-Now add a VLAN interface on the VF interface::
-
-  #ip link add link eth2 name eth2.100 type vlan id 100
-
-Note that the order in which you set the VF to promiscuous mode and add the
-VLAN interface does not matter (you can do either first). The end result in
-this example is that the VF will get all traffic that is tagged with VLAN 100.
-
-Intel(R) Ethernet Flow Director
--------------------------------
-The Intel Ethernet Flow Director performs the following tasks:
-
-- Directs receive packets according to their flows to different queues.
-- Enables tight control on routing a flow in the platform.
-- Matches flows and CPU cores for flow affinity.
-- Supports multiple parameters for flexible flow classification and load
-  balancing (in SFP mode only).
-
-NOTE: The Linux i40e driver supports the following flow types: IPv4, TCPv4, and
-UDPv4. For a given flow type, it supports valid combinations of IP addresses
-(source or destination) and UDP/TCP ports (source and destination). For
-example, you can supply only a source IP address, a source IP address and a
-destination port, or any combination of one or more of these four parameters.
-
-NOTE: The Linux i40e driver allows you to filter traffic based on a
-user-defined flexible two-byte pattern and offset by using the ethtool user-def
-and mask fields. Only L3 and L4 flow types are supported for user-defined
-flexible filters. For a given flow type, you must clear all Intel Ethernet Flow
-Director filters before changing the input set (for that flow type).
-
-To enable or disable the Intel Ethernet Flow Director::
-
-  # ethtool -K ethX ntuple <on|off>
-
-When disabling ntuple filters, all the user programmed filters are flushed from
-the driver cache and hardware. All needed filters must be re-added when ntuple
-is re-enabled.
-
-To add a filter that directs packet to queue 2, use -U or -N switch::
-
-  # ethtool -N ethX flow-type tcp4 src-ip 192.168.10.1 dst-ip \
-  192.168.10.2 src-port 2000 dst-port 2001 action 2 [loc 1]
-
-To set a filter using only the source and destination IP address::
-
-  # ethtool -N ethX flow-type tcp4 src-ip 192.168.10.1 dst-ip \
-  192.168.10.2 action 2 [loc 1]
-
-To see the list of filters currently present::
-
-  # ethtool <-u|-n> ethX
-
-Application Targeted Routing (ATR) Perfect Filters
---------------------------------------------------
-ATR is enabled by default when the kernel is in multiple transmit queue mode.
-An ATR Intel Ethernet Flow Director filter rule is added when a TCP-IP flow
-starts and is deleted when the flow ends. When a TCP-IP Intel Ethernet Flow
-Director rule is added from ethtool (Sideband filter), ATR is turned off by the
-driver. To re-enable ATR, the sideband can be disabled with the ethtool -K
-option. For example::
-
-  ethtool â€“K [adapter] ntuple [off|on]
-
-If sideband is re-enabled after ATR is re-enabled, ATR remains enabled until a
-TCP-IP flow is added. When all TCP-IP sideband rules are deleted, ATR is
-automatically re-enabled.
-
-Packets that match the ATR rules are counted in fdir_atr_match stats in
-ethtool, which also can be used to verify whether ATR rules still exist.
-
-Sideband Perfect Filters
-------------------------
-Sideband Perfect Filters are used to direct traffic that matches specified
-characteristics. They are enabled through ethtool's ntuple interface. To add a
-new filter use the following command::
-
-  ethtool -U <device> flow-type <type> src-ip <ip> dst-ip <ip> src-port <port> \
-  dst-port <port> action <queue>
-
-Where:
-  <device> - the ethernet device to program
-  <type> - can be ip4, tcp4, udp4, or sctp4
-  <ip> - the ip address to match on
-  <port> - the port number to match on
-  <queue> - the queue to direct traffic towards (-1 discards matching traffic)
-
-Use the following command to display all of the active filters::
-
-  ethtool -u <device>
-
-Use the following command to delete a filter::
-
-  ethtool -U <device> delete <N>
-
-Where <N> is the filter id displayed when printing all the active filters, and
-may also have been specified using "loc <N>" when adding the filter.
-
-The following example matches TCP traffic sent from 192.168.0.1, port 5300,
-directed to 192.168.0.5, port 80, and sends it to queue 7::
-
-  ethtool -U enp130s0 flow-type tcp4 src-ip 192.168.0.1 dst-ip 192.168.0.5 \
-  src-port 5300 dst-port 80 action 7
-
-For each flow-type, the programmed filters must all have the same matching
-input set. For example, issuing the following two commands is acceptable::
-
-  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.1 src-port 5300 action 7
-  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.5 src-port 55 action 10
-
-Issuing the next two commands, however, is not acceptable, since the first
-specifies src-ip and the second specifies dst-ip::
-
-  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.1 src-port 5300 action 7
-  ethtool -U enp130s0 flow-type ip4 dst-ip 192.168.0.5 src-port 55 action 10
-
-The second command will fail with an error. You may program multiple filters
-with the same fields, using different values, but, on one device, you may not
-program two tcp4 filters with different matching fields.
-
-Matching on a sub-portion of a field is not supported by the i40e driver, thus
-partial mask fields are not supported.
-
-The driver also supports matching user-defined data within the packet payload.
-This flexible data is specified using the "user-def" field of the ethtool
-command in the following way:
-
-+----------------------------+--------------------------+
-| 31    28    24    20    16 | 15    12    8    4    0  |
-+----------------------------+--------------------------+
-| offset into packet payload | 2 bytes of flexible data |
-+----------------------------+--------------------------+
-
-For example,
-
-::
-
-  ... user-def 0x4FFFF ...
-
-tells the filter to look 4 bytes into the payload and match that value against
-0xFFFF. The offset is based on the beginning of the payload, and not the
-beginning of the packet. Thus
-
-::
-
-  flow-type tcp4 ... user-def 0x8BEAF ...
-
-would match TCP/IPv4 packets which have the value 0xBEAF 8 bytes into the
-TCP/IPv4 payload.
-
-Note that ICMP headers are parsed as 4 bytes of header and 4 bytes of payload.
-Thus to match the first byte of the payload, you must actually add 4 bytes to
-the offset. Also note that ip4 filters match both ICMP frames as well as raw
-(unknown) ip4 frames, where the payload will be the L3 payload of the IP4 frame.
-
-The maximum offset is 64. The hardware will only read up to 64 bytes of data
-from the payload. The offset must be even because the flexible data is 2 bytes
-long and must be aligned to byte 0 of the packet payload.
-
-The user-defined flexible offset is also considered part of the input set and
-cannot be programmed separately for multiple filters of the same type. However,
-the flexible data is not part of the input set and multiple filters may use the
-same offset but match against different data.
-
-To create filters that direct traffic to a specific Virtual Function, use the
-"action" parameter. Specify the action as a 64 bit value, where the lower 32
-bits represents the queue number, while the next 8 bits represent which VF.
-Note that 0 is the PF, so the VF identifier is offset by 1. For example::
-
-  ... action 0x800000002 ...
-
-specifies to direct traffic to Virtual Function 7 (8 minus 1) into queue 2 of
-that VF.
-
-Note that these filters will not break internal routing rules, and will not
-route traffic that otherwise would not have been sent to the specified Virtual
-Function.
-
-Setting the link-down-on-close Private Flag
--------------------------------------------
-When the link-down-on-close private flag is set to "on", the port's link will
-go down when the interface is brought down using the ifconfig ethX down command.
-
-Use ethtool to view and set link-down-on-close, as follows::
-
-  ethtool --show-priv-flags ethX
-  ethtool --set-priv-flags ethX link-down-on-close [on|off]
-
-Viewing Link Messages
----------------------
-Link messages will not be displayed to the console if the distribution is
-restricting system messages. In order to see network driver link messages on
-your console, set dmesg to eight by entering the following::
-
-  dmesg -n 8
-
-NOTE: This setting is not saved across reboots.
-
-Jumbo Frames
-------------
-Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
-to a value larger than the default value of 1500.
-
-Use the ifconfig command to increase the MTU size. For example, enter the
-following where <x> is the interface number::
-
-  ifconfig eth<x> mtu 9000 up
-
-Alternatively, you can use the ip command as follows::
-
-  ip link set mtu 9000 dev eth<x>
-  ip link set up dev eth<x>
-
-This setting is not saved across reboots. The setting change can be made
-permanent by adding 'MTU=9000' to the file::
-
-  /etc/sysconfig/network-scripts/ifcfg-eth<x> // for RHEL
-  /etc/sysconfig/network/<config_file> // for SLES
-
-NOTE: The maximum MTU setting for Jumbo Frames is 9702. This value coincides
-with the maximum Jumbo Frames size of 9728 bytes.
-
-NOTE: This driver will attempt to use multiple page sized buffers to receive
-each jumbo packet. This should help to avoid buffer starvation issues when
-allocating receive packets.
-
-ethtool
--------
-The driver utilizes the ethtool interface for driver configuration and
-diagnostics, as well as displaying statistical information. The latest ethtool
-version is required for this functionality. Download it at:
-https://www.kernel.org/pub/software/network/ethtool/
-
-Supported ethtool Commands and Options for Filtering
-----------------------------------------------------
--n --show-nfc
-  Retrieves the receive network flow classification configurations.
-
-rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6
-  Retrieves the hash options for the specified network traffic type.
-
--N --config-nfc
-  Configures the receive network flow classification.
-
-rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r...
-  Configures the hash options for the specified network traffic type.
-
-udp4 UDP over IPv4
-udp6 UDP over IPv6
-
-f Hash on bytes 0 and 1 of the Layer 4 header of the Rx packet.
-n Hash on bytes 2 and 3 of the Layer 4 header of the Rx packet.
-
-Speed and Duplex Configuration
-------------------------------
-In addressing speed and duplex configuration issues, you need to distinguish
-between copper-based adapters and fiber-based adapters.
-
-In the default mode, an Intel(R) Ethernet Network Adapter using copper
-connections will attempt to auto-negotiate with its link partner to determine
-the best setting. If the adapter cannot establish link with the link partner
-using auto-negotiation, you may need to manually configure the adapter and link
-partner to identical settings to establish link and pass packets. This should
-only be needed when attempting to link with an older switch that does not
-support auto-negotiation or one that has been forced to a specific speed or
-duplex mode. Your link partner must match the setting you choose. 1 Gbps speeds
-and higher cannot be forced. Use the autonegotiation advertising setting to
-manually set devices for 1 Gbps and higher.
-
-NOTE: You cannot set the speed for devices based on the Intel(R) Ethernet
-Network Adapter XXV710 based devices.
-
-Speed, duplex, and autonegotiation advertising are configured through the
-ethtool* utility.
-
-Caution: Only experienced network administrators should force speed and duplex
-or change autonegotiation advertising manually. The settings at the switch must
-always match the adapter settings. Adapter performance may suffer or your
-adapter may not operate if you configure the adapter differently from your
-switch.
-
-An Intel(R) Ethernet Network Adapter using fiber-based connections, however,
-will not attempt to auto-negotiate with its link partner since those adapters
-operate only in full duplex and only at their native speed.
-
-NAPI
-----
-NAPI (Rx polling mode) is supported in the i40e driver.
-For more information on NAPI, see
-https://wiki.linuxfoundation.org/networking/napi
-
-Flow Control
-------------
-Ethernet Flow Control (IEEE 802.3x) can be configured with ethtool to enable
-receiving and transmitting pause frames for i40e. When transmit is enabled,
-pause frames are generated when the receive packet buffer crosses a predefined
-threshold. When receive is enabled, the transmit unit will halt for the time
-delay specified when a pause frame is received.
-
-NOTE: You must have a flow control capable link partner.
-
-Flow Control is on by default.
-
-Use ethtool to change the flow control settings.
-
-To enable or disable Rx or Tx Flow Control::
-
-  ethtool -A eth? rx <on|off> tx <on|off>
-
-Note: This command only enables or disables Flow Control if auto-negotiation is
-disabled. If auto-negotiation is enabled, this command changes the parameters
-used for auto-negotiation with the link partner.
-
-To enable or disable auto-negotiation::
-
-  ethtool -s eth? autoneg <on|off>
-
-Note: Flow Control auto-negotiation is part of link auto-negotiation. Depending
-on your device, you may not be able to change the auto-negotiation setting.
-
-RSS Hash Flow
--------------
-Allows you to set the hash bytes per flow type and any combination of one or
-more options for Receive Side Scaling (RSS) hash byte configuration.
-
-::
-
-  # ethtool -N <dev> rx-flow-hash <type> <option>
-
-Where <type> is:
-  tcp4 signifying TCP over IPv4
-  udp4 signifying UDP over IPv4
-  tcp6 signifying TCP over IPv6
-  udp6 signifying UDP over IPv6
-And <option> is one or more of:
-  s    Hash on the IP source address of the Rx packet.
-  d    Hash on the IP destination address of the Rx packet.
-  f    Hash on bytes 0 and 1 of the Layer 4 header of the Rx packet.
-  n    Hash on bytes 2 and 3 of the Layer 4 header of the Rx packet.
-
-MAC and VLAN anti-spoofing feature
-----------------------------------
-When a malicious driver attempts to send a spoofed packet, it is dropped by the
-hardware and not transmitted.
-NOTE: This feature can be disabled for a specific Virtual Function (VF)::
-
-  ip link set <pf dev> vf <vf id> spoofchk {off|on}
-
-IEEE 1588 Precision Time Protocol (PTP) Hardware Clock (PHC)
-------------------------------------------------------------
-Precision Time Protocol (PTP) is used to synchronize clocks in a computer
-network. PTP support varies among Intel devices that support this driver. Use
-"ethtool -T <netdev name>" to get a definitive list of PTP capabilities
-supported by the device.
-
-IEEE 802.1ad (QinQ) Support
----------------------------
-The IEEE 802.1ad standard, informally known as QinQ, allows for multiple VLAN
-IDs within a single Ethernet frame. VLAN IDs are sometimes referred to as
-"tags," and multiple VLAN IDs are thus referred to as a "tag stack." Tag stacks
-allow L2 tunneling and the ability to segregate traffic within a particular
-VLAN ID, among other uses.
-
-The following are examples of how to configure 802.1ad (QinQ)::
-
-  ip link add link eth0 eth0.24 type vlan proto 802.1ad id 24
-  ip link add link eth0.24 eth0.24.371 type vlan proto 802.1Q id 371
-
-Where "24" and "371" are example VLAN IDs.
-
-NOTES:
-  Receive checksum offloads, cloud filters, and VLAN acceleration are not
-  supported for 802.1ad (QinQ) packets.
-
-VXLAN and GENEVE Overlay HW Offloading
---------------------------------------
-Virtual Extensible LAN (VXLAN) allows you to extend an L2 network over an L3
-network, which may be useful in a virtualized or cloud environment. Some
-Intel(R) Ethernet Network devices perform VXLAN processing, offloading it from
-the operating system. This reduces CPU utilization.
-
-VXLAN offloading is controlled by the Tx and Rx checksum offload options
-provided by ethtool. That is, if Tx checksum offload is enabled, and the
-adapter has the capability, VXLAN offloading is also enabled.
-
-Support for VXLAN and GENEVE HW offloading is dependent on kernel support of
-the HW offloading features.
-
-Multiple Functions per Port
----------------------------
-Some adapters based on the Intel Ethernet Controller X710/XL710 support
-multiple functions on a single physical port. Configure these functions through
-the System Setup/BIOS.
-
-Minimum TX Bandwidth is the guaranteed minimum data transmission bandwidth, as
-a percentage of the full physical port link speed, that the partition will
-receive. The bandwidth the partition is awarded will never fall below the level
-you specify.
-
-The range for the minimum bandwidth values is:
-1 to ((100 minus # of partitions on the physical port) plus 1)
-For example, if a physical port has 4 partitions, the range would be:
-1 to ((100 - 4) + 1 = 97)
-
-The Maximum Bandwidth percentage represents the maximum transmit bandwidth
-allocated to the partition as a percentage of the full physical port link
-speed. The accepted range of values is 1-100. The value is used as a limiter,
-should you chose that any one particular function not be able to consume 100%
-of a port's bandwidth (should it be available). The sum of all the values for
-Maximum Bandwidth is not restricted, because no more than 100% of a port's
-bandwidth can ever be used.
-
-NOTE: X710/XXV710 devices fail to enable Max VFs (64) when Multiple Functions
-per Port (MFP) and SR-IOV are enabled. An error from i40e is logged that says
-"add vsi failed for VF N, aq_err 16". To workaround the issue, enable less than
-64 virtual functions (VFs).
-
-Data Center Bridging (DCB)
---------------------------
-DCB is a configuration Quality of Service implementation in hardware. It uses
-the VLAN priority tag (802.1p) to filter traffic. That means that there are 8
-different priorities that traffic can be filtered into. It also enables
-priority flow control (802.1Qbb) which can limit or eliminate the number of
-dropped packets during network stress. Bandwidth can be allocated to each of
-these priorities, which is enforced at the hardware level (802.1Qaz).
-
-Adapter firmware implements LLDP and DCBX protocol agents as per 802.1AB and
-802.1Qaz respectively. The firmware based DCBX agent runs in willing mode only
-and can accept settings from a DCBX capable peer. Software configuration of
-DCBX parameters via dcbtool/lldptool are not supported.
-
-NOTE: Firmware LLDP can be disabled by setting the private flag disable-fw-lldp.
-
-The i40e driver implements the DCB netlink interface layer to allow user-space
-to communicate with the driver and query DCB configuration for the port.
-
-NOTE:
-The kernel assumes that TC0 is available, and will disable Priority Flow
-Control (PFC) on the device if TC0 is not available. To fix this, ensure TC0 is
-enabled when setting up DCB on your switch.
-
-Interrupt Rate Limiting
------------------------
-:Valid Range: 0-235 (0=no limit)
-
-The Intel(R) Ethernet Controller XL710 family supports an interrupt rate
-limiting mechanism. The user can control, via ethtool, the number of
-microseconds between interrupts.
-
-Syntax::
-
-  # ethtool -C ethX rx-usecs-high N
-
-The range of 0-235 microseconds provides an effective range of 4,310 to 250,000
-interrupts per second. The value of rx-usecs-high can be set independently of
-rx-usecs and tx-usecs in the same ethtool command, and is also independent of
-the adaptive interrupt moderation algorithm. The underlying hardware supports
-granularity in 4-microsecond intervals, so adjacent values may result in the
-same interrupt rate.
-
-One possible use case is the following::
-
-  # ethtool -C ethX adaptive-rx off adaptive-tx off rx-usecs-high 20 rx-usecs \
-    5 tx-usecs 5
-
-The above command would disable adaptive interrupt moderation, and allow a
-maximum of 5 microseconds before indicating a receive or transmit was complete.
-However, instead of resulting in as many as 200,000 interrupts per second, it
-limits total interrupts per second to 50,000 via the rx-usecs-high parameter.
-
-Performance Optimization
-========================
-Driver defaults are meant to fit a wide variety of workloads, but if further
-optimization is required we recommend experimenting with the following settings.
-
-NOTE: For better performance when processing small (64B) frame sizes, try
-enabling Hyper threading in the BIOS in order to increase the number of logical
-cores in the system and subsequently increase the number of queues available to
-the adapter.
-
-Virtualized Environments
-------------------------
-1. Disable XPS on both ends by using the included virt_perf_default script
-or by running the following command as root::
-
-  for file in `ls /sys/class/net/<ethX>/queues/tx-*/xps_cpus`;
-  do echo 0 > $file; done
-
-2. Using the appropriate mechanism (vcpupin) in the vm, pin the cpu's to
-individual lcpu's, making sure to use a set of cpu's included in the
-device's local_cpulist: /sys/class/net/<ethX>/device/local_cpulist.
-
-3. Configure as many Rx/Tx queues in the VM as available. Do not rely on
-the default setting of 1.
-
-
-Non-virtualized Environments
-----------------------------
-Pin the adapter's IRQs to specific cores by disabling the irqbalance service
-and using the included set_irq_affinity script. Please see the script's help
-text for further options.
-
-- The following settings will distribute the IRQs across all the cores evenly::
-
-  # scripts/set_irq_affinity -x all <interface1> , [ <interface2>, ... ]
-
-- The following settings will distribute the IRQs across all the cores that are
-  local to the adapter (same NUMA node)::
-
-  # scripts/set_irq_affinity -x local <interface1> ,[ <interface2>, ... ]
-
-For very CPU intensive workloads, we recommend pinning the IRQs to all cores.
-
-For IP Forwarding: Disable Adaptive ITR and lower Rx and Tx interrupts per
-queue using ethtool.
-
-- Setting rx-usecs and tx-usecs to 125 will limit interrupts to about 8000
-  interrupts per second per queue.
-
-::
-
-  # ethtool -C <interface> adaptive-rx off adaptive-tx off rx-usecs 125 \
-    tx-usecs 125
-
-For lower CPU utilization: Disable Adaptive ITR and lower Rx and Tx interrupts
-per queue using ethtool.
-
-- Setting rx-usecs and tx-usecs to 250 will limit interrupts to about 4000
-  interrupts per second per queue.
-
-::
-
-  # ethtool -C <interface> adaptive-rx off adaptive-tx off rx-usecs 250 \
-    tx-usecs 250
-
-For lower latency: Disable Adaptive ITR and ITR by setting Rx and Tx to 0 using
-ethtool.
-
-::
-
-  # ethtool -C <interface> adaptive-rx off adaptive-tx off rx-usecs 0 \
-    tx-usecs 0
-
-Application Device Queues (ADq)
--------------------------------
-Application Device Queues (ADq) allows you to dedicate one or more queues to a
-specific application. This can reduce latency for the specified application,
-and allow Tx traffic to be rate limited per application. Follow the steps below
-to set ADq.
-
-1. Create traffic classes (TCs). Maximum of 8 TCs can be created per interface.
-The shaper bw_rlimit parameter is optional.
-
-Example: Sets up two tcs, tc0 and tc1, with 16 queues each and max tx rate set
-to 1Gbit for tc0 and 3Gbit for tc1.
-
-::
-
-  # tc qdisc add dev <interface> root mqprio num_tc 2 map 0 0 0 0 1 1 1 1
-  queues 16@0 16@16 hw 1 mode channel shaper bw_rlimit min_rate 1Gbit 2Gbit
-  max_rate 1Gbit 3Gbit
-
-map: priority mapping for up to 16 priorities to tcs (e.g. map 0 0 0 0 1 1 1 1
-sets priorities 0-3 to use tc0 and 4-7 to use tc1)
-
-queues: for each tc, <num queues>@<offset> (e.g. queues 16@0 16@16 assigns
-16 queues to tc0 at offset 0 and 16 queues to tc1 at offset 16. Max total
-number of queues for all tcs is 64 or number of cores, whichever is lower.)
-
-hw 1 mode channel: â€˜channel’ with â€˜hw’ set to 1 is a new new hardware
-offload mode in mqprio that makes full use of the mqprio options, the
-TCs, the queue configurations, and the QoS parameters.
-
-shaper bw_rlimit: for each tc, sets minimum and maximum bandwidth rates.
-Totals must be equal or less than port speed.
-
-For example: min_rate 1Gbit 3Gbit: Verify bandwidth limit using network
-monitoring tools such as ifstat or sar â€“n DEV [interval] [number of samples]
-
-2. Enable HW TC offload on interface::
-
-    # ethtool -K <interface> hw-tc-offload on
-
-3. Apply TCs to ingress (RX) flow of interface::
-
-    # tc qdisc add dev <interface> ingress
-
-NOTES:
- - Run all tc commands from the iproute2 <pathtoiproute2>/tc/ directory.
- - ADq is not compatible with cloud filters.
- - Setting up channels via ethtool (ethtool -L) is not supported when the
-   TCs are configured using mqprio.
- - You must have iproute2 latest version
- - NVM version 6.01 or later is required.
- - ADq cannot be enabled when any the following features are enabled: Data
-   Center Bridging (DCB), Multiple Functions per Port (MFP), or Sideband
-   Filters.
- - If another driver (for example, DPDK) has set cloud filters, you cannot
-   enable ADq.
- - Tunnel filters are not supported in ADq. If encapsulated packets do
-   arrive in non-tunnel mode, filtering will be done on the inner headers.
-   For example, for VXLAN traffic in non-tunnel mode, PCTYPE is identified
-   as a VXLAN encapsulated packet, outer headers are ignored. Therefore,
-   inner headers are matched.
- - If a TC filter on a PF matches traffic over a VF (on the PF), that
-   traffic will be routed to the appropriate queue of the PF, and will
-   not be passed on the VF. Such traffic will end up getting dropped higher
-   up in the TCP/IP stack as it does not match PF address data.
- - If traffic matches multiple TC filters that point to different TCs,
-   that traffic will be duplicated and sent to all matching TC queues.
-   The hardware switch mirrors the packet to a VSI list when multiple
-   filters are matched.
-
-
-Known Issues/Troubleshooting
-============================
-
-NOTE: 1 Gb devices based on the Intel(R) Ethernet Network Connection X722 do
-not support the following features:
-
-  * Data Center Bridging (DCB)
-  * QOS
-  * VMQ
-  * SR-IOV
-  * Task Encapsulation offload (VXLAN, NVGRE)
-  * Energy Efficient Ethernet (EEE)
-  * Auto-media detect
-
-Unexpected Issues when the device driver and DPDK share a device
-----------------------------------------------------------------
-Unexpected issues may result when an i40e device is in multi driver mode and
-the kernel driver and DPDK driver are sharing the device. This is because
-access to the global NIC resources is not synchronized between multiple
-drivers. Any change to the global NIC configuration (writing to a global
-register, setting global configuration by AQ, or changing switch modes) will
-affect all ports and drivers on the device. Loading DPDK with the
-"multi-driver" module parameter may mitigate some of the issues.
-
-TC0 must be enabled when setting up DCB on a switch
----------------------------------------------------
-The kernel assumes that TC0 is available, and will disable Priority Flow
-Control (PFC) on the device if TC0 is not available. To fix this, ensure TC0 is
-enabled when setting up DCB on your switch.
-
-
-Support
-=======
-For general information, go to the Intel support website at:
-
-https://www.intel.com/support/
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-https://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on a supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/iavf.rst b/Documentation/networking/iavf.rst
deleted file mode 100644 (file)
index f8b42b6..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Base Driver for Intel(R) Ethernet Adaptive Virtual Function
-==================================================================
-
-Intel Ethernet Adaptive Virtual Function Linux driver.
-Copyright(c) 2013-2018 Intel Corporation.
-
-Contents
-========
-
-- Identifying Your Adapter
-- Additional Configurations
-- Known Issues/Troubleshooting
-- Support
-
-This file describes the iavf Linux* Base Driver. This driver was formerly
-called i40evf.
-
-The iavf driver supports the below mentioned virtual function devices and
-can only be activated on kernels running the i40e or newer Physical Function
-(PF) driver compiled with CONFIG_PCI_IOV.  The iavf driver requires
-CONFIG_PCI_MSI to be enabled.
-
-The guest OS loading the iavf driver must support MSI-X interrupts.
-
-Identifying Your Adapter
-========================
-The driver in this kernel is compatible with devices based on the following:
- * Intel(R) XL710 X710 Virtual Function
- * Intel(R) X722 Virtual Function
- * Intel(R) XXV710 Virtual Function
- * Intel(R) Ethernet Adaptive Virtual Function
-
-For the best performance, make sure the latest NVM/FW is installed on your
-device.
-
-For information on how to identify your adapter, and for the latest NVM/FW
-images and Intel network drivers, refer to the Intel Support website:
-http://www.intel.com/support
-
-
-Additional Features and Configurations
-======================================
-
-Viewing Link Messages
----------------------
-Link messages will not be displayed to the console if the distribution is
-restricting system messages. In order to see network driver link messages on
-your console, set dmesg to eight by entering the following::
-
-  dmesg -n 8
-
-NOTE: This setting is not saved across reboots.
-
-ethtool
--------
-The driver utilizes the ethtool interface for driver configuration and
-diagnostics, as well as displaying statistical information. The latest ethtool
-version is required for this functionality. Download it at:
-https://www.kernel.org/pub/software/network/ethtool/
-
-Setting VLAN Tag Stripping
---------------------------
-If you have applications that require Virtual Functions (VFs) to receive
-packets with VLAN tags, you can disable VLAN tag stripping for the VF. The
-Physical Function (PF) processes requests issued from the VF to enable or
-disable VLAN tag stripping. Note that if the PF has assigned a VLAN to a VF,
-then requests from that VF to set VLAN tag stripping will be ignored.
-
-To enable/disable VLAN tag stripping for a VF, issue the following command
-from inside the VM in which you are running the VF::
-
-  ethtool -K <if_name> rxvlan on/off
-
-or alternatively::
-
-  ethtool --offload <if_name> rxvlan on/off
-
-Adaptive Virtual Function
--------------------------
-Adaptive Virtual Function (AVF) allows the virtual function driver, or VF, to
-adapt to changing feature sets of the physical function driver (PF) with which
-it is associated. This allows system administrators to update a PF without
-having to update all the VFs associated with it. All AVFs have a single common
-device ID and branding string.
-
-AVFs have a minimum set of features known as "base mode," but may provide
-additional features depending on what features are available in the PF with
-which the AVF is associated. The following are base mode features:
-
-- 4 Queue Pairs (QP) and associated Configuration Status Registers (CSRs)
-  for Tx/Rx.
-- i40e descriptors and ring format.
-- Descriptor write-back completion.
-- 1 control queue, with i40e descriptors, CSRs and ring format.
-- 5 MSI-X interrupt vectors and corresponding i40e CSRs.
-- 1 Interrupt Throttle Rate (ITR) index.
-- 1 Virtual Station Interface (VSI) per VF.
-- 1 Traffic Class (TC), TC0
-- Receive Side Scaling (RSS) with 64 entry indirection table and key,
-  configured through the PF.
-- 1 unicast MAC address reserved per VF.
-- 16 MAC address filters for each VF.
-- Stateless offloads - non-tunneled checksums.
-- AVF device ID.
-- HW mailbox is used for VF to PF communications (including on Windows).
-
-IEEE 802.1ad (QinQ) Support
----------------------------
-The IEEE 802.1ad standard, informally known as QinQ, allows for multiple VLAN
-IDs within a single Ethernet frame. VLAN IDs are sometimes referred to as
-"tags," and multiple VLAN IDs are thus referred to as a "tag stack." Tag stacks
-allow L2 tunneling and the ability to segregate traffic within a particular
-VLAN ID, among other uses.
-
-The following are examples of how to configure 802.1ad (QinQ)::
-
-  ip link add link eth0 eth0.24 type vlan proto 802.1ad id 24
-  ip link add link eth0.24 eth0.24.371 type vlan proto 802.1Q id 371
-
-Where "24" and "371" are example VLAN IDs.
-
-NOTES:
-  Receive checksum offloads, cloud filters, and VLAN acceleration are not
-  supported for 802.1ad (QinQ) packets.
-
-Application Device Queues (ADq)
--------------------------------
-Application Device Queues (ADq) allows you to dedicate one or more queues to a
-specific application. This can reduce latency for the specified application,
-and allow Tx traffic to be rate limited per application. Follow the steps below
-to set ADq.
-
-1. Create traffic classes (TCs). Maximum of 8 TCs can be created per interface.
-The shaper bw_rlimit parameter is optional.
-
-Example: Sets up two tcs, tc0 and tc1, with 16 queues each and max tx rate set
-to 1Gbit for tc0 and 3Gbit for tc1.
-
-::
-
-  # tc qdisc add dev <interface> root mqprio num_tc 2 map 0 0 0 0 1 1 1 1
-  queues 16@0 16@16 hw 1 mode channel shaper bw_rlimit min_rate 1Gbit 2Gbit
-  max_rate 1Gbit 3Gbit
-
-map: priority mapping for up to 16 priorities to tcs (e.g. map 0 0 0 0 1 1 1 1
-sets priorities 0-3 to use tc0 and 4-7 to use tc1)
-
-queues: for each tc, <num queues>@<offset> (e.g. queues 16@0 16@16 assigns
-16 queues to tc0 at offset 0 and 16 queues to tc1 at offset 16. Max total
-number of queues for all tcs is 64 or number of cores, whichever is lower.)
-
-hw 1 mode channel: â€˜channel’ with â€˜hw’ set to 1 is a new new hardware
-offload mode in mqprio that makes full use of the mqprio options, the
-TCs, the queue configurations, and the QoS parameters.
-
-shaper bw_rlimit: for each tc, sets minimum and maximum bandwidth rates.
-Totals must be equal or less than port speed.
-
-For example: min_rate 1Gbit 3Gbit: Verify bandwidth limit using network
-monitoring tools such as ifstat or sar â€“n DEV [interval] [number of samples]
-
-2. Enable HW TC offload on interface::
-
-    # ethtool -K <interface> hw-tc-offload on
-
-3. Apply TCs to ingress (RX) flow of interface::
-
-    # tc qdisc add dev <interface> ingress
-
-NOTES:
- - Run all tc commands from the iproute2 <pathtoiproute2>/tc/ directory.
- - ADq is not compatible with cloud filters.
- - Setting up channels via ethtool (ethtool -L) is not supported when the TCs
-   are configured using mqprio.
- - You must have iproute2 latest version
- - NVM version 6.01 or later is required.
- - ADq cannot be enabled when any the following features are enabled: Data
-   Center Bridging (DCB), Multiple Functions per Port (MFP), or Sideband Filters.
- - If another driver (for example, DPDK) has set cloud filters, you cannot
-   enable ADq.
- - Tunnel filters are not supported in ADq. If encapsulated packets do arrive
-   in non-tunnel mode, filtering will be done on the inner headers.  For example,
-   for VXLAN traffic in non-tunnel mode, PCTYPE is identified as a VXLAN
-   encapsulated packet, outer headers are ignored. Therefore, inner headers are
-   matched.
- - If a TC filter on a PF matches traffic over a VF (on the PF), that traffic
-   will be routed to the appropriate queue of the PF, and will not be passed on
-   the VF. Such traffic will end up getting dropped higher up in the TCP/IP
-   stack as it does not match PF address data.
- - If traffic matches multiple TC filters that point to different TCs, that
-   traffic will be duplicated and sent to all matching TC queues.  The hardware
-   switch mirrors the packet to a VSI list when multiple filters are matched.
-
-
-Known Issues/Troubleshooting
-============================
-
-Traffic Is Not Being Passed Between VM and Client
--------------------------------------------------
-You may not be able to pass traffic between a client system and a
-Virtual Machine (VM) running on a separate host if the Virtual Function
-(VF, or Virtual NIC) is not in trusted mode and spoof checking is enabled
-on the VF. Note that this situation can occur in any combination of client,
-host, and guest operating system. For information on how to set the VF to
-trusted mode, refer to the section "VLAN Tag Packet Steering" in this
-readme document. For information on setting spoof checking, refer to the
-section "MAC and VLAN anti-spoofing feature" in this readme document.
-
-Do not unload port driver if VF with active VM is bound to it
--------------------------------------------------------------
-Do not unload a port's driver if a Virtual Function (VF) with an active Virtual
-Machine (VM) is bound to it. Doing so will cause the port to appear to hang.
-Once the VM shuts down, or otherwise releases the VF, the command will complete.
-
-Virtual machine does not get link
----------------------------------
-If the virtual machine has more than one virtual port assigned to it, and those
-virtual ports are bound to different physical ports, you may not get link on
-all of the virtual ports. The following command may work around the issue::
-
-  ethtool -r <PF>
-
-Where <PF> is the PF interface in the host, for example: p5p1. You may need to
-run the command more than once to get link on all virtual ports.
-
-MAC address of Virtual Function changes unexpectedly
-----------------------------------------------------
-If a Virtual Function's MAC address is not assigned in the host, then the VF
-(virtual function) driver will use a random MAC address. This random MAC
-address may change each time the VF driver is reloaded. You can assign a static
-MAC address in the host machine. This static MAC address will survive
-a VF driver reload.
-
-Driver Buffer Overflow Fix
---------------------------
-The fix to resolve CVE-2016-8105, referenced in Intel SA-00069
-https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00069.html
-is included in this and future versions of the driver.
-
-Multiple Interfaces on Same Ethernet Broadcast Network
-------------------------------------------------------
-Due to the default ARP behavior on Linux, it is not possible to have one system
-on two IP networks in the same Ethernet broadcast domain (non-partitioned
-switch) behave as expected. All Ethernet interfaces will respond to IP traffic
-for any IP address assigned to the system. This results in unbalanced receive
-traffic.
-
-If you have multiple interfaces in a server, either turn on ARP filtering by
-entering::
-
-  echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
-
-NOTE: This setting is not saved across reboots. The configuration change can be
-made permanent by adding the following line to the file /etc/sysctl.conf::
-
-  net.ipv4.conf.all.arp_filter = 1
-
-Another alternative is to install the interfaces in separate broadcast domains
-(either in different switches or in a switch partitioned to VLANs).
-
-Rx Page Allocation Errors
--------------------------
-'Page allocation failure. order:0' errors may occur under stress.
-This is caused by the way the Linux kernel reports this stressed condition.
-
-
-Support
-=======
-For general information, go to the Intel support website at:
-
-https://support.intel.com
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-https://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on the supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net
diff --git a/Documentation/networking/ice.rst b/Documentation/networking/ice.rst
deleted file mode 100644 (file)
index 4d118b8..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Base Driver for the Intel(R) Ethernet Connection E800 Series
-===================================================================
-
-Intel ice Linux driver.
-Copyright(c) 2018 Intel Corporation.
-
-Contents
-========
-
-- Enabling the driver
-- Support
-
-The driver in this release supports Intel's E800 Series of products. For
-more information, visit Intel's support page at https://support.intel.com.
-
-Enabling the driver
-===================
-The driver is enabled via the standard kernel configuration system,
-using the make command::
-
-  make oldconfig/menuconfig/etc.
-
-The driver is located in the menu structure at:
-
-  -> Device Drivers
-    -> Network device support (NETDEVICES [=y])
-      -> Ethernet driver support
-        -> Intel devices
-          -> Intel(R) Ethernet Connection E800 Series Support
-
-Support
-=======
-For general information, go to the Intel support website at:
-
-https://www.intel.com/support/
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-https://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on a supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/igb.rst b/Documentation/networking/igb.rst
deleted file mode 100644 (file)
index ba16b86..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Base Driver for Intel(R) Ethernet Network Connection
-===========================================================
-
-Intel Gigabit Linux driver.
-Copyright(c) 1999-2018 Intel Corporation.
-
-Contents
-========
-
-- Identifying Your Adapter
-- Command Line Parameters
-- Additional Configurations
-- Support
-
-
-Identifying Your Adapter
-========================
-For information on how to identify your adapter, and for the latest Intel
-network drivers, refer to the Intel Support website:
-http://www.intel.com/support
-
-
-Command Line Parameters
-========================
-If the driver is built as a module, the following optional parameters are used
-by entering them on the command line with the modprobe command using this
-syntax::
-
-    modprobe igb [<option>=<VAL1>,<VAL2>,...]
-
-There needs to be a <VAL#> for each network port in the system supported by
-this driver. The values will be applied to each instance, in function order.
-For example::
-
-    modprobe igb max_vfs=2,4
-
-In this case, there are two network ports supported by igb in the system.
-
-NOTE: A descriptor describes a data buffer and attributes related to the data
-buffer. This information is accessed by the hardware.
-
-max_vfs
--------
-:Valid Range: 0-7
-
-This parameter adds support for SR-IOV. It causes the driver to spawn up to
-max_vfs worth of virtual functions.  If the value is greater than 0 it will
-also force the VMDq parameter to be 1 or more.
-
-The parameters for the driver are referenced by position. Thus, if you have a
-dual port adapter, or more than one adapter in your system, and want N virtual
-functions per port, you must specify a number for each port with each parameter
-separated by a comma. For example::
-
-    modprobe igb max_vfs=4
-
-This will spawn 4 VFs on the first port.
-
-::
-
-    modprobe igb max_vfs=2,4
-
-This will spawn 2 VFs on the first port and 4 VFs on the second port.
-
-NOTE: Caution must be used in loading the driver with these parameters.
-Depending on your system configuration, number of slots, etc., it is impossible
-to predict in all cases where the positions would be on the command line.
-
-NOTE: Neither the device nor the driver control how VFs are mapped into config
-space. Bus layout will vary by operating system. On operating systems that
-support it, you can check sysfs to find the mapping.
-
-NOTE: When either SR-IOV mode or VMDq mode is enabled, hardware VLAN filtering
-and VLAN tag stripping/insertion will remain enabled. Please remove the old
-VLAN filter before the new VLAN filter is added. For example::
-
-    ip link set eth0 vf 0 vlan 100     // set vlan 100 for VF 0
-    ip link set eth0 vf 0 vlan 0       // Delete vlan 100
-    ip link set eth0 vf 0 vlan 200     // set a new vlan 200 for VF 0
-
-Debug
------
-:Valid Range: 0-16 (0=none,...,16=all)
-:Default Value: 0
-
-This parameter adjusts the level debug messages displayed in the system logs.
-
-
-Additional Features and Configurations
-======================================
-
-Jumbo Frames
-------------
-Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
-to a value larger than the default value of 1500.
-
-Use the ifconfig command to increase the MTU size. For example, enter the
-following where <x> is the interface number::
-
-    ifconfig eth<x> mtu 9000 up
-
-Alternatively, you can use the ip command as follows::
-
-    ip link set mtu 9000 dev eth<x>
-    ip link set up dev eth<x>
-
-This setting is not saved across reboots. The setting change can be made
-permanent by adding 'MTU=9000' to the file:
-
-- For RHEL: /etc/sysconfig/network-scripts/ifcfg-eth<x>
-- For SLES: /etc/sysconfig/network/<config_file>
-
-NOTE: The maximum MTU setting for Jumbo Frames is 9216. This value coincides
-with the maximum Jumbo Frames size of 9234 bytes.
-
-NOTE: Using Jumbo frames at 10 or 100 Mbps is not supported and may result in
-poor performance or loss of link.
-
-
-ethtool
--------
-The driver utilizes the ethtool interface for driver configuration and
-diagnostics, as well as displaying statistical information. The latest ethtool
-version is required for this functionality. Download it at:
-
-https://www.kernel.org/pub/software/network/ethtool/
-
-
-Enabling Wake on LAN* (WoL)
----------------------------
-WoL is configured through the ethtool* utility.
-
-WoL will be enabled on the system during the next shut down or reboot. For
-this driver version, in order to enable WoL, the igb driver must be loaded
-prior to shutting down or suspending the system.
-
-NOTE: Wake on LAN is only supported on port A of multi-port devices.  Also
-Wake On LAN is not supported for the following device:
-- Intel(R) Gigabit VT Quad Port Server Adapter
-
-
-Multiqueue
-----------
-In this mode, a separate MSI-X vector is allocated for each queue and one for
-"other" interrupts such as link status change and errors. All interrupts are
-throttled via interrupt moderation. Interrupt moderation must be used to avoid
-interrupt storms while the driver is processing one interrupt. The moderation
-value should be at least as large as the expected time for the driver to
-process an interrupt. Multiqueue is off by default.
-
-REQUIREMENTS: MSI-X support is required for Multiqueue. If MSI-X is not found,
-the system will fallback to MSI or to Legacy interrupts. This driver supports
-receive multiqueue on all kernels that support MSI-X.
-
-NOTE: On some kernels a reboot is required to switch between single queue mode
-and multiqueue mode or vice-versa.
-
-
-MAC and VLAN anti-spoofing feature
-----------------------------------
-When a malicious driver attempts to send a spoofed packet, it is dropped by the
-hardware and not transmitted.
-
-An interrupt is sent to the PF driver notifying it of the spoof attempt. When a
-spoofed packet is detected, the PF driver will send the following message to
-the system log (displayed by the "dmesg" command):
-Spoof event(s) detected on VF(n), where n = the VF that attempted to do the
-spoofing
-
-
-Setting MAC Address, VLAN and Rate Limit Using IProute2 Tool
-------------------------------------------------------------
-You can set a MAC address of a Virtual Function (VF), a default VLAN and the
-rate limit using the IProute2 tool. Download the latest version of the
-IProute2 tool from Sourceforge if your version does not have all the features
-you require.
-
-
-Support
-=======
-For general information, go to the Intel support website at:
-
-https://www.intel.com/support/
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-https://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on a supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/igbvf.rst b/Documentation/networking/igbvf.rst
deleted file mode 100644 (file)
index a8a9ffa..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Base Virtual Function Driver for Intel(R) 1G Ethernet
-============================================================
-
-Intel Gigabit Virtual Function Linux driver.
-Copyright(c) 1999-2018 Intel Corporation.
-
-Contents
-========
-- Identifying Your Adapter
-- Additional Configurations
-- Support
-
-This driver supports Intel 82576-based virtual function devices-based virtual
-function devices that can only be activated on kernels that support SR-IOV.
-
-SR-IOV requires the correct platform and OS support.
-
-The guest OS loading this driver must support MSI-X interrupts.
-
-For questions related to hardware requirements, refer to the documentation
-supplied with your Intel adapter. All hardware requirements listed apply to use
-with Linux.
-
-Driver information can be obtained using ethtool, lspci, and ifconfig.
-Instructions on updating ethtool can be found in the section Additional
-Configurations later in this document.
-
-NOTE: There is a limit of a total of 32 shared VLANs to 1 or more VFs.
-
-
-Identifying Your Adapter
-========================
-For information on how to identify your adapter, and for the latest Intel
-network drivers, refer to the Intel Support website:
-http://www.intel.com/support
-
-
-Additional Features and Configurations
-======================================
-
-ethtool
--------
-The driver utilizes the ethtool interface for driver configuration and
-diagnostics, as well as displaying statistical information. The latest ethtool
-version is required for this functionality. Download it at:
-
-https://www.kernel.org/pub/software/network/ethtool/
-
-
-Support
-=======
-For general information, go to the Intel support website at:
-
-https://www.intel.com/support/
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-https://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on a supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net.
index af2a694..acdfb5d 100644 (file)
@@ -108,8 +108,8 @@ neigh/default/gc_thresh2 - INTEGER
        Default: 512
 
 neigh/default/gc_thresh3 - INTEGER
-       Maximum number of neighbor entries allowed.  Increase this
-       when using large numbers of interfaces and when communicating
+       Maximum number of non-PERMANENT neighbor entries allowed.  Increase
+       this when using large numbers of interfaces and when communicating
        with large numbers of directly-connected peers.
        Default: 1024
 
diff --git a/Documentation/networking/ixgb.rst b/Documentation/networking/ixgb.rst
deleted file mode 100644 (file)
index 8bd80e2..0000000
+++ /dev/null
@@ -1,467 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux Base Driver for 10 Gigabit Intel(R) Ethernet Network Connection
-=====================================================================
-
-October 1, 2018
-
-
-Contents
-========
-
-- In This Release
-- Identifying Your Adapter
-- Command Line Parameters
-- Improving Performance
-- Additional Configurations
-- Known Issues/Troubleshooting
-- Support
-
-
-
-In This Release
-===============
-
-This file describes the ixgb Linux Base Driver for the 10 Gigabit Intel(R)
-Network Connection.  This driver includes support for Itanium(R)2-based
-systems.
-
-For questions related to hardware requirements, refer to the documentation
-supplied with your 10 Gigabit adapter.  All hardware requirements listed apply
-to use with Linux.
-
-The following features are available in this kernel:
- - Native VLANs
- - Channel Bonding (teaming)
- - SNMP
-
-Channel Bonding documentation can be found in the Linux kernel source:
-/Documentation/networking/bonding.txt
-
-The driver information previously displayed in the /proc filesystem is not
-supported in this release.  Alternatively, you can use ethtool (version 1.6
-or later), lspci, and iproute2 to obtain the same information.
-
-Instructions on updating ethtool can be found in the section "Additional
-Configurations" later in this document.
-
-
-Identifying Your Adapter
-========================
-
-The following Intel network adapters are compatible with the drivers in this
-release:
-
-+------------+------------------------------+----------------------------------+
-| Controller | Adapter Name                 | Physical Layer                   |
-+============+==============================+==================================+
-| 82597EX    | Intel(R) PRO/10GbE LR/SR/CX4 | - 10G Base-LR (fiber)            |
-|            | Server Adapters              | - 10G Base-SR (fiber)            |
-|            |                              | - 10G Base-CX4 (copper)          |
-+------------+------------------------------+----------------------------------+
-
-For more information on how to identify your adapter, go to the Adapter &
-Driver ID Guide at:
-
-    https://support.intel.com
-
-
-Command Line Parameters
-=======================
-
-If the driver is built as a module, the  following optional parameters are
-used by entering them on the command line with the modprobe command using
-this syntax::
-
-    modprobe ixgb [<option>=<VAL1>,<VAL2>,...]
-
-For example, with two 10GbE PCI adapters, entering::
-
-    modprobe ixgb TxDescriptors=80,128
-
-loads the ixgb driver with 80 TX resources for the first adapter and 128 TX
-resources for the second adapter.
-
-The default value for each parameter is generally the recommended setting,
-unless otherwise noted.
-
-Copybreak
----------
-:Valid Range: 0-XXXX
-:Default Value: 256
-
-    This is the maximum size of packet that is copied to a new buffer on
-    receive.
-
-Debug
------
-:Valid Range: 0-16 (0=none,...,16=all)
-:Default Value: 0
-
-    This parameter adjusts the level of debug messages displayed in the
-    system logs.
-
-FlowControl
------------
-:Valid Range: 0-3 (0=none, 1=Rx only, 2=Tx only, 3=Rx&Tx)
-:Default Value: 1 if no EEPROM, otherwise read from EEPROM
-
-    This parameter controls the automatic generation(Tx) and response(Rx) to
-    Ethernet PAUSE frames.  There are hardware bugs associated with enabling
-    Tx flow control so beware.
-
-RxDescriptors
--------------
-:Valid Range: 64-4096
-:Default Value: 1024
-
-    This value is the number of receive descriptors allocated by the driver.
-    Increasing this value allows the driver to buffer more incoming packets.
-    Each descriptor is 16 bytes.  A receive buffer is also allocated for
-    each descriptor and can be either 2048, 4056, 8192, or 16384 bytes,
-    depending on the MTU setting.  When the MTU size is 1500 or less, the
-    receive buffer size is 2048 bytes. When the MTU is greater than 1500 the
-    receive buffer size will be either 4056, 8192, or 16384 bytes.  The
-    maximum MTU size is 16114.
-
-TxDescriptors
--------------
-:Valid Range: 64-4096
-:Default Value: 256
-
-    This value is the number of transmit descriptors allocated by the driver.
-    Increasing this value allows the driver to queue more transmits.  Each
-    descriptor is 16 bytes.
-
-RxIntDelay
-----------
-:Valid Range: 0-65535 (0=off)
-:Default Value: 72
-
-    This value delays the generation of receive interrupts in units of
-    0.8192 microseconds.  Receive interrupt reduction can improve CPU
-    efficiency if properly tuned for specific network traffic.  Increasing
-    this value adds extra latency to frame reception and can end up
-    decreasing the throughput of TCP traffic.  If the system is reporting
-    dropped receives, this value may be set too high, causing the driver to
-    run out of available receive descriptors.
-
-TxIntDelay
-----------
-:Valid Range: 0-65535 (0=off)
-:Default Value: 32
-
-    This value delays the generation of transmit interrupts in units of
-    0.8192 microseconds.  Transmit interrupt reduction can improve CPU
-    efficiency if properly tuned for specific network traffic.  Increasing
-    this value adds extra latency to frame transmission and can end up
-    decreasing the throughput of TCP traffic.  If this value is set too high,
-    it will cause the driver to run out of available transmit descriptors.
-
-XsumRX
-------
-:Valid Range: 0-1
-:Default Value: 1
-
-    A value of '1' indicates that the driver should enable IP checksum
-    offload for received packets (both UDP and TCP) to the adapter hardware.
-
-RxFCHighThresh
---------------
-:Valid Range: 1,536-262,136 (0x600 - 0x3FFF8, 8 byte granularity)
-:Default Value: 196,608 (0x30000)
-
-    Receive Flow control high threshold (when we send a pause frame)
-
-RxFCLowThresh
--------------
-:Valid Range: 64-262,136 (0x40 - 0x3FFF8, 8 byte granularity)
-:Default Value: 163,840 (0x28000)
-
-    Receive Flow control low threshold (when we send a resume frame)
-
-FCReqTimeout
-------------
-:Valid Range: 1-65535
-:Default Value: 65535
-
-    Flow control request timeout (how long to pause the link partner's tx)
-
-IntDelayEnable
---------------
-:Value Range: 0,1
-:Default Value: 1
-
-    Interrupt Delay, 0 disables transmit interrupt delay and 1 enables it.
-
-
-Improving Performance
-=====================
-
-With the 10 Gigabit server adapters, the default Linux configuration will
-very likely limit the total available throughput artificially.  There is a set
-of configuration changes that, when applied together, will increase the ability
-of Linux to transmit and receive data.  The following enhancements were
-originally acquired from settings published at http://www.spec.org/web99/ for
-various submitted results using Linux.
-
-NOTE:
-  These changes are only suggestions, and serve as a starting point for
-  tuning your network performance.
-
-The changes are made in three major ways, listed in order of greatest effect:
-
-- Use ip link to modify the mtu (maximum transmission unit) and the txqueuelen
-  parameter.
-- Use sysctl to modify /proc parameters (essentially kernel tuning)
-- Use setpci to modify the MMRBC field in PCI-X configuration space to increase
-  transmit burst lengths on the bus.
-
-NOTE:
-  setpci modifies the adapter's configuration registers to allow it to read
-  up to 4k bytes at a time (for transmits).  However, for some systems the
-  behavior after modifying this register may be undefined (possibly errors of
-  some kind).  A power-cycle, hard reset or explicitly setting the e6 register
-  back to 22 (setpci -d 8086:1a48 e6.b=22) may be required to get back to a
-  stable configuration.
-
-- COPY these lines and paste them into ixgb_perf.sh:
-
-::
-
-  #!/bin/bash
-  echo "configuring network performance , edit this file to change the interface
-  or device ID of 10GbE card"
-  # set mmrbc to 4k reads, modify only Intel 10GbE device IDs
-  # replace 1a48 with appropriate 10GbE device's ID installed on the system,
-  # if needed.
-  setpci -d 8086:1a48 e6.b=2e
-  # set the MTU (max transmission unit) - it requires your switch and clients
-  # to change as well.
-  # set the txqueuelen
-  # your ixgb adapter should be loaded as eth1 for this to work, change if needed
-  ip li set dev eth1 mtu 9000 txqueuelen 1000 up
-  # call the sysctl utility to modify /proc/sys entries
-  sysctl -p ./sysctl_ixgb.conf
-
-- COPY these lines and paste them into sysctl_ixgb.conf:
-
-::
-
-  # some of the defaults may be different for your kernel
-  # call this file with sysctl -p <this file>
-  # these are just suggested values that worked well to increase throughput in
-  # several network benchmark tests, your mileage may vary
-
-  ### IPV4 specific settings
-  # turn TCP timestamp support off, default 1, reduces CPU use
-  net.ipv4.tcp_timestamps = 0
-  # turn SACK support off, default on
-  # on systems with a VERY fast bus -> memory interface this is the big gainer
-  net.ipv4.tcp_sack = 0
-  # set min/default/max TCP read buffer, default 4096 87380 174760
-  net.ipv4.tcp_rmem = 10000000 10000000 10000000
-  # set min/pressure/max TCP write buffer, default 4096 16384 131072
-  net.ipv4.tcp_wmem = 10000000 10000000 10000000
-  # set min/pressure/max TCP buffer space, default 31744 32256 32768
-  net.ipv4.tcp_mem = 10000000 10000000 10000000
-
-  ### CORE settings (mostly for socket and UDP effect)
-  # set maximum receive socket buffer size, default 131071
-  net.core.rmem_max = 524287
-  # set maximum send socket buffer size, default 131071
-  net.core.wmem_max = 524287
-  # set default receive socket buffer size, default 65535
-  net.core.rmem_default = 524287
-  # set default send socket buffer size, default 65535
-  net.core.wmem_default = 524287
-  # set maximum amount of option memory buffers, default 10240
-  net.core.optmem_max = 524287
-  # set number of unprocessed input packets before kernel starts dropping them; default 300
-  net.core.netdev_max_backlog = 300000
-
-Edit the ixgb_perf.sh script if necessary to change eth1 to whatever interface
-your ixgb driver is using and/or replace '1a48' with appropriate 10GbE device's
-ID installed on the system.
-
-NOTE:
-  Unless these scripts are added to the boot process, these changes will
-  only last only until the next system reboot.
-
-
-Resolving Slow UDP Traffic
---------------------------
-If your server does not seem to be able to receive UDP traffic as fast as it
-can receive TCP traffic, it could be because Linux, by default, does not set
-the network stack buffers as large as they need to be to support high UDP
-transfer rates.  One way to alleviate this problem is to allow more memory to
-be used by the IP stack to store incoming data.
-
-For instance, use the commands::
-
-    sysctl -w net.core.rmem_max=262143
-
-and::
-
-    sysctl -w net.core.rmem_default=262143
-
-to increase the read buffer memory max and default to 262143 (256k - 1) from
-defaults of max=131071 (128k - 1) and default=65535 (64k - 1).  These variables
-will increase the amount of memory used by the network stack for receives, and
-can be increased significantly more if necessary for your application.
-
-
-Additional Configurations
-=========================
-
-Configuring the Driver on Different Distributions
--------------------------------------------------
-Configuring a network driver to load properly when the system is started is
-distribution dependent. Typically, the configuration process involves adding
-an alias line to /etc/modprobe.conf as well as editing other system startup
-scripts and/or configuration files.  Many popular Linux distributions ship
-with tools to make these changes for you.  To learn the proper way to
-configure a network device for your system, refer to your distribution
-documentation.  If during this process you are asked for the driver or module
-name, the name for the Linux Base Driver for the Intel 10GbE Family of
-Adapters is ixgb.
-
-Viewing Link Messages
----------------------
-Link messages will not be displayed to the console if the distribution is
-restricting system messages. In order to see network driver link messages on
-your console, set dmesg to eight by entering the following::
-
-    dmesg -n 8
-
-NOTE: This setting is not saved across reboots.
-
-Jumbo Frames
-------------
-The driver supports Jumbo Frames for all adapters. Jumbo Frames support is
-enabled by changing the MTU to a value larger than the default of 1500.
-The maximum value for the MTU is 16114.  Use the ip command to
-increase the MTU size.  For example::
-
-    ip li set dev ethx mtu 9000
-
-The maximum MTU setting for Jumbo Frames is 16114.  This value coincides
-with the maximum Jumbo Frames size of 16128.
-
-Ethtool
--------
-The driver utilizes the ethtool interface for driver configuration and
-diagnostics, as well as displaying statistical information.  The ethtool
-version 1.6 or later is required for this functionality.
-
-The latest release of ethtool can be found from
-https://www.kernel.org/pub/software/network/ethtool/
-
-NOTE:
-  The ethtool version 1.6 only supports a limited set of ethtool options.
-  Support for a more complete ethtool feature set can be enabled by
-  upgrading to the latest version.
-
-NAPI
-----
-NAPI (Rx polling mode) is supported in the ixgb driver.
-
-See https://wiki.linuxfoundation.org/networking/napi for more information on
-NAPI.
-
-
-Known Issues/Troubleshooting
-============================
-
-NOTE:
-  After installing the driver, if your Intel Network Connection is not
-  working, verify in the "In This Release" section of the readme that you have
-  installed the correct driver.
-
-Cable Interoperability Issue with Fujitsu XENPAK Module in SmartBits Chassis
-----------------------------------------------------------------------------
-Excessive CRC errors may be observed if the Intel(R) PRO/10GbE CX4
-Server adapter is connected to a Fujitsu XENPAK CX4 module in a SmartBits
-chassis using 15 m/24AWG cable assemblies manufactured by Fujitsu or Leoni.
-The CRC errors may be received either by the Intel(R) PRO/10GbE CX4
-Server adapter or the SmartBits. If this situation occurs using a different
-cable assembly may resolve the issue.
-
-Cable Interoperability Issues with HP Procurve 3400cl Switch Port
------------------------------------------------------------------
-Excessive CRC errors may be observed if the Intel(R) PRO/10GbE CX4 Server
-adapter is connected to an HP Procurve 3400cl switch port using short cables
-(1 m or shorter). If this situation occurs, using a longer cable may resolve
-the issue.
-
-Excessive CRC errors may be observed using Fujitsu 24AWG cable assemblies that
-Are 10 m or longer or where using a Leoni 15 m/24AWG cable assembly. The CRC
-errors may be received either by the CX4 Server adapter or at the switch. If
-this situation occurs, using a different cable assembly may resolve the issue.
-
-Jumbo Frames System Requirement
--------------------------------
-Memory allocation failures have been observed on Linux systems with 64 MB
-of RAM or less that are running Jumbo Frames.  If you are using Jumbo
-Frames, your system may require more than the advertised minimum
-requirement of 64 MB of system memory.
-
-Performance Degradation with Jumbo Frames
------------------------------------------
-Degradation in throughput performance may be observed in some Jumbo frames
-environments.  If this is observed, increasing the application's socket buffer
-size and/or increasing the /proc/sys/net/ipv4/tcp_*mem entry values may help.
-See the specific application manual and /usr/src/linux*/Documentation/
-networking/ip-sysctl.txt for more details.
-
-Allocating Rx Buffers when Using Jumbo Frames
----------------------------------------------
-Allocating Rx buffers when using Jumbo Frames on 2.6.x kernels may fail if
-the available memory is heavily fragmented. This issue may be seen with PCI-X
-adapters or with packet split disabled. This can be reduced or eliminated
-by changing the amount of available memory for receive buffer allocation, by
-increasing /proc/sys/vm/min_free_kbytes.
-
-Multiple Interfaces on Same Ethernet Broadcast Network
-------------------------------------------------------
-Due to the default ARP behavior on Linux, it is not possible to have
-one system on two IP networks in the same Ethernet broadcast domain
-(non-partitioned switch) behave as expected.  All Ethernet interfaces
-will respond to IP traffic for any IP address assigned to the system.
-This results in unbalanced receive traffic.
-
-If you have multiple interfaces in a server, do either of the following:
-
-  - Turn on ARP filtering by entering::
-
-      echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
-
-  - Install the interfaces in separate broadcast domains - either in
-    different switches or in a switch partitioned to VLANs.
-
-UDP Stress Test Dropped Packet Issue
---------------------------------------
-Under small packets UDP stress test with 10GbE driver, the Linux system
-may drop UDP packets due to the fullness of socket buffers. You may want
-to change the driver's Flow Control variables to the minimum value for
-controlling packet reception.
-
-Tx Hangs Possible Under Stress
-------------------------------
-Under stress conditions, if TX hangs occur, turning off TSO
-"ethtool -K eth0 tso off" may resolve the problem.
-
-
-Support
-=======
-For general information, go to the Intel support website at:
-
-https://www.intel.com/support/
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-https://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on a supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net
diff --git a/Documentation/networking/ixgbe.rst b/Documentation/networking/ixgbe.rst
deleted file mode 100644 (file)
index 725fc69..0000000
+++ /dev/null
@@ -1,527 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Base Driver for the Intel(R) Ethernet 10 Gigabit PCI Express Adapters
-=============================================================================
-
-Intel 10 Gigabit Linux driver.
-Copyright(c) 1999-2018 Intel Corporation.
-
-Contents
-========
-
-- Identifying Your Adapter
-- Command Line Parameters
-- Additional Configurations
-- Known Issues
-- Support
-
-Identifying Your Adapter
-========================
-The driver is compatible with devices based on the following:
-
- * Intel(R) Ethernet Controller 82598
- * Intel(R) Ethernet Controller 82599
- * Intel(R) Ethernet Controller X520
- * Intel(R) Ethernet Controller X540
- * Intel(R) Ethernet Controller x550
- * Intel(R) Ethernet Controller X552
- * Intel(R) Ethernet Controller X553
-
-For information on how to identify your adapter, and for the latest Intel
-network drivers, refer to the Intel Support website:
-https://www.intel.com/support
-
-SFP+ Devices with Pluggable Optics
-----------------------------------
-
-82599-BASED ADAPTERS
-~~~~~~~~~~~~~~~~~~~~
-NOTES:
-- If your 82599-based Intel(R) Network Adapter came with Intel optics or is an
-Intel(R) Ethernet Server Adapter X520-2, then it only supports Intel optics
-and/or the direct attach cables listed below.
-- When 82599-based SFP+ devices are connected back to back, they should be set
-to the same Speed setting via ethtool. Results may vary if you mix speed
-settings.
-
-+---------------+---------------------------------------+------------------+
-| Supplier      | Type                                  | Part Numbers     |
-+===============+=======================================+==================+
-| SR Modules                                                               |
-+---------------+---------------------------------------+------------------+
-| Intel         | DUAL RATE 1G/10G SFP+ SR (bailed)     | FTLX8571D3BCV-IT |
-+---------------+---------------------------------------+------------------+
-| Intel         | DUAL RATE 1G/10G SFP+ SR (bailed)     | AFBR-703SDZ-IN2  |
-+---------------+---------------------------------------+------------------+
-| Intel         | DUAL RATE 1G/10G SFP+ SR (bailed)     | AFBR-703SDDZ-IN1 |
-+---------------+---------------------------------------+------------------+
-| LR Modules                                                               |
-+---------------+---------------------------------------+------------------+
-| Intel         | DUAL RATE 1G/10G SFP+ LR (bailed)     | FTLX1471D3BCV-IT |
-+---------------+---------------------------------------+------------------+
-| Intel         | DUAL RATE 1G/10G SFP+ LR (bailed)     | AFCT-701SDZ-IN2  |
-+---------------+---------------------------------------+------------------+
-| Intel         | DUAL RATE 1G/10G SFP+ LR (bailed)     | AFCT-701SDDZ-IN1 |
-+---------------+---------------------------------------+------------------+
-
-The following is a list of 3rd party SFP+ modules that have received some
-testing. Not all modules are applicable to all devices.
-
-+---------------+---------------------------------------+------------------+
-| Supplier      | Type                                  | Part Numbers     |
-+===============+=======================================+==================+
-| Finisar       | SFP+ SR bailed, 10g single rate       | FTLX8571D3BCL    |
-+---------------+---------------------------------------+------------------+
-| Avago         | SFP+ SR bailed, 10g single rate       | AFBR-700SDZ      |
-+---------------+---------------------------------------+------------------+
-| Finisar       | SFP+ LR bailed, 10g single rate       | FTLX1471D3BCL    |
-+---------------+---------------------------------------+------------------+
-| Finisar       | DUAL RATE 1G/10G SFP+ SR (No Bail)    | FTLX8571D3QCV-IT |
-+---------------+---------------------------------------+------------------+
-| Avago         | DUAL RATE 1G/10G SFP+ SR (No Bail)    | AFBR-703SDZ-IN1  |
-+---------------+---------------------------------------+------------------+
-| Finisar       | DUAL RATE 1G/10G SFP+ LR (No Bail)    | FTLX1471D3QCV-IT |
-+---------------+---------------------------------------+------------------+
-| Avago         | DUAL RATE 1G/10G SFP+ LR (No Bail)    | AFCT-701SDZ-IN1  |
-+---------------+---------------------------------------+------------------+
-| Finisar       | 1000BASE-T SFP                        | FCLF8522P2BTL    |
-+---------------+---------------------------------------+------------------+
-| Avago         | 1000BASE-T                            | ABCU-5710RZ      |
-+---------------+---------------------------------------+------------------+
-| HP            | 1000BASE-SX SFP                       | 453153-001       |
-+---------------+---------------------------------------+------------------+
-
-82599-based adapters support all passive and active limiting direct attach
-cables that comply with SFF-8431 v4.1 and SFF-8472 v10.4 specifications.
-
-Laser turns off for SFP+ when ifconfig ethX down
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"ifconfig ethX down" turns off the laser for 82599-based SFP+ fiber adapters.
-"ifconfig ethX up" turns on the laser.
-Alternatively, you can use "ip link set [down/up] dev ethX" to turn the
-laser off and on.
-
-
-82599-based QSFP+ Adapters
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-NOTES:
-- If your 82599-based Intel(R) Network Adapter came with Intel optics, it only
-supports Intel optics.
-- 82599-based QSFP+ adapters only support 4x10 Gbps connections.  1x40 Gbps
-connections are not supported. QSFP+ link partners must be configured for
-4x10 Gbps.
-- 82599-based QSFP+ adapters do not support automatic link speed detection.
-The link speed must be configured to either 10 Gbps or 1 Gbps to match the link
-partners speed capabilities. Incorrect speed configurations will result in
-failure to link.
-- Intel(R) Ethernet Converged Network Adapter X520-Q1 only supports the optics
-and direct attach cables listed below.
-
-+---------------+---------------------------------------+------------------+
-| Supplier      | Type                                  | Part Numbers     |
-+===============+=======================================+==================+
-| Intel         | DUAL RATE 1G/10G QSFP+ SRL (bailed)   | E10GQSFPSR       |
-+---------------+---------------------------------------+------------------+
-
-82599-based QSFP+ adapters support all passive and active limiting QSFP+
-direct attach cables that comply with SFF-8436 v4.1 specifications.
-
-82598-BASED ADAPTERS
-~~~~~~~~~~~~~~~~~~~~
-NOTES:
-- Intel(r) Ethernet Network Adapters that support removable optical modules
-only support their original module type (for example, the Intel(R) 10 Gigabit
-SR Dual Port Express Module only supports SR optical modules). If you plug in
-a different type of module, the driver will not load.
-- Hot Swapping/hot plugging optical modules is not supported.
-- Only single speed, 10 gigabit modules are supported.
-- LAN on Motherboard (LOMs) may support DA, SR, or LR modules. Other module
-types are not supported. Please see your system documentation for details.
-
-The following is a list of SFP+ modules and direct attach cables that have
-received some testing. Not all modules are applicable to all devices.
-
-+---------------+---------------------------------------+------------------+
-| Supplier      | Type                                  | Part Numbers     |
-+===============+=======================================+==================+
-| Finisar       | SFP+ SR bailed, 10g single rate       | FTLX8571D3BCL    |
-+---------------+---------------------------------------+------------------+
-| Avago         | SFP+ SR bailed, 10g single rate       | AFBR-700SDZ      |
-+---------------+---------------------------------------+------------------+
-| Finisar       | SFP+ LR bailed, 10g single rate       | FTLX1471D3BCL    |
-+---------------+---------------------------------------+------------------+
-
-82598-based adapters support all passive direct attach cables that comply with
-SFF-8431 v4.1 and SFF-8472 v10.4 specifications. Active direct attach cables
-are not supported.
-
-Third party optic modules and cables referred to above are listed only for the
-purpose of highlighting third party specifications and potential
-compatibility, and are not recommendations or endorsements or sponsorship of
-any third party's product by Intel. Intel is not endorsing or promoting
-products made by any third party and the third party reference is provided
-only to share information regarding certain optic modules and cables with the
-above specifications. There may be other manufacturers or suppliers, producing
-or supplying optic modules and cables with similar or matching descriptions.
-Customers must use their own discretion and diligence to purchase optic
-modules and cables from any third party of their choice. Customers are solely
-responsible for assessing the suitability of the product and/or devices and
-for the selection of the vendor for purchasing any product. THE OPTIC MODULES
-AND CABLES REFERRED TO ABOVE ARE NOT WARRANTED OR SUPPORTED BY INTEL. INTEL
-ASSUMES NO LIABILITY WHATSOEVER, AND INTEL DISCLAIMS ANY EXPRESS OR IMPLIED
-WARRANTY, RELATING TO SALE AND/OR USE OF SUCH THIRD PARTY PRODUCTS OR
-SELECTION OF VENDOR BY CUSTOMERS.
-
-Command Line Parameters
-=======================
-
-max_vfs
--------
-:Valid Range: 1-63
-
-This parameter adds support for SR-IOV. It causes the driver to spawn up to
-max_vfs worth of virtual functions.
-If the value is greater than 0 it will also force the VMDq parameter to be 1 or
-more.
-
-NOTE: This parameter is only used on kernel 3.7.x and below. On kernel 3.8.x
-and above, use sysfs to enable VFs. Also, for Red Hat distributions, this
-parameter is only used on version 6.6 and older. For version 6.7 and newer, use
-sysfs. For example::
-
-  #echo $num_vf_enabled > /sys/class/net/$dev/device/sriov_numvfs // enable VFs
-  #echo 0 > /sys/class/net/$dev/device/sriov_numvfs               //disable VFs
-
-The parameters for the driver are referenced by position. Thus, if you have a
-dual port adapter, or more than one adapter in your system, and want N virtual
-functions per port, you must specify a number for each port with each parameter
-separated by a comma. For example::
-
-  modprobe ixgbe max_vfs=4
-
-This will spawn 4 VFs on the first port.
-
-::
-
-  modprobe ixgbe max_vfs=2,4
-
-This will spawn 2 VFs on the first port and 4 VFs on the second port.
-
-NOTE: Caution must be used in loading the driver with these parameters.
-Depending on your system configuration, number of slots, etc., it is impossible
-to predict in all cases where the positions would be on the command line.
-
-NOTE: Neither the device nor the driver control how VFs are mapped into config
-space. Bus layout will vary by operating system. On operating systems that
-support it, you can check sysfs to find the mapping.
-
-NOTE: When either SR-IOV mode or VMDq mode is enabled, hardware VLAN filtering
-and VLAN tag stripping/insertion will remain enabled. Please remove the old
-VLAN filter before the new VLAN filter is added. For example,
-
-::
-
-  ip link set eth0 vf 0 vlan 100 // set VLAN 100 for VF 0
-  ip link set eth0 vf 0 vlan 0   // Delete VLAN 100
-  ip link set eth0 vf 0 vlan 200 // set a new VLAN 200 for VF 0
-
-With kernel 3.6, the driver supports the simultaneous usage of max_vfs and DCB
-features, subject to the constraints described below. Prior to kernel 3.6, the
-driver did not support the simultaneous operation of max_vfs greater than 0 and
-the DCB features (multiple traffic classes utilizing Priority Flow Control and
-Extended Transmission Selection).
-
-When DCB is enabled, network traffic is transmitted and received through
-multiple traffic classes (packet buffers in the NIC). The traffic is associated
-with a specific class based on priority, which has a value of 0 through 7 used
-in the VLAN tag. When SR-IOV is not enabled, each traffic class is associated
-with a set of receive/transmit descriptor queue pairs. The number of queue
-pairs for a given traffic class depends on the hardware configuration. When
-SR-IOV is enabled, the descriptor queue pairs are grouped into pools. The
-Physical Function (PF) and each Virtual Function (VF) is allocated a pool of
-receive/transmit descriptor queue pairs. When multiple traffic classes are
-configured (for example, DCB is enabled), each pool contains a queue pair from
-each traffic class. When a single traffic class is configured in the hardware,
-the pools contain multiple queue pairs from the single traffic class.
-
-The number of VFs that can be allocated depends on the number of traffic
-classes that can be enabled. The configurable number of traffic classes for
-each enabled VF is as follows:
-0 - 15 VFs = Up to 8 traffic classes, depending on device support
-16 - 31 VFs = Up to 4 traffic classes
-32 - 63 VFs = 1 traffic class
-
-When VFs are configured, the PF is allocated one pool as well. The PF supports
-the DCB features with the constraint that each traffic class will only use a
-single queue pair. When zero VFs are configured, the PF can support multiple
-queue pairs per traffic class.
-
-allow_unsupported_sfp
----------------------
-:Valid Range: 0,1
-:Default Value: 0 (disabled)
-
-This parameter allows unsupported and untested SFP+ modules on 82599-based
-adapters, as long as the type of module is known to the driver.
-
-debug
------
-:Valid Range: 0-16 (0=none,...,16=all)
-:Default Value: 0
-
-This parameter adjusts the level of debug messages displayed in the system
-logs.
-
-
-Additional Features and Configurations
-======================================
-
-Flow Control
-------------
-Ethernet Flow Control (IEEE 802.3x) can be configured with ethtool to enable
-receiving and transmitting pause frames for ixgbe. When transmit is enabled,
-pause frames are generated when the receive packet buffer crosses a predefined
-threshold. When receive is enabled, the transmit unit will halt for the time
-delay specified when a pause frame is received.
-
-NOTE: You must have a flow control capable link partner.
-
-Flow Control is enabled by default.
-
-Use ethtool to change the flow control settings. To enable or disable Rx or
-Tx Flow Control::
-
-  ethtool -A eth? rx <on|off> tx <on|off>
-
-Note: This command only enables or disables Flow Control if auto-negotiation is
-disabled. If auto-negotiation is enabled, this command changes the parameters
-used for auto-negotiation with the link partner.
-
-To enable or disable auto-negotiation::
-
-  ethtool -s eth? autoneg <on|off>
-
-Note: Flow Control auto-negotiation is part of link auto-negotiation. Depending
-on your device, you may not be able to change the auto-negotiation setting.
-
-NOTE: For 82598 backplane cards entering 1 gigabit mode, flow control default
-behavior is changed to off. Flow control in 1 gigabit mode on these devices can
-lead to transmit hangs.
-
-Intel(R) Ethernet Flow Director
--------------------------------
-The Intel Ethernet Flow Director performs the following tasks:
-
-- Directs receive packets according to their flows to different queues.
-- Enables tight control on routing a flow in the platform.
-- Matches flows and CPU cores for flow affinity.
-- Supports multiple parameters for flexible flow classification and load
-  balancing (in SFP mode only).
-
-NOTE: Intel Ethernet Flow Director masking works in the opposite manner from
-subnet masking. In the following command::
-
-  #ethtool -N eth11 flow-type ip4 src-ip 172.4.1.2 m 255.0.0.0 dst-ip \
-  172.21.1.1 m 255.128.0.0 action 31
-
-The src-ip value that is written to the filter will be 0.4.1.2, not 172.0.0.0
-as might be expected. Similarly, the dst-ip value written to the filter will be
-0.21.1.1, not 172.0.0.0.
-
-To enable or disable the Intel Ethernet Flow Director::
-
-  # ethtool -K ethX ntuple <on|off>
-
-When disabling ntuple filters, all the user programmed filters are flushed from
-the driver cache and hardware. All needed filters must be re-added when ntuple
-is re-enabled.
-
-To add a filter that directs packet to queue 2, use -U or -N switch::
-
-  # ethtool -N ethX flow-type tcp4 src-ip 192.168.10.1 dst-ip \
-  192.168.10.2 src-port 2000 dst-port 2001 action 2 [loc 1]
-
-To see the list of filters currently present::
-
-  # ethtool <-u|-n> ethX
-
-Sideband Perfect Filters
-------------------------
-Sideband Perfect Filters are used to direct traffic that matches specified
-characteristics. They are enabled through ethtool's ntuple interface. To add a
-new filter use the following command::
-
-  ethtool -U <device> flow-type <type> src-ip <ip> dst-ip <ip> src-port <port> \
-  dst-port <port> action <queue>
-
-Where:
-  <device> - the ethernet device to program
-  <type> - can be ip4, tcp4, udp4, or sctp4
-  <ip> - the IP address to match on
-  <port> - the port number to match on
-  <queue> - the queue to direct traffic towards (-1 discards the matched traffic)
-
-Use the following command to delete a filter::
-
-  ethtool -U <device> delete <N>
-
-Where <N> is the filter id displayed when printing all the active filters, and
-may also have been specified using "loc <N>" when adding the filter.
-
-The following example matches TCP traffic sent from 192.168.0.1, port 5300,
-directed to 192.168.0.5, port 80, and sends it to queue 7::
-
-  ethtool -U enp130s0 flow-type tcp4 src-ip 192.168.0.1 dst-ip 192.168.0.5 \
-  src-port 5300 dst-port 80 action 7
-
-For each flow-type, the programmed filters must all have the same matching
-input set. For example, issuing the following two commands is acceptable::
-
-  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.1 src-port 5300 action 7
-  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.5 src-port 55 action 10
-
-Issuing the next two commands, however, is not acceptable, since the first
-specifies src-ip and the second specifies dst-ip::
-
-  ethtool -U enp130s0 flow-type ip4 src-ip 192.168.0.1 src-port 5300 action 7
-  ethtool -U enp130s0 flow-type ip4 dst-ip 192.168.0.5 src-port 55 action 10
-
-The second command will fail with an error. You may program multiple filters
-with the same fields, using different values, but, on one device, you may not
-program two TCP4 filters with different matching fields.
-
-Matching on a sub-portion of a field is not supported by the ixgbe driver, thus
-partial mask fields are not supported.
-
-To create filters that direct traffic to a specific Virtual Function, use the
-"user-def" parameter. Specify the user-def as a 64 bit value, where the lower 32
-bits represents the queue number, while the next 8 bits represent which VF.
-Note that 0 is the PF, so the VF identifier is offset by 1. For example::
-
-  ... user-def 0x800000002 ...
-
-specifies to direct traffic to Virtual Function 7 (8 minus 1) into queue 2 of
-that VF.
-
-Note that these filters will not break internal routing rules, and will not
-route traffic that otherwise would not have been sent to the specified Virtual
-Function.
-
-Jumbo Frames
-------------
-Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
-to a value larger than the default value of 1500.
-
-Use the ifconfig command to increase the MTU size. For example, enter the
-following where <x> is the interface number::
-
-  ifconfig eth<x> mtu 9000 up
-
-Alternatively, you can use the ip command as follows::
-
-  ip link set mtu 9000 dev eth<x>
-  ip link set up dev eth<x>
-
-This setting is not saved across reboots. The setting change can be made
-permanent by adding 'MTU=9000' to the file::
-
-  /etc/sysconfig/network-scripts/ifcfg-eth<x> // for RHEL
-  /etc/sysconfig/network/<config_file> // for SLES
-
-NOTE: The maximum MTU setting for Jumbo Frames is 9710. This value coincides
-with the maximum Jumbo Frames size of 9728 bytes.
-
-NOTE: This driver will attempt to use multiple page sized buffers to receive
-each jumbo packet. This should help to avoid buffer starvation issues when
-allocating receive packets.
-
-NOTE: For 82599-based network connections, if you are enabling jumbo frames in
-a virtual function (VF), jumbo frames must first be enabled in the physical
-function (PF). The VF MTU setting cannot be larger than the PF MTU.
-
-Generic Receive Offload, aka GRO
---------------------------------
-The driver supports the in-kernel software implementation of GRO. GRO has
-shown that by coalescing Rx traffic into larger chunks of data, CPU
-utilization can be significantly reduced when under large Rx load. GRO is an
-evolution of the previously-used LRO interface. GRO is able to coalesce
-other protocols besides TCP. It's also safe to use with configurations that
-are problematic for LRO, namely bridging and iSCSI.
-
-Data Center Bridging (DCB)
---------------------------
-NOTE:
-The kernel assumes that TC0 is available, and will disable Priority Flow
-Control (PFC) on the device if TC0 is not available. To fix this, ensure TC0 is
-enabled when setting up DCB on your switch.
-
-DCB is a configuration Quality of Service implementation in hardware. It uses
-the VLAN priority tag (802.1p) to filter traffic. That means that there are 8
-different priorities that traffic can be filtered into. It also enables
-priority flow control (802.1Qbb) which can limit or eliminate the number of
-dropped packets during network stress. Bandwidth can be allocated to each of
-these priorities, which is enforced at the hardware level (802.1Qaz).
-
-Adapter firmware implements LLDP and DCBX protocol agents as per 802.1AB and
-802.1Qaz respectively. The firmware based DCBX agent runs in willing mode only
-and can accept settings from a DCBX capable peer. Software configuration of
-DCBX parameters via dcbtool/lldptool are not supported.
-
-The ixgbe driver implements the DCB netlink interface layer to allow user-space
-to communicate with the driver and query DCB configuration for the port.
-
-ethtool
--------
-The driver utilizes the ethtool interface for driver configuration and
-diagnostics, as well as displaying statistical information. The latest ethtool
-version is required for this functionality. Download it at:
-https://www.kernel.org/pub/software/network/ethtool/
-
-FCoE
-----
-The ixgbe driver supports Fiber Channel over Ethernet (FCoE) and Data Center
-Bridging (DCB). This code has no default effect on the regular driver
-operation. Configuring DCB and FCoE is outside the scope of this README. Refer
-to http://www.open-fcoe.org/ for FCoE project information and contact
-ixgbe-eedc@lists.sourceforge.net for DCB information.
-
-MAC and VLAN anti-spoofing feature
-----------------------------------
-When a malicious driver attempts to send a spoofed packet, it is dropped by the
-hardware and not transmitted.
-
-An interrupt is sent to the PF driver notifying it of the spoof attempt. When a
-spoofed packet is detected, the PF driver will send the following message to
-the system log (displayed by the "dmesg" command)::
-
-  ixgbe ethX: ixgbe_spoof_check: n spoofed packets detected
-
-where "x" is the PF interface number; and "n" is number of spoofed packets.
-NOTE: This feature can be disabled for a specific Virtual Function (VF)::
-
-  ip link set <pf dev> vf <vf id> spoofchk {off|on}
-
-
-Known Issues/Troubleshooting
-============================
-
-Enabling SR-IOV in a 64-bit Microsoft* Windows Server* 2012/R2 guest OS
------------------------------------------------------------------------
-Linux KVM Hypervisor/VMM supports direct assignment of a PCIe device to a VM.
-This includes traditional PCIe devices, as well as SR-IOV-capable devices based
-on the Intel Ethernet Controller XL710.
-
-
-Support
-=======
-For general information, go to the Intel support website at:
-
-https://www.intel.com/support/
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-https://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on a supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net.
diff --git a/Documentation/networking/ixgbevf.rst b/Documentation/networking/ixgbevf.rst
deleted file mode 100644 (file)
index 56cde63..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0+
-
-Linux* Base Virtual Function Driver for Intel(R) 10G Ethernet
-=============================================================
-
-Intel 10 Gigabit Virtual Function Linux driver.
-Copyright(c) 1999-2018 Intel Corporation.
-
-Contents
-========
-
-- Identifying Your Adapter
-- Known Issues
-- Support
-
-This driver supports 82599, X540, X550, and X552-based virtual function devices
-that can only be activated on kernels that support SR-IOV.
-
-For questions related to hardware requirements, refer to the documentation
-supplied with your Intel adapter. All hardware requirements listed apply to use
-with Linux.
-
-
-Identifying Your Adapter
-========================
-The driver is compatible with devices based on the following:
-
-  * Intel(R) Ethernet Controller 82598
-  * Intel(R) Ethernet Controller 82599
-  * Intel(R) Ethernet Controller X520
-  * Intel(R) Ethernet Controller X540
-  * Intel(R) Ethernet Controller x550
-  * Intel(R) Ethernet Controller X552
-  * Intel(R) Ethernet Controller X553
-
-For information on how to identify your adapter, and for the latest Intel
-network drivers, refer to the Intel Support website:
-https://www.intel.com/support
-
-Known Issues/Troubleshooting
-============================
-
-SR-IOV requires the correct platform and OS support.
-
-The guest OS loading this driver must support MSI-X interrupts.
-
-This driver is only supported as a loadable module at this time. Intel is not
-supplying patches against the kernel source to allow for static linking of the
-drivers.
-
-VLANs: There is a limit of a total of 64 shared VLANs to 1 or more VFs.
-
-
-Support
-=======
-For general information, go to the Intel support website at:
-
-https://www.intel.com/support/
-
-or the Intel Wired Networking project hosted by Sourceforge at:
-
-https://sourceforge.net/projects/e1000
-
-If an issue is identified with the released source code on a supported kernel
-with a supported adapter, email the specific information related to the issue
-to e1000-devel@lists.sf.net.
index c4a54c1..58dd1c1 100644 (file)
@@ -115,7 +115,7 @@ set, be it TCPv4 (when NETIF_F_TSO is enabled) or TCPv6 (NETIF_F_TSO6).
 
  * Transmit UDP segmentation offload
 
-NETIF_F_GSO_UDP_GSO_L4 accepts a single UDP header with a payload that exceeds
+NETIF_F_GSO_UDP_L4 accepts a single UDP header with a payload that exceeds
 gso_size. On segmentation, it segments the payload on gso_size boundaries and
 replicates the network and UDP headers (fixing up the last one if less than
 gso_size).
diff --git a/Documentation/networking/netvsc.txt b/Documentation/networking/netvsc.txt
deleted file mode 100644 (file)
index 3bfa635..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-Hyper-V network driver
-======================
-
-Compatibility
-=============
-
-This driver is compatible with Windows Server 2012 R2, 2016 and
-Windows 10.
-
-Features
-========
-
-  Checksum offload
-  ----------------
-  The netvsc driver supports checksum offload as long as the
-  Hyper-V host version does. Windows Server 2016 and Azure
-  support checksum offload for TCP and UDP for both IPv4 and
-  IPv6. Windows Server 2012 only supports checksum offload for TCP.
-
-  Receive Side Scaling
-  --------------------
-  Hyper-V supports receive side scaling. For TCP & UDP, packets can
-  be distributed among available queues based on IP address and port
-  number.
-
-  For TCP & UDP, we can switch hash level between L3 and L4 by ethtool
-  command. TCP/UDP over IPv4 and v6 can be set differently. The default
-  hash level is L4. We currently only allow switching TX hash level
-  from within the guests.
-
-  On Azure, fragmented UDP packets have high loss rate with L4
-  hashing. Using L3 hashing is recommended in this case.
-
-  For example, for UDP over IPv4 on eth0:
-  To include UDP port numbers in hashing:
-        ethtool -N eth0 rx-flow-hash udp4 sdfn
-  To exclude UDP port numbers in hashing:
-        ethtool -N eth0 rx-flow-hash udp4 sd
-  To show UDP hash level:
-        ethtool -n eth0 rx-flow-hash udp4
-
-  Generic Receive Offload, aka GRO
-  --------------------------------
-  The driver supports GRO and it is enabled by default. GRO coalesces
-  like packets and significantly reduces CPU usage under heavy Rx
-  load.
-
-  Large Receive Offload (LRO), or Receive Side Coalescing (RSC)
-  -------------------------------------------------------------
-  The driver supports LRO/RSC in the vSwitch feature. It reduces the per packet
-  processing overhead by coalescing multiple TCP segments when possible. The
-  feature is enabled by default on VMs running on Windows Server 2019 and
-  later. It may be changed by ethtool command:
-       ethtool -K eth0 lro on
-       ethtool -K eth0 lro off
-
-  SR-IOV support
-  --------------
-  Hyper-V supports SR-IOV as a hardware acceleration option. If SR-IOV
-  is enabled in both the vSwitch and the guest configuration, then the
-  Virtual Function (VF) device is passed to the guest as a PCI
-  device. In this case, both a synthetic (netvsc) and VF device are
-  visible in the guest OS and both NIC's have the same MAC address.
-
-  The VF is enslaved by netvsc device.  The netvsc driver will transparently
-  switch the data path to the VF when it is available and up.
-  Network state (addresses, firewall, etc) should be applied only to the
-  netvsc device; the slave device should not be accessed directly in
-  most cases.  The exceptions are if some special queue discipline or
-  flow direction is desired, these should be applied directly to the
-  VF slave device.
-
-  Receive Buffer
-  --------------
-  Packets are received into a receive area which is created when device
-  is probed. The receive area is broken into MTU sized chunks and each may
-  contain one or more packets. The number of receive sections may be changed
-  via ethtool Rx ring parameters.
-
-  There is a similar send buffer which is used to aggregate packets for sending.
-  The send area is broken into chunks of 6144 bytes, each of section may
-  contain one or more packets. The send buffer is an optimization, the driver
-  will use slower method to handle very large packets or if the send buffer
-  area is exhausted.
diff --git a/Documentation/networking/rmnet.txt b/Documentation/networking/rmnet.txt
deleted file mode 100644 (file)
index 6b341ea..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-1. Introduction
-
-rmnet driver is used for supporting the Multiplexing and aggregation
-Protocol (MAP). This protocol is used by all recent chipsets using Qualcomm
-Technologies, Inc. modems.
-
-This driver can be used to register onto any physical network device in
-IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator.
-
-Multiplexing allows for creation of logical netdevices (rmnet devices) to
-handle multiple private data networks (PDN) like a default internet, tethering,
-multimedia messaging service (MMS) or IP media subsystem (IMS). Hardware sends
-packets with MAP headers to rmnet. Based on the multiplexer id, rmnet
-routes to the appropriate PDN after removing the MAP header.
-
-Aggregation is required to achieve high data rates. This involves hardware
-sending aggregated bunch of MAP frames. rmnet driver will de-aggregate
-these MAP frames and send them to appropriate PDN's.
-
-2. Packet format
-
-a. MAP packet (data / control)
-
-MAP header has the same endianness of the IP packet.
-
-Packet format -
-
-Bit             0             1           2-7      8 - 15           16 - 31
-Function   Command / Data   Reserved     Pad   Multiplexer ID    Payload length
-Bit            32 - x
-Function     Raw  Bytes
-
-Command (1)/ Data (0) bit value is to indicate if the packet is a MAP command
-or data packet. Control packet is used for transport level flow control. Data
-packets are standard IP packets.
-
-Reserved bits are usually zeroed out and to be ignored by receiver.
-
-Padding is number of bytes to be added for 4 byte alignment if required by
-hardware.
-
-Multiplexer ID is to indicate the PDN on which data has to be sent.
-
-Payload length includes the padding length but does not include MAP header
-length.
-
-b. MAP packet (command specific)
-
-Bit             0             1           2-7      8 - 15           16 - 31
-Function   Command         Reserved     Pad   Multiplexer ID    Payload length
-Bit          32 - 39        40 - 45    46 - 47       48 - 63
-Function   Command name    Reserved   Command Type   Reserved
-Bit          64 - 95
-Function   Transaction ID
-Bit          96 - 127
-Function   Command data
-
-Command 1 indicates disabling flow while 2 is enabling flow
-
-Command types -
-0 for MAP command request
-1 is to acknowledge the receipt of a command
-2 is for unsupported commands
-3 is for error during processing of commands
-
-c. Aggregation
-
-Aggregation is multiple MAP packets (can be data or command) delivered to
-rmnet in a single linear skb. rmnet will process the individual
-packets and either ACK the MAP command or deliver the IP packet to the
-network stack as needed
-
-MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding....
-MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad...
-
-3. Userspace configuration
-
-rmnet userspace configuration is done through netlink library librmnetctl
-and command line utility rmnetcli. Utility is hosted in codeaurora forum git.
-The driver uses rtnl_link_ops for communication.
-
-https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl
index 605e00c..89f1302 100644 (file)
@@ -1056,18 +1056,23 @@ The kernel interface functions are as follows:
 
        u32 rxrpc_kernel_check_life(struct socket *sock,
                                    struct rxrpc_call *call);
+       void rxrpc_kernel_probe_life(struct socket *sock,
+                                    struct rxrpc_call *call);
 
-     This returns a number that is updated when ACKs are received from the peer
-     (notably including PING RESPONSE ACKs which we can elicit by sending PING
-     ACKs to see if the call still exists on the server).  The caller should
-     compare the numbers of two calls to see if the call is still alive after
-     waiting for a suitable interval.
+     The first function returns a number that is updated when ACKs are received
+     from the peer (notably including PING RESPONSE ACKs which we can elicit by
+     sending PING ACKs to see if the call still exists on the server).  The
+     caller should compare the numbers of two calls to see if the call is still
+     alive after waiting for a suitable interval.
 
      This allows the caller to work out if the server is still contactable and
      if the call is still alive on the server whilst waiting for the server to
      process a client operation.
 
-     This function may transmit a PING ACK.
+     The second function causes a ping ACK to be transmitted to try to provoke
+     the peer into responding, which would then cause the value returned by the
+     first function to change.  Note that this must be called in TASK_RUNNING
+     state.
 
  (*) Get reply timestamp.
 
diff --git a/Documentation/networking/s2io.txt b/Documentation/networking/s2io.txt
deleted file mode 100644 (file)
index 0362a42..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-Release notes for Neterion's (Formerly S2io) Xframe I/II PCI-X 10GbE driver.
-
-Contents
-=======
-- 1.  Introduction
-- 2.  Identifying the adapter/interface
-- 3.  Features supported
-- 4.  Command line parameters
-- 5.  Performance suggestions
-- 6.  Available Downloads 
-
-
-1.     Introduction:
-This Linux driver supports Neterion's Xframe I PCI-X 1.0 and
-Xframe II PCI-X 2.0 adapters. It supports several features 
-such as jumbo frames, MSI/MSI-X, checksum offloads, TSO, UFO and so on.
-See below for complete list of features.
-All features are supported for both IPv4 and IPv6.
-
-2.     Identifying the adapter/interface:
-a. Insert the adapter(s) in your system.
-b. Build and load driver 
-# insmod s2io.ko
-c. View log messages
-# dmesg | tail -40
-You will see messages similar to:
-eth3: Neterion Xframe I 10GbE adapter (rev 3), Version 2.0.9.1, Intr type INTA
-eth4: Neterion Xframe II 10GbE adapter (rev 2), Version 2.0.9.1, Intr type INTA
-eth4: Device is on 64 bit 133MHz PCIX(M1) bus
-
-The above messages identify the adapter type(Xframe I/II), adapter revision,
-driver version, interface name(eth3, eth4), Interrupt type(INTA, MSI, MSI-X).
-In case of Xframe II, the PCI/PCI-X bus width and frequency are displayed
-as well.
-
-To associate an interface with a physical adapter use "ethtool -p <ethX>".
-The corresponding adapter's LED will blink multiple times.
-
-3.     Features supported:
-a. Jumbo frames. Xframe I/II supports MTU up to 9600 bytes,
-modifiable using ip command.
-
-b. Offloads. Supports checksum offload(TCP/UDP/IP) on transmit
-and receive, TSO.
-
-c. Multi-buffer receive mode. Scattering of packet across multiple
-buffers. Currently driver supports 2-buffer mode which yields
-significant performance improvement on certain platforms(SGI Altix,
-IBM xSeries).
-
-d. MSI/MSI-X. Can be enabled on platforms which support this feature
-(IA64, Xeon) resulting in noticeable performance improvement(up to 7%
-on certain platforms).
-
-e. Statistics. Comprehensive MAC-level and software statistics displayed
-using "ethtool -S" option.
-
-f. Multi-FIFO/Ring. Supports up to 8 transmit queues and receive rings,
-with multiple steering options.
-
-4.  Command line parameters
-a. tx_fifo_num
-Number of transmit queues
-Valid range: 1-8
-Default: 1
-
-b. rx_ring_num
-Number of receive rings
-Valid range: 1-8
-Default: 1
-
-c. tx_fifo_len
-Size of each transmit queue
-Valid range: Total length of all queues should not exceed 8192
-Default: 4096
-
-d. rx_ring_sz 
-Size of each receive ring(in 4K blocks)
-Valid range: Limited by memory on system
-Default: 30 
-
-e. intr_type
-Specifies interrupt type. Possible values 0(INTA), 2(MSI-X)
-Valid values: 0, 2
-Default: 2
-
-5.  Performance suggestions
-General:
-a. Set MTU to maximum(9000 for switch setup, 9600 in back-to-back configuration)
-b. Set TCP windows size to optimal value. 
-For instance, for MTU=1500 a value of 210K has been observed to result in 
-good performance.
-# sysctl -w net.ipv4.tcp_rmem="210000 210000 210000"
-# sysctl -w net.ipv4.tcp_wmem="210000 210000 210000"
-For MTU=9000, TCP window size of 10 MB is recommended.
-# sysctl -w net.ipv4.tcp_rmem="10000000 10000000 10000000"
-# sysctl -w net.ipv4.tcp_wmem="10000000 10000000 10000000"
-
-Transmit performance:
-a. By default, the driver respects BIOS settings for PCI bus parameters. 
-However, you may want to experiment with PCI bus parameters 
-max-split-transactions(MOST) and MMRBC (use setpci command). 
-A MOST value of 2 has been found optimal for Opterons and 3 for Itanium.  
-It could be different for your hardware.  
-Set MMRBC to 4K**.
-
-For example you can set 
-For opteron
-#setpci -d 17d5:* 62=1d 
-For Itanium
-#setpci -d 17d5:* 62=3d 
-
-For detailed description of the PCI registers, please see Xframe User Guide.
-
-b. Ensure Transmit Checksum offload is enabled. Use ethtool to set/verify this 
-parameter.
-c. Turn on TSO(using "ethtool -K")
-# ethtool -K <ethX> tso on
-
-Receive performance:
-a. By default, the driver respects BIOS settings for PCI bus parameters. 
-However, you may want to set PCI latency timer to 248.
-#setpci -d 17d5:* LATENCY_TIMER=f8
-For detailed description of the PCI registers, please see Xframe User Guide.
-b. Use 2-buffer mode. This results in large performance boost on
-certain platforms(eg. SGI Altix, IBM xSeries).
-c. Ensure Receive Checksum offload is enabled. Use "ethtool -K ethX" command to 
-set/verify this option.
-d. Enable NAPI feature(in kernel configuration Device Drivers ---> Network 
-device support --->  Ethernet (10000 Mbit) ---> S2IO 10Gbe Xframe NIC) to 
-bring down CPU utilization.
-
-** For AMD opteron platforms with 8131 chipset, MMRBC=1 and MOST=1 are 
-recommended as safe parameters.
-For more information, please review the AMD8131 errata at
-http://vip.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/
-26310_AMD-8131_HyperTransport_PCI-X_Tunnel_Revision_Guide_rev_3_18.pdf
-
-6. Support
-For further support please contact either your 10GbE Xframe NIC vendor (IBM, 
-HP, SGI etc.)
diff --git a/Documentation/networking/smc9.txt b/Documentation/networking/smc9.txt
deleted file mode 100644 (file)
index d1e1507..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-
-SMC 9xxxx Driver 
-Revision 0.12 
-3/5/96
-Copyright 1996  Erik Stahlman 
-Released under terms of the GNU General Public License. 
-
-This file contains the instructions and caveats for my SMC9xxx driver.  You
-should not be using the driver without reading this file.  
-
-Things to note about installation:
-
-  1. The driver should work on all kernels from 1.2.13 until 1.3.71.
-       (A kernel patch is supplied for 1.3.71 )
-
-  2. If you include this into the kernel, you might need to change some
-       options, such as for forcing IRQ.   
-
-  3.  To compile as a module, run 'make' .   
-       Make will give you the appropriate options for various kernel support.
-  4.  Loading the driver as a module :
-
-       use:   insmod smc9194.o 
-       optional parameters:
-               io=xxxx    : your base address
-               irq=xx     : your irq 
-               ifport=x   :    0 for whatever is default
-                               1 for twisted pair
-                               2 for AUI  ( or BNC on some cards )
-
-How to obtain the latest version? 
-       
-FTP:  
-       ftp://fenris.campus.vt.edu/smc9/smc9-12.tar.gz
-       ftp://sfbox.vt.edu/filebox/F/fenris/smc9/smc9-12.tar.gz 
-   
-
-Contacting me:
-    erik@mail.vt.edu
index e0d588f..f8eb77d 100644 (file)
@@ -17,7 +17,9 @@ Defined in `RFC1213 ipInReceives`_
 
 The number of packets received by the IP layer. It gets increasing at the
 beginning of ip_rcv function, always be updated together with
-IpExtInOctets. It indicates the number of aggregated segments after
+IpExtInOctets. It will be increased even if the packet is dropped
+later (e.g. due to the IP header is invalid or the checksum is wrong
+and so on).  It indicates the number of aggregated segments after
 GRO/LRO.
 
 * IpInDelivers
@@ -40,7 +42,7 @@ multicast packets, and would always be updated together with
 IpExtOutOctets.
 
 * IpExtInOctets and IpExtOutOctets
-They are linux kernel extensions, no RFC definitions. Please note,
+They are Linux kernel extensions, no RFC definitions. Please note,
 RFC1213 indeed defines ifInOctets  and ifOutOctets, but they
 are different things. The ifInOctets and ifOutOctets include the MAC
 layer header size but IpExtInOctets and IpExtOutOctets don't, they
@@ -57,6 +59,58 @@ status. They count the real frame number regardless the LRO/GRO. So
 for the same packet, you might find that IpInReceives count 1, but
 IpExtInNoECTPkts counts 2 or more.
 
+* IpInHdrErrors
+Defined in `RFC1213 ipInHdrErrors`_. It indicates the packet is
+dropped due to the IP header error. It might happen in both IP input
+and IP forward paths.
+
+.. _RFC1213 ipInHdrErrors: https://tools.ietf.org/html/rfc1213#page-27
+
+* IpInAddrErrors
+Defined in `RFC1213 ipInAddrErrors`_. It will be increased in two
+scenarios: (1) The IP address is invalid. (2) The destination IP
+address is not a local address and IP forwarding is not enabled
+
+.. _RFC1213 ipInAddrErrors: https://tools.ietf.org/html/rfc1213#page-27
+
+* IpExtInNoRoutes
+This counter means the packet is dropped when the IP stack receives a
+packet and can't find a route for it from the route table. It might
+happen when IP forwarding is enabled and the destination IP address is
+not a local address and there is no route for the destination IP
+address.
+
+* IpInUnknownProtos
+Defined in `RFC1213 ipInUnknownProtos`_. It will be increased if the
+layer 4 protocol is unsupported by kernel. If an application is using
+raw socket, kernel will always deliver the packet to the raw socket
+and this counter won't be increased.
+
+.. _RFC1213 ipInUnknownProtos: https://tools.ietf.org/html/rfc1213#page-27
+
+* IpExtInTruncatedPkts
+For IPv4 packet, it means the actual data size is smaller than the
+"Total Length" field in the IPv4 header.
+
+* IpInDiscards
+Defined in `RFC1213 ipInDiscards`_. It indicates the packet is dropped
+in the IP receiving path and due to kernel internal reasons (e.g. no
+enough memory).
+
+.. _RFC1213 ipInDiscards: https://tools.ietf.org/html/rfc1213#page-28
+
+* IpOutDiscards
+Defined in `RFC1213 ipOutDiscards`_. It indicates the packet is
+dropped in the IP sending path and due to kernel internal reasons.
+
+.. _RFC1213 ipOutDiscards: https://tools.ietf.org/html/rfc1213#page-28
+
+* IpOutNoRoutes
+Defined in `RFC1213 ipOutNoRoutes`_. It indicates the packet is
+dropped in the IP sending path and no route is found for it.
+
+.. _RFC1213 ipOutNoRoutes: https://tools.ietf.org/html/rfc1213#page-29
+
 ICMP counters
 ============
 * IcmpInMsgs and IcmpOutMsgs
@@ -174,6 +228,351 @@ IcmpMsgOutType[N]. If the errors occur in both step (2) and step (4),
 IcmpInMsgs should be less than the sum of IcmpMsgOutType[N] plus
 IcmpInErrors.
 
+General TCP counters
+==================
+* TcpInSegs
+Defined in `RFC1213 tcpInSegs`_
+
+.. _RFC1213 tcpInSegs: https://tools.ietf.org/html/rfc1213#page-48
+
+The number of packets received by the TCP layer. As mentioned in
+RFC1213, it includes the packets received in error, such as checksum
+error, invalid TCP header and so on. Only one error won't be included:
+if the layer 2 destination address is not the NIC's layer 2
+address. It might happen if the packet is a multicast or broadcast
+packet, or the NIC is in promiscuous mode. In these situations, the
+packets would be delivered to the TCP layer, but the TCP layer will discard
+these packets before increasing TcpInSegs. The TcpInSegs counter
+isn't aware of GRO. So if two packets are merged by GRO, the TcpInSegs
+counter would only increase 1.
+
+* TcpOutSegs
+Defined in `RFC1213 tcpOutSegs`_
+
+.. _RFC1213 tcpOutSegs: https://tools.ietf.org/html/rfc1213#page-48
+
+The number of packets sent by the TCP layer. As mentioned in RFC1213,
+it excludes the retransmitted packets. But it includes the SYN, ACK
+and RST packets. Doesn't like TcpInSegs, the TcpOutSegs is aware of
+GSO, so if a packet would be split to 2 by GSO, TcpOutSegs will
+increase 2.
+
+* TcpActiveOpens
+Defined in `RFC1213 tcpActiveOpens`_
+
+.. _RFC1213 tcpActiveOpens: https://tools.ietf.org/html/rfc1213#page-47
+
+It means the TCP layer sends a SYN, and come into the SYN-SENT
+state. Every time TcpActiveOpens increases 1, TcpOutSegs should always
+increase 1.
+
+* TcpPassiveOpens
+Defined in `RFC1213 tcpPassiveOpens`_
+
+.. _RFC1213 tcpPassiveOpens: https://tools.ietf.org/html/rfc1213#page-47
+
+It means the TCP layer receives a SYN, replies a SYN+ACK, come into
+the SYN-RCVD state.
+
+* TcpExtTCPRcvCoalesce
+When packets are received by the TCP layer and are not be read by the
+application, the TCP layer will try to merge them. This counter
+indicate how many packets are merged in such situation. If GRO is
+enabled, lots of packets would be merged by GRO, these packets
+wouldn't be counted to TcpExtTCPRcvCoalesce.
+
+* TcpExtTCPAutoCorking
+When sending packets, the TCP layer will try to merge small packets to
+a bigger one. This counter increase 1 for every packet merged in such
+situation. Please refer to the LWN article for more details:
+https://lwn.net/Articles/576263/
+
+* TcpExtTCPOrigDataSent
+This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
+explaination below::
+
+  TCPOrigDataSent: number of outgoing packets with original data (excluding
+  retransmission but including data-in-SYN). This counter is different from
+  TcpOutSegs because TcpOutSegs also tracks pure ACKs. TCPOrigDataSent is
+  more useful to track the TCP retransmission rate.
+
+* TCPSynRetrans
+This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
+explaination below::
+
+  TCPSynRetrans: number of SYN and SYN/ACK retransmits to break down
+  retransmissions into SYN, fast-retransmits, timeout retransmits, etc.
+
+* TCPFastOpenActiveFail
+This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
+explaination below::
+
+  TCPFastOpenActiveFail: Fast Open attempts (SYN/data) failed because
+  the remote does not accept it or the attempts timed out.
+
+.. _kernel commit f19c29e3e391: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f19c29e3e391a66a273e9afebaf01917245148cd
+
+* TcpExtListenOverflows and TcpExtListenDrops
+When kernel receives a SYN from a client, and if the TCP accept queue
+is full, kernel will drop the SYN and add 1 to TcpExtListenOverflows.
+At the same time kernel will also add 1 to TcpExtListenDrops. When a
+TCP socket is in LISTEN state, and kernel need to drop a packet,
+kernel would always add 1 to TcpExtListenDrops. So increase
+TcpExtListenOverflows would let TcpExtListenDrops increasing at the
+same time, but TcpExtListenDrops would also increase without
+TcpExtListenOverflows increasing, e.g. a memory allocation fail would
+also let TcpExtListenDrops increase.
+
+Note: The above explanation is based on kernel 4.10 or above version, on
+an old kernel, the TCP stack has different behavior when TCP accept
+queue is full. On the old kernel, TCP stack won't drop the SYN, it
+would complete the 3-way handshake. As the accept queue is full, TCP
+stack will keep the socket in the TCP half-open queue. As it is in the
+half open queue, TCP stack will send SYN+ACK on an exponential backoff
+timer, after client replies ACK, TCP stack checks whether the accept
+queue is still full, if it is not full, moves the socket to the accept
+queue, if it is full, keeps the socket in the half-open queue, at next
+time client replies ACK, this socket will get another chance to move
+to the accept queue.
+
+
+TCP Fast Open
+============
+When kernel receives a TCP packet, it has two paths to handler the
+packet, one is fast path, another is slow path. The comment in kernel
+code provides a good explanation of them, I pasted them below::
+
+  It is split into a fast path and a slow path. The fast path is
+  disabled when:
+
+  - A zero window was announced from us
+  - zero window probing
+    is only handled properly on the slow path.
+  - Out of order segments arrived.
+  - Urgent data is expected.
+  - There is no buffer space left
+  - Unexpected TCP flags/window values/header lengths are received
+    (detected by checking the TCP header against pred_flags)
+  - Data is sent in both directions. The fast path only supports pure senders
+    or pure receivers (this means either the sequence number or the ack
+    value must stay constant)
+  - Unexpected TCP option.
+
+Kernel will try to use fast path unless any of the above conditions
+are satisfied. If the packets are out of order, kernel will handle
+them in slow path, which means the performance might be not very
+good. Kernel would also come into slow path if the "Delayed ack" is
+used, because when using "Delayed ack", the data is sent in both
+directions. When the TCP window scale option is not used, kernel will
+try to enable fast path immediately when the connection comes into the
+established state, but if the TCP window scale option is used, kernel
+will disable the fast path at first, and try to enable it after kernel
+receives packets.
+
+* TcpExtTCPPureAcks and TcpExtTCPHPAcks
+If a packet set ACK flag and has no data, it is a pure ACK packet, if
+kernel handles it in the fast path, TcpExtTCPHPAcks will increase 1,
+if kernel handles it in the slow path, TcpExtTCPPureAcks will
+increase 1.
+
+* TcpExtTCPHPHits
+If a TCP packet has data (which means it is not a pure ACK packet),
+and this packet is handled in the fast path, TcpExtTCPHPHits will
+increase 1.
+
+
+TCP abort
+========
+
+
+* TcpExtTCPAbortOnData
+It means TCP layer has data in flight, but need to close the
+connection. So TCP layer sends a RST to the other side, indicate the
+connection is not closed very graceful. An easy way to increase this
+counter is using the SO_LINGER option. Please refer to the SO_LINGER
+section of the `socket man page`_:
+
+.. _socket man page: http://man7.org/linux/man-pages/man7/socket.7.html
+
+By default, when an application closes a connection, the close function
+will return immediately and kernel will try to send the in-flight data
+async. If you use the SO_LINGER option, set l_onoff to 1, and l_linger
+to a positive number, the close function won't return immediately, but
+wait for the in-flight data are acked by the other side, the max wait
+time is l_linger seconds. If set l_onoff to 1 and set l_linger to 0,
+when the application closes a connection, kernel will send a RST
+immediately and increase the TcpExtTCPAbortOnData counter.
+
+* TcpExtTCPAbortOnClose
+This counter means the application has unread data in the TCP layer when
+the application wants to close the TCP connection. In such a situation,
+kernel will send a RST to the other side of the TCP connection.
+
+* TcpExtTCPAbortOnMemory
+When an application closes a TCP connection, kernel still need to track
+the connection, let it complete the TCP disconnect process. E.g. an
+app calls the close method of a socket, kernel sends fin to the other
+side of the connection, then the app has no relationship with the
+socket any more, but kernel need to keep the socket, this socket
+becomes an orphan socket, kernel waits for the reply of the other side,
+and would come to the TIME_WAIT state finally. When kernel has no
+enough memory to keep the orphan socket, kernel would send an RST to
+the other side, and delete the socket, in such situation, kernel will
+increase 1 to the TcpExtTCPAbortOnMemory. Two conditions would trigger
+TcpExtTCPAbortOnMemory:
+
+1. the memory used by the TCP protocol is higher than the third value of
+the tcp_mem. Please refer the tcp_mem section in the `TCP man page`_:
+
+.. _TCP man page: http://man7.org/linux/man-pages/man7/tcp.7.html
+
+2. the orphan socket count is higher than net.ipv4.tcp_max_orphans
+
+
+* TcpExtTCPAbortOnTimeout
+This counter will increase when any of the TCP timers expire. In such
+situation, kernel won't send RST, just give up the connection.
+
+* TcpExtTCPAbortOnLinger
+When a TCP connection comes into FIN_WAIT_2 state, instead of waiting
+for the fin packet from the other side, kernel could send a RST and
+delete the socket immediately. This is not the default behavior of
+Linux kernel TCP stack. By configuring the TCP_LINGER2 socket option,
+you could let kernel follow this behavior.
+
+* TcpExtTCPAbortFailed
+The kernel TCP layer will send RST if the `RFC2525 2.17 section`_ is
+satisfied. If an internal error occurs during this process,
+TcpExtTCPAbortFailed will be increased.
+
+.. _RFC2525 2.17 section: https://tools.ietf.org/html/rfc2525#page-50
+
+TCP Hybrid Slow Start
+====================
+The Hybrid Slow Start algorithm is an enhancement of the traditional
+TCP congestion window Slow Start algorithm. It uses two pieces of
+information to detect whether the max bandwidth of the TCP path is
+approached. The two pieces of information are ACK train length and
+increase in packet delay. For detail information, please refer the
+`Hybrid Slow Start paper`_. Either ACK train length or packet delay
+hits a specific threshold, the congestion control algorithm will come
+into the Congestion Avoidance state. Until v4.20, two congestion
+control algorithms are using Hybrid Slow Start, they are cubic (the
+default congestion control algorithm) and cdg. Four snmp counters
+relate with the Hybrid Slow Start algorithm.
+
+.. _Hybrid Slow Start paper: https://pdfs.semanticscholar.org/25e9/ef3f03315782c7f1cbcd31b587857adae7d1.pdf
+
+* TcpExtTCPHystartTrainDetect
+How many times the ACK train length threshold is detected
+
+* TcpExtTCPHystartTrainCwnd
+The sum of CWND detected by ACK train length. Dividing this value by
+TcpExtTCPHystartTrainDetect is the average CWND which detected by the
+ACK train length.
+
+* TcpExtTCPHystartDelayDetect
+How many times the packet delay threshold is detected.
+
+* TcpExtTCPHystartDelayCwnd
+The sum of CWND detected by packet delay. Dividing this value by
+TcpExtTCPHystartDelayDetect is the average CWND which detected by the
+packet delay.
+
+TCP retransmission and congestion control
+======================================
+The TCP protocol has two retransmission mechanisms: SACK and fast
+recovery. They are exclusive with each other. When SACK is enabled,
+the kernel TCP stack would use SACK, or kernel would use fast
+recovery. The SACK is a TCP option, which is defined in `RFC2018`_,
+the fast recovery is defined in `RFC6582`_, which is also called
+'Reno'.
+
+The TCP congestion control is a big and complex topic. To understand
+the related snmp counter, we need to know the states of the congestion
+control state machine. There are 5 states: Open, Disorder, CWR,
+Recovery and Loss. For details about these states, please refer page 5
+and page 6 of this document:
+https://pdfs.semanticscholar.org/0e9c/968d09ab2e53e24c4dca5b2d67c7f7140f8e.pdf
+
+.. _RFC2018: https://tools.ietf.org/html/rfc2018
+.. _RFC6582: https://tools.ietf.org/html/rfc6582
+
+* TcpExtTCPRenoRecovery and TcpExtTCPSackRecovery
+When the congestion control comes into Recovery state, if sack is
+used, TcpExtTCPSackRecovery increases 1, if sack is not used,
+TcpExtTCPRenoRecovery increases 1. These two counters mean the TCP
+stack begins to retransmit the lost packets.
+
+* TcpExtTCPSACKReneging
+A packet was acknowledged by SACK, but the receiver has dropped this
+packet, so the sender needs to retransmit this packet. In this
+situation, the sender adds 1 to TcpExtTCPSACKReneging. A receiver
+could drop a packet which has been acknowledged by SACK, although it is
+unusual, it is allowed by the TCP protocol. The sender doesn't really
+know what happened on the receiver side. The sender just waits until
+the RTO expires for this packet, then the sender assumes this packet
+has been dropped by the receiver.
+
+* TcpExtTCPRenoReorder
+The reorder packet is detected by fast recovery. It would only be used
+if SACK is disabled. The fast recovery algorithm detects recorder by
+the duplicate ACK number. E.g., if retransmission is triggered, and
+the original retransmitted packet is not lost, it is just out of
+order, the receiver would acknowledge multiple times, one for the
+retransmitted packet, another for the arriving of the original out of
+order packet. Thus the sender would find more ACks than its
+expectation, and the sender knows out of order occurs.
+
+* TcpExtTCPTSReorder
+The reorder packet is detected when a hole is filled. E.g., assume the
+sender sends packet 1,2,3,4,5, and the receiving order is
+1,2,4,5,3. When the sender receives the ACK of packet 3 (which will
+fill the hole), two conditions will let TcpExtTCPTSReorder increase
+1: (1) if the packet 3 is not re-retransmitted yet. (2) if the packet
+3 is retransmitted but the timestamp of the packet 3's ACK is earlier
+than the retransmission timestamp.
+
+* TcpExtTCPSACKReorder
+The reorder packet detected by SACK. The SACK has two methods to
+detect reorder: (1) DSACK is received by the sender. It means the
+sender sends the same packet more than one times. And the only reason
+is the sender believes an out of order packet is lost so it sends the
+packet again. (2) Assume packet 1,2,3,4,5 are sent by the sender, and
+the sender has received SACKs for packet 2 and 5, now the sender
+receives SACK for packet 4 and the sender doesn't retransmit the
+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.
+
+
+DSACK
+=====
+The DSACK is defined in `RFC2883`_. The receiver uses DSACK to report
+duplicate packets to the sender. There are two kinds of
+duplications: (1) a packet which has been acknowledged is
+duplicate. (2) an out of order packet is duplicate. The TCP stack
+counts these two kinds of duplications on both receiver side and
+sender side.
+
+.. _RFC2883 : https://tools.ietf.org/html/rfc2883
+
+* TcpExtTCPDSACKOldSent
+The TCP stack receives a duplicate packet which has been acked, so it
+sends a DSACK to the sender.
+
+* TcpExtTCPDSACKOfoSent
+The TCP stack receives an out of order duplicate packet, so it sends a
+DSACK to the sender.
+
+* TcpExtTCPDSACKRecv
+The TCP stack receives a DSACK, which indicate an acknowledged
+duplicate packet is received.
+
+* TcpExtTCPDSACKOfoRecv
+The TCP stack receives a DSACK, which indicate an out of order
+duplciate packet is received.
+
 examples
 =======
 
@@ -220,3 +619,572 @@ and its corresponding Echo Reply packet are constructed by:
 * 48 bytes data (default value of the ping command)
 
 So the IpExtInOctets and IpExtOutOctets are 20+16+48=84.
+
+tcp 3-way handshake
+------------------
+On server side, we run::
+
+  nstatuser@nstat-b:~$ nc -lknv 0.0.0.0 9000
+  Listening on [0.0.0.0] (family 0, port 9000)
+
+On client side, we run::
+
+  nstatuser@nstat-a:~$ nc -nv 192.168.122.251 9000
+  Connection to 192.168.122.251 9000 port [tcp/*] succeeded!
+
+The server listened on tcp 9000 port, the client connected to it, they
+completed the 3-way handshake.
+
+On server side, we can find below nstat output::
+
+  nstatuser@nstat-b:~$ nstat | grep -i tcp
+  TcpPassiveOpens                 1                  0.0
+  TcpInSegs                       2                  0.0
+  TcpOutSegs                      1                  0.0
+  TcpExtTCPPureAcks               1                  0.0
+
+On client side, we can find below nstat output::
+
+  nstatuser@nstat-a:~$ nstat | grep -i tcp
+  TcpActiveOpens                  1                  0.0
+  TcpInSegs                       1                  0.0
+  TcpOutSegs                      2                  0.0
+
+When the server received the first SYN, it replied a SYN+ACK, and came into
+SYN-RCVD state, so TcpPassiveOpens increased 1. The server received
+SYN, sent SYN+ACK, received ACK, so server sent 1 packet, received 2
+packets, TcpInSegs increased 2, TcpOutSegs increased 1. The last ACK
+of the 3-way handshake is a pure ACK without data, so
+TcpExtTCPPureAcks increased 1.
+
+When the client sent SYN, the client came into the SYN-SENT state, so
+TcpActiveOpens increased 1, the client sent SYN, received SYN+ACK, sent
+ACK, so client sent 2 packets, received 1 packet, TcpInSegs increased
+1, TcpOutSegs increased 2.
+
+TCP normal traffic
+-----------------
+Run nc on server::
+
+  nstatuser@nstat-b:~$ nc -lkv 0.0.0.0 9000
+  Listening on [0.0.0.0] (family 0, port 9000)
+
+Run nc on client::
+
+  nstatuser@nstat-a:~$ nc -v nstat-b 9000
+  Connection to nstat-b 9000 port [tcp/*] succeeded!
+
+Input a string in the nc client ('hello' in our example)::
+
+  nstatuser@nstat-a:~$ nc -v nstat-b 9000
+  Connection to nstat-b 9000 port [tcp/*] succeeded!
+  hello
+
+The client side nstat output::
+
+  nstatuser@nstat-a:~$ nstat
+  #kernel
+  IpInReceives                    1                  0.0
+  IpInDelivers                    1                  0.0
+  IpOutRequests                   1                  0.0
+  TcpInSegs                       1                  0.0
+  TcpOutSegs                      1                  0.0
+  TcpExtTCPPureAcks               1                  0.0
+  TcpExtTCPOrigDataSent           1                  0.0
+  IpExtInOctets                   52                 0.0
+  IpExtOutOctets                  58                 0.0
+  IpExtInNoECTPkts                1                  0.0
+
+The server side nstat output::
+
+  nstatuser@nstat-b:~$ nstat
+  #kernel
+  IpInReceives                    1                  0.0
+  IpInDelivers                    1                  0.0
+  IpOutRequests                   1                  0.0
+  TcpInSegs                       1                  0.0
+  TcpOutSegs                      1                  0.0
+  IpExtInOctets                   58                 0.0
+  IpExtOutOctets                  52                 0.0
+  IpExtInNoECTPkts                1                  0.0
+
+Input a string in nc client side again ('world' in our exmaple)::
+
+  nstatuser@nstat-a:~$ nc -v nstat-b 9000
+  Connection to nstat-b 9000 port [tcp/*] succeeded!
+  hello
+  world
+
+Client side nstat output::
+
+  nstatuser@nstat-a:~$ nstat
+  #kernel
+  IpInReceives                    1                  0.0
+  IpInDelivers                    1                  0.0
+  IpOutRequests                   1                  0.0
+  TcpInSegs                       1                  0.0
+  TcpOutSegs                      1                  0.0
+  TcpExtTCPHPAcks                 1                  0.0
+  TcpExtTCPOrigDataSent           1                  0.0
+  IpExtInOctets                   52                 0.0
+  IpExtOutOctets                  58                 0.0
+  IpExtInNoECTPkts                1                  0.0
+
+
+Server side nstat output::
+
+  nstatuser@nstat-b:~$ nstat
+  #kernel
+  IpInReceives                    1                  0.0
+  IpInDelivers                    1                  0.0
+  IpOutRequests                   1                  0.0
+  TcpInSegs                       1                  0.0
+  TcpOutSegs                      1                  0.0
+  TcpExtTCPHPHits                 1                  0.0
+  IpExtInOctets                   58                 0.0
+  IpExtOutOctets                  52                 0.0
+  IpExtInNoECTPkts                1                  0.0
+
+Compare the first client-side nstat and the second client-side nstat,
+we could find one difference: the first one had a 'TcpExtTCPPureAcks',
+but the second one had a 'TcpExtTCPHPAcks'. The first server-side
+nstat and the second server-side nstat had a difference too: the
+second server-side nstat had a TcpExtTCPHPHits, but the first
+server-side nstat didn't have it. The network traffic patterns were
+exactly the same: the client sent a packet to the server, the server
+replied an ACK. But kernel handled them in different ways. When the
+TCP window scale option is not used, kernel will try to enable fast
+path immediately when the connection comes into the established state,
+but if the TCP window scale option is used, kernel will disable the
+fast path at first, and try to enable it after kerenl receives
+packets. We could use the 'ss' command to verify whether the window
+scale option is used. e.g. run below command on either server or
+client::
+
+  nstatuser@nstat-a:~$ ss -o state established -i '( dport = :9000 or sport = :9000 )
+  Netid    Recv-Q     Send-Q            Local Address:Port             Peer Address:Port
+  tcp      0          0               192.168.122.250:40654         192.168.122.251:9000
+             ts sack cubic wscale:7,7 rto:204 rtt:0.98/0.49 mss:1448 pmtu:1500 rcvmss:536 advmss:1448 cwnd:10 bytes_acked:1 segs_out:2 segs_in:1 send 118.2Mbps lastsnd:46572 lastrcv:46572 lastack:46572 pacing_rate 236.4Mbps rcv_space:29200 rcv_ssthresh:29200 minrtt:0.98
+
+The 'wscale:7,7' means both server and client set the window scale
+option to 7. Now we could explain the nstat output in our test:
+
+In the first nstat output of client side, the client sent a packet, server
+reply an ACK, when kernel handled this ACK, the fast path was not
+enabled, so the ACK was counted into 'TcpExtTCPPureAcks'.
+
+In the second nstat output of client side, the client sent a packet again,
+and received another ACK from the server, in this time, the fast path is
+enabled, and the ACK was qualified for fast path, so it was handled by
+the fast path, so this ACK was counted into TcpExtTCPHPAcks.
+
+In the first nstat output of server side, fast path was not enabled,
+so there was no 'TcpExtTCPHPHits'.
+
+In the second nstat output of server side, the fast path was enabled,
+and the packet received from client qualified for fast path, so it
+was counted into 'TcpExtTCPHPHits'.
+
+TcpExtTCPAbortOnClose
+--------------------
+On the server side, we run below python script::
+
+  import socket
+  import time
+
+  port = 9000
+
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.bind(('0.0.0.0', port))
+  s.listen(1)
+  sock, addr = s.accept()
+  while True:
+      time.sleep(9999999)
+
+This python script listen on 9000 port, but doesn't read anything from
+the connection.
+
+On the client side, we send the string "hello" by nc::
+
+  nstatuser@nstat-a:~$ echo "hello" | nc nstat-b 9000
+
+Then, we come back to the server side, the server has received the "hello"
+packet, and the TCP layer has acked this packet, but the application didn't
+read it yet. We type Ctrl-C to terminate the server script. Then we
+could find TcpExtTCPAbortOnClose increased 1 on the server side::
+
+  nstatuser@nstat-b:~$ nstat | grep -i abort
+  TcpExtTCPAbortOnClose           1                  0.0
+
+If we run tcpdump on the server side, we could find the server sent a
+RST after we type Ctrl-C.
+
+TcpExtTCPAbortOnMemory and TcpExtTCPAbortOnTimeout
+-----------------------------------------------
+Below is an example which let the orphan socket count be higher than
+net.ipv4.tcp_max_orphans.
+Change tcp_max_orphans to a smaller value on client::
+
+  sudo bash -c "echo 10 > /proc/sys/net/ipv4/tcp_max_orphans"
+
+Client code (create 64 connection to server)::
+
+  nstatuser@nstat-a:~$ cat client_orphan.py
+  import socket
+  import time
+
+  server = 'nstat-b' # server address
+  port = 9000
+
+  count = 64
+
+  connection_list = []
+
+  for i in range(64):
+      s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+      s.connect((server, port))
+      connection_list.append(s)
+      print("connection_count: %d" % len(connection_list))
+
+  while True:
+      time.sleep(99999)
+
+Server code (accept 64 connection from client)::
+
+  nstatuser@nstat-b:~$ cat server_orphan.py
+  import socket
+  import time
+
+  port = 9000
+  count = 64
+
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.bind(('0.0.0.0', port))
+  s.listen(count)
+  connection_list = []
+  while True:
+      sock, addr = s.accept()
+      connection_list.append((sock, addr))
+      print("connection_count: %d" % len(connection_list))
+
+Run the python scripts on server and client.
+
+On server::
+
+  python3 server_orphan.py
+
+On client::
+
+  python3 client_orphan.py
+
+Run iptables on server::
+
+  sudo iptables -A INPUT -i ens3 -p tcp --destination-port 9000 -j DROP
+
+Type Ctrl-C on client, stop client_orphan.py.
+
+Check TcpExtTCPAbortOnMemory on client::
+
+  nstatuser@nstat-a:~$ nstat | grep -i abort
+  TcpExtTCPAbortOnMemory          54                 0.0
+
+Check orphane socket count on client::
+
+  nstatuser@nstat-a:~$ ss -s
+  Total: 131 (kernel 0)
+  TCP:   14 (estab 1, closed 0, orphaned 10, synrecv 0, timewait 0/0), ports 0
+
+  Transport Total     IP        IPv6
+  *         0         -         -
+  RAW       1         0         1
+  UDP       1         1         0
+  TCP       14        13        1
+  INET      16        14        2
+  FRAG      0         0         0
+
+The explanation of the test: after run server_orphan.py and
+client_orphan.py, we set up 64 connections between server and
+client. Run the iptables command, the server will drop all packets from
+the client, type Ctrl-C on client_orphan.py, the system of the client
+would try to close these connections, and before they are closed
+gracefully, these connections became orphan sockets. As the iptables
+of the server blocked packets from the client, the server won't receive fin
+from the client, so all connection on clients would be stuck on FIN_WAIT_1
+stage, so they will keep as orphan sockets until timeout. We have echo
+10 to /proc/sys/net/ipv4/tcp_max_orphans, so the client system would
+only keep 10 orphan sockets, for all other orphan sockets, the client
+system sent RST for them and delete them. We have 64 connections, so
+the 'ss -s' command shows the system has 10 orphan sockets, and the
+value of TcpExtTCPAbortOnMemory was 54.
+
+An additional explanation about orphan socket count: You could find the
+exactly orphan socket count by the 'ss -s' command, but when kernel
+decide whither increases TcpExtTCPAbortOnMemory and sends RST, kernel
+doesn't always check the exactly orphan socket count. For increasing
+performance, kernel checks an approximate count firstly, if the
+approximate count is more than tcp_max_orphans, kernel checks the
+exact count again. So if the approximate count is less than
+tcp_max_orphans, but exactly count is more than tcp_max_orphans, you
+would find TcpExtTCPAbortOnMemory is not increased at all. If
+tcp_max_orphans is large enough, it won't occur, but if you decrease
+tcp_max_orphans to a small value like our test, you might find this
+issue. So in our test, the client set up 64 connections although the
+tcp_max_orphans is 10. If the client only set up 11 connections, we
+can't find the change of TcpExtTCPAbortOnMemory.
+
+Continue the previous test, we wait for several minutes. Because of the
+iptables on the server blocked the traffic, the server wouldn't receive
+fin, and all the client's orphan sockets would timeout on the
+FIN_WAIT_1 state finally. So we wait for a few minutes, we could find
+10 timeout on the client::
+
+  nstatuser@nstat-a:~$ nstat | grep -i abort
+  TcpExtTCPAbortOnTimeout         10                 0.0
+
+TcpExtTCPAbortOnLinger
+---------------------
+The server side code::
+
+  nstatuser@nstat-b:~$ cat server_linger.py
+  import socket
+  import time
+
+  port = 9000
+
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.bind(('0.0.0.0', port))
+  s.listen(1)
+  sock, addr = s.accept()
+  while True:
+      time.sleep(9999999)
+
+The client side code::
+
+  nstatuser@nstat-a:~$ cat client_linger.py
+  import socket
+  import struct
+
+  server = 'nstat-b' # server address
+  port = 9000
+
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 10))
+  s.setsockopt(socket.SOL_TCP, socket.TCP_LINGER2, struct.pack('i', -1))
+  s.connect((server, port))
+  s.close()
+
+Run server_linger.py on server::
+
+  nstatuser@nstat-b:~$ python3 server_linger.py
+
+Run client_linger.py on client::
+
+  nstatuser@nstat-a:~$ python3 client_linger.py
+
+After run client_linger.py, check the output of nstat::
+
+  nstatuser@nstat-a:~$ nstat | grep -i abort
+  TcpExtTCPAbortOnLinger          1                  0.0
+
+TcpExtTCPRcvCoalesce
+-------------------
+On the server, we run a program which listen on TCP port 9000, but
+doesn't read any data::
+
+  import socket
+  import time
+  port = 9000
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.bind(('0.0.0.0', port))
+  s.listen(1)
+  sock, addr = s.accept()
+  while True:
+      time.sleep(9999999)
+
+Save the above code as server_coalesce.py, and run::
+
+  python3 server_coalesce.py
+
+On the client, save below code as client_coalesce.py::
+
+  import socket
+  server = 'nstat-b'
+  port = 9000
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.connect((server, port))
+
+Run::
+
+  nstatuser@nstat-a:~$ python3 -i client_coalesce.py
+
+We use '-i' to come into the interactive mode, then a packet::
+
+  >>> s.send(b'foo')
+  3
+
+Send a packet again::
+
+  >>> s.send(b'bar')
+  3
+
+On the server, run nstat::
+
+  ubuntu@nstat-b:~$ nstat
+  #kernel
+  IpInReceives                    2                  0.0
+  IpInDelivers                    2                  0.0
+  IpOutRequests                   2                  0.0
+  TcpInSegs                       2                  0.0
+  TcpOutSegs                      2                  0.0
+  TcpExtTCPRcvCoalesce            1                  0.0
+  IpExtInOctets                   110                0.0
+  IpExtOutOctets                  104                0.0
+  IpExtInNoECTPkts                2                  0.0
+
+The client sent two packets, server didn't read any data. When
+the second packet arrived at server, the first packet was still in
+the receiving queue. So the TCP layer merged the two packets, and we
+could find the TcpExtTCPRcvCoalesce increased 1.
+
+TcpExtListenOverflows and TcpExtListenDrops
+----------------------------------------
+On server, run the nc command, listen on port 9000::
+
+  nstatuser@nstat-b:~$ nc -lkv 0.0.0.0 9000
+  Listening on [0.0.0.0] (family 0, port 9000)
+
+On client, run 3 nc commands in different terminals::
+
+  nstatuser@nstat-a:~$ nc -v nstat-b 9000
+  Connection to nstat-b 9000 port [tcp/*] succeeded!
+
+The nc command only accepts 1 connection, and the accept queue length
+is 1. On current linux implementation, set queue length to n means the
+actual queue length is n+1. Now we create 3 connections, 1 is accepted
+by nc, 2 in accepted queue, so the accept queue is full.
+
+Before running the 4th nc, we clean the nstat history on the server::
+
+  nstatuser@nstat-b:~$ nstat -n
+
+Run the 4th nc on the client::
+
+  nstatuser@nstat-a:~$ nc -v nstat-b 9000
+
+If the nc server is running on kernel 4.10 or higher version, you
+won't see the "Connection to ... succeeded!" string, because kernel
+will drop the SYN if the accept queue is full. If the nc client is running
+on an old kernel, you would see that the connection is succeeded,
+because kernel would complete the 3 way handshake and keep the socket
+on half open queue. I did the test on kernel 4.15. Below is the nstat
+on the server::
+
+  nstatuser@nstat-b:~$ nstat
+  #kernel
+  IpInReceives                    4                  0.0
+  IpInDelivers                    4                  0.0
+  TcpInSegs                       4                  0.0
+  TcpExtListenOverflows           4                  0.0
+  TcpExtListenDrops               4                  0.0
+  IpExtInOctets                   240                0.0
+  IpExtInNoECTPkts                4                  0.0
+
+Both TcpExtListenOverflows and TcpExtListenDrops were 4. If the time
+between the 4th nc and the nstat was longer, the value of
+TcpExtListenOverflows and TcpExtListenDrops would be larger, because
+the SYN of the 4th nc was dropped, the client was retrying.
+
+IpInAddrErrors, IpExtInNoRoutes and IpOutNoRoutes
+----------------------------------------------
+server A IP address: 192.168.122.250
+server B IP address: 192.168.122.251
+Prepare on server A, add a route to server B::
+
+  $ sudo ip route add 8.8.8.8/32 via 192.168.122.251
+
+Prepare on server B, disable send_redirects for all interfaces::
+
+  $ sudo sysctl -w net.ipv4.conf.all.send_redirects=0
+  $ sudo sysctl -w net.ipv4.conf.ens3.send_redirects=0
+  $ sudo sysctl -w net.ipv4.conf.lo.send_redirects=0
+  $ sudo sysctl -w net.ipv4.conf.default.send_redirects=0
+
+We want to let sever A send a packet to 8.8.8.8, and route the packet
+to server B. When server B receives such packet, it might send a ICMP
+Redirect message to server A, set send_redirects to 0 will disable
+this behavior.
+
+First, generate InAddrErrors. On server B, we disable IP forwarding::
+
+  $ sudo sysctl -w net.ipv4.conf.all.forwarding=0
+
+On server A, we send packets to 8.8.8.8::
+
+  $ nc -v 8.8.8.8 53
+
+On server B, we check the output of nstat::
+
+  $ nstat
+  #kernel
+  IpInReceives                    3                  0.0
+  IpInAddrErrors                  3                  0.0
+  IpExtInOctets                   180                0.0
+  IpExtInNoECTPkts                3                  0.0
+
+As we have let server A route 8.8.8.8 to server B, and we disabled IP
+forwarding on server B, Server A sent packets to server B, then server B
+dropped packets and increased IpInAddrErrors. As the nc command would
+re-send the SYN packet if it didn't receive a SYN+ACK, we could find
+multiple IpInAddrErrors.
+
+Second, generate IpExtInNoRoutes. On server B, we enable IP
+forwarding::
+
+  $ sudo sysctl -w net.ipv4.conf.all.forwarding=1
+
+Check the route table of server B and remove the default route::
+
+  $ ip route show
+  default via 192.168.122.1 dev ens3 proto static
+  192.168.122.0/24 dev ens3 proto kernel scope link src 192.168.122.251
+  $ sudo ip route delete default via 192.168.122.1 dev ens3 proto static
+
+On server A, we contact 8.8.8.8 again::
+
+  $ nc -v 8.8.8.8 53
+  nc: connect to 8.8.8.8 port 53 (tcp) failed: Network is unreachable
+
+On server B, run nstat::
+
+  $ nstat
+  #kernel
+  IpInReceives                    1                  0.0
+  IpOutRequests                   1                  0.0
+  IcmpOutMsgs                     1                  0.0
+  IcmpOutDestUnreachs             1                  0.0
+  IcmpMsgOutType3                 1                  0.0
+  IpExtInNoRoutes                 1                  0.0
+  IpExtInOctets                   60                 0.0
+  IpExtOutOctets                  88                 0.0
+  IpExtInNoECTPkts                1                  0.0
+
+We enabled IP forwarding on server B, when server B received a packet
+which destination IP address is 8.8.8.8, server B will try to forward
+this packet. We have deleted the default route, there was no route for
+8.8.8.8, so server B increase IpExtInNoRoutes and sent the "ICMP
+Destination Unreachable" message to server A.
+
+Third, generate IpOutNoRoutes. Run ping command on server B::
+
+  $ ping -c 1 8.8.8.8
+  connect: Network is unreachable
+
+Run nstat on server B::
+
+  $ nstat
+  #kernel
+  IpOutNoRoutes                   1                  0.0
+
+We have deleted the default route on server B. Server B couldn't find
+a route for the 8.8.8.8 IP address, so server B increased
+IpOutNoRoutes.
diff --git a/Documentation/networking/spider_net.txt b/Documentation/networking/spider_net.txt
deleted file mode 100644 (file)
index b0b75f8..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-
-            The Spidernet Device Driver
-            ===========================
-
-Written by Linas Vepstas <linas@austin.ibm.com>
-
-Version of 7 June 2007
-
-Abstract
-========
-This document sketches the structure of portions of the spidernet
-device driver in the Linux kernel tree. The spidernet is a gigabit
-ethernet device built into the Toshiba southbridge commonly used
-in the SONY Playstation 3 and the IBM QS20 Cell blade.
-
-The Structure of the RX Ring.
-=============================
-The receive (RX) ring is a circular linked list of RX descriptors,
-together with three pointers into the ring that are used to manage its
-contents.
-
-The elements of the ring are called "descriptors" or "descrs"; they
-describe the received data. This includes a pointer to a buffer
-containing the received data, the buffer size, and various status bits.
-
-There are three primary states that a descriptor can be in: "empty",
-"full" and "not-in-use".  An "empty" or "ready" descriptor is ready
-to receive data from the hardware. A "full" descriptor has data in it,
-and is waiting to be emptied and processed by the OS. A "not-in-use"
-descriptor is neither empty or full; it is simply not ready. It may
-not even have a data buffer in it, or is otherwise unusable.
-
-During normal operation, on device startup, the OS (specifically, the
-spidernet device driver) allocates a set of RX descriptors and RX
-buffers. These are all marked "empty", ready to receive data. This
-ring is handed off to the hardware, which sequentially fills in the
-buffers, and marks them "full". The OS follows up, taking the full
-buffers, processing them, and re-marking them empty.
-
-This filling and emptying is managed by three pointers, the "head"
-and "tail" pointers, managed by the OS, and a hardware current
-descriptor pointer (GDACTDPA). The GDACTDPA points at the descr
-currently being filled. When this descr is filled, the hardware
-marks it full, and advances the GDACTDPA by one.  Thus, when there is
-flowing RX traffic, every descr behind it should be marked "full",
-and everything in front of it should be "empty".  If the hardware
-discovers that the current descr is not empty, it will signal an
-interrupt, and halt processing.
-
-The tail pointer tails or trails the hardware pointer. When the
-hardware is ahead, the tail pointer will be pointing at a "full"
-descr. The OS will process this descr, and then mark it "not-in-use",
-and advance the tail pointer.  Thus, when there is flowing RX traffic,
-all of the descrs in front of the tail pointer should be "full", and
-all of those behind it should be "not-in-use". When RX traffic is not
-flowing, then the tail pointer can catch up to the hardware pointer.
-The OS will then note that the current tail is "empty", and halt
-processing.
-
-The head pointer (somewhat mis-named) follows after the tail pointer.
-When traffic is flowing, then the head pointer will be pointing at
-a "not-in-use" descr. The OS will perform various housekeeping duties
-on this descr. This includes allocating a new data buffer and
-dma-mapping it so as to make it visible to the hardware. The OS will
-then mark the descr as "empty", ready to receive data. Thus, when there
-is flowing RX traffic, everything in front of the head pointer should
-be "not-in-use", and everything behind it should be "empty". If no
-RX traffic is flowing, then the head pointer can catch up to the tail
-pointer, at which point the OS will notice that the head descr is
-"empty", and it will halt processing.
-
-Thus, in an idle system, the GDACTDPA, tail and head pointers will
-all be pointing at the same descr, which should be "empty". All of the
-other descrs in the ring should be "empty" as well.
-
-The show_rx_chain() routine will print out the locations of the
-GDACTDPA, tail and head pointers. It will also summarize the contents
-of the ring, starting at the tail pointer, and listing the status
-of the descrs that follow.
-
-A typical example of the output, for a nearly idle system, might be
-
-net eth1: Total number of descrs=256
-net eth1: Chain tail located at descr=20
-net eth1: Chain head is at 20
-net eth1: HW curr desc (GDACTDPA) is at 21
-net eth1: Have 1 descrs with stat=x40800101
-net eth1: HW next desc (GDACNEXTDA) is at 22
-net eth1: Last 255 descrs with stat=xa0800000
-
-In the above, the hardware has filled in one descr, number 20. Both
-head and tail are pointing at 20, because it has not yet been emptied.
-Meanwhile, hw is pointing at 21, which is free.
-
-The "Have nnn decrs" refers to the descr starting at the tail: in this
-case, nnn=1 descr, starting at descr 20. The "Last nnn descrs" refers
-to all of the rest of the descrs, from the last status change. The "nnn"
-is a count of how many descrs have exactly the same status.
-
-The status x4... corresponds to "full" and status xa... corresponds
-to "empty". The actual value printed is RXCOMST_A.
-
-In the device driver source code, a different set of names are
-used for these same concepts, so that
-
-"empty" == SPIDER_NET_DESCR_CARDOWNED == 0xa
-"full"  == SPIDER_NET_DESCR_FRAME_END == 0x4
-"not in use" == SPIDER_NET_DESCR_NOT_IN_USE == 0xf
-
-
-The RX RAM full bug/feature
-===========================
-
-As long as the OS can empty out the RX buffers at a rate faster than
-the hardware can fill them, there is no problem. If, for some reason,
-the OS fails to empty the RX ring fast enough, the hardware GDACTDPA
-pointer will catch up to the head, notice the not-empty condition,
-ad stop. However, RX packets may still continue arriving on the wire.
-The spidernet chip can save some limited number of these in local RAM.
-When this local ram fills up, the spider chip will issue an interrupt
-indicating this (GHIINT0STS will show ERRINT, and the GRMFLLINT bit
-will be set in GHIINT1STS).  When the RX ram full condition occurs,
-a certain bug/feature is triggered that has to be specially handled.
-This section describes the special handling for this condition.
-
-When the OS finally has a chance to run, it will empty out the RX ring.
-In particular, it will clear the descriptor on which the hardware had
-stopped. However, once the hardware has decided that a certain
-descriptor is invalid, it will not restart at that descriptor; instead
-it will restart at the next descr. This potentially will lead to a
-deadlock condition, as the tail pointer will be pointing at this descr,
-which, from the OS point of view, is empty; the OS will be waiting for
-this descr to be filled. However, the hardware has skipped this descr,
-and is filling the next descrs. Since the OS doesn't see this, there
-is a potential deadlock, with the OS waiting for one descr to fill,
-while the hardware is waiting for a different set of descrs to become
-empty.
-
-A call to show_rx_chain() at this point indicates the nature of the
-problem. A typical print when the network is hung shows the following:
-
-net eth1: Spider RX RAM full, incoming packets might be discarded!
-net eth1: Total number of descrs=256
-net eth1: Chain tail located at descr=255
-net eth1: Chain head is at 255
-net eth1: HW curr desc (GDACTDPA) is at 0
-net eth1: Have 1 descrs with stat=xa0800000
-net eth1: HW next desc (GDACNEXTDA) is at 1
-net eth1: Have 127 descrs with stat=x40800101
-net eth1: Have 1 descrs with stat=x40800001
-net eth1: Have 126 descrs with stat=x40800101
-net eth1: Last 1 descrs with stat=xa0800000
-
-Both the tail and head pointers are pointing at descr 255, which is
-marked xa... which is "empty". Thus, from the OS point of view, there
-is nothing to be done. In particular, there is the implicit assumption
-that everything in front of the "empty" descr must surely also be empty,
-as explained in the last section. The OS is waiting for descr 255 to
-become non-empty, which, in this case, will never happen.
-
-The HW pointer is at descr 0. This descr is marked 0x4.. or "full".
-Since its already full, the hardware can do nothing more, and thus has
-halted processing. Notice that descrs 0 through 254 are all marked
-"full", while descr 254 and 255 are empty. (The "Last 1 descrs" is
-descr 254, since tail was at 255.) Thus, the system is deadlocked,
-and there can be no forward progress; the OS thinks there's nothing
-to do, and the hardware has nowhere to put incoming data.
-
-This bug/feature is worked around with the spider_net_resync_head_ptr()
-routine. When the driver receives RX interrupts, but an examination
-of the RX chain seems to show it is empty, then it is probable that
-the hardware has skipped a descr or two (sometimes dozens under heavy
-network conditions). The spider_net_resync_head_ptr() subroutine will
-search the ring for the next full descr, and the driver will resume
-operations there.  Since this will leave "holes" in the ring, there
-is also a spider_net_resync_tail_ptr() that will skip over such holes.
-
-As of this writing, the spider_net_resync() strategy seems to work very
-well, even under heavy network loads.
-
-
-The TX ring
-===========
-The TX ring uses a low-watermark interrupt scheme to make sure that
-the TX queue is appropriately serviced for large packet sizes.
-
-For packet sizes greater than about 1KBytes, the kernel can fill
-the TX ring quicker than the device can drain it. Once the ring
-is full, the netdev is stopped. When there is room in the ring,
-the netdev needs to be reawakened, so that more TX packets are placed
-in the ring. The hardware can empty the ring about four times per jiffy,
-so its not appropriate to wait for the poll routine to refill, since
-the poll routine runs only once per jiffy.  The low-watermark mechanism
-marks a descr about 1/4th of the way from the bottom of the queue, so
-that an interrupt is generated when the descr is processed. This
-interrupt wakes up the netdev, which can then refill the queue.
-For large packets, this mechanism generates a relatively small number
-of interrupts, about 1K/sec. For smaller packets, this will drop to zero
-interrupts, as the hardware can empty the queue faster than the kernel
-can fill it.
-
-
- ======= END OF DOCUMENT ========
-
diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt
deleted file mode 100644 (file)
index 2bb0707..0000000
+++ /dev/null
@@ -1,401 +0,0 @@
-       STMicroelectronics 10/100/1000 Synopsys Ethernet driver
-
-Copyright (C) 2007-2015  STMicroelectronics Ltd
-Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
-
-This is the driver for the MAC 10/100/1000 on-chip Ethernet controllers
-(Synopsys IP blocks).
-
-Currently this network device driver is for all STi embedded MAC/GMAC
-(i.e. 7xxx/5xxx SoCs), SPEAr (arm), Loongson1B (mips) and XLINX XC2V3000
-FF1152AMT0221 D1215994A VIRTEX FPGA board.
-
-DWC Ether MAC 10/100/1000 Universal version 3.70a (and older) and DWC Ether
-MAC 10/100 Universal version 4.0 have been used for developing this driver.
-
-This driver supports both the platform bus and PCI.
-
-Please, for more information also visit: www.stlinux.com
-
-1) Kernel Configuration
-The kernel configuration option is STMMAC_ETH:
- Device Drivers ---> Network device support ---> Ethernet (1000 Mbit) --->
- STMicroelectronics 10/100/1000 Ethernet driver (STMMAC_ETH)
-
-CONFIG_STMMAC_PLATFORM: is to enable the platform driver.
-CONFIG_STMMAC_PCI: is to enable the pci driver.
-
-2) Driver parameters list:
-       debug: message level (0: no output, 16: all);
-       phyaddr: to manually provide the physical address to the PHY device;
-       buf_sz: DMA buffer size;
-       tc: control the HW FIFO threshold;
-       watchdog: transmit timeout (in milliseconds);
-       flow_ctrl: Flow control ability [on/off];
-       pause: Flow Control Pause Time;
-       eee_timer: tx EEE timer;
-       chain_mode: select chain mode instead of ring.
-
-3) Command line options
-Driver parameters can be also passed in command line by using:
-       stmmaceth=watchdog:100,chain_mode=1
-
-4) Driver information and notes
-
-4.1) Transmit process
-The xmit method is invoked when the kernel needs to transmit a packet; it sets
-the descriptors in the ring and informs the DMA engine, that there is a packet
-ready to be transmitted.
-By default, the driver sets the NETIF_F_SG bit in the features field of the
-net_device structure, enabling the scatter-gather feature. This is true on
-chips and configurations where the checksum can be done in hardware.
-Once the controller has finished transmitting the packet, timer will be
-scheduled to release the transmit resources.
-
-4.2) Receive process
-When one or more packets are received, an interrupt happens. The interrupts
-are not queued, so the driver has to scan all the descriptors in the ring during
-the receive process.
-This is based on NAPI, so the interrupt handler signals only if there is work
-to be done, and it exits.
-Then the poll method will be scheduled at some future point.
-The incoming packets are stored, by the DMA, in a list of pre-allocated socket
-buffers in order to avoid the memcpy (zero-copy).
-
-4.3) Interrupt mitigation
-The driver is able to mitigate the number of its DMA interrupts
-using NAPI for the reception on chips older than the 3.50.
-New chips have an HW RX-Watchdog used for this mitigation.
-Mitigation parameters can be tuned by ethtool.
-
-4.4) WOL
-Wake up on Lan feature through Magic and Unicast frames are supported for the
-GMAC core.
-
-4.5) DMA descriptors
-Driver handles both normal and alternate descriptors. The latter has been only
-tested on DWC Ether MAC 10/100/1000 Universal version 3.41a and later.
-
-STMMAC supports DMA descriptor to operate both in dual buffer (RING)
-and linked-list(CHAINED) mode. In RING each descriptor points to two
-data buffer pointers whereas in CHAINED mode they point to only one data
-buffer pointer. RING mode is the default.
-
-In CHAINED mode each descriptor will have pointer to next descriptor in
-the list, hence creating the explicit chaining in the descriptor itself,
-whereas such explicit chaining is not possible in RING mode.
-
-4.5.1) Extended descriptors
-The extended descriptors give us information about the Ethernet payload
-when it is carrying PTP packets or TCP/UDP/ICMP over IP.
-These are not available on GMAC Synopsys chips older than the 3.50.
-At probe time the driver will decide if these can be actually used.
-This support also is mandatory for PTPv2 because the extra descriptors
-are used for saving the hardware timestamps and Extended Status.
-
-4.6) Ethtool support
-Ethtool is supported.
-
-For example, driver statistics (including RMON), internal errors can be taken
-using:
-  # ethtool -S ethX
-command
-
-4.7) Jumbo and Segmentation Offloading
-Jumbo frames are supported and tested for the GMAC.
-The GSO has been also added but it's performed in software.
-LRO is not supported.
-
-4.8) Physical
-The driver is compatible with Physical Abstraction Layer to be connected with
-PHY and GPHY devices.
-
-4.9) Platform information
-Several information can be passed through the platform and device-tree.
-
-struct plat_stmmacenet_data {
-       char *phy_bus_name;
-       int bus_id;
-       int phy_addr;
-       int interface;
-       struct stmmac_mdio_bus_data *mdio_bus_data;
-       struct stmmac_dma_cfg *dma_cfg;
-       int clk_csr;
-       int has_gmac;
-       int enh_desc;
-       int tx_coe;
-       int rx_coe;
-       int bugged_jumbo;
-       int pmt;
-       int force_sf_dma_mode;
-       int force_thresh_dma_mode;
-       int riwt_off;
-       int max_speed;
-       int maxmtu;
-       void (*fix_mac_speed)(void *priv, unsigned int speed);
-       void (*bus_setup)(void __iomem *ioaddr);
-       int (*init)(struct platform_device *pdev, void *priv);
-       void (*exit)(struct platform_device *pdev, void *priv);
-       void *bsp_priv;
-       int has_gmac4;
-       bool tso_en;
-};
-
-Where:
- o phy_bus_name: phy bus name to attach to the stmmac.
- o bus_id: bus identifier.
- o phy_addr: the physical address can be passed from the platform.
-           If it is set to -1 the driver will automatically
-           detect it at run-time by probing all the 32 addresses.
- o interface: PHY device's interface.
- o mdio_bus_data: specific platform fields for the MDIO bus.
- o dma_cfg: internal DMA parameters
-   o pbl: the Programmable Burst Length is maximum number of beats to
-       be transferred in one DMA transaction.
-       GMAC also enables the 4xPBL by default. (8xPBL for GMAC 3.50 and newer)
-   o txpbl/rxpbl: GMAC and newer supports independent DMA pbl for tx/rx.
-   o pblx8: Enable 8xPBL (4xPBL for core rev < 3.50). Enabled by default.
-   o fixed_burst/mixed_burst/aal
- o clk_csr: fixed CSR Clock range selection.
- o has_gmac: uses the GMAC core.
- o enh_desc: if sets the MAC will use the enhanced descriptor structure.
- o tx_coe: core is able to perform the tx csum in HW.
- o rx_coe: the supports three check sum offloading engine types:
-          type_1, type_2 (full csum) and no RX coe.
- o bugged_jumbo: some HWs are not able to perform the csum in HW for
-               over-sized frames due to limited buffer sizes.
-               Setting this flag the csum will be done in SW on
-               JUMBO frames.
- o pmt: core has the embedded power module (optional).
- o force_sf_dma_mode: force DMA to use the Store and Forward mode
-                    instead of the Threshold.
- o force_thresh_dma_mode: force DMA to use the Threshold mode other than
-                    the Store and Forward mode.
- o riwt_off: force to disable the RX watchdog feature and switch to NAPI mode.
- o fix_mac_speed: this callback is used for modifying some syscfg registers
-                (on ST SoCs) according to the link speed negotiated by the
-                physical layer .
- o bus_setup: perform HW setup of the bus. For example, on some ST platforms
-            this field is used to configure the AMBA  bridge to generate more
-            efficient STBus traffic.
- o init/exit: callbacks used for calling a custom initialization;
-            this is sometime necessary on some platforms (e.g. ST boxes)
-            where the HW needs to have set some PIO lines or system cfg
-            registers.  init/exit callbacks should not use or modify
-            platform data.
- o bsp_priv: another private pointer.
- o has_gmac4: uses GMAC4 core.
- o tso_en: Enables TSO (TCP Segmentation Offload) feature.
-
-For MDIO bus The we have:
-
- struct stmmac_mdio_bus_data {
-       int (*phy_reset)(void *priv);
-       unsigned int phy_mask;
-       int *irqs;
-       int probed_phy_irq;
- };
-
-Where:
- o phy_reset: hook to reset the phy device attached to the bus.
- o phy_mask: phy mask passed when register the MDIO bus within the driver.
- o irqs: list of IRQs, one per PHY.
- o probed_phy_irq: if irqs is NULL, use this for probed PHY.
-
-For DMA engine we have the following internal fields that should be
-tuned according to the HW capabilities.
-
-struct stmmac_dma_cfg {
-       int pbl;
-       int txpbl;
-       int rxpbl;
-       bool pblx8;
-       int fixed_burst;
-       int mixed_burst;
-       bool aal;
-};
-
-Where:
- o pbl: Programmable Burst Length (tx and rx)
- o txpbl: Transmit Programmable Burst Length. Only for GMAC and newer.
-        If set, DMA tx will use this value rather than pbl.
- o rxpbl: Receive Programmable Burst Length. Only for GMAC and newer.
-        If set, DMA rx will use this value rather than pbl.
- o pblx8: Enable 8xPBL (4xPBL for core rev < 3.50). Enabled by default.
- o fixed_burst: program the DMA to use the fixed burst mode
- o mixed_burst: program the DMA to use the mixed burst mode
- o aal: Address-Aligned Beats
-
----
-
-Below an example how the structures above are using on ST platforms.
-
- static struct plat_stmmacenet_data stxYYY_ethernet_platform_data = {
-       .has_gmac = 0,
-       .enh_desc = 0,
-       .fix_mac_speed = stxYYY_ethernet_fix_mac_speed,
-                               |
-                               |-> to write an internal syscfg
-                               |   on this platform when the
-                               |   link speed changes from 10 to
-                               |   100 and viceversa
-       .init = &stmmac_claim_resource,
-                               |
-                               |-> On ST SoC this calls own "PAD"
-                               |   manager framework to claim
-                               |   all the resources necessary
-                               |   (GPIO ...). The .custom_cfg field
-                               |   is used to pass a custom config.
-};
-
-Below the usage of the stmmac_mdio_bus_data: on this SoC, in fact,
-there are two MAC cores: one MAC is for MDIO Bus/PHY emulation
-with fixed_link support.
-
-static struct stmmac_mdio_bus_data stmmac1_mdio_bus = {
-       .phy_reset = phy_reset;
-               |
-               |-> function to provide the phy_reset on this board
-       .phy_mask = 0,
-};
-
-static struct fixed_phy_status stmmac0_fixed_phy_status = {
-       .link = 1,
-       .speed = 100,
-       .duplex = 1,
-};
-
-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);
-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).
-
-Note that, starting from new chips, where it is available the HW capability
-register, many configurations are discovered at run-time for example to
-understand if EEE, HW csum, PTP, enhanced descriptor etc are actually
-available. As strategy adopted in this driver, the information from the HW
-capability register can replace what has been passed from the platform.
-
-4.10) Device-tree support.
-
-Please see the following document:
-       Documentation/devicetree/bindings/net/stmmac.txt
-
-4.11) This is a summary of the content of some relevant files:
- o stmmac_main.c: implements the main network device driver;
- o stmmac_mdio.c: provides MDIO functions;
- o stmmac_pci: this is the PCI driver;
- o stmmac_platform.c: this the platform driver (OF supported);
- o stmmac_ethtool.c: implements the ethtool support;
- o stmmac.h: private driver structure;
- o common.h: common definitions and VFTs;
- o mmc_core.c/mmc.h: Management MAC Counters;
- o stmmac_hwtstamp.c: HW timestamp support for PTP;
- o stmmac_ptp.c: PTP 1588 clock;
- o stmmac_pcs.h: Physical Coding Sublayer common implementation;
- o dwmac-<XXX>.c: these are for the platform glue-logic file; e.g. dwmac-sti.c
-   for STMicroelectronics SoCs.
-
-- GMAC 3.x
- o descs.h: descriptor structure definitions;
- o dwmac1000_core.c: dwmac GiGa core functions;
- o dwmac1000_dma.c: dma functions for the GMAC chip;
- o dwmac1000.h: specific header file for the dwmac GiGa;
- o dwmac100_core: dwmac 100 core code;
- o dwmac100_dma.c: dma functions for the dwmac 100 chip;
- o dwmac1000.h: specific header file for the MAC;
- o dwmac_lib.c: generic DMA functions;
- o enh_desc.c: functions for handling enhanced descriptors;
- o norm_desc.c: functions for handling normal descriptors;
- o chain_mode.c/ring_mode.c:: functions to manage RING/CHAINED modes;
-
-- GMAC4.x generation
- o dwmac4_core.c: dwmac GMAC4.x core functions;
- o dwmac4_desc.c: functions for handling GMAC4.x descriptors;
- o dwmac4_descs.h: descriptor definitions;
- o dwmac4_dma.c: dma functions for the GMAC4.x chip;
- o dwmac4_dma.h: dma definitions for the GMAC4.x chip;
- o dwmac4.h: core definitions for the GMAC4.x chip;
- o dwmac4_lib.c: generic GMAC4.x functions;
-
-4.12) TSO support (GMAC4.x)
-
-TSO (Tcp Segmentation Offload) feature is supported by GMAC 4.x chip family.
-When a packet is sent through TCP protocol, the TCP stack ensures that
-the SKB provided to the low level driver (stmmac in our case) matches with
-the maximum frame len (IP header + TCP header + payload <= 1500 bytes (for
-MTU set to 1500)). It means that if an application using TCP want to send a
-packet which will have a length (after adding headers) > 1514 the packet
-will be split in several TCP packets: The data payload is split and headers
-(TCP/IP ..) are added. It is done by software.
-
-When TSO is enabled, the TCP stack doesn't care about the maximum frame
-length and provide SKB packet to stmmac as it is. The GMAC IP will have to
-perform the segmentation by it self to match with maximum frame length.
-
-This feature can be enabled in device tree through "snps,tso" entry.
-
-5) Debug Information
-
-The driver exports many information i.e. internal statistics,
-debug information, MAC and DMA registers etc.
-
-These can be read in several ways depending on the
-type of the information actually needed.
-
-For example a user can be use the ethtool support
-to get statistics: e.g. using: ethtool -S ethX
-(that shows the Management counters (MMC) if supported)
-or sees the MAC/DMA registers: e.g. using: ethtool -d ethX
-
-Compiling the Kernel with CONFIG_DEBUG_FS the driver will export the following
-debugfs entries:
-
-/sys/kernel/debug/stmmaceth/descriptors_status
-  To show the DMA TX/RX descriptor rings
-
-Developer can also use the "debug" module parameter to get further debug
-information (please see: NETIF Msg Level).
-
-6) Energy Efficient Ethernet
-
-Energy Efficient Ethernet(EEE) enables IEEE 802.3 MAC sublayer along
-with a family of Physical layer to operate in the Low power Idle(LPI)
-mode. The EEE mode supports the IEEE 802.3 MAC operation at 100Mbps,
-1000Mbps & 10Gbps.
-
-The LPI mode allows power saving by switching off parts of the
-communication device functionality when there is no data to be
-transmitted & received. The system on both the side of the link can
-disable some functionalities & save power during the period of low-link
-utilization. The MAC controls whether the system should enter or exit
-the LPI mode & communicate this to PHY.
-
-As soon as the interface is opened, the driver verifies if the EEE can
-be supported. This is done by looking at both the DMA HW capability
-register and the PHY devices MCD registers.
-To enter in Tx LPI mode the driver needs to have a software timer
-that enable and disable the LPI mode when there is nothing to be
-transmitted.
-
-7) Precision Time Protocol (PTP)
-The driver supports the IEEE 1588-2002, Precision Time Protocol (PTP),
-which enables precise synchronization of clocks in measurement and
-control systems implemented with technologies such as network
-communication.
-
-In addition to the basic timestamp features mentioned in IEEE 1588-2002
-Timestamps, new GMAC cores support the advanced timestamp features.
-IEEE 1588-2008 that can be enabled when configure the Kernel.
-
-8) SGMII/RGMII support
-New GMAC devices provide own way to manage RGMII/SGMII.
-This information is available at run-time by looking at the
-HW capability register. This means that the stmmac can manage
-auto-negotiation and link status w/o using the PHYLIB stuff.
-In fact, the HW provides a subset of extended registers to
-restart the ANE, verify Full/Half duplex mode and Speed.
-Thanks to these registers, it is possible to look at the
-Auto-negotiated Link Parter Ability.
diff --git a/Documentation/networking/ti-cpsw.txt b/Documentation/networking/ti-cpsw.txt
deleted file mode 100644 (file)
index d4d4c07..0000000
+++ /dev/null
@@ -1,541 +0,0 @@
-* Texas Instruments CPSW ethernet driver
-
-Multiqueue & CBS & MQPRIO
-=====================================================================
-=====================================================================
-
-The cpsw has 3 CBS shapers for each external ports. This document
-describes MQPRIO and CBS Qdisc offload configuration for cpsw driver
-based on examples. It potentially can be used in audio video bridging
-(AVB) and time sensitive networking (TSN).
-
-The following examples were tested on AM572x EVM and BBB boards.
-
-Test setup
-==========
-
-Under consideration two examples with AM572x EVM running cpsw driver
-in dual_emac mode.
-
-Several prerequisites:
-- TX queues must be rated starting from txq0 that has highest priority
-- Traffic classes are used starting from 0, that has highest priority
-- CBS shapers should be used with rated queues
-- The bandwidth for CBS shapers has to be set a little bit more then
-  potential incoming rate, thus, rate of all incoming tx queues has
-  to be a little less
-- Real rates can differ, due to discreetness
-- Map skb-priority to txq is not enough, also skb-priority to l2 prio
-  map has to be created with ip or vconfig tool
-- Any l2/socket prio (0 - 7) for classes can be used, but for
-  simplicity default values are used: 3 and 2
-- only 2 classes tested: A and B, but checked and can work with more,
-  maximum allowed 4, but only for 3 rate can be set.
-
-Test setup for examples
-=======================
-                                    +-------------------------------+
-                                    |--+                            |
-                                    |  |      Workstation0          |
-                                    |E |  MAC 18:03:73:66:87:42     |
-+-----------------------------+  +--|t |                            |
-|                    | 1  | E |  |  |h |./tsn_listener -d \         |
-|  Target board:     | 0  | t |--+  |0 | 18:03:73:66:87:42 -i eth0 \|
-|  AM572x EVM        | 0  | h |     |  | -s 1500                    |
-|                    | 0  | 0 |     |--+                            |
-|  Only 2 classes:   |Mb  +---|     +-------------------------------+
-|  class A, class B  |        |
-|                    |    +---|     +-------------------------------+
-|                    | 1  | E |     |--+                            |
-|                    | 0  | t |     |  |      Workstation1          |
-|                    | 0  | h |--+  |E |  MAC 20:cf:30:85:7d:fd     |
-|                    |Mb  | 1 |  +--|t |                            |
-+-----------------------------+     |h |./tsn_listener -d \         |
-                                    |0 | 20:cf:30:85:7d:fd -i eth0 \|
-                                    |  | -s 1500                    |
-                                    |--+                            |
-                                    +-------------------------------+
-
-*********************************************************************
-*********************************************************************
-*********************************************************************
-Example 1: One port tx AVB configuration scheme for target board
-----------------------------------------------------------------------
-(prints and scheme for AM572x evm, applicable for single port boards)
-
-tc - traffic class
-txq - transmit queue
-p - priority
-f - fifo (cpsw fifo)
-S - shaper configured
-
-+------------------------------------------------------------------+ u
-| +---------------+  +---------------+  +------+ +------+          | s
-| |               |  |               |  |      | |      |          | e
-| | App 1         |  | App 2         |  | Apps | | Apps |          | r
-| | Class A       |  | Class B       |  | Rest | | Rest |          |
-| | Eth0          |  | Eth0          |  | Eth0 | | Eth1 |          | s
-| | VLAN100       |  | VLAN100       |  |   |  | |   |  |          | p
-| | 40 Mb/s       |  | 20 Mb/s       |  |   |  | |   |  |          | a
-| | SO_PRIORITY=3 |  | SO_PRIORITY=2 |  |   |  | |   |  |          | c
-| |   |           |  |   |           |  |   |  | |   |  |          | e
-| +---|-----------+  +---|-----------+  +---|--+ +---|--+          |
-+-----|------------------|------------------|--------|-------------+
-    +-+     +------------+                  |        |
-    |       |             +-----------------+     +--+
-    |       |             |                       |
-+---|-------|-------------|-----------------------|----------------+
-| +----+ +----+ +----+ +----+                   +----+             |
-| | p3 | | p2 | | p1 | | p0 |                   | p0 |             | k
-| \    / \    / \    / \    /                   \    /             | e
-|  \  /   \  /   \  /   \  /                     \  /              | r
-|   \/     \/     \/     \/                       \/               | n
-|    |     |             |                        |                | e
-|    |     |       +-----+                        |                | l
-|    |     |       |                              |                |
-| +----+ +----+ +----+                          +----+             | s
-| |tc0 | |tc1 | |tc2 |                          |tc0 |             | p
-| \    / \    / \    /                          \    /             | a
-|  \  /   \  /   \  /                            \  /              | c
-|   \/     \/     \/                              \/               | e
-|   |      |       +-----+                        |                |
-|   |      |       |     |                        |                |
-|   |      |       |     |                        |                |
-|   |      |       |     |                        |                |
-| +----+ +----+ +----+ +----+                   +----+             |
-| |txq0| |txq1| |txq2| |txq3|                   |txq4|             |
-| \    / \    / \    / \    /                   \    /             |
-|  \  /   \  /   \  /   \  /                     \  /              |
-|   \/     \/     \/     \/                       \/               |
-| +-|------|------|------|--+                  +--|--------------+ |
-| | |      |      |      |  | Eth0.100         |  |     Eth1     | |
-+---|------|------|------|------------------------|----------------+
-    |      |      |      |                        |
-    p      p      p      p                        |
-    3      2      0-1, 4-7  <- L2 priority        |
-    |      |      |      |                        |
-    |      |      |      |                        |
-+---|------|------|------|------------------------|----------------+
-|   |      |      |      |             |----------+                |
-| +----+ +----+ +----+ +----+       +----+                         |
-| |dma7| |dma6| |dma5| |dma4|       |dma3|                         |
-| \    / \    / \    / \    /       \    /                         | c
-|  \S /   \S /   \  /   \  /         \  /                          | p
-|   \/     \/     \/     \/           \/                           | s
-|   |      |      | +-----            |                            | w
-|   |      |      | |                 |                            |
-|   |      |      | |                 |                            | d
-| +----+ +----+ +----+p            p+----+                         | r
-| |    | |    | |    |o            o|    |                         | i
-| | f3 | | f2 | | f0 |r            r| f0 |                         | v
-| |tc0 | |tc1 | |tc2 |t            t|tc0 |                         | e
-| \CBS / \CBS / \CBS /1            2\CBS /                         | r
-|  \S /   \S /   \  /                \  /                          |
-|   \/     \/     \/                  \/                           |
-+------------------------------------------------------------------+
-========================================Eth==========================>
-
-1)
-// Add 4 tx queues, for interface Eth0, and 1 tx queue for Eth1
-$ ethtool -L eth0 rx 1 tx 5
-rx unmodified, ignoring
-
-2)
-// Check if num of queues is set correctly:
-$ ethtool -l eth0
-Channel parameters for eth0:
-Pre-set maximums:
-RX:             8
-TX:             8
-Other:          0
-Combined:       0
-Current hardware settings:
-RX:             1
-TX:             5
-Other:          0
-Combined:       0
-
-3)
-// TX queues must be rated starting from 0, so set bws for tx0 and tx1
-// Set rates 40 and 20 Mb/s appropriately.
-// Pay attention, real speed can differ a bit due to discreetness.
-// Leave last 2 tx queues not rated.
-$ echo 40 > /sys/class/net/eth0/queues/tx-0/tx_maxrate
-$ echo 20 > /sys/class/net/eth0/queues/tx-1/tx_maxrate
-
-4)
-// Check maximum rate of tx (cpdma) queues:
-$ cat /sys/class/net/eth0/queues/tx-*/tx_maxrate
-40
-20
-0
-0
-0
-
-5)
-// Map skb->priority to traffic class:
-// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
-// Map traffic class to transmit queue:
-// tc0 -> txq0, tc1 -> txq1, tc2 -> (txq2, txq3)
-$ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \
-map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@2 hw 1
-
-5a)
-// As two interface sharing same set of tx queues, assign all traffic
-// coming to interface Eth1 to separate queue in order to not mix it
-// with traffic from interface Eth0, so use separate txq to send
-// packets to Eth1, so all prio -> tc0 and tc0 -> txq4
-// Here hw 0, so here still default configuration for eth1 in hw
-$ tc qdisc replace dev eth1 handle 100: parent root mqprio num_tc 1 \
-map 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 queues 1@4 hw 0
-
-6)
-// Check classes settings
-$ tc -g class show dev eth0
-+---(100:ffe2) mqprio
-|    +---(100:3) mqprio
-|    +---(100:4) mqprio
-|
-+---(100:ffe1) mqprio
-|    +---(100:2) mqprio
-|
-+---(100:ffe0) mqprio
-     +---(100:1) mqprio
-
-$ tc -g class show dev eth1
-+---(100:ffe0) mqprio
-     +---(100:5) mqprio
-
-7)
-// Set rate for class A - 41 Mbit (tc0, txq0) using CBS Qdisc
-// Set it +1 Mb for reserve (important!)
-// here only idle slope is important, others arg are ignored
-// Pay attention, real speed can differ a bit due to discreetness
-$ tc qdisc add dev eth0 parent 100:1 cbs locredit -1438 \
-hicredit 62 sendslope -959000 idleslope 41000 offload 1
-net eth0: set FIFO3 bw = 50
-
-8)
-// Set rate for class B - 21 Mbit (tc1, txq1) using CBS Qdisc:
-// Set it +1 Mb for reserve (important!)
-$ tc qdisc add dev eth0 parent 100:2 cbs locredit -1468 \
-hicredit 65 sendslope -979000 idleslope 21000 offload 1
-net eth0: set FIFO2 bw = 30
-
-9)
-// Create vlan 100 to map sk->priority to vlan qos
-$ ip link add link eth0 name eth0.100 type vlan id 100
-8021q: 802.1Q VLAN Support v1.8
-8021q: adding VLAN 0 to HW filter on device eth0
-8021q: adding VLAN 0 to HW filter on device eth1
-net eth0: Adding vlanid 100 to vlan filter
-
-10)
-// Map skb->priority to L2 prio, 1 to 1
-$ ip link set eth0.100 type vlan \
-egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-11)
-// Check egress map for vlan 100
-$ cat /proc/net/vlan/eth0.100
-[...]
-INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
-EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-12)
-// Run your appropriate tools with socket option "SO_PRIORITY"
-// to 3 for class A and/or to 2 for class B
-// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
-./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500&
-./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500&
-
-13)
-// run your listener on workstation (should be in same vlan)
-// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
-./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39000 kbps
-
-14)
-// Restore default configuration if needed
-$ ip link del eth0.100
-$ tc qdisc del dev eth1 root
-$ tc qdisc del dev eth0 root
-net eth0: Prev FIFO2 is shaped
-net eth0: set FIFO3 bw = 0
-net eth0: set FIFO2 bw = 0
-$ ethtool -L eth0 rx 1 tx 1
-
-*********************************************************************
-*********************************************************************
-*********************************************************************
-Example 2: Two port tx AVB configuration scheme for target board
-----------------------------------------------------------------------
-(prints and scheme for AM572x evm, for dual emac boards only)
-
-+------------------------------------------------------------------+ u
-| +----------+  +----------+  +------+  +----------+  +----------+ | s
-| |          |  |          |  |      |  |          |  |          | | e
-| | App 1    |  | App 2    |  | Apps |  | App 3    |  | App 4    | | r
-| | Class A  |  | Class B  |  | Rest |  | Class B  |  | Class A  | |
-| | Eth0     |  | Eth0     |  |   |  |  | Eth1     |  | Eth1     | | s
-| | VLAN100  |  | VLAN100  |  |   |  |  | VLAN100  |  | VLAN100  | | p
-| | 40 Mb/s  |  | 20 Mb/s  |  |   |  |  | 10 Mb/s  |  | 30 Mb/s  | | a
-| | SO_PRI=3 |  | SO_PRI=2 |  |   |  |  | SO_PRI=3 |  | SO_PRI=2 | | c
-| |   |      |  |   |      |  |   |  |  |   |      |  |   |      | | e
-| +---|------+  +---|------+  +---|--+  +---|------+  +---|------+ |
-+-----|-------------|-------------|---------|-------------|--------+
-    +-+     +-------+             |         +----------+  +----+
-    |       |             +-------+------+             |       |
-    |       |             |              |             |       |
-+---|-------|-------------|--------------|-------------|-------|---+
-| +----+ +----+ +----+ +----+          +----+ +----+ +----+ +----+ |
-| | p3 | | p2 | | p1 | | p0 |          | p0 | | p1 | | p2 | | p3 | | k
-| \    / \    / \    / \    /          \    / \    / \    / \    / | e
-|  \  /   \  /   \  /   \  /            \  /   \  /   \  /   \  /  | r
-|   \/     \/     \/     \/              \/     \/     \/     \/   | n
-|   |      |             |                |             |      |   | e
-|   |      |        +----+                +----+        |      |   | l
-|   |      |        |                          |        |      |   |
-| +----+ +----+ +----+                        +----+ +----+ +----+ | s
-| |tc0 | |tc1 | |tc2 |                        |tc2 | |tc1 | |tc0 | | p
-| \    / \    / \    /                        \    / \    / \    / | a
-|  \  /   \  /   \  /                          \  /   \  /   \  /  | c
-|   \/     \/     \/                            \/     \/     \/   | e
-|   |      |       +-----+                +-----+      |       |   |
-|   |      |       |     |                |     |      |       |   |
-|   |      |       |     |                |     |      |       |   |
-|   |      |       |     |    E      E    |     |      |       |   |
-| +----+ +----+ +----+ +----+ t      t +----+ +----+ +----+ +----+ |
-| |txq0| |txq1| |txq4| |txq5| h      h |txq6| |txq7| |txq3| |txq2| |
-| \    / \    / \    / \    / 0      1 \    / \    / \    / \    / |
-|  \  /   \  /   \  /   \  /  .      .  \  /   \  /   \  /   \  /  |
-|   \/     \/     \/     \/   1      1   \/     \/     \/     \/   |
-| +-|------|------|------|--+ 0      0 +-|------|------|------|--+ |
-| | |      |      |      |  | 0      0 | |      |      |      |  | |
-+---|------|------|------|---------------|------|------|------|----+
-    |      |      |      |               |      |      |      |
-    p      p      p      p               p      p      p      p
-    3      2      0-1, 4-7   <-L2 pri->  0-1, 4-7      2      3
-    |      |      |      |               |      |      |      |
-    |      |      |      |               |      |      |      |
-+---|------|------|------|---------------|------|------|------|----+
-|   |      |      |      |               |      |      |      |    |
-| +----+ +----+ +----+ +----+          +----+ +----+ +----+ +----+ |
-| |dma7| |dma6| |dma3| |dma2|          |dma1| |dma0| |dma4| |dma5| |
-| \    / \    / \    / \    /          \    / \    / \    / \    / | c
-|  \S /   \S /   \  /   \  /            \  /   \  /   \S /   \S /  | p
-|   \/     \/     \/     \/              \/     \/     \/     \/   | s
-|   |      |      | +-----                |      |      |      |   | w
-|   |      |      | |                     +----+ |      |      |   |
-|   |      |      | |                          | |      |      |   | d
-| +----+ +----+ +----+p                      p+----+ +----+ +----+ | r
-| |    | |    | |    |o                      o|    | |    | |    | | i
-| | f3 | | f2 | | f0 |r        CPSW          r| f3 | | f2 | | f0 | | v
-| |tc0 | |tc1 | |tc2 |t                      t|tc0 | |tc1 | |tc2 | | e
-| \CBS / \CBS / \CBS /1                      2\CBS / \CBS / \CBS / | r
-|  \S /   \S /   \  /                          \S /   \S /   \  /  |
-|   \/     \/     \/                            \/     \/     \/   |
-+------------------------------------------------------------------+
-========================================Eth==========================>
-
-1)
-// Add 8 tx queues, for interface Eth0, but they are common, so are accessed
-// by two interfaces Eth0 and Eth1.
-$ ethtool -L eth1 rx 1 tx 8
-rx unmodified, ignoring
-
-2)
-// Check if num of queues is set correctly:
-$ ethtool -l eth0
-Channel parameters for eth0:
-Pre-set maximums:
-RX:             8
-TX:             8
-Other:          0
-Combined:       0
-Current hardware settings:
-RX:             1
-TX:             8
-Other:          0
-Combined:       0
-
-3)
-// TX queues must be rated starting from 0, so set bws for tx0 and tx1 for Eth0
-// and for tx2 and tx3 for Eth1. That is, rates 40 and 20 Mb/s appropriately
-// for Eth0 and 30 and 10 Mb/s for Eth1.
-// Real speed can differ a bit due to discreetness
-// Leave last 4 tx queues as not rated
-$ echo 40 > /sys/class/net/eth0/queues/tx-0/tx_maxrate
-$ echo 20 > /sys/class/net/eth0/queues/tx-1/tx_maxrate
-$ echo 30 > /sys/class/net/eth1/queues/tx-2/tx_maxrate
-$ echo 10 > /sys/class/net/eth1/queues/tx-3/tx_maxrate
-
-4)
-// Check maximum rate of tx (cpdma) queues:
-$ cat /sys/class/net/eth0/queues/tx-*/tx_maxrate
-40
-20
-30
-10
-0
-0
-0
-0
-
-5)
-// Map skb->priority to traffic class for Eth0:
-// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
-// Map traffic class to transmit queue:
-// tc0 -> txq0, tc1 -> txq1, tc2 -> (txq4, txq5)
-$ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \
-map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@4 hw 1
-
-6)
-// Check classes settings
-$ tc -g class show dev eth0
-+---(100:ffe2) mqprio
-|    +---(100:5) mqprio
-|    +---(100:6) mqprio
-|
-+---(100:ffe1) mqprio
-|    +---(100:2) mqprio
-|
-+---(100:ffe0) mqprio
-     +---(100:1) mqprio
-
-7)
-// Set rate for class A - 41 Mbit (tc0, txq0) using CBS Qdisc for Eth0
-// here only idle slope is important, others ignored
-// Real speed can differ a bit due to discreetness
-$ tc qdisc add dev eth0 parent 100:1 cbs locredit -1470 \
-hicredit 62 sendslope -959000 idleslope 41000 offload 1
-net eth0: set FIFO3 bw = 50
-
-8)
-// Set rate for class B - 21 Mbit (tc1, txq1) using CBS Qdisc for Eth0
-$ tc qdisc add dev eth0 parent 100:2 cbs locredit -1470 \
-hicredit 65 sendslope -979000 idleslope 21000 offload 1
-net eth0: set FIFO2 bw = 30
-
-9)
-// Create vlan 100 to map sk->priority to vlan qos for Eth0
-$ ip link add link eth0 name eth0.100 type vlan id 100
-net eth0: Adding vlanid 100 to vlan filter
-
-10)
-// Map skb->priority to L2 prio for Eth0.100, one to one
-$ ip link set eth0.100 type vlan \
-egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-11)
-// Check egress map for vlan 100
-$ cat /proc/net/vlan/eth0.100
-[...]
-INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
-EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-12)
-// Map skb->priority to traffic class for Eth1:
-// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
-// Map traffic class to transmit queue:
-// tc0 -> txq2, tc1 -> txq3, tc2 -> (txq6, txq7)
-$ tc qdisc replace dev eth1 handle 100: parent root mqprio num_tc 3 \
-map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@2 1@3 2@6 hw 1
-
-13)
-// Check classes settings
-$ tc -g class show dev eth1
-+---(100:ffe2) mqprio
-|    +---(100:7) mqprio
-|    +---(100:8) mqprio
-|
-+---(100:ffe1) mqprio
-|    +---(100:4) mqprio
-|
-+---(100:ffe0) mqprio
-     +---(100:3) mqprio
-
-14)
-// Set rate for class A - 31 Mbit (tc0, txq2) using CBS Qdisc for Eth1
-// here only idle slope is important, others ignored, but calculated
-// for interface speed - 100Mb for eth1 port.
-// Set it +1 Mb for reserve (important!)
-$ tc qdisc add dev eth1 parent 100:3 cbs locredit -1035 \
-hicredit 465 sendslope -69000 idleslope 31000 offload 1
-net eth1: set FIFO3 bw = 31
-
-15)
-// Set rate for class B - 11 Mbit (tc1, txq3) using CBS Qdisc for Eth1
-// Set it +1 Mb for reserve (important!)
-$ tc qdisc add dev eth1 parent 100:4 cbs locredit -1335 \
-hicredit 405 sendslope -89000 idleslope 11000 offload 1
-net eth1: set FIFO2 bw = 11
-
-16)
-// Create vlan 100 to map sk->priority to vlan qos for Eth1
-$ ip link add link eth1 name eth1.100 type vlan id 100
-net eth1: Adding vlanid 100 to vlan filter
-
-17)
-// Map skb->priority to L2 prio for Eth1.100, one to one
-$ ip link set eth1.100 type vlan \
-egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-18)
-// Check egress map for vlan 100
-$ cat /proc/net/vlan/eth1.100
-[...]
-INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
-EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-19)
-// Run appropriate tools with socket option "SO_PRIORITY" to 3
-// for class A and to 2 for class B. For both interfaces
-./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500&
-./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500&
-./tsn_talker -d 20:cf:30:85:7d:fd -i eth1.100 -p2 -s 1500&
-./tsn_talker -d 20:cf:30:85:7d:fd -i eth1.100 -p3 -s 1500&
-
-20)
-// run your listener on workstation (should be in same vlan)
-// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
-./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39000 kbps
-
-21)
-// Restore default configuration if needed
-$ ip link del eth1.100
-$ ip link del eth0.100
-$ tc qdisc del dev eth1 root
-net eth1: Prev FIFO2 is shaped
-net eth1: set FIFO3 bw = 0
-net eth1: set FIFO2 bw = 0
-$ tc qdisc del dev eth0 root
-net eth0: Prev FIFO2 is shaped
-net eth0: set FIFO3 bw = 0
-net eth0: set FIFO2 bw = 0
-$ ethtool -L eth0 rx 1 tx 1
diff --git a/Documentation/networking/tlan.txt b/Documentation/networking/tlan.txt
deleted file mode 100644 (file)
index 34550df..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-(C) 1997-1998 Caldera, Inc.
-(C) 1998 James Banks
-(C) 1999-2001 Torben Mathiasen <tmm@image.dk, torben.mathiasen@compaq.com>
-
-For driver information/updates visit http://www.compaq.com
-
-
-TLAN driver for Linux, version 1.14a
-README
-
-
-I.  Supported Devices.
-
-    Only PCI devices will work with this driver.
-
-    Supported:
-    Vendor ID  Device ID       Name
-    0e11       ae32            Compaq Netelligent 10/100 TX PCI UTP
-    0e11       ae34            Compaq Netelligent 10 T PCI UTP
-    0e11       ae35            Compaq Integrated NetFlex 3/P
-    0e11       ae40            Compaq Netelligent Dual 10/100 TX PCI UTP
-    0e11       ae43            Compaq Netelligent Integrated 10/100 TX UTP
-    0e11       b011            Compaq Netelligent 10/100 TX Embedded UTP
-    0e11       b012            Compaq Netelligent 10 T/2 PCI UTP/Coax
-    0e11       b030            Compaq Netelligent 10/100 TX UTP
-    0e11       f130            Compaq NetFlex 3/P
-    0e11       f150            Compaq NetFlex 3/P
-    108d       0012            Olicom OC-2325  
-    108d       0013            Olicom OC-2183
-    108d       0014            Olicom OC-2326  
-
-
-    Caveats:
-    
-    I am not sure if 100BaseTX daughterboards (for those cards which
-    support such things) will work.  I haven't had any solid evidence
-    either way.
-
-    However, if a card supports 100BaseTx without requiring an add
-    on daughterboard, it should work with 100BaseTx.
-
-    The "Netelligent 10 T/2 PCI UTP/Coax" (b012) device is untested,
-    but I do not expect any problems.
-    
-
-II.   Driver Options
-       1. You can append debug=x to the end of the insmod line to get
-           debug messages, where x is a bit field where the bits mean
-          the following:
-          
-          0x01         Turn on general debugging messages.
-          0x02         Turn on receive debugging messages.
-          0x04         Turn on transmit debugging messages.
-          0x08         Turn on list debugging messages.
-
-       2. You can append aui=1 to the end of the insmod line to cause
-           the adapter to use the AUI interface instead of the 10 Base T
-           interface.  This is also what to do if you want to use the BNC
-          connector on a TLAN based device.  (Setting this option on a
-          device that does not have an AUI/BNC connector will probably
-          cause it to not function correctly.)
-
-       3. You can set duplex=1 to force half duplex, and duplex=2 to
-          force full duplex.
-
-       4. You can set speed=10 to force 10Mbs operation, and speed=100
-          to force 100Mbs operation. (I'm not sure what will happen
-          if a card which only supports 10Mbs is forced into 100Mbs
-          mode.)
-
-       5. You have to use speed=X duplex=Y together now. If you just
-          do "insmod tlan.o speed=100" the driver will do Auto-Neg.
-          To force a 10Mbps Half-Duplex link do "insmod tlan.o speed=10 
-          duplex=1".
-
-       6. If the driver is built into the kernel, you can use the 3rd
-          and 4th parameters to set aui and debug respectively.  For
-          example:
-
-          ether=0,0,0x1,0x7,eth0
-
-          This sets aui to 0x1 and debug to 0x7, assuming eth0 is a
-          supported TLAN device.
-
-          The bits in the third byte are assigned as follows:
-
-               0x01 = aui
-               0x02 = use half duplex
-               0x04 = use full duplex
-               0x08 = use 10BaseT
-               0x10 = use 100BaseTx
-
-          You also need to set both speed and duplex settings when forcing
-          speeds with kernel-parameters. 
-          ether=0,0,0x12,0,eth0 will force link to 100Mbps Half-Duplex.
-
-       7. If you have more than one tlan adapter in your system, you can
-          use the above options on a per adapter basis. To force a 100Mbit/HD
-          link with your eth1 adapter use:
-          
-          insmod tlan speed=0,100 duplex=0,1
-
-          Now eth0 will use auto-neg and eth1 will be forced to 100Mbit/HD.
-          Note that the tlan driver supports a maximum of 8 adapters.
-
-
-III.  Things to try if you have problems.
-       1. Make sure your card's PCI id is among those listed in
-          section I, above.
-       2. Make sure routing is correct.
-       3. Try forcing different speed/duplex settings
-
-
-There is also a tlan mailing list which you can join by sending "subscribe tlan"
-in the body of an email to majordomo@vuser.vu.union.edu.
-There is also a tlan website at http://www.compaq.com
-
diff --git a/Documentation/networking/vortex.txt b/Documentation/networking/vortex.txt
deleted file mode 100644 (file)
index ad3dead..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-Documentation/networking/vortex.txt
-Andrew Morton
-30 April 2000
-
-
-This document describes the usage and errata of the 3Com "Vortex" device
-driver for Linux, 3c59x.c.
-
-The driver was written by Donald Becker <becker@scyld.com>
-
-Don is no longer the prime maintainer of this version of the driver. 
-Please report problems to one or more of:
-
-  Andrew Morton
-  Netdev mailing list <netdev@vger.kernel.org>
-  Linux kernel mailing list <linux-kernel@vger.kernel.org>
-
-Please note the 'Reporting and Diagnosing Problems' section at the end
-of this file.
-
-
-Since kernel 2.3.99-pre6, this driver incorporates the support for the
-3c575-series Cardbus cards which used to be handled by 3c575_cb.c.
-
-This driver supports the following hardware:
-
-       3c590 Vortex 10Mbps
-       3c592 EISA 10Mbps Demon/Vortex
-       3c597 EISA Fast Demon/Vortex
-       3c595 Vortex 100baseTx
-       3c595 Vortex 100baseT4
-       3c595 Vortex 100base-MII
-       3c900 Boomerang 10baseT
-       3c900 Boomerang 10Mbps Combo
-       3c900 Cyclone 10Mbps TPO
-       3c900 Cyclone 10Mbps Combo
-       3c900 Cyclone 10Mbps TPC
-       3c900B-FL Cyclone 10base-FL
-       3c905 Boomerang 100baseTx
-       3c905 Boomerang 100baseT4
-       3c905B Cyclone 100baseTx
-       3c905B Cyclone 10/100/BNC
-       3c905B-FX Cyclone 100baseFx
-       3c905C Tornado
-       3c920B-EMB-WNM (ATI Radeon 9100 IGP)
-       3c980 Cyclone
-       3c980C Python-T
-       3cSOHO100-TX Hurricane
-       3c555 Laptop Hurricane
-       3c556 Laptop Tornado
-       3c556B Laptop Hurricane
-       3c575 [Megahertz] 10/100 LAN  CardBus
-       3c575 Boomerang CardBus
-       3CCFE575BT Cyclone CardBus
-       3CCFE575CT Tornado CardBus
-       3CCFE656 Cyclone CardBus
-       3CCFEM656B Cyclone+Winmodem CardBus
-       3CXFEM656C Tornado+Winmodem CardBus
-       3c450 HomePNA Tornado
-       3c920 Tornado
-       3c982 Hydra Dual Port A
-       3c982 Hydra Dual Port B
-       3c905B-T4
-       3c920B-EMB-WNM Tornado
-
-Module parameters
-=================
-
-There are several parameters which may be provided to the driver when
-its module is loaded.  These are usually placed in /etc/modprobe.d/*.conf
-configuration files.  Example:
-
-options 3c59x debug=3 rx_copybreak=300
-
-If you are using the PCMCIA tools (cardmgr) then the options may be
-placed in /etc/pcmcia/config.opts:
-
-module "3c59x" opts "debug=3 rx_copybreak=300"
-
-
-The supported parameters are:
-
-debug=N
-
-  Where N is a number from 0 to 7.  Anything above 3 produces a lot
-  of output in your system logs.  debug=1 is default.
-
-options=N1,N2,N3,...
-
-  Each number in the list provides an option to the corresponding
-  network card.  So if you have two 3c905's and you wish to provide
-  them with option 0x204 you would use:
-
-    options=0x204,0x204
-
-  The individual options are composed of a number of bitfields which
-  have the following meanings:
-
-  Possible media type settings
-       0       10baseT
-       1       10Mbs AUI
-       2       undefined
-       3       10base2 (BNC)
-       4       100base-TX
-       5       100base-FX
-       6       MII (Media Independent Interface)
-       7       Use default setting from EEPROM
-       8       Autonegotiate
-       9       External MII
-       10      Use default setting from EEPROM
-
-  When generating a value for the 'options' setting, the above media
-  selection values may be OR'ed (or added to) the following:
-
-  0x8000  Set driver debugging level to 7
-  0x4000  Set driver debugging level to 2
-  0x0400  Enable Wake-on-LAN
-  0x0200  Force full duplex mode.
-  0x0010  Bus-master enable bit (Old Vortex cards only)
-
-  For example:
-
-    insmod 3c59x options=0x204
-
-  will force full-duplex 100base-TX, rather than allowing the usual
-  autonegotiation.
-
-global_options=N
-
-  Sets the `options' parameter for all 3c59x NICs in the machine. 
-  Entries in the `options' array above will override any setting of
-  this.
-
-full_duplex=N1,N2,N3...
-
-  Similar to bit 9 of 'options'.  Forces the corresponding card into
-  full-duplex mode.  Please use this in preference to the `options'
-  parameter.
-
-  In fact, please don't use this at all! You're better off getting
-  autonegotiation working properly.
-
-global_full_duplex=N1
-
-  Sets full duplex mode for all 3c59x NICs in the machine.  Entries
-  in the `full_duplex' array above will override any setting of this.
-
-flow_ctrl=N1,N2,N3...
-
-  Use 802.3x MAC-layer flow control.  The 3com cards only support the
-  PAUSE command, which means that they will stop sending packets for a
-  short period if they receive a PAUSE frame from the link partner. 
-
-  The driver only allows flow control on a link which is operating in
-  full duplex mode.
-
-  This feature does not appear to work on the 3c905 - only 3c905B and
-  3c905C have been tested.
-
-  The 3com cards appear to only respond to PAUSE frames which are
-  sent to the reserved destination address of 01:80:c2:00:00:01.  They
-  do not honour PAUSE frames which are sent to the station MAC address.
-
-rx_copybreak=M
-
-  The driver preallocates 32 full-sized (1536 byte) network buffers
-  for receiving.  When a packet arrives, the driver has to decide
-  whether to leave the packet in its full-sized buffer, or to allocate
-  a smaller buffer and copy the packet across into it.
-
-  This is a speed/space tradeoff.
-
-  The value of rx_copybreak is used to decide when to make the copy. 
-  If the packet size is less than rx_copybreak, the packet is copied. 
-  The default value for rx_copybreak is 200 bytes.
-
-max_interrupt_work=N
-
-  The driver's interrupt service routine can handle many receive and
-  transmit packets in a single invocation.  It does this in a loop. 
-  The value of max_interrupt_work governs how many times the interrupt
-  service routine will loop.  The default value is 32 loops.  If this
-  is exceeded the interrupt service routine gives up and generates a
-  warning message "eth0: Too much work in interrupt".
-
-hw_checksums=N1,N2,N3,...
-
-  Recent 3com NICs are able to generate IPv4, TCP and UDP checksums
-  in hardware.  Linux has used the Rx checksumming for a long time. 
-  The "zero copy" patch which is planned for the 2.4 kernel series
-  allows you to make use of the NIC's DMA scatter/gather and transmit
-  checksumming as well.
-
-  The driver is set up so that, when the zerocopy patch is applied,
-  all Tornado and Cyclone devices will use S/G and Tx checksums.
-
-  This module parameter has been provided so you can override this
-  decision.  If you think that Tx checksums are causing a problem, you
-  may disable the feature with `hw_checksums=0'.
-
-  If you think your NIC should be performing Tx checksumming and the
-  driver isn't enabling it, you can force the use of hardware Tx
-  checksumming with `hw_checksums=1'.
-
-  The driver drops a message in the logfiles to indicate whether or
-  not it is using hardware scatter/gather and hardware Tx checksums.
-
-  Scatter/gather and hardware checksums provide considerable
-  performance improvement for the sendfile() system call, but a small
-  decrease in throughput for send().  There is no effect upon receive
-  efficiency.
-
-compaq_ioaddr=N
-compaq_irq=N
-compaq_device_id=N
-
-  "Variables to work-around the Compaq PCI BIOS32 problem"....
-
-watchdog=N
-
-  Sets the time duration (in milliseconds) after which the kernel
-  decides that the transmitter has become stuck and needs to be reset. 
-  This is mainly for debugging purposes, although it may be advantageous
-  to increase this value on LANs which have very high collision rates.
-  The default value is 5000 (5.0 seconds).
-
-enable_wol=N1,N2,N3,...
-
-  Enable Wake-on-LAN support for the relevant interface.  Donald
-  Becker's `ether-wake' application may be used to wake suspended
-  machines.
-
-  Also enables the NIC's power management support.
-
-global_enable_wol=N
-
-  Sets enable_wol mode for all 3c59x NICs in the machine.  Entries in
-  the `enable_wol' array above will override any setting of this.
-
-Media selection
----------------
-
-A number of the older NICs such as the 3c590 and 3c900 series have
-10base2 and AUI interfaces.
-
-Prior to January, 2001 this driver would autoeselect the 10base2 or AUI
-port if it didn't detect activity on the 10baseT port.  It would then
-get stuck on the 10base2 port and a driver reload was necessary to
-switch back to 10baseT.  This behaviour could not be prevented with a
-module option override.
-
-Later (current) versions of the driver _do_ support locking of the
-media type.  So if you load the driver module with
-
-       modprobe 3c59x options=0
-
-it will permanently select the 10baseT port.  Automatic selection of
-other media types does not occur.
-
-
-Transmit error, Tx status register 82
--------------------------------------
-
-This is a common error which is almost always caused by another host on
-the same network being in full-duplex mode, while this host is in
-half-duplex mode.  You need to find that other host and make it run in
-half-duplex mode or fix this host to run in full-duplex mode.
-
-As a last resort, you can force the 3c59x driver into full-duplex mode
-with
-
-       options 3c59x full_duplex=1
-
-but this has to be viewed as a workaround for broken network gear and
-should only really be used for equipment which cannot autonegotiate.
-
-
-Additional resources
---------------------
-
-Details of the device driver implementation are at the top of the source file.
-
-Additional documentation is available at Don Becker's Linux Drivers site:
-
-     http://www.scyld.com/vortex.html
-
-Donald Becker's driver development site:
-
-     http://www.scyld.com/network.html
-
-Donald's vortex-diag program is useful for inspecting the NIC's state:
-
-     http://www.scyld.com/ethercard_diag.html
-
-Donald's mii-diag program may be used for inspecting and manipulating
-the NIC's Media Independent Interface subsystem:
-
-     http://www.scyld.com/ethercard_diag.html#mii-diag
-
-Donald's wake-on-LAN page:
-
-     http://www.scyld.com/wakeonlan.html
-
-3Com's DOS-based application for setting up the NICs EEPROMs:
-
-       ftp://ftp.3com.com/pub/nic/3c90x/3c90xx2.exe
-
-
-Autonegotiation notes
----------------------
-
-  The driver uses a one-minute heartbeat for adapting to changes in
-  the external LAN environment if link is up and 5 seconds if link is down.
-  This means that when, for example, a machine is unplugged from a hubbed
-  10baseT LAN plugged into a  switched 100baseT LAN, the throughput
-  will be quite dreadful for up to sixty seconds.  Be patient.
-
-  Cisco interoperability note from Walter Wong <wcw+@CMU.EDU>:
-
-  On a side note, adding HAS_NWAY seems to share a problem with the
-  Cisco 6509 switch.  Specifically, you need to change the spanning
-  tree parameter for the port the machine is plugged into to 'portfast'
-  mode.  Otherwise, the negotiation fails.  This has been an issue
-  we've noticed for a while but haven't had the time to track down.
-
-  Cisco switches    (Jeff Busch <jbusch@deja.com>)
-
-    My "standard config" for ports to which PC's/servers connect directly:
-
-        interface FastEthernet0/N
-        description machinename
-        load-interval 30
-        spanning-tree portfast
-
-    If autonegotiation is a problem, you may need to specify "speed
-    100" and "duplex full" as well (or "speed 10" and "duplex half").
-
-    WARNING: DO NOT hook up hubs/switches/bridges to these
-    specially-configured ports! The switch will become very confused.
-
-
-Reporting and diagnosing problems
----------------------------------
-
-Maintainers find that accurate and complete problem reports are
-invaluable in resolving driver problems.  We are frequently not able to
-reproduce problems and must rely on your patience and efforts to get to
-the bottom of the problem.
-
-If you believe you have a driver problem here are some of the
-steps you should take:
-
-- Is it really a driver problem?
-
-   Eliminate some variables: try different cards, different
-   computers, different cables, different ports on the switch/hub,
-   different versions of the kernel or of the driver, etc.
-
-- OK, it's a driver problem.
-
-   You need to generate a report.  Typically this is an email to the
-   maintainer and/or netdev@vger.kernel.org.  The maintainer's
-   email address will be in the driver source or in the MAINTAINERS file.
-
-- The contents of your report will vary a lot depending upon the
-  problem.  If it's a kernel crash then you should refer to the
-  admin-guide/reporting-bugs.rst file.
-
-  But for most problems it is useful to provide the following:
-
-   o Kernel version, driver version
-
-   o A copy of the banner message which the driver generates when
-     it is initialised.  For example:
-
-     eth0: 3Com PCI 3c905C Tornado at 0xa400,  00:50:da:6a:88:f0, IRQ 19
-     8K byte-wide RAM 5:3 Rx:Tx split, autoselect/Autonegotiate interface.
-     MII transceiver found at address 24, status 782d.
-     Enabling bus-master transmits and whole-frame receives.
-
-     NOTE: You must provide the `debug=2' modprobe option to generate
-     a full detection message.  Please do this:
-
-       modprobe 3c59x debug=2
-
-   o If it is a PCI device, the relevant output from 'lspci -vx', eg:
-
-     00:09.0 Ethernet controller: 3Com Corporation 3c905C-TX [Fast Etherlink] (rev 74)
-             Subsystem: 3Com Corporation: Unknown device 9200
-             Flags: bus master, medium devsel, latency 32, IRQ 19
-             I/O ports at a400 [size=128]
-             Memory at db000000 (32-bit, non-prefetchable) [size=128]
-             Expansion ROM at <unassigned> [disabled] [size=128K]
-             Capabilities: [dc] Power Management version 2
-     00: b7 10 00 92 07 00 10 02 74 00 00 02 08 20 00 00
-     10: 01 a4 00 00 00 00 00 db 00 00 00 00 00 00 00 00
-     20: 00 00 00 00 00 00 00 00 00 00 00 00 b7 10 00 10
-     30: 00 00 00 00 dc 00 00 00 00 00 00 00 05 01 0a 0a
-
-   o A description of the environment: 10baseT? 100baseT?
-     full/half duplex? switched or hubbed?
-
-   o Any additional module parameters which you may be providing to the driver.
-
-   o Any kernel logs which are produced.  The more the merrier. 
-     If this is a large file and you are sending your report to a
-     mailing list, mention that you have the logfile, but don't send
-     it.  If you're reporting direct to the maintainer then just send
-     it.
-
-     To ensure that all kernel logs are available, add the
-     following line to /etc/syslog.conf:
-
-         kern.* /var/log/messages
-
-     Then restart syslogd with:
-
-         /etc/rc.d/init.d/syslog restart
-
-     (The above may vary, depending upon which Linux distribution you use).
-
-    o If your problem is reproducible then that's great.  Try the
-      following:
-
-      1) Increase the debug level.  Usually this is done via:
-
-         a) modprobe driver debug=7
-         b) In /etc/modprobe.d/driver.conf:
-            options driver debug=7
-
-      2) Recreate the problem with the higher debug level,
-         send all logs to the maintainer.
-
-      3) Download you card's diagnostic tool from Donald
-         Becker's website <http://www.scyld.com/ethercard_diag.html>.
-         Download mii-diag.c as well.  Build these.
-
-         a) Run 'vortex-diag -aaee' and 'mii-diag -v' when the card is
-            working correctly.  Save the output.
-
-         b) Run the above commands when the card is malfunctioning.  Send
-            both sets of output.
-
-Finally, please be patient and be prepared to do some work.  You may
-end up working on this problem for a week or more as the maintainer
-asks more questions, asks for more tests, asks for patches to be
-applied, etc.  At the end of it all, the problem may even remain
-unresolved.
diff --git a/Documentation/networking/vxge.txt b/Documentation/networking/vxge.txt
deleted file mode 100644 (file)
index abfec24..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-Neterion's (Formerly S2io) X3100 Series 10GbE PCIe Server Adapter Linux driver
-==============================================================================
-
-Contents
---------
-
-1) Introduction
-2) Features supported
-3) Configurable driver parameters
-4) Troubleshooting
-
-1) Introduction:
-----------------
-This Linux driver supports all Neterion's X3100 series 10 GbE PCIe I/O
-Virtualized Server adapters.
-The X3100 series supports four modes of operation, configurable via
-firmware -
-       Single function mode
-       Multi function mode
-       SRIOV mode
-       MRIOV mode
-The functions share a 10GbE link and the pci-e bus, but hardly anything else
-inside the ASIC. Features like independent hw reset, statistics, bandwidth/
-priority allocation and guarantees, GRO, TSO, interrupt moderation etc are
-supported independently on each function.
-
-(See below for a complete list of features supported for both IPv4 and IPv6)
-
-2) Features supported:
-----------------------
-
-i)   Single function mode (up to 17 queues)
-
-ii)  Multi function mode (up to 17 functions)
-
-iii) PCI-SIG's I/O Virtualization
-       - Single Root mode: v1.0 (up to 17 functions)
-       - Multi-Root mode: v1.0 (up to 17 functions)
-
-iv)  Jumbo frames
-       X3100 Series supports MTU up to 9600 bytes, modifiable using
-       ip command.
-
-v)   Offloads supported: (Enabled by default)
-       Checksum offload (TCP/UDP/IP) on transmit and receive paths
-       TCP Segmentation Offload (TSO) on transmit path
-       Generic Receive Offload (GRO) on receive path
-
-vi)  MSI-X: (Enabled by default)
-       Resulting in noticeable performance improvement (up to 7% on certain
-       platforms).
-
-vii) NAPI: (Enabled by default)
-       For better Rx interrupt moderation.
-
-viii)RTH (Receive Traffic Hash): (Enabled by default)
-       Receive side steering for better scaling.
-
-ix)  Statistics
-       Comprehensive MAC-level and software statistics displayed using
-       "ethtool -S" option.
-
-x)   Multiple hardware queues: (Enabled by default)
-       Up to 17 hardware based transmit and receive data channels, with
-       multiple steering options (transmit multiqueue enabled by default).
-
-3) Configurable driver parameters:
-----------------------------------
-
-i)  max_config_dev
-       Specifies maximum device functions to be enabled.
-       Valid range: 1-8
-
-ii) max_config_port
-       Specifies number of ports to be enabled.
-       Valid range: 1,2
-       Default: 1
-
-iii)max_config_vpath
-       Specifies maximum VPATH(s) configured for each device function.
-       Valid range: 1-17
-
-iv) vlan_tag_strip
-       Enables/disables vlan tag stripping from all received tagged frames that
-       are not replicated at the internal L2 switch.
-       Valid range: 0,1 (disabled, enabled respectively)
-       Default: 1
-
-v)  addr_learn_en
-       Enable learning the mac address of the guest OS interface in
-       virtualization environment.
-       Valid range: 0,1 (disabled, enabled respectively)
-       Default: 0
index 267f55b..a1c904d 100644 (file)
@@ -111,9 +111,10 @@ the stack in xfrm_input().
                xfrm_state_hold(xs);
 
        store the state information into the skb
-               skb->sp = secpath_dup(skb->sp);
-               skb->sp->xvec[skb->sp->len++] = xs;
-               skb->sp->olen++;
+               sp = secpath_set(skb);
+               if (!sp) return;
+               sp->xvec[sp->len++] = xs;
+               sp->olen++;
 
        indicate the success and/or error status of the offload
                xo = xfrm_offload(skb);
index 32f3d55..c4dbe6f 100644 (file)
@@ -92,3 +92,12 @@ Speculation misfeature controls
    * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_ENABLE, 0, 0);
    * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_DISABLE, 0, 0);
    * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_FORCE_DISABLE, 0, 0);
+
+- PR_SPEC_INDIR_BRANCH: Indirect Branch Speculation in User Processes
+                        (Mitigate Spectre V2 style attacks against user processes)
+
+  Invocations:
+   * prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, 0, 0, 0);
+   * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_ENABLE, 0, 0);
+   * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_DISABLE, 0, 0);
+   * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0);
index 7727db8..5e9b826 100644 (file)
@@ -61,18 +61,6 @@ Protocol 2.12:       (Kernel 3.8) Added the xloadflags field and extension fields
                to struct boot_params for loading bzImage and ramdisk
                above 4G in 64bit.
 
-Protocol 2.13: (Kernel 3.14) Support 32- and 64-bit flags being set in
-               xloadflags to support booting a 64-bit kernel from 32-bit
-               EFI
-
-Protocol 2.14: (Kernel 4.20) Added acpi_rsdp_addr holding the physical
-               address of the ACPI RSDP table.
-               The bootloader updates version with:
-               0x8000 | min(kernel-version, bootloader-version)
-               kernel-version being the protocol version supported by
-               the kernel and bootloader-version the protocol version
-               supported by the bootloader.
-
 **** MEMORY LAYOUT
 
 The traditional memory map for the kernel loader, used for Image or
@@ -209,7 +197,6 @@ Offset      Proto   Name            Meaning
 0258/8 2.10+   pref_address    Preferred loading address
 0260/4 2.10+   init_size       Linear memory required during initialization
 0264/4 2.11+   handover_offset Offset of handover entry point
-0268/8 2.14+   acpi_rsdp_addr  Physical address of RSDP table
 
 (1) For backwards compatibility, if the setup_sects field contains 0, the
     real value is 4.
@@ -322,7 +309,7 @@ Protocol:   2.00+
   Contains the magic number "HdrS" (0x53726448).
 
 Field name:    version
-Type:          modify
+Type:          read
 Offset/size:   0x206/2
 Protocol:      2.00+
 
@@ -330,12 +317,6 @@ Protocol:  2.00+
   e.g. 0x0204 for version 2.04, and 0x0a11 for a hypothetical version
   10.17.
 
-  Up to protocol version 2.13 this information is only read by the
-  bootloader. From protocol version 2.14 onwards the bootloader will
-  write the used protocol version or-ed with 0x8000 to the field. The
-  used protocol version will be the minimum of the supported protocol
-  versions of the bootloader and the kernel.
-
 Field name:    realmode_swtch
 Type:          modify (optional)
 Offset/size:   0x208/4
@@ -763,17 +744,6 @@ Offset/size:       0x264/4
 
   See EFI HANDOVER PROTOCOL below for more details.
 
-Field name:    acpi_rsdp_addr
-Type:          write
-Offset/size:   0x268/8
-Protocol:      2.14+
-
-  This field can be set by the boot loader to tell the kernel the
-  physical address of the ACPI RSDP table.
-
-  A value of 0 indicates the kernel should fall back to the standard
-  methods to locate the RSDP.
-
 
 **** THE IMAGE CHECKSUM
 
index e110e32..6de660a 100644 (file)
@@ -140,7 +140,7 @@ Maintainers List (try to look for most precise areas first)
 M:     Steffen Klassert <klassert@kernel.org>
 L:     netdev@vger.kernel.org
 S:     Odd Fixes
-F:     Documentation/networking/vortex.txt
+F:     Documentation/networking/device_drivers/3com/vortex.txt
 F:     drivers/net/ethernet/3com/3c59x.c
 
 3CR990 NETWORK DRIVER
@@ -180,6 +180,7 @@ F:  drivers/net/hamradio/6pack.c
 
 8169 10/100/1000 GIGABIT ETHERNET DRIVER
 M:     Realtek linux nic maintainers <nic_swsd@realtek.com>
+M:     Heiner Kallweit <hkallweit1@gmail.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ethernet/realtek/r8169.c
@@ -717,7 +718,7 @@ F:  include/linux/mfd/altera-a10sr.h
 F:     include/dt-bindings/reset/altr,rst-mgr-a10sr.h
 
 ALTERA TRIPLE SPEED ETHERNET DRIVER
-M:     Vince Bridgers <vbridger@opensource.altera.com>
+M:     Thor Thayer <thor.thayer@linux.intel.com>
 L:     netdev@vger.kernel.org
 L:     nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
 S:     Maintained
@@ -739,7 +740,7 @@ R:  Saeed Bishara <saeedb@amazon.com>
 R:     Zorik Machulsky <zorik@amazon.com>
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/ena.txt
+F:     Documentation/networking/device_drivers/amazon/ena.txt
 F:     drivers/net/ethernet/amazon/
 
 AMD CRYPTOGRAPHIC COPROCESSOR (CCP) DRIVER
@@ -1471,6 +1472,7 @@ F:        drivers/clk/sirf/
 F:     drivers/clocksource/timer-prima2.c
 F:     drivers/clocksource/timer-atlas7.c
 N:     [^a-z]sirf
+X:     drivers/gnss
 
 ARM/EBSA110 MACHINE SUPPORT
 M:     Russell King <linux@armlinux.org.uk>
@@ -1737,13 +1739,17 @@ ARM/Mediatek SoC support
 M:     Matthias Brugger <matthias.bgg@gmail.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 L:     linux-mediatek@lists.infradead.org (moderated for non-subscribers)
+W:     https://mtk.bcnfs.org/
+C:     irc://chat.freenode.net/linux-mediatek
 S:     Maintained
 F:     arch/arm/boot/dts/mt6*
 F:     arch/arm/boot/dts/mt7*
 F:     arch/arm/boot/dts/mt8*
 F:     arch/arm/mach-mediatek/
 F:     arch/arm64/boot/dts/mediatek/
+F:     drivers/soc/mediatek/
 N:     mtk
+N:     mt[678]
 K:     mediatek
 
 ARM/Mediatek USB3 PHY DRIVER
@@ -1922,7 +1928,6 @@ ARM/QUALCOMM SUPPORT
 M:     Andy Gross <andy.gross@linaro.org>
 M:     David Brown <david.brown@linaro.org>
 L:     linux-arm-msm@vger.kernel.org
-L:     linux-soc@vger.kernel.org
 S:     Maintained
 F:     Documentation/devicetree/bindings/soc/qcom/
 F:     arch/arm/boot/dts/qcom-*.dts
@@ -2490,7 +2495,7 @@ F:        drivers/net/wireless/ath/*
 ATHEROS ATH5K WIRELESS DRIVER
 M:     Jiri Slaby <jirislaby@gmail.com>
 M:     Nick Kossifidis <mickflemm@gmail.com>
-M:     "Luis R. Rodriguez" <mcgrof@do-not-panic.com>
+M:     Luis Chamberlain <mcgrof@kernel.org>
 L:     linux-wireless@vger.kernel.org
 W:     http://wireless.kernel.org/en/users/Drivers/ath5k
 S:     Maintained
@@ -2800,7 +2805,7 @@ T:        git git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
 Q:     https://patchwork.ozlabs.org/project/netdev/list/?delegate=77147
 S:     Supported
-F:     arch/x86/net/bpf_jit*
+F:     arch/*/net/*
 F:     Documentation/networking/filter.txt
 F:     Documentation/bpf/
 F:     include/linux/bpf*
@@ -2820,6 +2825,67 @@ F:       tools/bpf/
 F:     tools/lib/bpf/
 F:     tools/testing/selftests/bpf/
 
+BPF JIT for ARM
+M:     Shubham Bansal <illusionist.neo@gmail.com>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     arch/arm/net/
+
+BPF JIT for ARM64
+M:     Daniel Borkmann <daniel@iogearbox.net>
+M:     Alexei Starovoitov <ast@kernel.org>
+M:     Zi Shen Lim <zlim.lnx@gmail.com>
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     arch/arm64/net/
+
+BPF JIT for MIPS (32-BIT AND 64-BIT)
+M:     Paul Burton <paul.burton@mips.com>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     arch/mips/net/
+
+BPF JIT for NFP NICs
+M:     Jakub Kicinski <jakub.kicinski@netronome.com>
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     drivers/net/ethernet/netronome/nfp/bpf/
+
+BPF JIT for POWERPC (32-BIT AND 64-BIT)
+M:     Naveen N. Rao <naveen.n.rao@linux.ibm.com>
+M:     Sandipan Das <sandipan@linux.ibm.com>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     arch/powerpc/net/
+
+BPF JIT for S390
+M:     Martin Schwidefsky <schwidefsky@de.ibm.com>
+M:     Heiko Carstens <heiko.carstens@de.ibm.com>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     arch/s390/net/
+X:     arch/s390/net/pnet.c
+
+BPF JIT for SPARC (32-BIT AND 64-BIT)
+M:     David S. Miller <davem@davemloft.net>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     arch/sparc/net/
+
+BPF JIT for X86 32-BIT
+M:     Wang YanQing <udknight@gmail.com>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     arch/x86/net/bpf_jit_comp32.c
+
+BPF JIT for X86 64-BIT
+M:     Alexei Starovoitov <ast@kernel.org>
+M:     Daniel Borkmann <daniel@iogearbox.net>
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     arch/x86/net/
+X:     arch/x86/net/bpf_jit_comp32.c
+
 BROADCOM B44 10/100 ETHERNET DRIVER
 M:     Michael Chan <michael.chan@broadcom.com>
 L:     netdev@vger.kernel.org
@@ -2860,7 +2926,7 @@ F:        drivers/staging/vc04_services
 BROADCOM BCM47XX MIPS ARCHITECTURE
 M:     Hauke Mehrtens <hauke@hauke-m.de>
 M:     RafaÅ‚ MiÅ‚ecki <zajec5@gmail.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     Documentation/devicetree/bindings/mips/brcm/
 F:     arch/mips/bcm47xx/*
@@ -2869,7 +2935,6 @@ F:        arch/mips/include/asm/mach-bcm47xx/*
 BROADCOM BCM5301X ARM ARCHITECTURE
 M:     Hauke Mehrtens <hauke@hauke-m.de>
 M:     RafaÅ‚ MiÅ‚ecki <zajec5@gmail.com>
-M:     Jon Mason <jonmason@broadcom.com>
 M:     bcm-kernel-feedback-list@broadcom.com
 L:     linux-arm-kernel@lists.infradead.org
 S:     Maintained
@@ -2924,7 +2989,7 @@ F:        drivers/cpufreq/bmips-cpufreq.c
 BROADCOM BMIPS MIPS ARCHITECTURE
 M:     Kevin Cernekee <cernekee@gmail.com>
 M:     Florian Fainelli <f.fainelli@gmail.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 T:     git git://github.com/broadcom/stblinux.git
 S:     Maintained
 F:     arch/mips/bmips/*
@@ -3015,7 +3080,6 @@ F:        drivers/net/ethernet/broadcom/genet/
 BROADCOM IPROC ARM ARCHITECTURE
 M:     Ray Jui <rjui@broadcom.com>
 M:     Scott Branden <sbranden@broadcom.com>
-M:     Jon Mason <jonmason@broadcom.com>
 M:     bcm-kernel-feedback-list@broadcom.com
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 T:     git git://github.com/broadcom/cygnus-linux.git
@@ -3062,7 +3126,7 @@ F:        include/uapi/rdma/bnxt_re-abi.h
 
 BROADCOM NVRAM DRIVER
 M:     RafaÅ‚ MiÅ‚ecki <zajec5@gmail.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     drivers/firmware/broadcom/*
 
@@ -3212,11 +3276,16 @@ S:      Maintained
 F:     sound/pci/oxygen/
 
 C-SKY ARCHITECTURE
-M:     Guo Ren <ren_guo@c-sky.com>
+M:     Guo Ren <guoren@kernel.org>
 T:     git https://github.com/c-sky/csky-linux.git
 S:     Supported
 F:     arch/csky/
 F:     Documentation/devicetree/bindings/csky/
+F:     drivers/irqchip/irq-csky-*
+F:     Documentation/devicetree/bindings/interrupt-controller/csky,*
+F:     drivers/clocksource/timer-gx6605s.c
+F:     drivers/clocksource/timer-mp-csky.c
+F:     Documentation/devicetree/bindings/timer/csky,*
 K:     csky
 N:     csky
 
@@ -3276,6 +3345,12 @@ F:       include/uapi/linux/caif/
 F:     include/net/caif/
 F:     net/caif/
 
+CAKE QDISC
+M:     Toke Høiland-Jørgensen <toke@toke.dk>
+L:     cake@lists.bufferbloat.net (moderated for non-subscribers)
+S:     Maintained
+F:     net/sched/sch_cake.c
+
 CALGARY x86-64 IOMMU
 M:     Muli Ben-Yehuda <mulix@mulix.org>
 M:     Jon Mason <jdmason@kudzu.us>
@@ -4119,7 +4194,7 @@ F:        net/ax25/sysctl_net_ax25.c
 DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER
 L:     netdev@vger.kernel.org
 S:     Orphan
-F:     Documentation/networking/dmfe.txt
+F:     Documentation/networking/device_drivers/dec/dmfe.txt
 F:     drivers/net/ethernet/dec/tulip/dmfe.c
 
 DC390/AM53C974 SCSI driver
@@ -4158,7 +4233,7 @@ F:        net/decnet/
 
 DECSTATION PLATFORM SUPPORT
 M:     "Maciej W. Rozycki" <macro@linux-mips.org>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 W:     http://www.linux-mips.org/wiki/DECstation
 S:     Maintained
 F:     arch/mips/dec/
@@ -4772,6 +4847,7 @@ F:        include/uapi/drm/vmwgfx_drm.h
 
 DRM DRIVERS
 M:     David Airlie <airlied@linux.ie>
+M:     Daniel Vetter <daniel@ffwll.ch>
 L:     dri-devel@lists.freedesktop.org
 T:     git git://anongit.freedesktop.org/drm/drm
 B:     https://bugs.freedesktop.org/
@@ -5249,7 +5325,7 @@ EDAC-CAVIUM OCTEON
 M:     Ralf Baechle <ralf@linux-mips.org>
 M:     David Daney <david.daney@cavium.com>
 L:     linux-edac@vger.kernel.org
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Supported
 F:     drivers/edac/octeon_edac*
 
@@ -5528,6 +5604,7 @@ F:        net/bridge/
 ETHERNET PHY LIBRARY
 M:     Andrew Lunn <andrew@lunn.ch>
 M:     Florian Fainelli <f.fainelli@gmail.com>
+M:     Heiner Kallweit <hkallweit1@gmail.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     Documentation/ABI/testing/sysfs-bus-mdio
@@ -5541,6 +5618,7 @@ F:        include/linux/of_net.h
 F:     include/linux/phy.h
 F:     include/linux/phy_fixed.h
 F:     include/linux/platform_data/mdio-bcm-unimac.h
+F:     include/linux/platform_data/mdio-gpio.h
 F:     include/trace/events/mdio.h
 F:     include/uapi/linux/mdio.h
 F:     include/uapi/linux/mii.h
@@ -5766,7 +5844,7 @@ F:        include/uapi/linux/firewire*.h
 F:     tools/firewire/
 
 FIRMWARE LOADER (request_firmware)
-M:     Luis R. Rodriguez <mcgrof@kernel.org>
+M:     Luis Chamberlain <mcgrof@kernel.org>
 L:     linux-kernel@vger.kernel.org
 S:     Maintained
 F:     Documentation/firmware_class/
@@ -6250,6 +6328,7 @@ F:        include/uapi/linux/gigaset_dev.h
 
 GNSS SUBSYSTEM
 M:     Johan Hovold <johan@kernel.org>
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/johan/gnss.git
 S:     Maintained
 F:     Documentation/ABI/testing/sysfs-class-gnss
 F:     Documentation/devicetree/bindings/gnss/
@@ -6299,6 +6378,7 @@ F:        tools/testing/selftests/gpio/
 
 GPIO SUBSYSTEM
 M:     Linus Walleij <linus.walleij@linaro.org>
+M:     Bartosz Golaszewski <bgolaszewski@baylibre.com>
 L:     linux-gpio@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git
 S:     Maintained
@@ -6827,9 +6907,11 @@ Hyper-V CORE AND DRIVERS
 M:     "K. Y. Srinivasan" <kys@microsoft.com>
 M:     Haiyang Zhang <haiyangz@microsoft.com>
 M:     Stephen Hemminger <sthemmin@microsoft.com>
+M:     Sasha Levin <sashal@kernel.org>
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git
 L:     devel@linuxdriverproject.org
-S:     Maintained
-F:     Documentation/networking/netvsc.txt
+S:     Supported
+F:     Documentation/networking/device_drivers/microsoft/netvsc.txt
 F:     arch/x86/include/asm/mshyperv.h
 F:     arch/x86/include/asm/trace/hyperv.h
 F:     arch/x86/include/asm/hyperv-tlfs.h
@@ -7413,18 +7495,18 @@ Q:      http://patchwork.ozlabs.org/project/intel-wired-lan/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-queue.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue.git
 S:     Supported
-F:     Documentation/networking/e100.rst
-F:     Documentation/networking/e1000.rst
-F:     Documentation/networking/e1000e.rst
-F:     Documentation/networking/fm10k.rst
-F:     Documentation/networking/igb.rst
-F:     Documentation/networking/igbvf.rst
-F:     Documentation/networking/ixgb.rst
-F:     Documentation/networking/ixgbe.rst
-F:     Documentation/networking/ixgbevf.rst
-F:     Documentation/networking/i40e.rst
-F:     Documentation/networking/iavf.rst
-F:     Documentation/networking/ice.rst
+F:     Documentation/networking/device_drivers/intel/e100.rst
+F:     Documentation/networking/device_drivers/intel/e1000.rst
+F:     Documentation/networking/device_drivers/intel/e1000e.rst
+F:     Documentation/networking/device_drivers/intel/fm10k.rst
+F:     Documentation/networking/device_drivers/intel/igb.rst
+F:     Documentation/networking/device_drivers/intel/igbvf.rst
+F:     Documentation/networking/device_drivers/intel/ixgb.rst
+F:     Documentation/networking/device_drivers/intel/ixgbe.rst
+F:     Documentation/networking/device_drivers/intel/ixgbevf.rst
+F:     Documentation/networking/device_drivers/intel/i40e.rst
+F:     Documentation/networking/device_drivers/intel/iavf.rst
+F:     Documentation/networking/device_drivers/intel/ice.rst
 F:     drivers/net/ethernet/intel/
 F:     drivers/net/ethernet/intel/*/
 F:     include/linux/avf/virtchnl.h
@@ -7436,6 +7518,20 @@ S:       Maintained
 F:     Documentation/fb/intelfb.txt
 F:     drivers/video/fbdev/intelfb/
 
+INTEL GPIO DRIVERS
+M:     Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+L:     linux-gpio@vger.kernel.org
+S:     Maintained
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel.git
+F:     drivers/gpio/gpio-ich.c
+F:     drivers/gpio/gpio-intel-mid.c
+F:     drivers/gpio/gpio-lynxpoint.c
+F:     drivers/gpio/gpio-merrifield.c
+F:     drivers/gpio/gpio-ml-ioh.c
+F:     drivers/gpio/gpio-pch.c
+F:     drivers/gpio/gpio-sch.c
+F:     drivers/gpio/gpio-sodaville.c
+
 INTEL GVT-g DRIVERS (Intel GPU Virtualization)
 M:     Zhenyu Wang <zhenyuw@linux.intel.com>
 M:     Zhi Wang <zhi.a.wang@intel.com>
@@ -7446,12 +7542,6 @@ T:       git https://github.com/intel/gvt-linux.git
 S:     Supported
 F:     drivers/gpu/drm/i915/gvt/
 
-INTEL PMIC GPIO DRIVER
-R:     Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-S:     Maintained
-F:     drivers/gpio/gpio-*cove.c
-F:     drivers/gpio/gpio-msic.c
-
 INTEL HID EVENT DRIVER
 M:     Alex Hung <alex.hung@canonical.com>
 L:     platform-driver-x86@vger.kernel.org
@@ -7539,12 +7629,6 @@ W:       https://01.org/linux-acpi
 S:     Supported
 F:     drivers/platform/x86/intel_menlow.c
 
-INTEL MERRIFIELD GPIO DRIVER
-M:     Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-L:     linux-gpio@vger.kernel.org
-S:     Maintained
-F:     drivers/gpio/gpio-merrifield.c
-
 INTEL MIC DRIVERS (mic)
 M:     Sudeep Dutt <sudeep.dutt@intel.com>
 M:     Ashutosh Dixit <ashutosh.dixit@intel.com>
@@ -7577,6 +7661,13 @@ F:       drivers/platform/x86/intel_punit_ipc.c
 F:     arch/x86/include/asm/intel_pmc_ipc.h
 F:     arch/x86/include/asm/intel_punit_ipc.h
 
+INTEL PMIC GPIO DRIVERS
+M:     Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+S:     Maintained
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel.git
+F:     drivers/gpio/gpio-*cove.c
+F:     drivers/gpio/gpio-msic.c
+
 INTEL MULTIFUNCTION PMIC DEVICE DRIVERS
 R:     Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 S:     Maintained
@@ -7589,8 +7680,8 @@ INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT
 M:     Stanislav Yakovlev <stas.yakovlev@gmail.com>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-F:     Documentation/networking/README.ipw2100
-F:     Documentation/networking/README.ipw2200
+F:     Documentation/networking/device_drivers/intel/ipw2100.txt
+F:     Documentation/networking/device_drivers/intel/ipw2200.txt
 F:     drivers/net/wireless/intel/ipw2x00/
 
 INTEL PSTATE DRIVER
@@ -7685,7 +7776,7 @@ F:        Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt
 
 IOC3 ETHERNET DRIVER
 M:     Ralf Baechle <ralf@linux-mips.org>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ethernet/sgi/ioc3-eth.c
 
@@ -8049,7 +8140,7 @@ F:        tools/testing/selftests/
 F:     Documentation/dev-tools/kselftest*
 
 KERNEL USERMODE HELPER
-M:     "Luis R. Rodriguez" <mcgrof@kernel.org>
+M:     Luis Chamberlain <mcgrof@kernel.org>
 L:     linux-kernel@vger.kernel.org
 S:     Maintained
 F:     kernel/umh.c
@@ -8106,7 +8197,7 @@ F:        arch/arm64/kvm/
 
 KERNEL VIRTUAL MACHINE FOR MIPS (KVM/mips)
 M:     James Hogan <jhogan@kernel.org>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Supported
 F:     arch/mips/include/uapi/asm/kvm*
 F:     arch/mips/include/asm/kvm*
@@ -8225,7 +8316,7 @@ F:        mm/kmemleak.c
 F:     mm/kmemleak-test.c
 
 KMOD KERNEL MODULE LOADER - USERMODE HELPER
-M:     "Luis R. Rodriguez" <mcgrof@kernel.org>
+M:     Luis Chamberlain <mcgrof@kernel.org>
 L:     linux-kernel@vger.kernel.org
 S:     Maintained
 F:     kernel/kmod.c
@@ -8279,7 +8370,7 @@ F:        drivers/net/dsa/lantiq_gswip.c
 
 LANTIQ MIPS ARCHITECTURE
 M:     John Crispin <john@phrozen.org>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     arch/mips/lantiq
 F:     drivers/soc/lantiq
@@ -8842,13 +8933,13 @@ S:      Maintained
 
 MARDUK (CREATOR CI40) DEVICE TREE SUPPORT
 M:     Rahul Bedarkar <rahulbedarkar89@gmail.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     arch/mips/boot/dts/img/pistachio_marduk.dts
 
 MARVELL 88E6XXX ETHERNET SWITCH FABRIC DRIVER
 M:     Andrew Lunn <andrew@lunn.ch>
-M:     Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+M:     Vivien Didelot <vivien.didelot@gmail.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/dsa/mv88e6xxx/
@@ -9353,6 +9444,13 @@ F:       drivers/media/platform/mtk-vpu/
 F:     Documentation/devicetree/bindings/media/mediatek-vcodec.txt
 F:     Documentation/devicetree/bindings/media/mediatek-vpu.txt
 
+MEDIATEK MT76 WIRELESS LAN DRIVER
+M:     Felix Fietkau <nbd@nbd.name>
+M:     Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
+L:     linux-wireless@vger.kernel.org
+S:     Maintained
+F:     drivers/net/wireless/mediatek/mt76/
+
 MEDIATEK MT7601U WIRELESS LAN DRIVER
 M:     Jakub Kicinski <kubakici@wp.pl>
 L:     linux-wireless@vger.kernel.org
@@ -9801,7 +9899,7 @@ F:        drivers/dma/at_xdmac.c
 
 MICROSEMI MIPS SOCS
 M:     Alexandre Belloni <alexandre.belloni@bootlin.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     arch/mips/generic/board-ocelot.c
 F:     arch/mips/configs/generic/board-ocelot.config
@@ -9822,6 +9920,7 @@ F:        Documentation/scsi/smartpqi.txt
 
 MICROSEMI ETHERNET SWITCH DRIVER
 M:     Alexandre Belloni <alexandre.belloni@bootlin.com>
+M:     Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
 L:     netdev@vger.kernel.org
 S:     Supported
 F:     drivers/net/ethernet/mscc/
@@ -9841,7 +9940,7 @@ MIPS
 M:     Ralf Baechle <ralf@linux-mips.org>
 M:     Paul Burton <paul.burton@mips.com>
 M:     James Hogan <jhogan@kernel.org>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 W:     http://www.linux-mips.org/
 T:     git git://git.linux-mips.org/pub/scm/ralf/linux.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux.git
@@ -9854,7 +9953,7 @@ F:        drivers/platform/mips/
 
 MIPS BOSTON DEVELOPMENT BOARD
 M:     Paul Burton <paul.burton@mips.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     Documentation/devicetree/bindings/clock/img,boston-clock.txt
 F:     arch/mips/boot/dts/img/boston.dts
@@ -9864,7 +9963,7 @@ F:        include/dt-bindings/clock/boston-clock.h
 
 MIPS GENERIC PLATFORM
 M:     Paul Burton <paul.burton@mips.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Supported
 F:     Documentation/devicetree/bindings/power/mti,mips-cpc.txt
 F:     arch/mips/generic/
@@ -9872,7 +9971,7 @@ F:        arch/mips/tools/generic-board-config.sh
 
 MIPS/LOONGSON1 ARCHITECTURE
 M:     Keguang Zhang <keguang.zhang@gmail.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     arch/mips/loongson32/
 F:     arch/mips/include/asm/mach-loongson32/
@@ -9881,7 +9980,7 @@ F:        drivers/*/*/*loongson1*
 
 MIPS/LOONGSON2 ARCHITECTURE
 M:     Jiaxun Yang <jiaxun.yang@flygoat.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     arch/mips/loongson64/fuloong-2e/
 F:     arch/mips/loongson64/lemote-2f/
@@ -9891,7 +9990,7 @@ F:        drivers/*/*/*loongson2*
 
 MIPS/LOONGSON3 ARCHITECTURE
 M:     Huacai Chen <chenhc@lemote.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     arch/mips/loongson64/
 F:     arch/mips/include/asm/mach-loongson64/
@@ -9901,7 +10000,7 @@ F:       drivers/*/*/*loongson3*
 
 MIPS RINT INSTRUCTION EMULATION
 M:     Aleksandar Markovic <aleksandar.markovic@mips.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Supported
 F:     arch/mips/math-emu/sp_rint.c
 F:     arch/mips/math-emu/dp_rint.c
@@ -9915,12 +10014,9 @@ S:      Odd Fixes
 F:     drivers/media/radio/radio-miropcm20*
 
 MMP SUPPORT
-M:     Eric Miao <eric.y.miao@gmail.com>
-M:     Haojian Zhuang <haojian.zhuang@gmail.com>
+R:     Lubomir Rintel <lkundrak@v3.sk>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-T:     git git://github.com/hzhuang1/linux.git
-T:     git git://git.linaro.org/people/ycmiao/pxa-linux.git
-S:     Maintained
+S:     Odd Fixes
 F:     arch/arm/boot/dts/mmp*
 F:     arch/arm/mach-mmp/
 
@@ -10236,8 +10332,8 @@ NETERION 10GbE DRIVERS (s2io/vxge)
 M:     Jon Mason <jdmason@kudzu.us>
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/s2io.txt
-F:     Documentation/networking/vxge.txt
+F:     Documentation/networking/device_drivers/neterion/s2io.txt
+F:     Documentation/networking/device_drivers/neterion/vxge.txt
 F:     drivers/net/ethernet/neterion/
 
 NETFILTER
@@ -10326,7 +10422,7 @@ F:      drivers/net/wireless/
 
 NETWORKING [DSA]
 M:     Andrew Lunn <andrew@lunn.ch>
-M:     Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+M:     Vivien Didelot <vivien.didelot@gmail.com>
 M:     Florian Fainelli <f.fainelli@gmail.com>
 S:     Maintained
 F:     Documentation/devicetree/bindings/net/dsa/
@@ -10679,6 +10775,14 @@ L:     linux-nfc@lists.01.org (moderated for non-subscribers)
 S:     Supported
 F:     drivers/nfc/nxp-nci
 
+OBJAGG
+M:     Jiri Pirko <jiri@mellanox.com>
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     lib/objagg.c
+F:     lib/test_objagg.c
+F:     include/linux/objagg.h
+
 OBJTOOL
 M:     Josh Poimboeuf <jpoimboe@redhat.com>
 M:     Peter Zijlstra <peterz@infradead.org>
@@ -10801,9 +10905,9 @@ F:      drivers/media/platform/omap3isp/
 F:     drivers/staging/media/omap4iss/
 
 OMAP MMC SUPPORT
-M:     Jarkko Lavinen <jarkko.lavinen@nokia.com>
+M:     Aaro Koskinen <aaro.koskinen@iki.fi>
 L:     linux-omap@vger.kernel.org
-S:     Maintained
+S:     Odd Fixes
 F:     drivers/mmc/host/omap.c
 
 OMAP POWER MANAGEMENT SUPPORT
@@ -10886,7 +10990,7 @@ F:      include/linux/platform_data/i2c-omap.h
 
 ONION OMEGA2+ BOARD
 M:     Harvey Hunt <harveyhuntnexus@gmail.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     arch/mips/boot/dts/ralink/omega2p.dts
 
@@ -11738,6 +11842,7 @@ F:      Documentation/devicetree/bindings/pinctrl/fsl,*
 PIN CONTROLLER - INTEL
 M:     Mika Westerberg <mika.westerberg@linux.intel.com>
 M:     Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/pinctrl/intel.git
 S:     Maintained
 F:     drivers/pinctrl/intel/
 
@@ -11794,7 +11899,7 @@ F:      drivers/pinctrl/spear/
 
 PISTACHIO SOC SUPPORT
 M:     James Hartley <james.hartley@sondrel.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Odd Fixes
 F:     arch/mips/pistachio/
 F:     arch/mips/include/asm/mach-pistachio/
@@ -11974,7 +12079,7 @@ F:      kernel/printk/
 F:     include/linux/printk.h
 
 PRISM54 WIRELESS DRIVER
-M:     "Luis R. Rodriguez" <mcgrof@gmail.com>
+M:     Luis Chamberlain <mcgrof@kernel.org>
 L:     linux-wireless@vger.kernel.org
 W:     http://wireless.kernel.org/en/users/Drivers/p54
 S:     Obsolete
@@ -11988,9 +12093,10 @@ S:     Maintained
 F:     fs/proc/
 F:     include/linux/proc_fs.h
 F:     tools/testing/selftests/proc/
+F:     Documentation/filesystems/proc.txt
 
 PROC SYSCTL
-M:     "Luis R. Rodriguez" <mcgrof@kernel.org>
+M:     Luis Chamberlain <mcgrof@kernel.org>
 M:     Kees Cook <keescook@chromium.org>
 L:     linux-kernel@vger.kernel.org
 L:     linux-fsdevel@vger.kernel.org
@@ -12237,7 +12343,7 @@ QLOGIC QLA3XXX NETWORK DRIVER
 M:     Dept-GELinuxNICDev@cavium.com
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/LICENSE.qla3xxx
+F:     Documentation/networking/device_drivers/qlogic/LICENSE.qla3xxx
 F:     drivers/net/ethernet/qlogic/qla3xxx.*
 
 QLOGIC QLA4XXX iSCSI DRIVER
@@ -12289,7 +12395,7 @@ L:      linux-kernel@vger.kernel.org
 S:     Maintained
 F:     drivers/bus/fsl-mc/
 F:     Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt
-F:     Documentation/networking/dpaa2/overview.rst
+F:     Documentation/networking/device_drivers/freescale/dpaa2/overview.rst
 
 QT1010 MEDIA DRIVER
 M:     Antti Palosaari <crope@iki.fi>
@@ -12453,7 +12559,7 @@ F:      drivers/media/usb/rainshadow-cec/*
 
 RALINK MIPS ARCHITECTURE
 M:     John Crispin <john@phrozen.org>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     arch/mips/ralink
 
@@ -12473,7 +12579,7 @@ F:      drivers/block/brd.c
 
 RANCHU VIRTUAL BOARD FOR MIPS
 M:     Miodrag Dinic <miodrag.dinic@mips.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Supported
 F:     arch/mips/generic/board-ranchu.c
 F:     arch/mips/configs/generic/board-ranchu.config
@@ -13798,6 +13904,13 @@ F:     drivers/md/raid*
 F:     include/linux/raid/
 F:     include/uapi/linux/raid/
 
+SOCIONEXT (SNI) AVE NETWORK DRIVER
+M:     Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     drivers/net/ethernet/socionext/sni_ave.c
+F:     Documentation/devicetree/bindings/net/socionext,uniphier-ave4.txt
+
 SOCIONEXT (SNI) NETSEC NETWORK DRIVER
 M:     Jassi Brar <jaswinder.singh@linaro.org>
 L:     netdev@vger.kernel.org
@@ -13923,6 +14036,7 @@ S:      Supported
 F:     Documentation/devicetree/bindings/sound/
 F:     Documentation/sound/soc/
 F:     sound/soc/
+F:     include/dt-bindings/sound/
 F:     include/sound/soc*
 
 SOUNDWIRE SUBSYSTEM
@@ -13970,11 +14084,10 @@ F:    drivers/tty/serial/sunzilog.h
 F:     drivers/tty/vcc.c
 
 SPARSE CHECKER
-M:     "Christopher Li" <sparse@chrisli.org>
+M:     "Luc Van Oostenryck" <luc.vanoostenryck@gmail.com>
 L:     linux-sparse@vger.kernel.org
 W:     https://sparse.wiki.kernel.org/
 T:     git git://git.kernel.org/pub/scm/devel/sparse/sparse.git
-T:     git git://git.kernel.org/pub/scm/devel/sparse/chrisl/sparse.git
 S:     Maintained
 F:     include/linux/compiler.h
 
@@ -14022,7 +14135,7 @@ SPIDERNET NETWORK DRIVER for CELL
 M:     Ishizaki Kou <kou.ishizaki@toshiba.co.jp>
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/spider_net.txt
+F:     Documentation/networking/device_drivers/toshiba/spider_net.txt
 F:     drivers/net/ethernet/toshiba/spider_net*
 
 SPMI SUBSYSTEM
@@ -14071,6 +14184,7 @@ F:      Documentation/devicetree/bindings/iio/proximity/vl53l0x.txt
 
 STABLE BRANCH
 M:     Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+M:     Sasha Levin <sashal@kernel.org>
 L:     stable@vger.kernel.org
 S:     Supported
 F:     Documentation/process/stable-kernel-rules.rst
@@ -15011,7 +15125,7 @@ M:      Samuel Chessman <chessman@tux.org>
 L:     tlan-devel@lists.sourceforge.net (subscribers-only)
 W:     http://sourceforge.net/projects/tlan/
 S:     Maintained
-F:     Documentation/networking/tlan.txt
+F:     Documentation/networking/device_drivers/ti/tlan.txt
 F:     drivers/net/ethernet/ti/tlan.*
 
 TM6000 VIDEO4LINUX DRIVER
@@ -15208,7 +15322,7 @@ F:      arch/um/os-Linux/drivers/
 TURBOCHANNEL SUBSYSTEM
 M:     "Maciej W. Rozycki" <macro@linux-mips.org>
 M:     Ralf Baechle <ralf@linux-mips.org>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 Q:     http://patchwork.linux-mips.org/project/linux-mips/list/
 S:     Maintained
 F:     drivers/tc/
@@ -16029,7 +16143,7 @@ F:      drivers/net/vmxnet3/
 
 VOCORE VOCORE2 BOARD
 M:     Harvey Hunt <harveyhuntnexus@gmail.com>
-L:     linux-mips@linux-mips.org
+L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     arch/mips/boot/dts/ralink/vocore2.dts
 
index 2f36db8..d45856f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,8 @@
 VERSION = 4
 PATCHLEVEL = 20
 SUBLEVEL = 0
-EXTRAVERSION = -rc2
-NAME = "People's Front"
+EXTRAVERSION = -rc7
+NAME = Shy Crocodile
 
 # *DOCUMENTATION*
 # To see a list of typical targets execute "make help"
@@ -962,11 +962,6 @@ ifdef CONFIG_STACK_VALIDATION
   ifeq ($(has_libelf),1)
     objtool_target := tools/objtool FORCE
   else
-    ifdef CONFIG_UNWINDER_ORC
-      $(error "Cannot generate ORC metadata for CONFIG_UNWINDER_ORC=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel")
-    else
-      $(warning "Cannot use CONFIG_STACK_VALIDATION=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel")
-    endif
     SKIP_STACK_VALIDATION := 1
     export SKIP_STACK_VALIDATION
   endif
@@ -1125,6 +1120,14 @@ uapi-asm-generic:
 
 PHONY += prepare-objtool
 prepare-objtool: $(objtool_target)
+ifeq ($(SKIP_STACK_VALIDATION),1)
+ifdef CONFIG_UNWINDER_ORC
+       @echo "error: Cannot generate ORC metadata for CONFIG_UNWINDER_ORC=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel" >&2
+       @false
+else
+       @echo "warning: Cannot use CONFIG_STACK_VALIDATION=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel" >&2
+endif
+endif
 
 # Generate some files
 # ---------------------------------------------------------------------------
index a37fd99..4b5b1b2 100644 (file)
@@ -634,6 +634,7 @@ setup_arch(char **cmdline_p)
 
        /* Find our memory.  */
        setup_memory(kernel_end);
+       memblock_set_bottom_up(true);
 
        /* First guess at cpu cache sizes.  Do this before init_arch.  */
        determine_cpu_caches(cpu->type);
index 7484655..d0b7337 100644 (file)
@@ -144,14 +144,14 @@ setup_memory_node(int nid, void *kernel_end)
        if (!nid && (node_max_pfn < end_kernel_pfn || node_min_pfn > start_kernel_pfn))
                panic("kernel loaded out of ram");
 
+       memblock_add(PFN_PHYS(node_min_pfn),
+                    (node_max_pfn - node_min_pfn) << PAGE_SHIFT);
+
        /* Zone start phys-addr must be 2^(MAX_ORDER-1) aligned.
           Note that we round this down, not up - node memory
           has much larger alignment than 8Mb, so it's safe. */
        node_min_pfn &= ~((1UL << (MAX_ORDER-1))-1);
 
-       memblock_add(PFN_PHYS(node_min_pfn),
-                    (node_max_pfn - node_min_pfn) << PAGE_SHIFT);
-
        NODE_DATA(nid)->node_start_pfn = node_min_pfn;
        NODE_DATA(nid)->node_present_pages = node_max_pfn - node_min_pfn;
 
index c9e2a13..6dd7835 100644 (file)
@@ -109,7 +109,7 @@ endmenu
 
 choice
        prompt "ARC Instruction Set"
-       default ISA_ARCOMPACT
+       default ISA_ARCV2
 
 config ISA_ARCOMPACT
        bool "ARCompact ISA"
@@ -176,13 +176,11 @@ endchoice
 
 config CPU_BIG_ENDIAN
        bool "Enable Big Endian Mode"
-       default n
        help
          Build kernel for Big Endian Mode of ARC CPU
 
 config SMP
        bool "Symmetric Multi-Processing"
-       default n
        select ARC_MCIP if ISA_ARCV2
        help
          This enables support for systems with more than one CPU.
@@ -254,7 +252,6 @@ config ARC_CACHE_PAGES
 config ARC_CACHE_VIPT_ALIASING
        bool "Support VIPT Aliasing D$"
        depends on ARC_HAS_DCACHE && ISA_ARCOMPACT
-       default n
 
 endif  #ARC_CACHE
 
@@ -262,7 +259,6 @@ config ARC_HAS_ICCM
        bool "Use ICCM"
        help
          Single Cycle RAMS to store Fast Path Code
-       default n
 
 config ARC_ICCM_SZ
        int "ICCM Size in KB"
@@ -273,7 +269,6 @@ config ARC_HAS_DCCM
        bool "Use DCCM"
        help
          Single Cycle RAMS to store Fast Path Data
-       default n
 
 config ARC_DCCM_SZ
        int "DCCM Size in KB"
@@ -366,13 +361,11 @@ if ISA_ARCOMPACT
 
 config ARC_COMPACT_IRQ_LEVELS
        bool "Setup Timer IRQ as high Priority"
-       default n
        # if SMP, LV2 enabled ONLY if ARC implementation has LV2 re-entrancy
        depends on !SMP
 
 config ARC_FPU_SAVE_RESTORE
        bool "Enable FPU state persistence across context switch"
-       default n
        help
          Double Precision Floating Point unit had dedicated regs which
          need to be saved/restored across context-switch.
@@ -453,7 +446,6 @@ config HIGHMEM
 
 config ARC_HAS_PAE40
        bool "Support for the 40-bit Physical Address Extension"
-       default n
        depends on ISA_ARCV2
        select HIGHMEM
        select PHYS_ADDR_T_64BIT
@@ -496,7 +488,6 @@ config HZ
 
 config ARC_METAWARE_HLINK
        bool "Support for Metaware debugger assisted Host access"
-       default n
        help
          This options allows a Linux userland apps to directly access
          host file system (open/creat/read/write etc) with help from
@@ -524,13 +515,11 @@ config ARC_DW2_UNWIND
 
 config ARC_DBG_TLB_PARANOIA
        bool "Paranoia Checks in Low Level TLB Handlers"
-       default n
 
 endif
 
 config ARC_UBOOT_SUPPORT
        bool "Support uboot arg Handling"
-       default n
        help
          ARC Linux by default checks for uboot provided args as pointers to
          external cmdline or DTB. This however breaks in absence of uboot,
index c64c505..df00578 100644 (file)
@@ -6,7 +6,7 @@
 # published by the Free Software Foundation.
 #
 
-KBUILD_DEFCONFIG := nsim_700_defconfig
+KBUILD_DEFCONFIG := nsim_hs_defconfig
 
 cflags-y       += -fno-common -pipe -fno-builtin -mmedium-calls -D__linux__
 cflags-$(CONFIG_ISA_ARCOMPACT) += -mA7
index ef149f5..43f17b5 100644 (file)
                        bus-width = <4>;
                        dma-coherent;
                };
+
+               gpio: gpio@3000 {
+                       compatible = "snps,dw-apb-gpio";
+                       reg = <0x3000 0x20>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       gpio_port_a: gpio-controller@0 {
+                               compatible = "snps,dw-apb-gpio-port";
+                               gpio-controller;
+                               #gpio-cells = <2>;
+                               snps,nr-gpios = <24>;
+                               reg = <0>;
+                       };
+               };
        };
 
        memory@80000000 {
index 41bc08b..020d449 100644 (file)
@@ -14,6 +14,7 @@ CONFIG_PERF_EVENTS=y
 # CONFIG_VM_EVENT_COUNTERS is not set
 # CONFIG_SLUB_DEBUG is not set
 # CONFIG_COMPAT_BRK is not set
+CONFIG_ISA_ARCOMPACT=y
 CONFIG_MODULES=y
 CONFIG_MODULE_FORCE_LOAD=y
 CONFIG_MODULE_UNLOAD=y
@@ -95,6 +96,7 @@ CONFIG_VFAT_FS=y
 CONFIG_NTFS_FS=y
 CONFIG_TMPFS=y
 CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
index 1e1c4a8..666314f 100644 (file)
@@ -94,6 +94,7 @@ CONFIG_VFAT_FS=y
 CONFIG_NTFS_FS=y
 CONFIG_TMPFS=y
 CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
index 6b0c0cf..429832b 100644 (file)
@@ -97,6 +97,7 @@ CONFIG_VFAT_FS=y
 CONFIG_NTFS_FS=y
 CONFIG_TMPFS=y
 CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
index 1dec2b4..87b23b7 100644 (file)
@@ -45,6 +45,9 @@ CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_SERIAL_8250_DW=y
 CONFIG_SERIAL_OF_PLATFORM=y
 # CONFIG_HW_RANDOM is not set
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_DWAPB=y
 # CONFIG_HWMON is not set
 CONFIG_DRM=y
 # CONFIG_DRM_FBDEV_EMULATION is not set
@@ -65,6 +68,7 @@ CONFIG_EXT3_FS=y
 CONFIG_VFAT_FS=y
 CONFIG_TMPFS=y
 CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
index 31ba224..6e84060 100644 (file)
@@ -15,6 +15,7 @@ CONFIG_SYSCTL_SYSCALL=y
 CONFIG_EMBEDDED=y
 CONFIG_PERF_EVENTS=y
 # CONFIG_COMPAT_BRK is not set
+CONFIG_ISA_ARCOMPACT=y
 CONFIG_KPROBES=y
 CONFIG_MODULES=y
 CONFIG_MODULE_FORCE_LOAD=y
@@ -73,6 +74,7 @@ CONFIG_PROC_KCORE=y
 CONFIG_TMPFS=y
 # CONFIG_MISC_FILESYSTEMS is not set
 CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
 CONFIG_ROOT_NFS=y
 CONFIG_DEBUG_INFO=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
index 8e0b8b1..219c2a6 100644 (file)
@@ -15,6 +15,7 @@ CONFIG_EMBEDDED=y
 CONFIG_PERF_EVENTS=y
 # CONFIG_SLUB_DEBUG is not set
 # CONFIG_COMPAT_BRK is not set
+CONFIG_ISA_ARCOMPACT=y
 CONFIG_KPROBES=y
 CONFIG_MODULES=y
 # CONFIG_LBDAF is not set
index f14eeff..35dfc64 100644 (file)
@@ -15,6 +15,7 @@ CONFIG_EMBEDDED=y
 CONFIG_PERF_EVENTS=y
 # CONFIG_SLUB_DEBUG is not set
 # CONFIG_COMPAT_BRK is not set
+CONFIG_ISA_ARCOMPACT=y
 CONFIG_KPROBES=y
 CONFIG_MODULES=y
 # CONFIG_LBDAF is not set
@@ -66,5 +67,6 @@ CONFIG_EXT2_FS_XATTR=y
 CONFIG_TMPFS=y
 # CONFIG_MISC_FILESYSTEMS is not set
 CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
 # CONFIG_ENABLE_MUST_CHECK is not set
index 025298a..1638e5b 100644 (file)
@@ -65,5 +65,6 @@ CONFIG_EXT2_FS_XATTR=y
 CONFIG_TMPFS=y
 # CONFIG_MISC_FILESYSTEMS is not set
 CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
 # CONFIG_ENABLE_MUST_CHECK is not set
index df7b77b..11cfbdb 100644 (file)
@@ -76,6 +76,7 @@ CONFIG_EXT2_FS_XATTR=y
 CONFIG_TMPFS=y
 # CONFIG_MISC_FILESYSTEMS is not set
 CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
 # CONFIG_ENABLE_MUST_CHECK is not set
 CONFIG_FTRACE=y
index a7f6531..e71ade3 100644 (file)
@@ -19,6 +19,7 @@ CONFIG_KALLSYMS_ALL=y
 # CONFIG_AIO is not set
 CONFIG_EMBEDDED=y
 # CONFIG_COMPAT_BRK is not set
+CONFIG_ISA_ARCOMPACT=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
 CONFIG_MODULE_FORCE_LOAD=y
index db47c35..1e59a2e 100644 (file)
@@ -85,6 +85,7 @@ CONFIG_NTFS_FS=y
 CONFIG_TMPFS=y
 CONFIG_JFFS2_FS=y
 CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
index a8ac5e9..b5c3f6c 100644 (file)
@@ -90,6 +90,7 @@ CONFIG_NTFS_FS=y
 CONFIG_TMPFS=y
 CONFIG_JFFS2_FS=y
 CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
index ff7d323..f393b66 100644 (file)
@@ -113,7 +113,9 @@ extern unsigned long perip_base, perip_end;
 
 /* IO coherency related Auxiliary registers */
 #define ARC_REG_IO_COH_ENABLE  0x500
+#define ARC_IO_COH_ENABLE_BIT  BIT(0)
 #define ARC_REG_IO_COH_PARTIAL 0x501
+#define ARC_IO_COH_PARTIAL_BIT BIT(0)
 #define ARC_REG_IO_COH_AP0_BASE        0x508
 #define ARC_REG_IO_COH_AP0_SIZE        0x509
 
index c22b181..2f39d9b 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/types.h>
 #include <asm/byteorder.h>
 #include <asm/page.h>
+#include <asm/unaligned.h>
 
 #ifdef CONFIG_ISA_ARCV2
 #include <asm/barrier.h>
@@ -94,6 +95,42 @@ static inline u32 __raw_readl(const volatile void __iomem *addr)
        return w;
 }
 
+/*
+ * {read,write}s{b,w,l}() repeatedly access the same IO address in
+ * native endianness in 8-, 16-, 32-bit chunks {into,from} memory,
+ * @count times
+ */
+#define __raw_readsx(t,f) \
+static inline void __raw_reads##f(const volatile void __iomem *addr,   \
+                                 void *ptr, unsigned int count)        \
+{                                                                      \
+       bool is_aligned = ((unsigned long)ptr % ((t) / 8)) == 0;        \
+       u##t *buf = ptr;                                                \
+                                                                       \
+       if (!count)                                                     \
+               return;                                                 \
+                                                                       \
+       /* Some ARC CPU's don't support unaligned accesses */           \
+       if (is_aligned) {                                               \
+               do {                                                    \
+                       u##t x = __raw_read##f(addr);                   \
+                       *buf++ = x;                                     \
+               } while (--count);                                      \
+       } else {                                                        \
+               do {                                                    \
+                       u##t x = __raw_read##f(addr);                   \
+                       put_unaligned(x, buf++);                        \
+               } while (--count);                                      \
+       }                                                               \
+}
+
+#define __raw_readsb __raw_readsb
+__raw_readsx(8, b)
+#define __raw_readsw __raw_readsw
+__raw_readsx(16, w)
+#define __raw_readsl __raw_readsl
+__raw_readsx(32, l)
+
 #define __raw_writeb __raw_writeb
 static inline void __raw_writeb(u8 b, volatile void __iomem *addr)
 {
@@ -126,6 +163,35 @@ static inline void __raw_writel(u32 w, volatile void __iomem *addr)
 
 }
 
+#define __raw_writesx(t,f)                                             \
+static inline void __raw_writes##f(volatile void __iomem *addr,        \
+                                  const void *ptr, unsigned int count) \
+{                                                                      \
+       bool is_aligned = ((unsigned long)ptr % ((t) / 8)) == 0;        \
+       const u##t *buf = ptr;                                          \
+                                                                       \
+       if (!count)                                                     \
+               return;                                                 \
+                                                                       \
+       /* Some ARC CPU's don't support unaligned accesses */           \
+       if (is_aligned) {                                               \
+               do {                                                    \
+                       __raw_write##f(*buf++, addr);                   \
+               } while (--count);                                      \
+       } else {                                                        \
+               do {                                                    \
+                       __raw_write##f(get_unaligned(buf++), addr);     \
+               } while (--count);                                      \
+       }                                                               \
+}
+
+#define __raw_writesb __raw_writesb
+__raw_writesx(8, b)
+#define __raw_writesw __raw_writesw
+__raw_writesx(16, w)
+#define __raw_writesl __raw_writesl
+__raw_writesx(32, l)
+
 /*
  * MMIO can also get buffered/optimized in micro-arch, so barriers needed
  * Based on ARM model for the typical use case
@@ -141,10 +207,16 @@ static inline void __raw_writel(u32 w, volatile void __iomem *addr)
 #define readb(c)               ({ u8  __v = readb_relaxed(c); __iormb(); __v; })
 #define readw(c)               ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
 #define readl(c)               ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
+#define readsb(p,d,l)          ({ __raw_readsb(p,d,l); __iormb(); })
+#define readsw(p,d,l)          ({ __raw_readsw(p,d,l); __iormb(); })
+#define readsl(p,d,l)          ({ __raw_readsl(p,d,l); __iormb(); })
 
 #define writeb(v,c)            ({ __iowmb(); writeb_relaxed(v,c); })
 #define writew(v,c)            ({ __iowmb(); writew_relaxed(v,c); })
 #define writel(v,c)            ({ __iowmb(); writel_relaxed(v,c); })
+#define writesb(p,d,l)         ({ __iowmb(); __raw_writesb(p,d,l); })
+#define writesw(p,d,l)         ({ __iowmb(); __raw_writesw(p,d,l); })
+#define writesl(p,d,l)         ({ __iowmb(); __raw_writesl(p,d,l); })
 
 /*
  * Relaxed API for drivers which can handle barrier ordering themselves
index b2cae79..eea8c5c 100644 (file)
@@ -243,7 +243,7 @@ static char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len)
 {
        struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id];
        struct bcr_identity *core = &cpu->core;
-       int i, n = 0;
+       int i, n = 0, ua = 0;
 
        FIX_PTR(cpu);
 
@@ -263,10 +263,13 @@ static char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len)
                       IS_AVAIL2(cpu->extn.rtc, "RTC [UP 64-bit] ", CONFIG_ARC_TIMERS_64BIT),
                       IS_AVAIL2(cpu->extn.gfrc, "GFRC [SMP 64-bit] ", CONFIG_ARC_TIMERS_64BIT));
 
-       n += i = scnprintf(buf + n, len - n, "%s%s%s%s%s",
+#ifdef __ARC_UNALIGNED__
+       ua = 1;
+#endif
+       n += i = scnprintf(buf + n, len - n, "%s%s%s%s%s%s",
                           IS_AVAIL2(cpu->isa.atomic, "atomic ", CONFIG_ARC_HAS_LLSC),
                           IS_AVAIL2(cpu->isa.ldd, "ll64 ", CONFIG_ARC_HAS_LL64),
-                          IS_AVAIL1(cpu->isa.unalign, "unalign (not used)"));
+                          IS_AVAIL1(cpu->isa.unalign, "unalign "), IS_USED_RUN(ua));
 
        if (i)
                n += scnprintf(buf + n, len - n, "\n\t\t: ");
index f2701c1..cf9619d 100644 (file)
@@ -1144,6 +1144,20 @@ noinline void __init arc_ioc_setup(void)
 {
        unsigned int ioc_base, mem_sz;
 
+       /*
+        * If IOC was already enabled (due to bootloader) it technically needs to
+        * be reconfigured with aperture base,size corresponding to Linux memory map
+        * which will certainly be different than uboot's. But disabling and
+        * reenabling IOC when DMA might be potentially active is tricky business.
+        * To avoid random memory issues later, just panic here and ask user to
+        * upgrade bootloader to one which doesn't enable IOC
+        */
+       if (read_aux_reg(ARC_REG_IO_COH_ENABLE) & ARC_IO_COH_ENABLE_BIT)
+               panic("IOC already enabled, please upgrade bootloader!\n");
+
+       if (!ioc_enable)
+               return;
+
        /*
         * As for today we don't support both IOC and ZONE_HIGHMEM enabled
         * simultaneously. This happens because as of today IOC aperture covers
@@ -1187,8 +1201,8 @@ noinline void __init arc_ioc_setup(void)
                panic("IOC Aperture start must be aligned to the size of the aperture");
 
        write_aux_reg(ARC_REG_IO_COH_AP0_BASE, ioc_base >> 12);
-       write_aux_reg(ARC_REG_IO_COH_PARTIAL, 1);
-       write_aux_reg(ARC_REG_IO_COH_ENABLE, 1);
+       write_aux_reg(ARC_REG_IO_COH_PARTIAL, ARC_IO_COH_PARTIAL_BIT);
+       write_aux_reg(ARC_REG_IO_COH_ENABLE, ARC_IO_COH_ENABLE_BIT);
 
        /* Re-enable L1 dcache */
        __dc_enable();
@@ -1265,7 +1279,7 @@ void __init arc_cache_init_master(void)
        if (is_isa_arcv2() && l2_line_sz && !slc_enable)
                arc_slc_disable();
 
-       if (is_isa_arcv2() && ioc_enable)
+       if (is_isa_arcv2() && ioc_exists)
                arc_ioc_setup();
 
        if (is_isa_arcv2() && l2_line_sz && slc_enable) {
index c9da610..e2d9fc3 100644 (file)
@@ -66,7 +66,7 @@ void do_page_fault(unsigned long address, struct pt_regs *regs)
        struct vm_area_struct *vma = NULL;
        struct task_struct *tsk = current;
        struct mm_struct *mm = tsk->mm;
-       int si_code;
+       int si_code = 0;
        int ret;
        vm_fault_t fault;
        int write = regs->ecr_cause & ECR_C_PROTV_STORE;  /* ST/EX */
index d4d33cd..1e2bb68 100644 (file)
        vmmc-supply = <&vmmc_fixed>;
        bus-width = <4>;
        wp-gpios = <&gpio4 30 GPIO_ACTIVE_HIGH>; /* gpio_126 */
-       cd-gpios = <&gpio4 31 GPIO_ACTIVE_HIGH>; /* gpio_127 */
+       cd-gpios = <&gpio4 31 GPIO_ACTIVE_LOW>; /* gpio_127 */
 };
 
 &mmc3 {
index dae6e45..b1c988e 100644 (file)
                compatible = "ti,wl1271";
                reg = <2>;
                interrupt-parent = <&gpio6>;
-               interrupts = <10 IRQ_TYPE_LEVEL_HIGH>; /* gpio_170 */
+               interrupts = <10 IRQ_TYPE_EDGE_RISING>; /* gpio_170 */
                ref-clock-frequency = <26000000>;
                tcxo-clock-frequency = <26000000>;
        };
index f2a1d25..83e0fbc 100644 (file)
@@ -45,7 +45,7 @@
        };
 
        /* The voltage to the MMC card is hardwired at 3.3V */
-       vmmc: fixedregulator@0 {
+       vmmc: regulator-vmmc {
                compatible = "regulator-fixed";
                regulator-name = "vmmc";
                regulator-min-microvolt = <3300000>;
@@ -53,7 +53,7 @@
                regulator-boot-on;
         };
 
-       veth: fixedregulator@0 {
+       veth: regulator-veth {
                compatible = "regulator-fixed";
                regulator-name = "veth";
                regulator-min-microvolt = <3300000>;
index 7f9cbdf..2f6aa24 100644 (file)
        };
 
        /* The voltage to the MMC card is hardwired at 3.3V */
-       vmmc: fixedregulator@0 {
+       vmmc: regulator-vmmc {
                compatible = "regulator-fixed";
                regulator-name = "vmmc";
                regulator-min-microvolt = <3300000>;
                regulator-boot-on;
         };
 
-       veth: fixedregulator@0 {
+       veth: regulator-veth {
                compatible = "regulator-fixed";
                regulator-name = "veth";
                regulator-min-microvolt = <3300000>;
index 4adb85e..9376224 100644 (file)
@@ -31,7 +31,7 @@
 
        wifi_pwrseq: wifi-pwrseq {
                compatible = "mmc-pwrseq-simple";
-               reset-gpios = <&expgpio 1 GPIO_ACTIVE_HIGH>;
+               reset-gpios = <&expgpio 1 GPIO_ACTIVE_LOW>;
        };
 };
 
index c318bcb..89e6fd5 100644 (file)
@@ -26,7 +26,7 @@
 
        wifi_pwrseq: wifi-pwrseq {
                compatible = "mmc-pwrseq-simple";
-               reset-gpios = <&expgpio 1 GPIO_ACTIVE_HIGH>;
+               reset-gpios = <&expgpio 1 GPIO_ACTIVE_LOW>;
        };
 };
 
index e45a15c..69d753c 100644 (file)
        pinctrl-0 = <&pinctrl_i2c2>;
        status = "okay";
 
-       eeprom@50 {
-               compatible = "atmel,24c04";
-               pagesize = <16>;
-               reg = <0x50>;
-       };
-
        hpa1: amp@60 {
                compatible = "ti,tpa6130a2";
                reg = <0x60>;
index d8aac4a..177d21f 100644 (file)
                compatible = "regulator-fixed";
                regulator-min-microvolt = <3300000>;
                regulator-max-microvolt = <3300000>;
-               clocks = <&clks IMX7D_CLKO2_ROOT_DIV>;
-               clock-names = "slow";
                regulator-name = "reg_wlan";
                startup-delay-us = <70000>;
                gpio = <&gpio4 21 GPIO_ACTIVE_HIGH>;
                enable-active-high;
        };
+
+       usdhc2_pwrseq: usdhc2_pwrseq {
+               compatible = "mmc-pwrseq-simple";
+               clocks = <&clks IMX7D_CLKO2_ROOT_DIV>;
+               clock-names = "ext_clock";
+       };
 };
 
 &adc1 {
        bus-width = <4>;
        non-removable;
        vmmc-supply = <&reg_wlan>;
+       mmc-pwrseq = <&usdhc2_pwrseq>;
        cap-power-off-card;
        keep-power-in-suspend;
        status = "okay";
index 21973eb..f27b384 100644 (file)
                regulator-min-microvolt = <1800000>;
                regulator-max-microvolt = <1800000>;
        };
+
+       usdhc2_pwrseq: usdhc2_pwrseq {
+               compatible = "mmc-pwrseq-simple";
+               clocks = <&clks IMX7D_CLKO2_ROOT_DIV>;
+               clock-names = "ext_clock";
+       };
+};
+
+&clks {
+       assigned-clocks = <&clks IMX7D_CLKO2_ROOT_SRC>,
+                         <&clks IMX7D_CLKO2_ROOT_DIV>;
+       assigned-clock-parents = <&clks IMX7D_CKIL>;
+       assigned-clock-rates = <0>, <32768>;
 };
 
 &i2c4 {
 
 &usdhc2 { /* Wifi SDIO */
        pinctrl-names = "default";
-       pinctrl-0 = <&pinctrl_usdhc2>;
+       pinctrl-0 = <&pinctrl_usdhc2 &pinctrl_wifi_clk>;
        no-1-8-v;
        non-removable;
        keep-power-in-suspend;
        wakeup-source;
        vmmc-supply = <&reg_ap6212>;
+       mmc-pwrseq = <&usdhc2_pwrseq>;
        status = "okay";
 };
 
 };
 
 &iomuxc_lpsr {
+       pinctrl_wifi_clk: wificlkgrp {
+               fsl,pins = <
+                       MX7D_PAD_LPSR_GPIO1_IO03__CCM_CLKO2     0x7d
+               >;
+       };
+
        pinctrl_wdog: wdoggrp {
                fsl,pins = <
                        MX7D_PAD_LPSR_GPIO1_IO00__WDOG1_WDOG_B  0x74
index ac34333..98b682a 100644 (file)
 };
 
 &mmc3 {
-       interrupts-extended = <&intc 94 &omap3_pmx_core2 0x46>;
+       interrupts-extended = <&intc 94 &omap3_pmx_core 0x136>;
        pinctrl-0 = <&mmc3_pins &wl127x_gpio>;
        pinctrl-names = "default";
        vmmc-supply = <&wl12xx_vmmc>;
index 9d5d53f..c39cf2c 100644 (file)
@@ -35,7 +35,7 @@
  * jumpering combinations for the long run.
  */
 &mmc3 {
-       interrupts-extended = <&intc 94 &omap3_pmx_core2 0x46>;
+       interrupts-extended = <&intc 94 &omap3_pmx_core 0x136>;
        pinctrl-0 = <&mmc3_pins &mmc3_core2_pins>;
        pinctrl-names = "default";
        vmmc-supply = <&wl12xx_vmmc>;
index 2075120..d8bf939 100644 (file)
 #include "rk3288.dtsi"
 
 / {
-       memory@0 {
+       /*
+        * The default coreboot on veyron devices ignores memory@0 nodes
+        * and would instead create another memory node.
+        */
+       memory {
                device_type = "memory";
                reg = <0x0 0x0 0x0 0x80000000>;
        };
index 843052f..dd0dda6 100644 (file)
                                  0x1 0x0 0x60000000 0x10000000
                                  0x2 0x0 0x70000000 0x10000000
                                  0x3 0x0 0x80000000 0x10000000>;
-                       clocks = <&mck>;
+                       clocks = <&h32ck>;
                        status = "disabled";
 
                        nand_controller: nand-controller {
index 742d294..583a5a0 100644 (file)
 
 &reg_dldo3 {
        regulator-always-on;
-       regulator-min-microvolt = <2500000>;
-       regulator-max-microvolt = <2500000>;
+       regulator-min-microvolt = <3300000>;
+       regulator-max-microvolt = <3300000>;
        regulator-name = "vcc-pd";
 };
 
index 0d28924..775cac3 100644 (file)
 #include <linux/kernel.h>
 
 extern unsigned int processor_id;
+struct proc_info_list *lookup_processor(u32 midr);
 
 #ifdef CONFIG_CPU_CP15
 #define read_cpuid(reg)                                                        \
index e25f439..e1b6f28 100644 (file)
@@ -23,7 +23,7 @@ struct mm_struct;
 /*
  * Don't change this structure - ASM code relies on it.
  */
-extern struct processor {
+struct processor {
        /* MISC
         * get data abort address/flags
         */
@@ -79,9 +79,13 @@ extern struct processor {
        unsigned int suspend_size;
        void (*do_suspend)(void *);
        void (*do_resume)(void *);
-} processor;
+};
 
 #ifndef MULTI_CPU
+static inline void init_proc_vtable(const struct processor *p)
+{
+}
+
 extern void cpu_proc_init(void);
 extern void cpu_proc_fin(void);
 extern int cpu_do_idle(void);
@@ -98,17 +102,50 @@ extern void cpu_reset(unsigned long addr, bool hvc) __attribute__((noreturn));
 extern void cpu_do_suspend(void *);
 extern void cpu_do_resume(void *);
 #else
-#define cpu_proc_init                  processor._proc_init
-#define cpu_proc_fin                   processor._proc_fin
-#define cpu_reset                      processor.reset
-#define cpu_do_idle                    processor._do_idle
-#define cpu_dcache_clean_area          processor.dcache_clean_area
-#define cpu_set_pte_ext                        processor.set_pte_ext
-#define cpu_do_switch_mm               processor.switch_mm
 
-/* These three are private to arch/arm/kernel/suspend.c */
-#define cpu_do_suspend                 processor.do_suspend
-#define cpu_do_resume                  processor.do_resume
+extern struct processor processor;
+#if defined(CONFIG_BIG_LITTLE) && defined(CONFIG_HARDEN_BRANCH_PREDICTOR)
+#include <linux/smp.h>
+/*
+ * This can't be a per-cpu variable because we need to access it before
+ * per-cpu has been initialised.  We have a couple of functions that are
+ * called in a pre-emptible context, and so can't use smp_processor_id()
+ * there, hence PROC_TABLE().  We insist in init_proc_vtable() that the
+ * function pointers for these are identical across all CPUs.
+ */
+extern struct processor *cpu_vtable[];
+#define PROC_VTABLE(f)                 cpu_vtable[smp_processor_id()]->f
+#define PROC_TABLE(f)                  cpu_vtable[0]->f
+static inline void init_proc_vtable(const struct processor *p)
+{
+       unsigned int cpu = smp_processor_id();
+       *cpu_vtable[cpu] = *p;
+       WARN_ON_ONCE(cpu_vtable[cpu]->dcache_clean_area !=
+                    cpu_vtable[0]->dcache_clean_area);
+       WARN_ON_ONCE(cpu_vtable[cpu]->set_pte_ext !=
+                    cpu_vtable[0]->set_pte_ext);
+}
+#else
+#define PROC_VTABLE(f)                 processor.f
+#define PROC_TABLE(f)                  processor.f
+static inline void init_proc_vtable(const struct processor *p)
+{
+       processor = *p;
+}
+#endif
+
+#define cpu_proc_init                  PROC_VTABLE(_proc_init)
+#define cpu_check_bugs                 PROC_VTABLE(check_bugs)
+#define cpu_proc_fin                   PROC_VTABLE(_proc_fin)
+#define cpu_reset                      PROC_VTABLE(reset)
+#define cpu_do_idle                    PROC_VTABLE(_do_idle)
+#define cpu_dcache_clean_area          PROC_TABLE(dcache_clean_area)
+#define cpu_set_pte_ext                        PROC_TABLE(set_pte_ext)
+#define cpu_do_switch_mm               PROC_VTABLE(switch_mm)
+
+/* These two are private to arch/arm/kernel/suspend.c */
+#define cpu_do_suspend                 PROC_VTABLE(do_suspend)
+#define cpu_do_resume                  PROC_VTABLE(do_resume)
 #endif
 
 extern void cpu_resume(void);
index 7be5113..d41d359 100644 (file)
@@ -6,8 +6,8 @@
 void check_other_bugs(void)
 {
 #ifdef MULTI_CPU
-       if (processor.check_bugs)
-               processor.check_bugs();
+       if (cpu_check_bugs)
+               cpu_check_bugs();
 #endif
 }
 
index 0142fcf..bda949f 100644 (file)
@@ -183,9 +183,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
                           unsigned long frame_pointer)
 {
        unsigned long return_hooker = (unsigned long) &return_to_handler;
-       struct ftrace_graph_ent trace;
        unsigned long old;
-       int err;
 
        if (unlikely(atomic_read(&current->tracing_graph_pause)))
                return;
@@ -193,21 +191,8 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
        old = *parent;
        *parent = return_hooker;
 
-       trace.func = self_addr;
-       trace.depth = current->curr_ret_stack + 1;
-
-       /* Only trace if the calling function expects to */
-       if (!ftrace_graph_entry(&trace)) {
+       if (function_graph_enter(old, self_addr, frame_pointer, NULL))
                *parent = old;
-               return;
-       }
-
-       err = ftrace_push_return_trace(old, self_addr, &trace.depth,
-                                      frame_pointer, NULL);
-       if (err == -EBUSY) {
-               *parent = old;
-               return;
-       }
 }
 
 #ifdef CONFIG_DYNAMIC_FTRACE
index 6e0375e..997b023 100644 (file)
@@ -145,6 +145,9 @@ __mmap_switched_data:
 #endif
        .size   __mmap_switched_data, . - __mmap_switched_data
 
+       __FINIT
+       .text
+
 /*
  * This provides a C-API version of __lookup_processor_type
  */
@@ -156,9 +159,6 @@ ENTRY(lookup_processor_type)
        ldmfd   sp!, {r4 - r6, r9, pc}
 ENDPROC(lookup_processor_type)
 
-       __FINIT
-       .text
-
 /*
  * Read processor ID register (CP#15, CR0), and look up in the linker-built
  * supported processor list.  Note that we can't use the absolute addresses
index ac7e088..375b13f 100644 (file)
@@ -114,6 +114,11 @@ EXPORT_SYMBOL(elf_hwcap2);
 
 #ifdef MULTI_CPU
 struct processor processor __ro_after_init;
+#if defined(CONFIG_BIG_LITTLE) && defined(CONFIG_HARDEN_BRANCH_PREDICTOR)
+struct processor *cpu_vtable[NR_CPUS] = {
+       [0] = &processor,
+};
+#endif
 #endif
 #ifdef MULTI_TLB
 struct cpu_tlb_fns cpu_tlb __ro_after_init;
@@ -666,28 +671,33 @@ static void __init smp_build_mpidr_hash(void)
 }
 #endif
 
-static void __init setup_processor(void)
+/*
+ * locate processor in the list of supported processor types.  The linker
+ * builds this table for us from the entries in arch/arm/mm/proc-*.S
+ */
+struct proc_info_list *lookup_processor(u32 midr)
 {
-       struct proc_info_list *list;
+       struct proc_info_list *list = lookup_processor_type(midr);
 
-       /*
-        * locate processor in the list of supported processor
-        * types.  The linker builds this table for us from the
-        * entries in arch/arm/mm/proc-*.S
-        */
-       list = lookup_processor_type(read_cpuid_id());
        if (!list) {
-               pr_err("CPU configuration botched (ID %08x), unable to continue.\n",
-                      read_cpuid_id());
-               while (1);
+               pr_err("CPU%u: configuration botched (ID %08x), CPU halted\n",
+                      smp_processor_id(), midr);
+               while (1)
+               /* can't use cpu_relax() here as it may require MMU setup */;
        }
 
+       return list;
+}
+
+static void __init setup_processor(void)
+{
+       unsigned int midr = read_cpuid_id();
+       struct proc_info_list *list = lookup_processor(midr);
+
        cpu_name = list->cpu_name;
        __cpu_architecture = __get_cpu_architecture();
 
-#ifdef MULTI_CPU
-       processor = *list->proc;
-#endif
+       init_proc_vtable(list->proc);
 #ifdef MULTI_TLB
        cpu_tlb = *list->tlb;
 #endif
@@ -699,7 +709,7 @@ static void __init setup_processor(void)
 #endif
 
        pr_info("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n",
-               cpu_name, read_cpuid_id(), read_cpuid_id() & 15,
+               list->cpu_name, midr, midr & 15,
                proc_arch[cpu_architecture()], get_cr());
 
        snprintf(init_utsname()->machine, __NEW_UTS_LEN + 1, "%s%c",
index 0978282..12a6172 100644 (file)
@@ -42,6 +42,7 @@
 #include <asm/mmu_context.h>
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
+#include <asm/procinfo.h>
 #include <asm/processor.h>
 #include <asm/sections.h>
 #include <asm/tlbflush.h>
@@ -102,6 +103,30 @@ static unsigned long get_arch_pgd(pgd_t *pgd)
 #endif
 }
 
+#if defined(CONFIG_BIG_LITTLE) && defined(CONFIG_HARDEN_BRANCH_PREDICTOR)
+static int secondary_biglittle_prepare(unsigned int cpu)
+{
+       if (!cpu_vtable[cpu])
+               cpu_vtable[cpu] = kzalloc(sizeof(*cpu_vtable[cpu]), GFP_KERNEL);
+
+       return cpu_vtable[cpu] ? 0 : -ENOMEM;
+}
+
+static void secondary_biglittle_init(void)
+{
+       init_proc_vtable(lookup_processor(read_cpuid_id())->proc);
+}
+#else
+static int secondary_biglittle_prepare(unsigned int cpu)
+{
+       return 0;
+}
+
+static void secondary_biglittle_init(void)
+{
+}
+#endif
+
 int __cpu_up(unsigned int cpu, struct task_struct *idle)
 {
        int ret;
@@ -109,6 +134,10 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
        if (!smp_ops.smp_boot_secondary)
                return -ENOSYS;
 
+       ret = secondary_biglittle_prepare(cpu);
+       if (ret)
+               return ret;
+
        /*
         * We need to tell the secondary core where to find
         * its stack and the page tables.
@@ -359,6 +388,8 @@ asmlinkage void secondary_start_kernel(void)
        struct mm_struct *mm = &init_mm;
        unsigned int cpu;
 
+       secondary_biglittle_init();
+
        /*
         * The identity mapping is uncached (strongly ordered), so
         * switch away from it before attempting any exclusive accesses.
index 0bc5bd2..2cc9fe4 100644 (file)
@@ -759,7 +759,9 @@ static struct davinci_id da830_ids[] = {
 };
 
 static struct davinci_gpio_platform_data da830_gpio_platform_data = {
-       .ngpio = 128,
+       .no_auto_base   = true,
+       .base           = 0,
+       .ngpio          = 128,
 };
 
 int __init da830_register_gpio(void)
index 4528bbf..e7b78df 100644 (file)
@@ -719,7 +719,9 @@ int __init da850_register_vpif_capture(struct vpif_capture_config
 }
 
 static struct davinci_gpio_platform_data da850_gpio_platform_data = {
-       .ngpio = 144,
+       .no_auto_base   = true,
+       .base           = 0,
+       .ngpio          = 144,
 };
 
 int __init da850_register_gpio(void)
index 1fd3619..cf78da5 100644 (file)
@@ -701,6 +701,46 @@ static struct resource da8xx_gpio_resources[] = {
        },
        { /* interrupt */
                .start  = IRQ_DA8XX_GPIO0,
+               .end    = IRQ_DA8XX_GPIO0,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DA8XX_GPIO1,
+               .end    = IRQ_DA8XX_GPIO1,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DA8XX_GPIO2,
+               .end    = IRQ_DA8XX_GPIO2,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DA8XX_GPIO3,
+               .end    = IRQ_DA8XX_GPIO3,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DA8XX_GPIO4,
+               .end    = IRQ_DA8XX_GPIO4,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DA8XX_GPIO5,
+               .end    = IRQ_DA8XX_GPIO5,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DA8XX_GPIO6,
+               .end    = IRQ_DA8XX_GPIO6,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DA8XX_GPIO7,
+               .end    = IRQ_DA8XX_GPIO7,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DA8XX_GPIO8,
                .end    = IRQ_DA8XX_GPIO8,
                .flags  = IORESOURCE_IRQ,
        },
index 9f7d38d..4c6e0be 100644 (file)
@@ -548,12 +548,44 @@ static struct resource dm355_gpio_resources[] = {
        },
        {       /* interrupt */
                .start  = IRQ_DM355_GPIOBNK0,
+               .end    = IRQ_DM355_GPIOBNK0,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM355_GPIOBNK1,
+               .end    = IRQ_DM355_GPIOBNK1,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM355_GPIOBNK2,
+               .end    = IRQ_DM355_GPIOBNK2,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM355_GPIOBNK3,
+               .end    = IRQ_DM355_GPIOBNK3,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM355_GPIOBNK4,
+               .end    = IRQ_DM355_GPIOBNK4,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM355_GPIOBNK5,
+               .end    = IRQ_DM355_GPIOBNK5,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM355_GPIOBNK6,
                .end    = IRQ_DM355_GPIOBNK6,
                .flags  = IORESOURCE_IRQ,
        },
 };
 
 static struct davinci_gpio_platform_data dm355_gpio_platform_data = {
+       .no_auto_base   = true,
+       .base           = 0,
        .ngpio          = 104,
 };
 
index abcf2a5..01fb2b0 100644 (file)
@@ -267,12 +267,49 @@ static struct resource dm365_gpio_resources[] = {
        },
        {       /* interrupt */
                .start  = IRQ_DM365_GPIO0,
+               .end    = IRQ_DM365_GPIO0,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM365_GPIO1,
+               .end    = IRQ_DM365_GPIO1,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM365_GPIO2,
+               .end    = IRQ_DM365_GPIO2,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM365_GPIO3,
+               .end    = IRQ_DM365_GPIO3,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM365_GPIO4,
+               .end    = IRQ_DM365_GPIO4,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM365_GPIO5,
+               .end    = IRQ_DM365_GPIO5,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM365_GPIO6,
+               .end    = IRQ_DM365_GPIO6,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM365_GPIO7,
                .end    = IRQ_DM365_GPIO7,
                .flags  = IORESOURCE_IRQ,
        },
 };
 
 static struct davinci_gpio_platform_data dm365_gpio_platform_data = {
+       .no_auto_base   = true,
+       .base           = 0,
        .ngpio          = 104,
        .gpio_unbanked  = 8,
 };
index 0720da7..38f92b7 100644 (file)
@@ -492,12 +492,34 @@ static struct resource dm644_gpio_resources[] = {
        },
        {       /* interrupt */
                .start  = IRQ_GPIOBNK0,
+               .end    = IRQ_GPIOBNK0,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_GPIOBNK1,
+               .end    = IRQ_GPIOBNK1,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_GPIOBNK2,
+               .end    = IRQ_GPIOBNK2,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_GPIOBNK3,
+               .end    = IRQ_GPIOBNK3,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_GPIOBNK4,
                .end    = IRQ_GPIOBNK4,
                .flags  = IORESOURCE_IRQ,
        },
 };
 
 static struct davinci_gpio_platform_data dm644_gpio_platform_data = {
+       .no_auto_base   = true,
+       .base           = 0,
        .ngpio          = 71,
 };
 
index 6bd2ed0..7dc54b2 100644 (file)
@@ -442,12 +442,24 @@ static struct resource dm646x_gpio_resources[] = {
        },
        {       /* interrupt */
                .start  = IRQ_DM646X_GPIOBNK0,
+               .end    = IRQ_DM646X_GPIOBNK0,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM646X_GPIOBNK1,
+               .end    = IRQ_DM646X_GPIOBNK1,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = IRQ_DM646X_GPIOBNK2,
                .end    = IRQ_DM646X_GPIOBNK2,
                .flags  = IORESOURCE_IRQ,
        },
 };
 
 static struct davinci_gpio_platform_data dm646x_gpio_platform_data = {
+       .no_auto_base   = true,
+       .base           = 0,
        .ngpio          = 43,
 };
 
index 243a108..fd0053e 100644 (file)
@@ -110,7 +110,7 @@ int __init imx6sx_cpuidle_init(void)
         * except for power up sw2iso which need to be
         * larger than LDO ramp up time.
         */
-       imx_gpc_set_arm_power_up_timing(2, 1);
+       imx_gpc_set_arm_power_up_timing(0xf, 1);
        imx_gpc_set_arm_power_down_timing(1, 1);
 
        return cpuidle_register(&imx6sx_cpuidle_driver, NULL);
index 446edae..a96abcf 100644 (file)
@@ -44,10 +44,12 @@ static inline int cpu_is_pxa910(void)
 #define cpu_is_pxa910()        (0)
 #endif
 
-#ifdef CONFIG_CPU_MMP2
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_MACH_MMP2_DT)
 static inline int cpu_is_mmp2(void)
 {
-       return (((read_cpuid_id() >> 8) & 0xff) == 0x58);
+       return (((read_cpuid_id() >> 8) & 0xff) == 0x58) &&
+               (((mmp_chip_id & 0xfff) == 0x410) ||
+                ((mmp_chip_id & 0xfff) == 0x610));
 }
 #else
 #define cpu_is_mmp2()  (0)
index 3d191fd..1788674 100644 (file)
@@ -750,6 +750,9 @@ static void modem_pm(struct uart_port *port, unsigned int state, unsigned old)
        struct modem_private_data *priv = port->private_data;
        int ret;
 
+       if (!priv)
+               return;
+
        if (IS_ERR(priv->regulator))
                return;
 
index 9500b6e..f86b72d 100644 (file)
@@ -209,11 +209,61 @@ static int __init omapdss_init_fbdev(void)
 
        return 0;
 }
-#else
-static inline int omapdss_init_fbdev(void)
+
+static const char * const omapdss_compat_names[] __initconst = {
+       "ti,omap2-dss",
+       "ti,omap3-dss",
+       "ti,omap4-dss",
+       "ti,omap5-dss",
+       "ti,dra7-dss",
+};
+
+static struct device_node * __init omapdss_find_dss_of_node(void)
 {
-       return 0;
+       struct device_node *node;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(omapdss_compat_names); ++i) {
+               node = of_find_compatible_node(NULL, NULL,
+                       omapdss_compat_names[i]);
+               if (node)
+                       return node;
+       }
+
+       return NULL;
 }
+
+static int __init omapdss_init_of(void)
+{
+       int r;
+       struct device_node *node;
+       struct platform_device *pdev;
+
+       /* only create dss helper devices if dss is enabled in the .dts */
+
+       node = omapdss_find_dss_of_node();
+       if (!node)
+               return 0;
+
+       if (!of_device_is_available(node))
+               return 0;
+
+       pdev = of_find_device_by_node(node);
+
+       if (!pdev) {
+               pr_err("Unable to find DSS platform device\n");
+               return -ENODEV;
+       }
+
+       r = of_platform_populate(node, NULL, NULL, &pdev->dev);
+       if (r) {
+               pr_err("Unable to populate DSS submodule devices\n");
+               return r;
+       }
+
+       return omapdss_init_fbdev();
+}
+omap_device_initcall(omapdss_init_of);
 #endif /* CONFIG_FB_OMAP2 */
 
 static void dispc_disable_outputs(void)
@@ -361,58 +411,3 @@ int omap_dss_reset(struct omap_hwmod *oh)
 
        return r;
 }
-
-static const char * const omapdss_compat_names[] __initconst = {
-       "ti,omap2-dss",
-       "ti,omap3-dss",
-       "ti,omap4-dss",
-       "ti,omap5-dss",
-       "ti,dra7-dss",
-};
-
-static struct device_node * __init omapdss_find_dss_of_node(void)
-{
-       struct device_node *node;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(omapdss_compat_names); ++i) {
-               node = of_find_compatible_node(NULL, NULL,
-                       omapdss_compat_names[i]);
-               if (node)
-                       return node;
-       }
-
-       return NULL;
-}
-
-static int __init omapdss_init_of(void)
-{
-       int r;
-       struct device_node *node;
-       struct platform_device *pdev;
-
-       /* only create dss helper devices if dss is enabled in the .dts */
-
-       node = omapdss_find_dss_of_node();
-       if (!node)
-               return 0;
-
-       if (!of_device_is_available(node))
-               return 0;
-
-       pdev = of_find_device_by_node(node);
-
-       if (!pdev) {
-               pr_err("Unable to find DSS platform device\n");
-               return -ENODEV;
-       }
-
-       r = of_platform_populate(node, NULL, NULL, &pdev->dev);
-       if (r) {
-               pr_err("Unable to populate DSS submodule devices\n");
-               return r;
-       }
-
-       return omapdss_init_fbdev();
-}
-omap_device_initcall(omapdss_init_of);
index 7b95729..38a1be6 100644 (file)
@@ -351,7 +351,7 @@ static void omap44xx_prm_reconfigure_io_chain(void)
  * to occur, WAKEUPENABLE bits must be set in the pad mux registers, and
  * omap44xx_prm_reconfigure_io_chain() must be called.  No return value.
  */
-static void __init omap44xx_prm_enable_io_wakeup(void)
+static void omap44xx_prm_enable_io_wakeup(void)
 {
        s32 inst = omap4_prmst_get_prm_dev_inst();
 
index 215df43..2149b47 100644 (file)
@@ -360,14 +360,16 @@ v7_dma_inv_range:
        ALT_UP(W(nop))
 #endif
        mcrne   p15, 0, r0, c7, c14, 1          @ clean & invalidate D / U line
+       addne   r0, r0, r2
 
        tst     r1, r3
        bic     r1, r1, r3
        mcrne   p15, 0, r1, c7, c14, 1          @ clean & invalidate D / U line
-1:
-       mcr     p15, 0, r0, c7, c6, 1           @ invalidate D / U line
-       add     r0, r0, r2
        cmp     r0, r1
+1:
+       mcrlo   p15, 0, r0, c7, c6, 1           @ invalidate D / U line
+       addlo   r0, r0, r2
+       cmplo   r0, r1
        blo     1b
        dsb     st
        ret     lr
index 788486e..32aa2a2 100644 (file)
 /*
  * dcimvac: Invalidate data cache line by MVA to PoC
  */
-.macro dcimvac, rt, tmp
-       v7m_cacheop \rt, \tmp, V7M_SCB_DCIMVAC
+.irp    c,,eq,ne,cs,cc,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,hs,lo
+.macro dcimvac\c, rt, tmp
+       v7m_cacheop \rt, \tmp, V7M_SCB_DCIMVAC, \c
 .endm
+.endr
 
 /*
  * dccmvau: Clean data cache line by MVA to PoU
@@ -369,14 +371,16 @@ v7m_dma_inv_range:
        tst     r0, r3
        bic     r0, r0, r3
        dccimvacne r0, r3
+       addne   r0, r0, r2
        subne   r3, r2, #1      @ restore r3, corrupted by v7m's dccimvac
        tst     r1, r3
        bic     r1, r1, r3
        dccimvacne r1, r3
-1:
-       dcimvac r0, r3
-       add     r0, r0, r2
        cmp     r0, r1
+1:
+       dcimvaclo r0, r3
+       addlo   r0, r0, r2
+       cmplo   r0, r1
        blo     1b
        dsb     st
        ret     lr
index 661fe48..78de138 100644 (file)
@@ -829,7 +829,7 @@ static int __arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
                 void *cpu_addr, dma_addr_t dma_addr, size_t size,
                 unsigned long attrs)
 {
-       int ret;
+       int ret = -ENXIO;
        unsigned long nr_vma_pages = vma_pages(vma);
        unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
        unsigned long pfn = dma_to_pfn(dev, dma_addr);
index 81d0efb..19516fb 100644 (file)
        .endm
 
 .macro define_processor_functions name:req, dabort:req, pabort:req, nommu=0, suspend=0, bugs=0
+/*
+ * If we are building for big.Little with branch predictor hardening,
+ * we need the processor function tables to remain available after boot.
+ */
+#if 1 // defined(CONFIG_BIG_LITTLE) && defined(CONFIG_HARDEN_BRANCH_PREDICTOR)
+       .section ".rodata"
+#endif
        .type   \name\()_processor_functions, #object
        .align 2
 ENTRY(\name\()_processor_functions)
@@ -309,6 +316,9 @@ ENTRY(\name\()_processor_functions)
        .endif
 
        .size   \name\()_processor_functions, . - \name\()_processor_functions
+#if 1 // defined(CONFIG_BIG_LITTLE) && defined(CONFIG_HARDEN_BRANCH_PREDICTOR)
+       .previous
+#endif
 .endm
 
 .macro define_cache_functions name:req
index 5544b82..9a07916 100644 (file)
@@ -52,8 +52,6 @@ static void cpu_v7_spectre_init(void)
        case ARM_CPU_PART_CORTEX_A17:
        case ARM_CPU_PART_CORTEX_A73:
        case ARM_CPU_PART_CORTEX_A75:
-               if (processor.switch_mm != cpu_v7_bpiall_switch_mm)
-                       goto bl_error;
                per_cpu(harden_branch_predictor_fn, cpu) =
                        harden_branch_predictor_bpiall;
                spectre_v2_method = "BPIALL";
@@ -61,8 +59,6 @@ static void cpu_v7_spectre_init(void)
 
        case ARM_CPU_PART_CORTEX_A15:
        case ARM_CPU_PART_BRAHMA_B15:
-               if (processor.switch_mm != cpu_v7_iciallu_switch_mm)
-                       goto bl_error;
                per_cpu(harden_branch_predictor_fn, cpu) =
                        harden_branch_predictor_iciallu;
                spectre_v2_method = "ICIALLU";
@@ -88,11 +84,9 @@ static void cpu_v7_spectre_init(void)
                                          ARM_SMCCC_ARCH_WORKAROUND_1, &res);
                        if ((int)res.a0 != 0)
                                break;
-                       if (processor.switch_mm != cpu_v7_hvc_switch_mm && cpu)
-                               goto bl_error;
                        per_cpu(harden_branch_predictor_fn, cpu) =
                                call_hvc_arch_workaround_1;
-                       processor.switch_mm = cpu_v7_hvc_switch_mm;
+                       cpu_do_switch_mm = cpu_v7_hvc_switch_mm;
                        spectre_v2_method = "hypervisor";
                        break;
 
@@ -101,11 +95,9 @@ static void cpu_v7_spectre_init(void)
                                          ARM_SMCCC_ARCH_WORKAROUND_1, &res);
                        if ((int)res.a0 != 0)
                                break;
-                       if (processor.switch_mm != cpu_v7_smc_switch_mm && cpu)
-                               goto bl_error;
                        per_cpu(harden_branch_predictor_fn, cpu) =
                                call_smc_arch_workaround_1;
-                       processor.switch_mm = cpu_v7_smc_switch_mm;
+                       cpu_do_switch_mm = cpu_v7_smc_switch_mm;
                        spectre_v2_method = "firmware";
                        break;
 
@@ -119,11 +111,6 @@ static void cpu_v7_spectre_init(void)
        if (spectre_v2_method)
                pr_info("CPU%u: Spectre v2: using %s workaround\n",
                        smp_processor_id(), spectre_v2_method);
-       return;
-
-bl_error:
-       pr_err("CPU%u: Spectre v2: incorrect context switching function, system vulnerable\n",
-               cpu);
 }
 #else
 static void cpu_v7_spectre_init(void)
index b2aa9b3..2c118a6 100644 (file)
@@ -247,7 +247,7 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *or
        }
 
        /* Copy arch-dep-instance from template. */
-       memcpy(code, &optprobe_template_entry,
+       memcpy(code, (unsigned char *)optprobe_template_entry,
                        TMPL_END_IDX * sizeof(kprobe_opcode_t));
 
        /* Adjust buffer according to instruction. */
index aff6e6e..ee7b079 100644 (file)
@@ -573,7 +573,7 @@ int vfp_preserve_user_clear_hwstate(struct user_vfp *ufp,
         */
        ufp_exc->fpexc = hwstate->fpexc;
        ufp_exc->fpinst = hwstate->fpinst;
-       ufp_exc->fpinst2 = ufp_exc->fpinst2;
+       ufp_exc->fpinst2 = hwstate->fpinst2;
 
        /* Ensure that VFP is disabled. */
        vfp_flush_hwstate(thread);
index 787d785..ea2ab03 100644 (file)
@@ -497,6 +497,24 @@ config ARM64_ERRATUM_1188873
 
          If unsure, say Y.
 
+config ARM64_ERRATUM_1286807
+       bool "Cortex-A76: Modification of the translation table for a virtual address might lead to read-after-read ordering violation"
+       default y
+       select ARM64_WORKAROUND_REPEAT_TLBI
+       help
+         This option adds workaround for ARM Cortex-A76 erratum 1286807
+
+         On the affected Cortex-A76 cores (r0p0 to r3p0), if a virtual
+         address for a cacheable mapping of a location is being
+         accessed by a core while another core is remapping the virtual
+         address to a new physical page using the recommended
+         break-before-make sequence, then under very rare circumstances
+         TLBI+DSB completes before a read using the translation being
+         invalidated has been observed by other observers. The
+         workaround repeats the TLBI+DSB operation.
+
+         If unsure, say Y.
+
 config CAVIUM_ERRATUM_22375
        bool "Cavium erratum 22375, 24313"
        default y
@@ -566,9 +584,16 @@ config QCOM_FALKOR_ERRATUM_1003
          is unchanged. Work around the erratum by invalidating the walk cache
          entries for the trampoline before entering the kernel proper.
 
+config ARM64_WORKAROUND_REPEAT_TLBI
+       bool
+       help
+         Enable the repeat TLBI workaround for Falkor erratum 1009 and
+         Cortex-A76 erratum 1286807.
+
 config QCOM_FALKOR_ERRATUM_1009
        bool "Falkor E1009: Prematurely complete a DSB after a TLBI"
        default y
+       select ARM64_WORKAROUND_REPEAT_TLBI
        help
          On Falkor v1, the CPU may prematurely complete a DSB following a
          TLBI xxIS invalidate maintenance operation. Repeat the TLBI operation
index 64632c8..01ea662 100644 (file)
                        compatible = "arm,cortex-a72", "arm,armv8";
                        reg = <0x000>;
                        enable-method = "psci";
-                       cpu-idle-states = <&CPU_SLEEP_0>;
                };
                cpu1: cpu@1 {
                        device_type = "cpu";
                        compatible = "arm,cortex-a72", "arm,armv8";
                        reg = <0x001>;
                        enable-method = "psci";
-                       cpu-idle-states = <&CPU_SLEEP_0>;
                };
                cpu2: cpu@100 {
                        device_type = "cpu";
                        compatible = "arm,cortex-a72", "arm,armv8";
                        reg = <0x100>;
                        enable-method = "psci";
-                       cpu-idle-states = <&CPU_SLEEP_0>;
                };
                cpu3: cpu@101 {
                        device_type = "cpu";
                        compatible = "arm,cortex-a72", "arm,armv8";
                        reg = <0x101>;
                        enable-method = "psci";
-                       cpu-idle-states = <&CPU_SLEEP_0>;
                };
        };
 };
index 073610a..7d94c1f 100644 (file)
                method = "smc";
        };
 
-       cpus {
-               #address-cells = <1>;
-               #size-cells = <0>;
-
-               idle_states {
-                       entry_method = "arm,pcsi";
-
-                       CPU_SLEEP_0: cpu-sleep-0 {
-                               compatible = "arm,idle-state";
-                               local-timer-stop;
-                               arm,psci-suspend-param = <0x0010000>;
-                               entry-latency-us = <80>;
-                               exit-latency-us  = <160>;
-                               min-residency-us = <320>;
-                       };
-
-                       CLUSTER_SLEEP_0: cluster-sleep-0 {
-                               compatible = "arm,idle-state";
-                               local-timer-stop;
-                               arm,psci-suspend-param = <0x1010000>;
-                               entry-latency-us = <500>;
-                               exit-latency-us = <1000>;
-                               min-residency-us = <2500>;
-                       };
-               };
-       };
-
        ap806 {
                #address-cells = <2>;
                #size-cells = <2>;
index 5d6005c..710c5c3 100644 (file)
        model = "Bananapi BPI-R64";
        compatible = "bananapi,bpi-r64", "mediatek,mt7622";
 
+       aliases {
+               serial0 = &uart0;
+       };
+
        chosen {
-               bootargs = "earlycon=uart8250,mmio32,0x11002000 console=ttyS0,115200n1 swiotlb=512";
+               stdout-path = "serial0:115200n8";
+               bootargs = "earlycon=uart8250,mmio32,0x11002000 swiotlb=512";
        };
 
        cpus {
index dcad086..3f78334 100644 (file)
        model = "MediaTek MT7622 RFB1 board";
        compatible = "mediatek,mt7622-rfb1", "mediatek,mt7622";
 
+       aliases {
+               serial0 = &uart0;
+       };
+
        chosen {
-               bootargs = "earlycon=uart8250,mmio32,0x11002000 console=ttyS0,115200n1 swiotlb=512";
+               stdout-path = "serial0:115200n8";
+               bootargs = "earlycon=uart8250,mmio32,0x11002000 swiotlb=512";
        };
 
        cpus {
index fe0c875..14a1028 100644 (file)
                #reset-cells = <1>;
        };
 
-       timer: timer@10004000 {
-               compatible = "mediatek,mt7622-timer",
-                            "mediatek,mt6577-timer";
-               reg = <0 0x10004000 0 0x80>;
-               interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_LOW>;
-               clocks = <&infracfg CLK_INFRA_APXGPT_PD>,
-                        <&topckgen CLK_TOP_RTC>;
-               clock-names = "system-clk", "rtc-clk";
-       };
-
        scpsys: scpsys@10006000 {
                compatible = "mediatek,mt7622-scpsys",
                             "syscon";
index b4276da..11fd1fe 100644 (file)
                };
        };
 };
+
+&tlmm {
+       gpio-reserved-ranges = <0 4>, <81 4>;
+};
index eedfaf8..b3def03 100644 (file)
        };
 };
 
+&gcc {
+       protected-clocks = <GCC_QSPI_CORE_CLK>,
+                          <GCC_QSPI_CORE_CLK_SRC>,
+                          <GCC_QSPI_CNOC_PERIPH_AHB_CLK>;
+};
+
 &i2c10 {
        status = "okay";
        clock-frequency = <400000>;
        status = "okay";
 };
 
+&tlmm {
+       gpio-reserved-ranges = <0 4>, <81 4>;
+};
+
 &uart9 {
        status = "okay";
 };
index 2dceeea..1e6a710 100644 (file)
 };
 
 &pcie0 {
-       ep-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>;
+       ep-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>;
        num-lanes = <4>;
        pinctrl-names = "default";
        pinctrl-0 = <&pcie_clkreqn_cpm>;
index 6c8c4ab..56abbb0 100644 (file)
                regulator-always-on;
                vin-supply = <&vcc_sys>;
        };
-
-       vdd_log: vdd-log {
-               compatible = "pwm-regulator";
-               pwms = <&pwm2 0 25000 0>;
-               regulator-name = "vdd_log";
-               regulator-min-microvolt = <800000>;
-               regulator-max-microvolt = <1400000>;
-               regulator-always-on;
-               regulator-boot-on;
-               vin-supply = <&vcc_sys>;
-       };
-
 };
 
 &cpu_l0 {
index affc3c3..8d7b47f 100644 (file)
@@ -36,7 +36,7 @@
 
        wkup_uart0: serial@42300000 {
                compatible = "ti,am654-uart";
-               reg = <0x00 0x42300000 0x00 0x100>;
+               reg = <0x42300000 0x100>;
                reg-shift = <2>;
                reg-io-width = <4>;
                interrupts = <GIC_SPI 697 IRQ_TYPE_LEVEL_HIGH>;
index caa955f..fac54fb 100644 (file)
@@ -56,6 +56,19 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
 {
        return is_compat_task();
 }
+
+#define ARCH_HAS_SYSCALL_MATCH_SYM_NAME
+
+static inline bool arch_syscall_match_sym_name(const char *sym,
+                                              const char *name)
+{
+       /*
+        * Since all syscall functions have __arm64_ prefix, we must skip it.
+        * However, as we described above, we decided to ignore compat
+        * syscalls, so we don't care about __arm64_compat_ prefix here.
+        */
+       return !strcmp(sym + 8, name);
+}
 #endif /* ifndef __ASSEMBLY__ */
 
 #endif /* __ASM_FTRACE_H */
index b964429..932c60e 100644 (file)
  */
 #define PCI_IO_SIZE            SZ_16M
 
-/*
- * Log2 of the upper bound of the size of a struct page. Used for sizing
- * the vmemmap region only, does not affect actual memory footprint.
- * We don't use sizeof(struct page) directly since taking its size here
- * requires its definition to be available at this point in the inclusion
- * chain, and it may not be a power of 2 in the first place.
- */
-#define STRUCT_PAGE_MAX_SHIFT  6
-
 /*
  * VMEMMAP_SIZE - allows the whole linear region to be covered by
  *                a struct page array
 #define PAGE_OFFSET            (UL(0xffffffffffffffff) - \
        (UL(1) << (VA_BITS - 1)) + 1)
 #define KIMAGE_VADDR           (MODULES_END)
+#define BPF_JIT_REGION_START   (VA_START + KASAN_SHADOW_SIZE)
+#define BPF_JIT_REGION_SIZE    (SZ_128M)
+#define BPF_JIT_REGION_END     (BPF_JIT_REGION_START + BPF_JIT_REGION_SIZE)
 #define MODULES_END            (MODULES_VADDR + MODULES_VSIZE)
-#define MODULES_VADDR          (VA_START + KASAN_SHADOW_SIZE)
+#define MODULES_VADDR          (BPF_JIT_REGION_END)
 #define MODULES_VSIZE          (SZ_128M)
 #define VMEMMAP_START          (PAGE_OFFSET - VMEMMAP_SIZE)
 #define PCI_IO_END             (VMEMMAP_START - SZ_2M)
index 0c909c4..842fb95 100644 (file)
                         SCTLR_ELx_SA     | SCTLR_ELx_I    | SCTLR_ELx_WXN | \
                         SCTLR_ELx_DSSBS | ENDIAN_CLEAR_EL2 | SCTLR_EL2_RES0)
 
-#if (SCTLR_EL2_SET ^ SCTLR_EL2_CLEAR) != 0xffffffffffffffff
+#if (SCTLR_EL2_SET ^ SCTLR_EL2_CLEAR) != 0xffffffffffffffffUL
 #error "Inconsistent SCTLR_EL2 set/clear bits"
 #endif
 
                         SCTLR_EL1_UMA | SCTLR_ELx_WXN     | ENDIAN_CLEAR_EL1 |\
                         SCTLR_ELx_DSSBS | SCTLR_EL1_NTWI  | SCTLR_EL1_RES0)
 
-#if (SCTLR_EL1_SET ^ SCTLR_EL1_CLEAR) != 0xffffffffffffffff
+#if (SCTLR_EL1_SET ^ SCTLR_EL1_CLEAR) != 0xffffffffffffffffUL
 #error "Inconsistent SCTLR_EL1 set/clear bits"
 #endif
 
index c3c0387..5dfd238 100644 (file)
                   ALTERNATIVE("nop\n                   nop",                  \
                               "dsb ish\n               tlbi " #op,            \
                               ARM64_WORKAROUND_REPEAT_TLBI,                   \
-                              CONFIG_QCOM_FALKOR_ERRATUM_1009)                \
+                              CONFIG_ARM64_WORKAROUND_REPEAT_TLBI)            \
                            : : )
 
 #define __TLBI_1(op, arg) asm ("tlbi " #op ", %0\n"                           \
                   ALTERNATIVE("nop\n                   nop",                  \
                               "dsb ish\n               tlbi " #op ", %0",     \
                               ARM64_WORKAROUND_REPEAT_TLBI,                   \
-                              CONFIG_QCOM_FALKOR_ERRATUM_1009)                \
+                              CONFIG_ARM64_WORKAROUND_REPEAT_TLBI)            \
                            : : "r" (arg))
 
 #define __TLBI_N(op, arg, n, ...) __TLBI_##n(op, arg)
index a509e35..6ad715d 100644 (file)
@@ -570,6 +570,20 @@ static const struct midr_range arm64_harden_el2_vectors[] = {
 
 #endif
 
+#ifdef CONFIG_ARM64_WORKAROUND_REPEAT_TLBI
+
+static const struct midr_range arm64_repeat_tlbi_cpus[] = {
+#ifdef CONFIG_QCOM_FALKOR_ERRATUM_1009
+       MIDR_RANGE(MIDR_QCOM_FALKOR_V1, 0, 0, 0, 0),
+#endif
+#ifdef CONFIG_ARM64_ERRATUM_1286807
+       MIDR_RANGE(MIDR_CORTEX_A76, 0, 0, 3, 0),
+#endif
+       {},
+};
+
+#endif
+
 const struct arm64_cpu_capabilities arm64_errata[] = {
 #if    defined(CONFIG_ARM64_ERRATUM_826319) || \
        defined(CONFIG_ARM64_ERRATUM_827319) || \
@@ -695,11 +709,11 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
                .matches = is_kryo_midr,
        },
 #endif
-#ifdef CONFIG_QCOM_FALKOR_ERRATUM_1009
+#ifdef CONFIG_ARM64_WORKAROUND_REPEAT_TLBI
        {
-               .desc = "Qualcomm Technologies Falkor erratum 1009",
+               .desc = "Qualcomm erratum 1009, ARM erratum 1286807",
                .capability = ARM64_WORKAROUND_REPEAT_TLBI,
-               ERRATA_MIDR_REV(MIDR_QCOM_FALKOR_V1, 0, 0),
+               ERRATA_MIDR_RANGE_LIST(arm64_repeat_tlbi_cpus),
        },
 #endif
 #ifdef CONFIG_ARM64_ERRATUM_858921
index af50064..aec5ecb 100644 (file)
@@ -1333,7 +1333,6 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
                .cpu_enable = cpu_enable_hw_dbm,
        },
 #endif
-#ifdef CONFIG_ARM64_SSBD
        {
                .desc = "CRC32 instructions",
                .capability = ARM64_HAS_CRC32,
@@ -1343,6 +1342,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
                .field_pos = ID_AA64ISAR0_CRC32_SHIFT,
                .min_field_value = 1,
        },
+#ifdef CONFIG_ARM64_SSBD
        {
                .desc = "Speculative Store Bypassing Safe (SSBS)",
                .capability = ARM64_SSBS,
index 50986e3..57e9622 100644 (file)
@@ -216,8 +216,6 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
 {
        unsigned long return_hooker = (unsigned long)&return_to_handler;
        unsigned long old;
-       struct ftrace_graph_ent trace;
-       int err;
 
        if (unlikely(atomic_read(&current->tracing_graph_pause)))
                return;
@@ -229,18 +227,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
         */
        old = *parent;
 
-       trace.func = self_addr;
-       trace.depth = current->curr_ret_stack + 1;
-
-       /* Only trace if the calling function expects to */
-       if (!ftrace_graph_entry(&trace))
-               return;
-
-       err = ftrace_push_return_trace(old, self_addr, &trace.depth,
-                                      frame_pointer, NULL);
-       if (err == -EBUSY)
-               return;
-       else
+       if (!function_graph_enter(old, self_addr, frame_pointer, NULL))
                *parent = return_hooker;
 }
 
index 6b2686d..29cdc99 100644 (file)
@@ -214,7 +214,7 @@ static int create_safe_exec_page(void *src_start, size_t length,
        }
 
        memcpy((void *)dst, src_start, length);
-       flush_icache_range(dst, dst + length);
+       __flush_icache_range(dst, dst + length);
 
        pgdp = pgd_offset_raw(allocator(mask), dst_addr);
        if (pgd_none(READ_ONCE(*pgdp))) {
index 953e316..f4fc1e0 100644 (file)
@@ -313,6 +313,7 @@ void __init setup_arch(char **cmdline_p)
        arm64_memblock_init();
 
        paging_init();
+       efi_apply_persistent_mem_reservations();
 
        acpi_table_upgrade();
 
index a3ac262..a537044 100644 (file)
@@ -429,9 +429,9 @@ static void *__iommu_alloc_attrs(struct device *dev, size_t size,
                                                   prot,
                                                   __builtin_return_address(0));
                if (addr) {
-                       memset(addr, 0, size);
                        if (!coherent)
                                __dma_flush_area(page_to_virt(page), iosize);
+                       memset(addr, 0, size);
                } else {
                        iommu_dma_unmap_page(dev, *handle, iosize, 0, attrs);
                        dma_release_from_contiguous(dev, page,
index 9b432d9..0340e45 100644 (file)
@@ -610,14 +610,6 @@ void __init mem_init(void)
        BUILD_BUG_ON(TASK_SIZE_32                       > TASK_SIZE_64);
 #endif
 
-#ifdef CONFIG_SPARSEMEM_VMEMMAP
-       /*
-        * Make sure we chose the upper bound of sizeof(struct page)
-        * correctly when sizing the VMEMMAP array.
-        */
-       BUILD_BUG_ON(sizeof(struct page) > (1 << STRUCT_PAGE_MAX_SHIFT));
-#endif
-
        if (PAGE_SIZE >= 16384 && get_num_physpages() <= 128) {
                extern int sysctl_overcommit_memory;
                /*
index a6fdaea..1542df0 100644 (file)
@@ -134,10 +134,9 @@ static inline void emit_a64_mov_i64(const int reg, const u64 val,
 }
 
 /*
- * This is an unoptimized 64 immediate emission used for BPF to BPF call
- * addresses. It will always do a full 64 bit decomposition as otherwise
- * more complexity in the last extra pass is required since we previously
- * reserved 4 instructions for the address.
+ * Kernel addresses in the vmalloc space use at most 48 bits, and the
+ * remaining bits are guaranteed to be 0x1. So we can compose the address
+ * with a fixed length movn/movk/movk sequence.
  */
 static inline void emit_addr_mov_i64(const int reg, const u64 val,
                                     struct jit_ctx *ctx)
@@ -145,8 +144,8 @@ static inline void emit_addr_mov_i64(const int reg, const u64 val,
        u64 tmp = val;
        int shift = 0;
 
-       emit(A64_MOVZ(1, reg, tmp & 0xffff, shift), ctx);
-       for (;shift < 48;) {
+       emit(A64_MOVN(1, reg, ~tmp & 0xffff, shift), ctx);
+       while (shift < 32) {
                tmp >>= 16;
                shift += 16;
                emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx);
@@ -351,7 +350,8 @@ static void build_epilogue(struct jit_ctx *ctx)
  * >0 - successfully JITed a 16-byte eBPF instruction.
  * <0 - failed to JIT.
  */
-static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
+static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
+                     bool extra_pass)
 {
        const u8 code = insn->code;
        const u8 dst = bpf2a64[insn->dst_reg];
@@ -625,12 +625,15 @@ emit_cond_jmp:
        case BPF_JMP | BPF_CALL:
        {
                const u8 r0 = bpf2a64[BPF_REG_0];
-               const u64 func = (u64)__bpf_call_base + imm;
+               bool func_addr_fixed;
+               u64 func_addr;
+               int ret;
 
-               if (ctx->prog->is_func)
-                       emit_addr_mov_i64(tmp, func, ctx);
-               else
-                       emit_a64_mov_i64(tmp, func, ctx);
+               ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass,
+                                           &func_addr, &func_addr_fixed);
+               if (ret < 0)
+                       return ret;
+               emit_addr_mov_i64(tmp, func_addr, ctx);
                emit(A64_BLR(tmp), ctx);
                emit(A64_MOV(1, r0, A64_R(0)), ctx);
                break;
@@ -753,7 +756,7 @@ emit_cond_jmp:
        return 0;
 }
 
-static int build_body(struct jit_ctx *ctx)
+static int build_body(struct jit_ctx *ctx, bool extra_pass)
 {
        const struct bpf_prog *prog = ctx->prog;
        int i;
@@ -762,7 +765,7 @@ static int build_body(struct jit_ctx *ctx)
                const struct bpf_insn *insn = &prog->insnsi[i];
                int ret;
 
-               ret = build_insn(insn, ctx);
+               ret = build_insn(insn, ctx, extra_pass);
                if (ret > 0) {
                        i++;
                        if (ctx->image == NULL)
@@ -858,7 +861,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
        /* 1. Initial fake pass to compute ctx->idx. */
 
        /* Fake pass to fill in ctx->offset. */
-       if (build_body(&ctx)) {
+       if (build_body(&ctx, extra_pass)) {
                prog = orig_prog;
                goto out_off;
        }
@@ -888,7 +891,7 @@ skip_init_ctx:
 
        build_prologue(&ctx, was_classic);
 
-       if (build_body(&ctx)) {
+       if (build_body(&ctx, extra_pass)) {
                bpf_jit_binary_free(header);
                prog = orig_prog;
                goto out_off;
@@ -929,6 +932,7 @@ skip_init_ctx:
        prog->jited_len = image_size;
 
        if (!prog->is_func || extra_pass) {
+               bpf_prog_fill_jited_linfo(prog, ctx.offset);
 out_off:
                kfree(ctx.offset);
                kfree(jit_data);
@@ -940,3 +944,16 @@ out:
                                           tmp : orig_prog);
        return prog;
 }
+
+void *bpf_jit_alloc_exec(unsigned long size)
+{
+       return __vmalloc_node_range(size, PAGE_SIZE, BPF_JIT_REGION_START,
+                                   BPF_JIT_REGION_END, GFP_KERNEL,
+                                   PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE,
+                                   __builtin_return_address(0));
+}
+
+void bpf_jit_free_exec(void *addr)
+{
+       return vfree(addr);
+}
index c410aa4..b2905c0 100644 (file)
@@ -16,7 +16,7 @@
 
 static inline void tlbmiss_handler_setup_pgd(unsigned long pgd, bool kernel)
 {
-       pgd &= ~(1<<31);
+       pgd -= PAGE_OFFSET;
        pgd += PHYS_OFFSET;
        pgd |= 1;
        setup_pgd(pgd, kernel);
@@ -29,7 +29,7 @@ static inline void tlbmiss_handler_setup_pgd(unsigned long pgd, bool kernel)
 
 static inline unsigned long tlb_get_pgd(void)
 {
-       return ((get_pgd()|(1<<31)) - PHYS_OFFSET) & ~1;
+       return ((get_pgd() - PHYS_OFFSET) & ~1) + PAGE_OFFSET;
 }
 
 #define cpu_context(cpu, mm)   ((mm)->context.asid[cpu])
index ebef7f4..c5c253c 100644 (file)
@@ -59,7 +59,9 @@ extern struct node_cpuid_s node_cpuid[NR_CPUS];
  */
 
 extern u8 numa_slit[MAX_NUMNODES * MAX_NUMNODES];
-#define node_distance(from,to) (numa_slit[(from) * MAX_NUMNODES + (to)])
+#define slit_distance(from,to) (numa_slit[(from) * MAX_NUMNODES + (to)])
+extern int __node_distance(int from, int to);
+#define node_distance(from,to) __node_distance(from, to)
 
 extern int paddr_to_nid(unsigned long paddr);
 
index 1dacbf5..41eb281 100644 (file)
@@ -578,8 +578,8 @@ void __init acpi_numa_fixup(void)
        if (!slit_table) {
                for (i = 0; i < MAX_NUMNODES; i++)
                        for (j = 0; j < MAX_NUMNODES; j++)
-                               node_distance(i, j) = i == j ? LOCAL_DISTANCE :
-                                                       REMOTE_DISTANCE;
+                               slit_distance(i, j) = i == j ?
+                                       LOCAL_DISTANCE : REMOTE_DISTANCE;
                return;
        }
 
@@ -592,7 +592,7 @@ void __init acpi_numa_fixup(void)
                        if (!pxm_bit_test(j))
                                continue;
                        node_to = pxm_to_node(j);
-                       node_distance(node_from, node_to) =
+                       slit_distance(node_from, node_to) =
                            slit_table->entry[i * slit_table->locality_count + j];
                }
        }
index 3861d6e..a038035 100644 (file)
@@ -36,6 +36,12 @@ struct node_cpuid_s node_cpuid[NR_CPUS] =
  */
 u8 numa_slit[MAX_NUMNODES * MAX_NUMNODES];
 
+int __node_distance(int from, int to)
+{
+       return slit_distance(from, to);
+}
+EXPORT_SYMBOL(__node_distance);
+
 /* Identify which cnode a physical address resides on */
 int
 paddr_to_nid(unsigned long paddr)
index a1a3eae..ad0195c 100644 (file)
@@ -164,8 +164,6 @@ static void __init m68k_parse_bootinfo(const struct bi_record *record)
                                        be32_to_cpu(m->addr);
                                m68k_memory[m68k_num_memory].size =
                                        be32_to_cpu(m->size);
-                               memblock_add(m68k_memory[m68k_num_memory].addr,
-                                            m68k_memory[m68k_num_memory].size);
                                m68k_num_memory++;
                        } else
                                pr_warn("%s: too many memory chunks\n",
index 7497cf3..3f3d0bf 100644 (file)
@@ -228,6 +228,7 @@ void __init paging_init(void)
 
        min_addr = m68k_memory[0].addr;
        max_addr = min_addr + m68k_memory[0].size;
+       memblock_add(m68k_memory[0].addr, m68k_memory[0].size);
        for (i = 1; i < m68k_num_memory;) {
                if (m68k_memory[i].addr < min_addr) {
                        printk("Ignoring memory chunk at 0x%lx:0x%lx before the first chunk\n",
@@ -238,6 +239,7 @@ void __init paging_init(void)
                                (m68k_num_memory - i) * sizeof(struct m68k_mem_info));
                        continue;
                }
+               memblock_add(m68k_memory[i].addr, m68k_memory[i].size);
                addr = m68k_memory[i].addr + m68k_memory[i].size;
                if (addr > max_addr)
                        max_addr = addr;
index d57563c..224eea4 100644 (file)
@@ -22,8 +22,7 @@
 void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
 {
        unsigned long old;
-       int faulted, err;
-       struct ftrace_graph_ent trace;
+       int faulted;
        unsigned long return_hooker = (unsigned long)
                                &return_to_handler;
 
@@ -63,18 +62,8 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
                return;
        }
 
-       err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0, NULL);
-       if (err == -EBUSY) {
+       if (function_graph_enter(old, self_addr, 0, NULL))
                *parent = old;
-               return;
-       }
-
-       trace.func = self_addr;
-       /* Only trace if the calling function expects to */
-       if (!ftrace_graph_entry(&trace)) {
-               current->curr_ret_stack--;
-               *parent = old;
-       }
 }
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 
index 490b12a..c52d0ef 100644 (file)
@@ -140,6 +140,7 @@ CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_DS1307=y
 CONFIG_STAGING=y
 CONFIG_OCTEON_ETHERNET=y
+CONFIG_OCTEON_USB=y
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_RAS=y
 CONFIG_EXT4_FS=y
index 0170602..6cf8ffb 100644 (file)
@@ -73,7 +73,7 @@ static inline unsigned long mips_get_syscall_arg(unsigned long *arg,
 #ifdef CONFIG_64BIT
        case 4: case 5: case 6: case 7:
 #ifdef CONFIG_MIPS32_O32
-               if (test_thread_flag(TIF_32BIT_REGS))
+               if (test_tsk_thread_flag(task, TIF_32BIT_REGS))
                        return get_user(*arg, (int *)usp + n);
                else
 #endif
index 59dae37..b1990dd 100644 (file)
@@ -157,6 +157,7 @@ Ip_u2u1s3(_slti);
 Ip_u2u1s3(_sltiu);
 Ip_u3u1u2(_sltu);
 Ip_u2u1u3(_sra);
+Ip_u3u2u1(_srav);
 Ip_u2u1u3(_srl);
 Ip_u3u2u1(_srlv);
 Ip_u3u1u2(_subu);
index c05dcf5..40fbb5d 100644 (file)
@@ -369,8 +369,9 @@ enum mm_32a_minor_op {
        mm_ext_op = 0x02c,
        mm_pool32axf_op = 0x03c,
        mm_srl32_op = 0x040,
+       mm_srlv32_op = 0x050,
        mm_sra_op = 0x080,
-       mm_srlv32_op = 0x090,
+       mm_srav_op = 0x090,
        mm_rotr_op = 0x0c0,
        mm_lwxs_op = 0x118,
        mm_addu32_op = 0x150,
index 7f3dfdb..b122cbb 100644 (file)
@@ -322,7 +322,6 @@ void prepare_ftrace_return(unsigned long *parent_ra_addr, unsigned long self_ra,
                           unsigned long fp)
 {
        unsigned long old_parent_ra;
-       struct ftrace_graph_ent trace;
        unsigned long return_hooker = (unsigned long)
            &return_to_handler;
        int faulted, insns;
@@ -369,12 +368,6 @@ void prepare_ftrace_return(unsigned long *parent_ra_addr, unsigned long self_ra,
        if (unlikely(faulted))
                goto out;
 
-       if (ftrace_push_return_trace(old_parent_ra, self_ra, &trace.depth, fp,
-                                    NULL) == -EBUSY) {
-               *parent_ra_addr = old_parent_ra;
-               return;
-       }
-
        /*
         * Get the recorded ip of the current mcount calling site in the
         * __mcount_loc section, which will be used to filter the function
@@ -382,13 +375,10 @@ void prepare_ftrace_return(unsigned long *parent_ra_addr, unsigned long self_ra,
         */
 
        insns = core_kernel_text(self_ra) ? 2 : MCOUNT_OFFSET_INSNS + 1;
-       trace.func = self_ra - (MCOUNT_INSN_SIZE * insns);
+       self_ra -= (MCOUNT_INSN_SIZE * insns);
 
-       /* Only trace if the calling function expects to */
-       if (!ftrace_graph_entry(&trace)) {
-               current->curr_ret_stack--;
+       if (function_graph_enter(old_parent_ra, self_ra, fp, NULL))
                *parent_ra_addr = old_parent_ra;
-       }
        return;
 out:
        ftrace_graph_stop();
index ea09ed6..8c6c48e 100644 (file)
@@ -794,6 +794,7 @@ static void __init arch_mem_init(char **cmdline_p)
 
        /* call board setup routine */
        plat_mem_setup();
+       memblock_set_bottom_up(true);
 
        /*
         * Make sure all kernel memory is in the maps.  The "UP" and
index 0f852e1..15e103c 100644 (file)
@@ -2260,10 +2260,8 @@ void __init trap_init(void)
                unsigned long size = 0x200 + VECTORSPACING*64;
                phys_addr_t ebase_pa;
 
-               memblock_set_bottom_up(true);
                ebase = (unsigned long)
                        memblock_alloc_from(size, 1 << fls(size), 0);
-               memblock_set_bottom_up(false);
 
                /*
                 * Try to ensure ebase resides in KSeg0 if possible.
@@ -2307,6 +2305,7 @@ void __init trap_init(void)
        if (board_ebase_setup)
                board_ebase_setup();
        per_cpu_trap_init(true);
+       memblock_set_bottom_up(false);
 
        /*
         * Copy the generic exception handlers to their final destination.
index 6227618..60bf0a1 100644 (file)
@@ -231,6 +231,8 @@ static __init void prom_meminit(void)
                        cpumask_clear(&__node_data[(node)]->cpumask);
                }
        }
+       max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
+
        for (cpu = 0; cpu < loongson_sysconf.nr_cpus; cpu++) {
                node = cpu / loongson_sysconf.cores_per_node;
                if (node >= num_online_nodes())
@@ -248,19 +250,9 @@ static __init void prom_meminit(void)
 
 void __init paging_init(void)
 {
-       unsigned node;
        unsigned long zones_size[MAX_NR_ZONES] = {0, };
 
        pagetable_init();
-
-       for_each_online_node(node) {
-               unsigned long  start_pfn, end_pfn;
-
-               get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
-
-               if (end_pfn > max_low_pfn)
-                       max_low_pfn = end_pfn;
-       }
 #ifdef CONFIG_ZONE_DMA32
        zones_size[ZONE_DMA32] = MAX_DMA32_PFN;
 #endif
index 24e5b0d..75ef904 100644 (file)
@@ -104,6 +104,7 @@ static const struct insn insn_table_MM[insn_invalid] = {
        [insn_sltiu]    = {M(mm_sltiu32_op, 0, 0, 0, 0, 0), RT | RS | SIMM},
        [insn_sltu]     = {M(mm_pool32a_op, 0, 0, 0, 0, mm_sltu_op), RT | RS | RD},
        [insn_sra]      = {M(mm_pool32a_op, 0, 0, 0, 0, mm_sra_op), RT | RS | RD},
+       [insn_srav]     = {M(mm_pool32a_op, 0, 0, 0, 0, mm_srav_op), RT | RS | RD},
        [insn_srl]      = {M(mm_pool32a_op, 0, 0, 0, 0, mm_srl32_op), RT | RS | RD},
        [insn_srlv]     = {M(mm_pool32a_op, 0, 0, 0, 0, mm_srlv32_op), RT | RS | RD},
        [insn_rotr]     = {M(mm_pool32a_op, 0, 0, 0, 0, mm_rotr_op), RT | RS | RD},
index 60ceb93..6abe40f 100644 (file)
@@ -171,6 +171,7 @@ static const struct insn insn_table[insn_invalid] = {
        [insn_sltiu]    = {M(sltiu_op, 0, 0, 0, 0, 0), RS | RT | SIMM},
        [insn_sltu]     = {M(spec_op, 0, 0, 0, 0, sltu_op), RS | RT | RD},
        [insn_sra]      = {M(spec_op, 0, 0, 0, 0, sra_op),  RT | RD | RE},
+       [insn_srav]     = {M(spec_op, 0, 0, 0, 0, srav_op), RS | RT | RD},
        [insn_srl]      = {M(spec_op, 0, 0, 0, 0, srl_op),  RT | RD | RE},
        [insn_srlv]     = {M(spec_op, 0, 0, 0, 0, srlv_op),  RS | RT | RD},
        [insn_subu]     = {M(spec_op, 0, 0, 0, 0, subu_op),     RS | RT | RD},
index 57570c0..45b6264 100644 (file)
@@ -61,10 +61,10 @@ enum opcode {
        insn_mthc0, insn_mthi, insn_mtlo, insn_mul, insn_multu, insn_nor,
        insn_or, insn_ori, insn_pref, insn_rfe, insn_rotr, insn_sb,
        insn_sc, insn_scd, insn_sd, insn_sh, insn_sll, insn_sllv,
-       insn_slt, insn_slti, insn_sltiu, insn_sltu, insn_sra, insn_srl,
-       insn_srlv, insn_subu, insn_sw, insn_sync, insn_syscall, insn_tlbp,
-       insn_tlbr, insn_tlbwi, insn_tlbwr, insn_wait, insn_wsbh, insn_xor,
-       insn_xori, insn_yield,
+       insn_slt, insn_slti, insn_sltiu, insn_sltu, insn_sra, insn_srav,
+       insn_srl, insn_srlv, insn_subu, insn_sw, insn_sync, insn_syscall,
+       insn_tlbp, insn_tlbr, insn_tlbwi, insn_tlbwr, insn_wait, insn_wsbh,
+       insn_xor, insn_xori, insn_yield,
        insn_invalid /* insn_invalid must be last */
 };
 
@@ -353,6 +353,7 @@ I_u2u1s3(_slti)
 I_u2u1s3(_sltiu)
 I_u3u1u2(_sltu)
 I_u2u1u3(_sra)
+I_u3u2u1(_srav)
 I_u2u1u3(_srl)
 I_u3u2u1(_srlv)
 I_u2u1u3(_rotr)
index 4d8cb9b..3a0e34f 100644 (file)
@@ -1159,19 +1159,19 @@ jmp_cmp:
                        emit_load(r_A, r_skb, off, ctx);
                        break;
                case BPF_ANC | SKF_AD_VLAN_TAG:
-               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                        ctx->flags |= SEEN_SKB | SEEN_A;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
                                                  vlan_tci) != 2);
                        off = offsetof(struct sk_buff, vlan_tci);
-                       emit_half_load_unsigned(r_s0, r_skb, off, ctx);
-                       if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) {
-                               emit_andi(r_A, r_s0, (u16)~VLAN_TAG_PRESENT, ctx);
-                       } else {
-                               emit_andi(r_A, r_s0, VLAN_TAG_PRESENT, ctx);
-                               /* return 1 if present */
-                               emit_sltu(r_A, r_zero, r_A, ctx);
-                       }
+                       emit_half_load_unsigned(r_A, r_skb, off, ctx);
+                       break;
+               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
+                       ctx->flags |= SEEN_SKB | SEEN_A;
+                       emit_load_byte(r_A, r_skb, PKT_VLAN_PRESENT_OFFSET(), ctx);
+                       if (PKT_VLAN_PRESENT_BIT)
+                               emit_srl(r_A, r_A, PKT_VLAN_PRESENT_BIT, ctx);
+                       if (PKT_VLAN_PRESENT_BIT < 7)
+                               emit_andi(r_A, r_A, 1, ctx);
                        break;
                case BPF_ANC | SKF_AD_PKTTYPE:
                        ctx->flags |= SEEN_SKB;
index aeb7b1b..b16710a 100644 (file)
@@ -854,6 +854,7 @@ static int build_one_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
        case BPF_ALU | BPF_MOD | BPF_X: /* ALU_REG */
        case BPF_ALU | BPF_LSH | BPF_X: /* ALU_REG */
        case BPF_ALU | BPF_RSH | BPF_X: /* ALU_REG */
+       case BPF_ALU | BPF_ARSH | BPF_X: /* ALU_REG */
                src = ebpf_to_mips_reg(ctx, insn, src_reg_no_fp);
                dst = ebpf_to_mips_reg(ctx, insn, dst_reg);
                if (src < 0 || dst < 0)
@@ -913,6 +914,9 @@ static int build_one_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
                case BPF_RSH:
                        emit_instr(ctx, srlv, dst, dst, src);
                        break;
+               case BPF_ARSH:
+                       emit_instr(ctx, srav, dst, dst, src);
+                       break;
                default:
                        pr_err("ALU_REG NOT HANDLED\n");
                        return -EINVAL;
index 41b71c4..c1ce6f4 100644 (file)
@@ -84,7 +84,7 @@ static struct rt2880_pmx_func pcie_rst_grp[] = {
 };
 static struct rt2880_pmx_func nd_sd_grp[] = {
        FUNC("nand", MT7620_GPIO_MODE_NAND, 45, 15),
-       FUNC("sd", MT7620_GPIO_MODE_SD, 45, 15)
+       FUNC("sd", MT7620_GPIO_MODE_SD, 47, 13)
 };
 
 static struct rt2880_pmx_group mt7620a_pinmux_data[] = {
index d8b8444..813d13f 100644 (file)
@@ -435,6 +435,7 @@ void __init prom_meminit(void)
 
        mlreset();
        szmem();
+       max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
 
        for (node = 0; node < MAX_COMPACT_NODES; node++) {
                if (node_online(node)) {
@@ -455,18 +456,8 @@ extern void setup_zero_pages(void);
 void __init paging_init(void)
 {
        unsigned long zones_size[MAX_NR_ZONES] = {0, };
-       unsigned node;
 
        pagetable_init();
-
-       for_each_online_node(node) {
-               unsigned long start_pfn, end_pfn;
-
-               get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
-
-               if (end_pfn > max_low_pfn)
-                       max_low_pfn = end_pfn;
-       }
        zones_size[ZONE_NORMAL] = max_low_pfn;
        free_area_init_nodes(zones_size);
 }
index a0a9679..8a41372 100644 (file)
@@ -211,29 +211,15 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
                           unsigned long frame_pointer)
 {
        unsigned long return_hooker = (unsigned long)&return_to_handler;
-       struct ftrace_graph_ent trace;
        unsigned long old;
-       int err;
 
        if (unlikely(atomic_read(&current->tracing_graph_pause)))
                return;
 
        old = *parent;
 
-       trace.func = self_addr;
-       trace.depth = current->curr_ret_stack + 1;
-
-       /* Only trace if the calling function expects to */
-       if (!ftrace_graph_entry(&trace))
-               return;
-
-       err = ftrace_push_return_trace(old, self_addr, &trace.depth,
-                                      frame_pointer, NULL);
-
-       if (err == -EBUSY)
-               return;
-
-       *parent = return_hooker;
+       if (!function_graph_enter(old, self_addr, frame_pointer, NULL))
+               *parent = return_hooker;
 }
 
 noinline void ftrace_graph_caller(void)
index d047a09..1085385 100644 (file)
@@ -71,6 +71,13 @@ ifdef CONFIG_MLONGCALLS
 KBUILD_CFLAGS_KERNEL += -mlong-calls
 endif
 
+# Without this, "ld -r" results in .text sections that are too big (> 0x40000)
+# for branches to reach stubs. And multiple .text sections trigger a warning
+# when creating the sysfs module information section.
+ifndef CONFIG_64BIT
+KBUILD_CFLAGS_MODULE += -ffunction-sections
+endif
+
 # select which processor to optimise for
 cflags-$(CONFIG_PA7000)                += -march=1.1 -mschedule=7100
 cflags-$(CONFIG_PA7200)                += -march=1.1 -mschedule=7200
index 16aec9b..8a63515 100644 (file)
@@ -37,8 +37,8 @@ static inline void arch_spin_unlock(arch_spinlock_t *x)
        volatile unsigned int *a;
 
        a = __ldcw_align(x);
-       /* Release with ordered store. */
-       __asm__ __volatile__("stw,ma %0,0(%1)" : : "r"(1), "r"(a) : "memory");
+       mb();
+       *a = 1;
 }
 
 static inline int arch_spin_trylock(arch_spinlock_t *x)
index 6fa8535..e46a415 100644 (file)
@@ -30,7 +30,6 @@ static void __hot prepare_ftrace_return(unsigned long *parent,
                                        unsigned long self_addr)
 {
        unsigned long old;
-       struct ftrace_graph_ent trace;
        extern int parisc_return_to_handler;
 
        if (unlikely(ftrace_graph_is_dead()))
@@ -41,19 +40,9 @@ static void __hot prepare_ftrace_return(unsigned long *parent,
 
        old = *parent;
 
-       trace.func = self_addr;
-       trace.depth = current->curr_ret_stack + 1;
-
-       /* Only trace if the calling function expects to */
-       if (!ftrace_graph_entry(&trace))
-               return;
-
-        if (ftrace_push_return_trace(old, self_addr, &trace.depth,
-                                    0, NULL) == -EBUSY)
-                return;
-
-       /* activate parisc_return_to_handler() as return point */
-       *parent = (unsigned long) &parisc_return_to_handler;
+       if (!function_graph_enter(old, self_addr, 0, NULL))
+               /* activate parisc_return_to_handler() as return point */
+               *parent = (unsigned long) &parisc_return_to_handler;
 }
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 
index 9505c31..a9bc90d 100644 (file)
@@ -640,7 +640,8 @@ cas_action:
        sub,<>  %r28, %r25, %r0
 2:     stw     %r24, 0(%r26)
        /* Free lock */
-       stw,ma  %r20, 0(%sr2,%r20)
+       sync
+       stw     %r20, 0(%sr2,%r20)
 #if ENABLE_LWS_DEBUG
        /* Clear thread register indicator */
        stw     %r0, 4(%sr2,%r20)
@@ -654,7 +655,8 @@ cas_action:
 3:             
        /* Error occurred on load or store */
        /* Free lock */
-       stw,ma  %r20, 0(%sr2,%r20)
+       sync
+       stw     %r20, 0(%sr2,%r20)
 #if ENABLE_LWS_DEBUG
        stw     %r0, 4(%sr2,%r20)
 #endif
@@ -855,7 +857,8 @@ cas2_action:
 
 cas2_end:
        /* Free lock */
-       stw,ma  %r20, 0(%sr2,%r20)
+       sync
+       stw     %r20, 0(%sr2,%r20)
        /* Enable interrupts */
        ssm     PSW_SM_I, %r0
        /* Return to userspace, set no error */
@@ -865,7 +868,8 @@ cas2_end:
 22:
        /* Error occurred on load or store */
        /* Free lock */
-       stw,ma  %r20, 0(%sr2,%r20)
+       sync
+       stw     %r20, 0(%sr2,%r20)
        ssm     PSW_SM_I, %r0
        ldo     1(%r0),%r28
        b       lws_exit
index 3935436..ed98831 100644 (file)
@@ -197,7 +197,7 @@ $(obj)/empty.c:
 $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds : $(obj)/%: $(srctree)/$(src)/%.S
        $(Q)cp $< $@
 
-$(obj)/serial.c: $(obj)/autoconf.h
+$(srctree)/$(src)/serial.c: $(obj)/autoconf.h
 
 $(obj)/autoconf.h: $(obj)/%: $(objtree)/include/generated/%
        $(Q)cp $< $@
index 32dfe6d..9b9d174 100644 (file)
@@ -15,7 +15,7 @@
 RELA = 7
 RELACOUNT = 0x6ffffff9
 
-       .text
+       .data
        /* A procedure descriptor used when booting this as a COFF file.
         * When making COFF, this comes first in the link and we're
         * linked at 0x500000.
@@ -23,6 +23,8 @@ RELACOUNT = 0x6ffffff9
        .globl  _zimage_start_opd
 _zimage_start_opd:
        .long   0x500000, 0, 0, 0
+       .text
+       b       _zimage_start
 
 #ifdef __powerpc64__
 .balign 8
index 3ef40b7..e746bec 100644 (file)
@@ -268,19 +268,13 @@ extern void _memcpy_toio(volatile void __iomem *dest, const void *src,
  * their hooks, a bitfield is reserved for use by the platform near the
  * top of MMIO addresses (not PIO, those have to cope the hard way).
  *
- * This bit field is 12 bits and is at the top of the IO virtual
- * addresses PCI_IO_INDIRECT_TOKEN_MASK.
+ * The highest address in the kernel virtual space are:
  *
- * The kernel virtual space is thus:
+ *  d0003fffffffffff   # with Hash MMU
+ *  c00fffffffffffff   # with Radix MMU
  *
- *  0xD000000000000000         : vmalloc
- *  0xD000080000000000         : PCI PHB IO space
- *  0xD000080080000000         : ioremap
- *  0xD0000fffffffffff         : end of ioremap region
- *
- * Since the top 4 bits are reserved as the region ID, we use thus
- * the next 12 bits and keep 4 bits available for the future if the
- * virtual address space is ever to be extended.
+ * The top 4 bits are reserved as the region ID on hash, leaving us 8 bits
+ * that can be used for the field.
  *
  * The direct IO mapping operations will then mask off those bits
  * before doing the actual access, though that only happen when
@@ -292,8 +286,8 @@ extern void _memcpy_toio(volatile void __iomem *dest, const void *src,
  */
 
 #ifdef CONFIG_PPC_INDIRECT_MMIO
-#define PCI_IO_IND_TOKEN_MASK  0x0fff000000000000ul
-#define PCI_IO_IND_TOKEN_SHIFT 48
+#define PCI_IO_IND_TOKEN_SHIFT 52
+#define PCI_IO_IND_TOKEN_MASK  (0xfful << PCI_IO_IND_TOKEN_SHIFT)
 #define PCI_FIX_ADDR(addr)                                             \
        ((PCI_IO_ADDR)(((unsigned long)(addr)) & ~PCI_IO_IND_TOKEN_MASK))
 #define PCI_GET_ADDR_TOKEN(addr)                                       \
index 8bf1b63..16a4981 100644 (file)
@@ -26,6 +26,8 @@
 #include <asm/ptrace.h>
 #include <asm/reg.h>
 
+#define perf_arch_bpf_user_pt_regs(regs) &regs->user_regs
+
 /*
  * Overload regs->result to specify whether we should use the MSR (result
  * is zero) or the SIAR (result is non zero).
index 6093bc8..9014592 100644 (file)
 #define PPC_INST_SLW                   0x7c000030
 #define PPC_INST_SLD                   0x7c000036
 #define PPC_INST_SRW                   0x7c000430
+#define PPC_INST_SRAW                  0x7c000630
+#define PPC_INST_SRAWI                 0x7c000670
 #define PPC_INST_SRD                   0x7c000436
 #define PPC_INST_SRAD                  0x7c000634
 #define PPC_INST_SRADI                 0x7c000674
                                        __PPC_RS(t) | __PPC_RA0(a) | __PPC_RB(b))
 #define PPC_SLBFEE_DOT(t, b)   stringify_in_c(.long PPC_INST_SLBFEE | \
                                        __PPC_RT(t) | __PPC_RB(b))
+#define __PPC_SLBFEE_DOT(t, b) stringify_in_c(.long PPC_INST_SLBFEE |  \
+                                              ___PPC_RT(t) | ___PPC_RB(b))
 #define PPC_ICBT(c,a,b)                stringify_in_c(.long PPC_INST_ICBT | \
                                       __PPC_CT(c) | __PPC_RA0(a) | __PPC_RB(b))
 /* PASemi instructions */
index f73886a..0b8a735 100644 (file)
@@ -54,6 +54,7 @@ struct pt_regs
 
 #ifdef CONFIG_PPC64
        unsigned long ppr;
+       unsigned long __pad;    /* Maintain 16 byte interrupt stack alignment */
 #endif
 };
 #endif
index a658091..3712152 100644 (file)
@@ -1,7 +1,6 @@
 # UAPI Header export list
 include include/uapi/asm-generic/Kbuild.asm
 
-generic-y += bpf_perf_event.h
 generic-y += param.h
 generic-y += poll.h
 generic-y += resource.h
diff --git a/arch/powerpc/include/uapi/asm/bpf_perf_event.h b/arch/powerpc/include/uapi/asm/bpf_perf_event.h
new file mode 100644 (file)
index 0000000..b551b74
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _UAPI__ASM_BPF_PERF_EVENT_H__
+#define _UAPI__ASM_BPF_PERF_EVENT_H__
+
+#include <asm/ptrace.h>
+
+typedef struct user_pt_regs bpf_user_pt_regs_t;
+
+#endif /* _UAPI__ASM_BPF_PERF_EVENT_H__ */
index 33b34a5..5b9dce1 100644 (file)
@@ -372,6 +372,8 @@ void __init find_legacy_serial_ports(void)
 
        /* Now find out if one of these is out firmware console */
        path = of_get_property(of_chosen, "linux,stdout-path", NULL);
+       if (path == NULL)
+               path = of_get_property(of_chosen, "stdout-path", NULL);
        if (path != NULL) {
                stdout = of_find_node_by_path(path);
                if (stdout)
@@ -595,8 +597,10 @@ static int __init check_legacy_serial_console(void)
        /* We are getting a weird phandle from OF ... */
        /* ... So use the full path instead */
        name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+       if (name == NULL)
+               name = of_get_property(of_chosen, "stdout-path", NULL);
        if (name == NULL) {
-               DBG(" no linux,stdout-path !\n");
+               DBG(" no stdout-path !\n");
                return -ENODEV;
        }
        prom_stdout = of_find_node_by_path(name);
index dab616a..f219765 100644 (file)
@@ -34,5 +34,10 @@ void arch_teardown_msi_irqs(struct pci_dev *dev)
 {
        struct pci_controller *phb = pci_bus_to_host(dev->bus);
 
-       phb->controller_ops.teardown_msi_irqs(dev);
+       /*
+        * We can be called even when arch_setup_msi_irqs() returns -ENOSYS,
+        * so check the pointer again.
+        */
+       if (phb->controller_ops.teardown_msi_irqs)
+               phb->controller_ops.teardown_msi_irqs(dev);
 }
index afb819f..714c348 100644 (file)
@@ -3266,12 +3266,17 @@ long do_syscall_trace_enter(struct pt_regs *regs)
        user_exit();
 
        if (test_thread_flag(TIF_SYSCALL_EMU)) {
-               ptrace_report_syscall(regs);
                /*
+                * A nonzero return code from tracehook_report_syscall_entry()
+                * tells us to prevent the syscall execution, but we are not
+                * going to execute it anyway.
+                *
                 * Returning -1 will skip the syscall execution. We want to
                 * avoid clobbering any register also, thus, not 'gotoing'
                 * skip label.
                 */
+               if (tracehook_report_syscall_entry(regs))
+                       ;
                return -1;
        }
 
index 2a51e4c..236c115 100644 (file)
@@ -636,6 +636,8 @@ static void *__init alloc_stack(unsigned long limit, int cpu)
 {
        unsigned long pa;
 
+       BUILD_BUG_ON(STACK_INT_FRAME_SIZE % 16);
+
        pa = memblock_alloc_base_nid(THREAD_SIZE, THREAD_SIZE, limit,
                                        early_cpu_to_node(cpu), MEMBLOCK_NONE);
        if (!pa) {
index 4bf051d..b65c8a3 100644 (file)
@@ -950,7 +950,6 @@ int ftrace_disable_ftrace_graph_caller(void)
  */
 unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip)
 {
-       struct ftrace_graph_ent trace;
        unsigned long return_hooker;
 
        if (unlikely(ftrace_graph_is_dead()))
@@ -961,18 +960,8 @@ unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip)
 
        return_hooker = ppc_function_entry(return_to_handler);
 
-       trace.func = ip;
-       trace.depth = current->curr_ret_stack + 1;
-
-       /* Only trace if the calling function expects to */
-       if (!ftrace_graph_entry(&trace))
-               goto out;
-
-       if (ftrace_push_return_trace(parent, ip, &trace.depth, 0,
-                                    NULL) == -EBUSY)
-               goto out;
-
-       parent = return_hooker;
+       if (!function_graph_enter(parent, ip, 0, NULL))
+               parent = return_hooker;
 out:
        return parent;
 }
index d65b961..a56f841 100644 (file)
@@ -983,6 +983,7 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
                ret = kvmhv_enter_nested_guest(vcpu);
                if (ret == H_INTERRUPT) {
                        kvmppc_set_gpr(vcpu, 3, 0);
+                       vcpu->arch.hcall_needed = 0;
                        return -EINTR;
                }
                break;
index 491b0f7..ea1d7c8 100644 (file)
@@ -6,8 +6,6 @@
 
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM kvm
-#define TRACE_INCLUDE_PATH .
-#define TRACE_INCLUDE_FILE trace
 
 /*
  * Tracepoint for guest mode entry.
@@ -120,4 +118,10 @@ TRACE_EVENT(kvm_check_requests,
 #endif /* _TRACE_KVM_H */
 
 /* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace
+
 #include <trace/define_trace.h>
index ac640e8..3837842 100644 (file)
@@ -6,8 +6,6 @@
 
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM kvm_booke
-#define TRACE_INCLUDE_PATH .
-#define TRACE_INCLUDE_FILE trace_booke
 
 #define kvm_trace_symbol_exit \
        {0, "CRITICAL"}, \
@@ -218,4 +216,11 @@ TRACE_EVENT(kvm_booke_queue_irqprio,
 #endif
 
 /* This part must be outside protection */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace_booke
+
 #include <trace/define_trace.h>
index bcfe8a9..8a1e3b0 100644 (file)
@@ -9,8 +9,6 @@
 
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM kvm_hv
-#define TRACE_INCLUDE_PATH .
-#define TRACE_INCLUDE_FILE trace_hv
 
 #define kvm_trace_symbol_hcall \
        {H_REMOVE,                      "H_REMOVE"}, \
@@ -497,4 +495,11 @@ TRACE_EVENT(kvmppc_run_vcpu_exit,
 #endif /* _TRACE_KVM_HV_H */
 
 /* This part must be outside protection */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace_hv
+
 #include <trace/define_trace.h>
index 2f9a882..46a46d3 100644 (file)
@@ -8,8 +8,6 @@
 
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM kvm_pr
-#define TRACE_INCLUDE_PATH .
-#define TRACE_INCLUDE_FILE trace_pr
 
 TRACE_EVENT(kvm_book3s_reenter,
        TP_PROTO(int r, struct kvm_vcpu *vcpu),
@@ -257,4 +255,11 @@ TRACE_EVENT(kvm_exit,
 #endif /* _TRACE_KVM_H */
 
 /* This part must be outside protection */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace_pr
+
 #include <trace/define_trace.h>
index 2b74f8a..6aa4166 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/hugetlb.h>
 #include <linux/io.h>
 #include <linux/mm.h>
+#include <linux/highmem.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
 #include <asm/fixmap.h>
index 7a9886f..a5091c0 100644 (file)
@@ -188,15 +188,20 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node,
        pr_debug("vmemmap_populate %lx..%lx, node %d\n", start, end, node);
 
        for (; start < end; start += page_size) {
-               void *p;
+               void *p = NULL;
                int rc;
 
                if (vmemmap_populated(start, page_size))
                        continue;
 
+               /*
+                * Allocate from the altmap first if we have one. This may
+                * fail due to alignment issues when using 16MB hugepages, so
+                * fall back to system memory if the altmap allocation fail.
+                */
                if (altmap)
                        p = altmap_alloc_block_buf(page_size, altmap);
-               else
+               if (!p)
                        p = vmemmap_alloc_block_buf(page_size, node);
                if (!p)
                        return -ENOMEM;
@@ -255,8 +260,15 @@ void __ref vmemmap_free(unsigned long start, unsigned long end,
 {
        unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
        unsigned long page_order = get_order(page_size);
+       unsigned long alt_start = ~0, alt_end = ~0;
+       unsigned long base_pfn;
 
        start = _ALIGN_DOWN(start, page_size);
+       if (altmap) {
+               alt_start = altmap->base_pfn;
+               alt_end = altmap->base_pfn + altmap->reserve +
+                         altmap->free + altmap->alloc + altmap->align;
+       }
 
        pr_debug("vmemmap_free %lx...%lx\n", start, end);
 
@@ -280,8 +292,9 @@ void __ref vmemmap_free(unsigned long start, unsigned long end,
                page = pfn_to_page(addr >> PAGE_SHIFT);
                section_base = pfn_to_page(vmemmap_section_start(start));
                nr_pages = 1 << page_order;
+               base_pfn = PHYS_PFN(addr);
 
-               if (altmap) {
+               if (base_pfn >= alt_start && base_pfn < alt_end) {
                        vmem_altmap_free(altmap, nr_pages);
                } else if (PageReserved(page)) {
                        /* allocated from bootmem */
index 3a048e9..ce28ae5 100644 (file)
@@ -1178,7 +1178,7 @@ static long vphn_get_associativity(unsigned long cpu,
 
        switch (rc) {
        case H_FUNCTION:
-               printk(KERN_INFO
+               printk_once(KERN_INFO
                        "VPHN is not supported. Disabling polling...\n");
                stop_topology_update();
                break;
index c3fdf29..bc3914d 100644 (file)
@@ -19,6 +19,7 @@
 #include <asm/mmu.h>
 #include <asm/mmu_context.h>
 #include <asm/paca.h>
+#include <asm/ppc-opcode.h>
 #include <asm/cputable.h>
 #include <asm/cacheflush.h>
 #include <asm/smp.h>
@@ -58,27 +59,19 @@ static inline unsigned long mk_vsid_data(unsigned long ea, int ssize,
        return __mk_vsid_data(get_kernel_vsid(ea, ssize), ssize, flags);
 }
 
-static void assert_slb_exists(unsigned long ea)
+static void assert_slb_presence(bool present, unsigned long ea)
 {
 #ifdef CONFIG_DEBUG_VM
        unsigned long tmp;
 
        WARN_ON_ONCE(mfmsr() & MSR_EE);
 
-       asm volatile("slbfee. %0, %1" : "=r"(tmp) : "r"(ea) : "cr0");
-       WARN_ON(tmp == 0);
-#endif
-}
-
-static void assert_slb_notexists(unsigned long ea)
-{
-#ifdef CONFIG_DEBUG_VM
-       unsigned long tmp;
+       if (!cpu_has_feature(CPU_FTR_ARCH_206))
+               return;
 
-       WARN_ON_ONCE(mfmsr() & MSR_EE);
+       asm volatile(__PPC_SLBFEE_DOT(%0, %1) : "=r"(tmp) : "r"(ea) : "cr0");
 
-       asm volatile("slbfee. %0, %1" : "=r"(tmp) : "r"(ea) : "cr0");
-       WARN_ON(tmp != 0);
+       WARN_ON(present == (tmp == 0));
 #endif
 }
 
@@ -114,7 +107,7 @@ static inline void create_shadowed_slbe(unsigned long ea, int ssize,
         */
        slb_shadow_update(ea, ssize, flags, index);
 
-       assert_slb_notexists(ea);
+       assert_slb_presence(false, ea);
        asm volatile("slbmte  %0,%1" :
                     : "r" (mk_vsid_data(ea, ssize, flags)),
                       "r" (mk_esid_data(ea, ssize, index))
@@ -137,7 +130,7 @@ void __slb_restore_bolted_realmode(void)
                       "r" (be64_to_cpu(p->save_area[index].esid)));
        }
 
-       assert_slb_exists(local_paca->kstack);
+       assert_slb_presence(true, local_paca->kstack);
 }
 
 /*
@@ -185,7 +178,7 @@ void slb_flush_and_restore_bolted(void)
                     :: "r" (be64_to_cpu(p->save_area[KSTACK_INDEX].vsid)),
                        "r" (be64_to_cpu(p->save_area[KSTACK_INDEX].esid))
                     : "memory");
-       assert_slb_exists(get_paca()->kstack);
+       assert_slb_presence(true, get_paca()->kstack);
 
        get_paca()->slb_cache_ptr = 0;
 
@@ -443,9 +436,9 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
                                ea = (unsigned long)
                                        get_paca()->slb_cache[i] << SID_SHIFT;
                                /*
-                                * Could assert_slb_exists here, but hypervisor
-                                * or machine check could have come in and
-                                * removed the entry at this point.
+                                * Could assert_slb_presence(true) here, but
+                                * hypervisor or machine check could have come
+                                * in and removed the entry at this point.
                                 */
 
                                slbie_data = ea;
@@ -676,7 +669,7 @@ static long slb_insert_entry(unsigned long ea, unsigned long context,
         * User preloads should add isync afterwards in case the kernel
         * accesses user memory before it returns to userspace with rfid.
         */
-       assert_slb_notexists(ea);
+       assert_slb_presence(false, ea);
        asm volatile("slbmte %0, %1" : : "r" (vsid_data), "r" (esid_data));
 
        barrier();
@@ -715,7 +708,7 @@ static long slb_allocate_kernel(unsigned long ea, unsigned long id)
                        return -EFAULT;
 
                if (ea < H_VMALLOC_END)
-                       flags = get_paca()->vmalloc_sllp;
+                       flags = local_paca->vmalloc_sllp;
                else
                        flags = SLB_VSID_KERNEL | mmu_psize_defs[mmu_io_psize].sllp;
        } else {
index 47fc666..c2d5192 100644 (file)
                                     ___PPC_RS(a) | ___PPC_RB(s))
 #define PPC_SRW(d, a, s)       EMIT(PPC_INST_SRW | ___PPC_RA(d) |            \
                                     ___PPC_RS(a) | ___PPC_RB(s))
+#define PPC_SRAW(d, a, s)      EMIT(PPC_INST_SRAW | ___PPC_RA(d) |           \
+                                    ___PPC_RS(a) | ___PPC_RB(s))
+#define PPC_SRAWI(d, a, i)     EMIT(PPC_INST_SRAWI | ___PPC_RA(d) |          \
+                                    ___PPC_RS(a) | __PPC_SH(i))
 #define PPC_SRD(d, a, s)       EMIT(PPC_INST_SRD | ___PPC_RA(d) |            \
                                     ___PPC_RS(a) | ___PPC_RB(s))
 #define PPC_SRAD(d, a, s)      EMIT(PPC_INST_SRAD | ___PPC_RA(d) |           \
index d5bfe24..91d223c 100644 (file)
@@ -379,18 +379,17 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
                                                          hash));
                        break;
                case BPF_ANC | SKF_AD_VLAN_TAG:
-               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
-                       BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
 
                        PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                          vlan_tci));
-                       if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) {
-                               PPC_ANDI(r_A, r_A, ~VLAN_TAG_PRESENT);
-                       } else {
-                               PPC_ANDI(r_A, r_A, VLAN_TAG_PRESENT);
-                               PPC_SRWI(r_A, r_A, 12);
-                       }
+                       break;
+               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
+                       PPC_LBZ_OFFS(r_A, r_skb, PKT_VLAN_PRESENT_OFFSET());
+                       if (PKT_VLAN_PRESENT_BIT)
+                               PPC_SRWI(r_A, r_A, PKT_VLAN_PRESENT_BIT);
+                       if (PKT_VLAN_PRESENT_BIT < 7)
+                               PPC_ANDI(r_A, r_A, 1);
                        break;
                case BPF_ANC | SKF_AD_QUEUE:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
index 50b1297..7ce5765 100644 (file)
@@ -166,7 +166,33 @@ static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
        PPC_BLR();
 }
 
-static void bpf_jit_emit_func_call(u32 *image, struct codegen_context *ctx, u64 func)
+static void bpf_jit_emit_func_call_hlp(u32 *image, struct codegen_context *ctx,
+                                      u64 func)
+{
+#ifdef PPC64_ELF_ABI_v1
+       /* func points to the function descriptor */
+       PPC_LI64(b2p[TMP_REG_2], func);
+       /* Load actual entry point from function descriptor */
+       PPC_BPF_LL(b2p[TMP_REG_1], b2p[TMP_REG_2], 0);
+       /* ... and move it to LR */
+       PPC_MTLR(b2p[TMP_REG_1]);
+       /*
+        * Load TOC from function descriptor at offset 8.
+        * We can clobber r2 since we get called through a
+        * function pointer (so caller will save/restore r2)
+        * and since we don't use a TOC ourself.
+        */
+       PPC_BPF_LL(2, b2p[TMP_REG_2], 8);
+#else
+       /* We can clobber r12 */
+       PPC_FUNC_ADDR(12, func);
+       PPC_MTLR(12);
+#endif
+       PPC_BLRL();
+}
+
+static void bpf_jit_emit_func_call_rel(u32 *image, struct codegen_context *ctx,
+                                      u64 func)
 {
        unsigned int i, ctx_idx = ctx->idx;
 
@@ -273,7 +299,7 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
 {
        const struct bpf_insn *insn = fp->insnsi;
        int flen = fp->len;
-       int i;
+       int i, ret;
 
        /* Start of epilogue code - will only be valid 2nd pass onwards */
        u32 exit_addr = addrs[flen];
@@ -284,8 +310,9 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
                u32 src_reg = b2p[insn[i].src_reg];
                s16 off = insn[i].off;
                s32 imm = insn[i].imm;
+               bool func_addr_fixed;
+               u64 func_addr;
                u64 imm64;
-               u8 *func;
                u32 true_cond;
                u32 tmp_idx;
 
@@ -502,9 +529,15 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
                        if (imm != 0)
                                PPC_SRDI(dst_reg, dst_reg, imm);
                        break;
+               case BPF_ALU | BPF_ARSH | BPF_X: /* (s32) dst >>= src */
+                       PPC_SRAW(dst_reg, dst_reg, src_reg);
+                       goto bpf_alu32_trunc;
                case BPF_ALU64 | BPF_ARSH | BPF_X: /* (s64) dst >>= src */
                        PPC_SRAD(dst_reg, dst_reg, src_reg);
                        break;
+               case BPF_ALU | BPF_ARSH | BPF_K: /* (s32) dst >>= imm */
+                       PPC_SRAWI(dst_reg, dst_reg, imm);
+                       goto bpf_alu32_trunc;
                case BPF_ALU64 | BPF_ARSH | BPF_K: /* (s64) dst >>= imm */
                        if (imm != 0)
                                PPC_SRADI(dst_reg, dst_reg, imm);
@@ -711,23 +744,15 @@ emit_clear:
                case BPF_JMP | BPF_CALL:
                        ctx->seen |= SEEN_FUNC;
 
-                       /* bpf function call */
-                       if (insn[i].src_reg == BPF_PSEUDO_CALL)
-                               if (!extra_pass)
-                                       func = NULL;
-                               else if (fp->aux->func && off < fp->aux->func_cnt)
-                                       /* use the subprog id from the off
-                                        * field to lookup the callee address
-                                        */
-                                       func = (u8 *) fp->aux->func[off]->bpf_func;
-                               else
-                                       return -EINVAL;
-                       /* kernel helper call */
-                       else
-                               func = (u8 *) __bpf_call_base + imm;
-
-                       bpf_jit_emit_func_call(image, ctx, (u64)func);
+                       ret = bpf_jit_get_func_addr(fp, &insn[i], extra_pass,
+                                                   &func_addr, &func_addr_fixed);
+                       if (ret < 0)
+                               return ret;
 
+                       if (func_addr_fixed)
+                               bpf_jit_emit_func_call_hlp(image, ctx, func_addr);
+                       else
+                               bpf_jit_emit_func_call_rel(image, ctx, func_addr);
                        /* move return value from r3 to BPF_REG_0 */
                        PPC_MR(b2p[BPF_REG_0], 3);
                        break;
@@ -872,6 +897,55 @@ cond_branch:
        return 0;
 }
 
+/* Fix the branch target addresses for subprog calls */
+static int bpf_jit_fixup_subprog_calls(struct bpf_prog *fp, u32 *image,
+                                      struct codegen_context *ctx, u32 *addrs)
+{
+       const struct bpf_insn *insn = fp->insnsi;
+       bool func_addr_fixed;
+       u64 func_addr;
+       u32 tmp_idx;
+       int i, ret;
+
+       for (i = 0; i < fp->len; i++) {
+               /*
+                * During the extra pass, only the branch target addresses for
+                * the subprog calls need to be fixed. All other instructions
+                * can left untouched.
+                *
+                * The JITed image length does not change because we already
+                * ensure that the JITed instruction sequence for these calls
+                * are of fixed length by padding them with NOPs.
+                */
+               if (insn[i].code == (BPF_JMP | BPF_CALL) &&
+                   insn[i].src_reg == BPF_PSEUDO_CALL) {
+                       ret = bpf_jit_get_func_addr(fp, &insn[i], true,
+                                                   &func_addr,
+                                                   &func_addr_fixed);
+                       if (ret < 0)
+                               return ret;
+
+                       /*
+                        * Save ctx->idx as this would currently point to the
+                        * end of the JITed image and set it to the offset of
+                        * the instruction sequence corresponding to the
+                        * subprog call temporarily.
+                        */
+                       tmp_idx = ctx->idx;
+                       ctx->idx = addrs[i] / 4;
+                       bpf_jit_emit_func_call_rel(image, ctx, func_addr);
+
+                       /*
+                        * Restore ctx->idx here. This is safe as the length
+                        * of the JITed sequence remains unchanged.
+                        */
+                       ctx->idx = tmp_idx;
+               }
+       }
+
+       return 0;
+}
+
 struct powerpc64_jit_data {
        struct bpf_binary_header *header;
        u32 *addrs;
@@ -970,6 +1044,22 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
 skip_init_ctx:
        code_base = (u32 *)(image + FUNCTION_DESCR_SIZE);
 
+       if (extra_pass) {
+               /*
+                * Do not touch the prologue and epilogue as they will remain
+                * unchanged. Only fix the branch target address for subprog
+                * calls in the body.
+                *
+                * This does not change the offsets and lengths of the subprog
+                * call instruction sequences and hence, the size of the JITed
+                * image as well.
+                */
+               bpf_jit_fixup_subprog_calls(fp, code_base, &cgctx, addrs);
+
+               /* There is no need to perform the usual passes. */
+               goto skip_codegen_passes;
+       }
+
        /* Code generation passes 1-2 */
        for (pass = 1; pass < 3; pass++) {
                /* Now build the prologue, body code & epilogue for real. */
@@ -983,6 +1073,7 @@ skip_init_ctx:
                                proglen - (cgctx.idx * 4), cgctx.seen);
        }
 
+skip_codegen_passes:
        if (bpf_jit_enable > 1)
                /*
                 * Note that we output the base address of the code_base
index 6f60e09..75b9352 100644 (file)
@@ -102,63 +102,6 @@ struct pci_dev *pnv_pci_get_npu_dev(struct pci_dev *gpdev, int index)
 }
 EXPORT_SYMBOL(pnv_pci_get_npu_dev);
 
-#define NPU_DMA_OP_UNSUPPORTED()                                       \
-       dev_err_once(dev, "%s operation unsupported for NVLink devices\n", \
-               __func__)
-
-static void *dma_npu_alloc(struct device *dev, size_t size,
-                          dma_addr_t *dma_handle, gfp_t flag,
-                          unsigned long attrs)
-{
-       NPU_DMA_OP_UNSUPPORTED();
-       return NULL;
-}
-
-static void dma_npu_free(struct device *dev, size_t size,
-                        void *vaddr, dma_addr_t dma_handle,
-                        unsigned long attrs)
-{
-       NPU_DMA_OP_UNSUPPORTED();
-}
-
-static dma_addr_t dma_npu_map_page(struct device *dev, struct page *page,
-                                  unsigned long offset, size_t size,
-                                  enum dma_data_direction direction,
-                                  unsigned long attrs)
-{
-       NPU_DMA_OP_UNSUPPORTED();
-       return 0;
-}
-
-static int dma_npu_map_sg(struct device *dev, struct scatterlist *sglist,
-                         int nelems, enum dma_data_direction direction,
-                         unsigned long attrs)
-{
-       NPU_DMA_OP_UNSUPPORTED();
-       return 0;
-}
-
-static int dma_npu_dma_supported(struct device *dev, u64 mask)
-{
-       NPU_DMA_OP_UNSUPPORTED();
-       return 0;
-}
-
-static u64 dma_npu_get_required_mask(struct device *dev)
-{
-       NPU_DMA_OP_UNSUPPORTED();
-       return 0;
-}
-
-static const struct dma_map_ops dma_npu_ops = {
-       .map_page               = dma_npu_map_page,
-       .map_sg                 = dma_npu_map_sg,
-       .alloc                  = dma_npu_alloc,
-       .free                   = dma_npu_free,
-       .dma_supported          = dma_npu_dma_supported,
-       .get_required_mask      = dma_npu_get_required_mask,
-};
-
 /*
  * Returns the PE assoicated with the PCI device of the given
  * NPU. Returns the linked pci device if pci_dev != NULL.
@@ -270,10 +213,11 @@ static void pnv_npu_dma_set_32(struct pnv_ioda_pe *npe)
        rc = pnv_npu_set_window(npe, 0, gpe->table_group.tables[0]);
 
        /*
-        * We don't initialise npu_pe->tce32_table as we always use
-        * dma_npu_ops which are nops.
+        * NVLink devices use the same TCE table configuration as
+        * their parent device so drivers shouldn't be doing DMA
+        * operations directly on these devices.
         */
-       set_dma_ops(&npe->pdev->dev, &dma_npu_ops);
+       set_dma_ops(&npe->pdev->dev, NULL);
 }
 
 /*
index 2e4bd32..472b784 100644 (file)
@@ -140,8 +140,7 @@ config IBMEBUS
          Bus device driver for GX bus based adapters.
 
 config PAPR_SCM
-       depends on PPC_PSERIES && MEMORY_HOTPLUG
-       select LIBNVDIMM
+       depends on PPC_PSERIES && MEMORY_HOTPLUG && LIBNVDIMM
        tristate "Support for the PAPR Storage Class Memory interface"
        help
          Enable access to hypervisor provided storage class memory.
index ee9372b..7d6457a 100644 (file)
@@ -55,7 +55,7 @@ static int drc_pmem_bind(struct papr_scm_priv *p)
        do {
                rc = plpar_hcall(H_SCM_BIND_MEM, ret, p->drc_index, 0,
                                p->blocks, BIND_ANY_ADDR, token);
-               token = be64_to_cpu(ret[0]);
+               token = ret[0];
                cond_resched();
        } while (rc == H_BUSY);
 
@@ -64,7 +64,7 @@ static int drc_pmem_bind(struct papr_scm_priv *p)
                return -ENXIO;
        }
 
-       p->bound_addr = be64_to_cpu(ret[1]);
+       p->bound_addr = ret[1];
 
        dev_dbg(&p->pdev->dev, "bound drc %x to %pR\n", p->drc_index, &p->res);
 
@@ -82,7 +82,7 @@ static int drc_pmem_unbind(struct papr_scm_priv *p)
        do {
                rc = plpar_hcall(H_SCM_UNBIND_MEM, ret, p->drc_index,
                                p->bound_addr, p->blocks, token);
-               token = be64_to_cpu(ret);
+               token = ret[0];
                cond_resched();
        } while (rc == H_BUSY);
 
@@ -223,6 +223,9 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p)
                goto err;
        }
 
+       if (nvdimm_bus_check_dimm_count(p->bus, 1))
+               goto err;
+
        /* now add the region */
 
        memset(&mapping, 0, sizeof(mapping));
@@ -257,9 +260,12 @@ err:       nvdimm_bus_unregister(p->bus);
 
 static int papr_scm_probe(struct platform_device *pdev)
 {
-       uint32_t drc_index, metadata_size, unit_cap[2];
        struct device_node *dn = pdev->dev.of_node;
+       u32 drc_index, metadata_size;
+       u64 blocks, block_size;
        struct papr_scm_priv *p;
+       const char *uuid_str;
+       u64 uuid[2];
        int rc;
 
        /* check we have all the required DT properties */
@@ -268,8 +274,18 @@ static int papr_scm_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       if (of_property_read_u32_array(dn, "ibm,unit-capacity", unit_cap, 2)) {
-               dev_err(&pdev->dev, "%pOF: missing unit-capacity!\n", dn);
+       if (of_property_read_u64(dn, "ibm,block-size", &block_size)) {
+               dev_err(&pdev->dev, "%pOF: missing block-size!\n", dn);
+               return -ENODEV;
+       }
+
+       if (of_property_read_u64(dn, "ibm,number-of-blocks", &blocks)) {
+               dev_err(&pdev->dev, "%pOF: missing number-of-blocks!\n", dn);
+               return -ENODEV;
+       }
+
+       if (of_property_read_string(dn, "ibm,unit-guid", &uuid_str)) {
+               dev_err(&pdev->dev, "%pOF: missing unit-guid!\n", dn);
                return -ENODEV;
        }
 
@@ -282,8 +298,13 @@ static int papr_scm_probe(struct platform_device *pdev)
 
        p->dn = dn;
        p->drc_index = drc_index;
-       p->block_size = unit_cap[0];
-       p->blocks     = unit_cap[1];
+       p->block_size = block_size;
+       p->blocks = blocks;
+
+       /* We just need to ensure that set cookies are unique across */
+       uuid_parse(uuid_str, (uuid_t *) uuid);
+       p->nd_set.cookie1 = uuid[0];
+       p->nd_set.cookie2 = uuid[1];
 
        /* might be zero */
        p->metadata_size = metadata_size;
@@ -296,7 +317,7 @@ static int papr_scm_probe(struct platform_device *pdev)
 
        /* setup the resource for the newly bound range */
        p->res.start = p->bound_addr;
-       p->res.end   = p->bound_addr + p->blocks * p->block_size;
+       p->res.end   = p->bound_addr + p->blocks * p->block_size - 1;
        p->res.name  = pdev->name;
        p->res.flags = IORESOURCE_MEM;
 
index d101461..4b594f2 100644 (file)
@@ -71,10 +71,27 @@ KBUILD_CFLAGS += $(call cc-option,-mstrict-align)
 # arch specific predefines for sparse
 CHECKFLAGS += -D__riscv -D__riscv_xlen=$(BITS)
 
+# Default target when executing plain make
+boot           := arch/riscv/boot
+KBUILD_IMAGE   := $(boot)/Image.gz
+
 head-y := arch/riscv/kernel/head.o
 
 core-y += arch/riscv/kernel/ arch/riscv/mm/
 
 libs-y += arch/riscv/lib/
 
-all: vmlinux
+PHONY += vdso_install
+vdso_install:
+       $(Q)$(MAKE) $(build)=arch/riscv/kernel/vdso $@
+
+all: Image.gz
+
+Image: vmlinux
+       $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+
+Image.%: Image
+       $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+
+zinstall install:
+       $(Q)$(MAKE) $(build)=$(boot) $@
diff --git a/arch/riscv/boot/.gitignore b/arch/riscv/boot/.gitignore
new file mode 100644 (file)
index 0000000..8dab0bb
--- /dev/null
@@ -0,0 +1,2 @@
+Image
+Image.gz
diff --git a/arch/riscv/boot/Makefile b/arch/riscv/boot/Makefile
new file mode 100644 (file)
index 0000000..0990a9f
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# arch/riscv/boot/Makefile
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies.
+#
+# 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.
+#
+# Copyright (C) 2018, Anup Patel.
+# Author: Anup Patel <anup@brainfault.org>
+#
+# Based on the ia64 and arm64 boot/Makefile.
+#
+
+OBJCOPYFLAGS_Image :=-O binary -R .note -R .note.gnu.build-id -R .comment -S
+
+targets := Image
+
+$(obj)/Image: vmlinux FORCE
+       $(call if_changed,objcopy)
+
+$(obj)/Image.gz: $(obj)/Image FORCE
+       $(call if_changed,gzip)
+
+install:
+       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
+       $(obj)/Image System.map "$(INSTALL_PATH)"
+
+zinstall:
+       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
+       $(obj)/Image.gz System.map "$(INSTALL_PATH)"
diff --git a/arch/riscv/boot/install.sh b/arch/riscv/boot/install.sh
new file mode 100644 (file)
index 0000000..18c3915
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# arch/riscv/boot/install.sh
+#
+# 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.
+#
+# Copyright (C) 1995 by Linus Torvalds
+#
+# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
+# Adapted from code in arch/i386/boot/install.sh by Russell King
+#
+# "make install" script for the RISC-V Linux port
+#
+# Arguments:
+#   $1 - kernel version
+#   $2 - kernel image file
+#   $3 - kernel map file
+#   $4 - default install path (blank if root directory)
+#
+
+verify () {
+       if [ ! -f "$1" ]; then
+               echo ""                                                   1>&2
+               echo " *** Missing file: $1"                              1>&2
+               echo ' *** You need to run "make" before "make install".' 1>&2
+               echo ""                                                   1>&2
+               exit 1
+       fi
+}
+
+# Make sure the files actually exist
+verify "$2"
+verify "$3"
+
+# User may have a custom install script
+if [ -x ~/bin/${INSTALLKERNEL} ]; then exec ~/bin/${INSTALLKERNEL} "$@"; fi
+if [ -x /sbin/${INSTALLKERNEL} ]; then exec /sbin/${INSTALLKERNEL} "$@"; fi
+
+if [ "$(basename $2)" = "Image.gz" ]; then
+# Compressed install
+  echo "Installing compressed kernel"
+  base=vmlinuz
+else
+# Normal install
+  echo "Installing normal kernel"
+  base=vmlinux
+fi
+
+if [ -f $4/$base-$1 ]; then
+  mv $4/$base-$1 $4/$base-$1.old
+fi
+cat $2 > $4/$base-$1
+
+# Install system map file
+if [ -f $4/System.map-$1 ]; then
+  mv $4/System.map-$1 $4/System.map-$1.old
+fi
+cp $3 $4/System.map-$1
index 07fa9ea..ef4f15d 100644 (file)
@@ -76,4 +76,5 @@ CONFIG_NFS_V4_1=y
 CONFIG_NFS_V4_2=y
 CONFIG_ROOT_NFS=y
 CONFIG_CRYPTO_USER_API_HASH=y
+CONFIG_PRINTK_TIME=y
 # CONFIG_RCU_TRACE is not set
index 349df33..cd2af4b 100644 (file)
@@ -8,6 +8,7 @@
 
 #define MODULE_ARCH_VERMAGIC    "riscv"
 
+struct module;
 u64 module_emit_got_entry(struct module *mod, u64 val);
 u64 module_emit_plt_entry(struct module *mod, u64 val);
 
index 2c5df94..bbe1862 100644 (file)
@@ -56,8 +56,8 @@ struct pt_regs {
        unsigned long sstatus;
        unsigned long sbadaddr;
        unsigned long scause;
-        /* a0 value before the syscall */
-        unsigned long orig_a0;
+       /* a0 value before the syscall */
+       unsigned long orig_a0;
 };
 
 #ifdef CONFIG_64BIT
index 473cfc8..8c3e3e3 100644 (file)
@@ -400,13 +400,13 @@ extern unsigned long __must_check __asm_copy_from_user(void *to,
 static inline unsigned long
 raw_copy_from_user(void *to, const void __user *from, unsigned long n)
 {
-       return __asm_copy_to_user(to, from, n);
+       return __asm_copy_from_user(to, from, n);
 }
 
 static inline unsigned long
 raw_copy_to_user(void __user *to, const void *from, unsigned long n)
 {
-       return __asm_copy_from_user(to, from, n);
+       return __asm_copy_to_user(to, from, n);
 }
 
 extern long strncpy_from_user(char *dest, const char __user *src, long count);
index eff7aa9..fef96f1 100644 (file)
 
 /*
  * There is explicitly no include guard here because this file is expected to
- * be included multiple times.  See uapi/asm/syscalls.h for more info.
+ * be included multiple times.
  */
 
-#define __ARCH_WANT_NEW_STAT
 #define __ARCH_WANT_SYS_CLONE
+
 #include <uapi/asm/unistd.h>
-#include <uapi/asm/syscalls.h>
diff --git a/arch/riscv/include/uapi/asm/syscalls.h b/arch/riscv/include/uapi/asm/syscalls.h
deleted file mode 100644 (file)
index 206dc4b..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2017-2018 SiFive
- */
-
-/*
- * There is explicitly no include guard here because this file is expected to
- * be included multiple times in order to define the syscall macros via
- * __SYSCALL.
- */
-
-/*
- * Allows the instruction cache to be flushed from userspace.  Despite RISC-V
- * having a direct 'fence.i' instruction available to userspace (which we
- * can't trap!), that's not actually viable when running on Linux because the
- * kernel might schedule a process on another hart.  There is no way for
- * userspace to handle this without invoking the kernel (as it doesn't know the
- * thread->hart mappings), so we've defined a RISC-V specific system call to
- * flush the instruction cache.
- *
- * __NR_riscv_flush_icache is defined to flush the instruction cache over an
- * address range, with the flush applying to either all threads or just the
- * caller.  We don't currently do anything with the address range, that's just
- * in there for forwards compatibility.
- */
-#ifndef __NR_riscv_flush_icache
-#define __NR_riscv_flush_icache (__NR_arch_specific_syscall + 15)
-#endif
-__SYSCALL(__NR_riscv_flush_icache, sys_riscv_flush_icache)
diff --git a/arch/riscv/include/uapi/asm/unistd.h b/arch/riscv/include/uapi/asm/unistd.h
new file mode 100644 (file)
index 0000000..1f3bd3e
--- /dev/null
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2018 David Abdurachmanov <david.abdurachmanov@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef __LP64__
+#define __ARCH_WANT_NEW_STAT
+#endif /* __LP64__ */
+
+#include <asm-generic/unistd.h>
+
+/*
+ * Allows the instruction cache to be flushed from userspace.  Despite RISC-V
+ * having a direct 'fence.i' instruction available to userspace (which we
+ * can't trap!), that's not actually viable when running on Linux because the
+ * kernel might schedule a process on another hart.  There is no way for
+ * userspace to handle this without invoking the kernel (as it doesn't know the
+ * thread->hart mappings), so we've defined a RISC-V specific system call to
+ * flush the instruction cache.
+ *
+ * __NR_riscv_flush_icache is defined to flush the instruction cache over an
+ * address range, with the flush applying to either all threads or just the
+ * caller.  We don't currently do anything with the address range, that's just
+ * in there for forwards compatibility.
+ */
+#ifndef __NR_riscv_flush_icache
+#define __NR_riscv_flush_icache (__NR_arch_specific_syscall + 15)
+#endif
+__SYSCALL(__NR_riscv_flush_icache, sys_riscv_flush_icache)
index 3a5a2ee..b4a7d44 100644 (file)
@@ -64,7 +64,7 @@ int riscv_of_processor_hartid(struct device_node *node)
 
 static void print_isa(struct seq_file *f, const char *orig_isa)
 {
-       static const char *ext = "mafdc";
+       static const char *ext = "mafdcsu";
        const char *isa = orig_isa;
        const char *e;
 
@@ -88,11 +88,14 @@ static void print_isa(struct seq_file *f, const char *orig_isa)
        /*
         * Check the rest of the ISA string for valid extensions, printing those
         * we find.  RISC-V ISA strings define an order, so we only print the
-        * extension bits when they're in order.
+        * extension bits when they're in order. Hide the supervisor (S)
+        * extension from userspace as it's not accessible from there.
         */
        for (e = ext; *e != '\0'; ++e) {
                if (isa[0] == e[0]) {
-                       seq_write(f, isa, 1);
+                       if (isa[0] != 's')
+                               seq_write(f, isa, 1);
+
                        isa++;
                }
        }
index 1157b6b..c433f6d 100644 (file)
@@ -132,7 +132,6 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
 {
        unsigned long return_hooker = (unsigned long)&return_to_handler;
        unsigned long old;
-       struct ftrace_graph_ent trace;
        int err;
 
        if (unlikely(atomic_read(&current->tracing_graph_pause)))
@@ -144,17 +143,8 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
         */
        old = *parent;
 
-       trace.func = self_addr;
-       trace.depth = current->curr_ret_stack + 1;
-
-       if (!ftrace_graph_entry(&trace))
-               return;
-
-       err = ftrace_push_return_trace(old, self_addr, &trace.depth,
-                                      frame_pointer, parent);
-       if (err == -EBUSY)
-               return;
-       *parent = return_hooker;
+       if (function_graph_enter(old, self_addr, frame_pointer, parent))
+               *parent = return_hooker;
 }
 
 #ifdef CONFIG_DYNAMIC_FTRACE
index 711190d..fe884cd 100644 (file)
@@ -44,6 +44,16 @@ ENTRY(_start)
        amoadd.w a3, a2, (a3)
        bnez a3, .Lsecondary_start
 
+       /* Clear BSS for flat non-ELF images */
+       la a3, __bss_start
+       la a4, __bss_stop
+       ble a4, a3, clear_bss_done
+clear_bss:
+       REG_S zero, (a3)
+       add a3, a3, RISCV_SZPTR
+       blt a3, a4, clear_bss
+clear_bss_done:
+
        /* Save hart ID and DTB physical address */
        mv s0, a0
        mv s1, a1
index 3303ed2..7dd3081 100644 (file)
@@ -21,7 +21,7 @@ static int apply_r_riscv_32_rela(struct module *me, u32 *location, Elf_Addr v)
 {
        if (v != (u32)v) {
                pr_err("%s: value %016llx out of range for 32-bit field\n",
-                      me->name, v);
+                      me->name, (long long)v);
                return -EINVAL;
        }
        *location = v;
@@ -102,7 +102,7 @@ static int apply_r_riscv_pcrel_hi20_rela(struct module *me, u32 *location,
        if (offset != (s32)offset) {
                pr_err(
                  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
-                 me->name, v, location);
+                 me->name, (long long)v, location);
                return -EINVAL;
        }
 
@@ -144,7 +144,7 @@ static int apply_r_riscv_hi20_rela(struct module *me, u32 *location,
        if (IS_ENABLED(CMODEL_MEDLOW)) {
                pr_err(
                  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
-                 me->name, v, location);
+                 me->name, (long long)v, location);
                return -EINVAL;
        }
 
@@ -188,7 +188,7 @@ static int apply_r_riscv_got_hi20_rela(struct module *me, u32 *location,
        } else {
                pr_err(
                  "%s: can not generate the GOT entry for symbol = %016llx from PC = %p\n",
-                 me->name, v, location);
+                 me->name, (long long)v, location);
                return -EINVAL;
        }
 
@@ -212,7 +212,7 @@ static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location,
                } else {
                        pr_err(
                          "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
-                         me->name, v, location);
+                         me->name, (long long)v, location);
                        return -EINVAL;
                }
        }
@@ -234,7 +234,7 @@ static int apply_r_riscv_call_rela(struct module *me, u32 *location,
        if (offset != fill_v) {
                pr_err(
                  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
-                 me->name, v, location);
+                 me->name, (long long)v, location);
                return -EINVAL;
        }
 
index ece8499..65df1df 100644 (file)
@@ -74,7 +74,7 @@ SECTIONS
                *(.sbss*)
        }
 
-       BSS_SECTION(0, 0, 0)
+       BSS_SECTION(PAGE_SIZE, PAGE_SIZE, 0)
 
        EXCEPTION_TABLE(0x10)
        NOTES
index 5739bd0..4e2e600 100644 (file)
@@ -3,6 +3,6 @@ lib-y   += memcpy.o
 lib-y  += memset.o
 lib-y  += uaccess.o
 
-lib-(CONFIG_64BIT) += tishift.o
+lib-$(CONFIG_64BIT) += tishift.o
 
 lib-$(CONFIG_32BIT) += udivdi3.o
index 84be7f0..39b13d7 100644 (file)
@@ -203,22 +203,13 @@ device_initcall(ftrace_plt_init);
  */
 unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip)
 {
-       struct ftrace_graph_ent trace;
-
        if (unlikely(ftrace_graph_is_dead()))
                goto out;
        if (unlikely(atomic_read(&current->tracing_graph_pause)))
                goto out;
        ip -= MCOUNT_INSN_SIZE;
-       trace.func = ip;
-       trace.depth = current->curr_ret_stack + 1;
-       /* Only trace if the calling function expects to. */
-       if (!ftrace_graph_entry(&trace))
-               goto out;
-       if (ftrace_push_return_trace(parent, ip, &trace.depth, 0,
-                                    NULL) == -EBUSY)
-               goto out;
-       parent = (unsigned long) return_to_handler;
+       if (!function_graph_enter(parent, ip, 0, NULL))
+               parent = (unsigned long) return_to_handler;
 out:
        return parent;
 }
index 74091fd..d5523ad 100644 (file)
@@ -346,6 +346,8 @@ static int __hw_perf_event_init(struct perf_event *event)
                break;
 
        case PERF_TYPE_HARDWARE:
+               if (is_sampling_event(event))   /* No sampling support */
+                       return -ENOENT;
                ev = attr->config;
                /* Count user space (problem-state) only */
                if (!attr->exclude_user && attr->exclude_kernel) {
index 814f265..6791562 100644 (file)
@@ -131,6 +131,7 @@ void crst_table_downgrade(struct mm_struct *mm)
        }
 
        pgd = mm->pgd;
+       mm_dec_nr_pmds(mm);
        mm->pgd = (pgd_t *) (pgd_val(*pgd) & _REGION_ENTRY_ORIGIN);
        mm->context.asce_limit = _REGION3_SIZE;
        mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH |
index d7052cb..3ff758e 100644 (file)
@@ -821,10 +821,22 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i
        /*
         * BPF_ARSH
         */
+       case BPF_ALU | BPF_ARSH | BPF_X: /* ((s32) dst) >>= src */
+               /* sra %dst,%dst,0(%src) */
+               EMIT4_DISP(0x8a000000, dst_reg, src_reg, 0);
+               EMIT_ZERO(dst_reg);
+               break;
        case BPF_ALU64 | BPF_ARSH | BPF_X: /* ((s64) dst) >>= src */
                /* srag %dst,%dst,0(%src) */
                EMIT6_DISP_LH(0xeb000000, 0x000a, dst_reg, dst_reg, src_reg, 0);
                break;
+       case BPF_ALU | BPF_ARSH | BPF_K: /* ((s32) dst >> imm */
+               if (imm == 0)
+                       break;
+               /* sra %dst,imm(%r0) */
+               EMIT4_DISP(0x8a000000, dst_reg, REG_0, imm);
+               EMIT_ZERO(dst_reg);
+               break;
        case BPF_ALU64 | BPF_ARSH | BPF_K: /* ((s64) dst) >>= imm */
                if (imm == 0)
                        break;
index 98cb8c8..4f7f235 100644 (file)
@@ -24,6 +24,7 @@
 #define __IO_PREFIX     generic
 #include <asm/io_generic.h>
 #include <asm/io_trapped.h>
+#include <asm-generic/pci_iomap.h>
 #include <mach/mangle-port.h>
 
 #define __raw_writeb(v,a)      (__chk_io_ptr(a), *(volatile u8  __force *)(a) = (v))
index 96dd9f7..1b04270 100644 (file)
@@ -321,8 +321,7 @@ int ftrace_disable_ftrace_graph_caller(void)
 void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
 {
        unsigned long old;
-       int faulted, err;
-       struct ftrace_graph_ent trace;
+       int faulted;
        unsigned long return_hooker = (unsigned long)&return_to_handler;
 
        if (unlikely(ftrace_graph_is_dead()))
@@ -365,18 +364,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
                return;
        }
 
-       err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0, NULL);
-       if (err == -EBUSY) {
+       if (function_graph_enter(old, self_addr, 0, NULL))
                __raw_writel(old, parent);
-               return;
-       }
-
-       trace.func = self_addr;
-
-       /* Only trace if the calling function expects to */
-       if (!ftrace_graph_entry(&trace)) {
-               current->curr_ret_stack--;
-               __raw_writel(old, parent);
-       }
 }
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
index 915dda4..684b84c 100644 (file)
@@ -126,20 +126,11 @@ unsigned long prepare_ftrace_return(unsigned long parent,
                                    unsigned long frame_pointer)
 {
        unsigned long return_hooker = (unsigned long) &return_to_handler;
-       struct ftrace_graph_ent trace;
 
        if (unlikely(atomic_read(&current->tracing_graph_pause)))
                return parent + 8UL;
 
-       trace.func = self_addr;
-       trace.depth = current->curr_ret_stack + 1;
-
-       /* Only trace if the calling function expects to */
-       if (!ftrace_graph_entry(&trace))
-               return parent + 8UL;
-
-       if (ftrace_push_return_trace(parent, self_addr, &trace.depth,
-                                    frame_pointer, NULL) == -EBUSY)
+       if (function_graph_enter(parent, self_addr, frame_pointer, NULL))
                return parent + 8UL;
 
        return return_hooker;
index 40d008b..05eb016 100644 (file)
@@ -108,10 +108,9 @@ int iommu_table_init(struct iommu *iommu, int tsbsize,
        /* Allocate and initialize the free area map.  */
        sz = num_tsb_entries / 8;
        sz = (sz + 7UL) & ~7UL;
-       iommu->tbl.map = kmalloc_node(sz, GFP_KERNEL, numa_node);
+       iommu->tbl.map = kzalloc_node(sz, GFP_KERNEL, numa_node);
        if (!iommu->tbl.map)
                return -ENOMEM;
-       memset(iommu->tbl.map, 0, sz);
 
        iommu_tbl_pool_init(&iommu->tbl, num_tsb_entries, IO_PAGE_SHIFT,
                            (tlb_type != hypervisor ? iommu_flushall : NULL),
index 4c5b3fc..e800ce1 100644 (file)
@@ -683,6 +683,7 @@ void do_signal32(struct pt_regs * regs)
                                regs->tpc -= 4;
                                regs->tnpc -= 4;
                                pt_regs_clear_syscall(regs);
+                               /* fall through */
                        case ERESTART_RESTARTBLOCK:
                                regs->u_regs[UREG_G1] = __NR_restart_syscall;
                                regs->tpc -= 4;
index 5665261..8395378 100644 (file)
@@ -508,6 +508,7 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
                                regs->pc -= 4;
                                regs->npc -= 4;
                                pt_regs_clear_syscall(regs);
+                               /* fall through */
                        case ERESTART_RESTARTBLOCK:
                                regs->u_regs[UREG_G1] = __NR_restart_syscall;
                                regs->pc -= 4;
index e9de180..ca70787 100644 (file)
@@ -533,6 +533,7 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
                                regs->tpc -= 4;
                                regs->tnpc -= 4;
                                pt_regs_clear_syscall(regs);
+                               /* fall through */
                        case ERESTART_RESTARTBLOCK:
                                regs->u_regs[UREG_G1] = __NR_restart_syscall;
                                regs->tpc -= 4;
index a5ff886..84cc8f7 100644 (file)
@@ -552,15 +552,14 @@ void bpf_jit_compile(struct bpf_prog *fp)
                                emit_skb_load32(hash, r_A);
                                break;
                        case BPF_ANC | SKF_AD_VLAN_TAG:
-                       case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                                emit_skb_load16(vlan_tci, r_A);
-                               if (code != (BPF_ANC | SKF_AD_VLAN_TAG)) {
-                                       emit_alu_K(SRL, 12);
+                               break;
+                       case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
+                               __emit_skb_load8(__pkt_vlan_present_offset, r_A);
+                               if (PKT_VLAN_PRESENT_BIT)
+                                       emit_alu_K(SRL, PKT_VLAN_PRESENT_BIT);
+                               if (PKT_VLAN_PRESENT_BIT < 7)
                                        emit_andi(r_A, 1, r_A);
-                               } else {
-                                       emit_loadimm(~VLAN_TAG_PRESENT, r_TMP);
-                                       emit_and(r_A, r_TMP, r_A);
-                               }
                                break;
                        case BPF_LD | BPF_W | BPF_LEN:
                                emit_skb_load32(len, r_A);
index 222785a..65428e7 100644 (file)
@@ -791,7 +791,7 @@ static int emit_compare_and_branch(const u8 code, const u8 dst, u8 src,
 }
 
 /* Just skip the save instruction and the ctx register move.  */
-#define BPF_TAILCALL_PROLOGUE_SKIP     16
+#define BPF_TAILCALL_PROLOGUE_SKIP     32
 #define BPF_TAILCALL_CNT_SP_OFF                (STACK_BIAS + 128)
 
 static void build_prologue(struct jit_ctx *ctx)
@@ -824,9 +824,15 @@ static void build_prologue(struct jit_ctx *ctx)
                const u8 vfp = bpf2sparc[BPF_REG_FP];
 
                emit(ADD | IMMED | RS1(FP) | S13(STACK_BIAS) | RD(vfp), ctx);
+       } else {
+               emit_nop(ctx);
        }
 
        emit_reg_move(I0, O0, ctx);
+       emit_reg_move(I1, O1, ctx);
+       emit_reg_move(I2, O2, ctx);
+       emit_reg_move(I3, O3, ctx);
+       emit_reg_move(I4, O4, ctx);
        /* If you add anything here, adjust BPF_TAILCALL_PROLOGUE_SKIP above. */
 }
 
@@ -1270,6 +1276,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
                const u8 tmp2 = bpf2sparc[TMP_REG_2];
                u32 opcode = 0, rs2;
 
+               if (insn->dst_reg == BPF_REG_FP)
+                       ctx->saw_frame_pointer = true;
+
                ctx->tmp_2_used = true;
                emit_loadimm(imm, tmp2, ctx);
 
@@ -1308,6 +1317,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
                const u8 tmp = bpf2sparc[TMP_REG_1];
                u32 opcode = 0, rs2;
 
+               if (insn->dst_reg == BPF_REG_FP)
+                       ctx->saw_frame_pointer = true;
+
                switch (BPF_SIZE(code)) {
                case BPF_W:
                        opcode = ST32;
@@ -1340,6 +1352,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
                const u8 tmp2 = bpf2sparc[TMP_REG_2];
                const u8 tmp3 = bpf2sparc[TMP_REG_3];
 
+               if (insn->dst_reg == BPF_REG_FP)
+                       ctx->saw_frame_pointer = true;
+
                ctx->tmp_1_used = true;
                ctx->tmp_2_used = true;
                ctx->tmp_3_used = true;
@@ -1360,6 +1375,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
                const u8 tmp2 = bpf2sparc[TMP_REG_2];
                const u8 tmp3 = bpf2sparc[TMP_REG_3];
 
+               if (insn->dst_reg == BPF_REG_FP)
+                       ctx->saw_frame_pointer = true;
+
                ctx->tmp_1_used = true;
                ctx->tmp_2_used = true;
                ctx->tmp_3_used = true;
@@ -1425,12 +1443,12 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
        struct bpf_prog *tmp, *orig_prog = prog;
        struct sparc64_jit_data *jit_data;
        struct bpf_binary_header *header;
+       u32 prev_image_size, image_size;
        bool tmp_blinded = false;
        bool extra_pass = false;
        struct jit_ctx ctx;
-       u32 image_size;
        u8 *image_ptr;
-       int pass;
+       int pass, i;
 
        if (!prog->jit_requested)
                return orig_prog;
@@ -1461,61 +1479,82 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
                header = jit_data->header;
                extra_pass = true;
                image_size = sizeof(u32) * ctx.idx;
+               prev_image_size = image_size;
+               pass = 1;
                goto skip_init_ctx;
        }
 
        memset(&ctx, 0, sizeof(ctx));
        ctx.prog = prog;
 
-       ctx.offset = kcalloc(prog->len, sizeof(unsigned int), GFP_KERNEL);
+       ctx.offset = kmalloc_array(prog->len, sizeof(unsigned int), GFP_KERNEL);
        if (ctx.offset == NULL) {
                prog = orig_prog;
                goto out_off;
        }
 
-       /* Fake pass to detect features used, and get an accurate assessment
-        * of what the final image size will be.
+       /* Longest sequence emitted is for bswap32, 12 instructions.  Pre-cook
+        * the offset array so that we converge faster.
         */
-       if (build_body(&ctx)) {
-               prog = orig_prog;
-               goto out_off;
-       }
-       build_prologue(&ctx);
-       build_epilogue(&ctx);
-
-       /* Now we know the actual image size. */
-       image_size = sizeof(u32) * ctx.idx;
-       header = bpf_jit_binary_alloc(image_size, &image_ptr,
-                                     sizeof(u32), jit_fill_hole);
-       if (header == NULL) {
-               prog = orig_prog;
-               goto out_off;
-       }
+       for (i = 0; i < prog->len; i++)
+               ctx.offset[i] = i * (12 * 4);
 
-       ctx.image = (u32 *)image_ptr;
-skip_init_ctx:
-       for (pass = 1; pass < 3; pass++) {
+       prev_image_size = ~0U;
+       for (pass = 1; pass < 40; pass++) {
                ctx.idx = 0;
 
                build_prologue(&ctx);
-
                if (build_body(&ctx)) {
-                       bpf_jit_binary_free(header);
                        prog = orig_prog;
                        goto out_off;
                }
-
                build_epilogue(&ctx);
 
                if (bpf_jit_enable > 1)
-                       pr_info("Pass %d: shrink = %d, seen = [%c%c%c%c%c%c]\n", pass,
-                               image_size - (ctx.idx * 4),
+                       pr_info("Pass %d: size = %u, seen = [%c%c%c%c%c%c]\n", pass,
+                               ctx.idx * 4,
                                ctx.tmp_1_used ? '1' : ' ',
                                ctx.tmp_2_used ? '2' : ' ',
                                ctx.tmp_3_used ? '3' : ' ',
                                ctx.saw_frame_pointer ? 'F' : ' ',
                                ctx.saw_call ? 'C' : ' ',
                                ctx.saw_tail_call ? 'T' : ' ');
+
+               if (ctx.idx * 4 == prev_image_size)
+                       break;
+               prev_image_size = ctx.idx * 4;
+               cond_resched();
+       }
+
+       /* Now we know the actual image size. */
+       image_size = sizeof(u32) * ctx.idx;
+       header = bpf_jit_binary_alloc(image_size, &image_ptr,
+                                     sizeof(u32), jit_fill_hole);
+       if (header == NULL) {
+               prog = orig_prog;
+               goto out_off;
+       }
+
+       ctx.image = (u32 *)image_ptr;
+skip_init_ctx:
+       ctx.idx = 0;
+
+       build_prologue(&ctx);
+
+       if (build_body(&ctx)) {
+               bpf_jit_binary_free(header);
+               prog = orig_prog;
+               goto out_off;
+       }
+
+       build_epilogue(&ctx);
+
+       if (ctx.idx * 4 != prev_image_size) {
+               pr_err("bpf_jit: Failed to converge, prev_size=%u size=%d\n",
+                      prev_image_size, ctx.idx * 4);
+               bpf_jit_binary_free(header);
+               prog = orig_prog;
+               goto out_off;
        }
 
        if (bpf_jit_enable > 1)
@@ -1536,6 +1575,7 @@ skip_init_ctx:
        prog->jited_len = image_size;
 
        if (!prog->is_func || extra_pass) {
+               bpf_prog_fill_jited_linfo(prog, ctx.offset);
 out_off:
                kfree(ctx.offset);
                kfree(jit_data);
index 9d734f3..8689e79 100644 (file)
@@ -444,10 +444,6 @@ config RETPOLINE
          branches. Requires a compiler with -mindirect-branch=thunk-extern
          support for full protection. The kernel may run slower.
 
-         Without compiler support, at least indirect branches in assembler
-         code are eliminated. Since this includes the syscall entry path,
-         it is not entirely pointless.
-
 config INTEL_RDT
        bool "Intel Resource Director Technology support"
        depends on X86 && CPU_SUP_INTEL
@@ -1004,13 +1000,7 @@ config NR_CPUS
          to the kernel image.
 
 config SCHED_SMT
-       bool "SMT (Hyperthreading) scheduler support"
-       depends on SMP
-       ---help---
-         SMT scheduler support improves the CPU scheduler's decision making
-         when dealing with Intel Pentium 4 chips with HyperThreading at a
-         cost of slightly increased overhead in some places. If unsure say
-         N here.
+       def_bool y if SMP
 
 config SCHED_MC
        def_bool y
index 88398fd..75ef499 100644 (file)
@@ -220,9 +220,7 @@ KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
 
 # Avoid indirect branches in kernel to deal with Spectre
 ifdef CONFIG_RETPOLINE
-ifneq ($(RETPOLINE_CFLAGS),)
-  KBUILD_CFLAGS += $(RETPOLINE_CFLAGS) -DRETPOLINE
-endif
+  KBUILD_CFLAGS += $(RETPOLINE_CFLAGS)
 endif
 
 archscripts: scripts_basic
@@ -306,6 +304,13 @@ ifndef CC_HAVE_ASM_GOTO
        @echo Compiler lacks asm-goto support.
        @exit 1
 endif
+ifdef CONFIG_RETPOLINE
+ifeq ($(RETPOLINE_CFLAGS),)
+       @echo "You are building kernel with non-retpoline compiler." >&2
+       @echo "Please update your compiler." >&2
+       @false
+endif
+endif
 
 archclean:
        $(Q)rm -rf $(objtree)/arch/i386
index 8b4c5e0..544ac4f 100644 (file)
@@ -1,3 +1,4 @@
+
 /* -----------------------------------------------------------------------
  *
  *   Copyright 2011 Intel Corporation; author Matt Fleming
@@ -634,37 +635,54 @@ static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
        return status;
 }
 
+static efi_status_t allocate_e820(struct boot_params *params,
+                                 struct setup_data **e820ext,
+                                 u32 *e820ext_size)
+{
+       unsigned long map_size, desc_size, buff_size;
+       struct efi_boot_memmap boot_map;
+       efi_memory_desc_t *map;
+       efi_status_t status;
+       __u32 nr_desc;
+
+       boot_map.map            = &map;
+       boot_map.map_size       = &map_size;
+       boot_map.desc_size      = &desc_size;
+       boot_map.desc_ver       = NULL;
+       boot_map.key_ptr        = NULL;
+       boot_map.buff_size      = &buff_size;
+
+       status = efi_get_memory_map(sys_table, &boot_map);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       nr_desc = buff_size / desc_size;
+
+       if (nr_desc > ARRAY_SIZE(params->e820_table)) {
+               u32 nr_e820ext = nr_desc - ARRAY_SIZE(params->e820_table);
+
+               status = alloc_e820ext(nr_e820ext, e820ext, e820ext_size);
+               if (status != EFI_SUCCESS)
+                       return status;
+       }
+
+       return EFI_SUCCESS;
+}
+
 struct exit_boot_struct {
        struct boot_params      *boot_params;
        struct efi_info         *efi;
-       struct setup_data       *e820ext;
-       __u32                   e820ext_size;
 };
 
 static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg,
                                   struct efi_boot_memmap *map,
                                   void *priv)
 {
-       static bool first = true;
        const char *signature;
        __u32 nr_desc;
        efi_status_t status;
        struct exit_boot_struct *p = priv;
 
-       if (first) {
-               nr_desc = *map->buff_size / *map->desc_size;
-               if (nr_desc > ARRAY_SIZE(p->boot_params->e820_table)) {
-                       u32 nr_e820ext = nr_desc -
-                                       ARRAY_SIZE(p->boot_params->e820_table);
-
-                       status = alloc_e820ext(nr_e820ext, &p->e820ext,
-                                              &p->e820ext_size);
-                       if (status != EFI_SUCCESS)
-                               return status;
-               }
-               first = false;
-       }
-
        signature = efi_is_64bit() ? EFI64_LOADER_SIGNATURE
                                   : EFI32_LOADER_SIGNATURE;
        memcpy(&p->efi->efi_loader_signature, signature, sizeof(__u32));
@@ -687,8 +705,8 @@ static efi_status_t exit_boot(struct boot_params *boot_params, void *handle)
 {
        unsigned long map_sz, key, desc_size, buff_size;
        efi_memory_desc_t *mem_map;
-       struct setup_data *e820ext;
-       __u32 e820ext_size;
+       struct setup_data *e820ext = NULL;
+       __u32 e820ext_size = 0;
        efi_status_t status;
        __u32 desc_version;
        struct efi_boot_memmap map;
@@ -702,8 +720,10 @@ static efi_status_t exit_boot(struct boot_params *boot_params, void *handle)
        map.buff_size           = &buff_size;
        priv.boot_params        = boot_params;
        priv.efi                = &boot_params->efi_info;
-       priv.e820ext            = NULL;
-       priv.e820ext_size       = 0;
+
+       status = allocate_e820(boot_params, &e820ext, &e820ext_size);
+       if (status != EFI_SUCCESS)
+               return status;
 
        /* Might as well exit boot services now */
        status = efi_exit_boot_services(sys_table, handle, &map, &priv,
@@ -711,9 +731,6 @@ static efi_status_t exit_boot(struct boot_params *boot_params, void *handle)
        if (status != EFI_SUCCESS)
                return status;
 
-       e820ext                 = priv.e820ext;
-       e820ext_size            = priv.e820ext_size;
-
        /* Historic? */
        boot_params->alt_mem_k  = 32 * 1024;
 
index 4c881c8..850b876 100644 (file)
@@ -300,7 +300,7 @@ _start:
        # Part 2 of the header, from the old setup.S
 
                .ascii  "HdrS"          # header signature
-               .word   0x020e          # header version number (>= 0x0105)
+               .word   0x020d          # header version number (>= 0x0105)
                                        # or else old loadlin-1.5 will fail)
                .globl realmode_swtch
 realmode_swtch:        .word   0, 0            # default_switch, SETUPSEG
@@ -558,10 +558,6 @@ pref_address:              .quad LOAD_PHYSICAL_ADDR        # preferred load addr
 init_size:             .long INIT_SIZE         # kernel initialization size
 handover_offset:       .long 0                 # Filled in by build.c
 
-acpi_rsdp_addr:                .quad 0                 # 64-bit physical pointer to the
-                                               # ACPI RSDP table, added with
-                                               # version 2.14
-
 # End of setup header #####################################################
 
        .section ".entrytext", "ax"
index ce25d84..1f0efdb 100644 (file)
@@ -566,6 +566,7 @@ ENTRY(interrupt_entry)
 
        ret
 END(interrupt_entry)
+_ASM_NOKPROBE(interrupt_entry)
 
 
 /* Interrupt entry/exit. */
@@ -766,6 +767,7 @@ native_irq_return_ldt:
        jmp     native_irq_return_iret
 #endif
 END(common_interrupt)
+_ASM_NOKPROBE(common_interrupt)
 
 /*
  * APIC interrupts.
@@ -780,6 +782,7 @@ ENTRY(\sym)
        call    \do_sym /* rdi points to pt_regs */
        jmp     ret_from_intr
 END(\sym)
+_ASM_NOKPROBE(\sym)
 .endm
 
 /* Make sure APIC interrupt handlers end up in the irqentry section: */
@@ -960,6 +963,7 @@ ENTRY(\sym)
 
        jmp     error_exit
        .endif
+_ASM_NOKPROBE(\sym)
 END(\sym)
 .endm
 
index 141d415..0624bf2 100644 (file)
@@ -47,7 +47,7 @@ targets += $(vdso_img_sodbg) $(vdso_img-y:%=vdso%.so)
 CPPFLAGS_vdso.lds += -P -C
 
 VDSO_LDFLAGS_vdso.lds = -m elf_x86_64 -soname linux-vdso.so.1 --no-undefined \
-                       -z max-page-size=4096 -z common-page-size=4096
+                       -z max-page-size=4096
 
 $(obj)/vdso64.so.dbg: $(obj)/vdso.lds $(vobjs) FORCE
        $(call if_changed,vdso)
@@ -98,7 +98,7 @@ CFLAGS_REMOVE_vvar.o = -pg
 
 CPPFLAGS_vdsox32.lds = $(CPPFLAGS_vdso.lds)
 VDSO_LDFLAGS_vdsox32.lds = -m elf32_x86_64 -soname linux-vdso.so.1 \
-                          -z max-page-size=4096 -z common-page-size=4096
+                          -z max-page-size=4096
 
 # x32-rebranded versions
 vobjx32s-y := $(vobjs-y:.o=-x32.o)
index 106911b..374a197 100644 (file)
@@ -438,26 +438,6 @@ int x86_setup_perfctr(struct perf_event *event)
        if (config == -1LL)
                return -EINVAL;
 
-       /*
-        * Branch tracing:
-        */
-       if (attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS &&
-           !attr->freq && hwc->sample_period == 1) {
-               /* BTS is not supported by this architecture. */
-               if (!x86_pmu.bts_active)
-                       return -EOPNOTSUPP;
-
-               /* BTS is currently only allowed for user-mode. */
-               if (!attr->exclude_kernel)
-                       return -EOPNOTSUPP;
-
-               /* disallow bts if conflicting events are present */
-               if (x86_add_exclusive(x86_lbr_exclusive_lbr))
-                       return -EBUSY;
-
-               event->destroy = hw_perf_lbr_event_destroy;
-       }
-
        hwc->config |= config;
 
        return 0;
index 273c62e..ecc3e34 100644 (file)
@@ -2306,14 +2306,18 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)
        return handled;
 }
 
-static bool disable_counter_freezing;
+static bool disable_counter_freezing = true;
 static int __init intel_perf_counter_freezing_setup(char *s)
 {
-       disable_counter_freezing = true;
-       pr_info("Intel PMU Counter freezing feature disabled\n");
+       bool res;
+
+       if (kstrtobool(s, &res))
+               return -EINVAL;
+
+       disable_counter_freezing = !res;
        return 1;
 }
-__setup("disable_counter_freezing", intel_perf_counter_freezing_setup);
+__setup("perf_v4_pmi=", intel_perf_counter_freezing_setup);
 
 /*
  * Simplified handler for Arch Perfmon v4:
@@ -2470,16 +2474,7 @@ done:
 static struct event_constraint *
 intel_bts_constraints(struct perf_event *event)
 {
-       struct hw_perf_event *hwc = &event->hw;
-       unsigned int hw_event, bts_event;
-
-       if (event->attr.freq)
-               return NULL;
-
-       hw_event = hwc->config & INTEL_ARCH_EVENT_MASK;
-       bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS);
-
-       if (unlikely(hw_event == bts_event && hwc->sample_period == 1))
+       if (unlikely(intel_pmu_has_bts(event)))
                return &bts_constraint;
 
        return NULL;
@@ -3098,10 +3093,51 @@ static unsigned long intel_pmu_large_pebs_flags(struct perf_event *event)
        return flags;
 }
 
+static int intel_pmu_bts_config(struct perf_event *event)
+{
+       struct perf_event_attr *attr = &event->attr;
+
+       if (unlikely(intel_pmu_has_bts(event))) {
+               /* BTS is not supported by this architecture. */
+               if (!x86_pmu.bts_active)
+                       return -EOPNOTSUPP;
+
+               /* BTS is currently only allowed for user-mode. */
+               if (!attr->exclude_kernel)
+                       return -EOPNOTSUPP;
+
+               /* BTS is not allowed for precise events. */
+               if (attr->precise_ip)
+                       return -EOPNOTSUPP;
+
+               /* disallow bts if conflicting events are present */
+               if (x86_add_exclusive(x86_lbr_exclusive_lbr))
+                       return -EBUSY;
+
+               event->destroy = hw_perf_lbr_event_destroy;
+       }
+
+       return 0;
+}
+
+static int core_pmu_hw_config(struct perf_event *event)
+{
+       int ret = x86_pmu_hw_config(event);
+
+       if (ret)
+               return ret;
+
+       return intel_pmu_bts_config(event);
+}
+
 static int intel_pmu_hw_config(struct perf_event *event)
 {
        int ret = x86_pmu_hw_config(event);
 
+       if (ret)
+               return ret;
+
+       ret = intel_pmu_bts_config(event);
        if (ret)
                return ret;
 
@@ -3127,7 +3163,7 @@ static int intel_pmu_hw_config(struct perf_event *event)
                /*
                 * BTS is set up earlier in this path, so don't account twice
                 */
-               if (!intel_pmu_has_bts(event)) {
+               if (!unlikely(intel_pmu_has_bts(event))) {
                        /* disallow lbr if conflicting events are present */
                        if (x86_add_exclusive(x86_lbr_exclusive_lbr))
                                return -EBUSY;
@@ -3596,7 +3632,7 @@ static __initconst const struct x86_pmu core_pmu = {
        .enable_all             = core_pmu_enable_all,
        .enable                 = core_pmu_enable_event,
        .disable                = x86_pmu_disable_event,
-       .hw_config              = x86_pmu_hw_config,
+       .hw_config              = core_pmu_hw_config,
        .schedule_events        = x86_schedule_events,
        .eventsel               = MSR_ARCH_PERFMON_EVENTSEL0,
        .perfctr                = MSR_ARCH_PERFMON_PERFCTR0,
index e17ab88..cb46d60 100644 (file)
@@ -129,8 +129,15 @@ struct intel_uncore_box {
        struct intel_uncore_extra_reg shared_regs[0];
 };
 
-#define UNCORE_BOX_FLAG_INITIATED      0
-#define UNCORE_BOX_FLAG_CTL_OFFS8      1 /* event config registers are 8-byte apart */
+/* CFL uncore 8th cbox MSRs */
+#define CFL_UNC_CBO_7_PERFEVTSEL0              0xf70
+#define CFL_UNC_CBO_7_PER_CTR0                 0xf76
+
+#define UNCORE_BOX_FLAG_INITIATED              0
+/* event config registers are 8-byte apart */
+#define UNCORE_BOX_FLAG_CTL_OFFS8              1
+/* CFL 8th CBOX has different MSR space */
+#define UNCORE_BOX_FLAG_CFL8_CBOX_MSR_OFFS     2
 
 struct uncore_event_desc {
        struct kobj_attribute attr;
@@ -297,17 +304,27 @@ unsigned int uncore_freerunning_counter(struct intel_uncore_box *box,
 static inline
 unsigned uncore_msr_event_ctl(struct intel_uncore_box *box, int idx)
 {
-       return box->pmu->type->event_ctl +
-               (box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) +
-               uncore_msr_box_offset(box);
+       if (test_bit(UNCORE_BOX_FLAG_CFL8_CBOX_MSR_OFFS, &box->flags)) {
+               return CFL_UNC_CBO_7_PERFEVTSEL0 +
+                      (box->pmu->type->pair_ctr_ctl ? 2 * idx : idx);
+       } else {
+               return box->pmu->type->event_ctl +
+                      (box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) +
+                      uncore_msr_box_offset(box);
+       }
 }
 
 static inline
 unsigned uncore_msr_perf_ctr(struct intel_uncore_box *box, int idx)
 {
-       return box->pmu->type->perf_ctr +
-               (box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) +
-               uncore_msr_box_offset(box);
+       if (test_bit(UNCORE_BOX_FLAG_CFL8_CBOX_MSR_OFFS, &box->flags)) {
+               return CFL_UNC_CBO_7_PER_CTR0 +
+                      (box->pmu->type->pair_ctr_ctl ? 2 * idx : idx);
+       } else {
+               return box->pmu->type->perf_ctr +
+                      (box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) +
+                      uncore_msr_box_offset(box);
+       }
 }
 
 static inline
index 8527c3e..2593b0d 100644 (file)
 #define PCI_DEVICE_ID_INTEL_SKL_HQ_IMC 0x1910
 #define PCI_DEVICE_ID_INTEL_SKL_SD_IMC 0x190f
 #define PCI_DEVICE_ID_INTEL_SKL_SQ_IMC 0x191f
+#define PCI_DEVICE_ID_INTEL_KBL_Y_IMC  0x590c
+#define PCI_DEVICE_ID_INTEL_KBL_U_IMC  0x5904
+#define PCI_DEVICE_ID_INTEL_KBL_UQ_IMC 0x5914
+#define PCI_DEVICE_ID_INTEL_KBL_SD_IMC 0x590f
+#define PCI_DEVICE_ID_INTEL_KBL_SQ_IMC 0x591f
+#define PCI_DEVICE_ID_INTEL_CFL_2U_IMC 0x3ecc
+#define PCI_DEVICE_ID_INTEL_CFL_4U_IMC 0x3ed0
+#define PCI_DEVICE_ID_INTEL_CFL_4H_IMC 0x3e10
+#define PCI_DEVICE_ID_INTEL_CFL_6H_IMC 0x3ec4
+#define PCI_DEVICE_ID_INTEL_CFL_2S_D_IMC       0x3e0f
+#define PCI_DEVICE_ID_INTEL_CFL_4S_D_IMC       0x3e1f
+#define PCI_DEVICE_ID_INTEL_CFL_6S_D_IMC       0x3ec2
+#define PCI_DEVICE_ID_INTEL_CFL_8S_D_IMC       0x3e30
+#define PCI_DEVICE_ID_INTEL_CFL_4S_W_IMC       0x3e18
+#define PCI_DEVICE_ID_INTEL_CFL_6S_W_IMC       0x3ec6
+#define PCI_DEVICE_ID_INTEL_CFL_8S_W_IMC       0x3e31
+#define PCI_DEVICE_ID_INTEL_CFL_4S_S_IMC       0x3e33
+#define PCI_DEVICE_ID_INTEL_CFL_6S_S_IMC       0x3eca
+#define PCI_DEVICE_ID_INTEL_CFL_8S_S_IMC       0x3e32
 
 /* SNB event control */
 #define SNB_UNC_CTL_EV_SEL_MASK                        0x000000ff
@@ -202,6 +221,10 @@ static void skl_uncore_msr_init_box(struct intel_uncore_box *box)
                wrmsrl(SKL_UNC_PERF_GLOBAL_CTL,
                        SNB_UNC_GLOBAL_CTL_EN | SKL_UNC_GLOBAL_CTL_CORE_ALL);
        }
+
+       /* The 8th CBOX has different MSR space */
+       if (box->pmu->pmu_idx == 7)
+               __set_bit(UNCORE_BOX_FLAG_CFL8_CBOX_MSR_OFFS, &box->flags);
 }
 
 static void skl_uncore_msr_enable_box(struct intel_uncore_box *box)
@@ -228,7 +251,7 @@ static struct intel_uncore_ops skl_uncore_msr_ops = {
 static struct intel_uncore_type skl_uncore_cbox = {
        .name           = "cbox",
        .num_counters   = 4,
-       .num_boxes      = 5,
+       .num_boxes      = 8,
        .perf_ctr_bits  = 44,
        .fixed_ctr_bits = 48,
        .perf_ctr       = SNB_UNC_CBO_0_PER_CTR0,
@@ -569,7 +592,82 @@ static const struct pci_device_id skl_uncore_pci_ids[] = {
                PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SKL_SQ_IMC),
                .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
        },
-
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBL_Y_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBL_U_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBL_UQ_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBL_SD_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBL_SQ_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_2U_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_4U_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_4H_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_6H_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_2S_D_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_4S_D_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_6S_D_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_8S_D_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_4S_W_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_6S_W_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_8S_W_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_4S_S_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_6S_S_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CFL_8S_S_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
        { /* end: all zeroes */ },
 };
 
@@ -618,6 +716,25 @@ static const struct imc_uncore_pci_dev desktop_imc_pci_ids[] = {
        IMC_DEV(SKL_HQ_IMC, &skl_uncore_pci_driver),  /* 6th Gen Core H Quad Core */
        IMC_DEV(SKL_SD_IMC, &skl_uncore_pci_driver),  /* 6th Gen Core S Dual Core */
        IMC_DEV(SKL_SQ_IMC, &skl_uncore_pci_driver),  /* 6th Gen Core S Quad Core */
+       IMC_DEV(KBL_Y_IMC, &skl_uncore_pci_driver),  /* 7th Gen Core Y */
+       IMC_DEV(KBL_U_IMC, &skl_uncore_pci_driver),  /* 7th Gen Core U */
+       IMC_DEV(KBL_UQ_IMC, &skl_uncore_pci_driver),  /* 7th Gen Core U Quad Core */
+       IMC_DEV(KBL_SD_IMC, &skl_uncore_pci_driver),  /* 7th Gen Core S Dual Core */
+       IMC_DEV(KBL_SQ_IMC, &skl_uncore_pci_driver),  /* 7th Gen Core S Quad Core */
+       IMC_DEV(CFL_2U_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core U 2 Cores */
+       IMC_DEV(CFL_4U_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core U 4 Cores */
+       IMC_DEV(CFL_4H_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core H 4 Cores */
+       IMC_DEV(CFL_6H_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core H 6 Cores */
+       IMC_DEV(CFL_2S_D_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core S 2 Cores Desktop */
+       IMC_DEV(CFL_4S_D_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core S 4 Cores Desktop */
+       IMC_DEV(CFL_6S_D_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core S 6 Cores Desktop */
+       IMC_DEV(CFL_8S_D_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core S 8 Cores Desktop */
+       IMC_DEV(CFL_4S_W_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core S 4 Cores Work Station */
+       IMC_DEV(CFL_6S_W_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core S 6 Cores Work Station */
+       IMC_DEV(CFL_8S_W_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core S 8 Cores Work Station */
+       IMC_DEV(CFL_4S_S_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core S 4 Cores Server */
+       IMC_DEV(CFL_6S_S_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core S 6 Cores Server */
+       IMC_DEV(CFL_8S_S_IMC, &skl_uncore_pci_driver),  /* 8th Gen Core S 8 Cores Server */
        {  /* end marker */ }
 };
 
index adae087..78d7b70 100644 (file)
@@ -859,11 +859,16 @@ static inline int amd_pmu_init(void)
 
 static inline bool intel_pmu_has_bts(struct perf_event *event)
 {
-       if (event->attr.config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS &&
-           !event->attr.freq && event->hw.sample_period == 1)
-               return true;
+       struct hw_perf_event *hwc = &event->hw;
+       unsigned int hw_event, bts_event;
+
+       if (event->attr.freq)
+               return false;
+
+       hw_event = hwc->config & INTEL_ARCH_EVENT_MASK;
+       bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS);
 
-       return false;
+       return hw_event == bts_event && hwc->sample_period == 1;
 }
 
 int intel_pmu_save_and_restart(struct perf_event *event);
index a07ffd2..f6f6ef4 100644 (file)
@@ -36,6 +36,7 @@ static void sanitize_boot_params(struct boot_params *boot_params)
         */
        if (boot_params->sentinel) {
                /* fields in boot_params are left uninitialized, clear them */
+               boot_params->acpi_rsdp_addr = 0;
                memset(&boot_params->ext_ramdisk_image, 0,
                       (char *)&boot_params->efi_info -
                        (char *)&boot_params->ext_ramdisk_image);
index 5f7290e..69dcdf1 100644 (file)
@@ -226,7 +226,7 @@ static inline void copy_fxregs_to_kernel(struct fpu *fpu)
                     "3: movl $-2,%[err]\n\t"                           \
                     "jmp 2b\n\t"                                       \
                     ".popsection\n\t"                                  \
-                    _ASM_EXTABLE_UA(1b, 3b)                            \
+                    _ASM_EXTABLE(1b, 3b)                               \
                     : [err] "=r" (err)                                 \
                     : "D" (st), "m" (*st), "a" (lmask), "d" (hmask)    \
                     : "memory")
index 55e51ff..fbda5a9 100644 (file)
@@ -1094,7 +1094,8 @@ struct kvm_x86_ops {
        bool (*has_wbinvd_exit)(void);
 
        u64 (*read_l1_tsc_offset)(struct kvm_vcpu *vcpu);
-       void (*write_tsc_offset)(struct kvm_vcpu *vcpu, u64 offset);
+       /* Returns actual tsc_offset set in active VMCS */
+       u64 (*write_l1_tsc_offset)(struct kvm_vcpu *vcpu, u64 offset);
 
        void (*get_exit_info)(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2);
 
index 80f4a4f..9e39cc8 100644 (file)
 
 #define MSR_IA32_SPEC_CTRL             0x00000048 /* Speculation Control */
 #define SPEC_CTRL_IBRS                 (1 << 0)   /* Indirect Branch Restricted Speculation */
-#define SPEC_CTRL_STIBP                        (1 << 1)   /* Single Thread Indirect Branch Predictors */
+#define SPEC_CTRL_STIBP_SHIFT          1          /* Single Thread Indirect Branch Predictor (STIBP) bit */
+#define SPEC_CTRL_STIBP                        (1 << SPEC_CTRL_STIBP_SHIFT)    /* STIBP mask */
 #define SPEC_CTRL_SSBD_SHIFT           2          /* Speculative Store Bypass Disable bit */
-#define SPEC_CTRL_SSBD                 (1 << SPEC_CTRL_SSBD_SHIFT)   /* Speculative Store Bypass Disable */
+#define SPEC_CTRL_SSBD                 (1 << SPEC_CTRL_SSBD_SHIFT)     /* Speculative Store Bypass Disable */
 
 #define MSR_IA32_PRED_CMD              0x00000049 /* Prediction Command */
 #define PRED_CMD_IBPB                  (1 << 0)   /* Indirect Branch Prediction Barrier */
 #define MSR_F15H_NB_PERF_CTR           0xc0010241
 #define MSR_F15H_PTSC                  0xc0010280
 #define MSR_F15H_IC_CFG                        0xc0011021
+#define MSR_F15H_EX_CFG                        0xc001102c
 
 /* Fam 10h MSRs */
 #define MSR_FAM10H_MMIO_CONF_BASE      0xc0010058
index 80dc144..032b600 100644 (file)
@@ -3,6 +3,8 @@
 #ifndef _ASM_X86_NOSPEC_BRANCH_H_
 #define _ASM_X86_NOSPEC_BRANCH_H_
 
+#include <linux/static_key.h>
+
 #include <asm/alternative.h>
 #include <asm/alternative-asm.h>
 #include <asm/cpufeatures.h>
        _ASM_PTR " 999b\n\t"                                    \
        ".popsection\n\t"
 
-#if defined(CONFIG_X86_64) && defined(RETPOLINE)
+#ifdef CONFIG_RETPOLINE
+#ifdef CONFIG_X86_64
 
 /*
- * Since the inline asm uses the %V modifier which is only in newer GCC,
- * the 64-bit one is dependent on RETPOLINE not CONFIG_RETPOLINE.
+ * Inline asm uses the %V modifier which is only in newer GCC
+ * which is ensured when CONFIG_RETPOLINE is defined.
  */
 # define CALL_NOSPEC                                           \
        ANNOTATE_NOSPEC_ALTERNATIVE                             \
        X86_FEATURE_RETPOLINE_AMD)
 # define THUNK_TARGET(addr) [thunk_target] "r" (addr)
 
-#elif defined(CONFIG_X86_32) && defined(CONFIG_RETPOLINE)
+#else /* CONFIG_X86_32 */
 /*
  * For i386 we use the original ret-equivalent retpoline, because
  * otherwise we'll run out of registers. We don't care about CET
        X86_FEATURE_RETPOLINE_AMD)
 
 # define THUNK_TARGET(addr) [thunk_target] "rm" (addr)
+#endif
 #else /* No retpoline for C / inline asm */
 # define CALL_NOSPEC "call *%[thunk_target]\n"
 # define THUNK_TARGET(addr) [thunk_target] "rm" (addr)
 /* The Spectre V2 mitigation variants */
 enum spectre_v2_mitigation {
        SPECTRE_V2_NONE,
-       SPECTRE_V2_RETPOLINE_MINIMAL,
-       SPECTRE_V2_RETPOLINE_MINIMAL_AMD,
        SPECTRE_V2_RETPOLINE_GENERIC,
        SPECTRE_V2_RETPOLINE_AMD,
        SPECTRE_V2_IBRS_ENHANCED,
 };
 
+/* The indirect branch speculation control variants */
+enum spectre_v2_user_mitigation {
+       SPECTRE_V2_USER_NONE,
+       SPECTRE_V2_USER_STRICT,
+       SPECTRE_V2_USER_PRCTL,
+       SPECTRE_V2_USER_SECCOMP,
+};
+
 /* The Speculative Store Bypass disable variants */
 enum ssb_mitigation {
        SPEC_STORE_BYPASS_NONE,
@@ -303,6 +313,10 @@ do {                                                                       \
        preempt_enable();                                               \
 } while (0)
 
+DECLARE_STATIC_KEY_FALSE(switch_to_cond_stibp);
+DECLARE_STATIC_KEY_FALSE(switch_mm_cond_ibpb);
+DECLARE_STATIC_KEY_FALSE(switch_mm_always_ibpb);
+
 #endif /* __ASSEMBLY__ */
 
 /*
index ae7c2c5..5393bab 100644 (file)
@@ -53,12 +53,24 @@ static inline u64 ssbd_tif_to_spec_ctrl(u64 tifn)
        return (tifn & _TIF_SSBD) >> (TIF_SSBD - SPEC_CTRL_SSBD_SHIFT);
 }
 
+static inline u64 stibp_tif_to_spec_ctrl(u64 tifn)
+{
+       BUILD_BUG_ON(TIF_SPEC_IB < SPEC_CTRL_STIBP_SHIFT);
+       return (tifn & _TIF_SPEC_IB) >> (TIF_SPEC_IB - SPEC_CTRL_STIBP_SHIFT);
+}
+
 static inline unsigned long ssbd_spec_ctrl_to_tif(u64 spec_ctrl)
 {
        BUILD_BUG_ON(TIF_SSBD < SPEC_CTRL_SSBD_SHIFT);
        return (spec_ctrl & SPEC_CTRL_SSBD) << (TIF_SSBD - SPEC_CTRL_SSBD_SHIFT);
 }
 
+static inline unsigned long stibp_spec_ctrl_to_tif(u64 spec_ctrl)
+{
+       BUILD_BUG_ON(TIF_SPEC_IB < SPEC_CTRL_STIBP_SHIFT);
+       return (spec_ctrl & SPEC_CTRL_STIBP) << (TIF_SPEC_IB - SPEC_CTRL_STIBP_SHIFT);
+}
+
 static inline u64 ssbd_tif_to_amd_ls_cfg(u64 tifn)
 {
        return (tifn & _TIF_SSBD) ? x86_amd_ls_cfg_ssbd_mask : 0ULL;
@@ -70,11 +82,7 @@ extern void speculative_store_bypass_ht_init(void);
 static inline void speculative_store_bypass_ht_init(void) { }
 #endif
 
-extern void speculative_store_bypass_update(unsigned long tif);
-
-static inline void speculative_store_bypass_update_current(void)
-{
-       speculative_store_bypass_update(current_thread_info()->flags);
-}
+extern void speculation_ctrl_update(unsigned long tif);
+extern void speculation_ctrl_update_current(void);
 
 #endif
index 36bd243..7cf1a27 100644 (file)
@@ -11,9 +11,6 @@ struct task_struct *__switch_to_asm(struct task_struct *prev,
 
 __visible struct task_struct *__switch_to(struct task_struct *prev,
                                          struct task_struct *next);
-struct tss_struct;
-void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
-                     struct tss_struct *tss);
 
 /* This runs runs on the previous thread's stack. */
 static inline void prepare_switch_to(struct task_struct *next)
index 2ff2a30..82b73b7 100644 (file)
@@ -79,10 +79,12 @@ struct thread_info {
 #define TIF_SIGPENDING         2       /* signal pending */
 #define TIF_NEED_RESCHED       3       /* rescheduling necessary */
 #define TIF_SINGLESTEP         4       /* reenable singlestep on user return*/
-#define TIF_SSBD                       5       /* Reduced data speculation */
+#define TIF_SSBD               5       /* Speculative store bypass disable */
 #define TIF_SYSCALL_EMU                6       /* syscall emulation active */
 #define TIF_SYSCALL_AUDIT      7       /* syscall auditing active */
 #define TIF_SECCOMP            8       /* secure computing */
+#define TIF_SPEC_IB            9       /* Indirect branch speculation mitigation */
+#define TIF_SPEC_FORCE_UPDATE  10      /* Force speculation MSR update in context switch */
 #define TIF_USER_RETURN_NOTIFY 11      /* notify kernel of userspace return */
 #define TIF_UPROBE             12      /* breakpointed or singlestepping */
 #define TIF_PATCH_PENDING      13      /* pending live patching update */
@@ -110,6 +112,8 @@ struct thread_info {
 #define _TIF_SYSCALL_EMU       (1 << TIF_SYSCALL_EMU)
 #define _TIF_SYSCALL_AUDIT     (1 << TIF_SYSCALL_AUDIT)
 #define _TIF_SECCOMP           (1 << TIF_SECCOMP)
+#define _TIF_SPEC_IB           (1 << TIF_SPEC_IB)
+#define _TIF_SPEC_FORCE_UPDATE (1 << TIF_SPEC_FORCE_UPDATE)
 #define _TIF_USER_RETURN_NOTIFY        (1 << TIF_USER_RETURN_NOTIFY)
 #define _TIF_UPROBE            (1 << TIF_UPROBE)
 #define _TIF_PATCH_PENDING     (1 << TIF_PATCH_PENDING)
@@ -145,8 +149,18 @@ struct thread_info {
         _TIF_FSCHECK)
 
 /* flags to check in __switch_to() */
-#define _TIF_WORK_CTXSW                                                        \
-       (_TIF_IO_BITMAP|_TIF_NOCPUID|_TIF_NOTSC|_TIF_BLOCKSTEP|_TIF_SSBD)
+#define _TIF_WORK_CTXSW_BASE                                           \
+       (_TIF_IO_BITMAP|_TIF_NOCPUID|_TIF_NOTSC|_TIF_BLOCKSTEP|         \
+        _TIF_SSBD | _TIF_SPEC_FORCE_UPDATE)
+
+/*
+ * Avoid calls to __switch_to_xtra() on UP as STIBP is not evaluated.
+ */
+#ifdef CONFIG_SMP
+# define _TIF_WORK_CTXSW       (_TIF_WORK_CTXSW_BASE | _TIF_SPEC_IB)
+#else
+# define _TIF_WORK_CTXSW       (_TIF_WORK_CTXSW_BASE)
+#endif
 
 #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY)
 #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW)
index d760611..f4204bf 100644 (file)
@@ -169,10 +169,14 @@ struct tlb_state {
 
 #define LOADED_MM_SWITCHING ((struct mm_struct *)1)
 
+       /* Last user mm for optimizing IBPB */
+       union {
+               struct mm_struct        *last_user_mm;
+               unsigned long           last_user_mm_ibpb;
+       };
+
        u16 loaded_mm_asid;
        u16 next_asid;
-       /* last user mm's ctx id */
-       u64 last_ctx_id;
 
        /*
         * We can be in one of several states:
index 0f84210..b85a7c5 100644 (file)
@@ -303,6 +303,4 @@ extern void x86_init_noop(void);
 extern void x86_init_uint_noop(unsigned int unused);
 extern bool x86_pnpbios_disabled(void);
 
-void x86_verify_bootdata_version(void);
-
 #endif
index 22f89d0..60733f1 100644 (file)
@@ -16,9 +16,6 @@
 #define RAMDISK_PROMPT_FLAG            0x8000
 #define RAMDISK_LOAD_FLAG              0x4000
 
-/* version flags */
-#define VERSION_WRITTEN        0x8000
-
 /* loadflags */
 #define LOADED_HIGH    (1<<0)
 #define KASLR_FLAG     (1<<1)
@@ -89,7 +86,6 @@ struct setup_header {
        __u64   pref_address;
        __u32   init_size;
        __u32   handover_offset;
-       __u64   acpi_rsdp_addr;
 } __attribute__((packed));
 
 struct sys_desc_table {
@@ -159,7 +155,8 @@ struct boot_params {
        __u8  _pad2[4];                                 /* 0x054 */
        __u64  tboot_addr;                              /* 0x058 */
        struct ist_info ist_info;                       /* 0x060 */
-       __u8  _pad3[16];                                /* 0x070 */
+       __u64 acpi_rsdp_addr;                           /* 0x070 */
+       __u8  _pad3[8];                                 /* 0x078 */
        __u8  hd0_info[16];     /* obsolete! */         /* 0x080 */
        __u8  hd1_info[16];     /* obsolete! */         /* 0x090 */
        struct sys_desc_table sys_desc_table; /* obsolete! */   /* 0x0a0 */
index 92c76bf..06635fb 100644 (file)
@@ -1776,5 +1776,5 @@ void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
 
 u64 x86_default_get_root_pointer(void)
 {
-       return boot_params.hdr.acpi_rsdp_addr;
+       return boot_params.acpi_rsdp_addr;
 }
index c37e66e..500278f 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/nospec.h>
 #include <linux/prctl.h>
+#include <linux/sched/smt.h>
 
 #include <asm/spec-ctrl.h>
 #include <asm/cmdline.h>
@@ -53,6 +54,13 @@ static u64 __ro_after_init x86_spec_ctrl_mask = SPEC_CTRL_IBRS;
 u64 __ro_after_init x86_amd_ls_cfg_base;
 u64 __ro_after_init x86_amd_ls_cfg_ssbd_mask;
 
+/* Control conditional STIPB in switch_to() */
+DEFINE_STATIC_KEY_FALSE(switch_to_cond_stibp);
+/* Control conditional IBPB in switch_mm() */
+DEFINE_STATIC_KEY_FALSE(switch_mm_cond_ibpb);
+/* Control unconditional IBPB in switch_mm() */
+DEFINE_STATIC_KEY_FALSE(switch_mm_always_ibpb);
+
 void __init check_bugs(void)
 {
        identify_boot_cpu();
@@ -123,31 +131,6 @@ void __init check_bugs(void)
 #endif
 }
 
-/* The kernel command line selection */
-enum spectre_v2_mitigation_cmd {
-       SPECTRE_V2_CMD_NONE,
-       SPECTRE_V2_CMD_AUTO,
-       SPECTRE_V2_CMD_FORCE,
-       SPECTRE_V2_CMD_RETPOLINE,
-       SPECTRE_V2_CMD_RETPOLINE_GENERIC,
-       SPECTRE_V2_CMD_RETPOLINE_AMD,
-};
-
-static const char *spectre_v2_strings[] = {
-       [SPECTRE_V2_NONE]                       = "Vulnerable",
-       [SPECTRE_V2_RETPOLINE_MINIMAL]          = "Vulnerable: Minimal generic ASM retpoline",
-       [SPECTRE_V2_RETPOLINE_MINIMAL_AMD]      = "Vulnerable: Minimal AMD ASM retpoline",
-       [SPECTRE_V2_RETPOLINE_GENERIC]          = "Mitigation: Full generic retpoline",
-       [SPECTRE_V2_RETPOLINE_AMD]              = "Mitigation: Full AMD retpoline",
-       [SPECTRE_V2_IBRS_ENHANCED]              = "Mitigation: Enhanced IBRS",
-};
-
-#undef pr_fmt
-#define pr_fmt(fmt)     "Spectre V2 : " fmt
-
-static enum spectre_v2_mitigation spectre_v2_enabled __ro_after_init =
-       SPECTRE_V2_NONE;
-
 void
 x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool setguest)
 {
@@ -169,6 +152,10 @@ x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool setguest)
                    static_cpu_has(X86_FEATURE_AMD_SSBD))
                        hostval |= ssbd_tif_to_spec_ctrl(ti->flags);
 
+               /* Conditional STIBP enabled? */
+               if (static_branch_unlikely(&switch_to_cond_stibp))
+                       hostval |= stibp_tif_to_spec_ctrl(ti->flags);
+
                if (hostval != guestval) {
                        msrval = setguest ? guestval : hostval;
                        wrmsrl(MSR_IA32_SPEC_CTRL, msrval);
@@ -202,7 +189,7 @@ x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool setguest)
                tif = setguest ? ssbd_spec_ctrl_to_tif(guestval) :
                                 ssbd_spec_ctrl_to_tif(hostval);
 
-               speculative_store_bypass_update(tif);
+               speculation_ctrl_update(tif);
        }
 }
 EXPORT_SYMBOL_GPL(x86_virt_spec_ctrl);
@@ -217,6 +204,15 @@ static void x86_amd_ssb_disable(void)
                wrmsrl(MSR_AMD64_LS_CFG, msrval);
 }
 
+#undef pr_fmt
+#define pr_fmt(fmt)     "Spectre V2 : " fmt
+
+static enum spectre_v2_mitigation spectre_v2_enabled __ro_after_init =
+       SPECTRE_V2_NONE;
+
+static enum spectre_v2_user_mitigation spectre_v2_user __ro_after_init =
+       SPECTRE_V2_USER_NONE;
+
 #ifdef RETPOLINE
 static bool spectre_v2_bad_module;
 
@@ -238,67 +234,217 @@ static inline const char *spectre_v2_module_string(void)
 static inline const char *spectre_v2_module_string(void) { return ""; }
 #endif
 
-static void __init spec2_print_if_insecure(const char *reason)
+static inline bool match_option(const char *arg, int arglen, const char *opt)
 {
-       if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
-               pr_info("%s selected on command line.\n", reason);
+       int len = strlen(opt);
+
+       return len == arglen && !strncmp(arg, opt, len);
 }
 
-static void __init spec2_print_if_secure(const char *reason)
+/* The kernel command line selection for spectre v2 */
+enum spectre_v2_mitigation_cmd {
+       SPECTRE_V2_CMD_NONE,
+       SPECTRE_V2_CMD_AUTO,
+       SPECTRE_V2_CMD_FORCE,
+       SPECTRE_V2_CMD_RETPOLINE,
+       SPECTRE_V2_CMD_RETPOLINE_GENERIC,
+       SPECTRE_V2_CMD_RETPOLINE_AMD,
+};
+
+enum spectre_v2_user_cmd {
+       SPECTRE_V2_USER_CMD_NONE,
+       SPECTRE_V2_USER_CMD_AUTO,
+       SPECTRE_V2_USER_CMD_FORCE,
+       SPECTRE_V2_USER_CMD_PRCTL,
+       SPECTRE_V2_USER_CMD_PRCTL_IBPB,
+       SPECTRE_V2_USER_CMD_SECCOMP,
+       SPECTRE_V2_USER_CMD_SECCOMP_IBPB,
+};
+
+static const char * const spectre_v2_user_strings[] = {
+       [SPECTRE_V2_USER_NONE]          = "User space: Vulnerable",
+       [SPECTRE_V2_USER_STRICT]        = "User space: Mitigation: STIBP protection",
+       [SPECTRE_V2_USER_PRCTL]         = "User space: Mitigation: STIBP via prctl",
+       [SPECTRE_V2_USER_SECCOMP]       = "User space: Mitigation: STIBP via seccomp and prctl",
+};
+
+static const struct {
+       const char                      *option;
+       enum spectre_v2_user_cmd        cmd;
+       bool                            secure;
+} v2_user_options[] __initdata = {
+       { "auto",               SPECTRE_V2_USER_CMD_AUTO,               false },
+       { "off",                SPECTRE_V2_USER_CMD_NONE,               false },
+       { "on",                 SPECTRE_V2_USER_CMD_FORCE,              true  },
+       { "prctl",              SPECTRE_V2_USER_CMD_PRCTL,              false },
+       { "prctl,ibpb",         SPECTRE_V2_USER_CMD_PRCTL_IBPB,         false },
+       { "seccomp",            SPECTRE_V2_USER_CMD_SECCOMP,            false },
+       { "seccomp,ibpb",       SPECTRE_V2_USER_CMD_SECCOMP_IBPB,       false },
+};
+
+static void __init spec_v2_user_print_cond(const char *reason, bool secure)
 {
-       if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
-               pr_info("%s selected on command line.\n", reason);
+       if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2) != secure)
+               pr_info("spectre_v2_user=%s forced on command line.\n", reason);
 }
 
-static inline bool retp_compiler(void)
+static enum spectre_v2_user_cmd __init
+spectre_v2_parse_user_cmdline(enum spectre_v2_mitigation_cmd v2_cmd)
 {
-       return __is_defined(RETPOLINE);
+       char arg[20];
+       int ret, i;
+
+       switch (v2_cmd) {
+       case SPECTRE_V2_CMD_NONE:
+               return SPECTRE_V2_USER_CMD_NONE;
+       case SPECTRE_V2_CMD_FORCE:
+               return SPECTRE_V2_USER_CMD_FORCE;
+       default:
+               break;
+       }
+
+       ret = cmdline_find_option(boot_command_line, "spectre_v2_user",
+                                 arg, sizeof(arg));
+       if (ret < 0)
+               return SPECTRE_V2_USER_CMD_AUTO;
+
+       for (i = 0; i < ARRAY_SIZE(v2_user_options); i++) {
+               if (match_option(arg, ret, v2_user_options[i].option)) {
+                       spec_v2_user_print_cond(v2_user_options[i].option,
+                                               v2_user_options[i].secure);
+                       return v2_user_options[i].cmd;
+               }
+       }
+
+       pr_err("Unknown user space protection option (%s). Switching to AUTO select\n", arg);
+       return SPECTRE_V2_USER_CMD_AUTO;
 }
 
-static inline bool match_option(const char *arg, int arglen, const char *opt)
+static void __init
+spectre_v2_user_select_mitigation(enum spectre_v2_mitigation_cmd v2_cmd)
 {
-       int len = strlen(opt);
+       enum spectre_v2_user_mitigation mode = SPECTRE_V2_USER_NONE;
+       bool smt_possible = IS_ENABLED(CONFIG_SMP);
+       enum spectre_v2_user_cmd cmd;
 
-       return len == arglen && !strncmp(arg, opt, len);
+       if (!boot_cpu_has(X86_FEATURE_IBPB) && !boot_cpu_has(X86_FEATURE_STIBP))
+               return;
+
+       if (cpu_smt_control == CPU_SMT_FORCE_DISABLED ||
+           cpu_smt_control == CPU_SMT_NOT_SUPPORTED)
+               smt_possible = false;
+
+       cmd = spectre_v2_parse_user_cmdline(v2_cmd);
+       switch (cmd) {
+       case SPECTRE_V2_USER_CMD_NONE:
+               goto set_mode;
+       case SPECTRE_V2_USER_CMD_FORCE:
+               mode = SPECTRE_V2_USER_STRICT;
+               break;
+       case SPECTRE_V2_USER_CMD_PRCTL:
+       case SPECTRE_V2_USER_CMD_PRCTL_IBPB:
+               mode = SPECTRE_V2_USER_PRCTL;
+               break;
+       case SPECTRE_V2_USER_CMD_AUTO:
+       case SPECTRE_V2_USER_CMD_SECCOMP:
+       case SPECTRE_V2_USER_CMD_SECCOMP_IBPB:
+               if (IS_ENABLED(CONFIG_SECCOMP))
+                       mode = SPECTRE_V2_USER_SECCOMP;
+               else
+                       mode = SPECTRE_V2_USER_PRCTL;
+               break;
+       }
+
+       /* Initialize Indirect Branch Prediction Barrier */
+       if (boot_cpu_has(X86_FEATURE_IBPB)) {
+               setup_force_cpu_cap(X86_FEATURE_USE_IBPB);
+
+               switch (cmd) {
+               case SPECTRE_V2_USER_CMD_FORCE:
+               case SPECTRE_V2_USER_CMD_PRCTL_IBPB:
+               case SPECTRE_V2_USER_CMD_SECCOMP_IBPB:
+                       static_branch_enable(&switch_mm_always_ibpb);
+                       break;
+               case SPECTRE_V2_USER_CMD_PRCTL:
+               case SPECTRE_V2_USER_CMD_AUTO:
+               case SPECTRE_V2_USER_CMD_SECCOMP:
+                       static_branch_enable(&switch_mm_cond_ibpb);
+                       break;
+               default:
+                       break;
+               }
+
+               pr_info("mitigation: Enabling %s Indirect Branch Prediction Barrier\n",
+                       static_key_enabled(&switch_mm_always_ibpb) ?
+                       "always-on" : "conditional");
+       }
+
+       /* If enhanced IBRS is enabled no STIPB required */
+       if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED)
+               return;
+
+       /*
+        * If SMT is not possible or STIBP is not available clear the STIPB
+        * mode.
+        */
+       if (!smt_possible || !boot_cpu_has(X86_FEATURE_STIBP))
+               mode = SPECTRE_V2_USER_NONE;
+set_mode:
+       spectre_v2_user = mode;
+       /* Only print the STIBP mode when SMT possible */
+       if (smt_possible)
+               pr_info("%s\n", spectre_v2_user_strings[mode]);
 }
 
+static const char * const spectre_v2_strings[] = {
+       [SPECTRE_V2_NONE]                       = "Vulnerable",
+       [SPECTRE_V2_RETPOLINE_GENERIC]          = "Mitigation: Full generic retpoline",
+       [SPECTRE_V2_RETPOLINE_AMD]              = "Mitigation: Full AMD retpoline",
+       [SPECTRE_V2_IBRS_ENHANCED]              = "Mitigation: Enhanced IBRS",
+};
+
 static const struct {
        const char *option;
        enum spectre_v2_mitigation_cmd cmd;
        bool secure;
-} mitigation_options[] = {
-       { "off",               SPECTRE_V2_CMD_NONE,              false },
-       { "on",                SPECTRE_V2_CMD_FORCE,             true },
-       { "retpoline",         SPECTRE_V2_CMD_RETPOLINE,         false },
-       { "retpoline,amd",     SPECTRE_V2_CMD_RETPOLINE_AMD,     false },
-       { "retpoline,generic", SPECTRE_V2_CMD_RETPOLINE_GENERIC, false },
-       { "auto",              SPECTRE_V2_CMD_AUTO,              false },
+} mitigation_options[] __initdata = {
+       { "off",                SPECTRE_V2_CMD_NONE,              false },
+       { "on",                 SPECTRE_V2_CMD_FORCE,             true  },
+       { "retpoline",          SPECTRE_V2_CMD_RETPOLINE,         false },
+       { "retpoline,amd",      SPECTRE_V2_CMD_RETPOLINE_AMD,     false },
+       { "retpoline,generic",  SPECTRE_V2_CMD_RETPOLINE_GENERIC, false },
+       { "auto",               SPECTRE_V2_CMD_AUTO,              false },
 };
 
+static void __init spec_v2_print_cond(const char *reason, bool secure)
+{
+       if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2) != secure)
+               pr_info("%s selected on command line.\n", reason);
+}
+
 static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void)
 {
+       enum spectre_v2_mitigation_cmd cmd = SPECTRE_V2_CMD_AUTO;
        char arg[20];
        int ret, i;
-       enum spectre_v2_mitigation_cmd cmd = SPECTRE_V2_CMD_AUTO;
 
        if (cmdline_find_option_bool(boot_command_line, "nospectre_v2"))
                return SPECTRE_V2_CMD_NONE;
-       else {
-               ret = cmdline_find_option(boot_command_line, "spectre_v2", arg, sizeof(arg));
-               if (ret < 0)
-                       return SPECTRE_V2_CMD_AUTO;
 
-               for (i = 0; i < ARRAY_SIZE(mitigation_options); i++) {
-                       if (!match_option(arg, ret, mitigation_options[i].option))
-                               continue;
-                       cmd = mitigation_options[i].cmd;
-                       break;
-               }
+       ret = cmdline_find_option(boot_command_line, "spectre_v2", arg, sizeof(arg));
+       if (ret < 0)
+               return SPECTRE_V2_CMD_AUTO;
 
-               if (i >= ARRAY_SIZE(mitigation_options)) {
-                       pr_err("unknown option (%s). Switching to AUTO select\n", arg);
-                       return SPECTRE_V2_CMD_AUTO;
-               }
+       for (i = 0; i < ARRAY_SIZE(mitigation_options); i++) {
+               if (!match_option(arg, ret, mitigation_options[i].option))
+                       continue;
+               cmd = mitigation_options[i].cmd;
+               break;
+       }
+
+       if (i >= ARRAY_SIZE(mitigation_options)) {
+               pr_err("unknown option (%s). Switching to AUTO select\n", arg);
+               return SPECTRE_V2_CMD_AUTO;
        }
 
        if ((cmd == SPECTRE_V2_CMD_RETPOLINE ||
@@ -316,54 +462,11 @@ static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void)
                return SPECTRE_V2_CMD_AUTO;
        }
 
-       if (mitigation_options[i].secure)
-               spec2_print_if_secure(mitigation_options[i].option);
-       else
-               spec2_print_if_insecure(mitigation_options[i].option);
-
+       spec_v2_print_cond(mitigation_options[i].option,
+                          mitigation_options[i].secure);
        return cmd;
 }
 
-static bool stibp_needed(void)
-{
-       if (spectre_v2_enabled == SPECTRE_V2_NONE)
-               return false;
-
-       if (!boot_cpu_has(X86_FEATURE_STIBP))
-               return false;
-
-       return true;
-}
-
-static void update_stibp_msr(void *info)
-{
-       wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
-}
-
-void arch_smt_update(void)
-{
-       u64 mask;
-
-       if (!stibp_needed())
-               return;
-
-       mutex_lock(&spec_ctrl_mutex);
-       mask = x86_spec_ctrl_base;
-       if (cpu_smt_control == CPU_SMT_ENABLED)
-               mask |= SPEC_CTRL_STIBP;
-       else
-               mask &= ~SPEC_CTRL_STIBP;
-
-       if (mask != x86_spec_ctrl_base) {
-               pr_info("Spectre v2 cross-process SMT mitigation: %s STIBP\n",
-                               cpu_smt_control == CPU_SMT_ENABLED ?
-                               "Enabling" : "Disabling");
-               x86_spec_ctrl_base = mask;
-               on_each_cpu(update_stibp_msr, NULL, 1);
-       }
-       mutex_unlock(&spec_ctrl_mutex);
-}
-
 static void __init spectre_v2_select_mitigation(void)
 {
        enum spectre_v2_mitigation_cmd cmd = spectre_v2_parse_cmdline();
@@ -417,14 +520,12 @@ retpoline_auto:
                        pr_err("Spectre mitigation: LFENCE not serializing, switching to generic retpoline\n");
                        goto retpoline_generic;
                }
-               mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_AMD :
-                                        SPECTRE_V2_RETPOLINE_MINIMAL_AMD;
+               mode = SPECTRE_V2_RETPOLINE_AMD;
                setup_force_cpu_cap(X86_FEATURE_RETPOLINE_AMD);
                setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
        } else {
        retpoline_generic:
-               mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_GENERIC :
-                                        SPECTRE_V2_RETPOLINE_MINIMAL;
+               mode = SPECTRE_V2_RETPOLINE_GENERIC;
                setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
        }
 
@@ -443,12 +544,6 @@ specv2_set_mode:
        setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
        pr_info("Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch\n");
 
-       /* Initialize Indirect Branch Prediction Barrier if supported */
-       if (boot_cpu_has(X86_FEATURE_IBPB)) {
-               setup_force_cpu_cap(X86_FEATURE_USE_IBPB);
-               pr_info("Spectre v2 mitigation: Enabling Indirect Branch Prediction Barrier\n");
-       }
-
        /*
         * Retpoline means the kernel is safe because it has no indirect
         * branches. Enhanced IBRS protects firmware too, so, enable restricted
@@ -465,10 +560,67 @@ specv2_set_mode:
                pr_info("Enabling Restricted Speculation for firmware calls\n");
        }
 
+       /* Set up IBPB and STIBP depending on the general spectre V2 command */
+       spectre_v2_user_select_mitigation(cmd);
+
        /* Enable STIBP if appropriate */
        arch_smt_update();
 }
 
+static void update_stibp_msr(void * __unused)
+{
+       wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
+}
+
+/* Update x86_spec_ctrl_base in case SMT state changed. */
+static void update_stibp_strict(void)
+{
+       u64 mask = x86_spec_ctrl_base & ~SPEC_CTRL_STIBP;
+
+       if (sched_smt_active())
+               mask |= SPEC_CTRL_STIBP;
+
+       if (mask == x86_spec_ctrl_base)
+               return;
+
+       pr_info("Update user space SMT mitigation: STIBP %s\n",
+               mask & SPEC_CTRL_STIBP ? "always-on" : "off");
+       x86_spec_ctrl_base = mask;
+       on_each_cpu(update_stibp_msr, NULL, 1);
+}
+
+/* Update the static key controlling the evaluation of TIF_SPEC_IB */
+static void update_indir_branch_cond(void)
+{
+       if (sched_smt_active())
+               static_branch_enable(&switch_to_cond_stibp);
+       else
+               static_branch_disable(&switch_to_cond_stibp);
+}
+
+void arch_smt_update(void)
+{
+       /* Enhanced IBRS implies STIBP. No update required. */
+       if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED)
+               return;
+
+       mutex_lock(&spec_ctrl_mutex);
+
+       switch (spectre_v2_user) {
+       case SPECTRE_V2_USER_NONE:
+               break;
+       case SPECTRE_V2_USER_STRICT:
+               update_stibp_strict();
+               break;
+       case SPECTRE_V2_USER_PRCTL:
+       case SPECTRE_V2_USER_SECCOMP:
+               update_indir_branch_cond();
+               break;
+       }
+
+       mutex_unlock(&spec_ctrl_mutex);
+}
+
 #undef pr_fmt
 #define pr_fmt(fmt)    "Speculative Store Bypass: " fmt
 
@@ -483,7 +635,7 @@ enum ssb_mitigation_cmd {
        SPEC_STORE_BYPASS_CMD_SECCOMP,
 };
 
-static const char *ssb_strings[] = {
+static const char * const ssb_strings[] = {
        [SPEC_STORE_BYPASS_NONE]        = "Vulnerable",
        [SPEC_STORE_BYPASS_DISABLE]     = "Mitigation: Speculative Store Bypass disabled",
        [SPEC_STORE_BYPASS_PRCTL]       = "Mitigation: Speculative Store Bypass disabled via prctl",
@@ -493,7 +645,7 @@ static const char *ssb_strings[] = {
 static const struct {
        const char *option;
        enum ssb_mitigation_cmd cmd;
-} ssb_mitigation_options[] = {
+} ssb_mitigation_options[]  __initdata = {
        { "auto",       SPEC_STORE_BYPASS_CMD_AUTO },    /* Platform decides */
        { "on",         SPEC_STORE_BYPASS_CMD_ON },      /* Disable Speculative Store Bypass */
        { "off",        SPEC_STORE_BYPASS_CMD_NONE },    /* Don't touch Speculative Store Bypass */
@@ -604,10 +756,25 @@ static void ssb_select_mitigation(void)
 #undef pr_fmt
 #define pr_fmt(fmt)     "Speculation prctl: " fmt
 
-static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl)
+static void task_update_spec_tif(struct task_struct *tsk)
 {
-       bool update;
+       /* Force the update of the real TIF bits */
+       set_tsk_thread_flag(tsk, TIF_SPEC_FORCE_UPDATE);
 
+       /*
+        * Immediately update the speculation control MSRs for the current
+        * task, but for a non-current task delay setting the CPU
+        * mitigation until it is scheduled next.
+        *
+        * This can only happen for SECCOMP mitigation. For PRCTL it's
+        * always the current task.
+        */
+       if (tsk == current)
+               speculation_ctrl_update_current();
+}
+
+static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl)
+{
        if (ssb_mode != SPEC_STORE_BYPASS_PRCTL &&
            ssb_mode != SPEC_STORE_BYPASS_SECCOMP)
                return -ENXIO;
@@ -618,28 +785,56 @@ static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl)
                if (task_spec_ssb_force_disable(task))
                        return -EPERM;
                task_clear_spec_ssb_disable(task);
-               update = test_and_clear_tsk_thread_flag(task, TIF_SSBD);
+               task_update_spec_tif(task);
                break;
        case PR_SPEC_DISABLE:
                task_set_spec_ssb_disable(task);
-               update = !test_and_set_tsk_thread_flag(task, TIF_SSBD);
+               task_update_spec_tif(task);
                break;
        case PR_SPEC_FORCE_DISABLE:
                task_set_spec_ssb_disable(task);
                task_set_spec_ssb_force_disable(task);
-               update = !test_and_set_tsk_thread_flag(task, TIF_SSBD);
+               task_update_spec_tif(task);
                break;
        default:
                return -ERANGE;
        }
+       return 0;
+}
 
-       /*
-        * If being set on non-current task, delay setting the CPU
-        * mitigation until it is next scheduled.
-        */
-       if (task == current && update)
-               speculative_store_bypass_update_current();
-
+static int ib_prctl_set(struct task_struct *task, unsigned long ctrl)
+{
+       switch (ctrl) {
+       case PR_SPEC_ENABLE:
+               if (spectre_v2_user == SPECTRE_V2_USER_NONE)
+                       return 0;
+               /*
+                * Indirect branch speculation is always disabled in strict
+                * mode.
+                */
+               if (spectre_v2_user == SPECTRE_V2_USER_STRICT)
+                       return -EPERM;
+               task_clear_spec_ib_disable(task);
+               task_update_spec_tif(task);
+               break;
+       case PR_SPEC_DISABLE:
+       case PR_SPEC_FORCE_DISABLE:
+               /*
+                * Indirect branch speculation is always allowed when
+                * mitigation is force disabled.
+                */
+               if (spectre_v2_user == SPECTRE_V2_USER_NONE)
+                       return -EPERM;
+               if (spectre_v2_user == SPECTRE_V2_USER_STRICT)
+                       return 0;
+               task_set_spec_ib_disable(task);
+               if (ctrl == PR_SPEC_FORCE_DISABLE)
+                       task_set_spec_ib_force_disable(task);
+               task_update_spec_tif(task);
+               break;
+       default:
+               return -ERANGE;
+       }
        return 0;
 }
 
@@ -649,6 +844,8 @@ int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which,
        switch (which) {
        case PR_SPEC_STORE_BYPASS:
                return ssb_prctl_set(task, ctrl);
+       case PR_SPEC_INDIRECT_BRANCH:
+               return ib_prctl_set(task, ctrl);
        default:
                return -ENODEV;
        }
@@ -659,6 +856,8 @@ void arch_seccomp_spec_mitigate(struct task_struct *task)
 {
        if (ssb_mode == SPEC_STORE_BYPASS_SECCOMP)
                ssb_prctl_set(task, PR_SPEC_FORCE_DISABLE);
+       if (spectre_v2_user == SPECTRE_V2_USER_SECCOMP)
+               ib_prctl_set(task, PR_SPEC_FORCE_DISABLE);
 }
 #endif
 
@@ -681,11 +880,35 @@ static int ssb_prctl_get(struct task_struct *task)
        }
 }
 
+static int ib_prctl_get(struct task_struct *task)
+{
+       if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
+               return PR_SPEC_NOT_AFFECTED;
+
+       switch (spectre_v2_user) {
+       case SPECTRE_V2_USER_NONE:
+               return PR_SPEC_ENABLE;
+       case SPECTRE_V2_USER_PRCTL:
+       case SPECTRE_V2_USER_SECCOMP:
+               if (task_spec_ib_force_disable(task))
+                       return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE;
+               if (task_spec_ib_disable(task))
+                       return PR_SPEC_PRCTL | PR_SPEC_DISABLE;
+               return PR_SPEC_PRCTL | PR_SPEC_ENABLE;
+       case SPECTRE_V2_USER_STRICT:
+               return PR_SPEC_DISABLE;
+       default:
+               return PR_SPEC_NOT_AFFECTED;
+       }
+}
+
 int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which)
 {
        switch (which) {
        case PR_SPEC_STORE_BYPASS:
                return ssb_prctl_get(task);
+       case PR_SPEC_INDIRECT_BRANCH:
+               return ib_prctl_get(task);
        default:
                return -ENODEV;
        }
@@ -823,7 +1046,7 @@ early_param("l1tf", l1tf_cmdline);
 #define L1TF_DEFAULT_MSG "Mitigation: PTE Inversion"
 
 #if IS_ENABLED(CONFIG_KVM_INTEL)
-static const char *l1tf_vmx_states[] = {
+static const char * const l1tf_vmx_states[] = {
        [VMENTER_L1D_FLUSH_AUTO]                = "auto",
        [VMENTER_L1D_FLUSH_NEVER]               = "vulnerable",
        [VMENTER_L1D_FLUSH_COND]                = "conditional cache flushes",
@@ -839,13 +1062,14 @@ static ssize_t l1tf_show_state(char *buf)
 
        if (l1tf_vmx_mitigation == VMENTER_L1D_FLUSH_EPT_DISABLED ||
            (l1tf_vmx_mitigation == VMENTER_L1D_FLUSH_NEVER &&
-            cpu_smt_control == CPU_SMT_ENABLED))
+            sched_smt_active())) {
                return sprintf(buf, "%s; VMX: %s\n", L1TF_DEFAULT_MSG,
                               l1tf_vmx_states[l1tf_vmx_mitigation]);
+       }
 
        return sprintf(buf, "%s; VMX: %s, SMT %s\n", L1TF_DEFAULT_MSG,
                       l1tf_vmx_states[l1tf_vmx_mitigation],
-                      cpu_smt_control == CPU_SMT_ENABLED ? "vulnerable" : "disabled");
+                      sched_smt_active() ? "vulnerable" : "disabled");
 }
 #else
 static ssize_t l1tf_show_state(char *buf)
@@ -854,11 +1078,39 @@ static ssize_t l1tf_show_state(char *buf)
 }
 #endif
 
+static char *stibp_state(void)
+{
+       if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED)
+               return "";
+
+       switch (spectre_v2_user) {
+       case SPECTRE_V2_USER_NONE:
+               return ", STIBP: disabled";
+       case SPECTRE_V2_USER_STRICT:
+               return ", STIBP: forced";
+       case SPECTRE_V2_USER_PRCTL:
+       case SPECTRE_V2_USER_SECCOMP:
+               if (static_key_enabled(&switch_to_cond_stibp))
+                       return ", STIBP: conditional";
+       }
+       return "";
+}
+
+static char *ibpb_state(void)
+{
+       if (boot_cpu_has(X86_FEATURE_IBPB)) {
+               if (static_key_enabled(&switch_mm_always_ibpb))
+                       return ", IBPB: always-on";
+               if (static_key_enabled(&switch_mm_cond_ibpb))
+                       return ", IBPB: conditional";
+               return ", IBPB: disabled";
+       }
+       return "";
+}
+
 static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr,
                               char *buf, unsigned int bug)
 {
-       int ret;
-
        if (!boot_cpu_has_bug(bug))
                return sprintf(buf, "Not affected\n");
 
@@ -876,13 +1128,12 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr
                return sprintf(buf, "Mitigation: __user pointer sanitization\n");
 
        case X86_BUG_SPECTRE_V2:
-               ret = sprintf(buf, "%s%s%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
-                              boot_cpu_has(X86_FEATURE_USE_IBPB) ? ", IBPB" : "",
+               return sprintf(buf, "%s%s%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
+                              ibpb_state(),
                               boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "",
-                              (x86_spec_ctrl_base & SPEC_CTRL_STIBP) ? ", STIBP" : "",
+                              stibp_state(),
                               boot_cpu_has(X86_FEATURE_RSB_CTXSW) ? ", RSB filling" : "",
                               spectre_v2_module_string());
-               return ret;
 
        case X86_BUG_SPEC_STORE_BYPASS:
                return sprintf(buf, "%s\n", ssb_strings[ssb_mode]);
index dd33c35..e12454e 100644 (file)
@@ -56,7 +56,7 @@
 /* Threshold LVT offset is at MSR0xC0000410[15:12] */
 #define SMCA_THR_LVT_OFF       0xF000
 
-static bool thresholding_en;
+static bool thresholding_irq_en;
 
 static const char * const th_names[] = {
        "load_store",
@@ -534,9 +534,8 @@ prepare_threshold_block(unsigned int bank, unsigned int block, u32 addr,
 
 set_offset:
        offset = setup_APIC_mce_threshold(offset, new);
-
-       if ((offset == new) && (mce_threshold_vector != amd_threshold_interrupt))
-               mce_threshold_vector = amd_threshold_interrupt;
+       if (offset == new)
+               thresholding_irq_en = true;
 
 done:
        mce_threshold_block_init(&b, offset);
@@ -1357,9 +1356,6 @@ int mce_threshold_remove_device(unsigned int cpu)
 {
        unsigned int bank;
 
-       if (!thresholding_en)
-               return 0;
-
        for (bank = 0; bank < mca_cfg.banks; ++bank) {
                if (!(per_cpu(bank_map, cpu) & (1 << bank)))
                        continue;
@@ -1377,9 +1373,6 @@ int mce_threshold_create_device(unsigned int cpu)
        struct threshold_bank **bp;
        int err = 0;
 
-       if (!thresholding_en)
-               return 0;
-
        bp = per_cpu(threshold_banks, cpu);
        if (bp)
                return 0;
@@ -1408,9 +1401,6 @@ static __init int threshold_init_device(void)
 {
        unsigned lcpu = 0;
 
-       if (mce_threshold_vector == amd_threshold_interrupt)
-               thresholding_en = true;
-
        /* to hit CPUs online before the notifier is up */
        for_each_online_cpu(lcpu) {
                int err = mce_threshold_create_device(lcpu);
@@ -1419,6 +1409,9 @@ static __init int threshold_init_device(void)
                        return err;
        }
 
+       if (thresholding_irq_en)
+               mce_threshold_vector = amd_threshold_interrupt;
+
        return 0;
 }
 /*
index 61a949d..d99a8ee 100644 (file)
@@ -344,10 +344,10 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)
                        sanitize_restored_xstate(tsk, &env, xfeatures, fx_only);
                }
 
+               local_bh_disable();
                fpu->initialized = 1;
-               preempt_disable();
                fpu__restore(fpu);
-               preempt_enable();
+               local_bh_enable();
 
                return err;
        } else {
index 01ebcb6..7ee8067 100644 (file)
@@ -994,7 +994,6 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
 {
        unsigned long old;
        int faulted;
-       struct ftrace_graph_ent trace;
        unsigned long return_hooker = (unsigned long)
                                &return_to_handler;
 
@@ -1046,19 +1045,7 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
                return;
        }
 
-       trace.func = self_addr;
-       trace.depth = current->curr_ret_stack + 1;
-
-       /* Only trace if the calling function expects to */
-       if (!ftrace_graph_entry(&trace)) {
+       if (function_graph_enter(old, self_addr, frame_pointer, parent))
                *parent = old;
-               return;
-       }
-
-       if (ftrace_push_return_trace(old, self_addr, &trace.depth,
-                                    frame_pointer, parent) == -EBUSY) {
-               *parent = old;
-               return;
-       }
 }
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
index 76fa3b8..ec6fefb 100644 (file)
@@ -37,7 +37,6 @@ asmlinkage __visible void __init i386_start_kernel(void)
        cr4_init_shadow();
 
        sanitize_boot_params(&boot_params);
-       x86_verify_bootdata_version();
 
        x86_early_init_platform_quirks();
 
index 7663a8e..16b1cbd 100644 (file)
@@ -457,8 +457,6 @@ void __init x86_64_start_reservations(char *real_mode_data)
        if (!boot_params.hdr.version)
                copy_bootdata(__va(real_mode_data));
 
-       x86_verify_bootdata_version();
-
        x86_early_init_platform_quirks();
 
        switch (boot_params.hdr.hardware_subarch) {
index 40b16b2..6adf6e6 100644 (file)
@@ -189,7 +189,7 @@ static int copy_optimized_instructions(u8 *dest, u8 *src, u8 *real)
        int len = 0, ret;
 
        while (len < RELATIVEJUMP_SIZE) {
-               ret = __copy_instruction(dest + len, src + len, real, &insn);
+               ret = __copy_instruction(dest + len, src + len, real + len, &insn);
                if (!ret || !can_boost(&insn, src + len))
                        return -EINVAL;
                len += ret;
index c93fcfd..7d31192 100644 (file)
@@ -40,6 +40,8 @@
 #include <asm/prctl.h>
 #include <asm/spec-ctrl.h>
 
+#include "process.h"
+
 /*
  * per-CPU TSS segments. Threads are completely 'soft' on Linux,
  * no more per-task TSS's. The TSS size is kept cacheline-aligned
@@ -252,11 +254,12 @@ void arch_setup_new_exec(void)
                enable_cpuid();
 }
 
-static inline void switch_to_bitmap(struct tss_struct *tss,
-                                   struct thread_struct *prev,
+static inline void switch_to_bitmap(struct thread_struct *prev,
                                    struct thread_struct *next,
                                    unsigned long tifp, unsigned long tifn)
 {
+       struct tss_struct *tss = this_cpu_ptr(&cpu_tss_rw);
+
        if (tifn & _TIF_IO_BITMAP) {
                /*
                 * Copy the relevant range of the IO bitmap.
@@ -395,32 +398,85 @@ static __always_inline void amd_set_ssb_virt_state(unsigned long tifn)
        wrmsrl(MSR_AMD64_VIRT_SPEC_CTRL, ssbd_tif_to_spec_ctrl(tifn));
 }
 
-static __always_inline void intel_set_ssb_state(unsigned long tifn)
+/*
+ * Update the MSRs managing speculation control, during context switch.
+ *
+ * tifp: Previous task's thread flags
+ * tifn: Next task's thread flags
+ */
+static __always_inline void __speculation_ctrl_update(unsigned long tifp,
+                                                     unsigned long tifn)
 {
-       u64 msr = x86_spec_ctrl_base | ssbd_tif_to_spec_ctrl(tifn);
+       unsigned long tif_diff = tifp ^ tifn;
+       u64 msr = x86_spec_ctrl_base;
+       bool updmsr = false;
+
+       /*
+        * If TIF_SSBD is different, select the proper mitigation
+        * method. Note that if SSBD mitigation is disabled or permanentely
+        * enabled this branch can't be taken because nothing can set
+        * TIF_SSBD.
+        */
+       if (tif_diff & _TIF_SSBD) {
+               if (static_cpu_has(X86_FEATURE_VIRT_SSBD)) {
+                       amd_set_ssb_virt_state(tifn);
+               } else if (static_cpu_has(X86_FEATURE_LS_CFG_SSBD)) {
+                       amd_set_core_ssb_state(tifn);
+               } else if (static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD) ||
+                          static_cpu_has(X86_FEATURE_AMD_SSBD)) {
+                       msr |= ssbd_tif_to_spec_ctrl(tifn);
+                       updmsr  = true;
+               }
+       }
+
+       /*
+        * Only evaluate TIF_SPEC_IB if conditional STIBP is enabled,
+        * otherwise avoid the MSR write.
+        */
+       if (IS_ENABLED(CONFIG_SMP) &&
+           static_branch_unlikely(&switch_to_cond_stibp)) {
+               updmsr |= !!(tif_diff & _TIF_SPEC_IB);
+               msr |= stibp_tif_to_spec_ctrl(tifn);
+       }
 
-       wrmsrl(MSR_IA32_SPEC_CTRL, msr);
+       if (updmsr)
+               wrmsrl(MSR_IA32_SPEC_CTRL, msr);
 }
 
-static __always_inline void __speculative_store_bypass_update(unsigned long tifn)
+static unsigned long speculation_ctrl_update_tif(struct task_struct *tsk)
 {
-       if (static_cpu_has(X86_FEATURE_VIRT_SSBD))
-               amd_set_ssb_virt_state(tifn);
-       else if (static_cpu_has(X86_FEATURE_LS_CFG_SSBD))
-               amd_set_core_ssb_state(tifn);
-       else
-               intel_set_ssb_state(tifn);
+       if (test_and_clear_tsk_thread_flag(tsk, TIF_SPEC_FORCE_UPDATE)) {
+               if (task_spec_ssb_disable(tsk))
+                       set_tsk_thread_flag(tsk, TIF_SSBD);
+               else
+                       clear_tsk_thread_flag(tsk, TIF_SSBD);
+
+               if (task_spec_ib_disable(tsk))
+                       set_tsk_thread_flag(tsk, TIF_SPEC_IB);
+               else
+                       clear_tsk_thread_flag(tsk, TIF_SPEC_IB);
+       }
+       /* Return the updated threadinfo flags*/
+       return task_thread_info(tsk)->flags;
 }
 
-void speculative_store_bypass_update(unsigned long tif)
+void speculation_ctrl_update(unsigned long tif)
 {
+       /* Forced update. Make sure all relevant TIF flags are different */
        preempt_disable();
-       __speculative_store_bypass_update(tif);
+       __speculation_ctrl_update(~tif, tif);
        preempt_enable();
 }
 
-void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
-                     struct tss_struct *tss)
+/* Called from seccomp/prctl update */
+void speculation_ctrl_update_current(void)
+{
+       preempt_disable();
+       speculation_ctrl_update(speculation_ctrl_update_tif(current));
+       preempt_enable();
+}
+
+void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p)
 {
        struct thread_struct *prev, *next;
        unsigned long tifp, tifn;
@@ -430,7 +486,7 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
 
        tifn = READ_ONCE(task_thread_info(next_p)->flags);
        tifp = READ_ONCE(task_thread_info(prev_p)->flags);
-       switch_to_bitmap(tss, prev, next, tifp, tifn);
+       switch_to_bitmap(prev, next, tifp, tifn);
 
        propagate_user_return_notify(prev_p, next_p);
 
@@ -451,8 +507,15 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
        if ((tifp ^ tifn) & _TIF_NOCPUID)
                set_cpuid_faulting(!!(tifn & _TIF_NOCPUID));
 
-       if ((tifp ^ tifn) & _TIF_SSBD)
-               __speculative_store_bypass_update(tifn);
+       if (likely(!((tifp | tifn) & _TIF_SPEC_FORCE_UPDATE))) {
+               __speculation_ctrl_update(tifp, tifn);
+       } else {
+               speculation_ctrl_update_tif(prev_p);
+               tifn = speculation_ctrl_update_tif(next_p);
+
+               /* Enforce MSR update to ensure consistent state */
+               __speculation_ctrl_update(~tifn, tifn);
+       }
 }
 
 /*
diff --git a/arch/x86/kernel/process.h b/arch/x86/kernel/process.h
new file mode 100644 (file)
index 0000000..898e97c
--- /dev/null
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Code shared between 32 and 64 bit
+
+#include <asm/spec-ctrl.h>
+
+void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p);
+
+/*
+ * This needs to be inline to optimize for the common case where no extra
+ * work needs to be done.
+ */
+static inline void switch_to_extra(struct task_struct *prev,
+                                  struct task_struct *next)
+{
+       unsigned long next_tif = task_thread_info(next)->flags;
+       unsigned long prev_tif = task_thread_info(prev)->flags;
+
+       if (IS_ENABLED(CONFIG_SMP)) {
+               /*
+                * Avoid __switch_to_xtra() invocation when conditional
+                * STIPB is disabled and the only different bit is
+                * TIF_SPEC_IB. For CONFIG_SMP=n TIF_SPEC_IB is not
+                * in the TIF_WORK_CTXSW masks.
+                */
+               if (!static_branch_likely(&switch_to_cond_stibp)) {
+                       prev_tif &= ~_TIF_SPEC_IB;
+                       next_tif &= ~_TIF_SPEC_IB;
+               }
+       }
+
+       /*
+        * __switch_to_xtra() handles debug registers, i/o bitmaps,
+        * speculation mitigations etc.
+        */
+       if (unlikely(next_tif & _TIF_WORK_CTXSW_NEXT ||
+                    prev_tif & _TIF_WORK_CTXSW_PREV))
+               __switch_to_xtra(prev, next);
+}
index 5046a3c..d3e593e 100644 (file)
@@ -59,6 +59,8 @@
 #include <asm/intel_rdt_sched.h>
 #include <asm/proto.h>
 
+#include "process.h"
+
 void __show_regs(struct pt_regs *regs, enum show_regs_mode mode)
 {
        unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L;
@@ -232,7 +234,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
        struct fpu *prev_fpu = &prev->fpu;
        struct fpu *next_fpu = &next->fpu;
        int cpu = smp_processor_id();
-       struct tss_struct *tss = &per_cpu(cpu_tss_rw, cpu);
 
        /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */
 
@@ -264,12 +265,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
        if (get_kernel_rpl() && unlikely(prev->iopl != next->iopl))
                set_iopl_mask(next->iopl);
 
-       /*
-        * Now maybe handle debug registers and/or IO bitmaps
-        */
-       if (unlikely(task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV ||
-                    task_thread_info(next_p)->flags & _TIF_WORK_CTXSW_NEXT))
-               __switch_to_xtra(prev_p, next_p, tss);
+       switch_to_extra(prev_p, next_p);
 
        /*
         * Leave lazy mode, flushing any hypercalls made here.
index 0e0b428..bbfbf01 100644 (file)
@@ -60,6 +60,8 @@
 #include <asm/unistd_32_ia32.h>
 #endif
 
+#include "process.h"
+
 /* Prints also some state that isn't saved in the pt_regs */
 void __show_regs(struct pt_regs *regs, enum show_regs_mode mode)
 {
@@ -553,7 +555,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
        struct fpu *prev_fpu = &prev->fpu;
        struct fpu *next_fpu = &next->fpu;
        int cpu = smp_processor_id();
-       struct tss_struct *tss = &per_cpu(cpu_tss_rw, cpu);
 
        WARN_ON_ONCE(IS_ENABLED(CONFIG_DEBUG_ENTRY) &&
                     this_cpu_read(irq_count) != -1);
@@ -617,12 +618,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
        /* Reload sp0. */
        update_task_stack(next_p);
 
-       /*
-        * Now maybe reload the debug registers and handle I/O bitmaps
-        */
-       if (unlikely(task_thread_info(next_p)->flags & _TIF_WORK_CTXSW_NEXT ||
-                    task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV))
-               __switch_to_xtra(prev_p, next_p, tss);
+       switch_to_extra(prev_p, next_p);
 
 #ifdef CONFIG_XEN_PV
        /*
index b74e7bf..d494b9b 100644 (file)
@@ -1280,23 +1280,6 @@ void __init setup_arch(char **cmdline_p)
        unwind_init();
 }
 
-/*
- * From boot protocol 2.14 onwards we expect the bootloader to set the
- * version to "0x8000 | <used version>". In case we find a version >= 2.14
- * without the 0x8000 we assume the boot loader supports 2.13 only and
- * reset the version accordingly. The 0x8000 flag is removed in any case.
- */
-void __init x86_verify_bootdata_version(void)
-{
-       if (boot_params.hdr.version & VERSION_WRITTEN)
-               boot_params.hdr.version &= ~VERSION_WRITTEN;
-       else if (boot_params.hdr.version >= 0x020e)
-               boot_params.hdr.version = 0x020d;
-
-       if (boot_params.hdr.version < 0x020e)
-               boot_params.hdr.acpi_rsdp_addr = 0;
-}
-
 #ifdef CONFIG_X86_32
 
 static struct resource video_ram_resource = {
index 89db20f..c4533d0 100644 (file)
@@ -55,7 +55,7 @@
 #define PRIo64 "o"
 
 /* #define apic_debug(fmt,arg...) printk(KERN_WARNING fmt,##arg) */
-#define apic_debug(fmt, arg...)
+#define apic_debug(fmt, arg...) do {} while (0)
 
 /* 14 is the version for Xeon and Pentium 8.4.8*/
 #define APIC_VERSION                   (0x14UL | ((KVM_APIC_LVT_NUM - 1) << 16))
@@ -576,6 +576,11 @@ int kvm_pv_send_ipi(struct kvm *kvm, unsigned long ipi_bitmap_low,
        rcu_read_lock();
        map = rcu_dereference(kvm->arch.apic_map);
 
+       if (unlikely(!map)) {
+               count = -EOPNOTSUPP;
+               goto out;
+       }
+
        if (min > map->max_apic_id)
                goto out;
        /* Bits above cluster_size are masked in the caller.  */
index cf5f572..7c03c0f 100644 (file)
@@ -5074,9 +5074,9 @@ static bool need_remote_flush(u64 old, u64 new)
 }
 
 static u64 mmu_pte_write_fetch_gpte(struct kvm_vcpu *vcpu, gpa_t *gpa,
-                                   const u8 *new, int *bytes)
+                                   int *bytes)
 {
-       u64 gentry;
+       u64 gentry = 0;
        int r;
 
        /*
@@ -5088,22 +5088,12 @@ static u64 mmu_pte_write_fetch_gpte(struct kvm_vcpu *vcpu, gpa_t *gpa,
                /* Handle a 32-bit guest writing two halves of a 64-bit gpte */
                *gpa &= ~(gpa_t)7;
                *bytes = 8;
-               r = kvm_vcpu_read_guest(vcpu, *gpa, &gentry, 8);
-               if (r)
-                       gentry = 0;
-               new = (const u8 *)&gentry;
        }
 
-       switch (*bytes) {
-       case 4:
-               gentry = *(const u32 *)new;
-               break;
-       case 8:
-               gentry = *(const u64 *)new;
-               break;
-       default:
-               gentry = 0;
-               break;
+       if (*bytes == 4 || *bytes == 8) {
+               r = kvm_vcpu_read_guest_atomic(vcpu, *gpa, &gentry, *bytes);
+               if (r)
+                       gentry = 0;
        }
 
        return gentry;
@@ -5207,8 +5197,6 @@ static void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
 
        pgprintk("%s: gpa %llx bytes %d\n", __func__, gpa, bytes);
 
-       gentry = mmu_pte_write_fetch_gpte(vcpu, &gpa, new, &bytes);
-
        /*
         * No need to care whether allocation memory is successful
         * or not since pte prefetch is skiped if it does not have
@@ -5217,6 +5205,9 @@ static void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
        mmu_topup_memory_caches(vcpu);
 
        spin_lock(&vcpu->kvm->mmu_lock);
+
+       gentry = mmu_pte_write_fetch_gpte(vcpu, &gpa, &bytes);
+
        ++vcpu->kvm->stat.mmu_pte_write;
        kvm_mmu_audit(vcpu, AUDIT_PRE_PTE_WRITE);
 
index 0e21ccc..cc6467b 100644 (file)
@@ -1446,7 +1446,7 @@ static u64 svm_read_l1_tsc_offset(struct kvm_vcpu *vcpu)
        return vcpu->arch.tsc_offset;
 }
 
-static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
+static u64 svm_write_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
        u64 g_tsc_offset = 0;
@@ -1464,6 +1464,7 @@ static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
        svm->vmcb->control.tsc_offset = offset + g_tsc_offset;
 
        mark_dirty(svm->vmcb, VMCB_INTERCEPTS);
+       return svm->vmcb->control.tsc_offset;
 }
 
 static void avic_init_vmcb(struct vcpu_svm *svm)
@@ -1664,20 +1665,23 @@ static u64 *avic_get_physical_id_entry(struct kvm_vcpu *vcpu,
 static int avic_init_access_page(struct kvm_vcpu *vcpu)
 {
        struct kvm *kvm = vcpu->kvm;
-       int ret;
+       int ret = 0;
 
+       mutex_lock(&kvm->slots_lock);
        if (kvm->arch.apic_access_page_done)
-               return 0;
+               goto out;
 
-       ret = x86_set_memory_region(kvm,
-                                   APIC_ACCESS_PAGE_PRIVATE_MEMSLOT,
-                                   APIC_DEFAULT_PHYS_BASE,
-                                   PAGE_SIZE);
+       ret = __x86_set_memory_region(kvm,
+                                     APIC_ACCESS_PAGE_PRIVATE_MEMSLOT,
+                                     APIC_DEFAULT_PHYS_BASE,
+                                     PAGE_SIZE);
        if (ret)
-               return ret;
+               goto out;
 
        kvm->arch.apic_access_page_done = true;
-       return 0;
+out:
+       mutex_unlock(&kvm->slots_lock);
+       return ret;
 }
 
 static int avic_init_backing_page(struct kvm_vcpu *vcpu)
@@ -2189,21 +2193,31 @@ out:
        return ERR_PTR(err);
 }
 
+static void svm_clear_current_vmcb(struct vmcb *vmcb)
+{
+       int i;
+
+       for_each_online_cpu(i)
+               cmpxchg(&per_cpu(svm_data, i)->current_vmcb, vmcb, NULL);
+}
+
 static void svm_free_vcpu(struct kvm_vcpu *vcpu)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
 
+       /*
+        * The vmcb page can be recycled, causing a false negative in
+        * svm_vcpu_load(). So, ensure that no logical CPU has this
+        * vmcb page recorded as its current vmcb.
+        */
+       svm_clear_current_vmcb(svm->vmcb);
+
        __free_page(pfn_to_page(__sme_clr(svm->vmcb_pa) >> PAGE_SHIFT));
        __free_pages(virt_to_page(svm->msrpm), MSRPM_ALLOC_ORDER);
        __free_page(virt_to_page(svm->nested.hsave));
        __free_pages(virt_to_page(svm->nested.msrpm), MSRPM_ALLOC_ORDER);
        kvm_vcpu_uninit(vcpu);
        kmem_cache_free(kvm_vcpu_cache, svm);
-       /*
-        * The vmcb page can be recycled, causing a false negative in
-        * svm_vcpu_load(). So do a full IBPB now.
-        */
-       indirect_branch_prediction_barrier();
 }
 
 static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
@@ -7149,7 +7163,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
        .has_wbinvd_exit = svm_has_wbinvd_exit,
 
        .read_l1_tsc_offset = svm_read_l1_tsc_offset,
-       .write_tsc_offset = svm_write_tsc_offset,
+       .write_l1_tsc_offset = svm_write_l1_tsc_offset,
 
        .set_tdp_cr3 = set_tdp_cr3,
 
index 4555077..8d5d984 100644 (file)
@@ -174,6 +174,7 @@ module_param_named(preemption_timer, enable_preemption_timer, bool, S_IRUGO);
  * refer SDM volume 3b section 21.6.13 & 22.1.3.
  */
 static unsigned int ple_gap = KVM_DEFAULT_PLE_GAP;
+module_param(ple_gap, uint, 0444);
 
 static unsigned int ple_window = KVM_VMX_DEFAULT_PLE_WINDOW;
 module_param(ple_window, uint, 0444);
@@ -984,6 +985,7 @@ struct vcpu_vmx {
        struct shared_msr_entry *guest_msrs;
        int                   nmsrs;
        int                   save_nmsrs;
+       bool                  guest_msrs_dirty;
        unsigned long         host_idt_base;
 #ifdef CONFIG_X86_64
        u64                   msr_host_kernel_gs_base;
@@ -1306,7 +1308,7 @@ static void vmx_set_nmi_mask(struct kvm_vcpu *vcpu, bool masked);
 static bool nested_vmx_is_page_fault_vmexit(struct vmcs12 *vmcs12,
                                            u16 error_code);
 static void vmx_update_msr_bitmap(struct kvm_vcpu *vcpu);
-static void __always_inline vmx_disable_intercept_for_msr(unsigned long *msr_bitmap,
+static __always_inline void vmx_disable_intercept_for_msr(unsigned long *msr_bitmap,
                                                          u32 msr, int type);
 
 static DEFINE_PER_CPU(struct vmcs *, vmxarea);
@@ -1610,12 +1612,6 @@ static int nested_enable_evmcs(struct kvm_vcpu *vcpu,
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
 
-       /* We don't support disabling the feature for simplicity. */
-       if (vmx->nested.enlightened_vmcs_enabled)
-               return 0;
-
-       vmx->nested.enlightened_vmcs_enabled = true;
-
        /*
         * vmcs_version represents the range of supported Enlightened VMCS
         * versions: lower 8 bits is the minimal version, higher 8 bits is the
@@ -1625,6 +1621,12 @@ static int nested_enable_evmcs(struct kvm_vcpu *vcpu,
        if (vmcs_version)
                *vmcs_version = (KVM_EVMCS_VERSION << 8) | 1;
 
+       /* We don't support disabling the feature for simplicity. */
+       if (vmx->nested.enlightened_vmcs_enabled)
+               return 0;
+
+       vmx->nested.enlightened_vmcs_enabled = true;
+
        vmx->nested.msrs.pinbased_ctls_high &= ~EVMCS1_UNSUPPORTED_PINCTRL;
        vmx->nested.msrs.entry_ctls_high &= ~EVMCS1_UNSUPPORTED_VMENTRY_CTRL;
        vmx->nested.msrs.exit_ctls_high &= ~EVMCS1_UNSUPPORTED_VMEXIT_CTRL;
@@ -2897,6 +2899,20 @@ static void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu)
 
        vmx->req_immediate_exit = false;
 
+       /*
+        * Note that guest MSRs to be saved/restored can also be changed
+        * when guest state is loaded. This happens when guest transitions
+        * to/from long-mode by setting MSR_EFER.LMA.
+        */
+       if (!vmx->loaded_cpu_state || vmx->guest_msrs_dirty) {
+               vmx->guest_msrs_dirty = false;
+               for (i = 0; i < vmx->save_nmsrs; ++i)
+                       kvm_set_shared_msr(vmx->guest_msrs[i].index,
+                                          vmx->guest_msrs[i].data,
+                                          vmx->guest_msrs[i].mask);
+
+       }
+
        if (vmx->loaded_cpu_state)
                return;
 
@@ -2957,11 +2973,6 @@ static void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu)
                vmcs_writel(HOST_GS_BASE, gs_base);
                host_state->gs_base = gs_base;
        }
-
-       for (i = 0; i < vmx->save_nmsrs; ++i)
-               kvm_set_shared_msr(vmx->guest_msrs[i].index,
-                                  vmx->guest_msrs[i].data,
-                                  vmx->guest_msrs[i].mask);
 }
 
 static void vmx_prepare_switch_to_host(struct vcpu_vmx *vmx)
@@ -3436,6 +3447,7 @@ static void setup_msrs(struct vcpu_vmx *vmx)
                move_msr_up(vmx, index, save_nmsrs++);
 
        vmx->save_nmsrs = save_nmsrs;
+       vmx->guest_msrs_dirty = true;
 
        if (cpu_has_vmx_msr_bitmap())
                vmx_update_msr_bitmap(&vmx->vcpu);
@@ -3452,11 +3464,9 @@ static u64 vmx_read_l1_tsc_offset(struct kvm_vcpu *vcpu)
        return vcpu->arch.tsc_offset;
 }
 
-/*
- * writes 'offset' into guest's timestamp counter offset register
- */
-static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
+static u64 vmx_write_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
 {
+       u64 active_offset = offset;
        if (is_guest_mode(vcpu)) {
                /*
                 * We're here if L1 chose not to trap WRMSR to TSC. According
@@ -3464,17 +3474,16 @@ static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
                 * set for L2 remains unchanged, and still needs to be added
                 * to the newly set TSC to get L2's TSC.
                 */
-               struct vmcs12 *vmcs12;
-               /* recalculate vmcs02.TSC_OFFSET: */
-               vmcs12 = get_vmcs12(vcpu);
-               vmcs_write64(TSC_OFFSET, offset +
-                       (nested_cpu_has(vmcs12, CPU_BASED_USE_TSC_OFFSETING) ?
-                        vmcs12->tsc_offset : 0));
+               struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
+               if (nested_cpu_has(vmcs12, CPU_BASED_USE_TSC_OFFSETING))
+                       active_offset += vmcs12->tsc_offset;
        } else {
                trace_kvm_write_tsc_offset(vcpu->vcpu_id,
                                           vmcs_read64(TSC_OFFSET), offset);
-               vmcs_write64(TSC_OFFSET, offset);
        }
+
+       vmcs_write64(TSC_OFFSET, active_offset);
+       return active_offset;
 }
 
 /*
@@ -5944,7 +5953,7 @@ static void free_vpid(int vpid)
        spin_unlock(&vmx_vpid_lock);
 }
 
-static void __always_inline vmx_disable_intercept_for_msr(unsigned long *msr_bitmap,
+static __always_inline void vmx_disable_intercept_for_msr(unsigned long *msr_bitmap,
                                                          u32 msr, int type)
 {
        int f = sizeof(unsigned long);
@@ -5982,7 +5991,7 @@ static void __always_inline vmx_disable_intercept_for_msr(unsigned long *msr_bit
        }
 }
 
-static void __always_inline vmx_enable_intercept_for_msr(unsigned long *msr_bitmap,
+static __always_inline void vmx_enable_intercept_for_msr(unsigned long *msr_bitmap,
                                                         u32 msr, int type)
 {
        int f = sizeof(unsigned long);
@@ -6020,7 +6029,7 @@ static void __always_inline vmx_enable_intercept_for_msr(unsigned long *msr_bitm
        }
 }
 
-static void __always_inline vmx_set_intercept_for_msr(unsigned long *msr_bitmap,
+static __always_inline void vmx_set_intercept_for_msr(unsigned long *msr_bitmap,
                                                      u32 msr, int type, bool value)
 {
        if (value)
@@ -8664,8 +8673,6 @@ static int copy_enlightened_to_vmcs12(struct vcpu_vmx *vmx)
        struct vmcs12 *vmcs12 = vmx->nested.cached_vmcs12;
        struct hv_enlightened_vmcs *evmcs = vmx->nested.hv_evmcs;
 
-       vmcs12->hdr.revision_id = evmcs->revision_id;
-
        /* HV_VMX_ENLIGHTENED_CLEAN_FIELD_NONE */
        vmcs12->tpr_threshold = evmcs->tpr_threshold;
        vmcs12->guest_rip = evmcs->guest_rip;
@@ -9369,7 +9376,30 @@ static int nested_vmx_handle_enlightened_vmptrld(struct kvm_vcpu *vcpu,
 
                vmx->nested.hv_evmcs = kmap(vmx->nested.hv_evmcs_page);
 
-               if (vmx->nested.hv_evmcs->revision_id != VMCS12_REVISION) {
+               /*
+                * Currently, KVM only supports eVMCS version 1
+                * (== KVM_EVMCS_VERSION) and thus we expect guest to set this
+                * value to first u32 field of eVMCS which should specify eVMCS
+                * VersionNumber.
+                *
+                * Guest should be aware of supported eVMCS versions by host by
+                * examining CPUID.0x4000000A.EAX[0:15]. Host userspace VMM is
+                * expected to set this CPUID leaf according to the value
+                * returned in vmcs_version from nested_enable_evmcs().
+                *
+                * However, it turns out that Microsoft Hyper-V fails to comply
+                * to their own invented interface: When Hyper-V use eVMCS, it
+                * just sets first u32 field of eVMCS to revision_id specified
+                * in MSR_IA32_VMX_BASIC. Instead of used eVMCS version number
+                * which is one of the supported versions specified in
+                * CPUID.0x4000000A.EAX[0:15].
+                *
+                * To overcome Hyper-V bug, we accept here either a supported
+                * eVMCS version or VMCS12 revision_id as valid values for first
+                * u32 field of eVMCS.
+                */
+               if ((vmx->nested.hv_evmcs->revision_id != KVM_EVMCS_VERSION) &&
+                   (vmx->nested.hv_evmcs->revision_id != VMCS12_REVISION)) {
                        nested_release_evmcs(vcpu);
                        return 0;
                }
@@ -9390,9 +9420,11 @@ static int nested_vmx_handle_enlightened_vmptrld(struct kvm_vcpu *vcpu,
                 * present in struct hv_enlightened_vmcs, ...). Make sure there
                 * are no leftovers.
                 */
-               if (from_launch)
-                       memset(vmx->nested.cached_vmcs12, 0,
-                              sizeof(*vmx->nested.cached_vmcs12));
+               if (from_launch) {
+                       struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
+                       memset(vmcs12, 0, sizeof(*vmcs12));
+                       vmcs12->hdr.revision_id = VMCS12_REVISION;
+               }
 
        }
        return 1;
@@ -11953,6 +11985,8 @@ static void nested_get_vmcs12_pages(struct kvm_vcpu *vcpu)
                        kunmap(vmx->nested.pi_desc_page);
                        kvm_release_page_dirty(vmx->nested.pi_desc_page);
                        vmx->nested.pi_desc_page = NULL;
+                       vmx->nested.pi_desc = NULL;
+                       vmcs_write64(POSTED_INTR_DESC_ADDR, -1ull);
                }
                page = kvm_vcpu_gpa_to_page(vcpu, vmcs12->posted_intr_desc_addr);
                if (is_error_page(page))
@@ -15062,7 +15096,7 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
        .has_wbinvd_exit = cpu_has_vmx_wbinvd_exit,
 
        .read_l1_tsc_offset = vmx_read_l1_tsc_offset,
-       .write_tsc_offset = vmx_write_tsc_offset,
+       .write_l1_tsc_offset = vmx_write_l1_tsc_offset,
 
        .set_tdp_cr3 = vmx_set_cr3,
 
index 5cd5647..f049ecf 100644 (file)
@@ -1665,8 +1665,7 @@ EXPORT_SYMBOL_GPL(kvm_read_l1_tsc);
 
 static void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
 {
-       kvm_x86_ops->write_tsc_offset(vcpu, offset);
-       vcpu->arch.tsc_offset = offset;
+       vcpu->arch.tsc_offset = kvm_x86_ops->write_l1_tsc_offset(vcpu, offset);
 }
 
 static inline bool kvm_check_tsc_unstable(void)
@@ -1794,7 +1793,8 @@ EXPORT_SYMBOL_GPL(kvm_write_tsc);
 static inline void adjust_tsc_offset_guest(struct kvm_vcpu *vcpu,
                                           s64 adjustment)
 {
-       kvm_vcpu_write_tsc_offset(vcpu, vcpu->arch.tsc_offset + adjustment);
+       u64 tsc_offset = kvm_x86_ops->read_l1_tsc_offset(vcpu);
+       kvm_vcpu_write_tsc_offset(vcpu, tsc_offset + adjustment);
 }
 
 static inline void adjust_tsc_offset_host(struct kvm_vcpu *vcpu, s64 adjustment)
@@ -2426,6 +2426,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        case MSR_AMD64_PATCH_LOADER:
        case MSR_AMD64_BU_CFG2:
        case MSR_AMD64_DC_CFG:
+       case MSR_F15H_EX_CFG:
                break;
 
        case MSR_IA32_UCODE_REV:
@@ -2721,6 +2722,7 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        case MSR_AMD64_BU_CFG2:
        case MSR_IA32_PERF_CTL:
        case MSR_AMD64_DC_CFG:
+       case MSR_F15H_EX_CFG:
                msr_info->data = 0;
                break;
        case MSR_F15H_PERF_CTL0 ... MSR_F15H_PERF_CTR5:
@@ -6918,6 +6920,7 @@ static int kvm_pv_clock_pairing(struct kvm_vcpu *vcpu, gpa_t paddr,
        clock_pairing.nsec = ts.tv_nsec;
        clock_pairing.tsc = kvm_read_l1_tsc(vcpu, cycle);
        clock_pairing.flags = 0;
+       memset(&clock_pairing.pad, 0, sizeof(clock_pairing.pad));
 
        ret = 0;
        if (kvm_write_guest(vcpu->kvm, paddr, &clock_pairing,
@@ -7445,7 +7448,7 @@ void kvm_make_scan_ioapic_request(struct kvm *kvm)
 
 static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
 {
-       if (!kvm_apic_hw_enabled(vcpu->arch.apic))
+       if (!kvm_apic_present(vcpu))
                return;
 
        bitmap_zero(vcpu->arch.ioapic_handled_vectors, 256);
@@ -7455,7 +7458,8 @@ static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
        else {
                if (vcpu->arch.apicv_active)
                        kvm_x86_ops->sync_pir_to_irr(vcpu);
-               kvm_ioapic_scan_entry(vcpu, vcpu->arch.ioapic_handled_vectors);
+               if (ioapic_in_kernel(vcpu->kvm))
+                       kvm_ioapic_scan_entry(vcpu, vcpu->arch.ioapic_handled_vectors);
        }
 
        if (is_guest_mode(vcpu))
index bddd6b3..03b6b4c 100644 (file)
@@ -7,7 +7,6 @@
 #include <linux/export.h>
 #include <linux/cpu.h>
 #include <linux/debugfs.h>
-#include <linux/ptrace.h>
 
 #include <asm/tlbflush.h>
 #include <asm/mmu_context.h>
  *     Implement flush IPI by CALL_FUNCTION_VECTOR, Alex Shi
  */
 
+/*
+ * Use bit 0 to mangle the TIF_SPEC_IB state into the mm pointer which is
+ * stored in cpu_tlb_state.last_user_mm_ibpb.
+ */
+#define LAST_USER_MM_IBPB      0x1UL
+
 /*
  * We get here when we do something requiring a TLB invalidation
  * but could not go invalidate all of the contexts.  We do the
@@ -181,17 +186,87 @@ static void sync_current_stack_to_mm(struct mm_struct *mm)
        }
 }
 
-static bool ibpb_needed(struct task_struct *tsk, u64 last_ctx_id)
+static inline unsigned long mm_mangle_tif_spec_ib(struct task_struct *next)
+{
+       unsigned long next_tif = task_thread_info(next)->flags;
+       unsigned long ibpb = (next_tif >> TIF_SPEC_IB) & LAST_USER_MM_IBPB;
+
+       return (unsigned long)next->mm | ibpb;
+}
+
+static void cond_ibpb(struct task_struct *next)
 {
+       if (!next || !next->mm)
+               return;
+
        /*
-        * Check if the current (previous) task has access to the memory
-        * of the @tsk (next) task. If access is denied, make sure to
-        * issue a IBPB to stop user->user Spectre-v2 attacks.
-        *
-        * Note: __ptrace_may_access() returns 0 or -ERRNO.
+        * Both, the conditional and the always IBPB mode use the mm
+        * pointer to avoid the IBPB when switching between tasks of the
+        * same process. Using the mm pointer instead of mm->context.ctx_id
+        * opens a hypothetical hole vs. mm_struct reuse, which is more or
+        * less impossible to control by an attacker. Aside of that it
+        * would only affect the first schedule so the theoretically
+        * exposed data is not really interesting.
         */
-       return (tsk && tsk->mm && tsk->mm->context.ctx_id != last_ctx_id &&
-               ptrace_may_access_sched(tsk, PTRACE_MODE_SPEC_IBPB));
+       if (static_branch_likely(&switch_mm_cond_ibpb)) {
+               unsigned long prev_mm, next_mm;
+
+               /*
+                * This is a bit more complex than the always mode because
+                * it has to handle two cases:
+                *
+                * 1) Switch from a user space task (potential attacker)
+                *    which has TIF_SPEC_IB set to a user space task
+                *    (potential victim) which has TIF_SPEC_IB not set.
+                *
+                * 2) Switch from a user space task (potential attacker)
+                *    which has TIF_SPEC_IB not set to a user space task
+                *    (potential victim) which has TIF_SPEC_IB set.
+                *
+                * This could be done by unconditionally issuing IBPB when
+                * a task which has TIF_SPEC_IB set is either scheduled in
+                * or out. Though that results in two flushes when:
+                *
+                * - the same user space task is scheduled out and later
+                *   scheduled in again and only a kernel thread ran in
+                *   between.
+                *
+                * - a user space task belonging to the same process is
+                *   scheduled in after a kernel thread ran in between
+                *
+                * - a user space task belonging to the same process is
+                *   scheduled in immediately.
+                *
+                * Optimize this with reasonably small overhead for the
+                * above cases. Mangle the TIF_SPEC_IB bit into the mm
+                * pointer of the incoming task which is stored in
+                * cpu_tlbstate.last_user_mm_ibpb for comparison.
+                */
+               next_mm = mm_mangle_tif_spec_ib(next);
+               prev_mm = this_cpu_read(cpu_tlbstate.last_user_mm_ibpb);
+
+               /*
+                * Issue IBPB only if the mm's are different and one or
+                * both have the IBPB bit set.
+                */
+               if (next_mm != prev_mm &&
+                   (next_mm | prev_mm) & LAST_USER_MM_IBPB)
+                       indirect_branch_prediction_barrier();
+
+               this_cpu_write(cpu_tlbstate.last_user_mm_ibpb, next_mm);
+       }
+
+       if (static_branch_unlikely(&switch_mm_always_ibpb)) {
+               /*
+                * Only flush when switching to a user space task with a
+                * different context than the user space task which ran
+                * last on this CPU.
+                */
+               if (this_cpu_read(cpu_tlbstate.last_user_mm) != next->mm) {
+                       indirect_branch_prediction_barrier();
+                       this_cpu_write(cpu_tlbstate.last_user_mm, next->mm);
+               }
+       }
 }
 
 void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
@@ -292,22 +367,12 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
                new_asid = prev_asid;
                need_flush = true;
        } else {
-               u64 last_ctx_id = this_cpu_read(cpu_tlbstate.last_ctx_id);
-
                /*
                 * Avoid user/user BTB poisoning by flushing the branch
                 * predictor when switching between processes. This stops
                 * one process from doing Spectre-v2 attacks on another.
-                *
-                * As an optimization, flush indirect branches only when
-                * switching into a processes that can't be ptrace by the
-                * current one (as in such case, attacker has much more
-                * convenient way how to tamper with the next process than
-                * branch buffer poisoning).
                 */
-               if (static_cpu_has(X86_FEATURE_USE_IBPB) &&
-                               ibpb_needed(tsk, last_ctx_id))
-                       indirect_branch_prediction_barrier();
+               cond_ibpb(tsk);
 
                if (IS_ENABLED(CONFIG_VMAP_STACK)) {
                        /*
@@ -365,14 +430,6 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
                trace_tlb_flush_rcuidle(TLB_FLUSH_ON_TASK_SWITCH, 0);
        }
 
-       /*
-        * Record last user mm's context id, so we can avoid
-        * flushing branch buffer with IBPB if we switch back
-        * to the same user.
-        */
-       if (next != &init_mm)
-               this_cpu_write(cpu_tlbstate.last_ctx_id, next->context.ctx_id);
-
        /* Make sure we write CR3 before loaded_mm. */
        barrier();
 
@@ -441,7 +498,7 @@ void initialize_tlbstate_and_flush(void)
        write_cr3(build_cr3(mm->pgd, 0));
 
        /* Reinitialize tlbstate. */
-       this_cpu_write(cpu_tlbstate.last_ctx_id, mm->context.ctx_id);
+       this_cpu_write(cpu_tlbstate.last_user_mm_ibpb, LAST_USER_MM_IBPB);
        this_cpu_write(cpu_tlbstate.loaded_mm_asid, 0);
        this_cpu_write(cpu_tlbstate.next_asid, 1);
        this_cpu_write(cpu_tlbstate.ctxs[0].ctx_id, mm->context.ctx_id);
index 2580cd2..5542303 100644 (file)
@@ -1181,6 +1181,8 @@ out_image:
        }
 
        if (!image || !prog->is_func || extra_pass) {
+               if (image)
+                       bpf_prog_fill_jited_linfo(prog, addrs);
 out_addrs:
                kfree(addrs);
                kfree(jit_data);
index 7476b3b..7138bc7 100644 (file)
@@ -183,7 +183,7 @@ early_efi_write(struct console *con, const char *str, unsigned int num)
                        num--;
                }
 
-               if (efi_x >= si->lfb_width) {
+               if (efi_x + font->width > si->lfb_width) {
                        efi_x = 0;
                        efi_y += font->height;
                }
index e996e8e..750f46a 100644 (file)
@@ -10,7 +10,6 @@
 #include <xen/xen.h>
 #include <xen/features.h>
 #include <xen/page.h>
-#include <xen/interface/memory.h>
 
 #include <asm/xen/hypercall.h>
 #include <asm/xen/hypervisor.h>
@@ -346,80 +345,3 @@ void xen_arch_unregister_cpu(int num)
 }
 EXPORT_SYMBOL(xen_arch_unregister_cpu);
 #endif
-
-#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
-void __init arch_xen_balloon_init(struct resource *hostmem_resource)
-{
-       struct xen_memory_map memmap;
-       int rc;
-       unsigned int i, last_guest_ram;
-       phys_addr_t max_addr = PFN_PHYS(max_pfn);
-       struct e820_table *xen_e820_table;
-       const struct e820_entry *entry;
-       struct resource *res;
-
-       if (!xen_initial_domain())
-               return;
-
-       xen_e820_table = kmalloc(sizeof(*xen_e820_table), GFP_KERNEL);
-       if (!xen_e820_table)
-               return;
-
-       memmap.nr_entries = ARRAY_SIZE(xen_e820_table->entries);
-       set_xen_guest_handle(memmap.buffer, xen_e820_table->entries);
-       rc = HYPERVISOR_memory_op(XENMEM_machine_memory_map, &memmap);
-       if (rc) {
-               pr_warn("%s: Can't read host e820 (%d)\n", __func__, rc);
-               goto out;
-       }
-
-       last_guest_ram = 0;
-       for (i = 0; i < memmap.nr_entries; i++) {
-               if (xen_e820_table->entries[i].addr >= max_addr)
-                       break;
-               if (xen_e820_table->entries[i].type == E820_TYPE_RAM)
-                       last_guest_ram = i;
-       }
-
-       entry = &xen_e820_table->entries[last_guest_ram];
-       if (max_addr >= entry->addr + entry->size)
-               goto out; /* No unallocated host RAM. */
-
-       hostmem_resource->start = max_addr;
-       hostmem_resource->end = entry->addr + entry->size;
-
-       /*
-        * Mark non-RAM regions between the end of dom0 RAM and end of host RAM
-        * as unavailable. The rest of that region can be used for hotplug-based
-        * ballooning.
-        */
-       for (; i < memmap.nr_entries; i++) {
-               entry = &xen_e820_table->entries[i];
-
-               if (entry->type == E820_TYPE_RAM)
-                       continue;
-
-               if (entry->addr >= hostmem_resource->end)
-                       break;
-
-               res = kzalloc(sizeof(*res), GFP_KERNEL);
-               if (!res)
-                       goto out;
-
-               res->name = "Unavailable host RAM";
-               res->start = entry->addr;
-               res->end = (entry->addr + entry->size < hostmem_resource->end) ?
-                           entry->addr + entry->size : hostmem_resource->end;
-               rc = insert_resource(hostmem_resource, res);
-               if (rc) {
-                       pr_warn("%s: Can't insert [%llx - %llx) (%d)\n",
-                               __func__, res->start, res->end, rc);
-                       kfree(res);
-                       goto  out;
-               }
-       }
-
- out:
-       kfree(xen_e820_table);
-}
-#endif /* CONFIG_XEN_BALLOON_MEMORY_HOTPLUG */
index 2bce795..0766a08 100644 (file)
@@ -69,6 +69,11 @@ void xen_mc_flush(void)
 
        trace_xen_mc_flush(b->mcidx, b->argidx, b->cbidx);
 
+#if MC_DEBUG
+       memcpy(b->debug, b->entries,
+              b->mcidx * sizeof(struct multicall_entry));
+#endif
+
        switch (b->mcidx) {
        case 0:
                /* no-op */
@@ -87,32 +92,34 @@ void xen_mc_flush(void)
                break;
 
        default:
-#if MC_DEBUG
-               memcpy(b->debug, b->entries,
-                      b->mcidx * sizeof(struct multicall_entry));
-#endif
-
                if (HYPERVISOR_multicall(b->entries, b->mcidx) != 0)
                        BUG();
                for (i = 0; i < b->mcidx; i++)
                        if (b->entries[i].result < 0)
                                ret++;
+       }
 
+       if (WARN_ON(ret)) {
+               pr_err("%d of %d multicall(s) failed: cpu %d\n",
+                      ret, b->mcidx, smp_processor_id());
+               for (i = 0; i < b->mcidx; i++) {
+                       if (b->entries[i].result < 0) {
 #if MC_DEBUG
-               if (ret) {
-                       printk(KERN_ERR "%d multicall(s) failed: cpu %d\n",
-                              ret, smp_processor_id());
-                       dump_stack();
-                       for (i = 0; i < b->mcidx; i++) {
-                               printk(KERN_DEBUG "  call %2d/%d: op=%lu arg=[%lx] result=%ld\t%pF\n",
-                                      i+1, b->mcidx,
+                               pr_err("  call %2d: op=%lu arg=[%lx] result=%ld\t%pF\n",
+                                      i + 1,
                                       b->debug[i].op,
                                       b->debug[i].args[0],
                                       b->entries[i].result,
                                       b->caller[i]);
+#else
+                               pr_err("  call %2d: op=%lu arg=[%lx] result=%ld\n",
+                                      i + 1,
+                                      b->entries[i].op,
+                                      b->entries[i].args[0],
+                                      b->entries[i].result);
+#endif
                        }
                }
-#endif
        }
 
        b->mcidx = 0;
@@ -126,8 +133,6 @@ void xen_mc_flush(void)
        b->cbidx = 0;
 
        local_irq_restore(flags);
-
-       WARN_ON(ret);
 }
 
 struct multicall_space __xen_mc_entry(size_t args)
index 1163e33..075ed47 100644 (file)
@@ -808,6 +808,7 @@ char * __init xen_memory_setup(void)
        addr = xen_e820_table.entries[0].addr;
        size = xen_e820_table.entries[0].size;
        while (i < xen_e820_table.nr_entries) {
+               bool discard = false;
 
                chunk_size = size;
                type = xen_e820_table.entries[i].type;
@@ -823,10 +824,11 @@ char * __init xen_memory_setup(void)
                                xen_add_extra_mem(pfn_s, n_pfns);
                                xen_max_p2m_pfn = pfn_s + n_pfns;
                        } else
-                               type = E820_TYPE_UNUSABLE;
+                               discard = true;
                }
 
-               xen_align_and_add_e820_region(addr, chunk_size, type);
+               if (!discard)
+                       xen_align_and_add_e820_region(addr, chunk_size, type);
 
                addr += chunk_size;
                size -= chunk_size;
index 1c8a881..3776122 100644 (file)
@@ -3,22 +3,17 @@
  * Split spinlock implementation out into its own file, so it can be
  * compiled in a FTRACE-compatible way.
  */
-#include <linux/kernel_stat.h>
+#include <linux/kernel.h>
 #include <linux/spinlock.h>
-#include <linux/debugfs.h>
-#include <linux/log2.h>
-#include <linux/gfp.h>
 #include <linux/slab.h>
 #include <linux/atomic.h>
 
 #include <asm/paravirt.h>
 #include <asm/qspinlock.h>
 
-#include <xen/interface/xen.h>
 #include <xen/events.h>
 
 #include "xen-ops.h"
-#include "debugfs.h"
 
 static DEFINE_PER_CPU(int, lock_kicker_irq) = -1;
 static DEFINE_PER_CPU(char *, irq_name);
index be9bfd9..34a2301 100644 (file)
 # error Linux requires the Xtensa Windowed Registers Option.
 #endif
 
-#define ARCH_SLAB_MINALIGN     XCHAL_DATA_WIDTH
+/* Xtensa ABI requires stack alignment to be at least 16 */
+
+#define STACK_ALIGN (XCHAL_DATA_WIDTH > 16 ? XCHAL_DATA_WIDTH : 16)
+
+#define ARCH_SLAB_MINALIGN STACK_ALIGN
 
 /*
  * User space process size: 1 GB.
index 67904f5..120dd74 100644 (file)
@@ -94,14 +94,14 @@ int main(void)
        DEFINE(THREAD_SP, offsetof (struct task_struct, thread.sp));
        DEFINE(THREAD_CPENABLE, offsetof (struct thread_info, cpenable));
 #if XTENSA_HAVE_COPROCESSORS
-       DEFINE(THREAD_XTREGS_CP0, offsetof (struct thread_info, xtregs_cp));
-       DEFINE(THREAD_XTREGS_CP1, offsetof (struct thread_info, xtregs_cp));
-       DEFINE(THREAD_XTREGS_CP2, offsetof (struct thread_info, xtregs_cp));
-       DEFINE(THREAD_XTREGS_CP3, offsetof (struct thread_info, xtregs_cp));
-       DEFINE(THREAD_XTREGS_CP4, offsetof (struct thread_info, xtregs_cp));
-       DEFINE(THREAD_XTREGS_CP5, offsetof (struct thread_info, xtregs_cp));
-       DEFINE(THREAD_XTREGS_CP6, offsetof (struct thread_info, xtregs_cp));
-       DEFINE(THREAD_XTREGS_CP7, offsetof (struct thread_info, xtregs_cp));
+       DEFINE(THREAD_XTREGS_CP0, offsetof(struct thread_info, xtregs_cp.cp0));
+       DEFINE(THREAD_XTREGS_CP1, offsetof(struct thread_info, xtregs_cp.cp1));
+       DEFINE(THREAD_XTREGS_CP2, offsetof(struct thread_info, xtregs_cp.cp2));
+       DEFINE(THREAD_XTREGS_CP3, offsetof(struct thread_info, xtregs_cp.cp3));
+       DEFINE(THREAD_XTREGS_CP4, offsetof(struct thread_info, xtregs_cp.cp4));
+       DEFINE(THREAD_XTREGS_CP5, offsetof(struct thread_info, xtregs_cp.cp5));
+       DEFINE(THREAD_XTREGS_CP6, offsetof(struct thread_info, xtregs_cp.cp6));
+       DEFINE(THREAD_XTREGS_CP7, offsetof(struct thread_info, xtregs_cp.cp7));
 #endif
        DEFINE(THREAD_XTREGS_USER, offsetof (struct thread_info, xtregs_user));
        DEFINE(XTREGS_USER_SIZE, sizeof(xtregs_user_t));
index 2f76118..9053a56 100644 (file)
@@ -88,9 +88,12 @@ _SetupMMU:
        initialize_mmu
 #if defined(CONFIG_MMU) && XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY
        rsr     a2, excsave1
-       movi    a3, 0x08000000
+       movi    a3, XCHAL_KSEG_PADDR
+       bltu    a2, a3, 1f
+       sub     a2, a2, a3
+       movi    a3, XCHAL_KSEG_SIZE
        bgeu    a2, a3, 1f
-       movi    a3, 0xd0000000
+       movi    a3, XCHAL_KSEG_CACHED_VADDR
        add     a2, a2, a3
        wsr     a2, excsave1
 1:
index 483dcfb..4bb6813 100644 (file)
@@ -94,18 +94,21 @@ void coprocessor_release_all(struct thread_info *ti)
 
 void coprocessor_flush_all(struct thread_info *ti)
 {
-       unsigned long cpenable;
+       unsigned long cpenable, old_cpenable;
        int i;
 
        preempt_disable();
 
+       RSR_CPENABLE(old_cpenable);
        cpenable = ti->cpenable;
+       WSR_CPENABLE(cpenable);
 
        for (i = 0; i < XCHAL_CP_MAX; i++) {
                if ((cpenable & 1) != 0 && coprocessor_owner[i] == ti)
                        coprocessor_flush(ti, i);
                cpenable >>= 1;
        }
+       WSR_CPENABLE(old_cpenable);
 
        preempt_enable();
 }
index c0845cb..d9541be 100644 (file)
@@ -127,12 +127,37 @@ static int ptrace_setregs(struct task_struct *child, void __user *uregs)
 }
 
 
+#if XTENSA_HAVE_COPROCESSORS
+#define CP_OFFSETS(cp) \
+       { \
+               .elf_xtregs_offset = offsetof(elf_xtregs_t, cp), \
+               .ti_offset = offsetof(struct thread_info, xtregs_cp.cp), \
+               .sz = sizeof(xtregs_ ## cp ## _t), \
+       }
+
+static const struct {
+       size_t elf_xtregs_offset;
+       size_t ti_offset;
+       size_t sz;
+} cp_offsets[] = {
+       CP_OFFSETS(cp0),
+       CP_OFFSETS(cp1),
+       CP_OFFSETS(cp2),
+       CP_OFFSETS(cp3),
+       CP_OFFSETS(cp4),
+       CP_OFFSETS(cp5),
+       CP_OFFSETS(cp6),
+       CP_OFFSETS(cp7),
+};
+#endif
+
 static int ptrace_getxregs(struct task_struct *child, void __user *uregs)
 {
        struct pt_regs *regs = task_pt_regs(child);
        struct thread_info *ti = task_thread_info(child);
        elf_xtregs_t __user *xtregs = uregs;
        int ret = 0;
+       int i __maybe_unused;
 
        if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t)))
                return -EIO;
@@ -140,8 +165,13 @@ static int ptrace_getxregs(struct task_struct *child, void __user *uregs)
 #if XTENSA_HAVE_COPROCESSORS
        /* Flush all coprocessor registers to memory. */
        coprocessor_flush_all(ti);
-       ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp,
-                             sizeof(xtregs_coprocessor_t));
+
+       for (i = 0; i < ARRAY_SIZE(cp_offsets); ++i)
+               ret |= __copy_to_user((char __user *)xtregs +
+                                     cp_offsets[i].elf_xtregs_offset,
+                                     (const char *)ti +
+                                     cp_offsets[i].ti_offset,
+                                     cp_offsets[i].sz);
 #endif
        ret |= __copy_to_user(&xtregs->opt, &regs->xtregs_opt,
                              sizeof(xtregs->opt));
@@ -157,6 +187,7 @@ static int ptrace_setxregs(struct task_struct *child, void __user *uregs)
        struct pt_regs *regs = task_pt_regs(child);
        elf_xtregs_t *xtregs = uregs;
        int ret = 0;
+       int i __maybe_unused;
 
        if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t)))
                return -EFAULT;
@@ -166,8 +197,11 @@ static int ptrace_setxregs(struct task_struct *child, void __user *uregs)
        coprocessor_flush_all(ti);
        coprocessor_release_all(ti);
 
-       ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0,
-                               sizeof(xtregs_coprocessor_t));
+       for (i = 0; i < ARRAY_SIZE(cp_offsets); ++i)
+               ret |= __copy_from_user((char *)ti + cp_offsets[i].ti_offset,
+                                       (const char __user *)xtregs +
+                                       cp_offsets[i].elf_xtregs_offset,
+                                       cp_offsets[i].sz);
 #endif
        ret |= __copy_from_user(&regs->xtregs_opt, &xtregs->opt,
                                sizeof(xtregs->opt));
index 3a27d31..9733721 100644 (file)
@@ -638,7 +638,7 @@ static bool bfq_varied_queue_weights_or_active_groups(struct bfq_data *bfqd)
                 bfqd->queue_weights_tree.rb_node->rb_right)
 #ifdef CONFIG_BFQ_GROUP_IOSCHED
               ) ||
-               (bfqd->num_active_groups > 0
+               (bfqd->num_groups_with_pending_reqs > 0
 #endif
               );
 }
@@ -802,7 +802,21 @@ void bfq_weights_tree_remove(struct bfq_data *bfqd,
                         */
                        break;
                }
-               bfqd->num_active_groups--;
+
+               /*
+                * The decrement of num_groups_with_pending_reqs is
+                * not performed immediately upon the deactivation of
+                * entity, but it is delayed to when it also happens
+                * that the first leaf descendant bfqq of entity gets
+                * all its pending requests completed. The following
+                * instructions perform this delayed decrement, if
+                * needed. See the comments on
+                * num_groups_with_pending_reqs for details.
+                */
+               if (entity->in_groups_with_pending_reqs) {
+                       entity->in_groups_with_pending_reqs = false;
+                       bfqd->num_groups_with_pending_reqs--;
+               }
        }
 }
 
@@ -3529,27 +3543,44 @@ static bool bfq_better_to_idle(struct bfq_queue *bfqq)
         * fact, if there are active groups, then, for condition (i)
         * to become false, it is enough that an active group contains
         * more active processes or sub-groups than some other active
-        * group. We address this issue with the following bi-modal
-        * behavior, implemented in the function
+        * group. More precisely, for condition (i) to hold because of
+        * such a group, it is not even necessary that the group is
+        * (still) active: it is sufficient that, even if the group
+        * has become inactive, some of its descendant processes still
+        * have some request already dispatched but still waiting for
+        * completion. In fact, requests have still to be guaranteed
+        * their share of the throughput even after being
+        * dispatched. In this respect, it is easy to show that, if a
+        * group frequently becomes inactive while still having
+        * in-flight requests, and if, when this happens, the group is
+        * not considered in the calculation of whether the scenario
+        * is asymmetric, then the group may fail to be guaranteed its
+        * fair share of the throughput (basically because idling may
+        * not be performed for the descendant processes of the group,
+        * but it had to be).  We address this issue with the
+        * following bi-modal behavior, implemented in the function
         * bfq_symmetric_scenario().
         *
-        * If there are active groups, then the scenario is tagged as
+        * If there are groups with requests waiting for completion
+        * (as commented above, some of these groups may even be
+        * already inactive), then the scenario is tagged as
         * asymmetric, conservatively, without checking any of the
         * conditions (i) and (ii). So the device is idled for bfqq.
         * This behavior matches also the fact that groups are created
-        * exactly if controlling I/O (to preserve bandwidth and
-        * latency guarantees) is a primary concern.
+        * exactly if controlling I/O is a primary concern (to
+        * preserve bandwidth and latency guarantees).
         *
-        * On the opposite end, if there are no active groups, then
-        * only condition (i) is actually controlled, i.e., provided
-        * that condition (i) holds, idling is not performed,
-        * regardless of whether condition (ii) holds. In other words,
-        * only if condition (i) does not hold, then idling is
-        * allowed, and the device tends to be prevented from queueing
-        * many requests, possibly of several processes. Since there
-        * are no active groups, then, to control condition (i) it is
-        * enough to check whether all active queues have the same
-        * weight.
+        * On the opposite end, if there are no groups with requests
+        * waiting for completion, then only condition (i) is actually
+        * controlled, i.e., provided that condition (i) holds, idling
+        * is not performed, regardless of whether condition (ii)
+        * holds. In other words, only if condition (i) does not hold,
+        * then idling is allowed, and the device tends to be
+        * prevented from queueing many requests, possibly of several
+        * processes. Since there are no groups with requests waiting
+        * for completion, then, to control condition (i) it is enough
+        * to check just whether all the queues with requests waiting
+        * for completion also have the same weight.
         *
         * Not checking condition (ii) evidently exposes bfqq to the
         * risk of getting less throughput than its fair share.
@@ -3607,10 +3638,11 @@ static bool bfq_better_to_idle(struct bfq_queue *bfqq)
         * bfqq is weight-raised is checked explicitly here. More
         * precisely, the compound condition below takes into account
         * also the fact that, even if bfqq is being weight-raised,
-        * the scenario is still symmetric if all active queues happen
-        * to be weight-raised. Actually, we should be even more
-        * precise here, and differentiate between interactive weight
-        * raising and soft real-time weight raising.
+        * the scenario is still symmetric if all queues with requests
+        * waiting for completion happen to be
+        * weight-raised. Actually, we should be even more precise
+        * here, and differentiate between interactive weight raising
+        * and soft real-time weight raising.
         *
         * As a side note, it is worth considering that the above
         * device-idling countermeasures may however fail in the
@@ -5417,7 +5449,7 @@ static int bfq_init_queue(struct request_queue *q, struct elevator_type *e)
        bfqd->idle_slice_timer.function = bfq_idle_slice_timer;
 
        bfqd->queue_weights_tree = RB_ROOT;
-       bfqd->num_active_groups = 0;
+       bfqd->num_groups_with_pending_reqs = 0;
 
        INIT_LIST_HEAD(&bfqd->active_list);
        INIT_LIST_HEAD(&bfqd->idle_list);
index 77651d8..0b02bf3 100644 (file)
@@ -196,6 +196,9 @@ struct bfq_entity {
 
        /* flag, set to request a weight, ioprio or ioprio_class change  */
        int prio_changed;
+
+       /* flag, set if the entity is counted in groups_with_pending_reqs */
+       bool in_groups_with_pending_reqs;
 };
 
 struct bfq_group;
@@ -448,10 +451,54 @@ struct bfq_data {
         * bfq_weights_tree_[add|remove] for further details).
         */
        struct rb_root queue_weights_tree;
+
        /*
-        * number of groups with requests still waiting for completion
+        * Number of groups with at least one descendant process that
+        * has at least one request waiting for completion. Note that
+        * this accounts for also requests already dispatched, but not
+        * yet completed. Therefore this number of groups may differ
+        * (be larger) than the number of active groups, as a group is
+        * considered active only if its corresponding entity has
+        * descendant queues with at least one request queued. This
+        * number is used to decide whether a scenario is symmetric.
+        * For a detailed explanation see comments on the computation
+        * of the variable asymmetric_scenario in the function
+        * bfq_better_to_idle().
+        *
+        * However, it is hard to compute this number exactly, for
+        * groups with multiple descendant processes. Consider a group
+        * that is inactive, i.e., that has no descendant process with
+        * pending I/O inside BFQ queues. Then suppose that
+        * num_groups_with_pending_reqs is still accounting for this
+        * group, because the group has descendant processes with some
+        * I/O request still in flight. num_groups_with_pending_reqs
+        * should be decremented when the in-flight request of the
+        * last descendant process is finally completed (assuming that
+        * nothing else has changed for the group in the meantime, in
+        * terms of composition of the group and active/inactive state of child
+        * groups and processes). To accomplish this, an additional
+        * pending-request counter must be added to entities, and must
+        * be updated correctly. To avoid this additional field and operations,
+        * we resort to the following tradeoff between simplicity and
+        * accuracy: for an inactive group that is still counted in
+        * num_groups_with_pending_reqs, we decrement
+        * num_groups_with_pending_reqs when the first descendant
+        * process of the group remains with no request waiting for
+        * completion.
+        *
+        * Even this simpler decrement strategy requires a little
+        * carefulness: to avoid multiple decrements, we flag a group,
+        * more precisely an entity representing a group, as still
+        * counted in num_groups_with_pending_reqs when it becomes
+        * inactive. Then, when the first descendant queue of the
+        * entity remains with no request waiting for completion,
+        * num_groups_with_pending_reqs is decremented, and this flag
+        * is reset. After this flag is reset for the entity,
+        * num_groups_with_pending_reqs won't be decremented any
+        * longer in case a new descendant queue of the entity remains
+        * with no request waiting for completion.
         */
-       unsigned int num_active_groups;
+       unsigned int num_groups_with_pending_reqs;
 
        /*
         * Number of bfq_queues containing requests (including the
index 4b0d5fb..63e0f12 100644 (file)
@@ -1012,7 +1012,10 @@ static void __bfq_activate_entity(struct bfq_entity *entity,
                        container_of(entity, struct bfq_group, entity);
                struct bfq_data *bfqd = bfqg->bfqd;
 
-               bfqd->num_active_groups++;
+               if (!entity->in_groups_with_pending_reqs) {
+                       entity->in_groups_with_pending_reqs = true;
+                       bfqd->num_groups_with_pending_reqs++;
+               }
        }
 #endif
 
index a50d592..4d86e90 100644 (file)
@@ -605,6 +605,7 @@ void __bio_clone_fast(struct bio *bio, struct bio *bio_src)
        if (bio_flagged(bio_src, BIO_THROTTLED))
                bio_set_flag(bio, BIO_THROTTLED);
        bio->bi_opf = bio_src->bi_opf;
+       bio->bi_ioprio = bio_src->bi_ioprio;
        bio->bi_write_hint = bio_src->bi_write_hint;
        bio->bi_iter = bio_src->bi_iter;
        bio->bi_io_vec = bio_src->bi_io_vec;
@@ -1260,7 +1261,8 @@ struct bio *bio_copy_user_iov(struct request_queue *q,
                if (ret)
                        goto cleanup;
        } else {
-               zero_fill_bio(bio);
+               if (bmd->is_our_pages)
+                       zero_fill_bio(bio);
                iov_iter_advance(iter, bio->bi_iter.bi_size);
        }
 
index ce12515..deb5693 100644 (file)
@@ -798,9 +798,8 @@ void blk_cleanup_queue(struct request_queue *q)
         * dispatch may still be in-progress since we dispatch requests
         * from more than one contexts.
         *
-        * No need to quiesce queue if it isn't initialized yet since
-        * blk_freeze_queue() should be enough for cases of passthrough
-        * request.
+        * We rely on driver to deal with the race in case that queue
+        * initialization isn't done.
         */
        if (q->mq_ops && blk_queue_init_done(q))
                blk_mq_quiesce_queue(q);
index e8b3bb9..5f2c429 100644 (file)
@@ -55,9 +55,11 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
                return -EINVAL;
 
        while (nr_sects) {
-               unsigned int req_sects = min_t(unsigned int, nr_sects,
+               sector_t req_sects = min_t(sector_t, nr_sects,
                                bio_allowed_max_sectors(q));
 
+               WARN_ON_ONCE((req_sects << 9) > UINT_MAX);
+
                bio = blk_next_bio(bio, 0, gfp_mask);
                bio->bi_iter.bi_sector = sector;
                bio_set_dev(bio, bdev);
index e7696c4..7695034 100644 (file)
@@ -820,7 +820,7 @@ static struct request *attempt_merge(struct request_queue *q,
 
        req->__data_len += blk_rq_bytes(next);
 
-       if (req_op(req) != REQ_OP_DISCARD)
+       if (!blk_discard_mergable(req))
                elv_merge_requests(q, req, next);
 
        /*
index 3f91c6e..6a75662 100644 (file)
@@ -1764,7 +1764,7 @@ insert:
        if (bypass_insert)
                return BLK_STS_RESOURCE;
 
-       blk_mq_sched_insert_request(rq, false, run_queue, false);
+       blk_mq_request_bypass_insert(rq, run_queue);
        return BLK_STS_OK;
 }
 
@@ -1780,7 +1780,7 @@ static void blk_mq_try_issue_directly(struct blk_mq_hw_ctx *hctx,
 
        ret = __blk_mq_try_issue_directly(hctx, rq, cookie, false);
        if (ret == BLK_STS_RESOURCE || ret == BLK_STS_DEV_RESOURCE)
-               blk_mq_sched_insert_request(rq, false, true, false);
+               blk_mq_request_bypass_insert(rq, true);
        else if (ret != BLK_STS_OK)
                blk_mq_end_request(rq, ret);
 
@@ -1815,7 +1815,8 @@ void blk_mq_try_issue_list_directly(struct blk_mq_hw_ctx *hctx,
                if (ret != BLK_STS_OK) {
                        if (ret == BLK_STS_RESOURCE ||
                                        ret == BLK_STS_DEV_RESOURCE) {
-                               list_add(&rq->queuelist, list);
+                               blk_mq_request_bypass_insert(rq,
+                                                       list_empty(list));
                                break;
                        }
                        blk_mq_end_request(rq, ret);
index 13ba201..a327bef 100644 (file)
@@ -378,7 +378,7 @@ static struct blk_zone *blk_alloc_zones(int node, unsigned int *nr_zones)
        struct page *page;
        int order;
 
-       for (order = get_order(size); order > 0; order--) {
+       for (order = get_order(size); order >= 0; order--) {
                page = alloc_pages_node(node, GFP_NOIO | __GFP_ZERO, order);
                if (page) {
                        *nr_zones = min_t(unsigned int, *nr_zones,
index 36869af..559c55b 100644 (file)
@@ -248,6 +248,7 @@ static struct bio *bounce_clone_bio(struct bio *bio_src, gfp_t gfp_mask,
                return NULL;
        bio->bi_disk            = bio_src->bi_disk;
        bio->bi_opf             = bio_src->bi_opf;
+       bio->bi_ioprio          = bio_src->bi_ioprio;
        bio->bi_write_hint      = bio_src->bi_write_hint;
        bio->bi_iter.bi_sector  = bio_src->bi_iter.bi_sector;
        bio->bi_iter.bi_size    = bio_src->bi_iter.bi_size;
index f7a235d..05c91eb 100644 (file)
@@ -1812,7 +1812,7 @@ config CRYPTO_USER_API_AEAD
          cipher algorithms.
 
 config CRYPTO_STATS
-       bool "Crypto usage statistics for User-space"
+       bool
        help
          This option enables the gathering of crypto stats.
          This will collect:
index b761b1f..dd5f332 100644 (file)
@@ -140,9 +140,8 @@ static int crypto_cbc_create(struct crypto_template *tmpl, struct rtattr **tb)
        spawn = skcipher_instance_ctx(inst);
        err = crypto_init_spawn(spawn, alg, skcipher_crypto_instance(inst),
                                CRYPTO_ALG_TYPE_MASK);
-       crypto_mod_put(alg);
        if (err)
-               goto err_free_inst;
+               goto err_put_alg;
 
        err = crypto_inst_setname(skcipher_crypto_instance(inst), "cbc", alg);
        if (err)
@@ -174,12 +173,15 @@ static int crypto_cbc_create(struct crypto_template *tmpl, struct rtattr **tb)
        err = skcipher_register_instance(tmpl, inst);
        if (err)
                goto err_drop_spawn;
+       crypto_mod_put(alg);
 
 out:
        return err;
 
 err_drop_spawn:
        crypto_drop_spawn(spawn);
+err_put_alg:
+       crypto_mod_put(alg);
 err_free_inst:
        kfree(inst);
        goto out;
index a0d68c0..20987d0 100644 (file)
@@ -286,9 +286,8 @@ static int crypto_cfb_create(struct crypto_template *tmpl, struct rtattr **tb)
        spawn = skcipher_instance_ctx(inst);
        err = crypto_init_spawn(spawn, alg, skcipher_crypto_instance(inst),
                                CRYPTO_ALG_TYPE_MASK);
-       crypto_mod_put(alg);
        if (err)
-               goto err_free_inst;
+               goto err_put_alg;
 
        err = crypto_inst_setname(skcipher_crypto_instance(inst), "cfb", alg);
        if (err)
@@ -317,12 +316,15 @@ static int crypto_cfb_create(struct crypto_template *tmpl, struct rtattr **tb)
        err = skcipher_register_instance(tmpl, inst);
        if (err)
                goto err_drop_spawn;
+       crypto_mod_put(alg);
 
 out:
        return err;
 
 err_drop_spawn:
        crypto_drop_spawn(spawn);
+err_put_alg:
+       crypto_mod_put(alg);
 err_free_inst:
        kfree(inst);
        goto out;
index e41f6cc..784748d 100644 (file)
@@ -84,7 +84,7 @@ static int crypto_report_cipher(struct sk_buff *skb, struct crypto_alg *alg)
 {
        struct crypto_report_cipher rcipher;
 
-       strlcpy(rcipher.type, "cipher", sizeof(rcipher.type));
+       strncpy(rcipher.type, "cipher", sizeof(rcipher.type));
 
        rcipher.blocksize = alg->cra_blocksize;
        rcipher.min_keysize = alg->cra_cipher.cia_min_keysize;
@@ -103,7 +103,7 @@ static int crypto_report_comp(struct sk_buff *skb, struct crypto_alg *alg)
 {
        struct crypto_report_comp rcomp;
 
-       strlcpy(rcomp.type, "compression", sizeof(rcomp.type));
+       strncpy(rcomp.type, "compression", sizeof(rcomp.type));
        if (nla_put(skb, CRYPTOCFGA_REPORT_COMPRESS,
                    sizeof(struct crypto_report_comp), &rcomp))
                goto nla_put_failure;
@@ -117,7 +117,7 @@ static int crypto_report_acomp(struct sk_buff *skb, struct crypto_alg *alg)
 {
        struct crypto_report_acomp racomp;
 
-       strlcpy(racomp.type, "acomp", sizeof(racomp.type));
+       strncpy(racomp.type, "acomp", sizeof(racomp.type));
 
        if (nla_put(skb, CRYPTOCFGA_REPORT_ACOMP,
                    sizeof(struct crypto_report_acomp), &racomp))
@@ -132,7 +132,7 @@ static int crypto_report_akcipher(struct sk_buff *skb, struct crypto_alg *alg)
 {
        struct crypto_report_akcipher rakcipher;
 
-       strlcpy(rakcipher.type, "akcipher", sizeof(rakcipher.type));
+       strncpy(rakcipher.type, "akcipher", sizeof(rakcipher.type));
 
        if (nla_put(skb, CRYPTOCFGA_REPORT_AKCIPHER,
                    sizeof(struct crypto_report_akcipher), &rakcipher))
@@ -147,7 +147,7 @@ static int crypto_report_kpp(struct sk_buff *skb, struct crypto_alg *alg)
 {
        struct crypto_report_kpp rkpp;
 
-       strlcpy(rkpp.type, "kpp", sizeof(rkpp.type));
+       strncpy(rkpp.type, "kpp", sizeof(rkpp.type));
 
        if (nla_put(skb, CRYPTOCFGA_REPORT_KPP,
                    sizeof(struct crypto_report_kpp), &rkpp))
@@ -161,10 +161,10 @@ nla_put_failure:
 static int crypto_report_one(struct crypto_alg *alg,
                             struct crypto_user_alg *ualg, struct sk_buff *skb)
 {
-       strlcpy(ualg->cru_name, alg->cra_name, sizeof(ualg->cru_name));
-       strlcpy(ualg->cru_driver_name, alg->cra_driver_name,
+       strncpy(ualg->cru_name, alg->cra_name, sizeof(ualg->cru_name));
+       strncpy(ualg->cru_driver_name, alg->cra_driver_name,
                sizeof(ualg->cru_driver_name));
-       strlcpy(ualg->cru_module_name, module_name(alg->cra_module),
+       strncpy(ualg->cru_module_name, module_name(alg->cra_module),
                sizeof(ualg->cru_module_name));
 
        ualg->cru_type = 0;
@@ -177,7 +177,7 @@ static int crypto_report_one(struct crypto_alg *alg,
        if (alg->cra_flags & CRYPTO_ALG_LARVAL) {
                struct crypto_report_larval rl;
 
-               strlcpy(rl.type, "larval", sizeof(rl.type));
+               strncpy(rl.type, "larval", sizeof(rl.type));
                if (nla_put(skb, CRYPTOCFGA_REPORT_LARVAL,
                            sizeof(struct crypto_report_larval), &rl))
                        goto nla_put_failure;
index 021ad06..1dfaa0c 100644 (file)
@@ -37,6 +37,8 @@ static int crypto_report_aead(struct sk_buff *skb, struct crypto_alg *alg)
        u64 v64;
        u32 v32;
 
+       memset(&raead, 0, sizeof(raead));
+
        strncpy(raead.type, "aead", sizeof(raead.type));
 
        v32 = atomic_read(&alg->encrypt_cnt);
@@ -65,6 +67,8 @@ static int crypto_report_cipher(struct sk_buff *skb, struct crypto_alg *alg)
        u64 v64;
        u32 v32;
 
+       memset(&rcipher, 0, sizeof(rcipher));
+
        strlcpy(rcipher.type, "cipher", sizeof(rcipher.type));
 
        v32 = atomic_read(&alg->encrypt_cnt);
@@ -93,6 +97,8 @@ static int crypto_report_comp(struct sk_buff *skb, struct crypto_alg *alg)
        u64 v64;
        u32 v32;
 
+       memset(&rcomp, 0, sizeof(rcomp));
+
        strlcpy(rcomp.type, "compression", sizeof(rcomp.type));
        v32 = atomic_read(&alg->compress_cnt);
        rcomp.stat_compress_cnt = v32;
@@ -120,6 +126,8 @@ static int crypto_report_acomp(struct sk_buff *skb, struct crypto_alg *alg)
        u64 v64;
        u32 v32;
 
+       memset(&racomp, 0, sizeof(racomp));
+
        strlcpy(racomp.type, "acomp", sizeof(racomp.type));
        v32 = atomic_read(&alg->compress_cnt);
        racomp.stat_compress_cnt = v32;
@@ -147,6 +155,8 @@ static int crypto_report_akcipher(struct sk_buff *skb, struct crypto_alg *alg)
        u64 v64;
        u32 v32;
 
+       memset(&rakcipher, 0, sizeof(rakcipher));
+
        strncpy(rakcipher.type, "akcipher", sizeof(rakcipher.type));
        v32 = atomic_read(&alg->encrypt_cnt);
        rakcipher.stat_encrypt_cnt = v32;
@@ -177,6 +187,8 @@ static int crypto_report_kpp(struct sk_buff *skb, struct crypto_alg *alg)
        struct crypto_stat rkpp;
        u32 v;
 
+       memset(&rkpp, 0, sizeof(rkpp));
+
        strlcpy(rkpp.type, "kpp", sizeof(rkpp.type));
 
        v = atomic_read(&alg->setsecret_cnt);
@@ -203,6 +215,8 @@ static int crypto_report_ahash(struct sk_buff *skb, struct crypto_alg *alg)
        u64 v64;
        u32 v32;
 
+       memset(&rhash, 0, sizeof(rhash));
+
        strncpy(rhash.type, "ahash", sizeof(rhash.type));
 
        v32 = atomic_read(&alg->hash_cnt);
@@ -227,6 +241,8 @@ static int crypto_report_shash(struct sk_buff *skb, struct crypto_alg *alg)
        u64 v64;
        u32 v32;
 
+       memset(&rhash, 0, sizeof(rhash));
+
        strncpy(rhash.type, "shash", sizeof(rhash.type));
 
        v32 = atomic_read(&alg->hash_cnt);
@@ -251,6 +267,8 @@ static int crypto_report_rng(struct sk_buff *skb, struct crypto_alg *alg)
        u64 v64;
        u32 v32;
 
+       memset(&rrng, 0, sizeof(rrng));
+
        strncpy(rrng.type, "rng", sizeof(rrng.type));
 
        v32 = atomic_read(&alg->generate_cnt);
@@ -275,6 +293,8 @@ static int crypto_reportstat_one(struct crypto_alg *alg,
                                 struct crypto_user_alg *ualg,
                                 struct sk_buff *skb)
 {
+       memset(ualg, 0, sizeof(*ualg));
+
        strlcpy(ualg->cru_name, alg->cra_name, sizeof(ualg->cru_name));
        strlcpy(ualg->cru_driver_name, alg->cra_driver_name,
                sizeof(ualg->cru_driver_name));
@@ -291,6 +311,7 @@ static int crypto_reportstat_one(struct crypto_alg *alg,
        if (alg->cra_flags & CRYPTO_ALG_LARVAL) {
                struct crypto_stat rl;
 
+               memset(&rl, 0, sizeof(rl));
                strlcpy(rl.type, "larval", sizeof(rl.type));
                if (nla_put(skb, CRYPTOCFGA_STAT_LARVAL,
                            sizeof(struct crypto_stat), &rl))
index ef802f6..8aa1014 100644 (file)
@@ -244,9 +244,8 @@ static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb)
        spawn = skcipher_instance_ctx(inst);
        err = crypto_init_spawn(spawn, alg, skcipher_crypto_instance(inst),
                                CRYPTO_ALG_TYPE_MASK);
-       crypto_mod_put(alg);
        if (err)
-               goto err_free_inst;
+               goto err_put_alg;
 
        err = crypto_inst_setname(skcipher_crypto_instance(inst), "pcbc", alg);
        if (err)
@@ -275,12 +274,15 @@ static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb)
        err = skcipher_register_instance(tmpl, inst);
        if (err)
                goto err_drop_spawn;
+       crypto_mod_put(alg);
 
 out:
        return err;
 
 err_drop_spawn:
        crypto_drop_spawn(spawn);
+err_put_alg:
+       crypto_mod_put(alg);
 err_free_inst:
        kfree(inst);
        goto out;
index ea7240b..78e8d03 100644 (file)
@@ -124,8 +124,9 @@ static int simd_skcipher_init(struct crypto_skcipher *tfm)
 
        ctx->cryptd_tfm = cryptd_tfm;
 
-       reqsize = sizeof(struct skcipher_request);
-       reqsize += crypto_skcipher_reqsize(&cryptd_tfm->base);
+       reqsize = crypto_skcipher_reqsize(cryptd_skcipher_child(cryptd_tfm));
+       reqsize = max(reqsize, crypto_skcipher_reqsize(&cryptd_tfm->base));
+       reqsize += sizeof(struct skcipher_request);
 
        crypto_skcipher_set_reqsize(tfm, reqsize);
 
index 8f3a444..7cea769 100644 (file)
@@ -512,7 +512,7 @@ config CRC_PMIC_OPREGION
 
 config XPOWER_PMIC_OPREGION
        bool "ACPI operation region support for XPower AXP288 PMIC"
-       depends on MFD_AXP20X_I2C && IOSF_MBI
+       depends on MFD_AXP20X_I2C && IOSF_MBI=y
        help
          This config adds ACPI operation region support for XPower AXP288 PMIC.
 
index eaa60c9..1f32caa 100644 (file)
@@ -30,6 +30,7 @@ static const struct acpi_device_id forbidden_id_list[] = {
        {"PNP0200",  0},        /* AT DMA Controller */
        {"ACPI0009", 0},        /* IOxAPIC */
        {"ACPI000A", 0},        /* IOAPIC */
+       {"SMB0001",  0},        /* ACPI SMBUS virtual device */
        {"", 0},
 };
 
index 0d42f30..9920fac 100644 (file)
@@ -244,7 +244,6 @@ acpi_ex_write_serial_bus(union acpi_operand_object *source_desc,
 {
        acpi_status status;
        u32 buffer_length;
-       u32 data_length;
        void *buffer;
        union acpi_operand_object *buffer_desc;
        u32 function;
@@ -282,14 +281,12 @@ acpi_ex_write_serial_bus(union acpi_operand_object *source_desc,
        case ACPI_ADR_SPACE_SMBUS:
 
                buffer_length = ACPI_SMBUS_BUFFER_SIZE;
-               data_length = ACPI_SMBUS_DATA_SIZE;
                function = ACPI_WRITE | (obj_desc->field.attribute << 16);
                break;
 
        case ACPI_ADR_SPACE_IPMI:
 
                buffer_length = ACPI_IPMI_BUFFER_SIZE;
-               data_length = ACPI_IPMI_DATA_SIZE;
                function = ACPI_WRITE;
                break;
 
@@ -310,7 +307,6 @@ acpi_ex_write_serial_bus(union acpi_operand_object *source_desc,
                /* Add header length to get the full size of the buffer */
 
                buffer_length += ACPI_SERIAL_HEADER_SIZE;
-               data_length = source_desc->buffer.pointer[1];
                function = ACPI_WRITE | (accessor_type << 16);
                break;
 
@@ -318,20 +314,6 @@ acpi_ex_write_serial_bus(union acpi_operand_object *source_desc,
                return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
        }
 
-#if 0
-       OBSOLETE ?
-           /* Check for possible buffer overflow */
-           if (data_length > source_desc->buffer.length) {
-               ACPI_ERROR((AE_INFO,
-                           "Length in buffer header (%u)(%u) is greater than "
-                           "the physical buffer length (%u) and will overflow",
-                           data_length, buffer_length,
-                           source_desc->buffer.length));
-
-               return_ACPI_STATUS(AE_AML_BUFFER_LIMIT);
-       }
-#endif
-
        /* Create the transfer/bidirectional/return buffer */
 
        buffer_desc = acpi_ut_create_buffer_object(buffer_length);
@@ -342,7 +324,8 @@ acpi_ex_write_serial_bus(union acpi_operand_object *source_desc,
        /* Copy the input buffer data to the transfer buffer */
 
        buffer = buffer_desc->buffer.pointer;
-       memcpy(buffer, source_desc->buffer.pointer, data_length);
+       memcpy(buffer, source_desc->buffer.pointer,
+              min(buffer_length, source_desc->buffer.length));
 
        /* Lock entire transaction if requested */
 
index 2a361e2..70f4e80 100644 (file)
@@ -700,7 +700,7 @@ static void iort_set_device_domain(struct device *dev,
  */
 static struct irq_domain *iort_get_platform_device_domain(struct device *dev)
 {
-       struct acpi_iort_node *node, *msi_parent;
+       struct acpi_iort_node *node, *msi_parent = NULL;
        struct fwnode_handle *iort_fwnode;
        struct acpi_iort_its_group *its;
        int i;
index f8c638f..5912d30 100644 (file)
@@ -1308,7 +1308,7 @@ static ssize_t scrub_store(struct device *dev,
        if (nd_desc) {
                struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
 
-               rc = acpi_nfit_ars_rescan(acpi_desc, 0);
+               rc = acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG);
        }
        device_unlock(dev);
        if (rc)
@@ -2928,9 +2928,9 @@ static int acpi_nfit_query_poison(struct acpi_nfit_desc *acpi_desc)
                return rc;
 
        if (ars_status_process_records(acpi_desc))
-               return -ENOMEM;
+               dev_err(acpi_desc->dev, "Failed to process ARS records\n");
 
-       return 0;
+       return rc;
 }
 
 static int ars_register(struct acpi_nfit_desc *acpi_desc,
@@ -3341,8 +3341,6 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
                struct nvdimm *nvdimm, unsigned int cmd)
 {
        struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
-       struct nfit_spa *nfit_spa;
-       int rc = 0;
 
        if (nvdimm)
                return 0;
@@ -3355,17 +3353,10 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
         * just needs guarantees that any ARS it initiates are not
         * interrupted by any intervening start requests from userspace.
         */
-       mutex_lock(&acpi_desc->init_mutex);
-       list_for_each_entry(nfit_spa, &acpi_desc->spas, list)
-               if (acpi_desc->scrub_spa
-                               || test_bit(ARS_REQ_SHORT, &nfit_spa->ars_state)
-                               || test_bit(ARS_REQ_LONG, &nfit_spa->ars_state)) {
-                       rc = -EBUSY;
-                       break;
-               }
-       mutex_unlock(&acpi_desc->init_mutex);
+       if (work_busy(&acpi_desc->dwork.work))
+               return -EBUSY;
 
-       return rc;
+       return 0;
 }
 
 int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc,
index cb30a52..9f1000d 100644 (file)
@@ -2974,7 +2974,6 @@ static void binder_transaction(struct binder_proc *proc,
                t->buffer = NULL;
                goto err_binder_alloc_buf_failed;
        }
-       t->buffer->allow_user_free = 0;
        t->buffer->debug_id = t->debug_id;
        t->buffer->transaction = t;
        t->buffer->target_node = target_node;
@@ -3510,14 +3509,18 @@ static int binder_thread_write(struct binder_proc *proc,
 
                        buffer = binder_alloc_prepare_to_free(&proc->alloc,
                                                              data_ptr);
-                       if (buffer == NULL) {
-                               binder_user_error("%d:%d BC_FREE_BUFFER u%016llx no match\n",
-                                       proc->pid, thread->pid, (u64)data_ptr);
-                               break;
-                       }
-                       if (!buffer->allow_user_free) {
-                               binder_user_error("%d:%d BC_FREE_BUFFER u%016llx matched unreturned buffer\n",
-                                       proc->pid, thread->pid, (u64)data_ptr);
+                       if (IS_ERR_OR_NULL(buffer)) {
+                               if (PTR_ERR(buffer) == -EPERM) {
+                                       binder_user_error(
+                                               "%d:%d BC_FREE_BUFFER u%016llx matched unreturned or currently freeing buffer\n",
+                                               proc->pid, thread->pid,
+                                               (u64)data_ptr);
+                               } else {
+                                       binder_user_error(
+                                               "%d:%d BC_FREE_BUFFER u%016llx no match\n",
+                                               proc->pid, thread->pid,
+                                               (u64)data_ptr);
+                               }
                                break;
                        }
                        binder_debug(BINDER_DEBUG_FREE_BUFFER,
index 64fd96e..030c98f 100644 (file)
@@ -151,16 +151,12 @@ static struct binder_buffer *binder_alloc_prepare_to_free_locked(
                else {
                        /*
                         * Guard against user threads attempting to
-                        * free the buffer twice
+                        * free the buffer when in use by kernel or
+                        * after it's already been freed.
                         */
-                       if (buffer->free_in_progress) {
-                               binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
-                                                  "%d:%d FREE_BUFFER u%016llx user freed buffer twice\n",
-                                                  alloc->pid, current->pid,
-                                                  (u64)user_ptr);
-                               return NULL;
-                       }
-                       buffer->free_in_progress = 1;
+                       if (!buffer->allow_user_free)
+                               return ERR_PTR(-EPERM);
+                       buffer->allow_user_free = 0;
                        return buffer;
                }
        }
@@ -500,7 +496,7 @@ static struct binder_buffer *binder_alloc_new_buf_locked(
 
        rb_erase(best_fit, &alloc->free_buffers);
        buffer->free = 0;
-       buffer->free_in_progress = 0;
+       buffer->allow_user_free = 0;
        binder_insert_allocated_buffer_locked(alloc, buffer);
        binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
                     "%d: binder_alloc_buf size %zd got %pK\n",
index 9ef64e5..fb3238c 100644 (file)
@@ -50,8 +50,7 @@ struct binder_buffer {
        unsigned free:1;
        unsigned allow_user_free:1;
        unsigned async_transaction:1;
-       unsigned free_in_progress:1;
-       unsigned debug_id:28;
+       unsigned debug_id:29;
 
        struct binder_transaction *transaction;
 
index 6e59464..b8c3f9e 100644 (file)
@@ -4553,7 +4553,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
        /* These specific Samsung models/firmware-revs do not handle LPM well */
        { "SAMSUNG MZMPC128HBFU-000MV", "CXM14M1Q", ATA_HORKAGE_NOLPM, },
        { "SAMSUNG SSD PM830 mSATA *",  "CXM13D1Q", ATA_HORKAGE_NOLPM, },
-       { "SAMSUNG MZ7TD256HAFV-000L9", "DXT02L5Q", ATA_HORKAGE_NOLPM, },
+       { "SAMSUNG MZ7TD256HAFV-000L9", NULL,       ATA_HORKAGE_NOLPM, },
 
        /* devices that don't properly handle queued TRIM commands */
        { "Micron_M500IT_*",            "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
@@ -4602,6 +4602,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
        { "SSD*INTEL*",                 NULL,   ATA_HORKAGE_ZERO_AFTER_TRIM, },
        { "Samsung*SSD*",               NULL,   ATA_HORKAGE_ZERO_AFTER_TRIM, },
        { "SAMSUNG*SSD*",               NULL,   ATA_HORKAGE_ZERO_AFTER_TRIM, },
+       { "SAMSUNG*MZ7KM*",             NULL,   ATA_HORKAGE_ZERO_AFTER_TRIM, },
        { "ST[1248][0248]0[FH]*",       NULL,   ATA_HORKAGE_ZERO_AFTER_TRIM, },
 
        /*
index 4e46dc9..11e1663 100644 (file)
@@ -1410,7 +1410,7 @@ static int init_q(struct fs_dev *dev, struct queue *txq, int queue,
 
        func_enter ();
 
-       fs_dprintk (FS_DEBUG_INIT, "Inititing queue at %x: %d entries:\n", 
+       fs_dprintk (FS_DEBUG_INIT, "Initializing queue at %x: %d entries:\n",
                    queue, nentries);
 
        p = aligned_kmalloc (sz, GFP_KERNEL, 0x10);
@@ -1443,7 +1443,7 @@ static int init_fp(struct fs_dev *dev, struct freepool *fp, int queue,
 {
        func_enter ();
 
-       fs_dprintk (FS_DEBUG_INIT, "Inititing free pool at %x:\n", queue);
+       fs_dprintk (FS_DEBUG_INIT, "Initializing free pool at %x:\n", queue);
 
        write_fs (dev, FP_CNF(queue), (bufsize * RBFP_RBS) | RBFP_RBSVAL | RBFP_CME);
        write_fs (dev, FP_SA(queue),  0);
index f55ffde..14053e0 100644 (file)
@@ -754,8 +754,8 @@ static int fore200e_sba_proc_read(struct fore200e *fore200e, char *page)
 
        regs = of_get_property(op->dev.of_node, "reg", NULL);
 
-       return sprintf(page, "   SBUS slot/device:\t\t%d/'%s'\n",
-                      (regs ? regs->which_io : 0), op->dev.of_node->name);
+       return sprintf(page, "   SBUS slot/device:\t\t%d/'%pOFn'\n",
+                      (regs ? regs->which_io : 0), op->dev.of_node);
 }
 
 static const struct fore200e_bus fore200e_sbus_ops = {
index 4aaf00d..e038e2b 100644 (file)
@@ -26,8 +26,14 @@ struct devres_node {
 
 struct devres {
        struct devres_node              node;
-       /* -- 3 pointers */
-       unsigned long long              data[]; /* guarantee ull alignment */
+       /*
+        * Some archs want to perform DMA into kmalloc caches
+        * and need a guaranteed alignment larger than
+        * the alignment of a 64-bit integer.
+        * Thus we use ARCH_KMALLOC_MINALIGN here and get exactly the same
+        * buffer alignment as if it was allocated by plain kmalloc().
+        */
+       u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];
 };
 
 struct devres_group {
index a8cfa01..fb23578 100644 (file)
@@ -4148,10 +4148,11 @@ static int __floppy_read_block_0(struct block_device *bdev, int drive)
        bio.bi_end_io = floppy_rb0_cb;
        bio_set_op_attrs(&bio, REQ_OP_READ, 0);
 
+       init_completion(&cbdata.complete);
+
        submit_bio(&bio);
        process_fd_request();
 
-       init_completion(&cbdata.complete);
        wait_for_completion(&cbdata.complete);
 
        __free_page(page);
index e3e4d92..d5d6e6e 100644 (file)
@@ -33,6 +33,8 @@
 #define VERSION "0.1"
 
 #define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
+#define BDADDR_BCM20702A1 (&(bdaddr_t) {{0x00, 0x00, 0xa0, 0x02, 0x70, 0x20}})
+#define BDADDR_BCM43430A0 (&(bdaddr_t) {{0xac, 0x1f, 0x12, 0xa0, 0x43, 0x43}})
 #define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
 #define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}})
 
@@ -64,15 +66,23 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
         * The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
         * with no configured address.
         *
+        * The address 20:70:02:A0:00:00 indicates a BCM20702A1 controller
+        * with no configured address.
+        *
         * The address 43:24:B3:00:00:00 indicates a BCM4324B3 controller
         * with waiting for configuration state.
         *
         * The address 43:30:B1:00:00:00 indicates a BCM4330B1 controller
         * with waiting for configuration state.
+        *
+        * The address 43:43:A0:12:1F:AC indicates a BCM43430A0 controller
+        * with no configured address.
         */
        if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) ||
+           !bacmp(&bda->bdaddr, BDADDR_BCM20702A1) ||
            !bacmp(&bda->bdaddr, BDADDR_BCM4324B3) ||
-           !bacmp(&bda->bdaddr, BDADDR_BCM4330B1)) {
+           !bacmp(&bda->bdaddr, BDADDR_BCM4330B1) ||
+           !bacmp(&bda->bdaddr, BDADDR_BCM43430A0)) {
                bt_dev_info(hdev, "BCM: Using default device address (%pMR)",
                            &bda->bdaddr);
                set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
@@ -330,6 +340,8 @@ static const struct bcm_subver_table bcm_uart_subver_table[] = {
        { 0x2209, "BCM43430A1"  },      /* 001.002.009 */
        { 0x6119, "BCM4345C0"   },      /* 003.001.025 */
        { 0x230f, "BCM4356A2"   },      /* 001.003.015 */
+       { 0x220e, "BCM20702A1"  },      /* 001.002.014 */
+       { 0x4217, "BCM4329B1"   },      /* 002.002.023 */
        { }
 };
 
index 7439a7e..4761499 100644 (file)
@@ -344,6 +344,7 @@ static const struct usb_device_id blacklist_table[] = {
        /* Intel Bluetooth devices */
        { USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_NEW },
        { USB_DEVICE(0x8087, 0x0026), .driver_info = BTUSB_INTEL_NEW },
+       { USB_DEVICE(0x8087, 0x0029), .driver_info = BTUSB_INTEL_NEW },
        { USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
        { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
        { USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
@@ -1935,10 +1936,8 @@ static void btusb_intel_bootup(struct btusb_data *data, const void *ptr,
        if (len != sizeof(*evt))
                return;
 
-       if (test_and_clear_bit(BTUSB_BOOTING, &data->flags)) {
-               smp_mb__after_atomic();
+       if (test_and_clear_bit(BTUSB_BOOTING, &data->flags))
                wake_up_bit(&data->flags, BTUSB_BOOTING);
-       }
 }
 
 static void btusb_intel_secure_send_result(struct btusb_data *data,
@@ -1953,10 +1952,8 @@ static void btusb_intel_secure_send_result(struct btusb_data *data,
                set_bit(BTUSB_FIRMWARE_FAILED, &data->flags);
 
        if (test_and_clear_bit(BTUSB_DOWNLOADING, &data->flags) &&
-           test_bit(BTUSB_FIRMWARE_LOADED, &data->flags)) {
-               smp_mb__after_atomic();
+           test_bit(BTUSB_FIRMWARE_LOADED, &data->flags))
                wake_up_bit(&data->flags, BTUSB_DOWNLOADING);
-       }
 }
 
 static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
@@ -2055,6 +2052,35 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
        return -EILSEQ;
 }
 
+static bool btusb_setup_intel_new_get_fw_name(struct intel_version *ver,
+                                            struct intel_boot_params *params,
+                                            char *fw_name, size_t len,
+                                            const char *suffix)
+{
+       switch (ver->hw_variant) {
+       case 0x0b:      /* SfP */
+       case 0x0c:      /* WsP */
+               snprintf(fw_name, len, "intel/ibt-%u-%u.%s",
+                       le16_to_cpu(ver->hw_variant),
+                       le16_to_cpu(params->dev_revid),
+                       suffix);
+               break;
+       case 0x11:      /* JfP */
+       case 0x12:      /* ThP */
+       case 0x13:      /* HrP */
+       case 0x14:      /* CcP */
+               snprintf(fw_name, len, "intel/ibt-%u-%u-%u.%s",
+                       le16_to_cpu(ver->hw_variant),
+                       le16_to_cpu(ver->hw_revision),
+                       le16_to_cpu(ver->fw_revision),
+                       suffix);
+               break;
+       default:
+               return false;
+       }
+       return true;
+}
+
 static int btusb_setup_intel_new(struct hci_dev *hdev)
 {
        struct btusb_data *data = hci_get_drvdata(hdev);
@@ -2106,7 +2132,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
        case 0x11:      /* JfP */
        case 0x12:      /* ThP */
        case 0x13:      /* HrP */
-       case 0x14:      /* QnJ, IcP */
+       case 0x14:      /* CcP */
                break;
        default:
                bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)",
@@ -2190,23 +2216,9 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
         * ibt-<hw_variant>-<hw_revision>-<fw_revision>.sfi.
         *
         */
-       switch (ver.hw_variant) {
-       case 0x0b:      /* SfP */
-       case 0x0c:      /* WsP */
-               snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u.sfi",
-                        le16_to_cpu(ver.hw_variant),
-                        le16_to_cpu(params.dev_revid));
-               break;
-       case 0x11:      /* JfP */
-       case 0x12:      /* ThP */
-       case 0x13:      /* HrP */
-       case 0x14:      /* QnJ, IcP */
-               snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u-%u.sfi",
-                        le16_to_cpu(ver.hw_variant),
-                        le16_to_cpu(ver.hw_revision),
-                        le16_to_cpu(ver.fw_revision));
-               break;
-       default:
+       err = btusb_setup_intel_new_get_fw_name(&ver, &params, fwname,
+                                               sizeof(fwname), "sfi");
+       if (!err) {
                bt_dev_err(hdev, "Unsupported Intel firmware naming");
                return -EINVAL;
        }
@@ -2222,23 +2234,9 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
        /* Save the DDC file name for later use to apply once the firmware
         * downloading is done.
         */
-       switch (ver.hw_variant) {
-       case 0x0b:      /* SfP */
-       case 0x0c:      /* WsP */
-               snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u.ddc",
-                        le16_to_cpu(ver.hw_variant),
-                        le16_to_cpu(params.dev_revid));
-               break;
-       case 0x11:      /* JfP */
-       case 0x12:      /* ThP */
-       case 0x13:      /* HrP */
-       case 0x14:      /* QnJ, IcP */
-               snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u-%u.ddc",
-                        le16_to_cpu(ver.hw_variant),
-                        le16_to_cpu(ver.hw_revision),
-                        le16_to_cpu(ver.fw_revision));
-               break;
-       default:
+       err = btusb_setup_intel_new_get_fw_name(&ver, &params, fwname,
+                                               sizeof(fwname), "ddc");
+       if (!err) {
                bt_dev_err(hdev, "Unsupported Intel firmware naming");
                return -EINVAL;
        }
index ddbd8c6..ddbe518 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/property.h>
 #include <linux/platform_data/x86/apple.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
 #include <linux/clk.h>
 #include <linux/gpio/consumer.h>
 #include <linux/tty.h>
 #define BCM_LM_DIAG_PKT 0x07
 #define BCM_LM_DIAG_SIZE 63
 
+#define BCM_TYPE49_PKT 0x31
+#define BCM_TYPE49_SIZE 0
+
+#define BCM_TYPE52_PKT 0x34
+#define BCM_TYPE52_SIZE 0
+
 #define BCM_AUTOSUSPEND_DELAY  5000 /* default autosleep delay */
 
+#define BCM_NUM_SUPPLIES 2
+
 /**
  * struct bcm_device - device driver resources
  * @serdev_hu: HCI UART controller struct
  * @btlp: Apple ACPI method to toggle BT_WAKE pin ("Bluetooth Low Power")
  * @btpu: Apple ACPI method to drive BT_REG_ON pin high ("Bluetooth Power Up")
  * @btpd: Apple ACPI method to drive BT_REG_ON pin low ("Bluetooth Power Down")
- * @clk: clock used by Bluetooth device
- * @clk_enabled: whether @clk is prepared and enabled
+ * @txco_clk: external reference frequency clock used by Bluetooth device
+ * @lpo_clk: external LPO clock used by Bluetooth device
+ * @supplies: VBAT and VDDIO supplies used by Bluetooth device
+ * @res_enabled: whether clocks and supplies are prepared and enabled
  * @init_speed: default baudrate of Bluetooth device;
  *     the host UART is initially set to this baudrate so that
  *     it can configure the Bluetooth device for @oper_speed
@@ -102,8 +113,10 @@ struct bcm_device {
        int                     gpio_int_idx;
 #endif
 
-       struct clk              *clk;
-       bool                    clk_enabled;
+       struct clk              *txco_clk;
+       struct clk              *lpo_clk;
+       struct regulator_bulk_data supplies[BCM_NUM_SUPPLIES];
+       bool                    res_enabled;
 
        u32                     init_speed;
        u32                     oper_speed;
@@ -214,32 +227,59 @@ static int bcm_gpio_set_power(struct bcm_device *dev, bool powered)
 {
        int err;
 
-       if (powered && !IS_ERR(dev->clk) && !dev->clk_enabled) {
-               err = clk_prepare_enable(dev->clk);
+       if (powered && !dev->res_enabled) {
+               err = regulator_bulk_enable(BCM_NUM_SUPPLIES, dev->supplies);
                if (err)
                        return err;
+
+               /* LPO clock needs to be 32.768 kHz */
+               err = clk_set_rate(dev->lpo_clk, 32768);
+               if (err) {
+                       dev_err(dev->dev, "Could not set LPO clock rate\n");
+                       goto err_regulator_disable;
+               }
+
+               err = clk_prepare_enable(dev->lpo_clk);
+               if (err)
+                       goto err_regulator_disable;
+
+               err = clk_prepare_enable(dev->txco_clk);
+               if (err)
+                       goto err_lpo_clk_disable;
        }
 
        err = dev->set_shutdown(dev, powered);
        if (err)
-               goto err_clk_disable;
+               goto err_txco_clk_disable;
 
        err = dev->set_device_wakeup(dev, powered);
        if (err)
                goto err_revert_shutdown;
 
-       if (!powered && !IS_ERR(dev->clk) && dev->clk_enabled)
-               clk_disable_unprepare(dev->clk);
+       if (!powered && dev->res_enabled) {
+               clk_disable_unprepare(dev->txco_clk);
+               clk_disable_unprepare(dev->lpo_clk);
+               regulator_bulk_disable(BCM_NUM_SUPPLIES, dev->supplies);
+       }
+
+       /* wait for device to power on and come out of reset */
+       usleep_range(10000, 20000);
 
-       dev->clk_enabled = powered;
+       dev->res_enabled = powered;
 
        return 0;
 
 err_revert_shutdown:
        dev->set_shutdown(dev, !powered);
-err_clk_disable:
-       if (powered && !IS_ERR(dev->clk) && !dev->clk_enabled)
-               clk_disable_unprepare(dev->clk);
+err_txco_clk_disable:
+       if (powered && !dev->res_enabled)
+               clk_disable_unprepare(dev->txco_clk);
+err_lpo_clk_disable:
+       if (powered && !dev->res_enabled)
+               clk_disable_unprepare(dev->lpo_clk);
+err_regulator_disable:
+       if (powered && !dev->res_enabled)
+               regulator_bulk_disable(BCM_NUM_SUPPLIES, dev->supplies);
        return err;
 }
 
@@ -561,12 +601,28 @@ finalize:
        .lsize = 0, \
        .maxlen = BCM_NULL_SIZE
 
+#define BCM_RECV_TYPE49 \
+       .type = BCM_TYPE49_PKT, \
+       .hlen = BCM_TYPE49_SIZE, \
+       .loff = 0, \
+       .lsize = 0, \
+       .maxlen = BCM_TYPE49_SIZE
+
+#define BCM_RECV_TYPE52 \
+       .type = BCM_TYPE52_PKT, \
+       .hlen = BCM_TYPE52_SIZE, \
+       .loff = 0, \
+       .lsize = 0, \
+       .maxlen = BCM_TYPE52_SIZE
+
 static const struct h4_recv_pkt bcm_recv_pkts[] = {
        { H4_RECV_ACL,      .recv = hci_recv_frame },
        { H4_RECV_SCO,      .recv = hci_recv_frame },
        { H4_RECV_EVENT,    .recv = hci_recv_frame },
        { BCM_RECV_LM_DIAG, .recv = hci_recv_diag  },
        { BCM_RECV_NULL,    .recv = hci_recv_diag  },
+       { BCM_RECV_TYPE49,  .recv = hci_recv_diag  },
+       { BCM_RECV_TYPE52,  .recv = hci_recv_diag  },
 };
 
 static int bcm_recv(struct hci_uart *hu, const void *data, int count)
@@ -896,16 +952,57 @@ static int bcm_gpio_set_shutdown(struct bcm_device *dev, bool powered)
        return 0;
 }
 
+/* Try a bunch of names for TXCO */
+static struct clk *bcm_get_txco(struct device *dev)
+{
+       struct clk *clk;
+
+       /* New explicit name */
+       clk = devm_clk_get(dev, "txco");
+       if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
+               return clk;
+
+       /* Deprecated name */
+       clk = devm_clk_get(dev, "extclk");
+       if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
+               return clk;
+
+       /* Original code used no name at all */
+       return devm_clk_get(dev, NULL);
+}
+
 static int bcm_get_resources(struct bcm_device *dev)
 {
        const struct dmi_system_id *dmi_id;
+       int err;
 
        dev->name = dev_name(dev->dev);
 
        if (x86_apple_machine && !bcm_apple_get_resources(dev))
                return 0;
 
-       dev->clk = devm_clk_get(dev->dev, NULL);
+       dev->txco_clk = bcm_get_txco(dev->dev);
+
+       /* Handle deferred probing */
+       if (dev->txco_clk == ERR_PTR(-EPROBE_DEFER))
+               return PTR_ERR(dev->txco_clk);
+
+       /* Ignore all other errors as before */
+       if (IS_ERR(dev->txco_clk))
+               dev->txco_clk = NULL;
+
+       dev->lpo_clk = devm_clk_get(dev->dev, "lpo");
+       if (dev->lpo_clk == ERR_PTR(-EPROBE_DEFER))
+               return PTR_ERR(dev->lpo_clk);
+
+       if (IS_ERR(dev->lpo_clk))
+               dev->lpo_clk = NULL;
+
+       /* Check if we accidentally fetched the lpo clock twice */
+       if (dev->lpo_clk && clk_is_match(dev->lpo_clk, dev->txco_clk)) {
+               devm_clk_put(dev->dev, dev->txco_clk);
+               dev->txco_clk = NULL;
+       }
 
        dev->device_wakeup = devm_gpiod_get_optional(dev->dev, "device-wakeup",
                                                     GPIOD_OUT_LOW);
@@ -920,6 +1017,13 @@ static int bcm_get_resources(struct bcm_device *dev)
        dev->set_device_wakeup = bcm_gpio_set_device_wakeup;
        dev->set_shutdown = bcm_gpio_set_shutdown;
 
+       dev->supplies[0].supply = "vbat";
+       dev->supplies[1].supply = "vddio";
+       err = devm_regulator_bulk_get(dev->dev, BCM_NUM_SUPPLIES,
+                                     dev->supplies);
+       if (err)
+               return err;
+
        /* IRQ can be declared in ACPI table as Interrupt or GpioInt */
        if (dev->irq <= 0) {
                struct gpio_desc *gpio;
@@ -1314,6 +1418,8 @@ static void bcm_serdev_remove(struct serdev_device *serdev)
 
 #ifdef CONFIG_OF
 static const struct of_device_id bcm_bluetooth_of_match[] = {
+       { .compatible = "brcm,bcm20702a1" },
+       { .compatible = "brcm,bcm4330-bt" },
        { .compatible = "brcm,bcm43438-bt" },
        { },
 };
index 8eede11..069d1c8 100644 (file)
@@ -115,6 +115,8 @@ struct h5_vnd {
        int (*setup)(struct h5 *h5);
        void (*open)(struct h5 *h5);
        void (*close)(struct h5 *h5);
+       int (*suspend)(struct h5 *h5);
+       int (*resume)(struct h5 *h5);
        const struct acpi_gpio_mapping *acpi_gpio_map;
 };
 
@@ -841,6 +843,28 @@ static void h5_serdev_remove(struct serdev_device *serdev)
        hci_uart_unregister_device(&h5->serdev_hu);
 }
 
+static int __maybe_unused h5_serdev_suspend(struct device *dev)
+{
+       struct h5 *h5 = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (h5->vnd && h5->vnd->suspend)
+               ret = h5->vnd->suspend(h5);
+
+       return ret;
+}
+
+static int __maybe_unused h5_serdev_resume(struct device *dev)
+{
+       struct h5 *h5 = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (h5->vnd && h5->vnd->resume)
+               ret = h5->vnd->resume(h5);
+
+       return ret;
+}
+
 #ifdef CONFIG_BT_HCIUART_RTL
 static int h5_btrtl_setup(struct h5 *h5)
 {
@@ -907,6 +931,56 @@ static void h5_btrtl_close(struct h5 *h5)
        gpiod_set_value_cansleep(h5->enable_gpio, 0);
 }
 
+/* Suspend/resume support. On many devices the RTL BT device loses power during
+ * suspend/resume, causing it to lose its firmware and all state. So we simply
+ * turn it off on suspend and reprobe on resume.  This mirrors how RTL devices
+ * are handled in the USB driver, where the USB_QUIRK_RESET_RESUME is used which
+ * also causes a reprobe on resume.
+ */
+static int h5_btrtl_suspend(struct h5 *h5)
+{
+       serdev_device_set_flow_control(h5->hu->serdev, false);
+       gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
+       gpiod_set_value_cansleep(h5->enable_gpio, 0);
+       return 0;
+}
+
+struct h5_btrtl_reprobe {
+       struct device *dev;
+       struct work_struct work;
+};
+
+static void h5_btrtl_reprobe_worker(struct work_struct *work)
+{
+       struct h5_btrtl_reprobe *reprobe =
+               container_of(work, struct h5_btrtl_reprobe, work);
+       int ret;
+
+       ret = device_reprobe(reprobe->dev);
+       if (ret && ret != -EPROBE_DEFER)
+               dev_err(reprobe->dev, "Reprobe error %d\n", ret);
+
+       put_device(reprobe->dev);
+       kfree(reprobe);
+       module_put(THIS_MODULE);
+}
+
+static int h5_btrtl_resume(struct h5 *h5)
+{
+       struct h5_btrtl_reprobe *reprobe;
+
+       reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
+       if (!reprobe)
+               return -ENOMEM;
+
+       __module_get(THIS_MODULE);
+
+       INIT_WORK(&reprobe->work, h5_btrtl_reprobe_worker);
+       reprobe->dev = get_device(&h5->hu->serdev->dev);
+       queue_work(system_long_wq, &reprobe->work);
+       return 0;
+}
+
 static const struct acpi_gpio_params btrtl_device_wake_gpios = { 0, 0, false };
 static const struct acpi_gpio_params btrtl_enable_gpios = { 1, 0, false };
 static const struct acpi_gpio_params btrtl_host_wake_gpios = { 2, 0, false };
@@ -921,6 +995,8 @@ static struct h5_vnd rtl_vnd = {
        .setup          = h5_btrtl_setup,
        .open           = h5_btrtl_open,
        .close          = h5_btrtl_close,
+       .suspend        = h5_btrtl_suspend,
+       .resume         = h5_btrtl_resume,
        .acpi_gpio_map  = acpi_btrtl_gpios,
 };
 #endif
@@ -935,12 +1011,17 @@ static const struct acpi_device_id h5_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, h5_acpi_match);
 #endif
 
+static const struct dev_pm_ops h5_serdev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(h5_serdev_suspend, h5_serdev_resume)
+};
+
 static struct serdev_device_driver h5_serdev_driver = {
        .probe = h5_serdev_probe,
        .remove = h5_serdev_remove,
        .driver = {
                .name = "hci_uart_h5",
                .acpi_match_table = ACPI_PTR(h5_acpi_match),
+               .pm = &h5_serdev_pm_ops,
        },
 };
 
index 46ace32..f314105 100644 (file)
@@ -596,8 +596,8 @@ static int intel_setup(struct hci_uart *hu)
         * is in bootloader mode or if it already has operational firmware
         * loaded.
         */
-        err = btintel_read_version(hdev, &ver);
-        if (err)
+       err = btintel_read_version(hdev, &ver);
+       if (err)
                return err;
 
        /* The hardware platform number has a fixed value of 0x37 and
@@ -909,10 +909,8 @@ static int intel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
                        set_bit(STATE_FIRMWARE_FAILED, &intel->flags);
 
                if (test_and_clear_bit(STATE_DOWNLOADING, &intel->flags) &&
-                   test_bit(STATE_FIRMWARE_LOADED, &intel->flags)) {
-                       smp_mb__after_atomic();
+                   test_bit(STATE_FIRMWARE_LOADED, &intel->flags))
                        wake_up_bit(&intel->flags, STATE_DOWNLOADING);
-               }
 
        /* When switching to the operational firmware the device
         * sends a vendor specific event indicating that the bootup
@@ -920,10 +918,8 @@ static int intel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
         */
        } else if (skb->len == 9 && hdr->evt == 0xff && hdr->plen == 0x07 &&
                   skb->data[2] == 0x02) {
-               if (test_and_clear_bit(STATE_BOOTING, &intel->flags)) {
-                       smp_mb__after_atomic();
+               if (test_and_clear_bit(STATE_BOOTING, &intel->flags))
                        wake_up_bit(&intel->flags, STATE_BOOTING);
-               }
        }
 recv:
        return hci_recv_frame(hdev, skb);
@@ -960,17 +956,13 @@ static int intel_recv_lpm(struct hci_dev *hdev, struct sk_buff *skb)
                break;
        case LPM_OP_SUSPEND_ACK:
                set_bit(STATE_SUSPENDED, &intel->flags);
-               if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags)) {
-                       smp_mb__after_atomic();
+               if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags))
                        wake_up_bit(&intel->flags, STATE_LPM_TRANSACTION);
-               }
                break;
        case LPM_OP_RESUME_ACK:
                clear_bit(STATE_SUSPENDED, &intel->flags);
-               if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags)) {
-                       smp_mb__after_atomic();
+               if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags))
                        wake_up_bit(&intel->flags, STATE_LPM_TRANSACTION);
-               }
                break;
        default:
                bt_dev_err(hdev, "Unknown LPM opcode (%02x)", lpm->opcode);
index c445aa9..490abba 100644 (file)
@@ -333,9 +333,6 @@ int hci_uart_register_device(struct hci_uart *hu,
        if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags))
                set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks);
 
-       if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags))
-               set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
-
        if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags))
                hdev->dev_type = HCI_AMP;
        else
index ad8d483..ca7d37e 100644 (file)
@@ -183,7 +183,7 @@ void mmp_clk_add(struct mmp_clk_unit *unit, unsigned int id,
                pr_err("CLK %d has invalid pointer %p\n", id, clk);
                return;
        }
-       if (id > unit->nr_clks) {
+       if (id >= unit->nr_clks) {
                pr_err("CLK %d is invalid\n", id);
                return;
        }
index 9781b1b..9235a33 100644 (file)
@@ -200,11 +200,11 @@ static struct clk_hw *cp110_of_clk_get(struct of_phandle_args *clkspec,
        unsigned int idx = clkspec->args[1];
 
        if (type == CP110_CLK_TYPE_CORE) {
-               if (idx > CP110_MAX_CORE_CLOCKS)
+               if (idx >= CP110_MAX_CORE_CLOCKS)
                        return ERR_PTR(-EINVAL);
                return clk_data->hws[idx];
        } else if (type == CP110_CLK_TYPE_GATABLE) {
-               if (idx > CP110_MAX_GATABLE_CLOCKS)
+               if (idx >= CP110_MAX_GATABLE_CLOCKS)
                        return ERR_PTR(-EINVAL);
                return clk_data->hws[CP110_MAX_CORE_CLOCKS + idx];
        }
index db9b247..0a48ed5 100644 (file)
@@ -191,6 +191,22 @@ int qcom_cc_register_sleep_clk(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
 
+/* Drop 'protected-clocks' from the list of clocks to register */
+static void qcom_cc_drop_protected(struct device *dev, struct qcom_cc *cc)
+{
+       struct device_node *np = dev->of_node;
+       struct property *prop;
+       const __be32 *p;
+       u32 i;
+
+       of_property_for_each_u32(np, "protected-clocks", prop, p, i) {
+               if (i >= cc->num_rclks)
+                       continue;
+
+               cc->rclks[i] = NULL;
+       }
+}
+
 static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
                                         void *data)
 {
@@ -251,6 +267,8 @@ int qcom_cc_really_probe(struct platform_device *pdev,
        cc->rclks = rclks;
        cc->num_rclks = num_clks;
 
+       qcom_cc_drop_protected(dev, cc);
+
        for (i = 0; i < num_clks; i++) {
                if (!rclks[i])
                        continue;
index ef1b267..64da032 100644 (file)
@@ -297,7 +297,7 @@ static struct clk_alpha_pll gpll0_out_main = {
                .hw.init = &(struct clk_init_data){
                        .name = "gpll0_out_main",
                        .parent_names = (const char *[])
-                                       { "gpll0_sleep_clk_src" },
+                                       { "cxo" },
                        .num_parents = 1,
                        .ops = &clk_alpha_pll_ops,
                },
index 9d7d297..f65cc0f 100644 (file)
@@ -128,7 +128,7 @@ static const struct zynqmp_eemi_ops *eemi_ops;
  */
 static inline int zynqmp_is_valid_clock(u32 clk_id)
 {
-       if (clk_id > clock_max_idx)
+       if (clk_id >= clock_max_idx)
                return -ENODEV;
 
        return clock[clk_id].valid;
@@ -279,6 +279,9 @@ struct clk_hw *zynqmp_clk_register_fixed_factor(const char *name, u32 clk_id,
        qdata.arg1 = clk_id;
 
        ret = eemi_ops->query_data(qdata, ret_payload);
+       if (ret)
+               return ERR_PTR(ret);
+
        mult = ret_payload[1];
        div = ret_payload[2];
 
index 8cfee0a..d8c3595 100644 (file)
@@ -160,8 +160,13 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
        /* Ensure the arm clock divider is what we expect */
        ret = clk_set_rate(clks[ARM].clk, new_freq * 1000);
        if (ret) {
+               int ret1;
+
                dev_err(cpu_dev, "failed to set clock rate: %d\n", ret);
-               regulator_set_voltage_tol(arm_reg, volt_old, 0);
+               ret1 = regulator_set_voltage_tol(arm_reg, volt_old, 0);
+               if (ret1)
+                       dev_warn(cpu_dev,
+                                "failed to restore vddarm voltage: %d\n", ret1);
                return ret;
        }
 
index 3f0e2a1..22b53bf 100644 (file)
@@ -201,19 +201,28 @@ static const struct of_device_id ti_cpufreq_of_match[] = {
        {},
 };
 
+static const struct of_device_id *ti_cpufreq_match_node(void)
+{
+       struct device_node *np;
+       const struct of_device_id *match;
+
+       np = of_find_node_by_path("/");
+       match = of_match_node(ti_cpufreq_of_match, np);
+       of_node_put(np);
+
+       return match;
+}
+
 static int ti_cpufreq_probe(struct platform_device *pdev)
 {
        u32 version[VERSION_COUNT];
-       struct device_node *np;
        const struct of_device_id *match;
        struct opp_table *ti_opp_table;
        struct ti_cpufreq_data *opp_data;
        const char * const reg_names[] = {"vdd", "vbb"};
        int ret;
 
-       np = of_find_node_by_path("/");
-       match = of_match_node(ti_cpufreq_of_match, np);
-       of_node_put(np);
+       match = dev_get_platdata(&pdev->dev);
        if (!match)
                return -ENODEV;
 
@@ -290,7 +299,14 @@ fail_put_node:
 
 static int ti_cpufreq_init(void)
 {
-       platform_device_register_simple("ti-cpufreq", -1, NULL, 0);
+       const struct of_device_id *match;
+
+       /* Check to ensure we are on a compatible platform */
+       match = ti_cpufreq_match_node();
+       if (match)
+               platform_device_register_data(NULL, "ti-cpufreq", -1, match,
+                                             sizeof(*match));
+
        return 0;
 }
 module_init(ti_cpufreq_init);
index 073557f..3a407a3 100644 (file)
@@ -82,7 +82,6 @@ static int __init arm_idle_init_cpu(int cpu)
 {
        int ret;
        struct cpuidle_driver *drv;
-       struct cpuidle_device *dev;
 
        drv = kmemdup(&arm_idle_driver, sizeof(*drv), GFP_KERNEL);
        if (!drv)
@@ -103,13 +102,6 @@ static int __init arm_idle_init_cpu(int cpu)
                goto out_kfree_drv;
        }
 
-       ret = cpuidle_register_driver(drv);
-       if (ret) {
-               if (ret != -EBUSY)
-                       pr_err("Failed to register cpuidle driver\n");
-               goto out_kfree_drv;
-       }
-
        /*
         * Call arch CPU operations in order to initialize
         * idle states suspend back-end specific data
@@ -117,37 +109,21 @@ static int __init arm_idle_init_cpu(int cpu)
        ret = arm_cpuidle_init(cpu);
 
        /*
-        * Skip the cpuidle device initialization if the reported
+        * Allow the initialization to continue for other CPUs, if the reported
         * failure is a HW misconfiguration/breakage (-ENXIO).
         */
-       if (ret == -ENXIO)
-               return 0;
-
        if (ret) {
                pr_err("CPU %d failed to init idle CPU ops\n", cpu);
-               goto out_unregister_drv;
-       }
-
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (!dev) {
-               ret = -ENOMEM;
-               goto out_unregister_drv;
+               ret = ret == -ENXIO ? 0 : ret;
+               goto out_kfree_drv;
        }
-       dev->cpu = cpu;
 
-       ret = cpuidle_register_device(dev);
-       if (ret) {
-               pr_err("Failed to register cpuidle device for CPU %d\n",
-                      cpu);
-               goto out_kfree_dev;
-       }
+       ret = cpuidle_register(drv, NULL);
+       if (ret)
+               goto out_kfree_drv;
 
        return 0;
 
-out_kfree_dev:
-       kfree(dev);
-out_unregister_drv:
-       cpuidle_unregister_driver(drv);
 out_kfree_drv:
        kfree(drv);
        return ret;
@@ -178,9 +154,7 @@ out_fail:
        while (--cpu >= 0) {
                dev = per_cpu(cpuidle_devices, cpu);
                drv = cpuidle_get_cpu_driver(dev);
-               cpuidle_unregister_device(dev);
-               cpuidle_unregister_driver(drv);
-               kfree(dev);
+               cpuidle_unregister(drv);
                kfree(drv);
        }
 
index 461b97e..ceaa16b 100644 (file)
@@ -570,6 +570,7 @@ int chcr_ipsec_xmit(struct sk_buff *skb, struct net_device *dev)
        struct sge_eth_txq *q;
        struct port_info *pi;
        dma_addr_t addr[MAX_SKB_FRAGS + 1];
+       struct sec_path *sp;
        bool immediate = false;
 
        if (!x->xso.offload_handle)
@@ -578,7 +579,8 @@ int chcr_ipsec_xmit(struct sk_buff *skb, struct net_device *dev)
        sa_entry = (struct ipsec_sa_entry *)x->xso.offload_handle;
        kctx_len = sa_entry->kctx_len;
 
-       if (skb->sp->len != 1) {
+       sp = skb_sec_path(skb);
+       if (sp->len != 1) {
 out_free:       dev_kfree_skb_any(skb);
                return NETDEV_TX_OK;
        }
index 7725b6e..59bb67d 100644 (file)
@@ -153,6 +153,11 @@ struct chtls_dev {
        unsigned int cdev_state;
 };
 
+struct chtls_listen {
+       struct chtls_dev *cdev;
+       struct sock *sk;
+};
+
 struct chtls_hws {
        struct sk_buff_head sk_recv_queue;
        u8 txqid;
@@ -215,6 +220,8 @@ struct chtls_sock {
        u16 resv2;
        u32 delack_mode;
        u32 delack_seq;
+       u32 snd_win;
+       u32 rcv_win;
 
        void *passive_reap_next;        /* placeholder for passive */
        struct chtls_hws tlshws;
index 20209e2..59b7529 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/kallsyms.h>
 #include <linux/kprobes.h>
 #include <linux/if_vlan.h>
+#include <net/inet_common.h>
 #include <net/tcp.h>
 #include <net/dst.h>
 
@@ -887,24 +888,6 @@ static unsigned int chtls_select_mss(const struct chtls_sock *csk,
        return mtu_idx;
 }
 
-static unsigned int select_rcv_wnd(struct chtls_sock *csk)
-{
-       unsigned int rcvwnd;
-       unsigned int wnd;
-       struct sock *sk;
-
-       sk = csk->sk;
-       wnd = tcp_full_space(sk);
-
-       if (wnd < MIN_RCV_WND)
-               wnd = MIN_RCV_WND;
-
-       rcvwnd = MAX_RCV_WND;
-
-       csk_set_flag(csk, CSK_UPDATE_RCV_WND);
-       return min(wnd, rcvwnd);
-}
-
 static unsigned int select_rcv_wscale(int space, int wscale_ok, int win_clamp)
 {
        int wscale = 0;
@@ -951,7 +934,7 @@ static void chtls_pass_accept_rpl(struct sk_buff *skb,
        csk->mtu_idx = chtls_select_mss(csk, dst_mtu(__sk_dst_get(sk)),
                                        req);
        opt0 = TCAM_BYPASS_F |
-              WND_SCALE_V((tp)->rx_opt.rcv_wscale) |
+              WND_SCALE_V(RCV_WSCALE(tp)) |
               MSS_IDX_V(csk->mtu_idx) |
               L2T_IDX_V(csk->l2t_entry->idx) |
               NAGLE_V(!(tp->nonagle & TCP_NAGLE_OFF)) |
@@ -1005,6 +988,25 @@ static int chtls_backlog_rcv(struct sock *sk, struct sk_buff *skb)
        return 0;
 }
 
+static void chtls_set_tcp_window(struct chtls_sock *csk)
+{
+       struct net_device *ndev = csk->egress_dev;
+       struct port_info *pi = netdev_priv(ndev);
+       unsigned int linkspeed;
+       u8 scale;
+
+       linkspeed = pi->link_cfg.speed;
+       scale = linkspeed / SPEED_10000;
+#define CHTLS_10G_RCVWIN (256 * 1024)
+       csk->rcv_win = CHTLS_10G_RCVWIN;
+       if (scale)
+               csk->rcv_win *= scale;
+#define CHTLS_10G_SNDWIN (256 * 1024)
+       csk->snd_win = CHTLS_10G_SNDWIN;
+       if (scale)
+               csk->snd_win *= scale;
+}
+
 static struct sock *chtls_recv_sock(struct sock *lsk,
                                    struct request_sock *oreq,
                                    void *network_hdr,
@@ -1067,6 +1069,9 @@ static struct sock *chtls_recv_sock(struct sock *lsk,
        csk->port_id = port_id;
        csk->egress_dev = ndev;
        csk->tos = PASS_OPEN_TOS_G(ntohl(req->tos_stid));
+       chtls_set_tcp_window(csk);
+       tp->rcv_wnd = csk->rcv_win;
+       csk->sndbuf = csk->snd_win;
        csk->ulp_mode = ULP_MODE_TLS;
        step = cdev->lldi->nrxq / cdev->lldi->nchan;
        csk->rss_qid = cdev->lldi->rxq_ids[port_id * step];
@@ -1074,11 +1079,10 @@ static struct sock *chtls_recv_sock(struct sock *lsk,
        csk->txq_idx = (rxq_idx < cdev->lldi->ntxq) ? rxq_idx :
                        port_id * step;
        csk->sndbuf = newsk->sk_sndbuf;
-       csk->smac_idx = cxgb4_tp_smt_idx(cdev->lldi->adapter_type,
-                                        cxgb4_port_viid(ndev));
-       tp->rcv_wnd = select_rcv_wnd(csk);
+       csk->smac_idx = ((struct port_info *)netdev_priv(ndev))->smt_idx;
        RCV_WSCALE(tp) = select_rcv_wscale(tcp_full_space(newsk),
-                                          WSCALE_OK(tp),
+                                          sock_net(newsk)->
+                                               ipv4.sysctl_tcp_window_scaling,
                                           tp->window_clamp);
        neigh_release(n);
        inet_inherit_port(&tcp_hashinfo, lsk, newsk);
@@ -1130,6 +1134,7 @@ static void chtls_pass_accept_request(struct sock *sk,
        struct cpl_t5_pass_accept_rpl *rpl;
        struct cpl_pass_accept_req *req;
        struct listen_ctx *listen_ctx;
+       struct vlan_ethhdr *vlan_eh;
        struct request_sock *oreq;
        struct sk_buff *reply_skb;
        struct chtls_sock *csk;
@@ -1142,6 +1147,10 @@ static void chtls_pass_accept_request(struct sock *sk,
        unsigned int stid;
        unsigned int len;
        unsigned int tid;
+       bool th_ecn, ect;
+       __u8 ip_dsfield; /* IPv4 tos or IPv6 dsfield */
+       u16 eth_hdr_len;
+       bool ecn_ok;
 
        req = cplhdr(skb) + RSS_HDR;
        tid = GET_TID(req);
@@ -1180,24 +1189,40 @@ static void chtls_pass_accept_request(struct sock *sk,
        oreq->mss = 0;
        oreq->ts_recent = 0;
 
-       eh = (struct ethhdr *)(req + 1);
-       iph = (struct iphdr *)(eh + 1);
+       eth_hdr_len = T6_ETH_HDR_LEN_G(ntohl(req->hdr_len));
+       if (eth_hdr_len == ETH_HLEN) {
+               eh = (struct ethhdr *)(req + 1);
+               iph = (struct iphdr *)(eh + 1);
+               network_hdr = (void *)(eh + 1);
+       } else {
+               vlan_eh = (struct vlan_ethhdr *)(req + 1);
+               iph = (struct iphdr *)(vlan_eh + 1);
+               network_hdr = (void *)(vlan_eh + 1);
+       }
        if (iph->version != 0x4)
                goto free_oreq;
 
-       network_hdr = (void *)(eh + 1);
        tcph = (struct tcphdr *)(iph + 1);
+       skb_set_network_header(skb, (void *)iph - (void *)req);
 
        tcp_rsk(oreq)->tfo_listener = false;
        tcp_rsk(oreq)->rcv_isn = ntohl(tcph->seq);
        chtls_set_req_port(oreq, tcph->source, tcph->dest);
-       inet_rsk(oreq)->ecn_ok = 0;
        chtls_set_req_addr(oreq, iph->daddr, iph->saddr);
-       if (req->tcpopt.wsf <= 14) {
+       ip_dsfield = ipv4_get_dsfield(iph);
+       if (req->tcpopt.wsf <= 14 &&
+           sock_net(sk)->ipv4.sysctl_tcp_window_scaling) {
                inet_rsk(oreq)->wscale_ok = 1;
                inet_rsk(oreq)->snd_wscale = req->tcpopt.wsf;
        }
        inet_rsk(oreq)->ir_iif = sk->sk_bound_dev_if;
+       th_ecn = tcph->ece && tcph->cwr;
+       if (th_ecn) {
+               ect = !INET_ECN_is_not_ect(ip_dsfield);
+               ecn_ok = sock_net(sk)->ipv4.sysctl_tcp_ecn;
+               if ((!ect && ecn_ok) || tcp_ca_needs_ecn(sk))
+                       inet_rsk(oreq)->ecn_ok = 1;
+       }
 
        newsk = chtls_recv_sock(sk, oreq, network_hdr, req, cdev);
        if (!newsk)
index afebbd8..18f553f 100644 (file)
@@ -397,7 +397,7 @@ static void tls_tx_data_wr(struct sock *sk, struct sk_buff *skb,
 
        req_wr->lsodisable_to_flags =
                        htonl(TX_ULP_MODE_V(ULP_MODE_TLS) |
-                             FW_OFLD_TX_DATA_WR_URGENT_V(skb_urgent(skb)) |
+                             TX_URG_V(skb_urgent(skb)) |
                              T6_TX_FORCE_F | wr_ulp_mode_force |
                              TX_SHOVE_V((!csk_flag(sk, CSK_TX_MORE_DATA)) &&
                                         skb_queue_empty(&csk->txq)));
@@ -534,10 +534,9 @@ static void make_tx_data_wr(struct sock *sk, struct sk_buff *skb,
                                FW_OFLD_TX_DATA_WR_SHOVE_F);
 
        req->tunnel_to_proxy = htonl(wr_ulp_mode_force |
-                       FW_OFLD_TX_DATA_WR_URGENT_V(skb_urgent(skb)) |
-                       FW_OFLD_TX_DATA_WR_SHOVE_V((!csk_flag
-                                       (sk, CSK_TX_MORE_DATA)) &&
-                                        skb_queue_empty(&csk->txq)));
+                       TX_URG_V(skb_urgent(skb)) |
+                       TX_SHOVE_V((!csk_flag(sk, CSK_TX_MORE_DATA)) &&
+                                  skb_queue_empty(&csk->txq)));
        req->plen = htonl(len);
 }
 
@@ -995,7 +994,6 @@ int chtls_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
        int mss, flags, err;
        int recordsz = 0;
        int copied = 0;
-       int hdrlen = 0;
        long timeo;
 
        lock_sock(sk);
@@ -1032,7 +1030,7 @@ int chtls_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
 
                        recordsz = tls_header_read(&hdr, &msg->msg_iter);
                        size -= TLS_HEADER_LENGTH;
-                       hdrlen += TLS_HEADER_LENGTH;
+                       copied += TLS_HEADER_LENGTH;
                        csk->tlshws.txleft = recordsz;
                        csk->tlshws.type = hdr.type;
                        if (skb)
@@ -1083,10 +1081,8 @@ new_buf:
                        int off = TCP_OFF(sk);
                        bool merge;
 
-                       if (!page)
-                               goto wait_for_memory;
-
-                       pg_size <<= compound_order(page);
+                       if (page)
+                               pg_size <<= compound_order(page);
                        if (off < pg_size &&
                            skb_can_coalesce(skb, i, page, off)) {
                                merge = 1;
@@ -1187,7 +1183,7 @@ out:
                chtls_tcp_push(sk, flags);
 done:
        release_sock(sk);
-       return copied + hdrlen;
+       return copied;
 do_fault:
        if (!skb->len) {
                __skb_unlink(skb, &csk->txq);
index f472c51..563f8fe 100644 (file)
@@ -55,24 +55,19 @@ static void unregister_listen_notifier(struct notifier_block *nb)
 static int listen_notify_handler(struct notifier_block *this,
                                 unsigned long event, void *data)
 {
-       struct chtls_dev *cdev;
-       struct sock *sk;
-       int ret;
+       struct chtls_listen *clisten;
+       int ret = NOTIFY_DONE;
 
-       sk = data;
-       ret =  NOTIFY_DONE;
+       clisten = (struct chtls_listen *)data;
 
        switch (event) {
        case CHTLS_LISTEN_START:
+               ret = chtls_listen_start(clisten->cdev, clisten->sk);
+               kfree(clisten);
+               break;
        case CHTLS_LISTEN_STOP:
-               mutex_lock(&cdev_list_lock);
-               list_for_each_entry(cdev, &cdev_list, list) {
-                       if (event == CHTLS_LISTEN_START)
-                               ret = chtls_listen_start(cdev, sk);
-                       else
-                               chtls_listen_stop(cdev, sk);
-               }
-               mutex_unlock(&cdev_list_lock);
+               chtls_listen_stop(clisten->cdev, clisten->sk);
+               kfree(clisten);
                break;
        }
        return ret;
@@ -90,8 +85,9 @@ static int listen_backlog_rcv(struct sock *sk, struct sk_buff *skb)
        return 0;
 }
 
-static int chtls_start_listen(struct sock *sk)
+static int chtls_start_listen(struct chtls_dev *cdev, struct sock *sk)
 {
+       struct chtls_listen *clisten;
        int err;
 
        if (sk->sk_protocol != IPPROTO_TCP)
@@ -102,21 +98,33 @@ static int chtls_start_listen(struct sock *sk)
                return -EADDRNOTAVAIL;
 
        sk->sk_backlog_rcv = listen_backlog_rcv;
+       clisten = kmalloc(sizeof(*clisten), GFP_KERNEL);
+       if (!clisten)
+               return -ENOMEM;
+       clisten->cdev = cdev;
+       clisten->sk = sk;
        mutex_lock(&notify_mutex);
        err = raw_notifier_call_chain(&listen_notify_list,
-                                     CHTLS_LISTEN_START, sk);
+                                     CHTLS_LISTEN_START, clisten);
        mutex_unlock(&notify_mutex);
        return err;
 }
 
-static void chtls_stop_listen(struct sock *sk)
+static void chtls_stop_listen(struct chtls_dev *cdev, struct sock *sk)
 {
+       struct chtls_listen *clisten;
+
        if (sk->sk_protocol != IPPROTO_TCP)
                return;
 
+       clisten = kmalloc(sizeof(*clisten), GFP_KERNEL);
+       if (!clisten)
+               return;
+       clisten->cdev = cdev;
+       clisten->sk = sk;
        mutex_lock(&notify_mutex);
        raw_notifier_call_chain(&listen_notify_list,
-                               CHTLS_LISTEN_STOP, sk);
+                               CHTLS_LISTEN_STOP, clisten);
        mutex_unlock(&notify_mutex);
 }
 
@@ -138,15 +146,43 @@ static int chtls_inline_feature(struct tls_device *dev)
 
 static int chtls_create_hash(struct tls_device *dev, struct sock *sk)
 {
+       struct chtls_dev *cdev = to_chtls_dev(dev);
+
        if (sk->sk_state == TCP_LISTEN)
-               return chtls_start_listen(sk);
+               return chtls_start_listen(cdev, sk);
        return 0;
 }
 
 static void chtls_destroy_hash(struct tls_device *dev, struct sock *sk)
 {
+       struct chtls_dev *cdev = to_chtls_dev(dev);
+
        if (sk->sk_state == TCP_LISTEN)
-               chtls_stop_listen(sk);
+               chtls_stop_listen(cdev, sk);
+}
+
+static void chtls_free_uld(struct chtls_dev *cdev)
+{
+       int i;
+
+       tls_unregister_device(&cdev->tlsdev);
+       kvfree(cdev->kmap.addr);
+       idr_destroy(&cdev->hwtid_idr);
+       for (i = 0; i < (1 << RSPQ_HASH_BITS); i++)
+               kfree_skb(cdev->rspq_skb_cache[i]);
+       kfree(cdev->lldi);
+       kfree_skb(cdev->askb);
+       kfree(cdev);
+}
+
+static inline void chtls_dev_release(struct kref *kref)
+{
+       struct chtls_dev *cdev;
+       struct tls_device *dev;
+
+       dev = container_of(kref, struct tls_device, kref);
+       cdev = to_chtls_dev(dev);
+       chtls_free_uld(cdev);
 }
 
 static void chtls_register_dev(struct chtls_dev *cdev)
@@ -159,15 +195,12 @@ static void chtls_register_dev(struct chtls_dev *cdev)
        tlsdev->feature = chtls_inline_feature;
        tlsdev->hash = chtls_create_hash;
        tlsdev->unhash = chtls_destroy_hash;
-       tls_register_device(&cdev->tlsdev);
+       tlsdev->release = chtls_dev_release;
+       kref_init(&tlsdev->kref);
+       tls_register_device(tlsdev);
        cdev->cdev_state = CHTLS_CDEV_STATE_UP;
 }
 
-static void chtls_unregister_dev(struct chtls_dev *cdev)
-{
-       tls_unregister_device(&cdev->tlsdev);
-}
-
 static void process_deferq(struct work_struct *task_param)
 {
        struct chtls_dev *cdev = container_of(task_param,
@@ -262,28 +295,16 @@ out:
        return NULL;
 }
 
-static void chtls_free_uld(struct chtls_dev *cdev)
-{
-       int i;
-
-       chtls_unregister_dev(cdev);
-       kvfree(cdev->kmap.addr);
-       idr_destroy(&cdev->hwtid_idr);
-       for (i = 0; i < (1 << RSPQ_HASH_BITS); i++)
-               kfree_skb(cdev->rspq_skb_cache[i]);
-       kfree(cdev->lldi);
-       kfree_skb(cdev->askb);
-       kfree(cdev);
-}
-
 static void chtls_free_all_uld(void)
 {
        struct chtls_dev *cdev, *tmp;
 
        mutex_lock(&cdev_mutex);
        list_for_each_entry_safe(cdev, tmp, &cdev_list, list) {
-               if (cdev->cdev_state == CHTLS_CDEV_STATE_UP)
-                       chtls_free_uld(cdev);
+               if (cdev->cdev_state == CHTLS_CDEV_STATE_UP) {
+                       list_del(&cdev->list);
+                       kref_put(&cdev->tlsdev.kref, cdev->tlsdev.release);
+               }
        }
        mutex_unlock(&cdev_mutex);
 }
@@ -304,7 +325,7 @@ static int chtls_uld_state_change(void *handle, enum cxgb4_state new_state)
                mutex_lock(&cdev_mutex);
                list_del(&cdev->list);
                mutex_unlock(&cdev_mutex);
-               chtls_free_uld(cdev);
+               kref_put(&cdev->tlsdev.kref, cdev->tlsdev.release);
                break;
        default:
                break;
index f7d6d69..cdc4f9a 100644 (file)
@@ -732,6 +732,7 @@ static int sec_alg_skcipher_crypto(struct skcipher_request *skreq,
        int *splits_in_nents;
        int *splits_out_nents = NULL;
        struct sec_request_el *el, *temp;
+       bool split = skreq->src != skreq->dst;
 
        mutex_init(&sec_req->lock);
        sec_req->req_base = &skreq->base;
@@ -750,7 +751,7 @@ static int sec_alg_skcipher_crypto(struct skcipher_request *skreq,
        if (ret)
                goto err_free_split_sizes;
 
-       if (skreq->src != skreq->dst) {
+       if (split) {
                sec_req->len_out = sg_nents(skreq->dst);
                ret = sec_map_and_split_sg(skreq->dst, split_sizes, steps,
                                           &splits_out, &splits_out_nents,
@@ -785,8 +786,9 @@ static int sec_alg_skcipher_crypto(struct skcipher_request *skreq,
                                               split_sizes[i],
                                               skreq->src != skreq->dst,
                                               splits_in[i], splits_in_nents[i],
-                                              splits_out[i],
-                                              splits_out_nents[i], info);
+                                              split ? splits_out[i] : NULL,
+                                              split ? splits_out_nents[i] : 0,
+                                              info);
                if (IS_ERR(el)) {
                        ret = PTR_ERR(el);
                        goto err_free_elements;
@@ -806,13 +808,6 @@ static int sec_alg_skcipher_crypto(struct skcipher_request *skreq,
         * more refined but this is unlikely to happen so no need.
         */
 
-       /* Cleanup - all elements in pointer arrays have been coppied */
-       kfree(splits_in_nents);
-       kfree(splits_in);
-       kfree(splits_out_nents);
-       kfree(splits_out);
-       kfree(split_sizes);
-
        /* Grab a big lock for a long time to avoid concurrency issues */
        mutex_lock(&queue->queuelock);
 
@@ -827,13 +822,13 @@ static int sec_alg_skcipher_crypto(struct skcipher_request *skreq,
             (!queue->havesoftqueue ||
              kfifo_avail(&queue->softqueue) > steps)) ||
            !list_empty(&ctx->backlog)) {
+               ret = -EBUSY;
                if ((skreq->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)) {
                        list_add_tail(&sec_req->backlog_head, &ctx->backlog);
                        mutex_unlock(&queue->queuelock);
-                       return -EBUSY;
+                       goto out;
                }
 
-               ret = -EBUSY;
                mutex_unlock(&queue->queuelock);
                goto err_free_elements;
        }
@@ -842,7 +837,15 @@ static int sec_alg_skcipher_crypto(struct skcipher_request *skreq,
        if (ret)
                goto err_free_elements;
 
-       return -EINPROGRESS;
+       ret = -EINPROGRESS;
+out:
+       /* Cleanup - all elements in pointer arrays have been copied */
+       kfree(splits_in_nents);
+       kfree(splits_in);
+       kfree(splits_out_nents);
+       kfree(splits_out);
+       kfree(split_sizes);
+       return ret;
 
 err_free_elements:
        list_for_each_entry_safe(el, temp, &sec_req->elements, head) {
@@ -854,7 +857,7 @@ err_free_elements:
                                 crypto_skcipher_ivsize(atfm),
                                 DMA_BIDIRECTIONAL);
 err_unmap_out_sg:
-       if (skreq->src != skreq->dst)
+       if (split)
                sec_unmap_sg_on_err(skreq->dst, steps, splits_out,
                                    splits_out_nents, sec_req->len_out,
                                    info->dev);
index 5b44ef2..fc359ca 100644 (file)
@@ -184,6 +184,7 @@ static long udmabuf_create(const struct udmabuf_create_list *head,
        exp_info.ops  = &udmabuf_ops;
        exp_info.size = ubuf->pagecount << PAGE_SHIFT;
        exp_info.priv = ubuf;
+       exp_info.flags = O_RDWR;
 
        buf = dma_buf_export(&exp_info);
        if (IS_ERR(buf)) {
index 7cbac6e..01d936c 100644 (file)
@@ -1641,6 +1641,12 @@ static void atc_free_chan_resources(struct dma_chan *chan)
        atchan->descs_allocated = 0;
        atchan->status = 0;
 
+       /*
+        * Free atslave allocated in at_dma_xlate()
+        */
+       kfree(chan->private);
+       chan->private = NULL;
+
        dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
 }
 
@@ -1675,7 +1681,7 @@ static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE, mask);
 
-       atslave = devm_kzalloc(&dmac_pdev->dev, sizeof(*atslave), GFP_KERNEL);
+       atslave = kzalloc(sizeof(*atslave), GFP_KERNEL);
        if (!atslave)
                return NULL;
 
@@ -2000,6 +2006,8 @@ static int at_dma_remove(struct platform_device *pdev)
        struct resource         *io;
 
        at_dma_off(atdma);
+       if (pdev->dev.of_node)
+               of_dma_controller_free(pdev->dev.of_node);
        dma_async_device_unregister(&atdma->dma_common);
 
        dma_pool_destroy(atdma->memset_pool);
index d0c3e50..1fc488e 100644 (file)
@@ -1059,12 +1059,12 @@ static void dwc_issue_pending(struct dma_chan *chan)
 /*
  * Program FIFO size of channels.
  *
- * By default full FIFO (1024 bytes) is assigned to channel 0. Here we
+ * By default full FIFO (512 bytes) is assigned to channel 0. Here we
  * slice FIFO on equal parts between channels.
  */
 static void idma32_fifo_partition(struct dw_dma *dw)
 {
-       u64 value = IDMA32C_FP_PSIZE_CH0(128) | IDMA32C_FP_PSIZE_CH1(128) |
+       u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) |
                    IDMA32C_FP_UPDATE;
        u64 fifo_partition = 0;
 
@@ -1077,7 +1077,7 @@ static void idma32_fifo_partition(struct dw_dma *dw)
        /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */
        fifo_partition |= value << 32;
 
-       /* Program FIFO Partition registers - 128 bytes for each channel */
+       /* Program FIFO Partition registers - 64 bytes per channel */
        idma32_writeq(dw, FIFO_PARTITION1, fifo_partition);
        idma32_writeq(dw, FIFO_PARTITION0, fifo_partition);
 }
index b4ec2d2..cb1b44d 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/spinlock.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
-#include <linux/dmapool.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
 #include <linux/platform_device.h>
@@ -33,6 +32,7 @@
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_dma.h>
+#include <linux/workqueue.h>
 
 #include <asm/irq.h>
 #include <linux/platform_data/dma-imx-sdma.h>
@@ -376,7 +376,7 @@ struct sdma_channel {
        u32                             shp_addr, per_addr;
        enum dma_status                 status;
        struct imx_dma_data             data;
-       struct dma_pool                 *bd_pool;
+       struct work_struct              terminate_worker;
 };
 
 #define IMX_DMA_SG_LOOP                BIT(0)
@@ -1027,31 +1027,49 @@ static int sdma_disable_channel(struct dma_chan *chan)
 
        return 0;
 }
-
-static int sdma_disable_channel_with_delay(struct dma_chan *chan)
+static void sdma_channel_terminate_work(struct work_struct *work)
 {
-       struct sdma_channel *sdmac = to_sdma_chan(chan);
+       struct sdma_channel *sdmac = container_of(work, struct sdma_channel,
+                                                 terminate_worker);
        unsigned long flags;
        LIST_HEAD(head);
 
-       sdma_disable_channel(chan);
-       spin_lock_irqsave(&sdmac->vc.lock, flags);
-       vchan_get_all_descriptors(&sdmac->vc, &head);
-       sdmac->desc = NULL;
-       spin_unlock_irqrestore(&sdmac->vc.lock, flags);
-       vchan_dma_desc_free_list(&sdmac->vc, &head);
-
        /*
         * According to NXP R&D team a delay of one BD SDMA cost time
         * (maximum is 1ms) should be added after disable of the channel
         * bit, to ensure SDMA core has really been stopped after SDMA
         * clients call .device_terminate_all.
         */
-       mdelay(1);
+       usleep_range(1000, 2000);
+
+       spin_lock_irqsave(&sdmac->vc.lock, flags);
+       vchan_get_all_descriptors(&sdmac->vc, &head);
+       sdmac->desc = NULL;
+       spin_unlock_irqrestore(&sdmac->vc.lock, flags);
+       vchan_dma_desc_free_list(&sdmac->vc, &head);
+}
+
+static int sdma_disable_channel_async(struct dma_chan *chan)
+{
+       struct sdma_channel *sdmac = to_sdma_chan(chan);
+
+       sdma_disable_channel(chan);
+
+       if (sdmac->desc)
+               schedule_work(&sdmac->terminate_worker);
 
        return 0;
 }
 
+static void sdma_channel_synchronize(struct dma_chan *chan)
+{
+       struct sdma_channel *sdmac = to_sdma_chan(chan);
+
+       vchan_synchronize(&sdmac->vc);
+
+       flush_work(&sdmac->terminate_worker);
+}
+
 static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
 {
        struct sdma_engine *sdma = sdmac->sdma;
@@ -1192,10 +1210,11 @@ out:
 
 static int sdma_alloc_bd(struct sdma_desc *desc)
 {
+       u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor);
        int ret = 0;
 
-       desc->bd = dma_pool_alloc(desc->sdmac->bd_pool, GFP_NOWAIT,
-                                 &desc->bd_phys);
+       desc->bd = dma_zalloc_coherent(NULL, bd_size, &desc->bd_phys,
+                                       GFP_NOWAIT);
        if (!desc->bd) {
                ret = -ENOMEM;
                goto out;
@@ -1206,7 +1225,9 @@ out:
 
 static void sdma_free_bd(struct sdma_desc *desc)
 {
-       dma_pool_free(desc->sdmac->bd_pool, desc->bd, desc->bd_phys);
+       u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor);
+
+       dma_free_coherent(NULL, bd_size, desc->bd, desc->bd_phys);
 }
 
 static void sdma_desc_free(struct virt_dma_desc *vd)
@@ -1272,10 +1293,6 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan)
        if (ret)
                goto disable_clk_ahb;
 
-       sdmac->bd_pool = dma_pool_create("bd_pool", chan->device->dev,
-                               sizeof(struct sdma_buffer_descriptor),
-                               32, 0);
-
        return 0;
 
 disable_clk_ahb:
@@ -1290,7 +1307,9 @@ static void sdma_free_chan_resources(struct dma_chan *chan)
        struct sdma_channel *sdmac = to_sdma_chan(chan);
        struct sdma_engine *sdma = sdmac->sdma;
 
-       sdma_disable_channel_with_delay(chan);
+       sdma_disable_channel_async(chan);
+
+       sdma_channel_synchronize(chan);
 
        if (sdmac->event_id0)
                sdma_event_disable(sdmac, sdmac->event_id0);
@@ -1304,9 +1323,6 @@ static void sdma_free_chan_resources(struct dma_chan *chan)
 
        clk_disable(sdma->clk_ipg);
        clk_disable(sdma->clk_ahb);
-
-       dma_pool_destroy(sdmac->bd_pool);
-       sdmac->bd_pool = NULL;
 }
 
 static struct sdma_desc *sdma_transfer_init(struct sdma_channel *sdmac,
@@ -1999,6 +2015,8 @@ static int sdma_probe(struct platform_device *pdev)
 
                sdmac->channel = i;
                sdmac->vc.desc_free = sdma_desc_free;
+               INIT_WORK(&sdmac->terminate_worker,
+                               sdma_channel_terminate_work);
                /*
                 * Add the channel to the DMAC list. Do not add channel 0 though
                 * because we need it internally in the SDMA driver. This also means
@@ -2050,7 +2068,8 @@ static int sdma_probe(struct platform_device *pdev)
        sdma->dma_device.device_prep_slave_sg = sdma_prep_slave_sg;
        sdma->dma_device.device_prep_dma_cyclic = sdma_prep_dma_cyclic;
        sdma->dma_device.device_config = sdma_config;
-       sdma->dma_device.device_terminate_all = sdma_disable_channel_with_delay;
+       sdma->dma_device.device_terminate_all = sdma_disable_channel_async;
+       sdma->dma_device.device_synchronize = sdma_channel_synchronize;
        sdma->dma_device.src_addr_widths = SDMA_DMA_BUSWIDTHS;
        sdma->dma_device.dst_addr_widths = SDMA_DMA_BUSWIDTHS;
        sdma->dma_device.directions = SDMA_DMA_DIRECTIONS;
index 1497da3..e507ec3 100644 (file)
@@ -723,8 +723,22 @@ static int cppi41_stop_chan(struct dma_chan *chan)
 
        desc_phys = lower_32_bits(c->desc_phys);
        desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc);
-       if (!cdd->chan_busy[desc_num])
+       if (!cdd->chan_busy[desc_num]) {
+               struct cppi41_channel *cc, *_ct;
+
+               /*
+                * channels might still be in the pendling list if
+                * cppi41_dma_issue_pending() is called after
+                * cppi41_runtime_suspend() is called
+                */
+               list_for_each_entry_safe(cc, _ct, &cdd->pending, node) {
+                       if (cc != c)
+                               continue;
+                       list_del(&cc->node);
+                       break;
+               }
                return 0;
+       }
 
        ret = cppi41_tear_down_chan(c);
        if (ret)
index 388a929..1a6a77d 100644 (file)
@@ -265,6 +265,10 @@ void __init efi_init(void)
                                    (params.mmap & ~PAGE_MASK)));
 
        init_screen_info();
+
+       /* ARM does not permit early mappings to persist across paging_init() */
+       if (IS_ENABLED(CONFIG_ARM))
+               efi_memmap_unmap();
 }
 
 static int __init register_gop_device(void)
index 922cfb8..a00934d 100644 (file)
@@ -110,7 +110,7 @@ static int __init arm_enable_runtime_services(void)
 {
        u64 mapsize;
 
-       if (!efi_enabled(EFI_BOOT) || !efi_enabled(EFI_MEMMAP)) {
+       if (!efi_enabled(EFI_BOOT)) {
                pr_info("EFI services will not be available.\n");
                return 0;
        }
index 249eb70..415849b 100644 (file)
@@ -592,7 +592,11 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz,
 
                early_memunmap(tbl, sizeof(*tbl));
        }
+       return 0;
+}
 
+int __init efi_apply_persistent_mem_reservations(void)
+{
        if (efi.mem_reserve != EFI_INVALID_TABLE_ADDR) {
                unsigned long prsv = efi.mem_reserve;
 
@@ -963,36 +967,59 @@ bool efi_is_table_address(unsigned long phys_addr)
 }
 
 static DEFINE_SPINLOCK(efi_mem_reserve_persistent_lock);
+static struct linux_efi_memreserve *efi_memreserve_root __ro_after_init;
 
-int efi_mem_reserve_persistent(phys_addr_t addr, u64 size)
+static int __init efi_memreserve_map_root(void)
 {
-       struct linux_efi_memreserve *rsv, *parent;
-
        if (efi.mem_reserve == EFI_INVALID_TABLE_ADDR)
                return -ENODEV;
 
-       rsv = kmalloc(sizeof(*rsv), GFP_KERNEL);
-       if (!rsv)
+       efi_memreserve_root = memremap(efi.mem_reserve,
+                                      sizeof(*efi_memreserve_root),
+                                      MEMREMAP_WB);
+       if (WARN_ON_ONCE(!efi_memreserve_root))
                return -ENOMEM;
+       return 0;
+}
 
-       parent = memremap(efi.mem_reserve, sizeof(*rsv), MEMREMAP_WB);
-       if (!parent) {
-               kfree(rsv);
-               return -ENOMEM;
+int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size)
+{
+       struct linux_efi_memreserve *rsv;
+       int rc;
+
+       if (efi_memreserve_root == (void *)ULONG_MAX)
+               return -ENODEV;
+
+       if (!efi_memreserve_root) {
+               rc = efi_memreserve_map_root();
+               if (rc)
+                       return rc;
        }
 
+       rsv = kmalloc(sizeof(*rsv), GFP_ATOMIC);
+       if (!rsv)
+               return -ENOMEM;
+
        rsv->base = addr;
        rsv->size = size;
 
        spin_lock(&efi_mem_reserve_persistent_lock);
-       rsv->next = parent->next;
-       parent->next = __pa(rsv);
+       rsv->next = efi_memreserve_root->next;
+       efi_memreserve_root->next = __pa(rsv);
        spin_unlock(&efi_mem_reserve_persistent_lock);
 
-       memunmap(parent);
+       return 0;
+}
 
+static int __init efi_memreserve_root_init(void)
+{
+       if (efi_memreserve_root)
+               return 0;
+       if (efi_memreserve_map_root())
+               efi_memreserve_root = (void *)ULONG_MAX;
        return 0;
 }
+early_initcall(efi_memreserve_root_init);
 
 #ifdef CONFIG_KEXEC
 static int update_efi_random_seed(struct notifier_block *nb,
index 30ac0c9..3d36142 100644 (file)
@@ -75,6 +75,9 @@ void install_memreserve_table(efi_system_table_t *sys_table_arg)
        efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID;
        efi_status_t status;
 
+       if (IS_ENABLED(CONFIG_ARM))
+               return;
+
        status = efi_call_early(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv),
                                (void **)&rsv);
        if (status != EFI_SUCCESS) {
index 8830fa6..0c0d231 100644 (file)
@@ -158,6 +158,10 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
                        return efi_status;
                }
        }
+
+       /* shrink the FDT back to its minimum size */
+       fdt_pack(fdt);
+
        return EFI_SUCCESS;
 
 fdt_set_fail:
index fa2904f..38b686c 100644 (file)
@@ -118,6 +118,9 @@ int __init efi_memmap_init_early(struct efi_memory_map_data *data)
 
 void __init efi_memmap_unmap(void)
 {
+       if (!efi_enabled(EFI_MEMMAP))
+               return;
+
        if (!efi.memmap.late) {
                unsigned long size;
 
index a19d845..8903b9c 100644 (file)
@@ -67,7 +67,7 @@ struct efi_runtime_work efi_rts_work;
        }                                                               \
                                                                        \
        init_completion(&efi_rts_work.efi_rts_comp);                    \
-       INIT_WORK_ONSTACK(&efi_rts_work.work, efi_call_rts);            \
+       INIT_WORK(&efi_rts_work.work, efi_call_rts);                    \
        efi_rts_work.arg1 = _arg1;                                      \
        efi_rts_work.arg2 = _arg2;                                      \
        efi_rts_work.arg3 = _arg3;                                      \
index af3a20d..99c99a5 100644 (file)
@@ -46,6 +46,7 @@ config FSI_MASTER_AST_CF
        tristate "FSI master based on Aspeed ColdFire coprocessor"
        depends on GPIOLIB
        depends on GPIO_ASPEED
+       select GENERIC_ALLOCATOR
        ---help---
        This option enables a FSI master using the AST2400 and AST2500 GPIO
        lines driven by the internal ColdFire coprocessor. This requires
index df94021..81dc01a 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/fs.h>
 #include <linux/uaccess.h>
 #include <linux/slab.h>
-#include <linux/cdev.h>
 #include <linux/list.h>
 
 #include <uapi/linux/fsi.h>
index b01ba44..31e891f 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/of.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
+#include <linux/sched.h>
 #include <linux/serdev.h>
 #include <linux/slab.h>
 
@@ -63,7 +64,7 @@ static int gnss_serial_write_raw(struct gnss_device *gdev,
        int ret;
 
        /* write is only buffered synchronously */
-       ret = serdev_device_write(serdev, buf, count, 0);
+       ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT);
        if (ret < 0)
                return ret;
 
index 79cb989..2c22836 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
+#include <linux/sched.h>
 #include <linux/serdev.h>
 #include <linux/slab.h>
 #include <linux/wait.h>
@@ -83,7 +84,7 @@ static int sirf_write_raw(struct gnss_device *gdev, const unsigned char *buf,
        int ret;
 
        /* write is only buffered synchronously */
-       ret = serdev_device_write(serdev, buf, count, 0);
+       ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT);
        if (ret < 0)
                return ret;
 
@@ -167,7 +168,7 @@ static int sirf_set_active(struct sirf_data *data, bool active)
        else
                timeout = SIRF_HIBERNATE_TIMEOUT;
 
-       while (retries-- > 0) {
+       do {
                sirf_pulse_on_off(data);
                ret = sirf_wait_for_power_state(data, active, timeout);
                if (ret < 0) {
@@ -178,9 +179,9 @@ static int sirf_set_active(struct sirf_data *data, bool active)
                }
 
                break;
-       }
+       } while (retries--);
 
-       if (retries == 0)
+       if (retries < 0)
                return -ETIMEDOUT;
 
        return 0;
index 5c1564f..bdb29e5 100644 (file)
@@ -258,7 +258,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)
        chips->chip.set = davinci_gpio_set;
 
        chips->chip.ngpio = ngpio;
-       chips->chip.base = -1;
+       chips->chip.base = pdata->no_auto_base ? pdata->base : -1;
 
 #ifdef CONFIG_OF_GPIO
        chips->chip.of_gpio_n_cells = 2;
index 8269cff..6a50f9f 100644 (file)
@@ -35,8 +35,8 @@
 #define gpio_mockup_err(...)   pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__)
 
 enum {
-       GPIO_MOCKUP_DIR_OUT = 0,
-       GPIO_MOCKUP_DIR_IN = 1,
+       GPIO_MOCKUP_DIR_IN = 0,
+       GPIO_MOCKUP_DIR_OUT = 1,
 };
 
 /*
@@ -131,7 +131,7 @@ static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset)
 {
        struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
 
-       return chip->lines[offset].dir;
+       return !chip->lines[offset].dir;
 }
 
 static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset)
index bfe4c5c..e9600b5 100644 (file)
@@ -268,8 +268,8 @@ static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
 
        if (pxa_gpio_has_pinctrl()) {
                ret = pinctrl_gpio_direction_input(chip->base + offset);
-               if (!ret)
-                       return 0;
+               if (ret)
+                       return ret;
        }
 
        spin_lock_irqsave(&gpio_lock, flags);
index 230e415..a2cbb47 100644 (file)
@@ -1295,7 +1295,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
        gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
        if (!gdev->descs) {
                status = -ENOMEM;
-               goto err_free_gdev;
+               goto err_free_ida;
        }
 
        if (chip->ngpio == 0) {
@@ -1427,8 +1427,9 @@ err_free_label:
        kfree_const(gdev->label);
 err_free_descs:
        kfree(gdev->descs);
-err_free_gdev:
+err_free_ida:
        ida_simple_remove(&gpio_ida, gdev->id);
+err_free_gdev:
        /* failures here can mean systems won't boot... */
        pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__,
               gdev->base, gdev->base + gdev->ngpio - 1,
index 104b2e0..b0fc116 100644 (file)
@@ -233,7 +233,7 @@ enum amdgpu_kiq_irq {
 
 #define MAX_KIQ_REG_WAIT       5000 /* in usecs, 5ms */
 #define MAX_KIQ_REG_BAILOUT_INTERVAL   5 /* in msecs, 5ms */
-#define MAX_KIQ_REG_TRY 20
+#define MAX_KIQ_REG_TRY 80 /* 20 -> 80 */
 
 int amdgpu_device_ip_set_clockgating_state(void *dev,
                                           enum amd_ip_block_type block_type,
index c31a884..1580ec6 100644 (file)
@@ -501,8 +501,11 @@ void amdgpu_amdkfd_set_compute_idle(struct kgd_dev *kgd, bool idle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)kgd;
 
-       amdgpu_dpm_switch_power_profile(adev,
-                                       PP_SMC_POWER_PROFILE_COMPUTE, !idle);
+       if (adev->powerplay.pp_funcs &&
+           adev->powerplay.pp_funcs->switch_power_profile)
+               amdgpu_dpm_switch_power_profile(adev,
+                                               PP_SMC_POWER_PROFILE_COMPUTE,
+                                               !idle);
 }
 
 bool amdgpu_amdkfd_is_kfd_vmid(struct amdgpu_device *adev, u32 vmid)
index 8816c69..387f1cf 100644 (file)
@@ -330,7 +330,9 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
                        case CHIP_TOPAZ:
                                if (((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0x81)) ||
                                    ((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0x83)) ||
-                                   ((adev->pdev->device == 0x6907) && (adev->pdev->revision == 0x87))) {
+                                   ((adev->pdev->device == 0x6907) && (adev->pdev->revision == 0x87)) ||
+                                   ((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0xD1)) ||
+                                   ((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0xD3))) {
                                        info->is_kicker = true;
                                        strcpy(fw_name, "amdgpu/topaz_k_smc.bin");
                                } else
@@ -351,7 +353,6 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
                                if (type == CGS_UCODE_ID_SMU) {
                                        if (((adev->pdev->device == 0x67ef) &&
                                             ((adev->pdev->revision == 0xe0) ||
-                                             (adev->pdev->revision == 0xe2) ||
                                              (adev->pdev->revision == 0xe5))) ||
                                            ((adev->pdev->device == 0x67ff) &&
                                             ((adev->pdev->revision == 0xcf) ||
@@ -359,8 +360,13 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
                                              (adev->pdev->revision == 0xff)))) {
                                                info->is_kicker = true;
                                                strcpy(fw_name, "amdgpu/polaris11_k_smc.bin");
-                                       } else
+                                       } else if ((adev->pdev->device == 0x67ef) &&
+                                                  (adev->pdev->revision == 0xe2)) {
+                                               info->is_kicker = true;
+                                               strcpy(fw_name, "amdgpu/polaris11_k2_smc.bin");
+                                       } else {
                                                strcpy(fw_name, "amdgpu/polaris11_smc.bin");
+                                       }
                                } else if (type == CGS_UCODE_ID_SMU_SK) {
                                        strcpy(fw_name, "amdgpu/polaris11_smc_sk.bin");
                                }
@@ -375,17 +381,35 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
                                              (adev->pdev->revision == 0xe7) ||
                                              (adev->pdev->revision == 0xef))) ||
                                            ((adev->pdev->device == 0x6fdf) &&
-                                            (adev->pdev->revision == 0xef))) {
+                                            ((adev->pdev->revision == 0xef) ||
+                                             (adev->pdev->revision == 0xff)))) {
                                                info->is_kicker = true;
                                                strcpy(fw_name, "amdgpu/polaris10_k_smc.bin");
-                                       } else
+                                       } else if ((adev->pdev->device == 0x67df) &&
+                                                  ((adev->pdev->revision == 0xe1) ||
+                                                   (adev->pdev->revision == 0xf7))) {
+                                               info->is_kicker = true;
+                                               strcpy(fw_name, "amdgpu/polaris10_k2_smc.bin");
+                                       } else {
                                                strcpy(fw_name, "amdgpu/polaris10_smc.bin");
+                                       }
                                } else if (type == CGS_UCODE_ID_SMU_SK) {
                                        strcpy(fw_name, "amdgpu/polaris10_smc_sk.bin");
                                }
                                break;
                        case CHIP_POLARIS12:
-                               strcpy(fw_name, "amdgpu/polaris12_smc.bin");
+                               if (((adev->pdev->device == 0x6987) &&
+                                    ((adev->pdev->revision == 0xc0) ||
+                                     (adev->pdev->revision == 0xc3))) ||
+                                   ((adev->pdev->device == 0x6981) &&
+                                    ((adev->pdev->revision == 0x00) ||
+                                     (adev->pdev->revision == 0x01) ||
+                                     (adev->pdev->revision == 0x10)))) {
+                                       info->is_kicker = true;
+                                       strcpy(fw_name, "amdgpu/polaris12_k_smc.bin");
+                               } else {
+                                       strcpy(fw_name, "amdgpu/polaris12_smc.bin");
+                               }
                                break;
                        case CHIP_VEGAM:
                                strcpy(fw_name, "amdgpu/vegam_smc.bin");
index 663043c..0acc8de 100644 (file)
@@ -124,14 +124,14 @@ static int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, union drm_amdgpu_cs
                goto free_chunk;
        }
 
+       mutex_lock(&p->ctx->lock);
+
        /* skip guilty context job */
        if (atomic_read(&p->ctx->guilty) == 1) {
                ret = -ECANCELED;
                goto free_chunk;
        }
 
-       mutex_lock(&p->ctx->lock);
-
        /* get chunks */
        chunk_array_user = u64_to_user_ptr(cs->in.chunks);
        if (copy_from_user(chunk_array, chunk_array_user,
index f9b5423..95f4c41 100644 (file)
@@ -39,6 +39,7 @@ const unsigned int amdgpu_ctx_num_entities[AMDGPU_HW_IP_NUM] = {
        [AMDGPU_HW_IP_UVD_ENC]  =       1,
        [AMDGPU_HW_IP_VCN_DEC]  =       1,
        [AMDGPU_HW_IP_VCN_ENC]  =       1,
+       [AMDGPU_HW_IP_VCN_JPEG] =       1,
 };
 
 static int amdgput_ctx_total_num_entities(void)
index 6748cd7..686a26d 100644 (file)
@@ -626,6 +626,13 @@ int amdgpu_display_modeset_create_props(struct amdgpu_device *adev)
                                         "dither",
                                         amdgpu_dither_enum_list, sz);
 
+       if (amdgpu_device_has_dc_support(adev)) {
+               adev->mode_info.max_bpc_property =
+                       drm_property_create_range(adev->ddev, 0, "max bpc", 8, 16);
+               if (!adev->mode_info.max_bpc_property)
+                       return -ENOMEM;
+       }
+
        return 0;
 }
 
index 8de55f7..74b611e 100644 (file)
@@ -872,7 +872,13 @@ static const struct pci_device_id pciidlist[] = {
        {0x1002, 0x6864, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
        {0x1002, 0x6867, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
        {0x1002, 0x6868, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
+       {0x1002, 0x6869, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
+       {0x1002, 0x686a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
+       {0x1002, 0x686b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
        {0x1002, 0x686c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
+       {0x1002, 0x686d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
+       {0x1002, 0x686e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
+       {0x1002, 0x686f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
        {0x1002, 0x687f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10},
        /* Vega 12 */
        {0x1002, 0x69A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA12},
@@ -885,6 +891,7 @@ static const struct pci_device_id pciidlist[] = {
        {0x1002, 0x66A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20},
        {0x1002, 0x66A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20},
        {0x1002, 0x66A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20},
+       {0x1002, 0x66A4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20},
        {0x1002, 0x66A7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20},
        {0x1002, 0x66AF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20},
        /* Raven */
index 81732a8..8f3d44e 100644 (file)
@@ -467,9 +467,6 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
        if (!info->return_size || !info->return_pointer)
                return -EINVAL;
 
-       /* Ensure IB tests are run on ring */
-       flush_delayed_work(&adev->late_init_work);
-
        switch (info->query) {
        case AMDGPU_INFO_ACCEL_WORKING:
                ui32 = adev->accel_working;
@@ -950,6 +947,9 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
        struct amdgpu_fpriv *fpriv;
        int r, pasid;
 
+       /* Ensure IB tests are run on ring */
+       flush_delayed_work(&adev->late_init_work);
+
        file_priv->driver_priv = NULL;
 
        r = pm_runtime_get_sync(dev->dev);
index b9e9e8b..d1b4d9b 100644 (file)
@@ -339,6 +339,8 @@ struct amdgpu_mode_info {
        struct drm_property *audio_property;
        /* FMT dithering */
        struct drm_property *dither_property;
+       /* maximum number of bits per channel for monitor color */
+       struct drm_property *max_bpc_property;
        /* hardcoded DFP edid from BIOS */
        struct edid *bios_hardcoded_edid;
        int bios_hardcoded_edid_size;
index 352b304..0877ff9 100644 (file)
@@ -181,7 +181,7 @@ static unsigned amdgpu_vm_num_entries(struct amdgpu_device *adev,
 
        if (level == adev->vm_manager.root_level)
                /* For the root directory */
-               return round_up(adev->vm_manager.max_pfn, 1 << shift) >> shift;
+               return round_up(adev->vm_manager.max_pfn, 1ULL << shift) >> shift;
        else if (level != AMDGPU_VM_PTB)
                /* Everything in between */
                return 512;
@@ -1632,13 +1632,6 @@ static int amdgpu_vm_update_ptes(struct amdgpu_pte_update_params *params,
                        continue;
                }
 
-               /* First check if the entry is already handled */
-               if (cursor.pfn < frag_start) {
-                       cursor.entry->huge = true;
-                       amdgpu_vm_pt_next(adev, &cursor);
-                       continue;
-               }
-
                /* If it isn't already handled it can't be a huge page */
                if (cursor.entry->huge) {
                        /* Add the entry to the relocated list to update it. */
@@ -1663,9 +1656,11 @@ static int amdgpu_vm_update_ptes(struct amdgpu_pte_update_params *params,
                        if (!amdgpu_vm_pt_descendant(adev, &cursor))
                                return -ENOENT;
                        continue;
-               } else if (frag >= parent_shift) {
+               } else if (frag >= parent_shift &&
+                          cursor.level - 1 != adev->vm_manager.root_level) {
                        /* If the fragment size is even larger than the parent
-                        * shift we should go up one level and check it again.
+                        * shift we should go up one level and check it again
+                        * unless one level up is the root level.
                         */
                        if (!amdgpu_vm_pt_ancestor(&cursor))
                                return -ENOENT;
@@ -1673,10 +1668,10 @@ static int amdgpu_vm_update_ptes(struct amdgpu_pte_update_params *params,
                }
 
                /* Looks good so far, calculate parameters for the update */
-               incr = AMDGPU_GPU_PAGE_SIZE << shift;
+               incr = (uint64_t)AMDGPU_GPU_PAGE_SIZE << shift;
                mask = amdgpu_vm_entries_mask(adev, cursor.level);
                pe_start = ((cursor.pfn >> shift) & mask) * 8;
-               entry_end = (mask + 1) << shift;
+               entry_end = (uint64_t)(mask + 1) << shift;
                entry_end += cursor.pfn & ~(entry_end - 1);
                entry_end = min(entry_end, end);
 
@@ -1689,7 +1684,7 @@ static int amdgpu_vm_update_ptes(struct amdgpu_pte_update_params *params,
                                              flags | AMDGPU_PTE_FRAG(frag));
 
                        pe_start += nptes * 8;
-                       dst += nptes * AMDGPU_GPU_PAGE_SIZE << shift;
+                       dst += (uint64_t)nptes * AMDGPU_GPU_PAGE_SIZE << shift;
 
                        frag_start = upd_end;
                        if (frag_start >= frag_end) {
@@ -1701,8 +1696,17 @@ static int amdgpu_vm_update_ptes(struct amdgpu_pte_update_params *params,
                        }
                } while (frag_start < entry_end);
 
-               if (frag >= shift)
+               if (amdgpu_vm_pt_descendant(adev, &cursor)) {
+                       /* Mark all child entries as huge */
+                       while (cursor.pfn < frag_start) {
+                               cursor.entry->huge = true;
+                               amdgpu_vm_pt_next(adev, &cursor);
+                       }
+
+               } else if (frag >= shift) {
+                       /* or just move on to the next on the same level. */
                        amdgpu_vm_pt_next(adev, &cursor);
+               }
        }
 
        return 0;
index 6d7baf5..21363b2 100644 (file)
@@ -2440,12 +2440,13 @@ static void gfx_v9_0_rlc_start(struct amdgpu_device *adev)
 #endif
 
        WREG32_FIELD15(GC, 0, RLC_CNTL, RLC_ENABLE_F32, 1);
+       udelay(50);
 
        /* carrizo do enable cp interrupt after cp inited */
-       if (!(adev->flags & AMD_IS_APU))
+       if (!(adev->flags & AMD_IS_APU)) {
                gfx_v9_0_enable_gui_idle_interrupt(adev, true);
-
-       udelay(50);
+               udelay(50);
+       }
 
 #ifdef AMDGPU_RLC_DEBUG_RETRY
        /* RLC_GPM_GENERAL_6 : RLC Ucode version */
index ceb7847..bfa317a 100644 (file)
@@ -72,7 +72,7 @@ static void gfxhub_v1_0_init_system_aperture_regs(struct amdgpu_device *adev)
 
        /* Program the system aperture low logical page number. */
        WREG32_SOC15(GC, 0, mmMC_VM_SYSTEM_APERTURE_LOW_ADDR,
-                    min(adev->gmc.vram_start, adev->gmc.agp_start) >> 18);
+                    min(adev->gmc.fb_start, adev->gmc.agp_start) >> 18);
 
        if (adev->asic_type == CHIP_RAVEN && adev->rev_id >= 0x8)
                /*
@@ -82,11 +82,11 @@ static void gfxhub_v1_0_init_system_aperture_regs(struct amdgpu_device *adev)
                 * to get rid of the VM fault and hardware hang.
                 */
                WREG32_SOC15(GC, 0, mmMC_VM_SYSTEM_APERTURE_HIGH_ADDR,
-                            max((adev->gmc.vram_end >> 18) + 0x1,
+                            max((adev->gmc.fb_end >> 18) + 0x1,
                                 adev->gmc.agp_end >> 18));
        else
                WREG32_SOC15(GC, 0, mmMC_VM_SYSTEM_APERTURE_HIGH_ADDR,
-                            max(adev->gmc.vram_end, adev->gmc.agp_end) >> 18);
+                            max(adev->gmc.fb_end, adev->gmc.agp_end) >> 18);
 
        /* Set default page address. */
        value = adev->vram_scratch.gpu_addr - adev->gmc.vram_start
index e1c2b4e..73ad02a 100644 (file)
@@ -46,6 +46,7 @@ MODULE_FIRMWARE("amdgpu/tahiti_mc.bin");
 MODULE_FIRMWARE("amdgpu/pitcairn_mc.bin");
 MODULE_FIRMWARE("amdgpu/verde_mc.bin");
 MODULE_FIRMWARE("amdgpu/oland_mc.bin");
+MODULE_FIRMWARE("amdgpu/hainan_mc.bin");
 MODULE_FIRMWARE("amdgpu/si58_mc.bin");
 
 #define MC_SEQ_MISC0__MT__MASK   0xf0000000
index 1d3265c..747c068 100644 (file)
@@ -56,6 +56,9 @@ MODULE_FIRMWARE("amdgpu/tonga_mc.bin");
 MODULE_FIRMWARE("amdgpu/polaris11_mc.bin");
 MODULE_FIRMWARE("amdgpu/polaris10_mc.bin");
 MODULE_FIRMWARE("amdgpu/polaris12_mc.bin");
+MODULE_FIRMWARE("amdgpu/polaris11_k_mc.bin");
+MODULE_FIRMWARE("amdgpu/polaris10_k_mc.bin");
+MODULE_FIRMWARE("amdgpu/polaris12_k_mc.bin");
 
 static const u32 golden_settings_tonga_a11[] =
 {
@@ -224,13 +227,39 @@ static int gmc_v8_0_init_microcode(struct amdgpu_device *adev)
                chip_name = "tonga";
                break;
        case CHIP_POLARIS11:
-               chip_name = "polaris11";
+               if (((adev->pdev->device == 0x67ef) &&
+                    ((adev->pdev->revision == 0xe0) ||
+                     (adev->pdev->revision == 0xe5))) ||
+                   ((adev->pdev->device == 0x67ff) &&
+                    ((adev->pdev->revision == 0xcf) ||
+                     (adev->pdev->revision == 0xef) ||
+                     (adev->pdev->revision == 0xff))))
+                       chip_name = "polaris11_k";
+               else if ((adev->pdev->device == 0x67ef) &&
+                        (adev->pdev->revision == 0xe2))
+                       chip_name = "polaris11_k";
+               else
+                       chip_name = "polaris11";
                break;
        case CHIP_POLARIS10:
-               chip_name = "polaris10";
+               if ((adev->pdev->device == 0x67df) &&
+                   ((adev->pdev->revision == 0xe1) ||
+                    (adev->pdev->revision == 0xf7)))
+                       chip_name = "polaris10_k";
+               else
+                       chip_name = "polaris10";
                break;
        case CHIP_POLARIS12:
-               chip_name = "polaris12";
+               if (((adev->pdev->device == 0x6987) &&
+                    ((adev->pdev->revision == 0xc0) ||
+                     (adev->pdev->revision == 0xc3))) ||
+                   ((adev->pdev->device == 0x6981) &&
+                    ((adev->pdev->revision == 0x00) ||
+                     (adev->pdev->revision == 0x01) ||
+                     (adev->pdev->revision == 0x10))))
+                       chip_name = "polaris12_k";
+               else
+                       chip_name = "polaris12";
                break;
        case CHIP_FIJI:
        case CHIP_CARRIZO:
@@ -337,7 +366,7 @@ static int gmc_v8_0_polaris_mc_load_microcode(struct amdgpu_device *adev)
        const struct mc_firmware_header_v1_0 *hdr;
        const __le32 *fw_data = NULL;
        const __le32 *io_mc_regs = NULL;
-       u32 data, vbios_version;
+       u32 data;
        int i, ucode_size, regs_size;
 
        /* Skip MC ucode loading on SR-IOV capable boards.
@@ -348,13 +377,6 @@ static int gmc_v8_0_polaris_mc_load_microcode(struct amdgpu_device *adev)
        if (amdgpu_sriov_bios(adev))
                return 0;
 
-       WREG32(mmMC_SEQ_IO_DEBUG_INDEX, 0x9F);
-       data = RREG32(mmMC_SEQ_IO_DEBUG_DATA);
-       vbios_version = data & 0xf;
-
-       if (vbios_version == 0)
-               return 0;
-
        if (!adev->gmc.fw)
                return -EINVAL;
 
index fd23ba1..a0db67a 100644 (file)
@@ -90,7 +90,7 @@ static void mmhub_v1_0_init_system_aperture_regs(struct amdgpu_device *adev)
 
        /* Program the system aperture low logical page number. */
        WREG32_SOC15(MMHUB, 0, mmMC_VM_SYSTEM_APERTURE_LOW_ADDR,
-                    min(adev->gmc.vram_start, adev->gmc.agp_start) >> 18);
+                    min(adev->gmc.fb_start, adev->gmc.agp_start) >> 18);
 
        if (adev->asic_type == CHIP_RAVEN && adev->rev_id >= 0x8)
                /*
@@ -100,11 +100,11 @@ static void mmhub_v1_0_init_system_aperture_regs(struct amdgpu_device *adev)
                 * to get rid of the VM fault and hardware hang.
                 */
                WREG32_SOC15(MMHUB, 0, mmMC_VM_SYSTEM_APERTURE_HIGH_ADDR,
-                            max((adev->gmc.vram_end >> 18) + 0x1,
+                            max((adev->gmc.fb_end >> 18) + 0x1,
                                 adev->gmc.agp_end >> 18));
        else
                WREG32_SOC15(MMHUB, 0, mmMC_VM_SYSTEM_APERTURE_HIGH_ADDR,
-                            max(adev->gmc.vram_end, adev->gmc.agp_end) >> 18);
+                            max(adev->gmc.fb_end, adev->gmc.agp_end) >> 18);
 
        /* Set default page address. */
        value = adev->vram_scratch.gpu_addr - adev->gmc.vram_start +
index bf5e6a4..4cc0dcb 100644 (file)
 #define mmMP0_MISC_LIGHT_SLEEP_CTRL                                                             0x01ba
 #define mmMP0_MISC_LIGHT_SLEEP_CTRL_BASE_IDX                                                    0
 
+/* for Vega20 register name change */
+#define mmHDP_MEM_POWER_CTRL   0x00d4
+#define HDP_MEM_POWER_CTRL__IPH_MEM_POWER_CTRL_EN_MASK 0x00000001L
+#define HDP_MEM_POWER_CTRL__IPH_MEM_POWER_LS_EN_MASK   0x00000002L
+#define HDP_MEM_POWER_CTRL__RC_MEM_POWER_CTRL_EN_MASK  0x00010000L
+#define HDP_MEM_POWER_CTRL__RC_MEM_POWER_LS_EN_MASK            0x00020000L
+#define mmHDP_MEM_POWER_CTRL_BASE_IDX  0
 /*
  * Indirect registers accessor
  */
@@ -870,15 +877,33 @@ static void soc15_update_hdp_light_sleep(struct amdgpu_device *adev, bool enable
 {
        uint32_t def, data;
 
-       def = data = RREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_LS));
+       if (adev->asic_type == CHIP_VEGA20) {
+               def = data = RREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_CTRL));
 
-       if (enable && (adev->cg_flags & AMD_CG_SUPPORT_HDP_LS))
-               data |= HDP_MEM_POWER_LS__LS_ENABLE_MASK;
-       else
-               data &= ~HDP_MEM_POWER_LS__LS_ENABLE_MASK;
+               if (enable && (adev->cg_flags & AMD_CG_SUPPORT_HDP_LS))
+                       data |= HDP_MEM_POWER_CTRL__IPH_MEM_POWER_CTRL_EN_MASK |
+                               HDP_MEM_POWER_CTRL__IPH_MEM_POWER_LS_EN_MASK |
+                               HDP_MEM_POWER_CTRL__RC_MEM_POWER_CTRL_EN_MASK |
+                               HDP_MEM_POWER_CTRL__RC_MEM_POWER_LS_EN_MASK;
+               else
+                       data &= ~(HDP_MEM_POWER_CTRL__IPH_MEM_POWER_CTRL_EN_MASK |
+                               HDP_MEM_POWER_CTRL__IPH_MEM_POWER_LS_EN_MASK |
+                               HDP_MEM_POWER_CTRL__RC_MEM_POWER_CTRL_EN_MASK |
+                               HDP_MEM_POWER_CTRL__RC_MEM_POWER_LS_EN_MASK);
 
-       if (def != data)
-               WREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_LS), data);
+               if (def != data)
+                       WREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_CTRL), data);
+       } else {
+               def = data = RREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_LS));
+
+               if (enable && (adev->cg_flags & AMD_CG_SUPPORT_HDP_LS))
+                       data |= HDP_MEM_POWER_LS__LS_ENABLE_MASK;
+               else
+                       data &= ~HDP_MEM_POWER_LS__LS_ENABLE_MASK;
+
+               if (def != data)
+                       WREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_LS), data);
+       }
 }
 
 static void soc15_update_drm_clock_gating(struct amdgpu_device *adev, bool enable)
index eae9092..322e09b 100644 (file)
@@ -48,6 +48,7 @@ static void vcn_v1_0_set_enc_ring_funcs(struct amdgpu_device *adev);
 static void vcn_v1_0_set_jpeg_ring_funcs(struct amdgpu_device *adev);
 static void vcn_v1_0_set_irq_funcs(struct amdgpu_device *adev);
 static void vcn_v1_0_jpeg_ring_set_patch_ring(struct amdgpu_ring *ring, uint32_t ptr);
+static int vcn_v1_0_set_powergating_state(void *handle, enum amd_powergating_state state);
 
 /**
  * vcn_v1_0_early_init - set function pointers
@@ -222,7 +223,7 @@ static int vcn_v1_0_hw_fini(void *handle)
        struct amdgpu_ring *ring = &adev->vcn.ring_dec;
 
        if (RREG32_SOC15(VCN, 0, mmUVD_STATUS))
-               vcn_v1_0_stop(adev);
+               vcn_v1_0_set_powergating_state(adev, AMD_PG_STATE_GATE);
 
        ring->ready = false;
 
index a99f717..a0fda6f 100644 (file)
@@ -129,7 +129,7 @@ static int vega10_ih_irq_init(struct amdgpu_device *adev)
        else
                wptr_off = adev->wb.gpu_addr + (adev->irq.ih.wptr_offs * 4);
        WREG32_SOC15(OSSSYS, 0, mmIH_RB_WPTR_ADDR_LO, lower_32_bits(wptr_off));
-       WREG32_SOC15(OSSSYS, 0, mmIH_RB_WPTR_ADDR_HI, upper_32_bits(wptr_off) & 0xFF);
+       WREG32_SOC15(OSSSYS, 0, mmIH_RB_WPTR_ADDR_HI, upper_32_bits(wptr_off) & 0xFFFF);
 
        /* set rptr, wptr to 0 */
        WREG32_SOC15(OSSSYS, 0, mmIH_RB_RPTR, 0);
index a9f18ea..e4ded89 100644 (file)
@@ -337,12 +337,19 @@ static const struct kfd_deviceid supported_devices[] = {
        { 0x6864, &vega10_device_info },        /* Vega10 */
        { 0x6867, &vega10_device_info },        /* Vega10 */
        { 0x6868, &vega10_device_info },        /* Vega10 */
+       { 0x6869, &vega10_device_info },        /* Vega10 */
+       { 0x686A, &vega10_device_info },        /* Vega10 */
+       { 0x686B, &vega10_device_info },        /* Vega10 */
        { 0x686C, &vega10_vf_device_info },     /* Vega10  vf*/
+       { 0x686D, &vega10_device_info },        /* Vega10 */
+       { 0x686E, &vega10_device_info },        /* Vega10 */
+       { 0x686F, &vega10_device_info },        /* Vega10 */
        { 0x687F, &vega10_device_info },        /* Vega10 */
        { 0x66a0, &vega20_device_info },        /* Vega20 */
        { 0x66a1, &vega20_device_info },        /* Vega20 */
        { 0x66a2, &vega20_device_info },        /* Vega20 */
        { 0x66a3, &vega20_device_info },        /* Vega20 */
+       { 0x66a4, &vega20_device_info },        /* Vega20 */
        { 0x66a7, &vega20_device_info },        /* Vega20 */
        { 0x66af, &vega20_device_info }         /* Vega20 */
 };
index c1262f6..5a6edf6 100644 (file)
@@ -2358,8 +2358,15 @@ static void update_stream_scaling_settings(const struct drm_display_mode *mode,
 static enum dc_color_depth
 convert_color_depth_from_display_info(const struct drm_connector *connector)
 {
+       struct dm_connector_state *dm_conn_state =
+               to_dm_connector_state(connector->state);
        uint32_t bpc = connector->display_info.bpc;
 
+       /* TODO: Remove this when there's support for max_bpc in drm */
+       if (dm_conn_state && bpc > dm_conn_state->max_bpc)
+               /* Round down to nearest even number. */
+               bpc = dm_conn_state->max_bpc - (dm_conn_state->max_bpc & 1);
+
        switch (bpc) {
        case 0:
                /*
@@ -2547,9 +2554,9 @@ static void fill_audio_info(struct audio_info *audio_info,
 
        cea_revision = drm_connector->display_info.cea_rev;
 
-       strncpy(audio_info->display_name,
+       strscpy(audio_info->display_name,
                edid_caps->display_name,
-               AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS - 1);
+               AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
 
        if (cea_revision >= 3) {
                audio_info->mode_count = edid_caps->audio_mode_count;
@@ -2943,6 +2950,9 @@ int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector,
        } else if (property == adev->mode_info.underscan_property) {
                dm_new_state->underscan_enable = val;
                ret = 0;
+       } else if (property == adev->mode_info.max_bpc_property) {
+               dm_new_state->max_bpc = val;
+               ret = 0;
        }
 
        return ret;
@@ -2985,6 +2995,9 @@ int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector,
        } else if (property == adev->mode_info.underscan_property) {
                *val = dm_state->underscan_enable;
                ret = 0;
+       } else if (property == adev->mode_info.max_bpc_property) {
+               *val = dm_state->max_bpc;
+               ret = 0;
        }
        return ret;
 }
@@ -3029,6 +3042,7 @@ void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector)
                state->underscan_enable = false;
                state->underscan_hborder = 0;
                state->underscan_vborder = 0;
+               state->max_bpc = 8;
 
                __drm_atomic_helper_connector_reset(connector, &state->base);
        }
@@ -3050,6 +3064,7 @@ amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector)
 
        new_state->freesync_capable = state->freesync_capable;
        new_state->freesync_enable = state->freesync_enable;
+       new_state->max_bpc = state->max_bpc;
 
        return &new_state->base;
 }
@@ -3637,7 +3652,7 @@ amdgpu_dm_create_common_mode(struct drm_encoder *encoder,
        mode->hdisplay = hdisplay;
        mode->vdisplay = vdisplay;
        mode->type &= ~DRM_MODE_TYPE_PREFERRED;
-       strncpy(mode->name, name, DRM_DISPLAY_MODE_LEN);
+       strscpy(mode->name, name, DRM_DISPLAY_MODE_LEN);
 
        return mode;
 
@@ -3795,6 +3810,9 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
        drm_object_attach_property(&aconnector->base.base,
                                adev->mode_info.underscan_vborder_property,
                                0);
+       drm_object_attach_property(&aconnector->base.base,
+                               adev->mode_info.max_bpc_property,
+                               0);
 
 }
 
index 924a38a..6e069d7 100644 (file)
@@ -204,6 +204,7 @@ struct dm_connector_state {
        enum amdgpu_rmx_type scaling;
        uint8_t underscan_vborder;
        uint8_t underscan_hborder;
+       uint8_t max_bpc;
        bool underscan_enable;
        bool freesync_enable;
        bool freesync_capable;
index d02c32a..1b0d209 100644 (file)
@@ -342,10 +342,9 @@ dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
                master->connector_id);
 
        aconnector->mst_encoder = dm_dp_create_fake_mst_encoder(master);
+       drm_connector_attach_encoder(&aconnector->base,
+                                    &aconnector->mst_encoder->base);
 
-       /*
-        * TODO: understand why this one is needed
-        */
        drm_object_attach_property(
                &connector->base,
                dev->mode_config.path_property,
index b459867..a6bcb90 100644 (file)
@@ -2512,6 +2512,8 @@ static void pplib_apply_display_requirements(
                        dc,
                        context->bw.dce.sclk_khz);
 
+       pp_display_cfg->min_dcfclock_khz = pp_display_cfg->min_engine_clock_khz;
+
        pp_display_cfg->min_engine_clock_deep_sleep_khz
                        = context->bw.dce.sclk_deep_sleep_khz;
 
index 85119c2..a2a7e0e 100644 (file)
@@ -80,7 +80,9 @@ int phm_enable_dynamic_state_management(struct pp_hwmgr *hwmgr)
        PHM_FUNC_CHECK(hwmgr);
        adev = hwmgr->adev;
 
-       if (smum_is_dpm_running(hwmgr) && !amdgpu_passthrough(adev)) {
+       /* Skip for suspend/resume case */
+       if (smum_is_dpm_running(hwmgr) && !amdgpu_passthrough(adev)
+               && adev->in_suspend) {
                pr_info("dpm has been enabled\n");
                return 0;
        }
index 47ac923..0173d04 100644 (file)
@@ -352,6 +352,9 @@ int hwmgr_handle_task(struct pp_hwmgr *hwmgr, enum amd_pp_task task_id,
 
        switch (task_id) {
        case AMD_PP_TASK_DISPLAY_CONFIG_CHANGE:
+               ret = phm_pre_display_configuration_changed(hwmgr);
+               if (ret)
+                       return ret;
                ret = phm_set_cpu_power_state(hwmgr);
                if (ret)
                        return ret;
index 91ffb7b..5643786 100644 (file)
@@ -265,8 +265,6 @@ int psm_adjust_power_state_dynamic(struct pp_hwmgr *hwmgr, bool skip,
        if (skip)
                return 0;
 
-       phm_pre_display_configuration_changed(hwmgr);
-
        phm_display_configuration_changed(hwmgr);
 
        if (hwmgr->ps)
index ed35ec0..b61a01f 100644 (file)
@@ -3589,8 +3589,10 @@ static int smu7_find_dpm_states_clocks_in_dpm_table(struct pp_hwmgr *hwmgr, cons
        }
 
        if (i >= sclk_table->count) {
-               data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
-               sclk_table->dpm_levels[i-1].value = sclk;
+               if (sclk > sclk_table->dpm_levels[i-1].value) {
+                       data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
+                       sclk_table->dpm_levels[i-1].value = sclk;
+               }
        } else {
        /* TODO: Check SCLK in DAL's minimum clocks
         * in case DeepSleep divider update is required.
@@ -3607,8 +3609,10 @@ static int smu7_find_dpm_states_clocks_in_dpm_table(struct pp_hwmgr *hwmgr, cons
        }
 
        if (i >= mclk_table->count) {
-               data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
-               mclk_table->dpm_levels[i-1].value = mclk;
+               if (mclk > mclk_table->dpm_levels[i-1].value) {
+                       data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
+                       mclk_table->dpm_levels[i-1].value = mclk;
+               }
        }
 
        if (data->display_timing.num_existing_displays != hwmgr->display_config->num_display)
@@ -4525,12 +4529,12 @@ static int smu7_get_sclk_od(struct pp_hwmgr *hwmgr)
        struct smu7_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
        struct smu7_single_dpm_table *golden_sclk_table =
                        &(data->golden_dpm_table.sclk_table);
-       int value;
+       int value = sclk_table->dpm_levels[sclk_table->count - 1].value;
+       int golden_value = golden_sclk_table->dpm_levels
+                       [golden_sclk_table->count - 1].value;
 
-       value = (sclk_table->dpm_levels[sclk_table->count - 1].value -
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value) *
-                       100 /
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
+       value -= golden_value;
+       value = DIV_ROUND_UP(value * 100, golden_value);
 
        return value;
 }
@@ -4567,12 +4571,12 @@ static int smu7_get_mclk_od(struct pp_hwmgr *hwmgr)
        struct smu7_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
        struct smu7_single_dpm_table *golden_mclk_table =
                        &(data->golden_dpm_table.mclk_table);
-       int value;
+        int value = mclk_table->dpm_levels[mclk_table->count - 1].value;
+       int golden_value = golden_mclk_table->dpm_levels
+                       [golden_mclk_table->count - 1].value;
 
-       value = (mclk_table->dpm_levels[mclk_table->count - 1].value -
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value) *
-                       100 /
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
+       value -= golden_value;
+       value = DIV_ROUND_UP(value * 100, golden_value);
 
        return value;
 }
index 99a33c3..101c09b 100644 (file)
@@ -713,20 +713,20 @@ int smu_set_watermarks_for_clocks_ranges(void *wt_table,
        for (i = 0; i < wm_with_clock_ranges->num_wm_dmif_sets; i++) {
                table->WatermarkRow[1][i].MinClock =
                        cpu_to_le16((uint16_t)
-                       (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_min_dcfclk_clk_in_khz) /
-                       1000);
+                       (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_min_dcfclk_clk_in_khz /
+                       1000));
                table->WatermarkRow[1][i].MaxClock =
                        cpu_to_le16((uint16_t)
-                       (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_max_dcfclk_clk_in_khz) /
-                       1000);
+                       (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_max_dcfclk_clk_in_khz /
+                       1000));
                table->WatermarkRow[1][i].MinUclk =
                        cpu_to_le16((uint16_t)
-                       (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_min_mem_clk_in_khz) /
-                       1000);
+                       (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_min_mem_clk_in_khz /
+                       1000));
                table->WatermarkRow[1][i].MaxUclk =
                        cpu_to_le16((uint16_t)
-                       (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_max_mem_clk_in_khz) /
-                       1000);
+                       (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_max_mem_clk_in_khz /
+                       1000));
                table->WatermarkRow[1][i].WmSetting = (uint8_t)
                                wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_set_id;
        }
@@ -734,20 +734,20 @@ int smu_set_watermarks_for_clocks_ranges(void *wt_table,
        for (i = 0; i < wm_with_clock_ranges->num_wm_mcif_sets; i++) {
                table->WatermarkRow[0][i].MinClock =
                        cpu_to_le16((uint16_t)
-                       (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_min_socclk_clk_in_khz) /
-                       1000);
+                       (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_min_socclk_clk_in_khz /
+                       1000));
                table->WatermarkRow[0][i].MaxClock =
                        cpu_to_le16((uint16_t)
-                       (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_max_socclk_clk_in_khz) /
-                       1000);
+                       (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_max_socclk_clk_in_khz /
+                       1000));
                table->WatermarkRow[0][i].MinUclk =
                        cpu_to_le16((uint16_t)
-                       (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_min_mem_clk_in_khz) /
-                       1000);
+                       (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_min_mem_clk_in_khz /
+                       1000));
                table->WatermarkRow[0][i].MaxUclk =
                        cpu_to_le16((uint16_t)
-                       (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_max_mem_clk_in_khz) /
-                       1000);
+                       (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_max_mem_clk_in_khz /
+                       1000));
                table->WatermarkRow[0][i].WmSetting = (uint8_t)
                                wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_set_id;
        }
index 8c4db86..79c8624 100644 (file)
@@ -3266,8 +3266,10 @@ static int vega10_find_dpm_states_clocks_in_dpm_table(struct pp_hwmgr *hwmgr, co
        }
 
        if (i >= sclk_table->count) {
-               data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
-               sclk_table->dpm_levels[i-1].value = sclk;
+               if (sclk > sclk_table->dpm_levels[i-1].value) {
+                       data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
+                       sclk_table->dpm_levels[i-1].value = sclk;
+               }
        }
 
        for (i = 0; i < mclk_table->count; i++) {
@@ -3276,8 +3278,10 @@ static int vega10_find_dpm_states_clocks_in_dpm_table(struct pp_hwmgr *hwmgr, co
        }
 
        if (i >= mclk_table->count) {
-               data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
-               mclk_table->dpm_levels[i-1].value = mclk;
+               if (mclk > mclk_table->dpm_levels[i-1].value) {
+                       data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
+                       mclk_table->dpm_levels[i-1].value = mclk;
+               }
        }
 
        if (data->display_timing.num_existing_displays != hwmgr->display_config->num_display)
@@ -4522,15 +4526,13 @@ static int vega10_get_sclk_od(struct pp_hwmgr *hwmgr)
        struct vega10_single_dpm_table *sclk_table = &(data->dpm_table.gfx_table);
        struct vega10_single_dpm_table *golden_sclk_table =
                        &(data->golden_dpm_table.gfx_table);
-       int value;
-
-       value = (sclk_table->dpm_levels[sclk_table->count - 1].value -
-                       golden_sclk_table->dpm_levels
-                       [golden_sclk_table->count - 1].value) *
-                       100 /
-                       golden_sclk_table->dpm_levels
+       int value = sclk_table->dpm_levels[sclk_table->count - 1].value;
+       int golden_value = golden_sclk_table->dpm_levels
                        [golden_sclk_table->count - 1].value;
 
+       value -= golden_value;
+       value = DIV_ROUND_UP(value * 100, golden_value);
+
        return value;
 }
 
@@ -4575,16 +4577,13 @@ static int vega10_get_mclk_od(struct pp_hwmgr *hwmgr)
        struct vega10_single_dpm_table *mclk_table = &(data->dpm_table.mem_table);
        struct vega10_single_dpm_table *golden_mclk_table =
                        &(data->golden_dpm_table.mem_table);
-       int value;
-
-       value = (mclk_table->dpm_levels
-                       [mclk_table->count - 1].value -
-                       golden_mclk_table->dpm_levels
-                       [golden_mclk_table->count - 1].value) *
-                       100 /
-                       golden_mclk_table->dpm_levels
+       int value = mclk_table->dpm_levels[mclk_table->count - 1].value;
+       int golden_value = golden_mclk_table->dpm_levels
                        [golden_mclk_table->count - 1].value;
 
+       value -= golden_value;
+       value = DIV_ROUND_UP(value * 100, golden_value);
+
        return value;
 }
 
index 74bc373..5436444 100644 (file)
@@ -2243,12 +2243,12 @@ static int vega12_get_sclk_od(struct pp_hwmgr *hwmgr)
        struct vega12_single_dpm_table *sclk_table = &(data->dpm_table.gfx_table);
        struct vega12_single_dpm_table *golden_sclk_table =
                        &(data->golden_dpm_table.gfx_table);
-       int value;
+       int value = sclk_table->dpm_levels[sclk_table->count - 1].value;
+       int golden_value = golden_sclk_table->dpm_levels
+                       [golden_sclk_table->count - 1].value;
 
-       value = (sclk_table->dpm_levels[sclk_table->count - 1].value -
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value) *
-                       100 /
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
+       value -= golden_value;
+       value = DIV_ROUND_UP(value * 100, golden_value);
 
        return value;
 }
@@ -2264,16 +2264,13 @@ static int vega12_get_mclk_od(struct pp_hwmgr *hwmgr)
        struct vega12_single_dpm_table *mclk_table = &(data->dpm_table.mem_table);
        struct vega12_single_dpm_table *golden_mclk_table =
                        &(data->golden_dpm_table.mem_table);
-       int value;
-
-       value = (mclk_table->dpm_levels
-                       [mclk_table->count - 1].value -
-                       golden_mclk_table->dpm_levels
-                       [golden_mclk_table->count - 1].value) *
-                       100 /
-                       golden_mclk_table->dpm_levels
+       int value = mclk_table->dpm_levels[mclk_table->count - 1].value;
+       int golden_value = golden_mclk_table->dpm_levels
                        [golden_mclk_table->count - 1].value;
 
+       value -= golden_value;
+       value = DIV_ROUND_UP(value * 100, golden_value);
+
        return value;
 }
 
index 99861f3..3b7fce5 100644 (file)
@@ -75,7 +75,17 @@ static void vega20_set_default_registry_data(struct pp_hwmgr *hwmgr)
        data->phy_clk_quad_eqn_b = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
        data->phy_clk_quad_eqn_c = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 
-       data->registry_data.disallowed_features = 0x0;
+       /*
+        * Disable the following features for now:
+        *   GFXCLK DS
+        *   SOCLK DS
+        *   LCLK DS
+        *   DCEFCLK DS
+        *   FCLK DS
+        *   MP1CLK DS
+        *   MP0CLK DS
+        */
+       data->registry_data.disallowed_features = 0xE0041C00;
        data->registry_data.od_state_in_dc_support = 0;
        data->registry_data.thermal_support = 1;
        data->registry_data.skip_baco_hardware = 0;
@@ -120,7 +130,7 @@ static void vega20_set_default_registry_data(struct pp_hwmgr *hwmgr)
        data->registry_data.disable_auto_wattman = 1;
        data->registry_data.auto_wattman_debug = 0;
        data->registry_data.auto_wattman_sample_period = 100;
-       data->registry_data.fclk_gfxclk_ratio = 0x3F6CCCCD;
+       data->registry_data.fclk_gfxclk_ratio = 0;
        data->registry_data.auto_wattman_threshold = 50;
        data->registry_data.gfxoff_controlled_by_driver = 1;
        data->gfxoff_allowed = false;
@@ -1313,12 +1323,13 @@ static int vega20_get_sclk_od(
                        &(data->dpm_table.gfx_table);
        struct vega20_single_dpm_table *golden_sclk_table =
                        &(data->golden_dpm_table.gfx_table);
-       int value;
+       int value = sclk_table->dpm_levels[sclk_table->count - 1].value;
+       int golden_value = golden_sclk_table->dpm_levels
+                       [golden_sclk_table->count - 1].value;
 
        /* od percentage */
-       value = DIV_ROUND_UP((sclk_table->dpm_levels[sclk_table->count - 1].value -
-               golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value) * 100,
-               golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value);
+       value -= golden_value;
+       value = DIV_ROUND_UP(value * 100, golden_value);
 
        return value;
 }
@@ -1358,12 +1369,13 @@ static int vega20_get_mclk_od(
                        &(data->dpm_table.mem_table);
        struct vega20_single_dpm_table *golden_mclk_table =
                        &(data->golden_dpm_table.mem_table);
-       int value;
+       int value = mclk_table->dpm_levels[mclk_table->count - 1].value;
+       int golden_value = golden_mclk_table->dpm_levels
+                       [golden_mclk_table->count - 1].value;
 
        /* od percentage */
-       value = DIV_ROUND_UP((mclk_table->dpm_levels[mclk_table->count - 1].value -
-               golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value) * 100,
-               golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value);
+       value -= golden_value;
+       value = DIV_ROUND_UP(value * 100, golden_value);
 
        return value;
 }
@@ -1648,14 +1660,15 @@ static uint32_t vega20_find_highest_dpm_level(
        return i;
 }
 
-static int vega20_upload_dpm_min_level(struct pp_hwmgr *hwmgr)
+static int vega20_upload_dpm_min_level(struct pp_hwmgr *hwmgr, uint32_t feature_mask)
 {
        struct vega20_hwmgr *data =
                        (struct vega20_hwmgr *)(hwmgr->backend);
        uint32_t min_freq;
        int ret = 0;
 
-       if (data->smu_features[GNLD_DPM_GFXCLK].enabled) {
+       if (data->smu_features[GNLD_DPM_GFXCLK].enabled &&
+          (feature_mask & FEATURE_DPM_GFXCLK_MASK)) {
                min_freq = data->dpm_table.gfx_table.dpm_state.soft_min_level;
                PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
                                        hwmgr, PPSMC_MSG_SetSoftMinByFreq,
@@ -1664,7 +1677,8 @@ static int vega20_upload_dpm_min_level(struct pp_hwmgr *hwmgr)
                                        return ret);
        }
 
-       if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+       if (data->smu_features[GNLD_DPM_UCLK].enabled &&
+          (feature_mask & FEATURE_DPM_UCLK_MASK)) {
                min_freq = data->dpm_table.mem_table.dpm_state.soft_min_level;
                PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
                                        hwmgr, PPSMC_MSG_SetSoftMinByFreq,
@@ -1680,7 +1694,8 @@ static int vega20_upload_dpm_min_level(struct pp_hwmgr *hwmgr)
                                        return ret);
        }
 
-       if (data->smu_features[GNLD_DPM_UVD].enabled) {
+       if (data->smu_features[GNLD_DPM_UVD].enabled &&
+          (feature_mask & FEATURE_DPM_UVD_MASK)) {
                min_freq = data->dpm_table.vclk_table.dpm_state.soft_min_level;
 
                PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
@@ -1698,7 +1713,8 @@ static int vega20_upload_dpm_min_level(struct pp_hwmgr *hwmgr)
                                        return ret);
        }
 
-       if (data->smu_features[GNLD_DPM_VCE].enabled) {
+       if (data->smu_features[GNLD_DPM_VCE].enabled &&
+          (feature_mask & FEATURE_DPM_VCE_MASK)) {
                min_freq = data->dpm_table.eclk_table.dpm_state.soft_min_level;
 
                PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
@@ -1708,7 +1724,8 @@ static int vega20_upload_dpm_min_level(struct pp_hwmgr *hwmgr)
                                        return ret);
        }
 
-       if (data->smu_features[GNLD_DPM_SOCCLK].enabled) {
+       if (data->smu_features[GNLD_DPM_SOCCLK].enabled &&
+          (feature_mask & FEATURE_DPM_SOCCLK_MASK)) {
                min_freq = data->dpm_table.soc_table.dpm_state.soft_min_level;
 
                PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
@@ -1721,14 +1738,15 @@ static int vega20_upload_dpm_min_level(struct pp_hwmgr *hwmgr)
        return ret;
 }
 
-static int vega20_upload_dpm_max_level(struct pp_hwmgr *hwmgr)
+static int vega20_upload_dpm_max_level(struct pp_hwmgr *hwmgr, uint32_t feature_mask)
 {
        struct vega20_hwmgr *data =
                        (struct vega20_hwmgr *)(hwmgr->backend);
        uint32_t max_freq;
        int ret = 0;
 
-       if (data->smu_features[GNLD_DPM_GFXCLK].enabled) {
+       if (data->smu_features[GNLD_DPM_GFXCLK].enabled &&
+          (feature_mask & FEATURE_DPM_GFXCLK_MASK)) {
                max_freq = data->dpm_table.gfx_table.dpm_state.soft_max_level;
 
                PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
@@ -1738,7 +1756,8 @@ static int vega20_upload_dpm_max_level(struct pp_hwmgr *hwmgr)
                                        return ret);
        }
 
-       if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+       if (data->smu_features[GNLD_DPM_UCLK].enabled &&
+          (feature_mask & FEATURE_DPM_UCLK_MASK)) {
                max_freq = data->dpm_table.mem_table.dpm_state.soft_max_level;
 
                PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
@@ -1748,7 +1767,8 @@ static int vega20_upload_dpm_max_level(struct pp_hwmgr *hwmgr)
                                        return ret);
        }
 
-       if (data->smu_features[GNLD_DPM_UVD].enabled) {
+       if (data->smu_features[GNLD_DPM_UVD].enabled &&
+          (feature_mask & FEATURE_DPM_UVD_MASK)) {
                max_freq = data->dpm_table.vclk_table.dpm_state.soft_max_level;
 
                PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
@@ -1765,7 +1785,8 @@ static int vega20_upload_dpm_max_level(struct pp_hwmgr *hwmgr)
                                        return ret);
        }
 
-       if (data->smu_features[GNLD_DPM_VCE].enabled) {
+       if (data->smu_features[GNLD_DPM_VCE].enabled &&
+          (feature_mask & FEATURE_DPM_VCE_MASK)) {
                max_freq = data->dpm_table.eclk_table.dpm_state.soft_max_level;
 
                PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
@@ -1775,7 +1796,8 @@ static int vega20_upload_dpm_max_level(struct pp_hwmgr *hwmgr)
                                        return ret);
        }
 
-       if (data->smu_features[GNLD_DPM_SOCCLK].enabled) {
+       if (data->smu_features[GNLD_DPM_SOCCLK].enabled &&
+          (feature_mask & FEATURE_DPM_SOCCLK_MASK)) {
                max_freq = data->dpm_table.soc_table.dpm_state.soft_max_level;
 
                PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
@@ -2114,12 +2136,12 @@ static int vega20_force_dpm_highest(struct pp_hwmgr *hwmgr)
                data->dpm_table.mem_table.dpm_state.soft_max_level =
                data->dpm_table.mem_table.dpm_levels[soft_level].value;
 
-       ret = vega20_upload_dpm_min_level(hwmgr);
+       ret = vega20_upload_dpm_min_level(hwmgr, 0xFFFFFFFF);
        PP_ASSERT_WITH_CODE(!ret,
                        "Failed to upload boot level to highest!",
                        return ret);
 
-       ret = vega20_upload_dpm_max_level(hwmgr);
+       ret = vega20_upload_dpm_max_level(hwmgr, 0xFFFFFFFF);
        PP_ASSERT_WITH_CODE(!ret,
                        "Failed to upload dpm max level to highest!",
                        return ret);
@@ -2146,12 +2168,12 @@ static int vega20_force_dpm_lowest(struct pp_hwmgr *hwmgr)
                data->dpm_table.mem_table.dpm_state.soft_max_level =
                data->dpm_table.mem_table.dpm_levels[soft_level].value;
 
-       ret = vega20_upload_dpm_min_level(hwmgr);
+       ret = vega20_upload_dpm_min_level(hwmgr, 0xFFFFFFFF);
        PP_ASSERT_WITH_CODE(!ret,
                        "Failed to upload boot level to highest!",
                        return ret);
 
-       ret = vega20_upload_dpm_max_level(hwmgr);
+       ret = vega20_upload_dpm_max_level(hwmgr, 0xFFFFFFFF);
        PP_ASSERT_WITH_CODE(!ret,
                        "Failed to upload dpm max level to highest!",
                        return ret);
@@ -2164,12 +2186,12 @@ static int vega20_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
 {
        int ret = 0;
 
-       ret = vega20_upload_dpm_min_level(hwmgr);
+       ret = vega20_upload_dpm_min_level(hwmgr, 0xFFFFFFFF);
        PP_ASSERT_WITH_CODE(!ret,
                        "Failed to upload DPM Bootup Levels!",
                        return ret);
 
-       ret = vega20_upload_dpm_max_level(hwmgr);
+       ret = vega20_upload_dpm_max_level(hwmgr, 0xFFFFFFFF);
        PP_ASSERT_WITH_CODE(!ret,
                        "Failed to upload DPM Max Levels!",
                        return ret);
@@ -2227,12 +2249,12 @@ static int vega20_force_clock_level(struct pp_hwmgr *hwmgr,
                data->dpm_table.gfx_table.dpm_state.soft_max_level =
                        data->dpm_table.gfx_table.dpm_levels[soft_max_level].value;
 
-               ret = vega20_upload_dpm_min_level(hwmgr);
+               ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_GFXCLK_MASK);
                PP_ASSERT_WITH_CODE(!ret,
                        "Failed to upload boot level to lowest!",
                        return ret);
 
-               ret = vega20_upload_dpm_max_level(hwmgr);
+               ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_GFXCLK_MASK);
                PP_ASSERT_WITH_CODE(!ret,
                        "Failed to upload dpm max level to highest!",
                        return ret);
@@ -2247,12 +2269,12 @@ static int vega20_force_clock_level(struct pp_hwmgr *hwmgr,
                data->dpm_table.mem_table.dpm_state.soft_max_level =
                        data->dpm_table.mem_table.dpm_levels[soft_max_level].value;
 
-               ret = vega20_upload_dpm_min_level(hwmgr);
+               ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_UCLK_MASK);
                PP_ASSERT_WITH_CODE(!ret,
                        "Failed to upload boot level to lowest!",
                        return ret);
 
-               ret = vega20_upload_dpm_max_level(hwmgr);
+               ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_UCLK_MASK);
                PP_ASSERT_WITH_CODE(!ret,
                        "Failed to upload dpm max level to highest!",
                        return ret);
index 62f36ba..c1a99df 100644 (file)
@@ -386,6 +386,8 @@ typedef uint16_t PPSMC_Result;
 #define PPSMC_MSG_AgmResetPsm                 ((uint16_t) 0x403)
 #define PPSMC_MSG_ReadVftCell                 ((uint16_t) 0x404)
 
+#define PPSMC_MSG_ApplyAvfsCksOffVoltage      ((uint16_t) 0x415)
+
 #define PPSMC_MSG_GFX_CU_PG_ENABLE            ((uint16_t) 0x280)
 #define PPSMC_MSG_GFX_CU_PG_DISABLE           ((uint16_t) 0x281)
 #define PPSMC_MSG_GetCurrPkgPwr               ((uint16_t) 0x282)
index 872d382..a1e0ac9 100644 (file)
@@ -1985,6 +1985,12 @@ int polaris10_thermal_avfs_enable(struct pp_hwmgr *hwmgr)
 
        smum_send_msg_to_smc(hwmgr, PPSMC_MSG_EnableAvfs);
 
+       /* Apply avfs cks-off voltages to avoid the overshoot
+        * when switching to the highest sclk frequency
+        */
+       if (data->apply_avfs_cks_off_voltage)
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ApplyAvfsCksOffVoltage);
+
        return 0;
 }
 
index 99d5e4f..a6edd5d 100644 (file)
@@ -37,10 +37,13 @@ MODULE_FIRMWARE("amdgpu/fiji_smc.bin");
 MODULE_FIRMWARE("amdgpu/polaris10_smc.bin");
 MODULE_FIRMWARE("amdgpu/polaris10_smc_sk.bin");
 MODULE_FIRMWARE("amdgpu/polaris10_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris10_k2_smc.bin");
 MODULE_FIRMWARE("amdgpu/polaris11_smc.bin");
 MODULE_FIRMWARE("amdgpu/polaris11_smc_sk.bin");
 MODULE_FIRMWARE("amdgpu/polaris11_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris11_k2_smc.bin");
 MODULE_FIRMWARE("amdgpu/polaris12_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris12_k_smc.bin");
 MODULE_FIRMWARE("amdgpu/vegam_smc.bin");
 MODULE_FIRMWARE("amdgpu/vega10_smc.bin");
 MODULE_FIRMWARE("amdgpu/vega10_acg_smc.bin");
index 69dab82..bf589c5 100644 (file)
@@ -60,8 +60,29 @@ static const struct pci_device_id pciidlist[] = {
 
 MODULE_DEVICE_TABLE(pci, pciidlist);
 
+static void ast_kick_out_firmware_fb(struct pci_dev *pdev)
+{
+       struct apertures_struct *ap;
+       bool primary = false;
+
+       ap = alloc_apertures(1);
+       if (!ap)
+               return;
+
+       ap->ranges[0].base = pci_resource_start(pdev, 0);
+       ap->ranges[0].size = pci_resource_len(pdev, 0);
+
+#ifdef CONFIG_X86
+       primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
+#endif
+       drm_fb_helper_remove_conflicting_framebuffers(ap, "astdrmfb", primary);
+       kfree(ap);
+}
+
 static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
+       ast_kick_out_firmware_fb(pdev);
+
        return drm_get_pci_dev(pdev, ent, &driver);
 }
 
index 0cd827e..de26df0 100644 (file)
@@ -263,6 +263,7 @@ static void ast_fbdev_destroy(struct drm_device *dev,
 {
        struct ast_framebuffer *afb = &afbdev->afb;
 
+       drm_crtc_force_disable_all(dev);
        drm_fb_helper_unregister_fbi(&afbdev->helper);
 
        if (afb->obj) {
index dac3558..373700c 100644 (file)
@@ -583,7 +583,8 @@ void ast_driver_unload(struct drm_device *dev)
        drm_mode_config_cleanup(dev);
 
        ast_mm_fini(ast);
-       pci_iounmap(dev->pdev, ast->ioregs);
+       if (ast->ioregs != ast->regs + AST_IO_MM_OFFSET)
+               pci_iounmap(dev->pdev, ast->ioregs);
        pci_iounmap(dev->pdev, ast->regs);
        kfree(ast);
 }
index 5e77d45..8bb355d 100644 (file)
@@ -568,6 +568,7 @@ static int ast_crtc_do_set_base(struct drm_crtc *crtc,
        }
        ast_bo_unreserve(bo);
 
+       ast_set_offset_reg(crtc);
        ast_set_start_address_crt1(crtc, (u32)gpu_addr);
 
        return 0;
@@ -972,9 +973,21 @@ static int get_clock(void *i2c_priv)
 {
        struct ast_i2c_chan *i2c = i2c_priv;
        struct ast_private *ast = i2c->dev->dev_private;
-       uint32_t val;
+       uint32_t val, val2, count, pass;
+
+       count = 0;
+       pass = 0;
+       val = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x10) >> 4) & 0x01;
+       do {
+               val2 = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x10) >> 4) & 0x01;
+               if (val == val2) {
+                       pass++;
+               } else {
+                       pass = 0;
+                       val = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x10) >> 4) & 0x01;
+               }
+       } while ((pass < 5) && (count++ < 0x10000));
 
-       val = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x10) >> 4;
        return val & 1 ? 1 : 0;
 }
 
@@ -982,9 +995,21 @@ static int get_data(void *i2c_priv)
 {
        struct ast_i2c_chan *i2c = i2c_priv;
        struct ast_private *ast = i2c->dev->dev_private;
-       uint32_t val;
+       uint32_t val, val2, count, pass;
+
+       count = 0;
+       pass = 0;
+       val = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x20) >> 5) & 0x01;
+       do {
+               val2 = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x20) >> 5) & 0x01;
+               if (val == val2) {
+                       pass++;
+               } else {
+                       pass = 0;
+                       val = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x20) >> 5) & 0x01;
+               }
+       } while ((pass < 5) && (count++ < 0x10000));
 
-       val = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x20) >> 5;
        return val & 1 ? 1 : 0;
 }
 
@@ -997,7 +1022,7 @@ static void set_clock(void *i2c_priv, int clock)
 
        for (i = 0; i < 0x10000; i++) {
                ujcrb7 = ((clock & 0x01) ? 0 : 1);
-               ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0xfe, ujcrb7);
+               ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0xf4, ujcrb7);
                jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x01);
                if (ujcrb7 == jtemp)
                        break;
@@ -1013,7 +1038,7 @@ static void set_data(void *i2c_priv, int data)
 
        for (i = 0; i < 0x10000; i++) {
                ujcrb7 = ((data & 0x01) ? 0 : 1) << 2;
-               ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0xfb, ujcrb7);
+               ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0xf1, ujcrb7);
                jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x04);
                if (ujcrb7 == jtemp)
                        break;
@@ -1254,7 +1279,7 @@ static int ast_cursor_move(struct drm_crtc *crtc,
        ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc7, ((y >> 8) & 0x07));
 
        /* dummy write to fire HWC */
-       ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xCB, 0xFF, 0x00);
+       ast_show_cursor(crtc);
 
        return 0;
 }
index 680566d..1024396 100644 (file)
@@ -54,7 +54,7 @@
 #define SN_AUX_ADDR_7_0_REG                    0x76
 #define SN_AUX_LENGTH_REG                      0x77
 #define SN_AUX_CMD_REG                         0x78
-#define  AUX_CMD_SEND                          BIT(1)
+#define  AUX_CMD_SEND                          BIT(0)
 #define  AUX_CMD_REQ(x)                                ((x) << 4)
 #define SN_AUX_RDATA_REG(x)                    (0x79 + (x))
 #define SN_SSC_CONFIG_REG                      0x93
index d9c0f75..1669c42 100644 (file)
@@ -142,6 +142,7 @@ static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
 
        lockdep_assert_held_once(&dev->master_mutex);
 
+       WARN_ON(fpriv->is_master);
        old_master = fpriv->master;
        fpriv->master = drm_master_create(dev);
        if (!fpriv->master) {
@@ -170,6 +171,7 @@ out_err:
        /* drop references and restore old master on failure */
        drm_master_put(&fpriv->master);
        fpriv->master = old_master;
+       fpriv->is_master = 0;
 
        return ret;
 }
index 5ff1d79..0e0df39 100644 (file)
@@ -1275,6 +1275,9 @@ static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_
        mutex_lock(&mgr->lock);
        mstb = mgr->mst_primary;
 
+       if (!mstb)
+               goto out;
+
        for (i = 0; i < lct - 1; i++) {
                int shift = (i % 2) ? 0 : 4;
                int port_num = (rad[i / 2] >> shift) & 0xf;
index a502f3e..9d64f87 100644 (file)
@@ -71,7 +71,7 @@ MODULE_PARM_DESC(drm_fbdev_overalloc,
 #if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)
 static bool drm_leak_fbdev_smem = false;
 module_param_unsafe(drm_leak_fbdev_smem, bool, 0600);
-MODULE_PARM_DESC(fbdev_emulation,
+MODULE_PARM_DESC(drm_leak_fbdev_smem,
                 "Allow unsafe leaking fbdev physical smem address [default=false]");
 #endif
 
@@ -219,6 +219,9 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
        mutex_lock(&fb_helper->lock);
        drm_connector_list_iter_begin(dev, &conn_iter);
        drm_for_each_connector_iter(connector, &conn_iter) {
+               if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+                       continue;
+
                ret = __drm_fb_helper_add_one_connector(fb_helper, connector);
                if (ret)
                        goto fail;
index 90a1c84..8aaa5e8 100644 (file)
@@ -97,9 +97,9 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format);
 
 /**
  * drm_driver_legacy_fb_format - compute drm fourcc code from legacy description
+ * @dev: DRM device
  * @bpp: bits per pixels
  * @depth: bit depth per pixel
- * @native: use host native byte order
  *
  * Computes a drm fourcc pixel format code for the given @bpp/@depth values.
  * Unlike drm_mode_legacy_fb_format() this looks at the drivers mode_config,
index 0c4eb4a..51e06de 100644 (file)
@@ -104,6 +104,8 @@ struct device *drm_sysfs_minor_alloc(struct drm_minor *minor);
 int drm_sysfs_connector_add(struct drm_connector *connector);
 void drm_sysfs_connector_remove(struct drm_connector *connector);
 
+void drm_sysfs_lease_event(struct drm_device *dev);
+
 /* drm_gem.c */
 int drm_gem_init(struct drm_device *dev);
 void drm_gem_destroy(struct drm_device *dev);
index 24a177e..c61680a 100644 (file)
@@ -296,7 +296,7 @@ void drm_lease_destroy(struct drm_master *master)
 
        if (master->lessor) {
                /* Tell the master to check the lessee list */
-               drm_sysfs_hotplug_event(dev);
+               drm_sysfs_lease_event(dev);
                drm_master_put(&master->lessor);
        }
 
index b3c1daa..ecb7b33 100644 (file)
@@ -301,6 +301,16 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
        connector->kdev = NULL;
 }
 
+void drm_sysfs_lease_event(struct drm_device *dev)
+{
+       char *event_string = "LEASE=1";
+       char *envp[] = { event_string, NULL };
+
+       DRM_DEBUG("generating lease event\n");
+
+       kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
+}
+
 /**
  * drm_sysfs_hotplug_event - generate a DRM uevent
  * @dev: DRM device
index fe75402..359d37d 100644 (file)
@@ -61,10 +61,12 @@ static int alloc_gm(struct intel_vgpu *vgpu, bool high_gm)
        }
 
        mutex_lock(&dev_priv->drm.struct_mutex);
+       mmio_hw_access_pre(dev_priv);
        ret = i915_gem_gtt_insert(&dev_priv->ggtt.vm, node,
                                  size, I915_GTT_PAGE_SIZE,
                                  I915_COLOR_UNEVICTABLE,
                                  start, end, flags);
+       mmio_hw_access_post(dev_priv);
        mutex_unlock(&dev_priv->drm.struct_mutex);
        if (ret)
                gvt_err("fail to alloc %s gm space from host\n",
index 481896f..85e6736 100644 (file)
@@ -235,7 +235,7 @@ int intel_vgpu_decode_primary_plane(struct intel_vgpu *vgpu,
                plane->bpp = skl_pixel_formats[fmt].bpp;
                plane->drm_format = skl_pixel_formats[fmt].drm_format;
        } else {
-               plane->tiled = !!(val & DISPPLANE_TILED);
+               plane->tiled = val & DISPPLANE_TILED;
                fmt = bdw_format_to_drm(val & DISPPLANE_PIXFORMAT_MASK);
                plane->bpp = bdw_pixel_formats[fmt].bpp;
                plane->drm_format = bdw_pixel_formats[fmt].drm_format;
index 58e166e..c7103dd 100644 (file)
@@ -2447,10 +2447,11 @@ static void intel_vgpu_destroy_all_ppgtt_mm(struct intel_vgpu *vgpu)
 
 static void intel_vgpu_destroy_ggtt_mm(struct intel_vgpu *vgpu)
 {
-       struct intel_gvt_partial_pte *pos;
+       struct intel_gvt_partial_pte *pos, *next;
 
-       list_for_each_entry(pos,
-                       &vgpu->gtt.ggtt_mm->ggtt_mm.partial_pte_list, list) {
+       list_for_each_entry_safe(pos, next,
+                                &vgpu->gtt.ggtt_mm->ggtt_mm.partial_pte_list,
+                                list) {
                gvt_dbg_mm("partial PTE update on hold 0x%lx : 0x%llx\n",
                        pos->offset, pos->data);
                kfree(pos);
index 36a5147..d6e02c1 100644 (file)
@@ -158,6 +158,8 @@ static void load_render_mocs(struct drm_i915_private *dev_priv)
        int ring_id, i;
 
        for (ring_id = 0; ring_id < ARRAY_SIZE(regs); ring_id++) {
+               if (!HAS_ENGINE(dev_priv, ring_id))
+                       continue;
                offset.reg = regs[ring_id];
                for (i = 0; i < GEN9_MOCS_SIZE; i++) {
                        gen9_render_mocs.control_table[ring_id][i] =
index ffdbbac..47062ee 100644 (file)
@@ -1444,6 +1444,7 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
 
        intel_uncore_sanitize(dev_priv);
 
+       intel_gt_init_workarounds(dev_priv);
        i915_gem_load_init_fences(dev_priv);
 
        /* On the 945G/GM, the chipset reports the MSI capability on the
index 9102571..872a2e1 100644 (file)
@@ -67,6 +67,7 @@
 #include "intel_ringbuffer.h"
 #include "intel_uncore.h"
 #include "intel_wopcm.h"
+#include "intel_workarounds.h"
 #include "intel_uc.h"
 
 #include "i915_gem.h"
@@ -1805,6 +1806,7 @@ struct drm_i915_private {
        int dpio_phy_iosf_port[I915_NUM_PHYS_VLV];
 
        struct i915_workarounds workarounds;
+       struct i915_wa_list gt_wa_list;
 
        struct i915_frontbuffer_tracking fb_tracking;
 
@@ -2148,6 +2150,8 @@ struct drm_i915_private {
                struct delayed_work idle_work;
 
                ktime_t last_init_time;
+
+               struct i915_vma *scratch;
        } gt;
 
        /* perform PHY state sanity checks? */
@@ -3870,4 +3874,9 @@ static inline int intel_hws_csb_write_index(struct drm_i915_private *i915)
                return I915_HWS_CSB_WRITE_INDEX;
 }
 
+static inline u32 i915_scratch_offset(const struct drm_i915_private *i915)
+{
+       return i915_ggtt_offset(i915->gt.scratch);
+}
+
 #endif
index 0c8aa57..6ae9a60 100644 (file)
@@ -5305,7 +5305,7 @@ int i915_gem_init_hw(struct drm_i915_private *dev_priv)
                }
        }
 
-       intel_gt_workarounds_apply(dev_priv);
+       intel_gt_apply_workarounds(dev_priv);
 
        i915_gem_init_swizzling(dev_priv);
 
@@ -5500,6 +5500,44 @@ err_active:
        goto out_ctx;
 }
 
+static int
+i915_gem_init_scratch(struct drm_i915_private *i915, unsigned int size)
+{
+       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
+       int ret;
+
+       obj = i915_gem_object_create_stolen(i915, size);
+       if (!obj)
+               obj = i915_gem_object_create_internal(i915, size);
+       if (IS_ERR(obj)) {
+               DRM_ERROR("Failed to allocate scratch page\n");
+               return PTR_ERR(obj);
+       }
+
+       vma = i915_vma_instance(obj, &i915->ggtt.vm, NULL);
+       if (IS_ERR(vma)) {
+               ret = PTR_ERR(vma);
+               goto err_unref;
+       }
+
+       ret = i915_vma_pin(vma, 0, 0, PIN_GLOBAL | PIN_HIGH);
+       if (ret)
+               goto err_unref;
+
+       i915->gt.scratch = vma;
+       return 0;
+
+err_unref:
+       i915_gem_object_put(obj);
+       return ret;
+}
+
+static void i915_gem_fini_scratch(struct drm_i915_private *i915)
+{
+       i915_vma_unpin_and_release(&i915->gt.scratch, 0);
+}
+
 int i915_gem_init(struct drm_i915_private *dev_priv)
 {
        int ret;
@@ -5546,12 +5584,19 @@ int i915_gem_init(struct drm_i915_private *dev_priv)
                goto err_unlock;
        }
 
-       ret = i915_gem_contexts_init(dev_priv);
+       ret = i915_gem_init_scratch(dev_priv,
+                                   IS_GEN2(dev_priv) ? SZ_256K : PAGE_SIZE);
        if (ret) {
                GEM_BUG_ON(ret == -EIO);
                goto err_ggtt;
        }
 
+       ret = i915_gem_contexts_init(dev_priv);
+       if (ret) {
+               GEM_BUG_ON(ret == -EIO);
+               goto err_scratch;
+       }
+
        ret = intel_engines_init(dev_priv);
        if (ret) {
                GEM_BUG_ON(ret == -EIO);
@@ -5624,6 +5669,8 @@ err_pm:
 err_context:
        if (ret != -EIO)
                i915_gem_contexts_fini(dev_priv);
+err_scratch:
+       i915_gem_fini_scratch(dev_priv);
 err_ggtt:
 err_unlock:
        intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
@@ -5675,8 +5722,11 @@ void i915_gem_fini(struct drm_i915_private *dev_priv)
        intel_uc_fini(dev_priv);
        i915_gem_cleanup_engines(dev_priv);
        i915_gem_contexts_fini(dev_priv);
+       i915_gem_fini_scratch(dev_priv);
        mutex_unlock(&dev_priv->drm.struct_mutex);
 
+       intel_wa_list_free(&dev_priv->gt_wa_list);
+
        intel_cleanup_gt_powersave(dev_priv);
 
        intel_uc_fini_misc(dev_priv);
index 47c3025..07999fe 100644 (file)
@@ -3413,6 +3413,11 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
                ggtt->vm.insert_page    = bxt_vtd_ggtt_insert_page__BKL;
                if (ggtt->vm.clear_range != nop_clear_range)
                        ggtt->vm.clear_range = bxt_vtd_ggtt_clear_range__BKL;
+
+               /* Prevent recursively calling stop_machine() and deadlocks. */
+               dev_info(dev_priv->drm.dev,
+                        "Disabling error capture for VT-d workaround\n");
+               i915_disable_error_state(dev_priv, -ENODEV);
        }
 
        ggtt->invalidate = gen6_ggtt_invalidate;
index 8762d17..db4128d 100644 (file)
@@ -648,6 +648,9 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
                return 0;
        }
 
+       if (IS_ERR(error))
+               return PTR_ERR(error);
+
        if (*error->error_msg)
                err_printf(m, "%s\n", error->error_msg);
        err_printf(m, "Kernel: " UTS_RELEASE "\n");
@@ -1492,7 +1495,7 @@ static void gem_record_rings(struct i915_gpu_state *error)
                        if (HAS_BROKEN_CS_TLB(i915))
                                ee->wa_batchbuffer =
                                        i915_error_object_create(i915,
-                                                                engine->scratch);
+                                                                i915->gt.scratch);
                        request_record_user_bo(request, ee);
 
                        ee->ctx =
@@ -1859,6 +1862,7 @@ void i915_capture_error_state(struct drm_i915_private *i915,
        error = i915_capture_gpu_state(i915);
        if (!error) {
                DRM_DEBUG_DRIVER("out of memory, not capturing error state\n");
+               i915_disable_error_state(i915, -ENOMEM);
                return;
        }
 
@@ -1914,5 +1918,14 @@ void i915_reset_error_state(struct drm_i915_private *i915)
        i915->gpu_error.first_error = NULL;
        spin_unlock_irq(&i915->gpu_error.lock);
 
-       i915_gpu_state_put(error);
+       if (!IS_ERR(error))
+               i915_gpu_state_put(error);
+}
+
+void i915_disable_error_state(struct drm_i915_private *i915, int err)
+{
+       spin_lock_irq(&i915->gpu_error.lock);
+       if (!i915->gpu_error.first_error)
+               i915->gpu_error.first_error = ERR_PTR(err);
+       spin_unlock_irq(&i915->gpu_error.lock);
 }
index 8710fb1..3ec89a5 100644 (file)
@@ -343,6 +343,7 @@ static inline void i915_gpu_state_put(struct i915_gpu_state *gpu)
 
 struct i915_gpu_state *i915_first_error_state(struct drm_i915_private *i915);
 void i915_reset_error_state(struct drm_i915_private *i915);
+void i915_disable_error_state(struct drm_i915_private *i915, int err);
 
 #else
 
@@ -355,13 +356,18 @@ static inline void i915_capture_error_state(struct drm_i915_private *dev_priv,
 static inline struct i915_gpu_state *
 i915_first_error_state(struct drm_i915_private *i915)
 {
-       return NULL;
+       return ERR_PTR(-ENODEV);
 }
 
 static inline void i915_reset_error_state(struct drm_i915_private *i915)
 {
 }
 
+static inline void i915_disable_error_state(struct drm_i915_private *i915,
+                                           int err)
+{
+}
+
 #endif /* IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) */
 
 #endif /* _I915_GPU_ERROR_H_ */
index 0ef0c64..01fa982 100644 (file)
@@ -474,7 +474,7 @@ static void broadwell_sseu_info_init(struct drm_i915_private *dev_priv)
                        u8 eu_disabled_mask;
                        u32 n_disabled;
 
-                       if (!(sseu->subslice_mask[ss] & BIT(ss)))
+                       if (!(sseu->subslice_mask[s] & BIT(ss)))
                                /* skip disabled subslice */
                                continue;
 
index 23d8008..c9878dd 100644 (file)
@@ -2890,6 +2890,7 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
        return;
 
 valid_fb:
+       intel_state->base.rotation = plane_config->rotation;
        intel_fill_fb_ggtt_view(&intel_state->view, fb,
                                intel_state->base.rotation);
        intel_state->color_plane[0].stride =
@@ -4850,8 +4851,31 @@ static void cpt_verify_modeset(struct drm_device *dev, int pipe)
  * chroma samples for both of the luma samples, and thus we don't
  * actually get the expected MPEG2 chroma siting convention :(
  * The same behaviour is observed on pre-SKL platforms as well.
+ *
+ * Theory behind the formula (note that we ignore sub-pixel
+ * source coordinates):
+ * s = source sample position
+ * d = destination sample position
+ *
+ * Downscaling 4:1:
+ * -0.5
+ * | 0.0
+ * | |     1.5 (initial phase)
+ * | |     |
+ * v v     v
+ * | s | s | s | s |
+ * |       d       |
+ *
+ * Upscaling 1:4:
+ * -0.5
+ * | -0.375 (initial phase)
+ * | |     0.0
+ * | |     |
+ * v v     v
+ * |       s       |
+ * | d | d | d | d |
  */
-u16 skl_scaler_calc_phase(int sub, bool chroma_cosited)
+u16 skl_scaler_calc_phase(int sub, int scale, bool chroma_cosited)
 {
        int phase = -0x8000;
        u16 trip = 0;
@@ -4859,6 +4883,15 @@ u16 skl_scaler_calc_phase(int sub, bool chroma_cosited)
        if (chroma_cosited)
                phase += (sub - 1) * 0x8000 / sub;
 
+       phase += scale / (2 * sub);
+
+       /*
+        * Hardware initial phase limited to [-0.5:1.5].
+        * Since the max hardware scale factor is 3.0, we
+        * should never actually excdeed 1.0 here.
+        */
+       WARN_ON(phase < -0x8000 || phase > 0x18000);
+
        if (phase < 0)
                phase = 0x10000 + phase;
        else
@@ -5067,13 +5100,20 @@ static void skylake_pfit_enable(struct intel_crtc *crtc)
 
        if (crtc->config->pch_pfit.enabled) {
                u16 uv_rgb_hphase, uv_rgb_vphase;
+               int pfit_w, pfit_h, hscale, vscale;
                int id;
 
                if (WARN_ON(crtc->config->scaler_state.scaler_id < 0))
                        return;
 
-               uv_rgb_hphase = skl_scaler_calc_phase(1, false);
-               uv_rgb_vphase = skl_scaler_calc_phase(1, false);
+               pfit_w = (crtc->config->pch_pfit.size >> 16) & 0xFFFF;
+               pfit_h = crtc->config->pch_pfit.size & 0xFFFF;
+
+               hscale = (crtc->config->pipe_src_w << 16) / pfit_w;
+               vscale = (crtc->config->pipe_src_h << 16) / pfit_h;
+
+               uv_rgb_hphase = skl_scaler_calc_phase(1, hscale, false);
+               uv_rgb_vphase = skl_scaler_calc_phase(1, vscale, false);
 
                id = scaler_state->scaler_id;
                I915_WRITE(SKL_PS_CTRL(pipe, id), PS_SCALER_EN |
@@ -7843,8 +7883,15 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc,
                        plane_config->tiling = I915_TILING_X;
                        fb->modifier = I915_FORMAT_MOD_X_TILED;
                }
+
+               if (val & DISPPLANE_ROTATE_180)
+                       plane_config->rotation = DRM_MODE_ROTATE_180;
        }
 
+       if (IS_CHERRYVIEW(dev_priv) && pipe == PIPE_B &&
+           val & DISPPLANE_MIRROR)
+               plane_config->rotation |= DRM_MODE_REFLECT_X;
+
        pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
        fourcc = i9xx_format_to_fourcc(pixel_format);
        fb->format = drm_format_info(fourcc);
@@ -8913,6 +8960,29 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc,
                goto error;
        }
 
+       /*
+        * DRM_MODE_ROTATE_ is counter clockwise to stay compatible with Xrandr
+        * while i915 HW rotation is clockwise, thats why this swapping.
+        */
+       switch (val & PLANE_CTL_ROTATE_MASK) {
+       case PLANE_CTL_ROTATE_0:
+               plane_config->rotation = DRM_MODE_ROTATE_0;
+               break;
+       case PLANE_CTL_ROTATE_90:
+               plane_config->rotation = DRM_MODE_ROTATE_270;
+               break;
+       case PLANE_CTL_ROTATE_180:
+               plane_config->rotation = DRM_MODE_ROTATE_180;
+               break;
+       case PLANE_CTL_ROTATE_270:
+               plane_config->rotation = DRM_MODE_ROTATE_90;
+               break;
+       }
+
+       if (INTEL_GEN(dev_priv) >= 10 &&
+           val & PLANE_CTL_FLIP_HORIZONTAL)
+               plane_config->rotation |= DRM_MODE_REFLECT_X;
+
        base = I915_READ(PLANE_SURF(pipe, plane_id)) & 0xfffff000;
        plane_config->base = base;
 
@@ -15228,6 +15298,14 @@ retry:
                        ret = drm_atomic_add_affected_planes(state, crtc);
                        if (ret)
                                goto out;
+
+                       /*
+                        * FIXME hack to force a LUT update to avoid the
+                        * plane update forcing the pipe gamma on without
+                        * having a proper LUT loaded. Remove once we
+                        * have readout for pipe gamma enable.
+                        */
+                       crtc_state->color_mgmt_changed = true;
                }
        }
 
index 1b00f8e..a911691 100644 (file)
@@ -452,6 +452,10 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo
        if (!intel_connector)
                return NULL;
 
+       intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
+       intel_connector->mst_port = intel_dp;
+       intel_connector->port = port;
+
        connector = &intel_connector->base;
        ret = drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs,
                                 DRM_MODE_CONNECTOR_DisplayPort);
@@ -462,10 +466,6 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo
 
        drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
 
-       intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
-       intel_connector->mst_port = intel_dp;
-       intel_connector->port = port;
-
        for_each_pipe(dev_priv, pipe) {
                struct drm_encoder *enc =
                        &intel_dp->mst_encoders[pipe]->base.base;
index f8dc84b..db6fa1d 100644 (file)
@@ -547,6 +547,7 @@ struct intel_initial_plane_config {
        unsigned int tiling;
        int size;
        u32 base;
+       u8 rotation;
 };
 
 #define SKL_MIN_SRC_W 8
@@ -1646,7 +1647,7 @@ void intel_mode_from_pipe_config(struct drm_display_mode *mode,
 void intel_crtc_arm_fifo_underrun(struct intel_crtc *crtc,
                                  struct intel_crtc_state *crtc_state);
 
-u16 skl_scaler_calc_phase(int sub, bool chroma_center);
+u16 skl_scaler_calc_phase(int sub, int scale, bool chroma_center);
 int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state);
 int skl_max_scale(const struct intel_crtc_state *crtc_state,
                  u32 pixel_format);
index 217ed3e..76b5f94 100644 (file)
@@ -490,46 +490,6 @@ void intel_engine_setup_common(struct intel_engine_cs *engine)
        intel_engine_init_cmd_parser(engine);
 }
 
-int intel_engine_create_scratch(struct intel_engine_cs *engine,
-                               unsigned int size)
-{
-       struct drm_i915_gem_object *obj;
-       struct i915_vma *vma;
-       int ret;
-
-       WARN_ON(engine->scratch);
-
-       obj = i915_gem_object_create_stolen(engine->i915, size);
-       if (!obj)
-               obj = i915_gem_object_create_internal(engine->i915, size);
-       if (IS_ERR(obj)) {
-               DRM_ERROR("Failed to allocate scratch page\n");
-               return PTR_ERR(obj);
-       }
-
-       vma = i915_vma_instance(obj, &engine->i915->ggtt.vm, NULL);
-       if (IS_ERR(vma)) {
-               ret = PTR_ERR(vma);
-               goto err_unref;
-       }
-
-       ret = i915_vma_pin(vma, 0, 0, PIN_GLOBAL | PIN_HIGH);
-       if (ret)
-               goto err_unref;
-
-       engine->scratch = vma;
-       return 0;
-
-err_unref:
-       i915_gem_object_put(obj);
-       return ret;
-}
-
-void intel_engine_cleanup_scratch(struct intel_engine_cs *engine)
-{
-       i915_vma_unpin_and_release(&engine->scratch, 0);
-}
-
 static void cleanup_status_page(struct intel_engine_cs *engine)
 {
        if (HWS_NEEDS_PHYSICAL(engine->i915)) {
@@ -704,8 +664,6 @@ void intel_engine_cleanup_common(struct intel_engine_cs *engine)
 {
        struct drm_i915_private *i915 = engine->i915;
 
-       intel_engine_cleanup_scratch(engine);
-
        cleanup_status_page(engine);
 
        intel_engine_fini_breadcrumbs(engine);
@@ -720,6 +678,8 @@ void intel_engine_cleanup_common(struct intel_engine_cs *engine)
        __intel_context_unpin(i915->kernel_context, engine);
 
        i915_timeline_fini(&engine->timeline);
+
+       intel_wa_list_free(&engine->wa_list);
 }
 
 u64 intel_engine_get_active_head(const struct intel_engine_cs *engine)
index 648a13c..9a80181 100644 (file)
@@ -228,7 +228,9 @@ static void intel_hpd_irq_storm_reenable_work(struct work_struct *work)
                drm_for_each_connector_iter(connector, &conn_iter) {
                        struct intel_connector *intel_connector = to_intel_connector(connector);
 
-                       if (intel_connector->encoder->hpd_pin == pin) {
+                       /* Don't check MST ports, they don't have pins */
+                       if (!intel_connector->mst_port &&
+                           intel_connector->encoder->hpd_pin == pin) {
                                if (connector->polled != intel_connector->polled)
                                        DRM_DEBUG_DRIVER("Reenabling HPD on connector %s\n",
                                                         connector->name);
@@ -395,37 +397,54 @@ void intel_hpd_irq_handler(struct drm_i915_private *dev_priv,
        struct intel_encoder *encoder;
        bool storm_detected = false;
        bool queue_dig = false, queue_hp = false;
+       u32 long_hpd_pulse_mask = 0;
+       u32 short_hpd_pulse_mask = 0;
+       enum hpd_pin pin;
 
        if (!pin_mask)
                return;
 
        spin_lock(&dev_priv->irq_lock);
+
+       /*
+        * Determine whether ->hpd_pulse() exists for each pin, and
+        * whether we have a short or a long pulse. This is needed
+        * as each pin may have up to two encoders (HDMI and DP) and
+        * only the one of them (DP) will have ->hpd_pulse().
+        */
        for_each_intel_encoder(&dev_priv->drm, encoder) {
-               enum hpd_pin pin = encoder->hpd_pin;
                bool has_hpd_pulse = intel_encoder_has_hpd_pulse(encoder);
+               enum port port = encoder->port;
+               bool long_hpd;
 
+               pin = encoder->hpd_pin;
                if (!(BIT(pin) & pin_mask))
                        continue;
 
-               if (has_hpd_pulse) {
-                       bool long_hpd = long_mask & BIT(pin);
-                       enum port port = encoder->port;
+               if (!has_hpd_pulse)
+                       continue;
 
-                       DRM_DEBUG_DRIVER("digital hpd port %c - %s\n", port_name(port),
-                                        long_hpd ? "long" : "short");
-                       /*
-                        * For long HPD pulses we want to have the digital queue happen,
-                        * but we still want HPD storm detection to function.
-                        */
-                       queue_dig = true;
-                       if (long_hpd) {
-                               dev_priv->hotplug.long_port_mask |= (1 << port);
-                       } else {
-                               /* for short HPD just trigger the digital queue */
-                               dev_priv->hotplug.short_port_mask |= (1 << port);
-                               continue;
-                       }
+               long_hpd = long_mask & BIT(pin);
+
+               DRM_DEBUG_DRIVER("digital hpd port %c - %s\n", port_name(port),
+                                long_hpd ? "long" : "short");
+               queue_dig = true;
+
+               if (long_hpd) {
+                       long_hpd_pulse_mask |= BIT(pin);
+                       dev_priv->hotplug.long_port_mask |= BIT(port);
+               } else {
+                       short_hpd_pulse_mask |= BIT(pin);
+                       dev_priv->hotplug.short_port_mask |= BIT(port);
                }
+       }
+
+       /* Now process each pin just once */
+       for_each_hpd_pin(pin) {
+               bool long_hpd;
+
+               if (!(BIT(pin) & pin_mask))
+                       continue;
 
                if (dev_priv->hotplug.stats[pin].state == HPD_DISABLED) {
                        /*
@@ -442,11 +461,22 @@ void intel_hpd_irq_handler(struct drm_i915_private *dev_priv,
                if (dev_priv->hotplug.stats[pin].state != HPD_ENABLED)
                        continue;
 
-               if (!has_hpd_pulse) {
+               /*
+                * Delegate to ->hpd_pulse() if one of the encoders for this
+                * pin has it, otherwise let the hotplug_work deal with this
+                * pin directly.
+                */
+               if (((short_hpd_pulse_mask | long_hpd_pulse_mask) & BIT(pin))) {
+                       long_hpd = long_hpd_pulse_mask & BIT(pin);
+               } else {
                        dev_priv->hotplug.event_bits |= BIT(pin);
+                       long_hpd = true;
                        queue_hp = true;
                }
 
+               if (!long_hpd)
+                       continue;
+
                if (intel_hpd_irq_storm_detect(dev_priv, pin)) {
                        dev_priv->hotplug.event_bits &= ~BIT(pin);
                        storm_detected = true;
index 43957bb..58d1d3d 100644 (file)
@@ -424,7 +424,8 @@ static u64 execlists_update_context(struct i915_request *rq)
 
        reg_state[CTX_RING_TAIL+1] = intel_ring_set_tail(rq->ring, rq->tail);
 
-       /* True 32b PPGTT with dynamic page allocation: update PDP
+       /*
+        * True 32b PPGTT with dynamic page allocation: update PDP
         * registers and point the unallocated PDPs to scratch page.
         * PML4 is allocated during ppgtt init, so this is not needed
         * in 48-bit mode.
@@ -432,6 +433,22 @@ static u64 execlists_update_context(struct i915_request *rq)
        if (ppgtt && !i915_vm_is_48bit(&ppgtt->vm))
                execlists_update_context_pdps(ppgtt, reg_state);
 
+       /*
+        * Make sure the context image is complete before we submit it to HW.
+        *
+        * Ostensibly, writes (including the WCB) should be flushed prior to
+        * an uncached write such as our mmio register access, the empirical
+        * evidence (esp. on Braswell) suggests that the WC write into memory
+        * may not be visible to the HW prior to the completion of the UC
+        * register write and that we may begin execution from the context
+        * before its image is complete leading to invalid PD chasing.
+        *
+        * Furthermore, Braswell, at least, wants a full mb to be sure that
+        * the writes are coherent in memory (visible to the GPU) prior to
+        * execution, and not just visible to other CPUs (as is the result of
+        * wmb).
+        */
+       mb();
        return ce->lrc_desc;
 }
 
@@ -1431,9 +1448,10 @@ static int execlists_request_alloc(struct i915_request *request)
 static u32 *
 gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine, u32 *batch)
 {
+       /* NB no one else is allowed to scribble over scratch + 256! */
        *batch++ = MI_STORE_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT;
        *batch++ = i915_mmio_reg_offset(GEN8_L3SQCREG4);
-       *batch++ = i915_ggtt_offset(engine->scratch) + 256;
+       *batch++ = i915_scratch_offset(engine->i915) + 256;
        *batch++ = 0;
 
        *batch++ = MI_LOAD_REGISTER_IMM(1);
@@ -1447,7 +1465,7 @@ gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine, u32 *batch)
 
        *batch++ = MI_LOAD_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT;
        *batch++ = i915_mmio_reg_offset(GEN8_L3SQCREG4);
-       *batch++ = i915_ggtt_offset(engine->scratch) + 256;
+       *batch++ = i915_scratch_offset(engine->i915) + 256;
        *batch++ = 0;
 
        return batch;
@@ -1484,7 +1502,7 @@ static u32 *gen8_init_indirectctx_bb(struct intel_engine_cs *engine, u32 *batch)
                                       PIPE_CONTROL_GLOBAL_GTT_IVB |
                                       PIPE_CONTROL_CS_STALL |
                                       PIPE_CONTROL_QW_WRITE,
-                                      i915_ggtt_offset(engine->scratch) +
+                                      i915_scratch_offset(engine->i915) +
                                       2 * CACHELINE_BYTES);
 
        *batch++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
@@ -1561,7 +1579,7 @@ static u32 *gen9_init_indirectctx_bb(struct intel_engine_cs *engine, u32 *batch)
                                               PIPE_CONTROL_GLOBAL_GTT_IVB |
                                               PIPE_CONTROL_CS_STALL |
                                               PIPE_CONTROL_QW_WRITE,
-                                              i915_ggtt_offset(engine->scratch)
+                                              i915_scratch_offset(engine->i915)
                                               + 2 * CACHELINE_BYTES);
        }
 
@@ -1781,6 +1799,8 @@ static bool unexpected_starting_state(struct intel_engine_cs *engine)
 
 static int gen8_init_common_ring(struct intel_engine_cs *engine)
 {
+       intel_engine_apply_workarounds(engine);
+
        intel_mocs_init_engine(engine);
 
        intel_engine_reset_breadcrumbs(engine);
@@ -2127,7 +2147,7 @@ static int gen8_emit_flush_render(struct i915_request *request,
 {
        struct intel_engine_cs *engine = request->engine;
        u32 scratch_addr =
-               i915_ggtt_offset(engine->scratch) + 2 * CACHELINE_BYTES;
+               i915_scratch_offset(engine->i915) + 2 * CACHELINE_BYTES;
        bool vf_flush_wa = false, dc_flush_wa = false;
        u32 *cs, flags = 0;
        int len;
@@ -2464,10 +2484,6 @@ int logical_render_ring_init(struct intel_engine_cs *engine)
        if (ret)
                return ret;
 
-       ret = intel_engine_create_scratch(engine, PAGE_SIZE);
-       if (ret)
-               goto err_cleanup_common;
-
        ret = intel_init_workaround_bb(engine);
        if (ret) {
                /*
@@ -2479,11 +2495,9 @@ int logical_render_ring_init(struct intel_engine_cs *engine)
                          ret);
        }
 
-       return 0;
+       intel_engine_init_workarounds(engine);
 
-err_cleanup_common:
-       intel_engine_cleanup_common(engine);
-       return ret;
+       return 0;
 }
 
 int logical_xcs_ring_init(struct intel_engine_cs *engine)
index 245f002..3fe358d 100644 (file)
@@ -2493,6 +2493,9 @@ static uint32_t ilk_compute_pri_wm(const struct intel_crtc_state *cstate,
        uint32_t method1, method2;
        int cpp;
 
+       if (mem_value == 0)
+               return U32_MAX;
+
        if (!intel_wm_plane_visible(cstate, pstate))
                return 0;
 
@@ -2522,6 +2525,9 @@ static uint32_t ilk_compute_spr_wm(const struct intel_crtc_state *cstate,
        uint32_t method1, method2;
        int cpp;
 
+       if (mem_value == 0)
+               return U32_MAX;
+
        if (!intel_wm_plane_visible(cstate, pstate))
                return 0;
 
@@ -2545,6 +2551,9 @@ static uint32_t ilk_compute_cur_wm(const struct intel_crtc_state *cstate,
 {
        int cpp;
 
+       if (mem_value == 0)
+               return U32_MAX;
+
        if (!intel_wm_plane_visible(cstate, pstate))
                return 0;
 
@@ -3008,6 +3017,34 @@ static void snb_wm_latency_quirk(struct drm_i915_private *dev_priv)
        intel_print_wm_latency(dev_priv, "Cursor", dev_priv->wm.cur_latency);
 }
 
+static void snb_wm_lp3_irq_quirk(struct drm_i915_private *dev_priv)
+{
+       /*
+        * On some SNB machines (Thinkpad X220 Tablet at least)
+        * LP3 usage can cause vblank interrupts to be lost.
+        * The DEIIR bit will go high but it looks like the CPU
+        * never gets interrupted.
+        *
+        * It's not clear whether other interrupt source could
+        * be affected or if this is somehow limited to vblank
+        * interrupts only. To play it safe we disable LP3
+        * watermarks entirely.
+        */
+       if (dev_priv->wm.pri_latency[3] == 0 &&
+           dev_priv->wm.spr_latency[3] == 0 &&
+           dev_priv->wm.cur_latency[3] == 0)
+               return;
+
+       dev_priv->wm.pri_latency[3] = 0;
+       dev_priv->wm.spr_latency[3] = 0;
+       dev_priv->wm.cur_latency[3] = 0;
+
+       DRM_DEBUG_KMS("LP3 watermarks disabled due to potential for lost interrupts\n");
+       intel_print_wm_latency(dev_priv, "Primary", dev_priv->wm.pri_latency);
+       intel_print_wm_latency(dev_priv, "Sprite", dev_priv->wm.spr_latency);
+       intel_print_wm_latency(dev_priv, "Cursor", dev_priv->wm.cur_latency);
+}
+
 static void ilk_setup_wm_latency(struct drm_i915_private *dev_priv)
 {
        intel_read_wm_latency(dev_priv, dev_priv->wm.pri_latency);
@@ -3024,8 +3061,10 @@ static void ilk_setup_wm_latency(struct drm_i915_private *dev_priv)
        intel_print_wm_latency(dev_priv, "Sprite", dev_priv->wm.spr_latency);
        intel_print_wm_latency(dev_priv, "Cursor", dev_priv->wm.cur_latency);
 
-       if (IS_GEN6(dev_priv))
+       if (IS_GEN6(dev_priv)) {
                snb_wm_latency_quirk(dev_priv);
+               snb_wm_lp3_irq_quirk(dev_priv);
+       }
 }
 
 static void skl_setup_wm_latency(struct drm_i915_private *dev_priv)
index d0ef50b..1f8d2a6 100644 (file)
@@ -69,19 +69,28 @@ unsigned int intel_ring_update_space(struct intel_ring *ring)
 static int
 gen2_render_ring_flush(struct i915_request *rq, u32 mode)
 {
+       unsigned int num_store_dw;
        u32 cmd, *cs;
 
        cmd = MI_FLUSH;
-
+       num_store_dw = 0;
        if (mode & EMIT_INVALIDATE)
                cmd |= MI_READ_FLUSH;
+       if (mode & EMIT_FLUSH)
+               num_store_dw = 4;
 
-       cs = intel_ring_begin(rq, 2);
+       cs = intel_ring_begin(rq, 2 + 3 * num_store_dw);
        if (IS_ERR(cs))
                return PTR_ERR(cs);
 
        *cs++ = cmd;
-       *cs++ = MI_NOOP;
+       while (num_store_dw--) {
+               *cs++ = MI_STORE_DWORD_IMM | MI_MEM_VIRTUAL;
+               *cs++ = i915_scratch_offset(rq->i915);
+               *cs++ = 0;
+       }
+       *cs++ = MI_FLUSH | MI_NO_WRITE_FLUSH;
+
        intel_ring_advance(rq, cs);
 
        return 0;
@@ -91,6 +100,7 @@ static int
 gen4_render_ring_flush(struct i915_request *rq, u32 mode)
 {
        u32 cmd, *cs;
+       int i;
 
        /*
         * read/write caches:
@@ -127,12 +137,43 @@ gen4_render_ring_flush(struct i915_request *rq, u32 mode)
                        cmd |= MI_INVALIDATE_ISP;
        }
 
-       cs = intel_ring_begin(rq, 2);
+       i = 2;
+       if (mode & EMIT_INVALIDATE)
+               i += 20;
+
+       cs = intel_ring_begin(rq, i);
        if (IS_ERR(cs))
                return PTR_ERR(cs);
 
        *cs++ = cmd;
-       *cs++ = MI_NOOP;
+
+       /*
+        * A random delay to let the CS invalidate take effect? Without this
+        * delay, the GPU relocation path fails as the CS does not see
+        * the updated contents. Just as important, if we apply the flushes
+        * to the EMIT_FLUSH branch (i.e. immediately after the relocation
+        * write and before the invalidate on the next batch), the relocations
+        * still fail. This implies that is a delay following invalidation
+        * that is required to reset the caches as opposed to a delay to
+        * ensure the memory is written.
+        */
+       if (mode & EMIT_INVALIDATE) {
+               *cs++ = GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE;
+               *cs++ = i915_scratch_offset(rq->i915) | PIPE_CONTROL_GLOBAL_GTT;
+               *cs++ = 0;
+               *cs++ = 0;
+
+               for (i = 0; i < 12; i++)
+                       *cs++ = MI_FLUSH;
+
+               *cs++ = GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE;
+               *cs++ = i915_scratch_offset(rq->i915) | PIPE_CONTROL_GLOBAL_GTT;
+               *cs++ = 0;
+               *cs++ = 0;
+       }
+
+       *cs++ = cmd;
+
        intel_ring_advance(rq, cs);
 
        return 0;
@@ -178,8 +219,7 @@ gen4_render_ring_flush(struct i915_request *rq, u32 mode)
 static int
 intel_emit_post_sync_nonzero_flush(struct i915_request *rq)
 {
-       u32 scratch_addr =
-               i915_ggtt_offset(rq->engine->scratch) + 2 * CACHELINE_BYTES;
+       u32 scratch_addr = i915_scratch_offset(rq->i915) + 2 * CACHELINE_BYTES;
        u32 *cs;
 
        cs = intel_ring_begin(rq, 6);
@@ -212,8 +252,7 @@ intel_emit_post_sync_nonzero_flush(struct i915_request *rq)
 static int
 gen6_render_ring_flush(struct i915_request *rq, u32 mode)
 {
-       u32 scratch_addr =
-               i915_ggtt_offset(rq->engine->scratch) + 2 * CACHELINE_BYTES;
+       u32 scratch_addr = i915_scratch_offset(rq->i915) + 2 * CACHELINE_BYTES;
        u32 *cs, flags = 0;
        int ret;
 
@@ -282,8 +321,7 @@ gen7_render_ring_cs_stall_wa(struct i915_request *rq)
 static int
 gen7_render_ring_flush(struct i915_request *rq, u32 mode)
 {
-       u32 scratch_addr =
-               i915_ggtt_offset(rq->engine->scratch) + 2 * CACHELINE_BYTES;
+       u32 scratch_addr = i915_scratch_offset(rq->i915) + 2 * CACHELINE_BYTES;
        u32 *cs, flags = 0;
 
        /*
@@ -937,7 +975,7 @@ i965_emit_bb_start(struct i915_request *rq,
 }
 
 /* Just userspace ABI convention to limit the wa batch bo to a resonable size */
-#define I830_BATCH_LIMIT (256*1024)
+#define I830_BATCH_LIMIT SZ_256K
 #define I830_TLB_ENTRIES (2)
 #define I830_WA_SIZE max(I830_TLB_ENTRIES*4096, I830_BATCH_LIMIT)
 static int
@@ -945,7 +983,9 @@ i830_emit_bb_start(struct i915_request *rq,
                   u64 offset, u32 len,
                   unsigned int dispatch_flags)
 {
-       u32 *cs, cs_offset = i915_ggtt_offset(rq->engine->scratch);
+       u32 *cs, cs_offset = i915_scratch_offset(rq->i915);
+
+       GEM_BUG_ON(rq->i915->gt.scratch->size < I830_WA_SIZE);
 
        cs = intel_ring_begin(rq, 6);
        if (IS_ERR(cs))
@@ -1403,7 +1443,6 @@ static int intel_init_ring_buffer(struct intel_engine_cs *engine)
 {
        struct i915_timeline *timeline;
        struct intel_ring *ring;
-       unsigned int size;
        int err;
 
        intel_engine_setup_common(engine);
@@ -1428,21 +1467,12 @@ static int intel_init_ring_buffer(struct intel_engine_cs *engine)
        GEM_BUG_ON(engine->buffer);
        engine->buffer = ring;
 
-       size = PAGE_SIZE;
-       if (HAS_BROKEN_CS_TLB(engine->i915))
-               size = I830_WA_SIZE;
-       err = intel_engine_create_scratch(engine, size);
-       if (err)
-               goto err_unpin;
-
        err = intel_engine_init_common(engine);
        if (err)
-               goto err_scratch;
+               goto err_unpin;
 
        return 0;
 
-err_scratch:
-       intel_engine_cleanup_scratch(engine);
 err_unpin:
        intel_ring_unpin(ring);
 err_ring:
@@ -1516,7 +1546,7 @@ static int flush_pd_dir(struct i915_request *rq)
        /* Stall until the page table load is complete */
        *cs++ = MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT;
        *cs++ = i915_mmio_reg_offset(RING_PP_DIR_BASE(engine));
-       *cs++ = i915_ggtt_offset(engine->scratch);
+       *cs++ = i915_scratch_offset(rq->i915);
        *cs++ = MI_NOOP;
 
        intel_ring_advance(rq, cs);
@@ -1625,7 +1655,7 @@ static inline int mi_set_context(struct i915_request *rq, u32 flags)
                        /* Insert a delay before the next switch! */
                        *cs++ = MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT;
                        *cs++ = i915_mmio_reg_offset(last_reg);
-                       *cs++ = i915_ggtt_offset(engine->scratch);
+                       *cs++ = i915_scratch_offset(rq->i915);
                        *cs++ = MI_NOOP;
                }
                *cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
index 2dfa585..767a719 100644 (file)
@@ -15,6 +15,7 @@
 #include "i915_selftest.h"
 #include "i915_timeline.h"
 #include "intel_gpu_commands.h"
+#include "intel_workarounds.h"
 
 struct drm_printer;
 struct i915_sched_attr;
@@ -440,7 +441,7 @@ struct intel_engine_cs {
 
        struct intel_hw_status_page status_page;
        struct i915_ctx_workarounds wa_ctx;
-       struct i915_vma *scratch;
+       struct i915_wa_list wa_list;
 
        u32             irq_keep_mask; /* always keep these interrupts */
        u32             irq_enable_mask; /* bitmask to enable ring interrupt */
@@ -898,10 +899,6 @@ void intel_engine_setup_common(struct intel_engine_cs *engine);
 int intel_engine_init_common(struct intel_engine_cs *engine);
 void intel_engine_cleanup_common(struct intel_engine_cs *engine);
 
-int intel_engine_create_scratch(struct intel_engine_cs *engine,
-                               unsigned int size);
-void intel_engine_cleanup_scratch(struct intel_engine_cs *engine);
-
 int intel_init_render_ring_buffer(struct intel_engine_cs *engine);
 int intel_init_bsd_ring_buffer(struct intel_engine_cs *engine);
 int intel_init_blt_ring_buffer(struct intel_engine_cs *engine);
index 0fdabce..44e4491 100644 (file)
@@ -2748,6 +2748,12 @@ static const struct i915_power_well_desc icl_power_wells[] = {
                        .hsw.has_fuses = true,
                },
        },
+       {
+               .name = "DC off",
+               .domains = ICL_DISPLAY_DC_OFF_POWER_DOMAINS,
+               .ops = &gen9_dc_off_power_well_ops,
+               .id = DISP_PW_ID_NONE,
+       },
        {
                .name = "power well 2",
                .domains = ICL_PW_2_POWER_DOMAINS,
@@ -2759,12 +2765,6 @@ static const struct i915_power_well_desc icl_power_wells[] = {
                        .hsw.has_fuses = true,
                },
        },
-       {
-               .name = "DC off",
-               .domains = ICL_DISPLAY_DC_OFF_POWER_DOMAINS,
-               .ops = &gen9_dc_off_power_well_ops,
-               .id = DISP_PW_ID_NONE,
-       },
        {
                .name = "power well 3",
                .domains = ICL_PW_3_POWER_DOMAINS,
@@ -3176,8 +3176,7 @@ static u8 intel_dbuf_max_slices(struct drm_i915_private *dev_priv)
 void icl_dbuf_slices_update(struct drm_i915_private *dev_priv,
                            u8 req_slices)
 {
-       u8 hw_enabled_slices = dev_priv->wm.skl_hw.ddb.enabled_slices;
-       u32 val;
+       const u8 hw_enabled_slices = dev_priv->wm.skl_hw.ddb.enabled_slices;
        bool ret;
 
        if (req_slices > intel_dbuf_max_slices(dev_priv)) {
@@ -3188,7 +3187,6 @@ void icl_dbuf_slices_update(struct drm_i915_private *dev_priv,
        if (req_slices == hw_enabled_slices || req_slices == 0)
                return;
 
-       val = I915_READ(DBUF_CTL_S2);
        if (req_slices > hw_enabled_slices)
                ret = intel_dbuf_slice_set(dev_priv, DBUF_CTL_S2, true);
        else
index 5fd2f7b..d3090a7 100644 (file)
@@ -302,13 +302,65 @@ skl_plane_max_stride(struct intel_plane *plane,
                return min(8192 * cpp, 32768);
 }
 
+static void
+skl_program_scaler(struct intel_plane *plane,
+                  const struct intel_crtc_state *crtc_state,
+                  const struct intel_plane_state *plane_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+       enum pipe pipe = plane->pipe;
+       int scaler_id = plane_state->scaler_id;
+       const struct intel_scaler *scaler =
+               &crtc_state->scaler_state.scalers[scaler_id];
+       int crtc_x = plane_state->base.dst.x1;
+       int crtc_y = plane_state->base.dst.y1;
+       uint32_t crtc_w = drm_rect_width(&plane_state->base.dst);
+       uint32_t crtc_h = drm_rect_height(&plane_state->base.dst);
+       u16 y_hphase, uv_rgb_hphase;
+       u16 y_vphase, uv_rgb_vphase;
+       int hscale, vscale;
+
+       hscale = drm_rect_calc_hscale(&plane_state->base.src,
+                                     &plane_state->base.dst,
+                                     0, INT_MAX);
+       vscale = drm_rect_calc_vscale(&plane_state->base.src,
+                                     &plane_state->base.dst,
+                                     0, INT_MAX);
+
+       /* TODO: handle sub-pixel coordinates */
+       if (plane_state->base.fb->format->format == DRM_FORMAT_NV12) {
+               y_hphase = skl_scaler_calc_phase(1, hscale, false);
+               y_vphase = skl_scaler_calc_phase(1, vscale, false);
+
+               /* MPEG2 chroma siting convention */
+               uv_rgb_hphase = skl_scaler_calc_phase(2, hscale, true);
+               uv_rgb_vphase = skl_scaler_calc_phase(2, vscale, false);
+       } else {
+               /* not used */
+               y_hphase = 0;
+               y_vphase = 0;
+
+               uv_rgb_hphase = skl_scaler_calc_phase(1, hscale, false);
+               uv_rgb_vphase = skl_scaler_calc_phase(1, vscale, false);
+       }
+
+       I915_WRITE_FW(SKL_PS_CTRL(pipe, scaler_id),
+                     PS_SCALER_EN | PS_PLANE_SEL(plane->id) | scaler->mode);
+       I915_WRITE_FW(SKL_PS_PWR_GATE(pipe, scaler_id), 0);
+       I915_WRITE_FW(SKL_PS_VPHASE(pipe, scaler_id),
+                     PS_Y_PHASE(y_vphase) | PS_UV_RGB_PHASE(uv_rgb_vphase));
+       I915_WRITE_FW(SKL_PS_HPHASE(pipe, scaler_id),
+                     PS_Y_PHASE(y_hphase) | PS_UV_RGB_PHASE(uv_rgb_hphase));
+       I915_WRITE_FW(SKL_PS_WIN_POS(pipe, scaler_id), (crtc_x << 16) | crtc_y);
+       I915_WRITE_FW(SKL_PS_WIN_SZ(pipe, scaler_id), (crtc_w << 16) | crtc_h);
+}
+
 void
 skl_update_plane(struct intel_plane *plane,
                 const struct intel_crtc_state *crtc_state,
                 const struct intel_plane_state *plane_state)
 {
        struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
-       const struct drm_framebuffer *fb = plane_state->base.fb;
        enum plane_id plane_id = plane->id;
        enum pipe pipe = plane->pipe;
        u32 plane_ctl = plane_state->ctl;
@@ -318,8 +370,6 @@ skl_update_plane(struct intel_plane *plane,
        u32 aux_stride = skl_plane_stride(plane_state, 1);
        int crtc_x = plane_state->base.dst.x1;
        int crtc_y = plane_state->base.dst.y1;
-       uint32_t crtc_w = drm_rect_width(&plane_state->base.dst);
-       uint32_t crtc_h = drm_rect_height(&plane_state->base.dst);
        uint32_t x = plane_state->color_plane[0].x;
        uint32_t y = plane_state->color_plane[0].y;
        uint32_t src_w = drm_rect_width(&plane_state->base.src) >> 16;
@@ -329,8 +379,6 @@ skl_update_plane(struct intel_plane *plane,
        /* Sizes are 0 based */
        src_w--;
        src_h--;
-       crtc_w--;
-       crtc_h--;
 
        spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
 
@@ -353,41 +401,8 @@ skl_update_plane(struct intel_plane *plane,
                      (plane_state->color_plane[1].y << 16) |
                      plane_state->color_plane[1].x);
 
-       /* program plane scaler */
        if (plane_state->scaler_id >= 0) {
-               int scaler_id = plane_state->scaler_id;
-               const struct intel_scaler *scaler =
-                       &crtc_state->scaler_state.scalers[scaler_id];
-               u16 y_hphase, uv_rgb_hphase;
-               u16 y_vphase, uv_rgb_vphase;
-
-               /* TODO: handle sub-pixel coordinates */
-               if (fb->format->format == DRM_FORMAT_NV12) {
-                       y_hphase = skl_scaler_calc_phase(1, false);
-                       y_vphase = skl_scaler_calc_phase(1, false);
-
-                       /* MPEG2 chroma siting convention */
-                       uv_rgb_hphase = skl_scaler_calc_phase(2, true);
-                       uv_rgb_vphase = skl_scaler_calc_phase(2, false);
-               } else {
-                       /* not used */
-                       y_hphase = 0;
-                       y_vphase = 0;
-
-                       uv_rgb_hphase = skl_scaler_calc_phase(1, false);
-                       uv_rgb_vphase = skl_scaler_calc_phase(1, false);
-               }
-
-               I915_WRITE_FW(SKL_PS_CTRL(pipe, scaler_id),
-                             PS_SCALER_EN | PS_PLANE_SEL(plane_id) | scaler->mode);
-               I915_WRITE_FW(SKL_PS_PWR_GATE(pipe, scaler_id), 0);
-               I915_WRITE_FW(SKL_PS_VPHASE(pipe, scaler_id),
-                             PS_Y_PHASE(y_vphase) | PS_UV_RGB_PHASE(uv_rgb_vphase));
-               I915_WRITE_FW(SKL_PS_HPHASE(pipe, scaler_id),
-                             PS_Y_PHASE(y_hphase) | PS_UV_RGB_PHASE(uv_rgb_hphase));
-               I915_WRITE_FW(SKL_PS_WIN_POS(pipe, scaler_id), (crtc_x << 16) | crtc_y);
-               I915_WRITE_FW(SKL_PS_WIN_SZ(pipe, scaler_id),
-                             ((crtc_w + 1) << 16)|(crtc_h + 1));
+               skl_program_scaler(plane, crtc_state, plane_state);
 
                I915_WRITE_FW(PLANE_POS(pipe, plane_id), 0);
        } else {
index 4bcdeaf..6e58089 100644 (file)
  * - Public functions to init or apply the given workaround type.
  */
 
+static void wa_init_start(struct i915_wa_list *wal, const char *name)
+{
+       wal->name = name;
+}
+
+static void wa_init_finish(struct i915_wa_list *wal)
+{
+       if (!wal->count)
+               return;
+
+       DRM_DEBUG_DRIVER("Initialized %u %s workarounds\n",
+                        wal->count, wal->name);
+}
+
 static void wa_add(struct drm_i915_private *i915,
                   i915_reg_t reg, const u32 mask, const u32 val)
 {
@@ -580,160 +594,175 @@ int intel_ctx_workarounds_emit(struct i915_request *rq)
        return 0;
 }
 
-static void bdw_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+static void
+wal_add(struct i915_wa_list *wal, const struct i915_wa *wa)
+{
+       const unsigned int grow = 1 << 4;
+
+       GEM_BUG_ON(!is_power_of_2(grow));
+
+       if (IS_ALIGNED(wal->count, grow)) { /* Either uninitialized or full. */
+               struct i915_wa *list;
+
+               list = kmalloc_array(ALIGN(wal->count + 1, grow), sizeof(*wa),
+                                    GFP_KERNEL);
+               if (!list) {
+                       DRM_ERROR("No space for workaround init!\n");
+                       return;
+               }
+
+               if (wal->list)
+                       memcpy(list, wal->list, sizeof(*wa) * wal->count);
+
+               wal->list = list;
+       }
+
+       wal->list[wal->count++] = *wa;
+}
+
+static void
+wa_masked_en(struct i915_wa_list *wal, i915_reg_t reg, u32 val)
+{
+       struct i915_wa wa = {
+               .reg = reg,
+               .mask = val,
+               .val = _MASKED_BIT_ENABLE(val)
+       };
+
+       wal_add(wal, &wa);
+}
+
+static void
+wa_write_masked_or(struct i915_wa_list *wal, i915_reg_t reg, u32 mask,
+                  u32 val)
 {
+       struct i915_wa wa = {
+               .reg = reg,
+               .mask = mask,
+               .val = val
+       };
+
+       wal_add(wal, &wa);
 }
 
-static void chv_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+static void
+wa_write(struct i915_wa_list *wal, i915_reg_t reg, u32 val)
 {
+       wa_write_masked_or(wal, reg, ~0, val);
 }
 
-static void gen9_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+static void
+wa_write_or(struct i915_wa_list *wal, i915_reg_t reg, u32 val)
 {
-       /* WaContextSwitchWithConcurrentTLBInvalidate:skl,bxt,kbl,glk,cfl */
-       I915_WRITE(GEN9_CSFE_CHICKEN1_RCS,
-                  _MASKED_BIT_ENABLE(GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE));
+       wa_write_masked_or(wal, reg, val, val);
+}
 
-       /* WaEnableLbsSlaRetryTimerDecrement:skl,bxt,kbl,glk,cfl */
-       I915_WRITE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) |
-                  GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE);
+static void gen9_gt_workarounds_init(struct drm_i915_private *i915)
+{
+       struct i915_wa_list *wal = &i915->gt_wa_list;
 
        /* WaDisableKillLogic:bxt,skl,kbl */
-       if (!IS_COFFEELAKE(dev_priv))
-               I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
-                          ECOCHK_DIS_TLB);
+       if (!IS_COFFEELAKE(i915))
+               wa_write_or(wal,
+                           GAM_ECOCHK,
+                           ECOCHK_DIS_TLB);
 
-       if (HAS_LLC(dev_priv)) {
+       if (HAS_LLC(i915)) {
                /* WaCompressedResourceSamplerPbeMediaNewHashMode:skl,kbl
                 *
                 * Must match Display Engine. See
                 * WaCompressedResourceDisplayNewHashMode.
                 */
-               I915_WRITE(MMCD_MISC_CTRL,
-                          I915_READ(MMCD_MISC_CTRL) |
-                          MMCD_PCLA |
-                          MMCD_HOTSPOT_EN);
+               wa_write_or(wal,
+                           MMCD_MISC_CTRL,
+                           MMCD_PCLA | MMCD_HOTSPOT_EN);
        }
 
        /* WaDisableHDCInvalidation:skl,bxt,kbl,cfl */
-       I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
-                  BDW_DISABLE_HDC_INVALIDATION);
-
-       /* WaProgramL3SqcReg1DefaultForPerf:bxt,glk */
-       if (IS_GEN9_LP(dev_priv)) {
-               u32 val = I915_READ(GEN8_L3SQCREG1);
-
-               val &= ~L3_PRIO_CREDITS_MASK;
-               val |= L3_GENERAL_PRIO_CREDITS(62) | L3_HIGH_PRIO_CREDITS(2);
-               I915_WRITE(GEN8_L3SQCREG1, val);
-       }
-
-       /* WaOCLCoherentLineFlush:skl,bxt,kbl,cfl */
-       I915_WRITE(GEN8_L3SQCREG4,
-                  I915_READ(GEN8_L3SQCREG4) | GEN8_LQSC_FLUSH_COHERENT_LINES);
-
-       /* WaEnablePreemptionGranularityControlByUMD:skl,bxt,kbl,cfl,[cnl] */
-       I915_WRITE(GEN7_FF_SLICE_CS_CHICKEN1,
-                  _MASKED_BIT_ENABLE(GEN9_FFSC_PERCTX_PREEMPT_CTRL));
+       wa_write_or(wal,
+                   GAM_ECOCHK,
+                   BDW_DISABLE_HDC_INVALIDATION);
 }
 
-static void skl_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+static void skl_gt_workarounds_init(struct drm_i915_private *i915)
 {
-       gen9_gt_workarounds_apply(dev_priv);
+       struct i915_wa_list *wal = &i915->gt_wa_list;
 
-       /* WaEnableGapsTsvCreditFix:skl */
-       I915_WRITE(GEN8_GARBCNTL,
-                  I915_READ(GEN8_GARBCNTL) | GEN9_GAPS_TSV_CREDIT_DISABLE);
+       gen9_gt_workarounds_init(i915);
 
        /* WaDisableGafsUnitClkGating:skl */
-       I915_WRITE(GEN7_UCGCTL4,
-                  I915_READ(GEN7_UCGCTL4) | GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
+       wa_write_or(wal,
+                   GEN7_UCGCTL4,
+                   GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
 
        /* WaInPlaceDecompressionHang:skl */
-       if (IS_SKL_REVID(dev_priv, SKL_REVID_H0, REVID_FOREVER))
-               I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
-                          I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
-                          GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
+       if (IS_SKL_REVID(i915, SKL_REVID_H0, REVID_FOREVER))
+               wa_write_or(wal,
+                           GEN9_GAMT_ECO_REG_RW_IA,
+                           GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
 }
 
-static void bxt_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+static void bxt_gt_workarounds_init(struct drm_i915_private *i915)
 {
-       gen9_gt_workarounds_apply(dev_priv);
+       struct i915_wa_list *wal = &i915->gt_wa_list;
 
-       /* WaDisablePooledEuLoadBalancingFix:bxt */
-       I915_WRITE(FF_SLICE_CS_CHICKEN2,
-                  _MASKED_BIT_ENABLE(GEN9_POOLED_EU_LOAD_BALANCING_FIX_DISABLE));
+       gen9_gt_workarounds_init(i915);
 
        /* WaInPlaceDecompressionHang:bxt */
-       I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
-                  I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
-                  GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
+       wa_write_or(wal,
+                   GEN9_GAMT_ECO_REG_RW_IA,
+                   GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
 }
 
-static void kbl_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+static void kbl_gt_workarounds_init(struct drm_i915_private *i915)
 {
-       gen9_gt_workarounds_apply(dev_priv);
+       struct i915_wa_list *wal = &i915->gt_wa_list;
 
-       /* WaEnableGapsTsvCreditFix:kbl */
-       I915_WRITE(GEN8_GARBCNTL,
-                  I915_READ(GEN8_GARBCNTL) | GEN9_GAPS_TSV_CREDIT_DISABLE);
+       gen9_gt_workarounds_init(i915);
 
        /* WaDisableDynamicCreditSharing:kbl */
-       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
-               I915_WRITE(GAMT_CHKN_BIT_REG,
-                          I915_READ(GAMT_CHKN_BIT_REG) |
-                          GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING);
+       if (IS_KBL_REVID(i915, 0, KBL_REVID_B0))
+               wa_write_or(wal,
+                           GAMT_CHKN_BIT_REG,
+                           GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING);
 
        /* WaDisableGafsUnitClkGating:kbl */
-       I915_WRITE(GEN7_UCGCTL4,
-                  I915_READ(GEN7_UCGCTL4) | GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
+       wa_write_or(wal,
+                   GEN7_UCGCTL4,
+                   GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
 
        /* WaInPlaceDecompressionHang:kbl */
-       I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
-                  I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
-                  GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
-
-       /* WaKBLVECSSemaphoreWaitPoll:kbl */
-       if (IS_KBL_REVID(dev_priv, KBL_REVID_A0, KBL_REVID_E0)) {
-               struct intel_engine_cs *engine;
-               unsigned int tmp;
-
-               for_each_engine(engine, dev_priv, tmp) {
-                       if (engine->id == RCS)
-                               continue;
-
-                       I915_WRITE(RING_SEMA_WAIT_POLL(engine->mmio_base), 1);
-               }
-       }
+       wa_write_or(wal,
+                   GEN9_GAMT_ECO_REG_RW_IA,
+                   GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
 }
 
-static void glk_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+static void glk_gt_workarounds_init(struct drm_i915_private *i915)
 {
-       gen9_gt_workarounds_apply(dev_priv);
+       gen9_gt_workarounds_init(i915);
 }
 
-static void cfl_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+static void cfl_gt_workarounds_init(struct drm_i915_private *i915)
 {
-       gen9_gt_workarounds_apply(dev_priv);
+       struct i915_wa_list *wal = &i915->gt_wa_list;
 
-       /* WaEnableGapsTsvCreditFix:cfl */
-       I915_WRITE(GEN8_GARBCNTL,
-                  I915_READ(GEN8_GARBCNTL) | GEN9_GAPS_TSV_CREDIT_DISABLE);
+       gen9_gt_workarounds_init(i915);
 
        /* WaDisableGafsUnitClkGating:cfl */
-       I915_WRITE(GEN7_UCGCTL4,
-                  I915_READ(GEN7_UCGCTL4) | GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
+       wa_write_or(wal,
+                   GEN7_UCGCTL4,
+                   GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
 
        /* WaInPlaceDecompressionHang:cfl */
-       I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
-                  I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
-                  GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
+       wa_write_or(wal,
+                   GEN9_GAMT_ECO_REG_RW_IA,
+                   GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
 }
 
 static void wa_init_mcr(struct drm_i915_private *dev_priv)
 {
        const struct sseu_dev_info *sseu = &(INTEL_INFO(dev_priv)->sseu);
-       u32 mcr;
+       struct i915_wa_list *wal = &dev_priv->gt_wa_list;
        u32 mcr_slice_subslice_mask;
 
        /*
@@ -770,8 +799,6 @@ static void wa_init_mcr(struct drm_i915_private *dev_priv)
                WARN_ON((enabled_mask & disabled_mask) != enabled_mask);
        }
 
-       mcr = I915_READ(GEN8_MCR_SELECTOR);
-
        if (INTEL_GEN(dev_priv) >= 11)
                mcr_slice_subslice_mask = GEN11_MCR_SLICE_MASK |
                                          GEN11_MCR_SUBSLICE_MASK;
@@ -789,148 +816,170 @@ static void wa_init_mcr(struct drm_i915_private *dev_priv)
         * occasions, such as INSTDONE, where this value is dependent
         * on s/ss combo, the read should be done with read_subslice_reg.
         */
-       mcr &= ~mcr_slice_subslice_mask;
-       mcr |= intel_calculate_mcr_s_ss_select(dev_priv);
-       I915_WRITE(GEN8_MCR_SELECTOR, mcr);
+       wa_write_masked_or(wal,
+                          GEN8_MCR_SELECTOR,
+                          mcr_slice_subslice_mask,
+                          intel_calculate_mcr_s_ss_select(dev_priv));
 }
 
-static void cnl_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+static void cnl_gt_workarounds_init(struct drm_i915_private *i915)
 {
-       wa_init_mcr(dev_priv);
+       struct i915_wa_list *wal = &i915->gt_wa_list;
+
+       wa_init_mcr(i915);
 
        /* WaDisableI2mCycleOnWRPort:cnl (pre-prod) */
-       if (IS_CNL_REVID(dev_priv, CNL_REVID_B0, CNL_REVID_B0))
-               I915_WRITE(GAMT_CHKN_BIT_REG,
-                          I915_READ(GAMT_CHKN_BIT_REG) |
-                          GAMT_CHKN_DISABLE_I2M_CYCLE_ON_WR_PORT);
+       if (IS_CNL_REVID(i915, CNL_REVID_B0, CNL_REVID_B0))
+               wa_write_or(wal,
+                           GAMT_CHKN_BIT_REG,
+                           GAMT_CHKN_DISABLE_I2M_CYCLE_ON_WR_PORT);
 
        /* WaInPlaceDecompressionHang:cnl */
-       I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
-                  I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
-                  GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
-
-       /* WaEnablePreemptionGranularityControlByUMD:cnl */
-       I915_WRITE(GEN7_FF_SLICE_CS_CHICKEN1,
-                  _MASKED_BIT_ENABLE(GEN9_FFSC_PERCTX_PREEMPT_CTRL));
+       wa_write_or(wal,
+                   GEN9_GAMT_ECO_REG_RW_IA,
+                   GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
 }
 
-static void icl_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+static void icl_gt_workarounds_init(struct drm_i915_private *i915)
 {
-       wa_init_mcr(dev_priv);
+       struct i915_wa_list *wal = &i915->gt_wa_list;
 
-       /* This is not an Wa. Enable for better image quality */
-       I915_WRITE(_3D_CHICKEN3,
-                  _MASKED_BIT_ENABLE(_3D_CHICKEN3_AA_LINE_QUALITY_FIX_ENABLE));
+       wa_init_mcr(i915);
 
        /* WaInPlaceDecompressionHang:icl */
-       I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA, I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
-                                           GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
-
-       /* WaPipelineFlushCoherentLines:icl */
-       I915_WRITE(GEN8_L3SQCREG4, I915_READ(GEN8_L3SQCREG4) |
-                                  GEN8_LQSC_FLUSH_COHERENT_LINES);
-
-       /* Wa_1405543622:icl
-        * Formerly known as WaGAPZPriorityScheme
-        */
-       I915_WRITE(GEN8_GARBCNTL, I915_READ(GEN8_GARBCNTL) |
-                                 GEN11_ARBITRATION_PRIO_ORDER_MASK);
-
-       /* Wa_1604223664:icl
-        * Formerly known as WaL3BankAddressHashing
-        */
-       I915_WRITE(GEN8_GARBCNTL,
-                  (I915_READ(GEN8_GARBCNTL) & ~GEN11_HASH_CTRL_EXCL_MASK) |
-                  GEN11_HASH_CTRL_EXCL_BIT0);
-       I915_WRITE(GEN11_GLBLINVL,
-                  (I915_READ(GEN11_GLBLINVL) & ~GEN11_BANK_HASH_ADDR_EXCL_MASK) |
-                  GEN11_BANK_HASH_ADDR_EXCL_BIT0);
+       wa_write_or(wal,
+                   GEN9_GAMT_ECO_REG_RW_IA,
+                   GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
 
        /* WaModifyGamTlbPartitioning:icl */
-       I915_WRITE(GEN11_GACB_PERF_CTRL,
-                  (I915_READ(GEN11_GACB_PERF_CTRL) & ~GEN11_HASH_CTRL_MASK) |
-                  GEN11_HASH_CTRL_BIT0 | GEN11_HASH_CTRL_BIT4);
-
-       /* Wa_1405733216:icl
-        * Formerly known as WaDisableCleanEvicts
-        */
-       I915_WRITE(GEN8_L3SQCREG4, I915_READ(GEN8_L3SQCREG4) |
-                                  GEN11_LQSC_CLEAN_EVICT_DISABLE);
+       wa_write_masked_or(wal,
+                          GEN11_GACB_PERF_CTRL,
+                          GEN11_HASH_CTRL_MASK,
+                          GEN11_HASH_CTRL_BIT0 | GEN11_HASH_CTRL_BIT4);
 
        /* Wa_1405766107:icl
         * Formerly known as WaCL2SFHalfMaxAlloc
         */
-       I915_WRITE(GEN11_LSN_UNSLCVC, I915_READ(GEN11_LSN_UNSLCVC) |
-                                     GEN11_LSN_UNSLCVC_GAFS_HALF_SF_MAXALLOC |
-                                     GEN11_LSN_UNSLCVC_GAFS_HALF_CL2_MAXALLOC);
+       wa_write_or(wal,
+                   GEN11_LSN_UNSLCVC,
+                   GEN11_LSN_UNSLCVC_GAFS_HALF_SF_MAXALLOC |
+                   GEN11_LSN_UNSLCVC_GAFS_HALF_CL2_MAXALLOC);
 
        /* Wa_220166154:icl
         * Formerly known as WaDisCtxReload
         */
-       I915_WRITE(GAMW_ECO_DEV_RW_IA_REG, I915_READ(GAMW_ECO_DEV_RW_IA_REG) |
-                                          GAMW_ECO_DEV_CTX_RELOAD_DISABLE);
+       wa_write_or(wal,
+                   GEN8_GAMW_ECO_DEV_RW_IA,
+                   GAMW_ECO_DEV_CTX_RELOAD_DISABLE);
 
        /* Wa_1405779004:icl (pre-prod) */
-       if (IS_ICL_REVID(dev_priv, ICL_REVID_A0, ICL_REVID_A0))
-               I915_WRITE(SLICE_UNIT_LEVEL_CLKGATE,
-                          I915_READ(SLICE_UNIT_LEVEL_CLKGATE) |
-                          MSCUNIT_CLKGATE_DIS);
+       if (IS_ICL_REVID(i915, ICL_REVID_A0, ICL_REVID_A0))
+               wa_write_or(wal,
+                           SLICE_UNIT_LEVEL_CLKGATE,
+                           MSCUNIT_CLKGATE_DIS);
 
        /* Wa_1406680159:icl */
-       I915_WRITE(SUBSLICE_UNIT_LEVEL_CLKGATE,
-                  I915_READ(SUBSLICE_UNIT_LEVEL_CLKGATE) |
-                  GWUNIT_CLKGATE_DIS);
-
-       /* Wa_1604302699:icl */
-       I915_WRITE(GEN10_L3_CHICKEN_MODE_REGISTER,
-                  I915_READ(GEN10_L3_CHICKEN_MODE_REGISTER) |
-                  GEN11_I2M_WRITE_DISABLE);
+       wa_write_or(wal,
+                   SUBSLICE_UNIT_LEVEL_CLKGATE,
+                   GWUNIT_CLKGATE_DIS);
 
        /* Wa_1406838659:icl (pre-prod) */
-       if (IS_ICL_REVID(dev_priv, ICL_REVID_A0, ICL_REVID_B0))
-               I915_WRITE(INF_UNIT_LEVEL_CLKGATE,
-                          I915_READ(INF_UNIT_LEVEL_CLKGATE) |
-                          CGPSF_CLKGATE_DIS);
-
-       /* WaForwardProgressSoftReset:icl */
-       I915_WRITE(GEN10_SCRATCH_LNCF2,
-                  I915_READ(GEN10_SCRATCH_LNCF2) |
-                  PMFLUSHDONE_LNICRSDROP |
-                  PMFLUSH_GAPL3UNBLOCK |
-                  PMFLUSHDONE_LNEBLK);
+       if (IS_ICL_REVID(i915, ICL_REVID_A0, ICL_REVID_B0))
+               wa_write_or(wal,
+                           INF_UNIT_LEVEL_CLKGATE,
+                           CGPSF_CLKGATE_DIS);
 
        /* Wa_1406463099:icl
         * Formerly known as WaGamTlbPendError
         */
-       I915_WRITE(GAMT_CHKN_BIT_REG,
-                  I915_READ(GAMT_CHKN_BIT_REG) |
-                  GAMT_CHKN_DISABLE_L3_COH_PIPE);
+       wa_write_or(wal,
+                   GAMT_CHKN_BIT_REG,
+                   GAMT_CHKN_DISABLE_L3_COH_PIPE);
 }
 
-void intel_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+void intel_gt_init_workarounds(struct drm_i915_private *i915)
 {
-       if (INTEL_GEN(dev_priv) < 8)
+       struct i915_wa_list *wal = &i915->gt_wa_list;
+
+       wa_init_start(wal, "GT");
+
+       if (INTEL_GEN(i915) < 8)
                return;
-       else if (IS_BROADWELL(dev_priv))
-               bdw_gt_workarounds_apply(dev_priv);
-       else if (IS_CHERRYVIEW(dev_priv))
-               chv_gt_workarounds_apply(dev_priv);
-       else if (IS_SKYLAKE(dev_priv))
-               skl_gt_workarounds_apply(dev_priv);
-       else if (IS_BROXTON(dev_priv))
-               bxt_gt_workarounds_apply(dev_priv);
-       else if (IS_KABYLAKE(dev_priv))
-               kbl_gt_workarounds_apply(dev_priv);
-       else if (IS_GEMINILAKE(dev_priv))
-               glk_gt_workarounds_apply(dev_priv);
-       else if (IS_COFFEELAKE(dev_priv))
-               cfl_gt_workarounds_apply(dev_priv);
-       else if (IS_CANNONLAKE(dev_priv))
-               cnl_gt_workarounds_apply(dev_priv);
-       else if (IS_ICELAKE(dev_priv))
-               icl_gt_workarounds_apply(dev_priv);
+       else if (IS_BROADWELL(i915))
+               return;
+       else if (IS_CHERRYVIEW(i915))
+               return;
+       else if (IS_SKYLAKE(i915))
+               skl_gt_workarounds_init(i915);
+       else if (IS_BROXTON(i915))
+               bxt_gt_workarounds_init(i915);
+       else if (IS_KABYLAKE(i915))
+               kbl_gt_workarounds_init(i915);
+       else if (IS_GEMINILAKE(i915))
+               glk_gt_workarounds_init(i915);
+       else if (IS_COFFEELAKE(i915))
+               cfl_gt_workarounds_init(i915);
+       else if (IS_CANNONLAKE(i915))
+               cnl_gt_workarounds_init(i915);
+       else if (IS_ICELAKE(i915))
+               icl_gt_workarounds_init(i915);
        else
-               MISSING_CASE(INTEL_GEN(dev_priv));
+               MISSING_CASE(INTEL_GEN(i915));
+
+       wa_init_finish(wal);
+}
+
+static enum forcewake_domains
+wal_get_fw_for_rmw(struct drm_i915_private *dev_priv,
+                  const struct i915_wa_list *wal)
+{
+       enum forcewake_domains fw = 0;
+       struct i915_wa *wa;
+       unsigned int i;
+
+       for (i = 0, wa = wal->list; i < wal->count; i++, wa++)
+               fw |= intel_uncore_forcewake_for_reg(dev_priv,
+                                                    wa->reg,
+                                                    FW_REG_READ |
+                                                    FW_REG_WRITE);
+
+       return fw;
+}
+
+static void
+wa_list_apply(struct drm_i915_private *dev_priv, const struct i915_wa_list *wal)
+{
+       enum forcewake_domains fw;
+       unsigned long flags;
+       struct i915_wa *wa;
+       unsigned int i;
+
+       if (!wal->count)
+               return;
+
+       fw = wal_get_fw_for_rmw(dev_priv, wal);
+
+       spin_lock_irqsave(&dev_priv->uncore.lock, flags);
+       intel_uncore_forcewake_get__locked(dev_priv, fw);
+
+       for (i = 0, wa = wal->list; i < wal->count; i++, wa++) {
+               u32 val = I915_READ_FW(wa->reg);
+
+               val &= ~wa->mask;
+               val |= wa->val;
+
+               I915_WRITE_FW(wa->reg, val);
+       }
+
+       intel_uncore_forcewake_put__locked(dev_priv, fw);
+       spin_unlock_irqrestore(&dev_priv->uncore.lock, flags);
+
+       DRM_DEBUG_DRIVER("Applied %u %s workarounds\n", wal->count, wal->name);
+}
+
+void intel_gt_apply_workarounds(struct drm_i915_private *dev_priv)
+{
+       wa_list_apply(dev_priv, &dev_priv->gt_wa_list);
 }
 
 struct whitelist {
@@ -1077,6 +1126,146 @@ void intel_whitelist_workarounds_apply(struct intel_engine_cs *engine)
        whitelist_apply(engine, whitelist_build(engine, &w));
 }
 
+static void rcs_engine_wa_init(struct intel_engine_cs *engine)
+{
+       struct drm_i915_private *i915 = engine->i915;
+       struct i915_wa_list *wal = &engine->wa_list;
+
+       if (IS_ICELAKE(i915)) {
+               /* This is not an Wa. Enable for better image quality */
+               wa_masked_en(wal,
+                            _3D_CHICKEN3,
+                            _3D_CHICKEN3_AA_LINE_QUALITY_FIX_ENABLE);
+
+               /* WaPipelineFlushCoherentLines:icl */
+               wa_write_or(wal,
+                           GEN8_L3SQCREG4,
+                           GEN8_LQSC_FLUSH_COHERENT_LINES);
+
+               /*
+                * Wa_1405543622:icl
+                * Formerly known as WaGAPZPriorityScheme
+                */
+               wa_write_or(wal,
+                           GEN8_GARBCNTL,
+                           GEN11_ARBITRATION_PRIO_ORDER_MASK);
+
+               /*
+                * Wa_1604223664:icl
+                * Formerly known as WaL3BankAddressHashing
+                */
+               wa_write_masked_or(wal,
+                                  GEN8_GARBCNTL,
+                                  GEN11_HASH_CTRL_EXCL_MASK,
+                                  GEN11_HASH_CTRL_EXCL_BIT0);
+               wa_write_masked_or(wal,
+                                  GEN11_GLBLINVL,
+                                  GEN11_BANK_HASH_ADDR_EXCL_MASK,
+                                  GEN11_BANK_HASH_ADDR_EXCL_BIT0);
+
+               /*
+                * Wa_1405733216:icl
+                * Formerly known as WaDisableCleanEvicts
+                */
+               wa_write_or(wal,
+                           GEN8_L3SQCREG4,
+                           GEN11_LQSC_CLEAN_EVICT_DISABLE);
+
+               /* Wa_1604302699:icl */
+               wa_write_or(wal,
+                           GEN10_L3_CHICKEN_MODE_REGISTER,
+                           GEN11_I2M_WRITE_DISABLE);
+
+               /* WaForwardProgressSoftReset:icl */
+               wa_write_or(wal,
+                           GEN10_SCRATCH_LNCF2,
+                           PMFLUSHDONE_LNICRSDROP |
+                           PMFLUSH_GAPL3UNBLOCK |
+                           PMFLUSHDONE_LNEBLK);
+       }
+
+       if (IS_GEN9(i915) || IS_CANNONLAKE(i915)) {
+               /* WaEnablePreemptionGranularityControlByUMD:skl,bxt,kbl,cfl,cnl */
+               wa_masked_en(wal,
+                            GEN7_FF_SLICE_CS_CHICKEN1,
+                            GEN9_FFSC_PERCTX_PREEMPT_CTRL);
+       }
+
+       if (IS_SKYLAKE(i915) || IS_KABYLAKE(i915) || IS_COFFEELAKE(i915)) {
+               /* WaEnableGapsTsvCreditFix:skl,kbl,cfl */
+               wa_write_or(wal,
+                           GEN8_GARBCNTL,
+                           GEN9_GAPS_TSV_CREDIT_DISABLE);
+       }
+
+       if (IS_BROXTON(i915)) {
+               /* WaDisablePooledEuLoadBalancingFix:bxt */
+               wa_masked_en(wal,
+                            FF_SLICE_CS_CHICKEN2,
+                            GEN9_POOLED_EU_LOAD_BALANCING_FIX_DISABLE);
+       }
+
+       if (IS_GEN9(i915)) {
+               /* WaContextSwitchWithConcurrentTLBInvalidate:skl,bxt,kbl,glk,cfl */
+               wa_masked_en(wal,
+                            GEN9_CSFE_CHICKEN1_RCS,
+                            GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE);
+
+               /* WaEnableLbsSlaRetryTimerDecrement:skl,bxt,kbl,glk,cfl */
+               wa_write_or(wal,
+                           BDW_SCRATCH1,
+                           GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE);
+
+               /* WaProgramL3SqcReg1DefaultForPerf:bxt,glk */
+               if (IS_GEN9_LP(i915))
+                       wa_write_masked_or(wal,
+                                          GEN8_L3SQCREG1,
+                                          L3_PRIO_CREDITS_MASK,
+                                          L3_GENERAL_PRIO_CREDITS(62) |
+                                          L3_HIGH_PRIO_CREDITS(2));
+
+               /* WaOCLCoherentLineFlush:skl,bxt,kbl,cfl */
+               wa_write_or(wal,
+                           GEN8_L3SQCREG4,
+                           GEN8_LQSC_FLUSH_COHERENT_LINES);
+       }
+}
+
+static void xcs_engine_wa_init(struct intel_engine_cs *engine)
+{
+       struct drm_i915_private *i915 = engine->i915;
+       struct i915_wa_list *wal = &engine->wa_list;
+
+       /* WaKBLVECSSemaphoreWaitPoll:kbl */
+       if (IS_KBL_REVID(i915, KBL_REVID_A0, KBL_REVID_E0)) {
+               wa_write(wal,
+                        RING_SEMA_WAIT_POLL(engine->mmio_base),
+                        1);
+       }
+}
+
+void intel_engine_init_workarounds(struct intel_engine_cs *engine)
+{
+       struct i915_wa_list *wal = &engine->wa_list;
+
+       if (GEM_WARN_ON(INTEL_GEN(engine->i915) < 8))
+               return;
+
+       wa_init_start(wal, engine->name);
+
+       if (engine->id == RCS)
+               rcs_engine_wa_init(engine);
+       else
+               xcs_engine_wa_init(engine);
+
+       wa_init_finish(wal);
+}
+
+void intel_engine_apply_workarounds(struct intel_engine_cs *engine)
+{
+       wa_list_apply(engine->i915, &engine->wa_list);
+}
+
 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
 #include "selftests/intel_workarounds.c"
 #endif
index b11d062..979695a 100644 (file)
@@ -7,11 +7,35 @@
 #ifndef _I915_WORKAROUNDS_H_
 #define _I915_WORKAROUNDS_H_
 
+#include <linux/slab.h>
+
+struct i915_wa {
+       i915_reg_t        reg;
+       u32               mask;
+       u32               val;
+};
+
+struct i915_wa_list {
+       const char      *name;
+       struct i915_wa  *list;
+       unsigned int    count;
+};
+
+static inline void intel_wa_list_free(struct i915_wa_list *wal)
+{
+       kfree(wal->list);
+       memset(wal, 0, sizeof(*wal));
+}
+
 int intel_ctx_workarounds_init(struct drm_i915_private *dev_priv);
 int intel_ctx_workarounds_emit(struct i915_request *rq);
 
-void intel_gt_workarounds_apply(struct drm_i915_private *dev_priv);
+void intel_gt_init_workarounds(struct drm_i915_private *dev_priv);
+void intel_gt_apply_workarounds(struct drm_i915_private *dev_priv);
 
 void intel_whitelist_workarounds_apply(struct intel_engine_cs *engine);
 
+void intel_engine_init_workarounds(struct intel_engine_cs *engine);
+void intel_engine_apply_workarounds(struct intel_engine_cs *engine);
+
 #endif
index 66df1b1..27b507e 100644 (file)
@@ -818,10 +818,13 @@ static int mtk_dsi_create_conn_enc(struct drm_device *drm, struct mtk_dsi *dsi)
        dsi->encoder.possible_crtcs = 1;
 
        /* If there's a bridge, attach to it and let it create the connector */
-       ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL);
-       if (ret) {
-               DRM_ERROR("Failed to attach bridge to drm\n");
-
+       if (dsi->bridge) {
+               ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL);
+               if (ret) {
+                       DRM_ERROR("Failed to attach bridge to drm\n");
+                       goto err_encoder_cleanup;
+               }
+       } else {
                /* Otherwise create our own connector and attach to a panel */
                ret = mtk_dsi_create_connector(drm, dsi);
                if (ret)
index 0552020..191b314 100644 (file)
@@ -45,6 +45,7 @@ struct meson_crtc {
        struct drm_crtc base;
        struct drm_pending_vblank_event *event;
        struct meson_drm *priv;
+       bool enabled;
 };
 #define to_meson_crtc(x) container_of(x, struct meson_crtc, base)
 
@@ -80,8 +81,7 @@ static const struct drm_crtc_funcs meson_crtc_funcs = {
 
 };
 
-static void meson_crtc_atomic_enable(struct drm_crtc *crtc,
-                                    struct drm_crtc_state *old_state)
+static void meson_crtc_enable(struct drm_crtc *crtc)
 {
        struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
        struct drm_crtc_state *crtc_state = crtc->state;
@@ -101,6 +101,22 @@ static void meson_crtc_atomic_enable(struct drm_crtc *crtc,
        writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE,
                            priv->io_base + _REG(VPP_MISC));
 
+       drm_crtc_vblank_on(crtc);
+
+       meson_crtc->enabled = true;
+}
+
+static void meson_crtc_atomic_enable(struct drm_crtc *crtc,
+                                    struct drm_crtc_state *old_state)
+{
+       struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
+       struct meson_drm *priv = meson_crtc->priv;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       if (!meson_crtc->enabled)
+               meson_crtc_enable(crtc);
+
        priv->viu.osd1_enabled = true;
 }
 
@@ -110,6 +126,8 @@ static void meson_crtc_atomic_disable(struct drm_crtc *crtc,
        struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
        struct meson_drm *priv = meson_crtc->priv;
 
+       drm_crtc_vblank_off(crtc);
+
        priv->viu.osd1_enabled = false;
        priv->viu.osd1_commit = false;
 
@@ -124,6 +142,8 @@ static void meson_crtc_atomic_disable(struct drm_crtc *crtc,
 
                crtc->state->event = NULL;
        }
+
+       meson_crtc->enabled = false;
 }
 
 static void meson_crtc_atomic_begin(struct drm_crtc *crtc,
@@ -132,6 +152,9 @@ static void meson_crtc_atomic_begin(struct drm_crtc *crtc,
        struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
        unsigned long flags;
 
+       if (crtc->state->enable && !meson_crtc->enabled)
+               meson_crtc_enable(crtc);
+
        if (crtc->state->event) {
                WARN_ON(drm_crtc_vblank_get(crtc) != 0);
 
index df7247c..2cb2ad2 100644 (file)
@@ -706,6 +706,7 @@ static const struct regmap_config meson_dw_hdmi_regmap_config = {
        .reg_read = meson_dw_hdmi_reg_read,
        .reg_write = meson_dw_hdmi_reg_write,
        .max_register = 0x10000,
+       .fast_io = true,
 };
 
 static bool meson_hdmi_connector_is_available(struct device *dev)
index 514245e..be76f3d 100644 (file)
@@ -71,6 +71,7 @@
  */
 
 /* HHI Registers */
+#define HHI_GCLK_MPEG2         0x148 /* 0x52 offset in data sheet */
 #define HHI_VDAC_CNTL0         0x2F4 /* 0xbd offset in data sheet */
 #define HHI_VDAC_CNTL1         0x2F8 /* 0xbe offset in data sheet */
 #define HHI_HDMI_PHY_CNTL0     0x3a0 /* 0xe8 offset in data sheet */
@@ -714,6 +715,7 @@ struct meson_hdmi_venc_vic_mode {
        { 5, &meson_hdmi_encp_mode_1080i60 },
        { 20, &meson_hdmi_encp_mode_1080i50 },
        { 32, &meson_hdmi_encp_mode_1080p24 },
+       { 33, &meson_hdmi_encp_mode_1080p50 },
        { 34, &meson_hdmi_encp_mode_1080p30 },
        { 31, &meson_hdmi_encp_mode_1080p50 },
        { 16, &meson_hdmi_encp_mode_1080p60 },
@@ -854,6 +856,13 @@ void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
        unsigned int sof_lines;
        unsigned int vsync_lines;
 
+       /* Use VENCI for 480i and 576i and double HDMI pixels */
+       if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
+               hdmi_repeat = true;
+               use_enci = true;
+               venc_hdmi_latency = 1;
+       }
+
        if (meson_venc_hdmi_supported_vic(vic)) {
                vmode = meson_venc_hdmi_get_vic_vmode(vic);
                if (!vmode) {
@@ -865,13 +874,7 @@ void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
        } else {
                meson_venc_hdmi_get_dmt_vmode(mode, &vmode_dmt);
                vmode = &vmode_dmt;
-       }
-
-       /* Use VENCI for 480i and 576i and double HDMI pixels */
-       if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
-               hdmi_repeat = true;
-               use_enci = true;
-               venc_hdmi_latency = 1;
+               use_enci = false;
        }
 
        /* Repeat VENC pixels for 480/576i/p, 720p50/60 and 1080p50/60 */
@@ -1529,10 +1532,12 @@ unsigned int meson_venci_get_field(struct meson_drm *priv)
 void meson_venc_enable_vsync(struct meson_drm *priv)
 {
        writel_relaxed(2, priv->io_base + _REG(VENC_INTCTRL));
+       regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), BIT(25));
 }
 
 void meson_venc_disable_vsync(struct meson_drm *priv)
 {
+       regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), 0);
        writel_relaxed(0, priv->io_base + _REG(VENC_INTCTRL));
 }
 
index 6bcfa52..26a0857 100644 (file)
@@ -184,18 +184,18 @@ void meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel,
        if (lut_sel == VIU_LUT_OSD_OETF) {
                writel(0, priv->io_base + _REG(addr_port));
 
-               for (i = 0; i < 20; i++)
+               for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
                        writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
                                priv->io_base + _REG(data_port));
 
                writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16),
                        priv->io_base + _REG(data_port));
 
-               for (i = 0; i < 20; i++)
+               for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
                        writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
                                priv->io_base + _REG(data_port));
 
-               for (i = 0; i < 20; i++)
+               for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
                        writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
                                priv->io_base + _REG(data_port));
 
@@ -211,18 +211,18 @@ void meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel,
        } else if (lut_sel == VIU_LUT_OSD_EOTF) {
                writel(0, priv->io_base + _REG(addr_port));
 
-               for (i = 0; i < 20; i++)
+               for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
                        writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
                                priv->io_base + _REG(data_port));
 
                writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16),
                        priv->io_base + _REG(data_port));
 
-               for (i = 0; i < 20; i++)
+               for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
                        writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
                                priv->io_base + _REG(data_port));
 
-               for (i = 0; i < 20; i++)
+               for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
                        writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
                                priv->io_base + _REG(data_port));
 
index d4530d6..ca169f0 100644 (file)
@@ -1594,7 +1594,6 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane,
                                NULL);
 
        drm_crtc_helper_add(crtc, &dpu_crtc_helper_funcs);
-       plane->crtc = crtc;
 
        /* save user friendly CRTC name for later */
        snprintf(dpu_crtc->name, DPU_CRTC_NAME_SIZE, "crtc%u", crtc->base.id);
index 96cdf06..d31d828 100644 (file)
@@ -488,8 +488,6 @@ static void dpu_encoder_destroy(struct drm_encoder *drm_enc)
 
        drm_encoder_cleanup(drm_enc);
        mutex_destroy(&dpu_enc->enc_lock);
-
-       kfree(dpu_enc);
 }
 
 void dpu_encoder_helper_split_config(
index bfcd165..d743e7c 100644 (file)
@@ -216,7 +216,7 @@ static const struct dpu_format dpu_format_map[] = {
        INTERLEAVED_RGB_FMT(XBGR8888,
                COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
                C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
-               true, 4, 0,
+               false, 4, 0,
                DPU_FETCH_LINEAR, 1),
 
        INTERLEAVED_RGB_FMT(RGBA8888,
index 4c03f0b..41bec57 100644 (file)
@@ -39,6 +39,8 @@
 #define DSI_PIXEL_PLL_CLK              1
 #define NUM_PROVIDED_CLKS              2
 
+#define VCO_REF_CLK_RATE               19200000
+
 struct dsi_pll_regs {
        u32 pll_prop_gain_rate;
        u32 pll_lockdet_rate;
@@ -316,7 +318,7 @@ static int dsi_pll_10nm_vco_set_rate(struct clk_hw *hw, unsigned long rate,
            parent_rate);
 
        pll_10nm->vco_current_rate = rate;
-       pll_10nm->vco_ref_clk_rate = parent_rate;
+       pll_10nm->vco_ref_clk_rate = VCO_REF_CLK_RATE;
 
        dsi_pll_setup_config(pll_10nm);
 
index c79659c..adbdce3 100644 (file)
@@ -332,6 +332,12 @@ int msm_hdmi_modeset_init(struct hdmi *hdmi,
                goto fail;
        }
 
+       ret = msm_hdmi_hpd_enable(hdmi->connector);
+       if (ret < 0) {
+               DRM_DEV_ERROR(&hdmi->pdev->dev, "failed to enable HPD: %d\n", ret);
+               goto fail;
+       }
+
        encoder->bridge = hdmi->bridge;
 
        priv->bridges[priv->num_bridges++]       = hdmi->bridge;
@@ -571,7 +577,7 @@ static int msm_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
        struct drm_device *drm = dev_get_drvdata(master);
        struct msm_drm_private *priv = drm->dev_private;
-       static struct hdmi_platform_config *hdmi_cfg;
+       struct hdmi_platform_config *hdmi_cfg;
        struct hdmi *hdmi;
        struct device_node *of_node = dev->of_node;
        int i, err;
index accc9a6..5c5df6a 100644 (file)
@@ -245,6 +245,7 @@ void msm_hdmi_bridge_destroy(struct drm_bridge *bridge);
 
 void msm_hdmi_connector_irq(struct drm_connector *connector);
 struct drm_connector *msm_hdmi_connector_init(struct hdmi *hdmi);
+int msm_hdmi_hpd_enable(struct drm_connector *connector);
 
 /*
  * i2c adapter for ddc:
index e9c9a0a..30e908d 100644 (file)
@@ -167,8 +167,9 @@ static void enable_hpd_clocks(struct hdmi *hdmi, bool enable)
        }
 }
 
-static int hpd_enable(struct hdmi_connector *hdmi_connector)
+int msm_hdmi_hpd_enable(struct drm_connector *connector)
 {
+       struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
        struct hdmi *hdmi = hdmi_connector->hdmi;
        const struct hdmi_platform_config *config = hdmi->config;
        struct device *dev = &hdmi->pdev->dev;
@@ -450,7 +451,6 @@ struct drm_connector *msm_hdmi_connector_init(struct hdmi *hdmi)
 {
        struct drm_connector *connector = NULL;
        struct hdmi_connector *hdmi_connector;
-       int ret;
 
        hdmi_connector = kzalloc(sizeof(*hdmi_connector), GFP_KERNEL);
        if (!hdmi_connector)
@@ -471,12 +471,6 @@ struct drm_connector *msm_hdmi_connector_init(struct hdmi *hdmi)
        connector->interlace_allowed = 0;
        connector->doublescan_allowed = 0;
 
-       ret = hpd_enable(hdmi_connector);
-       if (ret) {
-               dev_err(&hdmi->pdev->dev, "failed to enable HPD: %d\n", ret);
-               return ERR_PTR(ret);
-       }
-
        drm_connector_attach_encoder(connector, hdmi->encoder);
 
        return connector;
index 4bcdeca..2088a20 100644 (file)
@@ -34,7 +34,12 @@ static void msm_atomic_wait_for_commit_done(struct drm_device *dev,
                if (!new_crtc_state->active)
                        continue;
 
+               if (drm_crtc_vblank_get(crtc))
+                       continue;
+
                kms->funcs->wait_for_crtc_commit_done(kms, crtc);
+
+               drm_crtc_vblank_put(crtc);
        }
 }
 
index f0da0d3..d756436 100644 (file)
@@ -84,7 +84,7 @@ static int msm_gpu_open(struct inode *inode, struct file *file)
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
        if (ret)
-               return ret;
+               goto free_priv;
 
        pm_runtime_get_sync(&gpu->pdev->dev);
        show_priv->state = gpu->funcs->gpu_state_get(gpu);
@@ -94,13 +94,20 @@ static int msm_gpu_open(struct inode *inode, struct file *file)
 
        if (IS_ERR(show_priv->state)) {
                ret = PTR_ERR(show_priv->state);
-               kfree(show_priv);
-               return ret;
+               goto free_priv;
        }
 
        show_priv->dev = dev;
 
-       return single_open(file, msm_gpu_show, show_priv);
+       ret = single_open(file, msm_gpu_show, show_priv);
+       if (ret)
+               goto free_priv;
+
+       return 0;
+
+free_priv:
+       kfree(show_priv);
+       return ret;
 }
 
 static const struct file_operations msm_gpu_fops = {
index 4904d0d..dcff812 100644 (file)
@@ -553,17 +553,18 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
                        kthread_run(kthread_worker_fn,
                                &priv->disp_thread[i].worker,
                                "crtc_commit:%d", priv->disp_thread[i].crtc_id);
-               ret = sched_setscheduler(priv->disp_thread[i].thread,
-                                                       SCHED_FIFO, &param);
-               if (ret)
-                       pr_warn("display thread priority update failed: %d\n",
-                                                                       ret);
-
                if (IS_ERR(priv->disp_thread[i].thread)) {
                        dev_err(dev, "failed to create crtc_commit kthread\n");
                        priv->disp_thread[i].thread = NULL;
+                       goto err_msm_uninit;
                }
 
+               ret = sched_setscheduler(priv->disp_thread[i].thread,
+                                        SCHED_FIFO, &param);
+               if (ret)
+                       dev_warn(dev, "disp_thread set priority failed: %d\n",
+                                ret);
+
                /* initialize event thread */
                priv->event_thread[i].crtc_id = priv->crtcs[i]->base.id;
                kthread_init_worker(&priv->event_thread[i].worker);
@@ -572,6 +573,12 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
                        kthread_run(kthread_worker_fn,
                                &priv->event_thread[i].worker,
                                "crtc_event:%d", priv->event_thread[i].crtc_id);
+               if (IS_ERR(priv->event_thread[i].thread)) {
+                       dev_err(dev, "failed to create crtc_event kthread\n");
+                       priv->event_thread[i].thread = NULL;
+                       goto err_msm_uninit;
+               }
+
                /**
                 * event thread should also run at same priority as disp_thread
                 * because it is handling frame_done events. A lower priority
@@ -580,34 +587,10 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
                 * failure at crtc commit level.
                 */
                ret = sched_setscheduler(priv->event_thread[i].thread,
-                                                       SCHED_FIFO, &param);
+                                        SCHED_FIFO, &param);
                if (ret)
-                       pr_warn("display event thread priority update failed: %d\n",
-                                                                       ret);
-
-               if (IS_ERR(priv->event_thread[i].thread)) {
-                       dev_err(dev, "failed to create crtc_event kthread\n");
-                       priv->event_thread[i].thread = NULL;
-               }
-
-               if ((!priv->disp_thread[i].thread) ||
-                               !priv->event_thread[i].thread) {
-                       /* clean up previously created threads if any */
-                       for ( ; i >= 0; i--) {
-                               if (priv->disp_thread[i].thread) {
-                                       kthread_stop(
-                                               priv->disp_thread[i].thread);
-                                       priv->disp_thread[i].thread = NULL;
-                               }
-
-                               if (priv->event_thread[i].thread) {
-                                       kthread_stop(
-                                               priv->event_thread[i].thread);
-                                       priv->event_thread[i].thread = NULL;
-                               }
-                       }
-                       goto err_msm_uninit;
-               }
+                       dev_warn(dev, "event_thread set priority failed:%d\n",
+                                ret);
        }
 
        ret = drm_vblank_init(ddev, priv->num_crtcs);
index 7a7923e..6942604 100644 (file)
@@ -317,6 +317,9 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob
        uint32_t *ptr;
        int ret = 0;
 
+       if (!nr_relocs)
+               return 0;
+
        if (offset % 4) {
                DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
                return -EINVAL;
@@ -410,7 +413,6 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
        struct msm_file_private *ctx = file->driver_priv;
        struct msm_gem_submit *submit;
        struct msm_gpu *gpu = priv->gpu;
-       struct dma_fence *in_fence = NULL;
        struct sync_file *sync_file = NULL;
        struct msm_gpu_submitqueue *queue;
        struct msm_ringbuffer *ring;
@@ -443,6 +445,8 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
        ring = gpu->rb[queue->prio];
 
        if (args->flags & MSM_SUBMIT_FENCE_FD_IN) {
+               struct dma_fence *in_fence;
+
                in_fence = sync_file_get_fence(args->fence_fd);
 
                if (!in_fence)
@@ -452,11 +456,13 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
                 * Wait if the fence is from a foreign context, or if the fence
                 * array contains any fence from a foreign context.
                 */
-               if (!dma_fence_match_context(in_fence, ring->fctx->context)) {
+               ret = 0;
+               if (!dma_fence_match_context(in_fence, ring->fctx->context))
                        ret = dma_fence_wait(in_fence, true);
-                       if (ret)
-                               return ret;
-               }
+
+               dma_fence_put(in_fence);
+               if (ret)
+                       return ret;
        }
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -582,8 +588,6 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
        }
 
 out:
-       if (in_fence)
-               dma_fence_put(in_fence);
        submit_cleanup(submit);
        if (ret)
                msm_gem_submit_free(submit);
index 11aac83..2b7c894 100644 (file)
@@ -345,6 +345,10 @@ static void msm_gpu_crashstate_capture(struct msm_gpu *gpu,
 {
        struct msm_gpu_state *state;
 
+       /* Check if the target supports capturing crash state */
+       if (!gpu->funcs->gpu_state_get)
+               return;
+
        /* Only save one crash state at a time */
        if (gpu->crashstate)
                return;
@@ -434,10 +438,9 @@ static void recover_worker(struct work_struct *work)
        if (submit) {
                struct task_struct *task;
 
-               rcu_read_lock();
-               task = pid_task(submit->pid, PIDTYPE_PID);
+               task = get_pid_task(submit->pid, PIDTYPE_PID);
                if (task) {
-                       comm = kstrdup(task->comm, GFP_ATOMIC);
+                       comm = kstrdup(task->comm, GFP_KERNEL);
 
                        /*
                         * So slightly annoying, in other paths like
@@ -450,10 +453,10 @@ static void recover_worker(struct work_struct *work)
                         * about the submit going away.
                         */
                        mutex_unlock(&dev->struct_mutex);
-                       cmd = kstrdup_quotable_cmdline(task, GFP_ATOMIC);
+                       cmd = kstrdup_quotable_cmdline(task, GFP_KERNEL);
+                       put_task_struct(task);
                        mutex_lock(&dev->struct_mutex);
                }
-               rcu_read_unlock();
 
                if (comm && cmd) {
                        dev_err(dev->dev, "%s: offending task: %s (%s)\n",
index b23d336..2a90aa4 100644 (file)
@@ -66,7 +66,7 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova,
 //     pm_runtime_get_sync(mmu->dev);
        ret = iommu_map_sg(iommu->domain, iova, sgt->sgl, sgt->nents, prot);
 //     pm_runtime_put_sync(mmu->dev);
-       WARN_ON(ret < 0);
+       WARN_ON(!ret);
 
        return (ret == len) ? 0 : -EINVAL;
 }
index cca9334..0c2c8d2 100644 (file)
@@ -316,10 +316,11 @@ static void snapshot_buf(struct msm_rd_state *rd,
                uint64_t iova, uint32_t size)
 {
        struct msm_gem_object *obj = submit->bos[idx].obj;
+       unsigned offset = 0;
        const char *buf;
 
        if (iova) {
-               buf += iova - submit->bos[idx].iova;
+               offset = iova - submit->bos[idx].iova;
        } else {
                iova = submit->bos[idx].iova;
                size = obj->base.size;
@@ -340,6 +341,8 @@ static void snapshot_buf(struct msm_rd_state *rd,
        if (IS_ERR(buf))
                return;
 
+       buf += offset;
+
        rd_write_section(rd, RD_BUFFER_CONTENTS, buf, size);
 
        msm_gem_put_vaddr(&obj->base);
index 6cbbae3..db1bf7f 100644 (file)
@@ -198,6 +198,22 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
 /******************************************************************************
  * EVO channel helpers
  *****************************************************************************/
+static void
+evo_flush(struct nv50_dmac *dmac)
+{
+       /* Push buffer fetches are not coherent with BAR1, we need to ensure
+        * writes have been flushed right through to VRAM before writing PUT.
+        */
+       if (dmac->push.type & NVIF_MEM_VRAM) {
+               struct nvif_device *device = dmac->base.device;
+               nvif_wr32(&device->object, 0x070000, 0x00000001);
+               nvif_msec(device, 2000,
+                       if (!(nvif_rd32(&device->object, 0x070000) & 0x00000002))
+                               break;
+               );
+       }
+}
+
 u32 *
 evo_wait(struct nv50_dmac *evoc, int nr)
 {
@@ -208,6 +224,7 @@ evo_wait(struct nv50_dmac *evoc, int nr)
        mutex_lock(&dmac->lock);
        if (put + nr >= (PAGE_SIZE / 4) - 8) {
                dmac->ptr[put] = 0x20000000;
+               evo_flush(dmac);
 
                nvif_wr32(&dmac->base.user, 0x0000, 0x00000000);
                if (nvif_msec(device, 2000,
@@ -230,17 +247,7 @@ evo_kick(u32 *push, struct nv50_dmac *evoc)
 {
        struct nv50_dmac *dmac = evoc;
 
-       /* Push buffer fetches are not coherent with BAR1, we need to ensure
-        * writes have been flushed right through to VRAM before writing PUT.
-        */
-       if (dmac->push.type & NVIF_MEM_VRAM) {
-               struct nvif_device *device = dmac->base.device;
-               nvif_wr32(&device->object, 0x070000, 0x00000001);
-               nvif_msec(device, 2000,
-                       if (!(nvif_rd32(&device->object, 0x070000) & 0x00000002))
-                               break;
-               );
-       }
+       evo_flush(dmac);
 
        nvif_wr32(&dmac->base.user, 0x0000, (push - dmac->ptr) << 2);
        mutex_unlock(&dmac->lock);
@@ -1264,6 +1271,7 @@ nv50_mstm_del(struct nv50_mstm **pmstm)
 {
        struct nv50_mstm *mstm = *pmstm;
        if (mstm) {
+               drm_dp_mst_topology_mgr_destroy(&mstm->mgr);
                kfree(*pmstm);
                *pmstm = NULL;
        }
index 2b2baf6..d2928d4 100644 (file)
@@ -1171,10 +1171,16 @@ nouveau_platform_device_create(const struct nvkm_device_tegra_func *func,
                goto err_free;
        }
 
+       err = nouveau_drm_device_init(drm);
+       if (err)
+               goto err_put;
+
        platform_set_drvdata(pdev, drm);
 
        return drm;
 
+err_put:
+       drm_dev_put(drm);
 err_free:
        nvkm_device_del(pdevice);
 
index 1f8161b..4651208 100644 (file)
@@ -177,6 +177,7 @@ static int panel_dpi_probe(struct platform_device *pdev)
        dssdev->type = OMAP_DISPLAY_TYPE_DPI;
        dssdev->owner = THIS_MODULE;
        dssdev->of_ports = BIT(0);
+       drm_bus_flags_from_videomode(&ddata->vm, &dssdev->bus_flags);
 
        omapdss_display_init(dssdev);
        omapdss_device_register(dssdev);
index 394c129..00a9c2a 100644 (file)
@@ -5409,15 +5409,24 @@ static int dsi_probe(struct platform_device *pdev)
 
        /* DSI on OMAP3 doesn't have register DSI_GNQ, set number
         * of data to 3 by default */
-       if (dsi->data->quirks & DSI_QUIRK_GNQ)
+       if (dsi->data->quirks & DSI_QUIRK_GNQ) {
+               dsi_runtime_get(dsi);
                /* NB_DATA_LANES */
                dsi->num_lanes_supported = 1 + REG_GET(dsi, DSI_GNQ, 11, 9);
-       else
+               dsi_runtime_put(dsi);
+       } else {
                dsi->num_lanes_supported = 3;
+       }
+
+       r = of_platform_populate(dev->of_node, NULL, NULL, dev);
+       if (r) {
+               DSSERR("Failed to populate DSI child devices: %d\n", r);
+               goto err_pm_disable;
+       }
 
        r = dsi_init_output(dsi);
        if (r)
-               goto err_pm_disable;
+               goto err_of_depopulate;
 
        r = dsi_probe_of(dsi);
        if (r) {
@@ -5425,10 +5434,6 @@ static int dsi_probe(struct platform_device *pdev)
                goto err_uninit_output;
        }
 
-       r = of_platform_populate(dev->of_node, NULL, NULL, dev);
-       if (r)
-               DSSERR("Failed to populate DSI child devices: %d\n", r);
-
        r = component_add(&pdev->dev, &dsi_component_ops);
        if (r)
                goto err_uninit_output;
@@ -5437,6 +5442,8 @@ static int dsi_probe(struct platform_device *pdev)
 
 err_uninit_output:
        dsi_uninit_output(dsi);
+err_of_depopulate:
+       of_platform_depopulate(dev);
 err_pm_disable:
        pm_runtime_disable(dev);
        return r;
@@ -5470,19 +5477,12 @@ static int dsi_runtime_suspend(struct device *dev)
        /* wait for current handler to finish before turning the DSI off */
        synchronize_irq(dsi->irq);
 
-       dispc_runtime_put(dsi->dss->dispc);
-
        return 0;
 }
 
 static int dsi_runtime_resume(struct device *dev)
 {
        struct dsi_data *dsi = dev_get_drvdata(dev);
-       int r;
-
-       r = dispc_runtime_get(dsi->dss->dispc);
-       if (r)
-               return r;
 
        dsi->is_enabled = true;
        /* ensure the irq handler sees the is_enabled value */
index 1aaf260..7553c7f 100644 (file)
@@ -1484,16 +1484,23 @@ static int dss_probe(struct platform_device *pdev)
                                                   dss);
 
        /* Add all the child devices as components. */
+       r = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+       if (r)
+               goto err_uninit_debugfs;
+
        omapdss_gather_components(&pdev->dev);
 
        device_for_each_child(&pdev->dev, &match, dss_add_child_component);
 
        r = component_master_add_with_match(&pdev->dev, &dss_component_ops, match);
        if (r)
-               goto err_uninit_debugfs;
+               goto err_of_depopulate;
 
        return 0;
 
+err_of_depopulate:
+       of_platform_depopulate(&pdev->dev);
+
 err_uninit_debugfs:
        dss_debugfs_remove_file(dss->debugfs.clk);
        dss_debugfs_remove_file(dss->debugfs.dss);
@@ -1522,6 +1529,8 @@ static int dss_remove(struct platform_device *pdev)
 {
        struct dss_device *dss = platform_get_drvdata(pdev);
 
+       of_platform_depopulate(&pdev->dev);
+
        component_master_del(&pdev->dev, &dss_component_ops);
 
        dss_debugfs_remove_file(dss->debugfs.clk);
index cf6230e..aabdda3 100644 (file)
@@ -635,10 +635,14 @@ static int hdmi4_bind(struct device *dev, struct device *master, void *data)
 
        hdmi->dss = dss;
 
-       r = hdmi_pll_init(dss, hdmi->pdev, &hdmi->pll, &hdmi->wp);
+       r = hdmi_runtime_get(hdmi);
        if (r)
                return r;
 
+       r = hdmi_pll_init(dss, hdmi->pdev, &hdmi->pll, &hdmi->wp);
+       if (r)
+               goto err_runtime_put;
+
        r = hdmi4_cec_init(hdmi->pdev, &hdmi->core, &hdmi->wp);
        if (r)
                goto err_pll_uninit;
@@ -652,12 +656,16 @@ static int hdmi4_bind(struct device *dev, struct device *master, void *data)
        hdmi->debugfs = dss_debugfs_create_file(dss, "hdmi", hdmi_dump_regs,
                                               hdmi);
 
+       hdmi_runtime_put(hdmi);
+
        return 0;
 
 err_cec_uninit:
        hdmi4_cec_uninit(&hdmi->core);
 err_pll_uninit:
        hdmi_pll_uninit(&hdmi->pll);
+err_runtime_put:
+       hdmi_runtime_put(hdmi);
        return r;
 }
 
@@ -833,32 +841,6 @@ static int hdmi4_remove(struct platform_device *pdev)
        return 0;
 }
 
-static int hdmi_runtime_suspend(struct device *dev)
-{
-       struct omap_hdmi *hdmi = dev_get_drvdata(dev);
-
-       dispc_runtime_put(hdmi->dss->dispc);
-
-       return 0;
-}
-
-static int hdmi_runtime_resume(struct device *dev)
-{
-       struct omap_hdmi *hdmi = dev_get_drvdata(dev);
-       int r;
-
-       r = dispc_runtime_get(hdmi->dss->dispc);
-       if (r < 0)
-               return r;
-
-       return 0;
-}
-
-static const struct dev_pm_ops hdmi_pm_ops = {
-       .runtime_suspend = hdmi_runtime_suspend,
-       .runtime_resume = hdmi_runtime_resume,
-};
-
 static const struct of_device_id hdmi_of_match[] = {
        { .compatible = "ti,omap4-hdmi", },
        {},
@@ -869,7 +851,6 @@ struct platform_driver omapdss_hdmi4hw_driver = {
        .remove         = hdmi4_remove,
        .driver         = {
                .name   = "omapdss_hdmi",
-               .pm     = &hdmi_pm_ops,
                .of_match_table = hdmi_of_match,
                .suppress_bind_attrs = true,
        },
index b0e4a74..9e8556f 100644 (file)
@@ -825,32 +825,6 @@ static int hdmi5_remove(struct platform_device *pdev)
        return 0;
 }
 
-static int hdmi_runtime_suspend(struct device *dev)
-{
-       struct omap_hdmi *hdmi = dev_get_drvdata(dev);
-
-       dispc_runtime_put(hdmi->dss->dispc);
-
-       return 0;
-}
-
-static int hdmi_runtime_resume(struct device *dev)
-{
-       struct omap_hdmi *hdmi = dev_get_drvdata(dev);
-       int r;
-
-       r = dispc_runtime_get(hdmi->dss->dispc);
-       if (r < 0)
-               return r;
-
-       return 0;
-}
-
-static const struct dev_pm_ops hdmi_pm_ops = {
-       .runtime_suspend = hdmi_runtime_suspend,
-       .runtime_resume = hdmi_runtime_resume,
-};
-
 static const struct of_device_id hdmi_of_match[] = {
        { .compatible = "ti,omap5-hdmi", },
        { .compatible = "ti,dra7-hdmi", },
@@ -862,7 +836,6 @@ struct platform_driver omapdss_hdmi5hw_driver = {
        .remove         = hdmi5_remove,
        .driver         = {
                .name   = "omapdss_hdmi5",
-               .pm     = &hdmi_pm_ops,
                .of_match_table = hdmi_of_match,
                .suppress_bind_attrs = true,
        },
index 1f698a9..33e15cb 100644 (file)
@@ -432,7 +432,7 @@ struct omap_dss_device {
        const struct omap_dss_driver *driver;
        const struct omap_dss_device_ops *ops;
        unsigned long ops_flags;
-       unsigned long bus_flags;
+       u32 bus_flags;
 
        /* helper variable for driver suspend/resume */
        bool activate_after_resume;
index ff0b18c..b5f5272 100644 (file)
@@ -946,19 +946,12 @@ static int venc_runtime_suspend(struct device *dev)
        if (venc->tv_dac_clk)
                clk_disable_unprepare(venc->tv_dac_clk);
 
-       dispc_runtime_put(venc->dss->dispc);
-
        return 0;
 }
 
 static int venc_runtime_resume(struct device *dev)
 {
        struct venc_device *venc = dev_get_drvdata(dev);
-       int r;
-
-       r = dispc_runtime_get(venc->dss->dispc);
-       if (r < 0)
-               return r;
 
        if (venc->tv_dac_clk)
                clk_prepare_enable(venc->tv_dac_clk);
index 62928ec..caffc54 100644 (file)
@@ -350,11 +350,14 @@ static void omap_crtc_arm_event(struct drm_crtc *crtc)
 static void omap_crtc_atomic_enable(struct drm_crtc *crtc,
                                    struct drm_crtc_state *old_state)
 {
+       struct omap_drm_private *priv = crtc->dev->dev_private;
        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
        int ret;
 
        DBG("%s", omap_crtc->name);
 
+       priv->dispc_ops->runtime_get(priv->dispc);
+
        spin_lock_irq(&crtc->dev->event_lock);
        drm_crtc_vblank_on(crtc);
        ret = drm_crtc_vblank_get(crtc);
@@ -367,6 +370,7 @@ static void omap_crtc_atomic_enable(struct drm_crtc *crtc,
 static void omap_crtc_atomic_disable(struct drm_crtc *crtc,
                                     struct drm_crtc_state *old_state)
 {
+       struct omap_drm_private *priv = crtc->dev->dev_private;
        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 
        DBG("%s", omap_crtc->name);
@@ -379,6 +383,8 @@ static void omap_crtc_atomic_disable(struct drm_crtc *crtc,
        spin_unlock_irq(&crtc->dev->event_lock);
 
        drm_crtc_vblank_off(crtc);
+
+       priv->dispc_ops->runtime_put(priv->dispc);
 }
 
 static enum drm_mode_status omap_crtc_mode_valid(struct drm_crtc *crtc,
index 452e625..933ebc9 100644 (file)
@@ -52,17 +52,44 @@ static const struct drm_encoder_funcs omap_encoder_funcs = {
        .destroy = omap_encoder_destroy,
 };
 
+static void omap_encoder_hdmi_mode_set(struct drm_encoder *encoder,
+                                      struct drm_display_mode *adjusted_mode)
+{
+       struct drm_device *dev = encoder->dev;
+       struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+       struct omap_dss_device *dssdev = omap_encoder->output;
+       struct drm_connector *connector;
+       bool hdmi_mode;
+
+       hdmi_mode = false;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (connector->encoder == encoder) {
+                       hdmi_mode = omap_connector_get_hdmi_mode(connector);
+                       break;
+               }
+       }
+
+       if (dssdev->ops->hdmi.set_hdmi_mode)
+               dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode);
+
+       if (hdmi_mode && dssdev->ops->hdmi.set_infoframe) {
+               struct hdmi_avi_infoframe avi;
+               int r;
+
+               r = drm_hdmi_avi_infoframe_from_display_mode(&avi, adjusted_mode,
+                                                            false);
+               if (r == 0)
+                       dssdev->ops->hdmi.set_infoframe(dssdev, &avi);
+       }
+}
+
 static void omap_encoder_mode_set(struct drm_encoder *encoder,
                                  struct drm_display_mode *mode,
                                  struct drm_display_mode *adjusted_mode)
 {
-       struct drm_device *dev = encoder->dev;
        struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-       struct drm_connector *connector;
        struct omap_dss_device *dssdev;
        struct videomode vm = { 0 };
-       bool hdmi_mode;
-       int r;
 
        drm_display_mode_to_videomode(adjusted_mode, &vm);
 
@@ -112,27 +139,8 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
        }
 
        /* Set the HDMI mode and HDMI infoframe if applicable. */
-       hdmi_mode = false;
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               if (connector->encoder == encoder) {
-                       hdmi_mode = omap_connector_get_hdmi_mode(connector);
-                       break;
-               }
-       }
-
-       dssdev = omap_encoder->output;
-
-       if (dssdev->ops->hdmi.set_hdmi_mode)
-               dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode);
-
-       if (hdmi_mode && dssdev->ops->hdmi.set_infoframe) {
-               struct hdmi_avi_infoframe avi;
-
-               r = drm_hdmi_avi_infoframe_from_display_mode(&avi, adjusted_mode,
-                                                            false);
-               if (r == 0)
-                       dssdev->ops->hdmi.set_infoframe(dssdev, &avi);
-       }
+       if (omap_encoder->output->output_type == OMAP_DISPLAY_TYPE_HDMI)
+               omap_encoder_hdmi_mode_set(encoder, adjusted_mode);
 }
 
 static void omap_encoder_disable(struct drm_encoder *encoder)
index d85f0a1..cebf313 100644 (file)
@@ -202,10 +202,25 @@ void rcar_du_group_put(struct rcar_du_group *rgrp)
 
 static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
 {
-       struct rcar_du_crtc *rcrtc = &rgrp->dev->crtcs[rgrp->index * 2];
+       struct rcar_du_device *rcdu = rgrp->dev;
+
+       /*
+        * Group start/stop is controlled by the DRES and DEN bits of DSYSR0
+        * for the first group and DSYSR2 for the second group. On most DU
+        * instances, this maps to the first CRTC of the group, and we can just
+        * use rcar_du_crtc_dsysr_clr_set() to access the correct DSYSR. On
+        * M3-N, however, DU2 doesn't exist, but DSYSR2 does. We thus need to
+        * access the register directly using group read/write.
+        */
+       if (rcdu->info->channels_mask & BIT(rgrp->index * 2)) {
+               struct rcar_du_crtc *rcrtc = &rgrp->dev->crtcs[rgrp->index * 2];
 
-       rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_DRES | DSYSR_DEN,
-                                  start ? DSYSR_DEN : DSYSR_DRES);
+               rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_DRES | DSYSR_DEN,
+                                          start ? DSYSR_DEN : DSYSR_DRES);
+       } else {
+               rcar_du_group_write(rgrp, DSYSR,
+                                   start ? DSYSR_DEN : DSYSR_DRES);
+       }
 }
 
 void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
index 941f352..5864cb4 100644 (file)
@@ -448,11 +448,6 @@ static int rockchip_drm_platform_remove(struct platform_device *pdev)
        return 0;
 }
 
-static void rockchip_drm_platform_shutdown(struct platform_device *pdev)
-{
-       rockchip_drm_platform_remove(pdev);
-}
-
 static const struct of_device_id rockchip_drm_dt_ids[] = {
        { .compatible = "rockchip,display-subsystem", },
        { /* sentinel */ },
@@ -462,7 +457,6 @@ MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
 static struct platform_driver rockchip_drm_platform_driver = {
        .probe = rockchip_drm_platform_probe,
        .remove = rockchip_drm_platform_remove,
-       .shutdown = rockchip_drm_platform_shutdown,
        .driver = {
                .name = "rockchip-drm",
                .of_match_table = rockchip_drm_dt_ids,
index ba80150..895d77d 100644 (file)
@@ -492,8 +492,10 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
        if (!fbo)
                return -ENOMEM;
 
-       ttm_bo_get(bo);
        fbo->base = *bo;
+       fbo->base.mem.placement |= TTM_PL_FLAG_NO_EVICT;
+
+       ttm_bo_get(bo);
        fbo->bo = bo;
 
        /**
index 1274687..1f94b9a 100644 (file)
@@ -214,6 +214,12 @@ static int vc4_atomic_commit(struct drm_device *dev,
                return 0;
        }
 
+       /* We know for sure we don't want an async update here. Set
+        * state->legacy_cursor_update to false to prevent
+        * drm_atomic_helper_setup_commit() from auto-completing
+        * commit->flip_done.
+        */
+       state->legacy_cursor_update = false;
        ret = drm_atomic_helper_setup_commit(state, nonblock);
        if (ret)
                return ret;
index 9dc3fcb..c6635f2 100644 (file)
@@ -807,7 +807,7 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
 static void vc4_plane_atomic_async_update(struct drm_plane *plane,
                                          struct drm_plane_state *state)
 {
-       struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
+       struct vc4_plane_state *vc4_state, *new_vc4_state;
 
        if (plane->state->fb != state->fb) {
                vc4_plane_async_set_fb(plane, state->fb);
@@ -828,7 +828,18 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane,
        plane->state->src_y = state->src_y;
 
        /* Update the display list based on the new crtc_x/y. */
-       vc4_plane_atomic_check(plane, plane->state);
+       vc4_plane_atomic_check(plane, state);
+
+       new_vc4_state = to_vc4_plane_state(state);
+       vc4_state = to_vc4_plane_state(plane->state);
+
+       /* Update the current vc4_state pos0, pos2 and ptr0 dlist entries. */
+       vc4_state->dlist[vc4_state->pos0_offset] =
+               new_vc4_state->dlist[vc4_state->pos0_offset];
+       vc4_state->dlist[vc4_state->pos2_offset] =
+               new_vc4_state->dlist[vc4_state->pos2_offset];
+       vc4_state->dlist[vc4_state->ptr0_offset] =
+               new_vc4_state->dlist[vc4_state->ptr0_offset];
 
        /* Note that we can't just call vc4_plane_write_dlist()
         * because that would smash the context data that the HVS is
index 61a84b9..d7a2dfb 100644 (file)
@@ -49,6 +49,8 @@
 
 #define VMWGFX_REPO "In Tree"
 
+#define VMWGFX_VALIDATION_MEM_GRAN (16*PAGE_SIZE)
+
 
 /**
  * Fully encoded drm commands. Might move to vmw_drm.h
@@ -918,7 +920,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
                spin_unlock(&dev_priv->cap_lock);
        }
 
-
+       vmw_validation_mem_init_ttm(dev_priv, VMWGFX_VALIDATION_MEM_GRAN);
        ret = vmw_kms_init(dev_priv);
        if (unlikely(ret != 0))
                goto out_no_kms;
index 59f6142..aca974b 100644 (file)
@@ -606,6 +606,9 @@ struct vmw_private {
 
        struct vmw_cmdbuf_man *cman;
        DECLARE_BITMAP(irqthread_pending, VMW_IRQTHREAD_MAX);
+
+       /* Validation memory reservation */
+       struct vmw_validation_mem vvm;
 };
 
 static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res)
@@ -846,6 +849,8 @@ extern int vmw_ttm_global_init(struct vmw_private *dev_priv);
 extern void vmw_ttm_global_release(struct vmw_private *dev_priv);
 extern int vmw_mmap(struct file *filp, struct vm_area_struct *vma);
 
+extern void vmw_validation_mem_init_ttm(struct vmw_private *dev_priv,
+                                       size_t gran);
 /**
  * TTM buffer object driver - vmwgfx_ttm_buffer.c
  */
index 5a6b70b..f2d13a7 100644 (file)
@@ -1738,7 +1738,6 @@ static int vmw_cmd_check_define_gmrfb(struct vmw_private *dev_priv,
                                      void *buf)
 {
        struct vmw_buffer_object *vmw_bo;
-       int ret;
 
        struct {
                uint32_t header;
@@ -1748,7 +1747,6 @@ static int vmw_cmd_check_define_gmrfb(struct vmw_private *dev_priv,
        return vmw_translate_guest_ptr(dev_priv, sw_context,
                                       &cmd->body.ptr,
                                       &vmw_bo);
-       return ret;
 }
 
 
@@ -3837,6 +3835,8 @@ int vmw_execbuf_process(struct drm_file *file_priv,
        struct sync_file *sync_file = NULL;
        DECLARE_VAL_CONTEXT(val_ctx, &sw_context->res_ht, 1);
 
+       vmw_validation_set_val_mem(&val_ctx, &dev_priv->vvm);
+
        if (flags & DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD) {
                out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
                if (out_fence_fd < 0) {
index 7b1e5a5..f882470 100644 (file)
@@ -96,3 +96,39 @@ void vmw_ttm_global_release(struct vmw_private *dev_priv)
        drm_global_item_unref(&dev_priv->bo_global_ref.ref);
        drm_global_item_unref(&dev_priv->mem_global_ref);
 }
+
+/* struct vmw_validation_mem callback */
+static int vmw_vmt_reserve(struct vmw_validation_mem *m, size_t size)
+{
+       static struct ttm_operation_ctx ctx = {.interruptible = false,
+                                              .no_wait_gpu = false};
+       struct vmw_private *dev_priv = container_of(m, struct vmw_private, vvm);
+
+       return ttm_mem_global_alloc(vmw_mem_glob(dev_priv), size, &ctx);
+}
+
+/* struct vmw_validation_mem callback */
+static void vmw_vmt_unreserve(struct vmw_validation_mem *m, size_t size)
+{
+       struct vmw_private *dev_priv = container_of(m, struct vmw_private, vvm);
+
+       return ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
+}
+
+/**
+ * vmw_validation_mem_init_ttm - Interface the validation memory tracker
+ * to ttm.
+ * @dev_priv: Pointer to struct vmw_private. The reason we choose a vmw private
+ * rather than a struct vmw_validation_mem is to make sure assumption in the
+ * callbacks that struct vmw_private derives from struct vmw_validation_mem
+ * holds true.
+ * @gran: The recommended allocation granularity
+ */
+void vmw_validation_mem_init_ttm(struct vmw_private *dev_priv, size_t gran)
+{
+       struct vmw_validation_mem *vvm = &dev_priv->vvm;
+
+       vvm->reserve_mem = vmw_vmt_reserve;
+       vvm->unreserve_mem = vmw_vmt_unreserve;
+       vvm->gran = gran;
+}
index 184025f..f116f09 100644 (file)
@@ -104,11 +104,25 @@ void *vmw_validation_mem_alloc(struct vmw_validation_context *ctx,
                return NULL;
 
        if (ctx->mem_size_left < size) {
-               struct page *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+               struct page *page;
 
+               if (ctx->vm && ctx->vm_size_left < PAGE_SIZE) {
+                       int ret = ctx->vm->reserve_mem(ctx->vm, ctx->vm->gran);
+
+                       if (ret)
+                               return NULL;
+
+                       ctx->vm_size_left += ctx->vm->gran;
+                       ctx->total_mem += ctx->vm->gran;
+               }
+
+               page = alloc_page(GFP_KERNEL | __GFP_ZERO);
                if (!page)
                        return NULL;
 
+               if (ctx->vm)
+                       ctx->vm_size_left -= PAGE_SIZE;
+
                list_add_tail(&page->lru, &ctx->page_list);
                ctx->page_address = page_address(page);
                ctx->mem_size_left = PAGE_SIZE;
@@ -138,6 +152,11 @@ static void vmw_validation_mem_free(struct vmw_validation_context *ctx)
        }
 
        ctx->mem_size_left = 0;
+       if (ctx->vm && ctx->total_mem) {
+               ctx->vm->unreserve_mem(ctx->vm, ctx->total_mem);
+               ctx->total_mem = 0;
+               ctx->vm_size_left = 0;
+       }
 }
 
 /**
index b57e329..3b396fe 100644 (file)
 #include <linux/ww_mutex.h>
 #include <drm/ttm/ttm_execbuf_util.h>
 
+/**
+ * struct vmw_validation_mem - Custom interface to provide memory reservations
+ * for the validation code.
+ * @reserve_mem: Callback to reserve memory
+ * @unreserve_mem: Callback to unreserve memory
+ * @gran: Reservation granularity. Contains a hint how much memory should
+ * be reserved in each call to @reserve_mem(). A slow implementation may want
+ * reservation to be done in large batches.
+ */
+struct vmw_validation_mem {
+       int (*reserve_mem)(struct vmw_validation_mem *m, size_t size);
+       void (*unreserve_mem)(struct vmw_validation_mem *m, size_t size);
+       size_t gran;
+};
+
 /**
  * struct vmw_validation_context - Per command submission validation context
  * @ht: Hash table used to find resource- or buffer object duplicates
  * buffer objects
  * @mem_size_left: Free memory left in the last page in @page_list
  * @page_address: Kernel virtual address of the last page in @page_list
+ * @vm: A pointer to the memory reservation interface or NULL if no
+ * memory reservation is needed.
+ * @vm_size_left: Amount of reserved memory that so far has not been allocated.
+ * @total_mem: Amount of reserved memory.
  */
 struct vmw_validation_context {
        struct drm_open_hash *ht;
@@ -59,6 +78,9 @@ struct vmw_validation_context {
        unsigned int merge_dups;
        unsigned int mem_size_left;
        u8 *page_address;
+       struct vmw_validation_mem *vm;
+       size_t vm_size_left;
+       size_t total_mem;
 };
 
 struct vmw_buffer_object;
@@ -101,6 +123,21 @@ vmw_validation_has_bos(struct vmw_validation_context *ctx)
        return !list_empty(&ctx->bo_list);
 }
 
+/**
+ * vmw_validation_set_val_mem - Register a validation mem object for
+ * validation memory reservation
+ * @ctx: The validation context
+ * @vm: Pointer to a struct vmw_validation_mem
+ *
+ * Must be set before the first attempt to allocate validation memory.
+ */
+static inline void
+vmw_validation_set_val_mem(struct vmw_validation_context *ctx,
+                          struct vmw_validation_mem *vm)
+{
+       ctx->vm = vm;
+}
+
 /**
  * vmw_validation_set_ht - Register a hash table for duplicate finding
  * @ctx: The validation context
index b372854..704049e 100644 (file)
@@ -309,7 +309,7 @@ static void mousevsc_on_receive(struct hv_device *device,
                hid_input_report(input_dev->hid_device, HID_INPUT_REPORT,
                                 input_dev->input_buf, len, 1);
 
-               pm_wakeup_event(&input_dev->device->device, 0);
+               pm_wakeup_hard_event(&input_dev->device->device);
 
                break;
        default:
index c0d6689..27519eb 100644 (file)
@@ -17,6 +17,9 @@
 #ifndef HID_IDS_H_FILE
 #define HID_IDS_H_FILE
 
+#define USB_VENDOR_ID_258A             0x258a
+#define USB_DEVICE_ID_258A_6A88                0x6a88
+
 #define USB_VENDOR_ID_3M               0x0596
 #define USB_DEVICE_ID_3M1968           0x0500
 #define USB_DEVICE_ID_3M2256           0x0502
 
 #define USB_VENDOR_ID_CIDC             0x1677
 
+#define I2C_VENDOR_ID_CIRQUE           0x0488
+#define I2C_PRODUCT_ID_CIRQUE_121F     0x121F
+
 #define USB_VENDOR_ID_CJTOUCH          0x24b8
 #define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0020 0x0020
 #define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0040 0x0040
 #define USB_VENDOR_ID_LG               0x1fd2
 #define USB_DEVICE_ID_LG_MULTITOUCH    0x0064
 #define USB_DEVICE_ID_LG_MELFAS_MT     0x6007
+#define I2C_DEVICE_ID_LG_8001          0x8001
 
 #define USB_VENDOR_ID_LOGITECH         0x046d
 #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
 #define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9
 #define USB_DEVICE_ID_MS_POWER_COVER     0x07da
 #define USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER 0x02fd
+#define USB_DEVICE_ID_MS_PIXART_MOUSE    0x00cb
 
 #define USB_VENDOR_ID_MOJO             0x8282
 #define USB_DEVICE_ID_RETRO_ADAPTER    0x3201
 #define USB_VENDOR_ID_REALTEK          0x0bda
 #define USB_DEVICE_ID_REALTEK_READER   0x0152
 
+#define USB_VENDOR_ID_RETROUSB         0xf000
+#define USB_DEVICE_ID_RETROUSB_SNES_RETROPAD   0x0003
+#define USB_DEVICE_ID_RETROUSB_SNES_RETROPORT  0x00f1
+
 #define USB_VENDOR_ID_ROCCAT           0x1e7d
 #define USB_DEVICE_ID_ROCCAT_ARVO      0x30d4
 #define USB_DEVICE_ID_ROCCAT_ISKU      0x319c
 #define USB_VENDOR_ID_SYMBOL           0x05e0
 #define USB_DEVICE_ID_SYMBOL_SCANNER_1 0x0800
 #define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300
+#define USB_DEVICE_ID_SYMBOL_SCANNER_3 0x1200
 
 #define USB_VENDOR_ID_SYNAPTICS                0x06cb
 #define USB_DEVICE_ID_SYNAPTICS_TP     0x0001
 #define USB_DEVICE_ID_PRIMAX_MOUSE_4D22        0x4d22
 #define USB_DEVICE_ID_PRIMAX_KEYBOARD  0x4e05
 #define USB_DEVICE_ID_PRIMAX_REZEL     0x4e72
+#define USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4D0F 0x4d0f
+#define USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4E22 0x4e22
 
 
 #define USB_VENDOR_ID_RISO_KAGAKU      0x1294  /* Riso Kagaku Corp. */
index a2f74e6..d6fab57 100644 (file)
@@ -325,6 +325,9 @@ static const struct hid_device_id hid_battery_quirks[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM,
                USB_DEVICE_ID_ELECOM_BM084),
          HID_BATTERY_QUIRK_IGNORE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SYMBOL,
+               USB_DEVICE_ID_SYMBOL_SCANNER_3),
+         HID_BATTERY_QUIRK_IGNORE },
        {}
 };
 
@@ -1838,47 +1841,3 @@ void hidinput_disconnect(struct hid_device *hid)
 }
 EXPORT_SYMBOL_GPL(hidinput_disconnect);
 
-/**
- * hid_scroll_counter_handle_scroll() - Send high- and low-resolution scroll
- *                                      events given a high-resolution wheel
- *                                      movement.
- * @counter: a hid_scroll_counter struct describing the wheel.
- * @hi_res_value: the movement of the wheel, in the mouse's high-resolution
- *                units.
- *
- * Given a high-resolution movement, this function converts the movement into
- * microns and emits high-resolution scroll events for the input device. It also
- * uses the multiplier from &struct hid_scroll_counter to emit low-resolution
- * scroll events when appropriate for backwards-compatibility with userspace
- * input libraries.
- */
-void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
-                                     int hi_res_value)
-{
-       int low_res_value, remainder, multiplier;
-
-       input_report_rel(counter->dev, REL_WHEEL_HI_RES,
-                        hi_res_value * counter->microns_per_hi_res_unit);
-
-       /*
-        * Update the low-res remainder with the high-res value,
-        * but reset if the direction has changed.
-        */
-       remainder = counter->remainder;
-       if ((remainder ^ hi_res_value) < 0)
-               remainder = 0;
-       remainder += hi_res_value;
-
-       /*
-        * Then just use the resolution multiplier to see if
-        * we should send a low-res (aka regular wheel) event.
-        */
-       multiplier = counter->resolution_multiplier;
-       low_res_value = remainder / multiplier;
-       remainder -= low_res_value * multiplier;
-       counter->remainder = remainder;
-
-       if (low_res_value)
-               input_report_rel(counter->dev, REL_WHEEL, low_res_value);
-}
-EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);
index 1882a4a..98b059d 100644 (file)
@@ -42,6 +42,7 @@ static int ite_event(struct hid_device *hdev, struct hid_field *field,
 
 static const struct hid_device_id ite_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_258A, USB_DEVICE_ID_258A_6A88) },
        { }
 };
 MODULE_DEVICE_TABLE(hid, ite_devices);
index f012808..19cc980 100644 (file)
@@ -64,14 +64,6 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_QUIRK_NO_HIDINPUT                        BIT(23)
 #define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS       BIT(24)
 #define HIDPP_QUIRK_UNIFYING                   BIT(25)
-#define HIDPP_QUIRK_HI_RES_SCROLL_1P0          BIT(26)
-#define HIDPP_QUIRK_HI_RES_SCROLL_X2120                BIT(27)
-#define HIDPP_QUIRK_HI_RES_SCROLL_X2121                BIT(28)
-
-/* Convenience constant to check for any high-res support. */
-#define HIDPP_QUIRK_HI_RES_SCROLL      (HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \
-                                        HIDPP_QUIRK_HI_RES_SCROLL_X2120 | \
-                                        HIDPP_QUIRK_HI_RES_SCROLL_X2121)
 
 #define HIDPP_QUIRK_DELAYED_INIT               HIDPP_QUIRK_NO_HIDINPUT
 
@@ -157,7 +149,6 @@ struct hidpp_device {
        unsigned long capabilities;
 
        struct hidpp_battery battery;
-       struct hid_scroll_counter vertical_wheel_counter;
 };
 
 /* HID++ 1.0 error codes */
@@ -409,53 +400,32 @@ static void hidpp_prefix_name(char **name, int name_length)
 #define HIDPP_SET_LONG_REGISTER                                0x82
 #define HIDPP_GET_LONG_REGISTER                                0x83
 
-/**
- * hidpp10_set_register_bit() - Sets a single bit in a HID++ 1.0 register.
- * @hidpp_dev: the device to set the register on.
- * @register_address: the address of the register to modify.
- * @byte: the byte of the register to modify. Should be less than 3.
- * Return: 0 if successful, otherwise a negative error code.
- */
-static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev,
-       u8 register_address, u8 byte, u8 bit)
+#define HIDPP_REG_GENERAL                              0x00
+
+static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
 {
        struct hidpp_report response;
        int ret;
        u8 params[3] = { 0 };
 
        ret = hidpp_send_rap_command_sync(hidpp_dev,
-                                         REPORT_ID_HIDPP_SHORT,
-                                         HIDPP_GET_REGISTER,
-                                         register_address,
-                                         NULL, 0, &response);
+                                       REPORT_ID_HIDPP_SHORT,
+                                       HIDPP_GET_REGISTER,
+                                       HIDPP_REG_GENERAL,
+                                       NULL, 0, &response);
        if (ret)
                return ret;
 
        memcpy(params, response.rap.params, 3);
 
-       params[byte] |= BIT(bit);
+       /* Set the battery bit */
+       params[0] |= BIT(4);
 
        return hidpp_send_rap_command_sync(hidpp_dev,
-                                          REPORT_ID_HIDPP_SHORT,
-                                          HIDPP_SET_REGISTER,
-                                          register_address,
-                                          params, 3, &response);
-}
-
-
-#define HIDPP_REG_GENERAL                              0x00
-
-static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
-{
-       return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_GENERAL, 0, 4);
-}
-
-#define HIDPP_REG_FEATURES                             0x01
-
-/* On HID++ 1.0 devices, high-res scroll was called "scrolling acceleration". */
-static int hidpp10_enable_scrolling_acceleration(struct hidpp_device *hidpp_dev)
-{
-       return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_FEATURES, 0, 6);
+                                       REPORT_ID_HIDPP_SHORT,
+                                       HIDPP_SET_REGISTER,
+                                       HIDPP_REG_GENERAL,
+                                       params, 3, &response);
 }
 
 #define HIDPP_REG_BATTERY_STATUS                       0x07
@@ -1166,100 +1136,6 @@ static int hidpp_battery_get_property(struct power_supply *psy,
        return ret;
 }
 
-/* -------------------------------------------------------------------------- */
-/* 0x2120: Hi-resolution scrolling                                            */
-/* -------------------------------------------------------------------------- */
-
-#define HIDPP_PAGE_HI_RESOLUTION_SCROLLING                     0x2120
-
-#define CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE 0x10
-
-static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp,
-       bool enabled, u8 *multiplier)
-{
-       u8 feature_index;
-       u8 feature_type;
-       int ret;
-       u8 params[1];
-       struct hidpp_report response;
-
-       ret = hidpp_root_get_feature(hidpp,
-                                    HIDPP_PAGE_HI_RESOLUTION_SCROLLING,
-                                    &feature_index,
-                                    &feature_type);
-       if (ret)
-               return ret;
-
-       params[0] = enabled ? BIT(0) : 0;
-       ret = hidpp_send_fap_command_sync(hidpp, feature_index,
-                                         CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE,
-                                         params, sizeof(params), &response);
-       if (ret)
-               return ret;
-       *multiplier = response.fap.params[1];
-       return 0;
-}
-
-/* -------------------------------------------------------------------------- */
-/* 0x2121: HiRes Wheel                                                        */
-/* -------------------------------------------------------------------------- */
-
-#define HIDPP_PAGE_HIRES_WHEEL         0x2121
-
-#define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY   0x00
-#define CMD_HIRES_WHEEL_SET_WHEEL_MODE         0x20
-
-static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp,
-       u8 *multiplier)
-{
-       u8 feature_index;
-       u8 feature_type;
-       int ret;
-       struct hidpp_report response;
-
-       ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
-                                    &feature_index, &feature_type);
-       if (ret)
-               goto return_default;
-
-       ret = hidpp_send_fap_command_sync(hidpp, feature_index,
-                                         CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY,
-                                         NULL, 0, &response);
-       if (ret)
-               goto return_default;
-
-       *multiplier = response.fap.params[0];
-       return 0;
-return_default:
-       hid_warn(hidpp->hid_dev,
-                "Couldn't get wheel multiplier (error %d), assuming %d.\n",
-                ret, *multiplier);
-       return ret;
-}
-
-static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert,
-       bool high_resolution, bool use_hidpp)
-{
-       u8 feature_index;
-       u8 feature_type;
-       int ret;
-       u8 params[1];
-       struct hidpp_report response;
-
-       ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
-                                    &feature_index, &feature_type);
-       if (ret)
-               return ret;
-
-       params[0] = (invert          ? BIT(2) : 0) |
-                   (high_resolution ? BIT(1) : 0) |
-                   (use_hidpp       ? BIT(0) : 0);
-
-       return hidpp_send_fap_command_sync(hidpp, feature_index,
-                                          CMD_HIRES_WHEEL_SET_WHEEL_MODE,
-                                          params, sizeof(params), &response);
-}
-
 /* -------------------------------------------------------------------------- */
 /* 0x4301: Solar Keyboard                                                     */
 /* -------------------------------------------------------------------------- */
@@ -2523,8 +2399,7 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
                input_report_rel(mydata->input, REL_Y, v);
 
                v = hid_snto32(data[6], 8);
-               hid_scroll_counter_handle_scroll(
-                               &hidpp->vertical_wheel_counter, v);
+               input_report_rel(mydata->input, REL_WHEEL, v);
 
                input_sync(mydata->input);
        }
@@ -2652,72 +2527,6 @@ static int g920_get_config(struct hidpp_device *hidpp)
        return 0;
 }
 
-/* -------------------------------------------------------------------------- */
-/* High-resolution scroll wheels                                              */
-/* -------------------------------------------------------------------------- */
-
-/**
- * struct hi_res_scroll_info - Stores info on a device's high-res scroll wheel.
- * @product_id: the HID product ID of the device being described.
- * @microns_per_hi_res_unit: the distance moved by the user's finger for each
- *                         high-resolution unit reported by the device, in
- *                         256ths of a millimetre.
- */
-struct hi_res_scroll_info {
-       __u32 product_id;
-       int microns_per_hi_res_unit;
-};
-
-static struct hi_res_scroll_info hi_res_scroll_devices[] = {
-       { /* Anywhere MX */
-         .product_id = 0x1017, .microns_per_hi_res_unit = 445 },
-       { /* Performance MX */
-         .product_id = 0x101a, .microns_per_hi_res_unit = 406 },
-       { /* M560 */
-         .product_id = 0x402d, .microns_per_hi_res_unit = 435 },
-       { /* MX Master 2S */
-         .product_id = 0x4069, .microns_per_hi_res_unit = 406 },
-};
-
-static int hi_res_scroll_look_up_microns(__u32 product_id)
-{
-       int i;
-       int num_devices = sizeof(hi_res_scroll_devices)
-                         / sizeof(hi_res_scroll_devices[0]);
-       for (i = 0; i < num_devices; i++) {
-               if (hi_res_scroll_devices[i].product_id == product_id)
-                       return hi_res_scroll_devices[i].microns_per_hi_res_unit;
-       }
-       /* We don't have a value for this device, so use a sensible default. */
-       return 406;
-}
-
-static int hi_res_scroll_enable(struct hidpp_device *hidpp)
-{
-       int ret;
-       u8 multiplier = 8;
-
-       if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2121) {
-               ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false);
-               hidpp_hrw_get_wheel_capability(hidpp, &multiplier);
-       } else if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2120) {
-               ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true,
-                                                          &multiplier);
-       } else /* if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) */
-               ret = hidpp10_enable_scrolling_acceleration(hidpp);
-
-       if (ret)
-               return ret;
-
-       hidpp->vertical_wheel_counter.resolution_multiplier = multiplier;
-       hidpp->vertical_wheel_counter.microns_per_hi_res_unit =
-               hi_res_scroll_look_up_microns(hidpp->hid_dev->product);
-       hid_info(hidpp->hid_dev, "multiplier = %d, microns = %d\n",
-                multiplier,
-                hidpp->vertical_wheel_counter.microns_per_hi_res_unit);
-       return 0;
-}
-
 /* -------------------------------------------------------------------------- */
 /* Generic HID++ devices                                                      */
 /* -------------------------------------------------------------------------- */
@@ -2763,11 +2572,6 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
                wtp_populate_input(hidpp, input, origin_is_hid_core);
        else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
                m560_populate_input(hidpp, input, origin_is_hid_core);
-
-       if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) {
-               input_set_capability(input, EV_REL, REL_WHEEL_HI_RES);
-               hidpp->vertical_wheel_counter.dev = input;
-       }
 }
 
 static int hidpp_input_configured(struct hid_device *hdev,
@@ -2886,27 +2690,6 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
        return 0;
 }
 
-static int hidpp_event(struct hid_device *hdev, struct hid_field *field,
-       struct hid_usage *usage, __s32 value)
-{
-       /* This function will only be called for scroll events, due to the
-        * restriction imposed in hidpp_usages.
-        */
-       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
-       struct hid_scroll_counter *counter = &hidpp->vertical_wheel_counter;
-       /* A scroll event may occur before the multiplier has been retrieved or
-        * the input device set, or high-res scroll enabling may fail. In such
-        * cases we must return early (falling back to default behaviour) to
-        * avoid a crash in hid_scroll_counter_handle_scroll.
-        */
-       if (!(hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) || value == 0
-           || counter->dev == NULL || counter->resolution_multiplier == 0)
-               return 0;
-
-       hid_scroll_counter_handle_scroll(counter, value);
-       return 1;
-}
-
 static int hidpp_initialize_battery(struct hidpp_device *hidpp)
 {
        static atomic_t battery_no = ATOMIC_INIT(0);
@@ -3118,9 +2901,6 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
        if (hidpp->battery.ps)
                power_supply_changed(hidpp->battery.ps);
 
-       if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL)
-               hi_res_scroll_enable(hidpp);
-
        if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
                /* if the input nodes are already created, we can stop now */
                return;
@@ -3306,63 +3086,35 @@ static void hidpp_remove(struct hid_device *hdev)
        mutex_destroy(&hidpp->send_mutex);
 }
 
-#define LDJ_DEVICE(product) \
-       HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \
-                  USB_VENDOR_ID_LOGITECH, (product))
-
 static const struct hid_device_id hidpp_devices[] = {
        { /* wireless touchpad */
-         LDJ_DEVICE(0x4011),
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x4011),
          .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
                         HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
        { /* wireless touchpad T650 */
-         LDJ_DEVICE(0x4101),
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x4101),
          .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
        { /* wireless touchpad T651 */
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
                USB_DEVICE_ID_LOGITECH_T651),
          .driver_data = HIDPP_QUIRK_CLASS_WTP },
-       { /* Mouse Logitech Anywhere MX */
-         LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
-       { /* Mouse Logitech Cube */
-         LDJ_DEVICE(0x4010), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
-       { /* Mouse Logitech M335 */
-         LDJ_DEVICE(0x4050), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { /* Mouse Logitech M515 */
-         LDJ_DEVICE(0x4007), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
        { /* Mouse logitech M560 */
-         LDJ_DEVICE(0x402d),
-         .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560
-               | HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
-       { /* Mouse Logitech M705 (firmware RQM17) */
-         LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
-       { /* Mouse Logitech M705 (firmware RQM67) */
-         LDJ_DEVICE(0x406d), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { /* Mouse Logitech M720 */
-         LDJ_DEVICE(0x405e), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { /* Mouse Logitech MX Anywhere 2 */
-         LDJ_DEVICE(0x404a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { LDJ_DEVICE(0xb013), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { LDJ_DEVICE(0xb018), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { LDJ_DEVICE(0xb01f), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { /* Mouse Logitech MX Anywhere 2S */
-         LDJ_DEVICE(0x406a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { /* Mouse Logitech MX Master */
-         LDJ_DEVICE(0x4041), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { LDJ_DEVICE(0x4060), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { /* Mouse Logitech MX Master 2S */
-         LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
-       { /* Mouse Logitech Performance MX */
-         LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x402d),
+         .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
        { /* Keyboard logitech K400 */
-         LDJ_DEVICE(0x4024),
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x4024),
          .driver_data = HIDPP_QUIRK_CLASS_K400 },
        { /* Solar Keyboard Logitech K750 */
-         LDJ_DEVICE(0x4002),
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x4002),
          .driver_data = HIDPP_QUIRK_CLASS_K750 },
 
-       { LDJ_DEVICE(HID_ANY_ID) },
+       { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
 
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
                .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
@@ -3371,19 +3123,12 @@ static const struct hid_device_id hidpp_devices[] = {
 
 MODULE_DEVICE_TABLE(hid, hidpp_devices);
 
-static const struct hid_usage_id hidpp_usages[] = {
-       { HID_GD_WHEEL, EV_REL, REL_WHEEL },
-       { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
-};
-
 static struct hid_driver hidpp_driver = {
        .name = "logitech-hidpp-device",
        .id_table = hidpp_devices,
        .probe = hidpp_probe,
        .remove = hidpp_remove,
        .raw_event = hidpp_raw_event,
-       .usage_table = hidpp_usages,
-       .event = hidpp_event,
        .input_configured = hidpp_input_configured,
        .input_mapping = hidpp_input_mapping,
        .input_mapped = hidpp_input_mapped,
index f7c6de2..dca0a3a 100644 (file)
@@ -1814,6 +1814,12 @@ static const struct hid_device_id mt_devices[] = {
                MT_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT,
                        USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) },
 
+       /* Cirque devices */
+       { .driver_data = MT_CLS_WIN_8_DUAL,
+               HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
+                       I2C_VENDOR_ID_CIRQUE,
+                       I2C_PRODUCT_ID_CIRQUE_121F) },
+
        /* CJTouch panels */
        { .driver_data = MT_CLS_NSMU,
                MT_USB_DEVICE(USB_VENDOR_ID_CJTOUCH,
index 8237dd8..94088c0 100644 (file)
@@ -107,6 +107,7 @@ static const struct hid_device_id hid_quirks[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C05A), HID_QUIRK_ALWAYS_POLL },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C06A), HID_QUIRK_ALWAYS_POLL },
        { HID_USB_DEVICE(USB_VENDOR_ID_MCS, USB_DEVICE_ID_MCS_GAMEPADBLOCK), HID_QUIRK_MULTI_INPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PIXART_MOUSE), HID_QUIRK_ALWAYS_POLL },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER), HID_QUIRK_NO_INIT_REPORTS },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2), HID_QUIRK_NO_INIT_REPORTS },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2), HID_QUIRK_NO_INIT_REPORTS },
@@ -129,11 +130,15 @@ static const struct hid_device_id hid_quirks[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN), HID_QUIRK_NO_INIT_REPORTS },
        { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_MOUSE_4D22), HID_QUIRK_ALWAYS_POLL },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4D0F), HID_QUIRK_ALWAYS_POLL },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4E22), HID_QUIRK_ALWAYS_POLL },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS), HID_QUIRK_NOGET },
        { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001), HID_QUIRK_NOGET },
        { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3003), HID_QUIRK_NOGET },
        { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008), HID_QUIRK_NOGET },
        { HID_USB_DEVICE(USB_VENDOR_ID_REALTEK, USB_DEVICE_ID_REALTEK_READER), HID_QUIRK_NO_INIT_REPORTS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_RETROUSB, USB_DEVICE_ID_RETROUSB_SNES_RETROPAD), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_RETROUSB, USB_DEVICE_ID_RETROUSB_SNES_RETROPORT), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD), HID_QUIRK_BADPAD },
        { HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD2), HID_QUIRK_NO_INIT_REPORTS },
        { HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD), HID_QUIRK_NO_INIT_REPORTS },
index e8a1141..bb012bc 100644 (file)
@@ -358,7 +358,7 @@ static ssize_t show_value(struct device *dev, struct device_attribute *attr,
                                                sensor_inst->hsdev,
                                                sensor_inst->hsdev->usage,
                                                usage, report_id,
-                                               SENSOR_HUB_SYNC);
+                                               SENSOR_HUB_SYNC, false);
        } else if (!strncmp(name, "units", strlen("units")))
                value = sensor_inst->fields[field_index].attribute.units;
        else if (!strncmp(name, "unit-expo", strlen("unit-expo")))
index 2b63487..4256fdc 100644 (file)
@@ -299,7 +299,8 @@ EXPORT_SYMBOL_GPL(sensor_hub_get_feature);
 int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
                                        u32 usage_id,
                                        u32 attr_usage_id, u32 report_id,
-                                       enum sensor_hub_read_flags flag)
+                                       enum sensor_hub_read_flags flag,
+                                       bool is_signed)
 {
        struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
        unsigned long flags;
@@ -331,10 +332,16 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
                                                &hsdev->pending.ready, HZ*5);
                switch (hsdev->pending.raw_size) {
                case 1:
-                       ret_val = *(u8 *)hsdev->pending.raw_data;
+                       if (is_signed)
+                               ret_val = *(s8 *)hsdev->pending.raw_data;
+                       else
+                               ret_val = *(u8 *)hsdev->pending.raw_data;
                        break;
                case 2:
-                       ret_val = *(u16 *)hsdev->pending.raw_data;
+                       if (is_signed)
+                               ret_val = *(s16 *)hsdev->pending.raw_data;
+                       else
+                               ret_val = *(u16 *)hsdev->pending.raw_data;
                        break;
                case 4:
                        ret_val = *(u32 *)hsdev->pending.raw_data;
index 0422ec2..dc4128b 100644 (file)
@@ -23,8 +23,9 @@
  * In order to avoid breaking them this driver creates a layered hidraw device,
  * so it can detect when the client is running and then:
  *  - it will not send any command to the controller.
- *  - this input device will be disabled, to avoid double input of the same
+ *  - this input device will be removed, to avoid double input of the same
  *    user action.
+ * When the client is closed, this input device will be created again.
  *
  * For additional functions, such as changing the right-pad margin or switching
  * the led, you can use the user-space tool at:
@@ -113,7 +114,7 @@ struct steam_device {
        spinlock_t lock;
        struct hid_device *hdev, *client_hdev;
        struct mutex mutex;
-       bool client_opened, input_opened;
+       bool client_opened;
        struct input_dev __rcu *input;
        unsigned long quirks;
        struct work_struct work_connect;
@@ -279,18 +280,6 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
        }
 }
 
-static void steam_update_lizard_mode(struct steam_device *steam)
-{
-       mutex_lock(&steam->mutex);
-       if (!steam->client_opened) {
-               if (steam->input_opened)
-                       steam_set_lizard_mode(steam, false);
-               else
-                       steam_set_lizard_mode(steam, lizard_mode);
-       }
-       mutex_unlock(&steam->mutex);
-}
-
 static int steam_input_open(struct input_dev *dev)
 {
        struct steam_device *steam = input_get_drvdata(dev);
@@ -301,7 +290,6 @@ static int steam_input_open(struct input_dev *dev)
                return ret;
 
        mutex_lock(&steam->mutex);
-       steam->input_opened = true;
        if (!steam->client_opened && lizard_mode)
                steam_set_lizard_mode(steam, false);
        mutex_unlock(&steam->mutex);
@@ -313,7 +301,6 @@ static void steam_input_close(struct input_dev *dev)
        struct steam_device *steam = input_get_drvdata(dev);
 
        mutex_lock(&steam->mutex);
-       steam->input_opened = false;
        if (!steam->client_opened && lizard_mode)
                steam_set_lizard_mode(steam, true);
        mutex_unlock(&steam->mutex);
@@ -400,7 +387,7 @@ static int steam_battery_register(struct steam_device *steam)
        return 0;
 }
 
-static int steam_register(struct steam_device *steam)
+static int steam_input_register(struct steam_device *steam)
 {
        struct hid_device *hdev = steam->hdev;
        struct input_dev *input;
@@ -414,17 +401,6 @@ static int steam_register(struct steam_device *steam)
                return 0;
        }
 
-       /*
-        * Unlikely, but getting the serial could fail, and it is not so
-        * important, so make up a serial number and go on.
-        */
-       if (steam_get_serial(steam) < 0)
-               strlcpy(steam->serial_no, "XXXXXXXXXX",
-                               sizeof(steam->serial_no));
-
-       hid_info(hdev, "Steam Controller '%s' connected",
-                       steam->serial_no);
-
        input = input_allocate_device();
        if (!input)
                return -ENOMEM;
@@ -492,11 +468,6 @@ static int steam_register(struct steam_device *steam)
                goto input_register_fail;
 
        rcu_assign_pointer(steam->input, input);
-
-       /* ignore battery errors, we can live without it */
-       if (steam->quirks & STEAM_QUIRK_WIRELESS)
-               steam_battery_register(steam);
-
        return 0;
 
 input_register_fail:
@@ -504,27 +475,88 @@ input_register_fail:
        return ret;
 }
 
-static void steam_unregister(struct steam_device *steam)
+static void steam_input_unregister(struct steam_device *steam)
 {
        struct input_dev *input;
+       rcu_read_lock();
+       input = rcu_dereference(steam->input);
+       rcu_read_unlock();
+       if (!input)
+               return;
+       RCU_INIT_POINTER(steam->input, NULL);
+       synchronize_rcu();
+       input_unregister_device(input);
+}
+
+static void steam_battery_unregister(struct steam_device *steam)
+{
        struct power_supply *battery;
 
        rcu_read_lock();
-       input = rcu_dereference(steam->input);
        battery = rcu_dereference(steam->battery);
        rcu_read_unlock();
 
-       if (battery) {
-               RCU_INIT_POINTER(steam->battery, NULL);
-               synchronize_rcu();
-               power_supply_unregister(battery);
+       if (!battery)
+               return;
+       RCU_INIT_POINTER(steam->battery, NULL);
+       synchronize_rcu();
+       power_supply_unregister(battery);
+}
+
+static int steam_register(struct steam_device *steam)
+{
+       int ret;
+
+       /*
+        * This function can be called several times in a row with the
+        * wireless adaptor, without steam_unregister() between them, because
+        * another client send a get_connection_status command, for example.
+        * The battery and serial number are set just once per device.
+        */
+       if (!steam->serial_no[0]) {
+               /*
+                * Unlikely, but getting the serial could fail, and it is not so
+                * important, so make up a serial number and go on.
+                */
+               if (steam_get_serial(steam) < 0)
+                       strlcpy(steam->serial_no, "XXXXXXXXXX",
+                                       sizeof(steam->serial_no));
+
+               hid_info(steam->hdev, "Steam Controller '%s' connected",
+                               steam->serial_no);
+
+               /* ignore battery errors, we can live without it */
+               if (steam->quirks & STEAM_QUIRK_WIRELESS)
+                       steam_battery_register(steam);
+
+               mutex_lock(&steam_devices_lock);
+               list_add(&steam->list, &steam_devices);
+               mutex_unlock(&steam_devices_lock);
        }
-       if (input) {
-               RCU_INIT_POINTER(steam->input, NULL);
-               synchronize_rcu();
+
+       mutex_lock(&steam->mutex);
+       if (!steam->client_opened) {
+               steam_set_lizard_mode(steam, lizard_mode);
+               ret = steam_input_register(steam);
+       } else {
+               ret = 0;
+       }
+       mutex_unlock(&steam->mutex);
+
+       return ret;
+}
+
+static void steam_unregister(struct steam_device *steam)
+{
+       steam_battery_unregister(steam);
+       steam_input_unregister(steam);
+       if (steam->serial_no[0]) {
                hid_info(steam->hdev, "Steam Controller '%s' disconnected",
                                steam->serial_no);
-               input_unregister_device(input);
+               mutex_lock(&steam_devices_lock);
+               list_del(&steam->list);
+               mutex_unlock(&steam_devices_lock);
+               steam->serial_no[0] = 0;
        }
 }
 
@@ -600,6 +632,9 @@ static int steam_client_ll_open(struct hid_device *hdev)
        mutex_lock(&steam->mutex);
        steam->client_opened = true;
        mutex_unlock(&steam->mutex);
+
+       steam_input_unregister(steam);
+
        return ret;
 }
 
@@ -609,13 +644,13 @@ static void steam_client_ll_close(struct hid_device *hdev)
 
        mutex_lock(&steam->mutex);
        steam->client_opened = false;
-       if (steam->input_opened)
-               steam_set_lizard_mode(steam, false);
-       else
-               steam_set_lizard_mode(steam, lizard_mode);
        mutex_unlock(&steam->mutex);
 
        hid_hw_close(steam->hdev);
+       if (steam->connected) {
+               steam_set_lizard_mode(steam, lizard_mode);
+               steam_input_register(steam);
+       }
 }
 
 static int steam_client_ll_raw_request(struct hid_device *hdev,
@@ -744,11 +779,6 @@ static int steam_probe(struct hid_device *hdev,
                }
        }
 
-       mutex_lock(&steam_devices_lock);
-       steam_update_lizard_mode(steam);
-       list_add(&steam->list, &steam_devices);
-       mutex_unlock(&steam_devices_lock);
-
        return 0;
 
 hid_hw_open_fail:
@@ -774,10 +804,6 @@ static void steam_remove(struct hid_device *hdev)
                return;
        }
 
-       mutex_lock(&steam_devices_lock);
-       list_del(&steam->list);
-       mutex_unlock(&steam_devices_lock);
-
        hid_destroy_device(steam->client_hdev);
        steam->client_opened = false;
        cancel_work_sync(&steam->work_connect);
@@ -792,12 +818,14 @@ static void steam_remove(struct hid_device *hdev)
 static void steam_do_connect_event(struct steam_device *steam, bool connected)
 {
        unsigned long flags;
+       bool changed;
 
        spin_lock_irqsave(&steam->lock, flags);
+       changed = steam->connected != connected;
        steam->connected = connected;
        spin_unlock_irqrestore(&steam->lock, flags);
 
-       if (schedule_work(&steam->work_connect) == 0)
+       if (changed && schedule_work(&steam->work_connect) == 0)
                dbg_hid("%s: connected=%d event already queued\n",
                                __func__, connected);
 }
@@ -1019,13 +1047,8 @@ static int steam_raw_event(struct hid_device *hdev,
                        return 0;
                rcu_read_lock();
                input = rcu_dereference(steam->input);
-               if (likely(input)) {
+               if (likely(input))
                        steam_do_input_event(steam, input, data);
-               } else {
-                       dbg_hid("%s: input data without connect event\n",
-                                       __func__);
-                       steam_do_connect_event(steam, true);
-               }
                rcu_read_unlock();
                break;
        case STEAM_EV_CONNECT:
@@ -1074,7 +1097,10 @@ static int steam_param_set_lizard_mode(const char *val,
 
        mutex_lock(&steam_devices_lock);
        list_for_each_entry(steam, &steam_devices, list) {
-               steam_update_lizard_mode(steam);
+               mutex_lock(&steam->mutex);
+               if (!steam->client_opened)
+                       steam_set_lizard_mode(steam, lizard_mode);
+               mutex_unlock(&steam->mutex);
        }
        mutex_unlock(&steam_devices_lock);
        return 0;
index 3cde7c1..8555ce7 100644 (file)
@@ -177,6 +177,8 @@ static const struct i2c_hid_quirks {
                I2C_HID_QUIRK_NO_RUNTIME_PM },
        { I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_4B33,
                I2C_HID_QUIRK_DELAY_AFTER_SLEEP },
+       { USB_VENDOR_ID_LG, I2C_DEVICE_ID_LG_8001,
+               I2C_HID_QUIRK_NO_RUNTIME_PM },
        { 0, 0 }
 };
 
index 3c55073..840634e 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/atomic.h>
 #include <linux/compat.h>
+#include <linux/cred.h>
 #include <linux/device.h>
 #include <linux/fs.h>
 #include <linux/hid.h>
@@ -496,12 +497,13 @@ static int uhid_dev_create2(struct uhid_device *uhid,
                goto err_free;
        }
 
-       len = min(sizeof(hid->name), sizeof(ev->u.create2.name));
-       strlcpy(hid->name, ev->u.create2.name, len);
-       len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys));
-       strlcpy(hid->phys, ev->u.create2.phys, len);
-       len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq));
-       strlcpy(hid->uniq, ev->u.create2.uniq, len);
+       /* @hid is zero-initialized, strncpy() is correct, strlcpy() not */
+       len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1;
+       strncpy(hid->name, ev->u.create2.name, len);
+       len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1;
+       strncpy(hid->phys, ev->u.create2.phys, len);
+       len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1;
+       strncpy(hid->uniq, ev->u.create2.uniq, len);
 
        hid->ll_driver = &uhid_hid_driver;
        hid->bus = ev->u.create2.bus;
@@ -722,6 +724,17 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
 
        switch (uhid->input_buf.type) {
        case UHID_CREATE:
+               /*
+                * 'struct uhid_create_req' contains a __user pointer which is
+                * copied from, so it's unsafe to allow this with elevated
+                * privileges (e.g. from a setuid binary) or via kernel_write().
+                */
+               if (file->f_cred != current_cred() || uaccess_kernel()) {
+                       pr_err_once("UHID_CREATE from different security context by process %d (%s), this is not allowed.\n",
+                                   task_tgid_vnr(current), current->comm);
+                       ret = -EACCES;
+                       goto unlock;
+               }
                ret = uhid_dev_create(uhid, &uhid->input_buf);
                break;
        case UHID_CREATE2:
index 97954f5..1c1a251 100644 (file)
@@ -4,7 +4,7 @@ menu "Microsoft Hyper-V guest support"
 
 config HYPERV
        tristate "Microsoft Hyper-V client drivers"
-       depends on X86 && ACPI && PCI && X86_LOCAL_APIC && HYPERVISOR_GUEST
+       depends on X86 && ACPI && X86_LOCAL_APIC && HYPERVISOR_GUEST
        select PARAVIRT
        help
          Select this option to run Linux as a Hyper-V client operating
index de8193f..fe00b12 100644 (file)
@@ -516,6 +516,14 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
        }
        wait_for_completion(&msginfo->waitevent);
 
+       if (msginfo->response.gpadl_created.creation_status != 0) {
+               pr_err("Failed to establish GPADL: err = 0x%x\n",
+                      msginfo->response.gpadl_created.creation_status);
+
+               ret = -EDQUOT;
+               goto cleanup;
+       }
+
        if (channel->rescind) {
                ret = -ENODEV;
                goto cleanup;
index 6277597..edd34c1 100644 (file)
@@ -435,61 +435,16 @@ void vmbus_free_channels(void)
        }
 }
 
-/*
- * vmbus_process_offer - Process the offer by creating a channel/device
- * associated with this offer
- */
-static void vmbus_process_offer(struct vmbus_channel *newchannel)
+/* Note: the function can run concurrently for primary/sub channels. */
+static void vmbus_add_channel_work(struct work_struct *work)
 {
-       struct vmbus_channel *channel;
-       bool fnew = true;
+       struct vmbus_channel *newchannel =
+               container_of(work, struct vmbus_channel, add_channel_work);
+       struct vmbus_channel *primary_channel = newchannel->primary_channel;
        unsigned long flags;
        u16 dev_type;
        int ret;
 
-       /* Make sure this is a new offer */
-       mutex_lock(&vmbus_connection.channel_mutex);
-
-       /*
-        * Now that we have acquired the channel_mutex,
-        * we can release the potentially racing rescind thread.
-        */
-       atomic_dec(&vmbus_connection.offer_in_progress);
-
-       list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
-               if (!uuid_le_cmp(channel->offermsg.offer.if_type,
-                       newchannel->offermsg.offer.if_type) &&
-                       !uuid_le_cmp(channel->offermsg.offer.if_instance,
-                               newchannel->offermsg.offer.if_instance)) {
-                       fnew = false;
-                       break;
-               }
-       }
-
-       if (fnew)
-               list_add_tail(&newchannel->listentry,
-                             &vmbus_connection.chn_list);
-
-       mutex_unlock(&vmbus_connection.channel_mutex);
-
-       if (!fnew) {
-               /*
-                * Check to see if this is a sub-channel.
-                */
-               if (newchannel->offermsg.offer.sub_channel_index != 0) {
-                       /*
-                        * Process the sub-channel.
-                        */
-                       newchannel->primary_channel = channel;
-                       spin_lock_irqsave(&channel->lock, flags);
-                       list_add_tail(&newchannel->sc_list, &channel->sc_list);
-                       channel->num_sc++;
-                       spin_unlock_irqrestore(&channel->lock, flags);
-               } else {
-                       goto err_free_chan;
-               }
-       }
-
        dev_type = hv_get_dev_type(newchannel);
 
        init_vp_index(newchannel, dev_type);
@@ -507,27 +462,26 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
        /*
         * This state is used to indicate a successful open
         * so that when we do close the channel normally, we
-        * can cleanup properly
+        * can cleanup properly.
         */
        newchannel->state = CHANNEL_OPEN_STATE;
 
-       if (!fnew) {
-               struct hv_device *dev
-                       = newchannel->primary_channel->device_obj;
+       if (primary_channel != NULL) {
+               /* newchannel is a sub-channel. */
+               struct hv_device *dev = primary_channel->device_obj;
 
                if (vmbus_add_channel_kobj(dev, newchannel))
-                       goto err_free_chan;
+                       goto err_deq_chan;
+
+               if (primary_channel->sc_creation_callback != NULL)
+                       primary_channel->sc_creation_callback(newchannel);
 
-               if (channel->sc_creation_callback != NULL)
-                       channel->sc_creation_callback(newchannel);
                newchannel->probe_done = true;
                return;
        }
 
        /*
-        * Start the process of binding this offer to the driver
-        * We need to set the DeviceObject field before calling
-        * vmbus_child_dev_add()
+        * Start the process of binding the primary channel to the driver
         */
        newchannel->device_obj = vmbus_device_create(
                &newchannel->offermsg.offer.if_type,
@@ -556,13 +510,28 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
 
 err_deq_chan:
        mutex_lock(&vmbus_connection.channel_mutex);
-       list_del(&newchannel->listentry);
+
+       /*
+        * We need to set the flag, otherwise
+        * vmbus_onoffer_rescind() can be blocked.
+        */
+       newchannel->probe_done = true;
+
+       if (primary_channel == NULL) {
+               list_del(&newchannel->listentry);
+       } else {
+               spin_lock_irqsave(&primary_channel->lock, flags);
+               list_del(&newchannel->sc_list);
+               spin_unlock_irqrestore(&primary_channel->lock, flags);
+       }
+
        mutex_unlock(&vmbus_connection.channel_mutex);
 
        if (newchannel->target_cpu != get_cpu()) {
                put_cpu();
                smp_call_function_single(newchannel->target_cpu,
-                                        percpu_channel_deq, newchannel, true);
+                                        percpu_channel_deq,
+                                        newchannel, true);
        } else {
                percpu_channel_deq(newchannel);
                put_cpu();
@@ -570,14 +539,104 @@ err_deq_chan:
 
        vmbus_release_relid(newchannel->offermsg.child_relid);
 
-err_free_chan:
        free_channel(newchannel);
 }
 
+/*
+ * vmbus_process_offer - Process the offer by creating a channel/device
+ * associated with this offer
+ */
+static void vmbus_process_offer(struct vmbus_channel *newchannel)
+{
+       struct vmbus_channel *channel;
+       struct workqueue_struct *wq;
+       unsigned long flags;
+       bool fnew = true;
+
+       mutex_lock(&vmbus_connection.channel_mutex);
+
+       /*
+        * Now that we have acquired the channel_mutex,
+        * we can release the potentially racing rescind thread.
+        */
+       atomic_dec(&vmbus_connection.offer_in_progress);
+
+       list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
+               if (!uuid_le_cmp(channel->offermsg.offer.if_type,
+                                newchannel->offermsg.offer.if_type) &&
+                   !uuid_le_cmp(channel->offermsg.offer.if_instance,
+                                newchannel->offermsg.offer.if_instance)) {
+                       fnew = false;
+                       break;
+               }
+       }
+
+       if (fnew)
+               list_add_tail(&newchannel->listentry,
+                             &vmbus_connection.chn_list);
+       else {
+               /*
+                * Check to see if this is a valid sub-channel.
+                */
+               if (newchannel->offermsg.offer.sub_channel_index == 0) {
+                       mutex_unlock(&vmbus_connection.channel_mutex);
+                       /*
+                        * Don't call free_channel(), because newchannel->kobj
+                        * is not initialized yet.
+                        */
+                       kfree(newchannel);
+                       WARN_ON_ONCE(1);
+                       return;
+               }
+               /*
+                * Process the sub-channel.
+                */
+               newchannel->primary_channel = channel;
+               spin_lock_irqsave(&channel->lock, flags);
+               list_add_tail(&newchannel->sc_list, &channel->sc_list);
+               spin_unlock_irqrestore(&channel->lock, flags);
+       }
+
+       mutex_unlock(&vmbus_connection.channel_mutex);
+
+       /*
+        * vmbus_process_offer() mustn't call channel->sc_creation_callback()
+        * directly for sub-channels, because sc_creation_callback() ->
+        * vmbus_open() may never get the host's response to the
+        * OPEN_CHANNEL message (the host may rescind a channel at any time,
+        * e.g. in the case of hot removing a NIC), and vmbus_onoffer_rescind()
+        * may not wake up the vmbus_open() as it's blocked due to a non-zero
+        * vmbus_connection.offer_in_progress, and finally we have a deadlock.
+        *
+        * The above is also true for primary channels, if the related device
+        * drivers use sync probing mode by default.
+        *
+        * And, usually the handling of primary channels and sub-channels can
+        * depend on each other, so we should offload them to different
+        * workqueues to avoid possible deadlock, e.g. in sync-probing mode,
+        * NIC1's netvsc_subchan_work() can race with NIC2's netvsc_probe() ->
+        * rtnl_lock(), and causes deadlock: the former gets the rtnl_lock
+        * and waits for all the sub-channels to appear, but the latter
+        * can't get the rtnl_lock and this blocks the handling of
+        * sub-channels.
+        */
+       INIT_WORK(&newchannel->add_channel_work, vmbus_add_channel_work);
+       wq = fnew ? vmbus_connection.handle_primary_chan_wq :
+                   vmbus_connection.handle_sub_chan_wq;
+       queue_work(wq, &newchannel->add_channel_work);
+}
+
 /*
  * We use this state to statically distribute the channel interrupt load.
  */
 static int next_numa_node_id;
+/*
+ * init_vp_index() accesses global variables like next_numa_node_id, and
+ * it can run concurrently for primary channels and sub-channels: see
+ * vmbus_process_offer(), so we need the lock to protect the global
+ * variables.
+ */
+static DEFINE_SPINLOCK(bind_channel_to_cpu_lock);
 
 /*
  * Starting with Win8, we can statically distribute the incoming
@@ -613,6 +672,8 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
                return;
        }
 
+       spin_lock(&bind_channel_to_cpu_lock);
+
        /*
         * Based on the channel affinity policy, we will assign the NUMA
         * nodes.
@@ -695,6 +756,8 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
        channel->target_cpu = cur_cpu;
        channel->target_vp = hv_cpu_number_to_vp_number(cur_cpu);
 
+       spin_unlock(&bind_channel_to_cpu_lock);
+
        free_cpumask_var(available_mask);
 }
 
index f4d08c8..4fe117b 100644 (file)
@@ -190,6 +190,20 @@ int vmbus_connect(void)
                goto cleanup;
        }
 
+       vmbus_connection.handle_primary_chan_wq =
+               create_workqueue("hv_pri_chan");
+       if (!vmbus_connection.handle_primary_chan_wq) {
+               ret = -ENOMEM;
+               goto cleanup;
+       }
+
+       vmbus_connection.handle_sub_chan_wq =
+               create_workqueue("hv_sub_chan");
+       if (!vmbus_connection.handle_sub_chan_wq) {
+               ret = -ENOMEM;
+               goto cleanup;
+       }
+
        INIT_LIST_HEAD(&vmbus_connection.chn_msg_list);
        spin_lock_init(&vmbus_connection.channelmsg_lock);
 
@@ -280,10 +294,14 @@ void vmbus_disconnect(void)
         */
        vmbus_initiate_unload(false);
 
-       if (vmbus_connection.work_queue) {
-               drain_workqueue(vmbus_connection.work_queue);
+       if (vmbus_connection.handle_sub_chan_wq)
+               destroy_workqueue(vmbus_connection.handle_sub_chan_wq);
+
+       if (vmbus_connection.handle_primary_chan_wq)
+               destroy_workqueue(vmbus_connection.handle_primary_chan_wq);
+
+       if (vmbus_connection.work_queue)
                destroy_workqueue(vmbus_connection.work_queue);
-       }
 
        if (vmbus_connection.int_page) {
                free_pages((unsigned long)vmbus_connection.int_page, 0);
index a7513a8..d6106e1 100644 (file)
@@ -353,6 +353,9 @@ static void process_ib_ipinfo(void *in_msg, void *out_msg, int op)
 
                out->body.kvp_ip_val.dhcp_enabled = in->kvp_ip_val.dhcp_enabled;
 
+               /* fallthrough */
+
+       case KVP_OP_GET_IP_INFO:
                utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id,
                                MAX_ADAPTER_ID_SIZE,
                                UTF16_LITTLE_ENDIAN,
@@ -405,7 +408,11 @@ kvp_send_key(struct work_struct *dummy)
                process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO);
                break;
        case KVP_OP_GET_IP_INFO:
-               /* We only need to pass on message->kvp_hdr.operation.  */
+               /*
+                * We only need to pass on the info of operation, adapter_id
+                * and addr_family to the userland kvp daemon.
+                */
+               process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO);
                break;
        case KVP_OP_SET:
                switch (in_msg->body.kvp_set.data.value_type) {
@@ -446,9 +453,9 @@ kvp_send_key(struct work_struct *dummy)
 
                }
 
-               break;
-
-       case KVP_OP_GET:
+               /*
+                * The key is always a string - utf16 encoding.
+                */
                message->body.kvp_set.data.key_size =
                        utf16s_to_utf8s(
                        (wchar_t *)in_msg->body.kvp_set.data.key,
@@ -456,6 +463,17 @@ kvp_send_key(struct work_struct *dummy)
                        UTF16_LITTLE_ENDIAN,
                        message->body.kvp_set.data.key,
                        HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
+
+               break;
+
+       case KVP_OP_GET:
+               message->body.kvp_get.data.key_size =
+                       utf16s_to_utf8s(
+                       (wchar_t *)in_msg->body.kvp_get.data.key,
+                       in_msg->body.kvp_get.data.key_size,
+                       UTF16_LITTLE_ENDIAN,
+                       message->body.kvp_get.data.key,
+                       HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
                break;
 
        case KVP_OP_DELETE:
index 72eaba3..87d3d7d 100644 (file)
@@ -335,7 +335,14 @@ struct vmbus_connection {
        struct list_head chn_list;
        struct mutex channel_mutex;
 
+       /*
+        * An offer message is handled first on the work_queue, and then
+        * is further handled on handle_primary_chan_wq or
+        * handle_sub_chan_wq.
+        */
        struct workqueue_struct *work_queue;
+       struct workqueue_struct *handle_primary_chan_wq;
+       struct workqueue_struct *handle_sub_chan_wq;
 };
 
 
index 283d184..d0ff656 100644 (file)
@@ -316,6 +316,8 @@ static ssize_t out_intr_mask_show(struct device *dev,
 
        if (!hv_dev->channel)
                return -ENODEV;
+       if (hv_dev->channel->state != CHANNEL_OPENED_STATE)
+               return -EINVAL;
        hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
        return sprintf(buf, "%d\n", outbound.current_interrupt_mask);
 }
@@ -329,6 +331,8 @@ static ssize_t out_read_index_show(struct device *dev,
 
        if (!hv_dev->channel)
                return -ENODEV;
+       if (hv_dev->channel->state != CHANNEL_OPENED_STATE)
+               return -EINVAL;
        hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
        return sprintf(buf, "%d\n", outbound.current_read_index);
 }
@@ -343,6 +347,8 @@ static ssize_t out_write_index_show(struct device *dev,
 
        if (!hv_dev->channel)
                return -ENODEV;
+       if (hv_dev->channel->state != CHANNEL_OPENED_STATE)
+               return -EINVAL;
        hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
        return sprintf(buf, "%d\n", outbound.current_write_index);
 }
@@ -357,6 +363,8 @@ static ssize_t out_read_bytes_avail_show(struct device *dev,
 
        if (!hv_dev->channel)
                return -ENODEV;
+       if (hv_dev->channel->state != CHANNEL_OPENED_STATE)
+               return -EINVAL;
        hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
        return sprintf(buf, "%d\n", outbound.bytes_avail_toread);
 }
@@ -371,6 +379,8 @@ static ssize_t out_write_bytes_avail_show(struct device *dev,
 
        if (!hv_dev->channel)
                return -ENODEV;
+       if (hv_dev->channel->state != CHANNEL_OPENED_STATE)
+               return -EINVAL;
        hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
        return sprintf(buf, "%d\n", outbound.bytes_avail_towrite);
 }
@@ -384,6 +394,8 @@ static ssize_t in_intr_mask_show(struct device *dev,
 
        if (!hv_dev->channel)
                return -ENODEV;
+       if (hv_dev->channel->state != CHANNEL_OPENED_STATE)
+               return -EINVAL;
        hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
        return sprintf(buf, "%d\n", inbound.current_interrupt_mask);
 }
@@ -397,6 +409,8 @@ static ssize_t in_read_index_show(struct device *dev,
 
        if (!hv_dev->channel)
                return -ENODEV;
+       if (hv_dev->channel->state != CHANNEL_OPENED_STATE)
+               return -EINVAL;
        hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
        return sprintf(buf, "%d\n", inbound.current_read_index);
 }
@@ -410,6 +424,8 @@ static ssize_t in_write_index_show(struct device *dev,
 
        if (!hv_dev->channel)
                return -ENODEV;
+       if (hv_dev->channel->state != CHANNEL_OPENED_STATE)
+               return -EINVAL;
        hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
        return sprintf(buf, "%d\n", inbound.current_write_index);
 }
@@ -424,6 +440,8 @@ static ssize_t in_read_bytes_avail_show(struct device *dev,
 
        if (!hv_dev->channel)
                return -ENODEV;
+       if (hv_dev->channel->state != CHANNEL_OPENED_STATE)
+               return -EINVAL;
        hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
        return sprintf(buf, "%d\n", inbound.bytes_avail_toread);
 }
@@ -438,6 +456,8 @@ static ssize_t in_write_bytes_avail_show(struct device *dev,
 
        if (!hv_dev->channel)
                return -ENODEV;
+       if (hv_dev->channel->state != CHANNEL_OPENED_STATE)
+               return -EINVAL;
        hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
        return sprintf(buf, "%d\n", inbound.bytes_avail_towrite);
 }
index 71d3445..07ee195 100644 (file)
@@ -274,7 +274,7 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
                break;
        case INA2XX_CURRENT:
                /* signed register, result in mA */
-               val = regval * data->current_lsb_uA;
+               val = (s16)regval * data->current_lsb_uA;
                val = DIV_ROUND_CLOSEST(val, 1000);
                break;
        case INA2XX_CALIBRATION:
@@ -491,7 +491,7 @@ static int ina2xx_probe(struct i2c_client *client,
        }
 
        data->groups[group++] = &ina2xx_group;
-       if (id->driver_data == ina226)
+       if (chip == ina226)
                data->groups[group++] = &ina226_group;
 
        hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
@@ -500,7 +500,7 @@ static int ina2xx_probe(struct i2c_client *client,
                return PTR_ERR(hwmon_dev);
 
        dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n",
-                id->name, data->rshunt);
+                client->name, data->rshunt);
 
        return 0;
 }
index de46577..d8fa4be 100644 (file)
@@ -51,7 +51,7 @@
  */
 #define MLXREG_FAN_GET_RPM(rval, d, s) (DIV_ROUND_CLOSEST(15000000 * 100, \
                                         ((rval) + (s)) * (d)))
-#define MLXREG_FAN_GET_FAULT(val, mask) (!!((val) ^ (mask)))
+#define MLXREG_FAN_GET_FAULT(val, mask) (!((val) ^ (mask)))
 #define MLXREG_FAN_PWM_DUTY2STATE(duty)        (DIV_ROUND_CLOSEST((duty) *     \
                                         MLXREG_FAN_MAX_STATE,          \
                                         MLXREG_FAN_MAX_DUTY))
index be5ba46..0d04572 100644 (file)
@@ -115,7 +115,6 @@ static int rpi_hwmon_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct rpi_hwmon_data *data;
-       int ret;
 
        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
@@ -124,11 +123,6 @@ static int rpi_hwmon_probe(struct platform_device *pdev)
        /* Parent driver assure that firmware is correct */
        data->fw = dev_get_drvdata(dev->parent);
 
-       /* Init throttled */
-       ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED,
-                                   &data->last_throttled,
-                                   sizeof(data->last_throttled));
-
        data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt",
                                                               data,
                                                               &rpi_chip_info,
index 49276bb..1bb80f9 100644 (file)
@@ -1691,7 +1691,7 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
  * somewhere else in the code
  */
 #define SENSOR_ATTR_TEMP(index) {                                      \
-       SENSOR_ATTR_2(temp##index##_type, S_IRUGO | (index < 4 ? S_IWUSR : 0), \
+       SENSOR_ATTR_2(temp##index##_type, S_IRUGO | (index < 5 ? S_IWUSR : 0), \
                show_temp_mode, store_temp_mode, NOT_USED, index - 1),  \
        SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp,          \
                NULL, TEMP_READ, index - 1),                            \
index 8e60048..51d3495 100644 (file)
@@ -74,8 +74,7 @@
                                 MST_STATUS_ND)
 #define   MST_STATUS_ERR       (MST_STATUS_NAK | \
                                 MST_STATUS_AL  | \
-                                MST_STATUS_IP  | \
-                                MST_STATUS_TSS)
+                                MST_STATUS_IP)
 #define MST_TX_BYTES_XFRD      0x50
 #define MST_RX_BYTES_XFRD      0x54
 #define SCL_HIGH_PERIOD                0x80
@@ -241,7 +240,7 @@ static int axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev)
                         */
                        if (c <= 0 || c > I2C_SMBUS_BLOCK_MAX) {
                                idev->msg_err = -EPROTO;
-                               i2c_int_disable(idev, ~0);
+                               i2c_int_disable(idev, ~MST_STATUS_TSS);
                                complete(&idev->msg_complete);
                                break;
                        }
@@ -299,14 +298,19 @@ static irqreturn_t axxia_i2c_isr(int irq, void *_dev)
 
        if (status & MST_STATUS_SCC) {
                /* Stop completed */
-               i2c_int_disable(idev, ~0);
+               i2c_int_disable(idev, ~MST_STATUS_TSS);
                complete(&idev->msg_complete);
        } else if (status & MST_STATUS_SNS) {
                /* Transfer done */
-               i2c_int_disable(idev, ~0);
+               i2c_int_disable(idev, ~MST_STATUS_TSS);
                if (i2c_m_rd(idev->msg) && idev->msg_xfrd < idev->msg->len)
                        axxia_i2c_empty_rx_fifo(idev);
                complete(&idev->msg_complete);
+       } else if (status & MST_STATUS_TSS) {
+               /* Transfer timeout */
+               idev->msg_err = -ETIMEDOUT;
+               i2c_int_disable(idev, ~MST_STATUS_TSS);
+               complete(&idev->msg_complete);
        } else if (unlikely(status & MST_STATUS_ERR)) {
                /* Transfer error */
                i2c_int_disable(idev, ~0);
@@ -339,10 +343,10 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
        u32 rx_xfer, tx_xfer;
        u32 addr_1, addr_2;
        unsigned long time_left;
+       unsigned int wt_value;
 
        idev->msg = msg;
        idev->msg_xfrd = 0;
-       idev->msg_err = 0;
        reinit_completion(&idev->msg_complete);
 
        if (i2c_m_ten(msg)) {
@@ -383,9 +387,18 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
        else if (axxia_i2c_fill_tx_fifo(idev) != 0)
                int_mask |= MST_STATUS_TFL;
 
+       wt_value = WT_VALUE(readl(idev->base + WAIT_TIMER_CONTROL));
+       /* Disable wait timer temporarly */
+       writel(wt_value, idev->base + WAIT_TIMER_CONTROL);
+       /* Check if timeout error happened */
+       if (idev->msg_err)
+               goto out;
+
        /* Start manual mode */
        writel(CMD_MANUAL, idev->base + MST_COMMAND);
 
+       writel(WT_EN | wt_value, idev->base + WAIT_TIMER_CONTROL);
+
        i2c_int_enable(idev, int_mask);
 
        time_left = wait_for_completion_timeout(&idev->msg_complete,
@@ -396,13 +409,15 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
        if (readl(idev->base + MST_COMMAND) & CMD_BUSY)
                dev_warn(idev->dev, "busy after xfer\n");
 
-       if (time_left == 0)
+       if (time_left == 0) {
                idev->msg_err = -ETIMEDOUT;
-
-       if (idev->msg_err == -ETIMEDOUT)
                i2c_recover_bus(&idev->adapter);
+               axxia_i2c_init(idev);
+       }
 
-       if (unlikely(idev->msg_err) && idev->msg_err != -ENXIO)
+out:
+       if (unlikely(idev->msg_err) && idev->msg_err != -ENXIO &&
+                       idev->msg_err != -ETIMEDOUT)
                axxia_i2c_init(idev);
 
        return idev->msg_err;
@@ -410,7 +425,7 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
 
 static int axxia_i2c_stop(struct axxia_i2c_dev *idev)
 {
-       u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC;
+       u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC | MST_STATUS_TSS;
        unsigned long time_left;
 
        reinit_completion(&idev->msg_complete);
@@ -437,6 +452,9 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
        int i;
        int ret = 0;
 
+       idev->msg_err = 0;
+       i2c_int_enable(idev, MST_STATUS_TSS);
+
        for (i = 0; ret == 0 && i < num; ++i)
                ret = axxia_i2c_xfer_msg(idev, &msgs[i]);
 
index 8822357..e99c3bb 100644 (file)
@@ -89,7 +89,7 @@ static int gpu_i2c_check_status(struct gpu_i2c_dev *i2cd)
 
        if (time_is_before_jiffies(target)) {
                dev_err(i2cd->dev, "i2c timeout error %x\n", val);
-               return -ETIME;
+               return -ETIMEDOUT;
        }
 
        val = readl(i2cd->regs + I2C_MST_CNTL);
@@ -97,9 +97,9 @@ static int gpu_i2c_check_status(struct gpu_i2c_dev *i2cd)
        case I2C_MST_CNTL_STATUS_OKAY:
                return 0;
        case I2C_MST_CNTL_STATUS_NO_ACK:
-               return -EIO;
+               return -ENXIO;
        case I2C_MST_CNTL_STATUS_TIMEOUT:
-               return -ETIME;
+               return -ETIMEDOUT;
        default:
                return 0;
        }
@@ -218,6 +218,7 @@ stop:
 
 static const struct i2c_adapter_quirks gpu_i2c_quirks = {
        .max_read_len = 4,
+       .max_comb_2nd_msg_len = 4,
        .flags = I2C_AQ_COMB_WRITE_THEN_READ,
 };
 
index 4aa7dde..254e621 100644 (file)
@@ -779,6 +779,11 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
 
        pm_runtime_get_sync(dev);
 
+       /* Check bus state before init otherwise bus busy info will be lost */
+       ret = rcar_i2c_bus_barrier(priv);
+       if (ret < 0)
+               goto out;
+
        /* Gen3 needs a reset before allowing RXDMA once */
        if (priv->devtype == I2C_RCAR_GEN3) {
                priv->flags |= ID_P_NO_RXDMA;
@@ -791,10 +796,6 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
 
        rcar_i2c_init(priv);
 
-       ret = rcar_i2c_bus_barrier(priv);
-       if (ret < 0)
-               goto out;
-
        for (i = 0; i < num; i++)
                rcar_i2c_request_dma(priv, msgs + i);
 
index 7e9a2bb..ff3f455 100644 (file)
@@ -367,6 +367,7 @@ static int acpi_smbus_cmi_add(struct acpi_device *device)
 {
        struct acpi_smbus_cmi *smbus_cmi;
        const struct acpi_device_id *id;
+       int ret;
 
        smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL);
        if (!smbus_cmi)
@@ -388,8 +389,10 @@ static int acpi_smbus_cmi_add(struct acpi_device *device)
        acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,
                            acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL);
 
-       if (smbus_cmi->cap_info == 0)
+       if (smbus_cmi->cap_info == 0) {
+               ret = -ENODEV;
                goto err;
+       }
 
        snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name),
                "SMBus CMI adapter %s",
@@ -400,7 +403,8 @@ static int acpi_smbus_cmi_add(struct acpi_device *device)
        smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        smbus_cmi->adapter.dev.parent = &device->dev;
 
-       if (i2c_add_adapter(&smbus_cmi->adapter)) {
+       ret = i2c_add_adapter(&smbus_cmi->adapter);
+       if (ret) {
                dev_err(&device->dev, "Couldn't register adapter!\n");
                goto err;
        }
@@ -410,7 +414,7 @@ static int acpi_smbus_cmi_add(struct acpi_device *device)
 err:
        kfree(smbus_cmi);
        device->driver_data = NULL;
-       return -EIO;
+       return ret;
 }
 
 static int acpi_smbus_cmi_remove(struct acpi_device *device)
index dd38474..03da4a5 100644 (file)
@@ -173,8 +173,6 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
                "interrupt: enabled_irqs=%04x, irq_status=%04x\n",
                priv->enabled_irqs, irq_status);
 
-       uniphier_fi2c_clear_irqs(priv, irq_status);
-
        if (irq_status & UNIPHIER_FI2C_INT_STOP)
                goto complete;
 
@@ -214,7 +212,13 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
 
        if (irq_status & (UNIPHIER_FI2C_INT_RF | UNIPHIER_FI2C_INT_RB)) {
                uniphier_fi2c_drain_rxfifo(priv);
-               if (!priv->len)
+               /*
+                * If the number of bytes to read is multiple of the FIFO size
+                * (msg->len == 8, 16, 24, ...), the INT_RF bit is set a little
+                * earlier than INT_RB. We wait for INT_RB to confirm the
+                * completion of the current message.
+                */
+               if (!priv->len && (irq_status & UNIPHIER_FI2C_INT_RB))
                        goto data_done;
 
                if (unlikely(priv->flags & UNIPHIER_FI2C_MANUAL_NACK)) {
@@ -253,12 +257,20 @@ complete:
        }
 
 handled:
+       /*
+        * This controller makes a pause while any bit of the IRQ status is
+        * asserted. Clear the asserted bit to kick the controller just before
+        * exiting the handler.
+        */
+       uniphier_fi2c_clear_irqs(priv, irq_status);
+
        spin_unlock(&priv->lock);
 
        return IRQ_HANDLED;
 }
 
-static void uniphier_fi2c_tx_init(struct uniphier_fi2c_priv *priv, u16 addr)
+static void uniphier_fi2c_tx_init(struct uniphier_fi2c_priv *priv, u16 addr,
+                                 bool repeat)
 {
        priv->enabled_irqs |= UNIPHIER_FI2C_INT_TE;
        uniphier_fi2c_set_irqs(priv);
@@ -268,8 +280,12 @@ static void uniphier_fi2c_tx_init(struct uniphier_fi2c_priv *priv, u16 addr)
        /* set slave address */
        writel(UNIPHIER_FI2C_DTTX_CMD | addr << 1,
               priv->membase + UNIPHIER_FI2C_DTTX);
-       /* first chunk of data */
-       uniphier_fi2c_fill_txfifo(priv, true);
+       /*
+        * First chunk of data. For a repeated START condition, do not write
+        * data to the TX fifo here to avoid the timing issue.
+        */
+       if (!repeat)
+               uniphier_fi2c_fill_txfifo(priv, true);
 }
 
 static void uniphier_fi2c_rx_init(struct uniphier_fi2c_priv *priv, u16 addr)
@@ -350,7 +366,7 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
        if (is_read)
                uniphier_fi2c_rx_init(priv, msg->addr);
        else
-               uniphier_fi2c_tx_init(priv, msg->addr);
+               uniphier_fi2c_tx_init(priv, msg->addr, repeat);
 
        dev_dbg(&adap->dev, "start condition\n");
        /*
@@ -502,9 +518,26 @@ static void uniphier_fi2c_hw_init(struct uniphier_fi2c_priv *priv)
 
        uniphier_fi2c_reset(priv);
 
+       /*
+        *  Standard-mode: tLOW + tHIGH = 10 us
+        *  Fast-mode:     tLOW + tHIGH = 2.5 us
+        */
        writel(cyc, priv->membase + UNIPHIER_FI2C_CYC);
-       writel(cyc / 2, priv->membase + UNIPHIER_FI2C_LCTL);
+       /*
+        *  Standard-mode: tLOW = 4.7 us, tHIGH = 4.0 us, tBUF = 4.7 us
+        *  Fast-mode:     tLOW = 1.3 us, tHIGH = 0.6 us, tBUF = 1.3 us
+        * "tLow/tHIGH = 5/4" meets both.
+        */
+       writel(cyc * 5 / 9, priv->membase + UNIPHIER_FI2C_LCTL);
+       /*
+        *  Standard-mode: tHD;STA = 4.0 us, tSU;STA = 4.7 us, tSU;STO = 4.0 us
+        *  Fast-mode:     tHD;STA = 0.6 us, tSU;STA = 0.6 us, tSU;STO = 0.6 us
+        */
        writel(cyc / 2, priv->membase + UNIPHIER_FI2C_SSUT);
+       /*
+        *  Standard-mode: tSU;DAT = 250 ns
+        *  Fast-mode:     tSU;DAT = 100 ns
+        */
        writel(cyc / 16, priv->membase + UNIPHIER_FI2C_DSUT);
 
        uniphier_fi2c_prepare_operation(priv);
index 454f914..c488e55 100644 (file)
@@ -320,7 +320,13 @@ static void uniphier_i2c_hw_init(struct uniphier_i2c_priv *priv)
 
        uniphier_i2c_reset(priv, true);
 
-       writel((cyc / 2 << 16) | cyc, priv->membase + UNIPHIER_I2C_CLK);
+       /*
+        * Bit30-16: clock cycles of tLOW.
+        *  Standard-mode: tLOW = 4.7 us, tHIGH = 4.0 us
+        *  Fast-mode:     tLOW = 1.3 us, tHIGH = 0.6 us
+        * "tLow/tHIGH = 5/4" meets both.
+        */
+       writel((cyc * 5 / 9 << 16) | cyc, priv->membase + UNIPHIER_I2C_CLK);
 
        uniphier_i2c_reset(priv, false);
 }
index 45c9974..4c8c7a6 100644 (file)
@@ -614,18 +614,7 @@ static int ide_drivers_show(struct seq_file *s, void *p)
        return 0;
 }
 
-static int ide_drivers_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, &ide_drivers_show, NULL);
-}
-
-static const struct file_operations ide_drivers_operations = {
-       .owner          = THIS_MODULE,
-       .open           = ide_drivers_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(ide_drivers);
 
 void proc_ide_create(void)
 {
@@ -634,7 +623,7 @@ void proc_ide_create(void)
        if (!proc_ide_root)
                return;
 
-       proc_create("drivers", 0, proc_ide_root, &ide_drivers_operations);
+       proc_create("drivers", 0, proc_ide_root, &ide_drivers_fops);
 }
 
 void proc_ide_destroy(void)
index c5b902b..203ed4a 100644 (file)
@@ -920,6 +920,7 @@ static u8 pmac_ide_cable_detect(ide_hwif_t *hwif)
        struct device_node *root = of_find_node_by_path("/");
        const char *model = of_get_property(root, "model", NULL);
 
+       of_node_put(root);
        /* Get cable type from device-tree. */
        if (cable && !strncmp(cable, "80-", 3)) {
                /* Some drives fail to detect 80c cable in PowerBook */
index 41d97fa..38ff374 100644 (file)
@@ -149,6 +149,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
        int report_id = -1;
        u32 address;
        int ret_type;
+       s32 min;
        struct hid_sensor_hub_device *hsdev =
                                        accel_state->common_attributes.hsdev;
 
@@ -158,12 +159,14 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_RAW:
                hid_sensor_power_state(&accel_state->common_attributes, true);
                report_id = accel_state->accel[chan->scan_index].report_id;
+               min = accel_state->accel[chan->scan_index].logical_minimum;
                address = accel_3d_addresses[chan->scan_index];
                if (report_id >= 0)
                        *val = sensor_hub_input_attr_get_raw_value(
                                        accel_state->common_attributes.hsdev,
                                        hsdev->usage, address, report_id,
-                                       SENSOR_HUB_SYNC);
+                                       SENSOR_HUB_SYNC,
+                                       min < 0);
                else {
                        *val = 0;
                        hid_sensor_power_state(&accel_state->common_attributes,
index 36941e6..88e857c 100644 (file)
@@ -111,6 +111,7 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
        int report_id = -1;
        u32 address;
        int ret_type;
+       s32 min;
 
        *val = 0;
        *val2 = 0;
@@ -118,13 +119,15 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_RAW:
                hid_sensor_power_state(&gyro_state->common_attributes, true);
                report_id = gyro_state->gyro[chan->scan_index].report_id;
+               min = gyro_state->gyro[chan->scan_index].logical_minimum;
                address = gyro_3d_addresses[chan->scan_index];
                if (report_id >= 0)
                        *val = sensor_hub_input_attr_get_raw_value(
                                        gyro_state->common_attributes.hsdev,
                                        HID_USAGE_SENSOR_GYRO_3D, address,
                                        report_id,
-                                       SENSOR_HUB_SYNC);
+                                       SENSOR_HUB_SYNC,
+                                       min < 0);
                else {
                        *val = 0;
                        hid_sensor_power_state(&gyro_state->common_attributes,
index beab6d6..4bc95f3 100644 (file)
@@ -75,7 +75,8 @@ static int humidity_read_raw(struct iio_dev *indio_dev,
                                HID_USAGE_SENSOR_HUMIDITY,
                                HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY,
                                humid_st->humidity_attr.report_id,
-                               SENSOR_HUB_SYNC);
+                               SENSOR_HUB_SYNC,
+                               humid_st->humidity_attr.logical_minimum < 0);
                hid_sensor_power_state(&humid_st->common_attributes, false);
 
                return IIO_VAL_INT;
index 406caae..94f3325 100644 (file)
@@ -93,6 +93,7 @@ static int als_read_raw(struct iio_dev *indio_dev,
        int report_id = -1;
        u32 address;
        int ret_type;
+       s32 min;
 
        *val = 0;
        *val2 = 0;
@@ -102,8 +103,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
                case  CHANNEL_SCAN_INDEX_INTENSITY:
                case  CHANNEL_SCAN_INDEX_ILLUM:
                        report_id = als_state->als_illum.report_id;
-                       address =
-                       HID_USAGE_SENSOR_LIGHT_ILLUM;
+                       min = als_state->als_illum.logical_minimum;
+                       address = HID_USAGE_SENSOR_LIGHT_ILLUM;
                        break;
                default:
                        report_id = -1;
@@ -116,7 +117,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
                                        als_state->common_attributes.hsdev,
                                        HID_USAGE_SENSOR_ALS, address,
                                        report_id,
-                                       SENSOR_HUB_SYNC);
+                                       SENSOR_HUB_SYNC,
+                                       min < 0);
                        hid_sensor_power_state(&als_state->common_attributes,
                                                false);
                } else {
index 45107f7..cf5a0c2 100644 (file)
@@ -73,6 +73,7 @@ static int prox_read_raw(struct iio_dev *indio_dev,
        int report_id = -1;
        u32 address;
        int ret_type;
+       s32 min;
 
        *val = 0;
        *val2 = 0;
@@ -81,8 +82,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
                switch (chan->scan_index) {
                case  CHANNEL_SCAN_INDEX_PRESENCE:
                        report_id = prox_state->prox_attr.report_id;
-                       address =
-                       HID_USAGE_SENSOR_HUMAN_PRESENCE;
+                       min = prox_state->prox_attr.logical_minimum;
+                       address = HID_USAGE_SENSOR_HUMAN_PRESENCE;
                        break;
                default:
                        report_id = -1;
@@ -95,7 +96,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
                                prox_state->common_attributes.hsdev,
                                HID_USAGE_SENSOR_PROX, address,
                                report_id,
-                               SENSOR_HUB_SYNC);
+                               SENSOR_HUB_SYNC,
+                               min < 0);
                        hid_sensor_power_state(&prox_state->common_attributes,
                                                false);
                } else {
index d55c488..f3c0d41 100644 (file)
@@ -163,21 +163,23 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
        int report_id = -1;
        u32 address;
        int ret_type;
+       s32 min;
 
        *val = 0;
        *val2 = 0;
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
                hid_sensor_power_state(&magn_state->magn_flux_attributes, true);
-               report_id =
-                       magn_state->magn[chan->address].report_id;
+               report_id = magn_state->magn[chan->address].report_id;
+               min = magn_state->magn[chan->address].logical_minimum;
                address = magn_3d_addresses[chan->address];
                if (report_id >= 0)
                        *val = sensor_hub_input_attr_get_raw_value(
                                magn_state->magn_flux_attributes.hsdev,
                                HID_USAGE_SENSOR_COMPASS_3D, address,
                                report_id,
-                               SENSOR_HUB_SYNC);
+                               SENSOR_HUB_SYNC,
+                               min < 0);
                else {
                        *val = 0;
                        hid_sensor_power_state(
index 0a9e8fa..37ab305 100644 (file)
@@ -30,11 +30,6 @@ int st_magn_trig_set_state(struct iio_trigger *trig, bool state)
        return st_sensors_set_dataready_irq(indio_dev, state);
 }
 
-static int st_magn_buffer_preenable(struct iio_dev *indio_dev)
-{
-       return st_sensors_set_enable(indio_dev, true);
-}
-
 static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
 {
        int err;
@@ -50,7 +45,7 @@ static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
        if (err < 0)
                goto st_magn_buffer_postenable_error;
 
-       return err;
+       return st_sensors_set_enable(indio_dev, true);
 
 st_magn_buffer_postenable_error:
        kfree(mdata->buffer_data);
@@ -63,11 +58,11 @@ static int st_magn_buffer_predisable(struct iio_dev *indio_dev)
        int err;
        struct st_sensor_data *mdata = iio_priv(indio_dev);
 
-       err = iio_triggered_buffer_predisable(indio_dev);
+       err = st_sensors_set_enable(indio_dev, false);
        if (err < 0)
                goto st_magn_buffer_predisable_error;
 
-       err = st_sensors_set_enable(indio_dev, false);
+       err = iio_triggered_buffer_predisable(indio_dev);
 
 st_magn_buffer_predisable_error:
        kfree(mdata->buffer_data);
@@ -75,7 +70,6 @@ st_magn_buffer_predisable_error:
 }
 
 static const struct iio_buffer_setup_ops st_magn_buffer_setup_ops = {
-       .preenable = &st_magn_buffer_preenable,
        .postenable = &st_magn_buffer_postenable,
        .predisable = &st_magn_buffer_predisable,
 };
index 1e5451d..bdc5e45 100644 (file)
@@ -111,21 +111,23 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev,
        int report_id = -1;
        u32 address;
        int ret_type;
+       s32 min;
 
        *val = 0;
        *val2 = 0;
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
                hid_sensor_power_state(&incl_state->common_attributes, true);
-               report_id =
-                       incl_state->incl[chan->scan_index].report_id;
+               report_id = incl_state->incl[chan->scan_index].report_id;
+               min = incl_state->incl[chan->scan_index].logical_minimum;
                address = incl_3d_addresses[chan->scan_index];
                if (report_id >= 0)
                        *val = sensor_hub_input_attr_get_raw_value(
                                incl_state->common_attributes.hsdev,
                                HID_USAGE_SENSOR_INCLINOMETER_3D, address,
                                report_id,
-                               SENSOR_HUB_SYNC);
+                               SENSOR_HUB_SYNC,
+                               min < 0);
                else {
                        hid_sensor_power_state(&incl_state->common_attributes,
                                                false);
index 4c43791..d7b1c00 100644 (file)
@@ -77,6 +77,7 @@ static int press_read_raw(struct iio_dev *indio_dev,
        int report_id = -1;
        u32 address;
        int ret_type;
+       s32 min;
 
        *val = 0;
        *val2 = 0;
@@ -85,8 +86,8 @@ static int press_read_raw(struct iio_dev *indio_dev,
                switch (chan->scan_index) {
                case  CHANNEL_SCAN_INDEX_PRESSURE:
                        report_id = press_state->press_attr.report_id;
-                       address =
-                       HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE;
+                       min = press_state->press_attr.logical_minimum;
+                       address = HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE;
                        break;
                default:
                        report_id = -1;
@@ -99,7 +100,8 @@ static int press_read_raw(struct iio_dev *indio_dev,
                                press_state->common_attributes.hsdev,
                                HID_USAGE_SENSOR_PRESSURE, address,
                                report_id,
-                               SENSOR_HUB_SYNC);
+                               SENSOR_HUB_SYNC,
+                               min < 0);
                        hid_sensor_power_state(&press_state->common_attributes,
                                                false);
                } else {
index beaf6fd..b592fc4 100644 (file)
@@ -76,7 +76,8 @@ static int temperature_read_raw(struct iio_dev *indio_dev,
                        HID_USAGE_SENSOR_TEMPERATURE,
                        HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE,
                        temp_st->temperature_attr.report_id,
-                       SENSOR_HUB_SYNC);
+                       SENSOR_HUB_SYNC,
+                       temp_st->temperature_attr.logical_minimum < 0);
                hid_sensor_power_state(
                                &temp_st->common_attributes,
                                false);
index ee36619..558de0b 100644 (file)
@@ -267,6 +267,9 @@ is_upper_ndev_bond_master_filter(struct ib_device *ib_dev, u8 port,
        struct net_device *cookie_ndev = cookie;
        bool match = false;
 
+       if (!rdma_ndev)
+               return false;
+
        rcu_read_lock();
        if (netif_is_bond_master(cookie_ndev) &&
            rdma_is_upper_dev_rcu(rdma_ndev, cookie_ndev))
@@ -767,8 +770,10 @@ static int netdevice_event(struct notifier_block *this, unsigned long event,
 
        case NETDEV_CHANGEADDR:
                cmds[0] = netdev_del_cmd;
-               cmds[1] = add_default_gid_cmd;
-               cmds[2] = add_cmd;
+               if (ndev->reg_state == NETREG_REGISTERED) {
+                       cmds[1] = add_default_gid_cmd;
+                       cmds[2] = add_cmd;
+               }
                break;
 
        case NETDEV_CHANGEUPPER:
index 2b4c5e7..9608681 100644 (file)
@@ -137,15 +137,6 @@ static void ib_umem_notifier_release(struct mmu_notifier *mn,
        up_read(&per_mm->umem_rwsem);
 }
 
-static int invalidate_page_trampoline(struct ib_umem_odp *item, u64 start,
-                                     u64 end, void *cookie)
-{
-       ib_umem_notifier_start_account(item);
-       item->umem.context->invalidate_range(item, start, start + PAGE_SIZE);
-       ib_umem_notifier_end_account(item);
-       return 0;
-}
-
 static int invalidate_range_start_trampoline(struct ib_umem_odp *item,
                                             u64 start, u64 end, void *cookie)
 {
@@ -553,12 +544,13 @@ out:
                put_page(page);
 
        if (remove_existing_mapping && umem->context->invalidate_range) {
-               invalidate_page_trampoline(
+               ib_umem_notifier_start_account(umem_odp);
+               umem->context->invalidate_range(
                        umem_odp,
-                       ib_umem_start(umem) + (page_index >> umem->page_shift),
-                       ib_umem_start(umem) + ((page_index + 1) >>
-                                              umem->page_shift),
-                       NULL);
+                       ib_umem_start(umem) + (page_index << umem->page_shift),
+                       ib_umem_start(umem) +
+                               ((page_index + 1) << umem->page_shift));
+               ib_umem_notifier_end_account(umem_odp);
                ret = -EAGAIN;
        }
 
@@ -655,8 +647,13 @@ int ib_umem_odp_map_dma_pages(struct ib_umem_odp *umem_odp, u64 user_virt,
                                flags, local_page_list, NULL, NULL);
                up_read(&owning_mm->mmap_sem);
 
-               if (npages < 0)
+               if (npages < 0) {
+                       if (npages != -EAGAIN)
+                               pr_warn("fail to get %zu user pages with error %d\n", gup_num_pages, npages);
+                       else
+                               pr_debug("fail to get %zu user pages with error %d\n", gup_num_pages, npages);
                        break;
+               }
 
                bcnt -= min_t(size_t, npages << PAGE_SHIFT, bcnt);
                mutex_lock(&umem_odp->umem_mutex);
@@ -674,8 +671,13 @@ int ib_umem_odp_map_dma_pages(struct ib_umem_odp *umem_odp, u64 user_virt,
                        ret = ib_umem_odp_map_dma_single_page(
                                        umem_odp, k, local_page_list[j],
                                        access_mask, current_seq);
-                       if (ret < 0)
+                       if (ret < 0) {
+                               if (ret != -EAGAIN)
+                                       pr_warn("ib_umem_odp_map_dma_single_page failed with error %d\n", ret);
+                               else
+                                       pr_debug("ib_umem_odp_map_dma_single_page failed with error %d\n", ret);
                                break;
+                       }
 
                        p = page_to_phys(local_page_list[j]);
                        k++;
index cf22826..77f095e 100644 (file)
@@ -1268,6 +1268,7 @@ static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev)
        /* Registered a new RoCE device instance to netdev */
        rc = bnxt_re_register_netdev(rdev);
        if (rc) {
+               rtnl_unlock();
                pr_err("Failed to register with netedev: %#x\n", rc);
                return -EINVAL;
        }
@@ -1466,6 +1467,7 @@ static void bnxt_re_task(struct work_struct *work)
                                "Failed to register with IB: %#x", rc);
                        bnxt_re_remove_one(rdev);
                        bnxt_re_dev_unreg(rdev);
+                       goto exit;
                }
                break;
        case NETDEV_UP:
@@ -1489,6 +1491,7 @@ static void bnxt_re_task(struct work_struct *work)
        }
        smp_mb__before_atomic();
        atomic_dec(&rdev->sched_count);
+exit:
        kfree(re_work);
 }
 
index 8ed01e0..97ecc8c 100644 (file)
@@ -2058,8 +2058,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
                }
                ep->mtu = pdev->mtu;
                ep->tx_chan = cxgb4_port_chan(pdev);
-               ep->smac_idx = cxgb4_tp_smt_idx(adapter_type,
-                                               cxgb4_port_viid(pdev));
+               ep->smac_idx = ((struct port_info *)netdev_priv(pdev))->smt_idx;
                step = cdev->rdev.lldi.ntxq /
                        cdev->rdev.lldi.nchan;
                ep->txq_idx = cxgb4_port_idx(pdev) * step;
@@ -2078,8 +2077,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
                        goto out;
                ep->mtu = dst_mtu(dst);
                ep->tx_chan = cxgb4_port_chan(pdev);
-               ep->smac_idx = cxgb4_tp_smt_idx(adapter_type,
-                                               cxgb4_port_viid(pdev));
+               ep->smac_idx = ((struct port_info *)netdev_priv(pdev))->smt_idx;
                step = cdev->rdev.lldi.ntxq /
                        cdev->rdev.lldi.nchan;
                ep->txq_idx = cxgb4_port_idx(pdev) * step;
index 9b20479..7e6d709 100644 (file)
@@ -12500,7 +12500,8 @@ static int init_cntrs(struct hfi1_devdata *dd)
        }
 
        /* allocate space for the counter values */
-       dd->cntrs = kcalloc(dd->ndevcntrs, sizeof(u64), GFP_KERNEL);
+       dd->cntrs = kcalloc(dd->ndevcntrs + num_driver_cntrs, sizeof(u64),
+                           GFP_KERNEL);
        if (!dd->cntrs)
                goto bail;
 
index 1401b6e..2b88234 100644 (file)
@@ -155,6 +155,8 @@ struct hfi1_ib_stats {
 extern struct hfi1_ib_stats hfi1_stats;
 extern const struct pci_error_handlers hfi1_pci_err_handler;
 
+extern int num_driver_cntrs;
+
 /*
  * First-cut criterion for "device is active" is
  * two thousand dwords combined Tx, Rx traffic per
index 6f3bc4d..1a01624 100644 (file)
@@ -340,6 +340,13 @@ int hfi1_setup_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe, bool *call_send)
        default:
                break;
        }
+
+       /*
+        * System latency between send and schedule is large enough that
+        * forcing call_send to true for piothreshold packets is necessary.
+        */
+       if (wqe->length <= piothreshold)
+               *call_send = true;
        return 0;
 }
 
index 48e11e5..a365089 100644 (file)
@@ -1479,7 +1479,7 @@ static const char * const driver_cntr_names[] = {
 static DEFINE_MUTEX(cntr_names_lock); /* protects the *_cntr_names bufers */
 static const char **dev_cntr_names;
 static const char **port_cntr_names;
-static int num_driver_cntrs = ARRAY_SIZE(driver_cntr_names);
+int num_driver_cntrs = ARRAY_SIZE(driver_cntr_names);
 static int num_dev_cntrs;
 static int num_port_cntrs;
 static int cntr_names_initialized;
index a4c62ae..3beb152 100644 (file)
@@ -1756,10 +1756,9 @@ static int hns_roce_v2_set_mac(struct hns_roce_dev *hr_dev, u8 phy_port,
        return hns_roce_cmq_send(hr_dev, &desc, 1);
 }
 
-static int hns_roce_v2_write_mtpt(void *mb_buf, struct hns_roce_mr *mr,
-                                 unsigned long mtpt_idx)
+static int set_mtpt_pbl(struct hns_roce_v2_mpt_entry *mpt_entry,
+                       struct hns_roce_mr *mr)
 {
-       struct hns_roce_v2_mpt_entry *mpt_entry;
        struct scatterlist *sg;
        u64 page_addr;
        u64 *pages;
@@ -1767,6 +1766,53 @@ static int hns_roce_v2_write_mtpt(void *mb_buf, struct hns_roce_mr *mr,
        int len;
        int entry;
 
+       mpt_entry->pbl_size = cpu_to_le32(mr->pbl_size);
+       mpt_entry->pbl_ba_l = cpu_to_le32(lower_32_bits(mr->pbl_ba >> 3));
+       roce_set_field(mpt_entry->byte_48_mode_ba,
+                      V2_MPT_BYTE_48_PBL_BA_H_M, V2_MPT_BYTE_48_PBL_BA_H_S,
+                      upper_32_bits(mr->pbl_ba >> 3));
+
+       pages = (u64 *)__get_free_page(GFP_KERNEL);
+       if (!pages)
+               return -ENOMEM;
+
+       i = 0;
+       for_each_sg(mr->umem->sg_head.sgl, sg, mr->umem->nmap, entry) {
+               len = sg_dma_len(sg) >> PAGE_SHIFT;
+               for (j = 0; j < len; ++j) {
+                       page_addr = sg_dma_address(sg) +
+                               (j << mr->umem->page_shift);
+                       pages[i] = page_addr >> 6;
+                       /* Record the first 2 entry directly to MTPT table */
+                       if (i >= HNS_ROCE_V2_MAX_INNER_MTPT_NUM - 1)
+                               goto found;
+                       i++;
+               }
+       }
+found:
+       mpt_entry->pa0_l = cpu_to_le32(lower_32_bits(pages[0]));
+       roce_set_field(mpt_entry->byte_56_pa0_h, V2_MPT_BYTE_56_PA0_H_M,
+                      V2_MPT_BYTE_56_PA0_H_S, upper_32_bits(pages[0]));
+
+       mpt_entry->pa1_l = cpu_to_le32(lower_32_bits(pages[1]));
+       roce_set_field(mpt_entry->byte_64_buf_pa1, V2_MPT_BYTE_64_PA1_H_M,
+                      V2_MPT_BYTE_64_PA1_H_S, upper_32_bits(pages[1]));
+       roce_set_field(mpt_entry->byte_64_buf_pa1,
+                      V2_MPT_BYTE_64_PBL_BUF_PG_SZ_M,
+                      V2_MPT_BYTE_64_PBL_BUF_PG_SZ_S,
+                      mr->pbl_buf_pg_sz + PG_SHIFT_OFFSET);
+
+       free_page((unsigned long)pages);
+
+       return 0;
+}
+
+static int hns_roce_v2_write_mtpt(void *mb_buf, struct hns_roce_mr *mr,
+                                 unsigned long mtpt_idx)
+{
+       struct hns_roce_v2_mpt_entry *mpt_entry;
+       int ret;
+
        mpt_entry = mb_buf;
        memset(mpt_entry, 0, sizeof(*mpt_entry));
 
@@ -1781,7 +1827,6 @@ static int hns_roce_v2_write_mtpt(void *mb_buf, struct hns_roce_mr *mr,
                       mr->pbl_ba_pg_sz + PG_SHIFT_OFFSET);
        roce_set_field(mpt_entry->byte_4_pd_hop_st, V2_MPT_BYTE_4_PD_M,
                       V2_MPT_BYTE_4_PD_S, mr->pd);
-       mpt_entry->byte_4_pd_hop_st = cpu_to_le32(mpt_entry->byte_4_pd_hop_st);
 
        roce_set_bit(mpt_entry->byte_8_mw_cnt_en, V2_MPT_BYTE_8_RA_EN_S, 0);
        roce_set_bit(mpt_entry->byte_8_mw_cnt_en, V2_MPT_BYTE_8_R_INV_EN_S, 1);
@@ -1796,13 +1841,11 @@ static int hns_roce_v2_write_mtpt(void *mb_buf, struct hns_roce_mr *mr,
                     (mr->access & IB_ACCESS_REMOTE_WRITE ? 1 : 0));
        roce_set_bit(mpt_entry->byte_8_mw_cnt_en, V2_MPT_BYTE_8_LW_EN_S,
                     (mr->access & IB_ACCESS_LOCAL_WRITE ? 1 : 0));
-       mpt_entry->byte_8_mw_cnt_en = cpu_to_le32(mpt_entry->byte_8_mw_cnt_en);
 
        roce_set_bit(mpt_entry->byte_12_mw_pa, V2_MPT_BYTE_12_PA_S,
                     mr->type == MR_TYPE_MR ? 0 : 1);
        roce_set_bit(mpt_entry->byte_12_mw_pa, V2_MPT_BYTE_12_INNER_PA_VLD_S,
                     1);
-       mpt_entry->byte_12_mw_pa = cpu_to_le32(mpt_entry->byte_12_mw_pa);
 
        mpt_entry->len_l = cpu_to_le32(lower_32_bits(mr->size));
        mpt_entry->len_h = cpu_to_le32(upper_32_bits(mr->size));
@@ -1813,53 +1856,9 @@ static int hns_roce_v2_write_mtpt(void *mb_buf, struct hns_roce_mr *mr,
        if (mr->type == MR_TYPE_DMA)
                return 0;
 
-       mpt_entry->pbl_size = cpu_to_le32(mr->pbl_size);
-
-       mpt_entry->pbl_ba_l = cpu_to_le32(lower_32_bits(mr->pbl_ba >> 3));
-       roce_set_field(mpt_entry->byte_48_mode_ba, V2_MPT_BYTE_48_PBL_BA_H_M,
-                      V2_MPT_BYTE_48_PBL_BA_H_S,
-                      upper_32_bits(mr->pbl_ba >> 3));
-       mpt_entry->byte_48_mode_ba = cpu_to_le32(mpt_entry->byte_48_mode_ba);
-
-       pages = (u64 *)__get_free_page(GFP_KERNEL);
-       if (!pages)
-               return -ENOMEM;
-
-       i = 0;
-       for_each_sg(mr->umem->sg_head.sgl, sg, mr->umem->nmap, entry) {
-               len = sg_dma_len(sg) >> PAGE_SHIFT;
-               for (j = 0; j < len; ++j) {
-                       page_addr = sg_dma_address(sg) +
-                                   (j << mr->umem->page_shift);
-                       pages[i] = page_addr >> 6;
-
-                       /* Record the first 2 entry directly to MTPT table */
-                       if (i >= HNS_ROCE_V2_MAX_INNER_MTPT_NUM - 1)
-                               goto found;
-                       i++;
-               }
-       }
-
-found:
-       mpt_entry->pa0_l = cpu_to_le32(lower_32_bits(pages[0]));
-       roce_set_field(mpt_entry->byte_56_pa0_h, V2_MPT_BYTE_56_PA0_H_M,
-                      V2_MPT_BYTE_56_PA0_H_S,
-                      upper_32_bits(pages[0]));
-       mpt_entry->byte_56_pa0_h = cpu_to_le32(mpt_entry->byte_56_pa0_h);
-
-       mpt_entry->pa1_l = cpu_to_le32(lower_32_bits(pages[1]));
-       roce_set_field(mpt_entry->byte_64_buf_pa1, V2_MPT_BYTE_64_PA1_H_M,
-                      V2_MPT_BYTE_64_PA1_H_S, upper_32_bits(pages[1]));
+       ret = set_mtpt_pbl(mpt_entry, mr);
 
-       free_page((unsigned long)pages);
-
-       roce_set_field(mpt_entry->byte_64_buf_pa1,
-                      V2_MPT_BYTE_64_PBL_BUF_PG_SZ_M,
-                      V2_MPT_BYTE_64_PBL_BUF_PG_SZ_S,
-                      mr->pbl_buf_pg_sz + PG_SHIFT_OFFSET);
-       mpt_entry->byte_64_buf_pa1 = cpu_to_le32(mpt_entry->byte_64_buf_pa1);
-
-       return 0;
+       return ret;
 }
 
 static int hns_roce_v2_rereg_write_mtpt(struct hns_roce_dev *hr_dev,
@@ -1868,6 +1867,7 @@ static int hns_roce_v2_rereg_write_mtpt(struct hns_roce_dev *hr_dev,
                                        u64 size, void *mb_buf)
 {
        struct hns_roce_v2_mpt_entry *mpt_entry = mb_buf;
+       int ret = 0;
 
        if (flags & IB_MR_REREG_PD) {
                roce_set_field(mpt_entry->byte_4_pd_hop_st, V2_MPT_BYTE_4_PD_M,
@@ -1880,14 +1880,14 @@ static int hns_roce_v2_rereg_write_mtpt(struct hns_roce_dev *hr_dev,
                             V2_MPT_BYTE_8_BIND_EN_S,
                             (mr_access_flags & IB_ACCESS_MW_BIND ? 1 : 0));
                roce_set_bit(mpt_entry->byte_8_mw_cnt_en,
-                          V2_MPT_BYTE_8_ATOMIC_EN_S,
-                          (mr_access_flags & IB_ACCESS_REMOTE_ATOMIC ? 1 : 0));
+                            V2_MPT_BYTE_8_ATOMIC_EN_S,
+                            mr_access_flags & IB_ACCESS_REMOTE_ATOMIC ? 1 : 0);
                roce_set_bit(mpt_entry->byte_8_mw_cnt_en, V2_MPT_BYTE_8_RR_EN_S,
-                            (mr_access_flags & IB_ACCESS_REMOTE_READ ? 1 : 0));
+                            mr_access_flags & IB_ACCESS_REMOTE_READ ? 1 : 0);
                roce_set_bit(mpt_entry->byte_8_mw_cnt_en, V2_MPT_BYTE_8_RW_EN_S,
-                           (mr_access_flags & IB_ACCESS_REMOTE_WRITE ? 1 : 0));
+                            mr_access_flags & IB_ACCESS_REMOTE_WRITE ? 1 : 0);
                roce_set_bit(mpt_entry->byte_8_mw_cnt_en, V2_MPT_BYTE_8_LW_EN_S,
-                            (mr_access_flags & IB_ACCESS_LOCAL_WRITE ? 1 : 0));
+                            mr_access_flags & IB_ACCESS_LOCAL_WRITE ? 1 : 0);
        }
 
        if (flags & IB_MR_REREG_TRANS) {
@@ -1896,21 +1896,13 @@ static int hns_roce_v2_rereg_write_mtpt(struct hns_roce_dev *hr_dev,
                mpt_entry->len_l = cpu_to_le32(lower_32_bits(size));
                mpt_entry->len_h = cpu_to_le32(upper_32_bits(size));
 
-               mpt_entry->pbl_size = cpu_to_le32(mr->pbl_size);
-               mpt_entry->pbl_ba_l =
-                               cpu_to_le32(lower_32_bits(mr->pbl_ba >> 3));
-               roce_set_field(mpt_entry->byte_48_mode_ba,
-                              V2_MPT_BYTE_48_PBL_BA_H_M,
-                              V2_MPT_BYTE_48_PBL_BA_H_S,
-                              upper_32_bits(mr->pbl_ba >> 3));
-               mpt_entry->byte_48_mode_ba =
-                               cpu_to_le32(mpt_entry->byte_48_mode_ba);
-
                mr->iova = iova;
                mr->size = size;
+
+               ret = set_mtpt_pbl(mpt_entry, mr);
        }
 
-       return 0;
+       return ret;
 }
 
 static int hns_roce_v2_frmr_write_mtpt(void *mb_buf, struct hns_roce_mr *mr)
index 82adc0d..4351234 100644 (file)
@@ -181,6 +181,7 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
        struct mlx4_ib_dev *dev = to_mdev(ibdev);
        struct mlx4_ib_cq *cq;
        struct mlx4_uar *uar;
+       void *buf_addr;
        int err;
 
        if (entries < 1 || entries > dev->dev->caps.max_cqes)
@@ -211,6 +212,8 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
                        goto err_cq;
                }
 
+               buf_addr = (void *)(unsigned long)ucmd.buf_addr;
+
                err = mlx4_ib_get_cq_umem(dev, context, &cq->buf, &cq->umem,
                                          ucmd.buf_addr, entries);
                if (err)
@@ -237,6 +240,8 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
                if (err)
                        goto err_db;
 
+               buf_addr = &cq->buf.buf;
+
                uar = &dev->priv_uar;
                cq->mcq.usage = MLX4_RES_USAGE_DRIVER;
        }
@@ -246,7 +251,9 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
 
        err = mlx4_cq_alloc(dev->dev, entries, &cq->buf.mtt, uar,
                            cq->db.dma, &cq->mcq, vector, 0,
-                           !!(cq->create_flags & IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION));
+                           !!(cq->create_flags &
+                              IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION),
+                           buf_addr, !!context);
        if (err)
                goto err_dbmap;
 
index b8e4b15..33f5adb 100644 (file)
@@ -1,6 +1,8 @@
 obj-$(CONFIG_MLX5_INFINIBAND)  += mlx5_ib.o
 
-mlx5_ib-y :=   main.o cq.o doorbell.o qp.o mem.o srq.o mr.o ah.o mad.o gsi.o ib_virt.o cmd.o cong.o
+mlx5_ib-y :=   main.o cq.o doorbell.o qp.o mem.o srq_cmd.o \
+               srq.o mr.o ah.o mad.o gsi.o ib_virt.o cmd.o \
+               cong.o
 mlx5_ib-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += odp.o
 mlx5_ib-$(CONFIG_MLX5_ESWITCH) += ib_rep.o
 mlx5_ib-$(CONFIG_INFINIBAND_USER_ACCESS) += devx.o
index 7d769b5..26ab904 100644 (file)
@@ -35,6 +35,7 @@
 #include <rdma/ib_user_verbs.h>
 #include <rdma/ib_cache.h>
 #include "mlx5_ib.h"
+#include "srq.h"
 
 static void mlx5_ib_cq_comp(struct mlx5_core_cq *cq)
 {
@@ -81,7 +82,7 @@ static void *get_sw_cqe(struct mlx5_ib_cq *cq, int n)
 
        cqe64 = (cq->mcq.cqe_sz == 64) ? cqe : cqe + 64;
 
-       if (likely((cqe64->op_own) >> 4 != MLX5_CQE_INVALID) &&
+       if (likely(get_cqe_opcode(cqe64) != MLX5_CQE_INVALID) &&
            !((cqe64->op_own & MLX5_CQE_OWNER_MASK) ^ !!(n & (cq->ibcq.cqe + 1)))) {
                return cqe;
        } else {
@@ -177,8 +178,7 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe,
                struct mlx5_core_srq *msrq = NULL;
 
                if (qp->ibqp.xrcd) {
-                       msrq = mlx5_core_get_srq(dev->mdev,
-                                                be32_to_cpu(cqe->srqn));
+                       msrq = mlx5_cmd_get_srq(dev, be32_to_cpu(cqe->srqn));
                        srq = to_mibsrq(msrq);
                } else {
                        srq = to_msrq(qp->ibqp.srq);
@@ -197,7 +197,7 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe,
        }
        wc->byte_len = be32_to_cpu(cqe->byte_cnt);
 
-       switch (cqe->op_own >> 4) {
+       switch (get_cqe_opcode(cqe)) {
        case MLX5_CQE_RESP_WR_IMM:
                wc->opcode      = IB_WC_RECV_RDMA_WITH_IMM;
                wc->wc_flags    = IB_WC_WITH_IMM;
@@ -537,7 +537,7 @@ repoll:
         */
        rmb();
 
-       opcode = cqe64->op_own >> 4;
+       opcode = get_cqe_opcode(cqe64);
        if (unlikely(opcode == MLX5_CQE_RESIZE_CQ)) {
                if (likely(cq->resize_buf)) {
                        free_cq_buf(dev, &cq->buf);
@@ -1295,7 +1295,7 @@ static int copy_resize_cqes(struct mlx5_ib_cq *cq)
                return -EINVAL;
        }
 
-       while ((scqe64->op_own >> 4) != MLX5_CQE_RESIZE_CQ) {
+       while (get_cqe_opcode(scqe64) != MLX5_CQE_RESIZE_CQ) {
                dcqe = mlx5_frag_buf_get_wqe(&cq->resize_buf->fbc,
                                             (i + 1) & cq->resize_buf->nent);
                dcqe64 = dsize == 64 ? dcqe : dcqe + 64;
index 61aab7c..45c421c 100644 (file)
@@ -1066,7 +1066,9 @@ static int devx_umem_get(struct mlx5_ib_dev *dev, struct ib_ucontext *ucontext,
 
        err = uverbs_get_flags32(&access, attrs,
                                 MLX5_IB_ATTR_DEVX_UMEM_REG_ACCESS,
-                                IB_ACCESS_SUPPORTED);
+                                IB_ACCESS_LOCAL_WRITE |
+                                IB_ACCESS_REMOTE_WRITE |
+                                IB_ACCESS_REMOTE_READ);
        if (err)
                return err;
 
index 584ff2e..46a9ddc 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include "ib_rep.h"
+#include "srq.h"
 
 static const struct mlx5_ib_profile rep_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_INIT,
@@ -21,6 +22,9 @@ static const struct mlx5_ib_profile rep_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_ROCE,
                     mlx5_ib_stage_rep_roce_init,
                     mlx5_ib_stage_rep_roce_cleanup),
+       STAGE_CREATE(MLX5_IB_STAGE_SRQ,
+                    mlx5_init_srq_table,
+                    mlx5_cleanup_srq_table),
        STAGE_CREATE(MLX5_IB_STAGE_DEVICE_RESOURCES,
                     mlx5_ib_stage_dev_res_init,
                     mlx5_ib_stage_dev_res_cleanup),
@@ -44,13 +48,21 @@ 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)
 {
-       rep->rep_if[REP_IB].priv = NULL;
+       struct mlx5_ib_dev *ibdev;
+
+       ibdev = mlx5_ib_rep_to_dev(rep);
+       __mlx5_ib_remove(ibdev, ibdev->profile, MLX5_IB_STAGE_MAX);
 }
 
 static int
@@ -85,6 +97,7 @@ mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep *rep)
        dev = mlx5_ib_rep_to_dev(rep);
        __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
        rep->rep_if[REP_IB].priv = NULL;
+       ib_dealloc_device(&dev->ib_dev);
 }
 
 static void *mlx5_ib_vport_get_proto_dev(struct mlx5_eswitch_rep *rep)
index e9c4280..e85974a 100644 (file)
@@ -60,6 +60,7 @@
 #include "mlx5_ib.h"
 #include "ib_rep.h"
 #include "cmd.h"
+#include "srq.h"
 #include <linux/mlx5/fs_helpers.h>
 #include <linux/mlx5/accel.h>
 #include <rdma/uverbs_std_types.h>
@@ -82,10 +83,13 @@ static char mlx5_version[] =
 
 struct mlx5_ib_event_work {
        struct work_struct      work;
-       struct mlx5_core_dev    *dev;
-       void                    *context;
-       enum mlx5_dev_event     event;
-       unsigned long           param;
+       union {
+               struct mlx5_ib_dev            *dev;
+               struct mlx5_ib_multiport_info *mpi;
+       };
+       bool                    is_slave;
+       unsigned int            event;
+       void                    *param;
 };
 
 enum {
@@ -441,7 +445,7 @@ static int mlx5_query_port_roce(struct ib_device *device, u8 port_num,
        if (!ndev)
                goto out;
 
-       if (mlx5_lag_is_active(dev->mdev)) {
+       if (dev->lag_active) {
                rcu_read_lock();
                upper = netdev_master_upper_dev_get_rcu(ndev);
                if (upper) {
@@ -1094,31 +1098,26 @@ enum mlx5_ib_width {
        MLX5_IB_WIDTH_12X       = 1 << 4
 };
 
-static int translate_active_width(struct ib_device *ibdev, u8 active_width,
+static void translate_active_width(struct ib_device *ibdev, u8 active_width,
                                  u8 *ib_width)
 {
        struct mlx5_ib_dev *dev = to_mdev(ibdev);
-       int err = 0;
 
-       if (active_width & MLX5_IB_WIDTH_1X) {
+       if (active_width & MLX5_IB_WIDTH_1X)
                *ib_width = IB_WIDTH_1X;
-       } else if (active_width & MLX5_IB_WIDTH_2X) {
-               mlx5_ib_dbg(dev, "active_width %d is not supported by IB spec\n",
-                           (int)active_width);
-               err = -EINVAL;
-       } else if (active_width & MLX5_IB_WIDTH_4X) {
+       else if (active_width & MLX5_IB_WIDTH_4X)
                *ib_width = IB_WIDTH_4X;
-       } else if (active_width & MLX5_IB_WIDTH_8X) {
+       else if (active_width & MLX5_IB_WIDTH_8X)
                *ib_width = IB_WIDTH_8X;
-       } else if (active_width & MLX5_IB_WIDTH_12X) {
+       else if (active_width & MLX5_IB_WIDTH_12X)
                *ib_width = IB_WIDTH_12X;
-       else {
-               mlx5_ib_dbg(dev, "Invalid active_width %d\n",
+       else {
+               mlx5_ib_dbg(dev, "Invalid active_width %d, setting width to default value: 4x\n",
                            (int)active_width);
-               err = -EINVAL;
+               *ib_width = IB_WIDTH_4X;
        }
 
-       return err;
+       return;
 }
 
 static int mlx5_mtu_to_ib_mtu(int mtu)
@@ -1225,10 +1224,8 @@ static int mlx5_query_hca_port(struct ib_device *ibdev, u8 port,
        if (err)
                goto out;
 
-       err = translate_active_width(ibdev, ib_link_width_oper,
-                                    &props->active_width);
-       if (err)
-               goto out;
+       translate_active_width(ibdev, ib_link_width_oper, &props->active_width);
+
        err = mlx5_query_port_ib_proto_oper(mdev, &props->active_speed, port);
        if (err)
                goto out;
@@ -1851,7 +1848,7 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
        context->lib_caps = req.lib_caps;
        print_lib_caps(dev, context->lib_caps);
 
-       if (mlx5_lag_is_active(dev->mdev)) {
+       if (dev->lag_active) {
                u8 port = mlx5_core_native_port_num(dev->mdev);
 
                atomic_set(&context->tx_port_affinity,
@@ -2676,11 +2673,11 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
                         ntohs(ib_spec->gre.val.protocol));
 
                memcpy(MLX5_ADDR_OF(fte_match_set_misc, misc_params_c,
-                                   gre_key_h),
+                                   gre_key.nvgre.hi),
                       &ib_spec->gre.mask.key,
                       sizeof(ib_spec->gre.mask.key));
                memcpy(MLX5_ADDR_OF(fte_match_set_misc, misc_params_v,
-                                   gre_key_h),
+                                   gre_key.nvgre.hi),
                       &ib_spec->gre.val.key,
                       sizeof(ib_spec->gre.val.key));
                break;
@@ -4233,6 +4230,63 @@ static void delay_drop_handler(struct work_struct *work)
        mutex_unlock(&delay_drop->lock);
 }
 
+static void handle_general_event(struct mlx5_ib_dev *ibdev, struct mlx5_eqe *eqe,
+                                struct ib_event *ibev)
+{
+       switch (eqe->sub_type) {
+       case MLX5_GENERAL_SUBTYPE_DELAY_DROP_TIMEOUT:
+               schedule_work(&ibdev->delay_drop.delay_drop_work);
+               break;
+       default: /* do nothing */
+               return;
+       }
+}
+
+static int handle_port_change(struct mlx5_ib_dev *ibdev, struct mlx5_eqe *eqe,
+                             struct ib_event *ibev)
+{
+       u8 port = (eqe->data.port.port >> 4) & 0xf;
+
+       ibev->element.port_num = port;
+
+       switch (eqe->sub_type) {
+       case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
+       case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
+       case MLX5_PORT_CHANGE_SUBTYPE_INITIALIZED:
+               /* In RoCE, port up/down events are handled in
+                * mlx5_netdev_event().
+                */
+               if (mlx5_ib_port_link_layer(&ibdev->ib_dev, port) ==
+                                           IB_LINK_LAYER_ETHERNET)
+                       return -EINVAL;
+
+               ibev->event = (eqe->sub_type == MLX5_PORT_CHANGE_SUBTYPE_ACTIVE) ?
+                               IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR;
+               break;
+
+       case MLX5_PORT_CHANGE_SUBTYPE_LID:
+               ibev->event = IB_EVENT_LID_CHANGE;
+               break;
+
+       case MLX5_PORT_CHANGE_SUBTYPE_PKEY:
+               ibev->event = IB_EVENT_PKEY_CHANGE;
+               schedule_work(&ibdev->devr.ports[port - 1].pkey_change_work);
+               break;
+
+       case MLX5_PORT_CHANGE_SUBTYPE_GUID:
+               ibev->event = IB_EVENT_GID_CHANGE;
+               break;
+
+       case MLX5_PORT_CHANGE_SUBTYPE_CLIENT_REREG:
+               ibev->event = IB_EVENT_CLIENT_REREGISTER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static void mlx5_ib_handle_event(struct work_struct *_work)
 {
        struct mlx5_ib_event_work *work =
@@ -4240,65 +4294,37 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
        struct mlx5_ib_dev *ibdev;
        struct ib_event ibev;
        bool fatal = false;
-       u8 port = (u8)work->param;
 
-       if (mlx5_core_is_mp_slave(work->dev)) {
-               ibdev = mlx5_ib_get_ibdev_from_mpi(work->context);
+       if (work->is_slave) {
+               ibdev = mlx5_ib_get_ibdev_from_mpi(work->mpi);
                if (!ibdev)
                        goto out;
        } else {
-               ibdev = work->context;
+               ibdev = work->dev;
        }
 
        switch (work->event) {
        case MLX5_DEV_EVENT_SYS_ERROR:
                ibev.event = IB_EVENT_DEVICE_FATAL;
                mlx5_ib_handle_internal_error(ibdev);
+               ibev.element.port_num  = (u8)(unsigned long)work->param;
                fatal = true;
                break;
-
-       case MLX5_DEV_EVENT_PORT_UP:
-       case MLX5_DEV_EVENT_PORT_DOWN:
-       case MLX5_DEV_EVENT_PORT_INITIALIZED:
-               /* In RoCE, port up/down events are handled in
-                * mlx5_netdev_event().
-                */
-               if (mlx5_ib_port_link_layer(&ibdev->ib_dev, port) ==
-                       IB_LINK_LAYER_ETHERNET)
+       case MLX5_EVENT_TYPE_PORT_CHANGE:
+               if (handle_port_change(ibdev, work->param, &ibev))
                        goto out;
-
-               ibev.event = (work->event == MLX5_DEV_EVENT_PORT_UP) ?
-                            IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR;
-               break;
-
-       case MLX5_DEV_EVENT_LID_CHANGE:
-               ibev.event = IB_EVENT_LID_CHANGE;
-               break;
-
-       case MLX5_DEV_EVENT_PKEY_CHANGE:
-               ibev.event = IB_EVENT_PKEY_CHANGE;
-               schedule_work(&ibdev->devr.ports[port - 1].pkey_change_work);
-               break;
-
-       case MLX5_DEV_EVENT_GUID_CHANGE:
-               ibev.event = IB_EVENT_GID_CHANGE;
-               break;
-
-       case MLX5_DEV_EVENT_CLIENT_REREG:
-               ibev.event = IB_EVENT_CLIENT_REREGISTER;
                break;
-       case MLX5_DEV_EVENT_DELAY_DROP_TIMEOUT:
-               schedule_work(&ibdev->delay_drop.delay_drop_work);
-               goto out;
+       case MLX5_EVENT_TYPE_GENERAL_EVENT:
+               handle_general_event(ibdev, work->param, &ibev);
+               /* fall through */
        default:
                goto out;
        }
 
-       ibev.device           = &ibdev->ib_dev;
-       ibev.element.port_num = port;
+       ibev.device = &ibdev->ib_dev;
 
-       if (!rdma_is_port_valid(&ibdev->ib_dev, port)) {
-               mlx5_ib_warn(ibdev, "warning: event on port %d\n", port);
+       if (!rdma_is_port_valid(&ibdev->ib_dev, ibev.element.port_num)) {
+               mlx5_ib_warn(ibdev, "warning: event on port %d\n",  ibev.element.port_num);
                goto out;
        }
 
@@ -4311,22 +4337,43 @@ out:
        kfree(work);
 }
 
-static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context,
-                         enum mlx5_dev_event event, unsigned long param)
+static int mlx5_ib_event(struct notifier_block *nb,
+                        unsigned long event, void *param)
 {
        struct mlx5_ib_event_work *work;
 
        work = kmalloc(sizeof(*work), GFP_ATOMIC);
        if (!work)
-               return;
+               return NOTIFY_DONE;
 
        INIT_WORK(&work->work, mlx5_ib_handle_event);
-       work->dev = dev;
+       work->dev = container_of(nb, struct mlx5_ib_dev, mdev_events);
+       work->is_slave = false;
        work->param = param;
-       work->context = context;
        work->event = event;
 
        queue_work(mlx5_ib_event_wq, &work->work);
+
+       return NOTIFY_OK;
+}
+
+static int mlx5_ib_event_slave_port(struct notifier_block *nb,
+                                   unsigned long event, void *param)
+{
+       struct mlx5_ib_event_work *work;
+
+       work = kmalloc(sizeof(*work), GFP_ATOMIC);
+       if (!work)
+               return NOTIFY_DONE;
+
+       INIT_WORK(&work->work, mlx5_ib_handle_event);
+       work->mpi = container_of(nb, struct mlx5_ib_multiport_info, mdev_events);
+       work->is_slave = true;
+       work->param = param;
+       work->event = event;
+       queue_work(mlx5_ib_event_wq, &work->work);
+
+       return NOTIFY_OK;
 }
 
 static int set_has_smi_cap(struct mlx5_ib_dev *dev)
@@ -4794,7 +4841,7 @@ static int mlx5_eth_lag_init(struct mlx5_ib_dev *dev)
        struct mlx5_flow_table *ft;
        int err;
 
-       if (!ns || !mlx5_lag_is_active(mdev))
+       if (!ns || !mlx5_lag_is_roce(mdev))
                return 0;
 
        err = mlx5_cmd_create_vport_lag(mdev);
@@ -4808,6 +4855,7 @@ static int mlx5_eth_lag_init(struct mlx5_ib_dev *dev)
        }
 
        dev->flow_db->lag_demux_ft = ft;
+       dev->lag_active = true;
        return 0;
 
 err_destroy_vport_lag:
@@ -4819,7 +4867,9 @@ static void mlx5_eth_lag_cleanup(struct mlx5_ib_dev *dev)
 {
        struct mlx5_core_dev *mdev = dev->mdev;
 
-       if (dev->flow_db->lag_demux_ft) {
+       if (dev->lag_active) {
+               dev->lag_active = false;
+
                mlx5_destroy_flow_table(dev->flow_db->lag_demux_ft);
                dev->flow_db->lag_demux_ft = NULL;
 
@@ -5337,7 +5387,7 @@ mlx5_ib_get_vector_affinity(struct ib_device *ibdev, int comp_vector)
 {
        struct mlx5_ib_dev *dev = to_mdev(ibdev);
 
-       return mlx5_get_vector_affinity_hint(dev->mdev, comp_vector);
+       return mlx5_comp_irq_get_affinity_mask(dev->mdev, comp_vector);
 }
 
 /* The mlx5_ib_multiport_mutex should be held when calling this function */
@@ -5357,6 +5407,11 @@ static void mlx5_ib_unbind_slave_port(struct mlx5_ib_dev *ibdev,
                spin_unlock(&port->mp.mpi_lock);
                return;
        }
+
+       if (mpi->mdev_events.notifier_call)
+               mlx5_notifier_unregister(mpi->mdev, &mpi->mdev_events);
+       mpi->mdev_events.notifier_call = NULL;
+
        mpi->ibdev = NULL;
 
        spin_unlock(&port->mp.mpi_lock);
@@ -5412,6 +5467,7 @@ static bool mlx5_ib_bind_slave_port(struct mlx5_ib_dev *ibdev,
 
        ibdev->port[port_num].mp.mpi = mpi;
        mpi->ibdev = ibdev;
+       mpi->mdev_events.notifier_call = NULL;
        spin_unlock(&ibdev->port[port_num].mp.mpi_lock);
 
        err = mlx5_nic_vport_affiliate_multiport(ibdev->mdev, mpi->mdev);
@@ -5429,6 +5485,9 @@ static bool mlx5_ib_bind_slave_port(struct mlx5_ib_dev *ibdev,
                goto unbind;
        }
 
+       mpi->mdev_events.notifier_call = mlx5_ib_event_slave_port;
+       mlx5_notifier_register(mpi->mdev, &mpi->mdev_events);
+
        err = mlx5_ib_init_cong_debugfs(ibdev, port_num);
        if (err)
                goto unbind;
@@ -5701,8 +5760,7 @@ int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
        dev->ib_dev.node_type           = RDMA_NODE_IB_CA;
        dev->ib_dev.local_dma_lkey      = 0 /* not supported for now */;
        dev->ib_dev.phys_port_cnt       = dev->num_ports;
-       dev->ib_dev.num_comp_vectors    =
-               dev->mdev->priv.eq_table.num_comp_vectors;
+       dev->ib_dev.num_comp_vectors    = mlx5_comp_vectors_count(mdev);
        dev->ib_dev.dev.parent          = &mdev->pdev->dev;
 
        mutex_init(&dev->cap_mask_mutex);
@@ -6041,6 +6099,11 @@ static int mlx5_ib_stage_odp_init(struct mlx5_ib_dev *dev)
        return mlx5_ib_odp_init_one(dev);
 }
 
+void mlx5_ib_stage_odp_cleanup(struct mlx5_ib_dev *dev)
+{
+       mlx5_ib_odp_cleanup_one(dev);
+}
+
 int mlx5_ib_stage_counters_init(struct mlx5_ib_dev *dev)
 {
        if (MLX5_CAP_GEN(dev->mdev, max_qp_cnt)) {
@@ -6113,7 +6176,7 @@ int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev)
        const char *name;
 
        rdma_set_device_sysfs_group(&dev->ib_dev, &mlx5_attr_group);
-       if (!mlx5_lag_is_active(dev->mdev))
+       if (!mlx5_lag_is_roce(dev->mdev))
                name = "mlx5_%d";
        else
                name = "mlx5_bond_%d";
@@ -6147,16 +6210,32 @@ static void mlx5_ib_stage_delay_drop_cleanup(struct mlx5_ib_dev *dev)
        cancel_delay_drop(dev);
 }
 
-static int mlx5_ib_stage_rep_reg_init(struct mlx5_ib_dev *dev)
+static int mlx5_ib_stage_dev_notifier_init(struct mlx5_ib_dev *dev)
 {
-       mlx5_ib_register_vport_reps(dev);
-
+       dev->mdev_events.notifier_call = mlx5_ib_event;
+       mlx5_notifier_register(dev->mdev, &dev->mdev_events);
        return 0;
 }
 
-static void mlx5_ib_stage_rep_reg_cleanup(struct mlx5_ib_dev *dev)
+static void mlx5_ib_stage_dev_notifier_cleanup(struct mlx5_ib_dev *dev)
+{
+       mlx5_notifier_unregister(dev->mdev, &dev->mdev_events);
+}
+
+static int mlx5_ib_stage_devx_init(struct mlx5_ib_dev *dev)
 {
-       mlx5_ib_unregister_vport_reps(dev);
+       int uid;
+
+       uid = mlx5_ib_devx_create(dev);
+       if (uid > 0)
+               dev->devx_whitelist_uid = uid;
+
+       return 0;
+}
+static void mlx5_ib_stage_devx_cleanup(struct mlx5_ib_dev *dev)
+{
+       if (dev->devx_whitelist_uid)
+               mlx5_ib_devx_destroy(dev, dev->devx_whitelist_uid);
 }
 
 void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
@@ -6169,10 +6248,6 @@ void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
                if (profile->stage[stage].cleanup)
                        profile->stage[stage].cleanup(dev);
        }
-
-       if (dev->devx_whitelist_uid)
-               mlx5_ib_devx_destroy(dev, dev->devx_whitelist_uid);
-       ib_dealloc_device((struct ib_device *)dev);
 }
 
 void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
@@ -6180,7 +6255,6 @@ void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
 {
        int err;
        int i;
-       int uid;
 
        for (i = 0; i < MLX5_IB_STAGE_MAX; i++) {
                if (profile->stage[i].init) {
@@ -6190,10 +6264,6 @@ void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
                }
        }
 
-       uid = mlx5_ib_devx_create(dev);
-       if (uid > 0)
-               dev->devx_whitelist_uid = uid;
-
        dev->profile = profile;
        dev->ib_active = true;
 
@@ -6221,12 +6291,18 @@ static const struct mlx5_ib_profile pf_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_ROCE,
                     mlx5_ib_stage_roce_init,
                     mlx5_ib_stage_roce_cleanup),
+       STAGE_CREATE(MLX5_IB_STAGE_SRQ,
+                    mlx5_init_srq_table,
+                    mlx5_cleanup_srq_table),
        STAGE_CREATE(MLX5_IB_STAGE_DEVICE_RESOURCES,
                     mlx5_ib_stage_dev_res_init,
                     mlx5_ib_stage_dev_res_cleanup),
+       STAGE_CREATE(MLX5_IB_STAGE_DEVICE_NOTIFIER,
+                    mlx5_ib_stage_dev_notifier_init,
+                    mlx5_ib_stage_dev_notifier_cleanup),
        STAGE_CREATE(MLX5_IB_STAGE_ODP,
                     mlx5_ib_stage_odp_init,
-                    NULL),
+                    mlx5_ib_stage_odp_cleanup),
        STAGE_CREATE(MLX5_IB_STAGE_COUNTERS,
                     mlx5_ib_stage_counters_init,
                     mlx5_ib_stage_counters_cleanup),
@@ -6245,6 +6321,9 @@ static const struct mlx5_ib_profile pf_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_SPECS,
                     mlx5_ib_stage_populate_specs,
                     NULL),
+       STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
+                    mlx5_ib_stage_devx_init,
+                    mlx5_ib_stage_devx_cleanup),
        STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
                     mlx5_ib_stage_ib_reg_init,
                     mlx5_ib_stage_ib_reg_cleanup),
@@ -6272,9 +6351,15 @@ static const struct mlx5_ib_profile nic_rep_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_ROCE,
                     mlx5_ib_stage_rep_roce_init,
                     mlx5_ib_stage_rep_roce_cleanup),
+       STAGE_CREATE(MLX5_IB_STAGE_SRQ,
+                    mlx5_init_srq_table,
+                    mlx5_cleanup_srq_table),
        STAGE_CREATE(MLX5_IB_STAGE_DEVICE_RESOURCES,
                     mlx5_ib_stage_dev_res_init,
                     mlx5_ib_stage_dev_res_cleanup),
+       STAGE_CREATE(MLX5_IB_STAGE_DEVICE_NOTIFIER,
+                    mlx5_ib_stage_dev_notifier_init,
+                    mlx5_ib_stage_dev_notifier_cleanup),
        STAGE_CREATE(MLX5_IB_STAGE_COUNTERS,
                     mlx5_ib_stage_counters_init,
                     mlx5_ib_stage_counters_cleanup),
@@ -6296,9 +6381,6 @@ static const struct mlx5_ib_profile nic_rep_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_POST_IB_REG_UMR,
                     mlx5_ib_stage_post_ib_reg_umr_init,
                     NULL),
-       STAGE_CREATE(MLX5_IB_STAGE_REP_REG,
-                    mlx5_ib_stage_rep_reg_init,
-                    mlx5_ib_stage_rep_reg_cleanup),
 };
 
 static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev)
@@ -6366,8 +6448,9 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
        if (MLX5_ESWITCH_MANAGER(mdev) &&
            mlx5_ib_eswitch_mode(mdev->priv.eswitch) == SRIOV_OFFLOADS) {
                dev->rep = mlx5_ib_vport_rep(mdev->priv.eswitch, 0);
-
-               return __mlx5_ib_add(dev, &nic_rep_profile);
+               dev->profile = &nic_rep_profile;
+               mlx5_ib_register_vport_reps(dev);
+               return dev;
        }
 
        return __mlx5_ib_add(dev, &pf_profile);
@@ -6389,16 +6472,17 @@ static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context)
        }
 
        dev = context;
-       __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
+       if (dev->profile == &nic_rep_profile)
+               mlx5_ib_unregister_vport_reps(dev);
+       else
+               __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
+
+       ib_dealloc_device((struct ib_device *)dev);
 }
 
 static struct mlx5_interface mlx5_ib_interface = {
        .add            = mlx5_ib_add,
        .remove         = mlx5_ib_remove,
-       .event          = mlx5_ib_event,
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       .pfault         = mlx5_ib_pfault,
-#endif
        .protocol       = MLX5_INTERFACE_PROTOCOL_IB,
 };
 
index b651a7a..e507b6e 100644 (file)
@@ -41,7 +41,6 @@
 #include <linux/mlx5/cq.h>
 #include <linux/mlx5/fs.h>
 #include <linux/mlx5/qp.h>
-#include <linux/mlx5/srq.h>
 #include <linux/mlx5/fs.h>
 #include <linux/types.h>
 #include <linux/mlx5/transobj.h>
@@ -50,6 +49,8 @@
 #include <rdma/uverbs_ioctl.h>
 #include <rdma/mlx5_user_ioctl_cmds.h>
 
+#include "srq.h"
+
 #define mlx5_ib_dbg(_dev, format, arg...)                                      \
        dev_dbg(&(_dev)->ib_dev.dev, "%s:%d:(pid %d): " format, __func__,      \
                __LINE__, current->pid, ##arg)
@@ -774,7 +775,9 @@ enum mlx5_ib_stages {
        MLX5_IB_STAGE_CAPS,
        MLX5_IB_STAGE_NON_DEFAULT_CB,
        MLX5_IB_STAGE_ROCE,
+       MLX5_IB_STAGE_SRQ,
        MLX5_IB_STAGE_DEVICE_RESOURCES,
+       MLX5_IB_STAGE_DEVICE_NOTIFIER,
        MLX5_IB_STAGE_ODP,
        MLX5_IB_STAGE_COUNTERS,
        MLX5_IB_STAGE_CONG_DEBUGFS,
@@ -782,11 +785,11 @@ enum mlx5_ib_stages {
        MLX5_IB_STAGE_BFREG,
        MLX5_IB_STAGE_PRE_IB_REG_UMR,
        MLX5_IB_STAGE_SPECS,
+       MLX5_IB_STAGE_WHITELIST_UID,
        MLX5_IB_STAGE_IB_REG,
        MLX5_IB_STAGE_POST_IB_REG_UMR,
        MLX5_IB_STAGE_DELAY_DROP,
        MLX5_IB_STAGE_CLASS_ATTR,
-       MLX5_IB_STAGE_REP_REG,
        MLX5_IB_STAGE_MAX,
 };
 
@@ -806,6 +809,7 @@ struct mlx5_ib_multiport_info {
        struct list_head list;
        struct mlx5_ib_dev *ibdev;
        struct mlx5_core_dev *mdev;
+       struct notifier_block mdev_events;
        struct completion unref_comp;
        u64 sys_image_guid;
        u32 mdev_refcnt;
@@ -880,10 +884,20 @@ struct mlx5_ib_lb_state {
        bool                    enabled;
 };
 
+struct mlx5_ib_pf_eq {
+       struct mlx5_ib_dev *dev;
+       struct mlx5_eq *core;
+       struct work_struct work;
+       spinlock_t lock; /* Pagefaults spinlock */
+       struct workqueue_struct *wq;
+       mempool_t *pool;
+};
+
 struct mlx5_ib_dev {
        struct ib_device                ib_dev;
        const struct uverbs_object_tree_def *driver_trees[7];
        struct mlx5_core_dev            *mdev;
+       struct notifier_block           mdev_events;
        struct mlx5_roce                roce[MLX5_MAX_PORTS];
        int                             num_ports;
        /* serialize update of capability mask
@@ -902,6 +916,8 @@ struct mlx5_ib_dev {
 #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
        struct ib_odp_caps      odp_caps;
        u64                     odp_max_size;
+       struct mlx5_ib_pf_eq    odp_pf_eq;
+
        /*
         * Sleepable RCU that prevents destruction of MRs while they are still
         * being used by a page fault handler.
@@ -920,6 +936,7 @@ struct mlx5_ib_dev {
        struct mlx5_ib_delay_drop       delay_drop;
        const struct mlx5_ib_profile    *profile;
        struct mlx5_eswitch_rep         *rep;
+       int                             lag_active;
 
        struct mlx5_ib_lb_state         lb;
        u8                      umr_fence;
@@ -927,6 +944,7 @@ struct mlx5_ib_dev {
        u64                     sys_image_guid;
        struct mlx5_memic       memic;
        u16                     devx_whitelist_uid;
+       struct mlx5_srq_table   srq_table;
 };
 
 static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq)
@@ -1158,9 +1176,8 @@ struct ib_mr *mlx5_ib_reg_dm_mr(struct ib_pd *pd, struct ib_dm *dm,
 
 #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
 void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev);
-void mlx5_ib_pfault(struct mlx5_core_dev *mdev, void *context,
-                   struct mlx5_pagefault *pfault);
 int mlx5_ib_odp_init_one(struct mlx5_ib_dev *ibdev);
+void mlx5_ib_odp_cleanup_one(struct mlx5_ib_dev *ibdev);
 int __init mlx5_ib_odp_init(void);
 void mlx5_ib_odp_cleanup(void);
 void mlx5_ib_invalidate_range(struct ib_umem_odp *umem_odp, unsigned long start,
@@ -1175,6 +1192,7 @@ static inline void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev)
 }
 
 static inline int mlx5_ib_odp_init_one(struct mlx5_ib_dev *ibdev) { return 0; }
+static inline void mlx5_ib_odp_cleanup_one(struct mlx5_ib_dev *ibdev) {}
 static inline int mlx5_ib_odp_init(void) { return 0; }
 static inline void mlx5_ib_odp_cleanup(void)                               {}
 static inline void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent) {}
index b04eb67..7309fb6 100644 (file)
 #include "mlx5_ib.h"
 #include "cmd.h"
 
+#include <linux/mlx5/eq.h>
+
+/* Contains the details of a pagefault. */
+struct mlx5_pagefault {
+       u32                     bytes_committed;
+       u32                     token;
+       u8                      event_subtype;
+       u8                      type;
+       union {
+               /* Initiator or send message responder pagefault details. */
+               struct {
+                       /* Received packet size, only valid for responders. */
+                       u32     packet_size;
+                       /*
+                        * Number of resource holding WQE, depends on type.
+                        */
+                       u32     wq_num;
+                       /*
+                        * WQE index. Refers to either the send queue or
+                        * receive queue, according to event_subtype.
+                        */
+                       u16     wqe_index;
+               } wqe;
+               /* RDMA responder pagefault details */
+               struct {
+                       u32     r_key;
+                       /*
+                        * Received packet size, minimal size page fault
+                        * resolution required for forward progress.
+                        */
+                       u32     packet_size;
+                       u32     rdma_op_len;
+                       u64     rdma_va;
+               } rdma;
+       };
+
+       struct mlx5_ib_pf_eq    *eq;
+       struct work_struct      work;
+};
+
 #define MAX_PREFETCH_LEN (4*1024*1024U)
 
 /* Timeout in ms to wait for an active mmu notifier to complete when handling
@@ -304,14 +344,20 @@ static void mlx5_ib_page_fault_resume(struct mlx5_ib_dev *dev,
 {
        int wq_num = pfault->event_subtype == MLX5_PFAULT_SUBTYPE_WQE ?
                     pfault->wqe.wq_num : pfault->token;
-       int ret = mlx5_core_page_fault_resume(dev->mdev,
-                                             pfault->token,
-                                             wq_num,
-                                             pfault->type,
-                                             error);
-       if (ret)
-               mlx5_ib_err(dev, "Failed to resolve the page fault on WQ 0x%x\n",
-                           wq_num);
+       u32 out[MLX5_ST_SZ_DW(page_fault_resume_out)] = { };
+       u32 in[MLX5_ST_SZ_DW(page_fault_resume_in)]   = { };
+       int err;
+
+       MLX5_SET(page_fault_resume_in, in, opcode, MLX5_CMD_OP_PAGE_FAULT_RESUME);
+       MLX5_SET(page_fault_resume_in, in, page_fault_type, pfault->type);
+       MLX5_SET(page_fault_resume_in, in, token, pfault->token);
+       MLX5_SET(page_fault_resume_in, in, wq_number, wq_num);
+       MLX5_SET(page_fault_resume_in, in, error, !!error);
+
+       err = mlx5_cmd_exec(dev->mdev, in, sizeof(in), out, sizeof(out));
+       if (err)
+               mlx5_ib_err(dev, "Failed to resolve the page fault on WQ 0x%x err %d\n",
+                           wq_num, err);
 }
 
 static struct mlx5_ib_mr *implicit_mr_alloc(struct ib_pd *pd,
@@ -506,14 +552,13 @@ void mlx5_ib_free_implicit_mr(struct mlx5_ib_mr *imr)
 static int pagefault_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr,
                        u64 io_virt, size_t bcnt, u32 *bytes_mapped)
 {
+       int npages = 0, current_seq, page_shift, ret, np;
+       bool implicit = false;
        struct ib_umem_odp *odp_mr = to_ib_umem_odp(mr->umem);
        u64 access_mask = ODP_READ_ALLOWED_BIT;
-       int npages = 0, page_shift, np;
        u64 start_idx, page_mask;
        struct ib_umem_odp *odp;
-       int current_seq;
        size_t size;
-       int ret;
 
        if (!odp_mr->page_list) {
                odp = implicit_mr_get_data(mr, io_virt, bcnt);
@@ -521,7 +566,7 @@ static int pagefault_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr,
                if (IS_ERR(odp))
                        return PTR_ERR(odp);
                mr = odp->private;
-
+               implicit = true;
        } else {
                odp = odp_mr;
        }
@@ -600,15 +645,15 @@ next_mr:
 
 out:
        if (ret == -EAGAIN) {
-               if (mr->parent || !odp->dying) {
+               if (implicit || !odp->dying) {
                        unsigned long timeout =
                                msecs_to_jiffies(MMU_NOTIFIER_TIMEOUT);
 
                        if (!wait_for_completion_timeout(
                                        &odp->notifier_completion,
                                        timeout)) {
-                               mlx5_ib_warn(dev, "timeout waiting for mmu notifier. seq %d against %d\n",
-                                            current_seq, odp->notifiers_seq);
+                               mlx5_ib_warn(dev, "timeout waiting for mmu notifier. seq %d against %d. notifiers_count=%d\n",
+                                            current_seq, odp->notifiers_seq, odp->notifiers_count);
                        }
                } else {
                        /* The MR is being killed, kill the QP as well. */
@@ -674,6 +719,15 @@ next_mr:
                        goto srcu_unlock;
                }
 
+               if (!mr->umem->is_odp) {
+                       mlx5_ib_dbg(dev, "skipping non ODP MR (lkey=0x%06x) in page fault handler.\n",
+                                   key);
+                       if (bytes_mapped)
+                               *bytes_mapped += bcnt;
+                       ret = 0;
+                       goto srcu_unlock;
+               }
+
                ret = pagefault_mr(dev, mr, io_virt, bcnt, bytes_mapped);
                if (ret < 0)
                        goto srcu_unlock;
@@ -735,6 +789,7 @@ next_mr:
                        head = frame;
 
                        bcnt -= frame->bcnt;
+                       offset = 0;
                }
                break;
 
@@ -1016,16 +1071,31 @@ invalid_transport_or_opcode:
        return 0;
 }
 
-static struct mlx5_ib_qp *mlx5_ib_odp_find_qp(struct mlx5_ib_dev *dev,
-                                             u32 wq_num)
+static inline struct mlx5_core_rsc_common *odp_get_rsc(struct mlx5_ib_dev *dev,
+                                                      u32 wq_num, int pf_type)
 {
-       struct mlx5_core_qp *mqp = __mlx5_qp_lookup(dev->mdev, wq_num);
+       enum mlx5_res_type res_type;
 
-       if (!mqp) {
-               mlx5_ib_err(dev, "QPN 0x%6x not found\n", wq_num);
+       switch (pf_type) {
+       case MLX5_WQE_PF_TYPE_RMP:
+               res_type = MLX5_RES_SRQ;
+               break;
+       case MLX5_WQE_PF_TYPE_REQ_SEND_OR_WRITE:
+       case MLX5_WQE_PF_TYPE_RESP:
+       case MLX5_WQE_PF_TYPE_REQ_READ_OR_ATOMIC:
+               res_type = MLX5_RES_QP;
+               break;
+       default:
                return NULL;
        }
 
+       return mlx5_core_res_hold(dev->mdev, wq_num, res_type);
+}
+
+static inline struct mlx5_ib_qp *res_to_qp(struct mlx5_core_rsc_common *res)
+{
+       struct mlx5_core_qp *mqp = (struct mlx5_core_qp *)res;
+
        return to_mibqp(mqp);
 }
 
@@ -1039,18 +1109,30 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev,
        int resume_with_error = 1;
        u16 wqe_index = pfault->wqe.wqe_index;
        int requestor = pfault->type & MLX5_PFAULT_REQUESTOR;
+       struct mlx5_core_rsc_common *res;
        struct mlx5_ib_qp *qp;
 
+       res = odp_get_rsc(dev, pfault->wqe.wq_num, pfault->type);
+       if (!res) {
+               mlx5_ib_dbg(dev, "wqe page fault for missing resource %d\n", pfault->wqe.wq_num);
+               return;
+       }
+
+       switch (res->res) {
+       case MLX5_RES_QP:
+               qp = res_to_qp(res);
+               break;
+       default:
+               mlx5_ib_err(dev, "wqe page fault for unsupported type %d\n", pfault->type);
+               goto resolve_page_fault;
+       }
+
        buffer = (char *)__get_free_page(GFP_KERNEL);
        if (!buffer) {
                mlx5_ib_err(dev, "Error allocating memory for IO page fault handling.\n");
                goto resolve_page_fault;
        }
 
-       qp = mlx5_ib_odp_find_qp(dev, pfault->wqe.wq_num);
-       if (!qp)
-               goto resolve_page_fault;
-
        ret = mlx5_ib_read_user_wqe(qp, requestor, wqe_index, buffer,
                                    PAGE_SIZE, &qp->trans_qp.base);
        if (ret < 0) {
@@ -1090,6 +1172,7 @@ resolve_page_fault:
        mlx5_ib_dbg(dev, "PAGE FAULT completed. QP 0x%x resume_with_error=%d, type: 0x%x\n",
                    pfault->wqe.wq_num, resume_with_error,
                    pfault->type);
+       mlx5_core_res_put(res);
        free_page((unsigned long)buffer);
 }
 
@@ -1168,10 +1251,8 @@ static void mlx5_ib_mr_rdma_pfault_handler(struct mlx5_ib_dev *dev,
        }
 }
 
-void mlx5_ib_pfault(struct mlx5_core_dev *mdev, void *context,
-                   struct mlx5_pagefault *pfault)
+static void mlx5_ib_pfault(struct mlx5_ib_dev *dev, struct mlx5_pagefault *pfault)
 {
-       struct mlx5_ib_dev *dev = context;
        u8 event_subtype = pfault->event_subtype;
 
        switch (event_subtype) {
@@ -1188,6 +1269,203 @@ void mlx5_ib_pfault(struct mlx5_core_dev *mdev, void *context,
        }
 }
 
+static void mlx5_ib_eqe_pf_action(struct work_struct *work)
+{
+       struct mlx5_pagefault *pfault = container_of(work,
+                                                    struct mlx5_pagefault,
+                                                    work);
+       struct mlx5_ib_pf_eq *eq = pfault->eq;
+
+       mlx5_ib_pfault(eq->dev, pfault);
+       mempool_free(pfault, eq->pool);
+}
+
+static void mlx5_ib_eq_pf_process(struct mlx5_ib_pf_eq *eq)
+{
+       struct mlx5_eqe_page_fault *pf_eqe;
+       struct mlx5_pagefault *pfault;
+       struct mlx5_eqe *eqe;
+       int cc = 0;
+
+       while ((eqe = mlx5_eq_get_eqe(eq->core, cc))) {
+               pfault = mempool_alloc(eq->pool, GFP_ATOMIC);
+               if (!pfault) {
+                       schedule_work(&eq->work);
+                       break;
+               }
+
+               pf_eqe = &eqe->data.page_fault;
+               pfault->event_subtype = eqe->sub_type;
+               pfault->bytes_committed = be32_to_cpu(pf_eqe->bytes_committed);
+
+               mlx5_ib_dbg(eq->dev,
+                           "PAGE_FAULT: subtype: 0x%02x, bytes_committed: 0x%06x\n",
+                           eqe->sub_type, pfault->bytes_committed);
+
+               switch (eqe->sub_type) {
+               case MLX5_PFAULT_SUBTYPE_RDMA:
+                       /* RDMA based event */
+                       pfault->type =
+                               be32_to_cpu(pf_eqe->rdma.pftype_token) >> 24;
+                       pfault->token =
+                               be32_to_cpu(pf_eqe->rdma.pftype_token) &
+                               MLX5_24BIT_MASK;
+                       pfault->rdma.r_key =
+                               be32_to_cpu(pf_eqe->rdma.r_key);
+                       pfault->rdma.packet_size =
+                               be16_to_cpu(pf_eqe->rdma.packet_length);
+                       pfault->rdma.rdma_op_len =
+                               be32_to_cpu(pf_eqe->rdma.rdma_op_len);
+                       pfault->rdma.rdma_va =
+                               be64_to_cpu(pf_eqe->rdma.rdma_va);
+                       mlx5_ib_dbg(eq->dev,
+                                   "PAGE_FAULT: type:0x%x, token: 0x%06x, r_key: 0x%08x\n",
+                                   pfault->type, pfault->token,
+                                   pfault->rdma.r_key);
+                       mlx5_ib_dbg(eq->dev,
+                                   "PAGE_FAULT: rdma_op_len: 0x%08x, rdma_va: 0x%016llx\n",
+                                   pfault->rdma.rdma_op_len,
+                                   pfault->rdma.rdma_va);
+                       break;
+
+               case MLX5_PFAULT_SUBTYPE_WQE:
+                       /* WQE based event */
+                       pfault->type =
+                               (be32_to_cpu(pf_eqe->wqe.pftype_wq) >> 24) & 0x7;
+                       pfault->token =
+                               be32_to_cpu(pf_eqe->wqe.token);
+                       pfault->wqe.wq_num =
+                               be32_to_cpu(pf_eqe->wqe.pftype_wq) &
+                               MLX5_24BIT_MASK;
+                       pfault->wqe.wqe_index =
+                               be16_to_cpu(pf_eqe->wqe.wqe_index);
+                       pfault->wqe.packet_size =
+                               be16_to_cpu(pf_eqe->wqe.packet_length);
+                       mlx5_ib_dbg(eq->dev,
+                                   "PAGE_FAULT: type:0x%x, token: 0x%06x, wq_num: 0x%06x, wqe_index: 0x%04x\n",
+                                   pfault->type, pfault->token,
+                                   pfault->wqe.wq_num,
+                                   pfault->wqe.wqe_index);
+                       break;
+
+               default:
+                       mlx5_ib_warn(eq->dev,
+                                    "Unsupported page fault event sub-type: 0x%02hhx\n",
+                                    eqe->sub_type);
+                       /* Unsupported page faults should still be
+                        * resolved by the page fault handler
+                        */
+               }
+
+               pfault->eq = eq;
+               INIT_WORK(&pfault->work, mlx5_ib_eqe_pf_action);
+               queue_work(eq->wq, &pfault->work);
+
+               cc = mlx5_eq_update_cc(eq->core, ++cc);
+       }
+
+       mlx5_eq_update_ci(eq->core, cc, 1);
+}
+
+static irqreturn_t mlx5_ib_eq_pf_int(int irq, void *eq_ptr)
+{
+       struct mlx5_ib_pf_eq *eq = eq_ptr;
+       unsigned long flags;
+
+       if (spin_trylock_irqsave(&eq->lock, flags)) {
+               mlx5_ib_eq_pf_process(eq);
+               spin_unlock_irqrestore(&eq->lock, flags);
+       } else {
+               schedule_work(&eq->work);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/* mempool_refill() was proposed but unfortunately wasn't accepted
+ * http://lkml.iu.edu/hypermail/linux/kernel/1512.1/05073.html
+ * Cheap workaround.
+ */
+static void mempool_refill(mempool_t *pool)
+{
+       while (pool->curr_nr < pool->min_nr)
+               mempool_free(mempool_alloc(pool, GFP_KERNEL), pool);
+}
+
+static void mlx5_ib_eq_pf_action(struct work_struct *work)
+{
+       struct mlx5_ib_pf_eq *eq =
+               container_of(work, struct mlx5_ib_pf_eq, work);
+
+       mempool_refill(eq->pool);
+
+       spin_lock_irq(&eq->lock);
+       mlx5_ib_eq_pf_process(eq);
+       spin_unlock_irq(&eq->lock);
+}
+
+enum {
+       MLX5_IB_NUM_PF_EQE      = 0x1000,
+       MLX5_IB_NUM_PF_DRAIN    = 64,
+};
+
+static int
+mlx5_ib_create_pf_eq(struct mlx5_ib_dev *dev, struct mlx5_ib_pf_eq *eq)
+{
+       struct mlx5_eq_param param = {};
+       int err;
+
+       INIT_WORK(&eq->work, mlx5_ib_eq_pf_action);
+       spin_lock_init(&eq->lock);
+       eq->dev = dev;
+
+       eq->pool = mempool_create_kmalloc_pool(MLX5_IB_NUM_PF_DRAIN,
+                                              sizeof(struct mlx5_pagefault));
+       if (!eq->pool)
+               return -ENOMEM;
+
+       eq->wq = alloc_workqueue("mlx5_ib_page_fault",
+                                WQ_HIGHPRI | WQ_UNBOUND | WQ_MEM_RECLAIM,
+                                MLX5_NUM_CMD_EQE);
+       if (!eq->wq) {
+               err = -ENOMEM;
+               goto err_mempool;
+       }
+
+       param = (struct mlx5_eq_param) {
+               .index = MLX5_EQ_PFAULT_IDX,
+               .mask = 1 << MLX5_EVENT_TYPE_PAGE_FAULT,
+               .nent = MLX5_IB_NUM_PF_EQE,
+               .context = eq,
+               .handler = mlx5_ib_eq_pf_int
+       };
+       eq->core = mlx5_eq_create_generic(dev->mdev, "mlx5_ib_page_fault_eq", &param);
+       if (IS_ERR(eq->core)) {
+               err = PTR_ERR(eq->core);
+               goto err_wq;
+       }
+
+       return 0;
+err_wq:
+       destroy_workqueue(eq->wq);
+err_mempool:
+       mempool_destroy(eq->pool);
+       return err;
+}
+
+static int
+mlx5_ib_destroy_pf_eq(struct mlx5_ib_dev *dev, struct mlx5_ib_pf_eq *eq)
+{
+       int err;
+
+       err = mlx5_eq_destroy_generic(dev->mdev, eq->core);
+       cancel_work_sync(&eq->work);
+       destroy_workqueue(eq->wq);
+       mempool_destroy(eq->pool);
+
+       return err;
+}
+
 void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent)
 {
        if (!(ent->dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT))
@@ -1216,7 +1494,7 @@ void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent)
 
 int mlx5_ib_odp_init_one(struct mlx5_ib_dev *dev)
 {
-       int ret;
+       int ret = 0;
 
        if (dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT) {
                ret = mlx5_cmd_null_mkey(dev->mdev, &dev->null_mkey);
@@ -1226,7 +1504,20 @@ int mlx5_ib_odp_init_one(struct mlx5_ib_dev *dev)
                }
        }
 
-       return 0;
+       if (!MLX5_CAP_GEN(dev->mdev, pg))
+               return ret;
+
+       ret = mlx5_ib_create_pf_eq(dev, &dev->odp_pf_eq);
+
+       return ret;
+}
+
+void mlx5_ib_odp_cleanup_one(struct mlx5_ib_dev *dev)
+{
+       if (!MLX5_CAP_GEN(dev->mdev, pg))
+               return;
+
+       mlx5_ib_destroy_pf_eq(dev, &dev->odp_pf_eq);
 }
 
 int mlx5_ib_odp_init(void)
@@ -1236,4 +1527,3 @@ int mlx5_ib_odp_init(void)
 
        return 0;
 }
-
index 6841c0f..a0e9ff7 100644 (file)
@@ -2633,8 +2633,7 @@ static int to_mlx5_access_flags(struct mlx5_ib_qp *qp,
 
        if (access_flags & IB_ACCESS_REMOTE_READ)
                *hw_access_flags |= MLX5_QP_BIT_RRE;
-       if ((access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
-           qp->ibqp.qp_type == IB_QPT_RC) {
+       if (access_flags & IB_ACCESS_REMOTE_ATOMIC) {
                int atomic_mode;
 
                atomic_mode = get_atomic_mode(dev, qp->ibqp.qp_type);
@@ -3259,7 +3258,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
                    (ibqp->qp_type == IB_QPT_RAW_PACKET) ||
                    (ibqp->qp_type == IB_QPT_XRC_INI) ||
                    (ibqp->qp_type == IB_QPT_XRC_TGT)) {
-                       if (mlx5_lag_is_active(dev->mdev)) {
+                       if (dev->lag_active) {
                                u8 p = mlx5_core_native_port_num(dev->mdev);
                                tx_affinity = get_tx_affinity(dev, pd, base, p);
                                context->flags |= cpu_to_be32(tx_affinity << 24);
@@ -4678,17 +4677,18 @@ static int _mlx5_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
                        goto out;
                }
 
-               if (wr->opcode == IB_WR_LOCAL_INV ||
-                   wr->opcode == IB_WR_REG_MR) {
+               if (wr->opcode == IB_WR_REG_MR) {
                        fence = dev->umr_fence;
                        next_fence = MLX5_FENCE_MODE_INITIATOR_SMALL;
-               } else if (wr->send_flags & IB_SEND_FENCE) {
-                       if (qp->next_fence)
-                               fence = MLX5_FENCE_MODE_SMALL_AND_FENCE;
-                       else
-                               fence = MLX5_FENCE_MODE_FENCE;
-               } else {
-                       fence = qp->next_fence;
+               } else  {
+                       if (wr->send_flags & IB_SEND_FENCE) {
+                               if (qp->next_fence)
+                                       fence = MLX5_FENCE_MODE_SMALL_AND_FENCE;
+                               else
+                                       fence = MLX5_FENCE_MODE_FENCE;
+                       } else {
+                               fence = qp->next_fence;
+                       }
                }
 
                switch (ibqp->qp_type) {
index d012e7d..91dcd39 100644 (file)
@@ -1,46 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
 /*
- * 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.
+ * Copyright (c) 2013-2018, Mellanox Technologies inc.  All rights reserved.
  */
 
 #include <linux/module.h>
 #include <linux/mlx5/qp.h>
-#include <linux/mlx5/srq.h>
 #include <linux/slab.h>
 #include <rdma/ib_umem.h>
 #include <rdma/ib_user_verbs.h>
-
 #include "mlx5_ib.h"
-
-/* not supported currently */
-static int srq_signature;
+#include "srq.h"
 
 static void *get_wqe(struct mlx5_ib_srq *srq, int n)
 {
@@ -202,7 +171,7 @@ static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq,
                err = -ENOMEM;
                goto err_in;
        }
-       srq->wq_sig = !!srq_signature;
+       srq->wq_sig = 0;
 
        in->log_page_size = srq->buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT;
        if (MLX5_CAP_GEN(dev->mdev, cqe_version) == MLX5_CQE_VERSION_V1 &&
@@ -327,7 +296,7 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd,
 
        in.pd = to_mpd(pd)->pdn;
        in.db_record = srq->db.dma;
-       err = mlx5_core_create_srq(dev->mdev, &srq->msrq, &in);
+       err = mlx5_cmd_create_srq(dev, &srq->msrq, &in);
        kvfree(in.pas);
        if (err) {
                mlx5_ib_dbg(dev, "create SRQ failed, err %d\n", err);
@@ -351,7 +320,7 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd,
        return &srq->ibsrq;
 
 err_core:
-       mlx5_core_destroy_srq(dev->mdev, &srq->msrq);
+       mlx5_cmd_destroy_srq(dev, &srq->msrq);
 
 err_usr_kern_srq:
        if (pd->uobject)
@@ -381,7 +350,7 @@ int mlx5_ib_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
                        return -EINVAL;
 
                mutex_lock(&srq->mutex);
-               ret = mlx5_core_arm_srq(dev->mdev, &srq->msrq, attr->srq_limit, 1);
+               ret = mlx5_cmd_arm_srq(dev, &srq->msrq, attr->srq_limit, 1);
                mutex_unlock(&srq->mutex);
 
                if (ret)
@@ -402,7 +371,7 @@ int mlx5_ib_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr)
        if (!out)
                return -ENOMEM;
 
-       ret = mlx5_core_query_srq(dev->mdev, &srq->msrq, out);
+       ret = mlx5_cmd_query_srq(dev, &srq->msrq, out);
        if (ret)
                goto out_box;
 
@@ -420,7 +389,7 @@ int mlx5_ib_destroy_srq(struct ib_srq *srq)
        struct mlx5_ib_dev *dev = to_mdev(srq->device);
        struct mlx5_ib_srq *msrq = to_msrq(srq);
 
-       mlx5_core_destroy_srq(dev->mdev, &msrq->msrq);
+       mlx5_cmd_destroy_srq(dev, &msrq->msrq);
 
        if (srq->uobject) {
                mlx5_ib_db_unmap_user(to_mucontext(srq->uobject->context), &msrq->db);
diff --git a/drivers/infiniband/hw/mlx5/srq.h b/drivers/infiniband/hw/mlx5/srq.h
new file mode 100644 (file)
index 0000000..75eb583
--- /dev/null
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/*
+ * Copyright (c) 2013-2018, Mellanox Technologies. All rights reserved.
+ */
+
+#ifndef MLX5_IB_SRQ_H
+#define MLX5_IB_SRQ_H
+
+enum {
+       MLX5_SRQ_FLAG_ERR    = (1 << 0),
+       MLX5_SRQ_FLAG_WQ_SIG = (1 << 1),
+       MLX5_SRQ_FLAG_RNDV   = (1 << 2),
+};
+
+struct mlx5_srq_attr {
+       u32 type;
+       u32 flags;
+       u32 log_size;
+       u32 wqe_shift;
+       u32 log_page_size;
+       u32 wqe_cnt;
+       u32 srqn;
+       u32 xrcd;
+       u32 page_offset;
+       u32 cqn;
+       u32 pd;
+       u32 lwm;
+       u32 user_index;
+       u64 db_record;
+       __be64 *pas;
+       u32 tm_log_list_size;
+       u32 tm_next_tag;
+       u32 tm_hw_phase_cnt;
+       u32 tm_sw_phase_cnt;
+       u16 uid;
+};
+
+struct mlx5_ib_dev;
+
+struct mlx5_core_srq {
+       struct mlx5_core_rsc_common common; /* must be first */
+       u32 srqn;
+       int max;
+       size_t max_gs;
+       size_t max_avail_gather;
+       int wqe_shift;
+       void (*event)(struct mlx5_core_srq *srq, enum mlx5_event e);
+
+       atomic_t refcount;
+       struct completion free;
+       u16 uid;
+};
+
+struct mlx5_srq_table {
+       struct notifier_block nb;
+       /* protect radix tree
+        */
+       spinlock_t lock;
+       struct radix_tree_root tree;
+};
+
+int mlx5_cmd_create_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                       struct mlx5_srq_attr *in);
+int mlx5_cmd_destroy_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq);
+int mlx5_cmd_query_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                      struct mlx5_srq_attr *out);
+int mlx5_cmd_arm_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                    u16 lwm, int is_srq);
+struct mlx5_core_srq *mlx5_cmd_get_srq(struct mlx5_ib_dev *dev, u32 srqn);
+
+int mlx5_init_srq_table(struct mlx5_ib_dev *dev);
+void mlx5_cleanup_srq_table(struct mlx5_ib_dev *dev);
+#endif /* MLX5_IB_SRQ_H */
diff --git a/drivers/infiniband/hw/mlx5/srq_cmd.c b/drivers/infiniband/hw/mlx5/srq_cmd.c
new file mode 100644 (file)
index 0000000..7aaaffb
--- /dev/null
@@ -0,0 +1,722 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/*
+ * Copyright (c) 2013-2018, Mellanox Technologies inc.  All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include "mlx5_ib.h"
+#include "srq.h"
+
+static int get_pas_size(struct mlx5_srq_attr *in)
+{
+       u32 log_page_size = in->log_page_size + 12;
+       u32 log_srq_size  = in->log_size;
+       u32 log_rq_stride = in->wqe_shift;
+       u32 page_offset   = in->page_offset;
+       u32 po_quanta     = 1 << (log_page_size - 6);
+       u32 rq_sz         = 1 << (log_srq_size + 4 + log_rq_stride);
+       u32 page_size     = 1 << log_page_size;
+       u32 rq_sz_po      = rq_sz + (page_offset * po_quanta);
+       u32 rq_num_pas    = DIV_ROUND_UP(rq_sz_po, page_size);
+
+       return rq_num_pas * sizeof(u64);
+}
+
+static void set_wq(void *wq, struct mlx5_srq_attr *in)
+{
+       MLX5_SET(wq,   wq, wq_signature,  !!(in->flags
+                & MLX5_SRQ_FLAG_WQ_SIG));
+       MLX5_SET(wq,   wq, log_wq_pg_sz,  in->log_page_size);
+       MLX5_SET(wq,   wq, log_wq_stride, in->wqe_shift + 4);
+       MLX5_SET(wq,   wq, log_wq_sz,     in->log_size);
+       MLX5_SET(wq,   wq, page_offset,   in->page_offset);
+       MLX5_SET(wq,   wq, lwm,           in->lwm);
+       MLX5_SET(wq,   wq, pd,            in->pd);
+       MLX5_SET64(wq, wq, dbr_addr,      in->db_record);
+}
+
+static void set_srqc(void *srqc, struct mlx5_srq_attr *in)
+{
+       MLX5_SET(srqc,   srqc, wq_signature,  !!(in->flags
+                & MLX5_SRQ_FLAG_WQ_SIG));
+       MLX5_SET(srqc,   srqc, log_page_size, in->log_page_size);
+       MLX5_SET(srqc,   srqc, log_rq_stride, in->wqe_shift);
+       MLX5_SET(srqc,   srqc, log_srq_size,  in->log_size);
+       MLX5_SET(srqc,   srqc, page_offset,   in->page_offset);
+       MLX5_SET(srqc,   srqc, lwm,           in->lwm);
+       MLX5_SET(srqc,   srqc, pd,            in->pd);
+       MLX5_SET64(srqc, srqc, dbr_addr,      in->db_record);
+       MLX5_SET(srqc,   srqc, xrcd,          in->xrcd);
+       MLX5_SET(srqc,   srqc, cqn,           in->cqn);
+}
+
+static void get_wq(void *wq, struct mlx5_srq_attr *in)
+{
+       if (MLX5_GET(wq, wq, wq_signature))
+               in->flags &= MLX5_SRQ_FLAG_WQ_SIG;
+       in->log_page_size = MLX5_GET(wq,   wq, log_wq_pg_sz);
+       in->wqe_shift     = MLX5_GET(wq,   wq, log_wq_stride) - 4;
+       in->log_size      = MLX5_GET(wq,   wq, log_wq_sz);
+       in->page_offset   = MLX5_GET(wq,   wq, page_offset);
+       in->lwm           = MLX5_GET(wq,   wq, lwm);
+       in->pd            = MLX5_GET(wq,   wq, pd);
+       in->db_record     = MLX5_GET64(wq, wq, dbr_addr);
+}
+
+static void get_srqc(void *srqc, struct mlx5_srq_attr *in)
+{
+       if (MLX5_GET(srqc, srqc, wq_signature))
+               in->flags &= MLX5_SRQ_FLAG_WQ_SIG;
+       in->log_page_size = MLX5_GET(srqc,   srqc, log_page_size);
+       in->wqe_shift     = MLX5_GET(srqc,   srqc, log_rq_stride);
+       in->log_size      = MLX5_GET(srqc,   srqc, log_srq_size);
+       in->page_offset   = MLX5_GET(srqc,   srqc, page_offset);
+       in->lwm           = MLX5_GET(srqc,   srqc, lwm);
+       in->pd            = MLX5_GET(srqc,   srqc, pd);
+       in->db_record     = MLX5_GET64(srqc, srqc, dbr_addr);
+}
+
+struct mlx5_core_srq *mlx5_cmd_get_srq(struct mlx5_ib_dev *dev, u32 srqn)
+{
+       struct mlx5_srq_table *table = &dev->srq_table;
+       struct mlx5_core_srq *srq;
+
+       spin_lock(&table->lock);
+
+       srq = radix_tree_lookup(&table->tree, srqn);
+       if (srq)
+               atomic_inc(&srq->refcount);
+
+       spin_unlock(&table->lock);
+
+       return srq;
+}
+
+static int create_srq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                         struct mlx5_srq_attr *in)
+{
+       u32 create_out[MLX5_ST_SZ_DW(create_srq_out)] = {0};
+       void *create_in;
+       void *srqc;
+       void *pas;
+       int pas_size;
+       int inlen;
+       int err;
+
+       pas_size  = get_pas_size(in);
+       inlen     = MLX5_ST_SZ_BYTES(create_srq_in) + pas_size;
+       create_in = kvzalloc(inlen, GFP_KERNEL);
+       if (!create_in)
+               return -ENOMEM;
+
+       MLX5_SET(create_srq_in, create_in, uid, in->uid);
+       srqc = MLX5_ADDR_OF(create_srq_in, create_in, srq_context_entry);
+       pas = MLX5_ADDR_OF(create_srq_in, create_in, pas);
+
+       set_srqc(srqc, in);
+       memcpy(pas, in->pas, pas_size);
+
+       MLX5_SET(create_srq_in, create_in, opcode,
+                MLX5_CMD_OP_CREATE_SRQ);
+
+       err = mlx5_cmd_exec(dev->mdev, create_in, inlen, create_out,
+                           sizeof(create_out));
+       kvfree(create_in);
+       if (!err) {
+               srq->srqn = MLX5_GET(create_srq_out, create_out, srqn);
+               srq->uid = in->uid;
+       }
+
+       return err;
+}
+
+static int destroy_srq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
+{
+       u32 srq_in[MLX5_ST_SZ_DW(destroy_srq_in)] = {0};
+       u32 srq_out[MLX5_ST_SZ_DW(destroy_srq_out)] = {0};
+
+       MLX5_SET(destroy_srq_in, srq_in, opcode,
+                MLX5_CMD_OP_DESTROY_SRQ);
+       MLX5_SET(destroy_srq_in, srq_in, srqn, srq->srqn);
+       MLX5_SET(destroy_srq_in, srq_in, uid, srq->uid);
+
+       return mlx5_cmd_exec(dev->mdev, srq_in, sizeof(srq_in), srq_out,
+                            sizeof(srq_out));
+}
+
+static int arm_srq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                      u16 lwm, int is_srq)
+{
+       u32 srq_in[MLX5_ST_SZ_DW(arm_rq_in)] = {0};
+       u32 srq_out[MLX5_ST_SZ_DW(arm_rq_out)] = {0};
+
+       MLX5_SET(arm_rq_in, srq_in, opcode, MLX5_CMD_OP_ARM_RQ);
+       MLX5_SET(arm_rq_in, srq_in, op_mod, MLX5_ARM_RQ_IN_OP_MOD_SRQ);
+       MLX5_SET(arm_rq_in, srq_in, srq_number, srq->srqn);
+       MLX5_SET(arm_rq_in, srq_in, lwm,      lwm);
+       MLX5_SET(arm_rq_in, srq_in, uid, srq->uid);
+
+       return mlx5_cmd_exec(dev->mdev, srq_in, sizeof(srq_in), srq_out,
+                            sizeof(srq_out));
+}
+
+static int query_srq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                        struct mlx5_srq_attr *out)
+{
+       u32 srq_in[MLX5_ST_SZ_DW(query_srq_in)] = {0};
+       u32 *srq_out;
+       void *srqc;
+       int err;
+
+       srq_out = kvzalloc(MLX5_ST_SZ_BYTES(query_srq_out), GFP_KERNEL);
+       if (!srq_out)
+               return -ENOMEM;
+
+       MLX5_SET(query_srq_in, srq_in, opcode,
+                MLX5_CMD_OP_QUERY_SRQ);
+       MLX5_SET(query_srq_in, srq_in, srqn, srq->srqn);
+       err = mlx5_cmd_exec(dev->mdev, srq_in, sizeof(srq_in), srq_out,
+                           MLX5_ST_SZ_BYTES(query_srq_out));
+       if (err)
+               goto out;
+
+       srqc = MLX5_ADDR_OF(query_srq_out, srq_out, srq_context_entry);
+       get_srqc(srqc, out);
+       if (MLX5_GET(srqc, srqc, state) != MLX5_SRQC_STATE_GOOD)
+               out->flags |= MLX5_SRQ_FLAG_ERR;
+out:
+       kvfree(srq_out);
+       return err;
+}
+
+static int create_xrc_srq_cmd(struct mlx5_ib_dev *dev,
+                             struct mlx5_core_srq *srq,
+                             struct mlx5_srq_attr *in)
+{
+       u32 create_out[MLX5_ST_SZ_DW(create_xrc_srq_out)];
+       void *create_in;
+       void *xrc_srqc;
+       void *pas;
+       int pas_size;
+       int inlen;
+       int err;
+
+       pas_size  = get_pas_size(in);
+       inlen     = MLX5_ST_SZ_BYTES(create_xrc_srq_in) + pas_size;
+       create_in = kvzalloc(inlen, GFP_KERNEL);
+       if (!create_in)
+               return -ENOMEM;
+
+       MLX5_SET(create_xrc_srq_in, create_in, uid, in->uid);
+       xrc_srqc = MLX5_ADDR_OF(create_xrc_srq_in, create_in,
+                               xrc_srq_context_entry);
+       pas      = MLX5_ADDR_OF(create_xrc_srq_in, create_in, pas);
+
+       set_srqc(xrc_srqc, in);
+       MLX5_SET(xrc_srqc, xrc_srqc, user_index, in->user_index);
+       memcpy(pas, in->pas, pas_size);
+       MLX5_SET(create_xrc_srq_in, create_in, opcode,
+                MLX5_CMD_OP_CREATE_XRC_SRQ);
+
+       memset(create_out, 0, sizeof(create_out));
+       err = mlx5_cmd_exec(dev->mdev, create_in, inlen, create_out,
+                           sizeof(create_out));
+       if (err)
+               goto out;
+
+       srq->srqn = MLX5_GET(create_xrc_srq_out, create_out, xrc_srqn);
+       srq->uid = in->uid;
+out:
+       kvfree(create_in);
+       return err;
+}
+
+static int destroy_xrc_srq_cmd(struct mlx5_ib_dev *dev,
+                              struct mlx5_core_srq *srq)
+{
+       u32 xrcsrq_in[MLX5_ST_SZ_DW(destroy_xrc_srq_in)]   = {0};
+       u32 xrcsrq_out[MLX5_ST_SZ_DW(destroy_xrc_srq_out)] = {0};
+
+       MLX5_SET(destroy_xrc_srq_in, xrcsrq_in, opcode,
+                MLX5_CMD_OP_DESTROY_XRC_SRQ);
+       MLX5_SET(destroy_xrc_srq_in, xrcsrq_in, xrc_srqn, srq->srqn);
+       MLX5_SET(destroy_xrc_srq_in, xrcsrq_in, uid, srq->uid);
+
+       return mlx5_cmd_exec(dev->mdev, xrcsrq_in, sizeof(xrcsrq_in),
+                            xrcsrq_out, sizeof(xrcsrq_out));
+}
+
+static int arm_xrc_srq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                          u16 lwm)
+{
+       u32 xrcsrq_in[MLX5_ST_SZ_DW(arm_xrc_srq_in)]   = {0};
+       u32 xrcsrq_out[MLX5_ST_SZ_DW(arm_xrc_srq_out)] = {0};
+
+       MLX5_SET(arm_xrc_srq_in, xrcsrq_in, opcode,   MLX5_CMD_OP_ARM_XRC_SRQ);
+       MLX5_SET(arm_xrc_srq_in, xrcsrq_in, op_mod,   MLX5_ARM_XRC_SRQ_IN_OP_MOD_XRC_SRQ);
+       MLX5_SET(arm_xrc_srq_in, xrcsrq_in, xrc_srqn, srq->srqn);
+       MLX5_SET(arm_xrc_srq_in, xrcsrq_in, lwm,      lwm);
+       MLX5_SET(arm_xrc_srq_in, xrcsrq_in, uid, srq->uid);
+
+       return  mlx5_cmd_exec(dev->mdev, xrcsrq_in, sizeof(xrcsrq_in),
+                             xrcsrq_out, sizeof(xrcsrq_out));
+}
+
+static int query_xrc_srq_cmd(struct mlx5_ib_dev *dev,
+                            struct mlx5_core_srq *srq,
+                            struct mlx5_srq_attr *out)
+{
+       u32 xrcsrq_in[MLX5_ST_SZ_DW(query_xrc_srq_in)];
+       u32 *xrcsrq_out;
+       void *xrc_srqc;
+       int err;
+
+       xrcsrq_out = kvzalloc(MLX5_ST_SZ_BYTES(query_xrc_srq_out), GFP_KERNEL);
+       if (!xrcsrq_out)
+               return -ENOMEM;
+       memset(xrcsrq_in, 0, sizeof(xrcsrq_in));
+
+       MLX5_SET(query_xrc_srq_in, xrcsrq_in, opcode,
+                MLX5_CMD_OP_QUERY_XRC_SRQ);
+       MLX5_SET(query_xrc_srq_in, xrcsrq_in, xrc_srqn, srq->srqn);
+
+       err = mlx5_cmd_exec(dev->mdev, xrcsrq_in, sizeof(xrcsrq_in),
+                           xrcsrq_out, MLX5_ST_SZ_BYTES(query_xrc_srq_out));
+       if (err)
+               goto out;
+
+       xrc_srqc = MLX5_ADDR_OF(query_xrc_srq_out, xrcsrq_out,
+                               xrc_srq_context_entry);
+       get_srqc(xrc_srqc, out);
+       if (MLX5_GET(xrc_srqc, xrc_srqc, state) != MLX5_XRC_SRQC_STATE_GOOD)
+               out->flags |= MLX5_SRQ_FLAG_ERR;
+
+out:
+       kvfree(xrcsrq_out);
+       return err;
+}
+
+static int create_rmp_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                         struct mlx5_srq_attr *in)
+{
+       void *create_out = NULL;
+       void *create_in = NULL;
+       void *rmpc;
+       void *wq;
+       int pas_size;
+       int outlen;
+       int inlen;
+       int err;
+
+       pas_size = get_pas_size(in);
+       inlen = MLX5_ST_SZ_BYTES(create_rmp_in) + pas_size;
+       outlen = MLX5_ST_SZ_BYTES(create_rmp_out);
+       create_in = kvzalloc(inlen, GFP_KERNEL);
+       create_out = kvzalloc(outlen, GFP_KERNEL);
+       if (!create_in || !create_out) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       rmpc = MLX5_ADDR_OF(create_rmp_in, create_in, ctx);
+       wq = MLX5_ADDR_OF(rmpc, rmpc, wq);
+
+       MLX5_SET(rmpc, rmpc, state, MLX5_RMPC_STATE_RDY);
+       MLX5_SET(create_rmp_in, create_in, uid, in->uid);
+       set_wq(wq, in);
+       memcpy(MLX5_ADDR_OF(rmpc, rmpc, wq.pas), in->pas, pas_size);
+
+       MLX5_SET(create_rmp_in, create_in, opcode, MLX5_CMD_OP_CREATE_RMP);
+       err = mlx5_cmd_exec(dev->mdev, create_in, inlen, create_out, outlen);
+       if (!err) {
+               srq->srqn = MLX5_GET(create_rmp_out, create_out, rmpn);
+               srq->uid = in->uid;
+       }
+
+out:
+       kvfree(create_in);
+       kvfree(create_out);
+       return err;
+}
+
+static int destroy_rmp_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
+{
+       u32 in[MLX5_ST_SZ_DW(destroy_rmp_in)]   = {};
+       u32 out[MLX5_ST_SZ_DW(destroy_rmp_out)] = {};
+
+       MLX5_SET(destroy_rmp_in, in, opcode, MLX5_CMD_OP_DESTROY_RMP);
+       MLX5_SET(destroy_rmp_in, in, rmpn, srq->srqn);
+       MLX5_SET(destroy_rmp_in, in, uid, srq->uid);
+       return mlx5_cmd_exec(dev->mdev, in, sizeof(in), out, sizeof(out));
+}
+
+static int arm_rmp_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                      u16 lwm)
+{
+       void *out = NULL;
+       void *in = NULL;
+       void *rmpc;
+       void *wq;
+       void *bitmask;
+       int outlen;
+       int inlen;
+       int err;
+
+       inlen = MLX5_ST_SZ_BYTES(modify_rmp_in);
+       outlen = MLX5_ST_SZ_BYTES(modify_rmp_out);
+
+       in = kvzalloc(inlen, GFP_KERNEL);
+       out = kvzalloc(outlen, GFP_KERNEL);
+       if (!in || !out) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       rmpc =    MLX5_ADDR_OF(modify_rmp_in,   in,   ctx);
+       bitmask = MLX5_ADDR_OF(modify_rmp_in,   in,   bitmask);
+       wq   =    MLX5_ADDR_OF(rmpc,            rmpc, wq);
+
+       MLX5_SET(modify_rmp_in, in,      rmp_state, MLX5_RMPC_STATE_RDY);
+       MLX5_SET(modify_rmp_in, in,      rmpn,      srq->srqn);
+       MLX5_SET(modify_rmp_in, in, uid, srq->uid);
+       MLX5_SET(wq,            wq,      lwm,       lwm);
+       MLX5_SET(rmp_bitmask,   bitmask, lwm,       1);
+       MLX5_SET(rmpc, rmpc, state, MLX5_RMPC_STATE_RDY);
+       MLX5_SET(modify_rmp_in, in, opcode, MLX5_CMD_OP_MODIFY_RMP);
+
+       err = mlx5_cmd_exec(dev->mdev, in, inlen, out, outlen);
+
+out:
+       kvfree(in);
+       kvfree(out);
+       return err;
+}
+
+static int query_rmp_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                        struct mlx5_srq_attr *out)
+{
+       u32 *rmp_out = NULL;
+       u32 *rmp_in = NULL;
+       void *rmpc;
+       int outlen;
+       int inlen;
+       int err;
+
+       outlen = MLX5_ST_SZ_BYTES(query_rmp_out);
+       inlen = MLX5_ST_SZ_BYTES(query_rmp_in);
+
+       rmp_out = kvzalloc(outlen, GFP_KERNEL);
+       rmp_in = kvzalloc(inlen, GFP_KERNEL);
+       if (!rmp_out || !rmp_in) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       MLX5_SET(query_rmp_in, rmp_in, opcode, MLX5_CMD_OP_QUERY_RMP);
+       MLX5_SET(query_rmp_in, rmp_in, rmpn,   srq->srqn);
+       err = mlx5_cmd_exec(dev->mdev, rmp_in, inlen, rmp_out, outlen);
+       if (err)
+               goto out;
+
+       rmpc = MLX5_ADDR_OF(query_rmp_out, rmp_out, rmp_context);
+       get_wq(MLX5_ADDR_OF(rmpc, rmpc, wq), out);
+       if (MLX5_GET(rmpc, rmpc, state) != MLX5_RMPC_STATE_RDY)
+               out->flags |= MLX5_SRQ_FLAG_ERR;
+
+out:
+       kvfree(rmp_out);
+       kvfree(rmp_in);
+       return err;
+}
+
+static int create_xrq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                         struct mlx5_srq_attr *in)
+{
+       u32 create_out[MLX5_ST_SZ_DW(create_xrq_out)] = {0};
+       void *create_in;
+       void *xrqc;
+       void *wq;
+       int pas_size;
+       int inlen;
+       int err;
+
+       pas_size = get_pas_size(in);
+       inlen = MLX5_ST_SZ_BYTES(create_xrq_in) + pas_size;
+       create_in = kvzalloc(inlen, GFP_KERNEL);
+       if (!create_in)
+               return -ENOMEM;
+
+       xrqc = MLX5_ADDR_OF(create_xrq_in, create_in, xrq_context);
+       wq = MLX5_ADDR_OF(xrqc, xrqc, wq);
+
+       set_wq(wq, in);
+       memcpy(MLX5_ADDR_OF(xrqc, xrqc, wq.pas), in->pas, pas_size);
+
+       if (in->type == IB_SRQT_TM) {
+               MLX5_SET(xrqc, xrqc, topology, MLX5_XRQC_TOPOLOGY_TAG_MATCHING);
+               if (in->flags & MLX5_SRQ_FLAG_RNDV)
+                       MLX5_SET(xrqc, xrqc, offload, MLX5_XRQC_OFFLOAD_RNDV);
+               MLX5_SET(xrqc, xrqc,
+                        tag_matching_topology_context.log_matching_list_sz,
+                        in->tm_log_list_size);
+       }
+       MLX5_SET(xrqc, xrqc, user_index, in->user_index);
+       MLX5_SET(xrqc, xrqc, cqn, in->cqn);
+       MLX5_SET(create_xrq_in, create_in, opcode, MLX5_CMD_OP_CREATE_XRQ);
+       MLX5_SET(create_xrq_in, create_in, uid, in->uid);
+       err = mlx5_cmd_exec(dev->mdev, create_in, inlen, create_out,
+                           sizeof(create_out));
+       kvfree(create_in);
+       if (!err) {
+               srq->srqn = MLX5_GET(create_xrq_out, create_out, xrqn);
+               srq->uid = in->uid;
+       }
+
+       return err;
+}
+
+static int destroy_xrq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
+{
+       u32 in[MLX5_ST_SZ_DW(destroy_xrq_in)] = {0};
+       u32 out[MLX5_ST_SZ_DW(destroy_xrq_out)] = {0};
+
+       MLX5_SET(destroy_xrq_in, in, opcode, MLX5_CMD_OP_DESTROY_XRQ);
+       MLX5_SET(destroy_xrq_in, in, xrqn,   srq->srqn);
+       MLX5_SET(destroy_xrq_in, in, uid, srq->uid);
+
+       return mlx5_cmd_exec(dev->mdev, in, sizeof(in), out, sizeof(out));
+}
+
+static int arm_xrq_cmd(struct mlx5_ib_dev *dev,
+                      struct mlx5_core_srq *srq,
+                      u16 lwm)
+{
+       u32 out[MLX5_ST_SZ_DW(arm_rq_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(arm_rq_in)] = {0};
+
+       MLX5_SET(arm_rq_in, in, opcode,     MLX5_CMD_OP_ARM_RQ);
+       MLX5_SET(arm_rq_in, in, op_mod,     MLX5_ARM_RQ_IN_OP_MOD_XRQ);
+       MLX5_SET(arm_rq_in, in, srq_number, srq->srqn);
+       MLX5_SET(arm_rq_in, in, lwm,        lwm);
+       MLX5_SET(arm_rq_in, in, uid, srq->uid);
+
+       return mlx5_cmd_exec(dev->mdev, in, sizeof(in), out, sizeof(out));
+}
+
+static int query_xrq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                        struct mlx5_srq_attr *out)
+{
+       u32 in[MLX5_ST_SZ_DW(query_xrq_in)] = {0};
+       u32 *xrq_out;
+       int outlen = MLX5_ST_SZ_BYTES(query_xrq_out);
+       void *xrqc;
+       int err;
+
+       xrq_out = kvzalloc(outlen, GFP_KERNEL);
+       if (!xrq_out)
+               return -ENOMEM;
+
+       MLX5_SET(query_xrq_in, in, opcode, MLX5_CMD_OP_QUERY_XRQ);
+       MLX5_SET(query_xrq_in, in, xrqn, srq->srqn);
+
+       err = mlx5_cmd_exec(dev->mdev, in, sizeof(in), xrq_out, outlen);
+       if (err)
+               goto out;
+
+       xrqc = MLX5_ADDR_OF(query_xrq_out, xrq_out, xrq_context);
+       get_wq(MLX5_ADDR_OF(xrqc, xrqc, wq), out);
+       if (MLX5_GET(xrqc, xrqc, state) != MLX5_XRQC_STATE_GOOD)
+               out->flags |= MLX5_SRQ_FLAG_ERR;
+       out->tm_next_tag =
+               MLX5_GET(xrqc, xrqc,
+                        tag_matching_topology_context.append_next_index);
+       out->tm_hw_phase_cnt =
+               MLX5_GET(xrqc, xrqc,
+                        tag_matching_topology_context.hw_phase_cnt);
+       out->tm_sw_phase_cnt =
+               MLX5_GET(xrqc, xrqc,
+                        tag_matching_topology_context.sw_phase_cnt);
+
+out:
+       kvfree(xrq_out);
+       return err;
+}
+
+static int create_srq_split(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                           struct mlx5_srq_attr *in)
+{
+       if (!dev->mdev->issi)
+               return create_srq_cmd(dev, srq, in);
+       switch (srq->common.res) {
+       case MLX5_RES_XSRQ:
+               return create_xrc_srq_cmd(dev, srq, in);
+       case MLX5_RES_XRQ:
+               return create_xrq_cmd(dev, srq, in);
+       default:
+               return create_rmp_cmd(dev, srq, in);
+       }
+}
+
+static int destroy_srq_split(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
+{
+       if (!dev->mdev->issi)
+               return destroy_srq_cmd(dev, srq);
+       switch (srq->common.res) {
+       case MLX5_RES_XSRQ:
+               return destroy_xrc_srq_cmd(dev, srq);
+       case MLX5_RES_XRQ:
+               return destroy_xrq_cmd(dev, srq);
+       default:
+               return destroy_rmp_cmd(dev, srq);
+       }
+}
+
+int mlx5_cmd_create_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                       struct mlx5_srq_attr *in)
+{
+       struct mlx5_srq_table *table = &dev->srq_table;
+       int err;
+
+       switch (in->type) {
+       case IB_SRQT_XRC:
+               srq->common.res = MLX5_RES_XSRQ;
+               break;
+       case IB_SRQT_TM:
+               srq->common.res = MLX5_RES_XRQ;
+               break;
+       default:
+               srq->common.res = MLX5_RES_SRQ;
+       }
+
+       err = create_srq_split(dev, srq, in);
+       if (err)
+               return err;
+
+       atomic_set(&srq->refcount, 1);
+       init_completion(&srq->free);
+
+       spin_lock_irq(&table->lock);
+       err = radix_tree_insert(&table->tree, srq->srqn, srq);
+       spin_unlock_irq(&table->lock);
+       if (err)
+               goto err_destroy_srq_split;
+
+       return 0;
+
+err_destroy_srq_split:
+       destroy_srq_split(dev, srq);
+
+       return err;
+}
+
+int mlx5_cmd_destroy_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
+{
+       struct mlx5_srq_table *table = &dev->srq_table;
+       struct mlx5_core_srq *tmp;
+       int err;
+
+       spin_lock_irq(&table->lock);
+       tmp = radix_tree_delete(&table->tree, srq->srqn);
+       spin_unlock_irq(&table->lock);
+       if (!tmp || tmp != srq)
+               return -EINVAL;
+
+       err = destroy_srq_split(dev, srq);
+       if (err)
+               return err;
+
+       if (atomic_dec_and_test(&srq->refcount))
+               complete(&srq->free);
+       wait_for_completion(&srq->free);
+
+       return 0;
+}
+
+int mlx5_cmd_query_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                      struct mlx5_srq_attr *out)
+{
+       if (!dev->mdev->issi)
+               return query_srq_cmd(dev, srq, out);
+       switch (srq->common.res) {
+       case MLX5_RES_XSRQ:
+               return query_xrc_srq_cmd(dev, srq, out);
+       case MLX5_RES_XRQ:
+               return query_xrq_cmd(dev, srq, out);
+       default:
+               return query_rmp_cmd(dev, srq, out);
+       }
+}
+
+int mlx5_cmd_arm_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
+                    u16 lwm, int is_srq)
+{
+       if (!dev->mdev->issi)
+               return arm_srq_cmd(dev, srq, lwm, is_srq);
+       switch (srq->common.res) {
+       case MLX5_RES_XSRQ:
+               return arm_xrc_srq_cmd(dev, srq, lwm);
+       case MLX5_RES_XRQ:
+               return arm_xrq_cmd(dev, srq, lwm);
+       default:
+               return arm_rmp_cmd(dev, srq, lwm);
+       }
+}
+
+static int srq_event_notifier(struct notifier_block *nb,
+                             unsigned long type, void *data)
+{
+       struct mlx5_srq_table *table;
+       struct mlx5_core_srq *srq;
+       struct mlx5_eqe *eqe;
+       u32 srqn;
+
+       if (type != MLX5_EVENT_TYPE_SRQ_CATAS_ERROR &&
+           type != MLX5_EVENT_TYPE_SRQ_RQ_LIMIT)
+               return NOTIFY_DONE;
+
+       table = container_of(nb, struct mlx5_srq_table, nb);
+
+       eqe = data;
+       srqn = be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff;
+
+       spin_lock(&table->lock);
+
+       srq = radix_tree_lookup(&table->tree, srqn);
+       if (srq)
+               atomic_inc(&srq->refcount);
+
+       spin_unlock(&table->lock);
+
+       if (!srq)
+               return NOTIFY_OK;
+
+       srq->event(srq, eqe->type);
+
+       if (atomic_dec_and_test(&srq->refcount))
+               complete(&srq->free);
+
+       return NOTIFY_OK;
+}
+
+int mlx5_init_srq_table(struct mlx5_ib_dev *dev)
+{
+       struct mlx5_srq_table *table = &dev->srq_table;
+
+       memset(table, 0, sizeof(*table));
+       spin_lock_init(&table->lock);
+       INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
+
+       table->nb.notifier_call = srq_event_notifier;
+       mlx5_notifier_register(dev->mdev, &table->nb);
+
+       return 0;
+}
+
+void mlx5_cleanup_srq_table(struct mlx5_ib_dev *dev)
+{
+       struct mlx5_srq_table *table = &dev->srq_table;
+
+       mlx5_notifier_unregister(dev->mdev, &table->nb);
+}
index fc0c191..cc4dce5 100644 (file)
@@ -551,14 +551,14 @@ static void queue_fpdus(struct sk_buff *skb, struct nes_vnic *nesvnic, struct ne
 
        /* Queue skb by sequence number */
        if (skb_queue_len(&nesqp->pau_list) == 0) {
-               skb_queue_head(&nesqp->pau_list, skb);
+               __skb_queue_head(&nesqp->pau_list, skb);
        } else {
                skb_queue_walk(&nesqp->pau_list, tmpskb) {
                        cb = (struct nes_rskb_cb *)&tmpskb->cb[0];
                        if (before(seqnum, cb->seqnum))
                                break;
                }
-               skb_insert(tmpskb, skb, &nesqp->pau_list);
+               __skb_insert(skb, tmpskb->prev, tmpskb, &nesqp->pau_list);
        }
        if (nesqp->pau_state == PAU_READY)
                process_it = true;
index 89ec0f6..084bb4b 100644 (file)
@@ -91,13 +91,15 @@ EXPORT_SYMBOL(rvt_check_ah);
  * rvt_create_ah - create an address handle
  * @pd: the protection domain
  * @ah_attr: the attributes of the AH
+ * @udata: pointer to user's input output buffer information.
  *
  * This may be called from interrupt context.
  *
  * Return: newly allocated ah
  */
 struct ib_ah *rvt_create_ah(struct ib_pd *pd,
-                           struct rdma_ah_attr *ah_attr)
+                           struct rdma_ah_attr *ah_attr,
+                           struct ib_udata *udata)
 {
        struct rvt_ah *ah;
        struct rvt_dev_info *dev = ib_to_rvt(pd->device);
index 16105af..25271b4 100644 (file)
@@ -51,7 +51,8 @@
 #include <rdma/rdma_vt.h>
 
 struct ib_ah *rvt_create_ah(struct ib_pd *pd,
-                           struct rdma_ah_attr *ah_attr);
+                           struct rdma_ah_attr *ah_attr,
+                           struct ib_udata *udata);
 int rvt_destroy_ah(struct ib_ah *ibah);
 int rvt_modify_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr);
 int rvt_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr);
index 8710214..6214d8c 100644 (file)
@@ -167,7 +167,7 @@ int ipoib_open(struct net_device *dev)
                        if (flags & IFF_UP)
                                continue;
 
-                       dev_change_flags(cpriv->dev, flags | IFF_UP);
+                       dev_change_flags(cpriv->dev, flags | IFF_UP, NULL);
                }
                up_read(&priv->vlan_rwsem);
        }
@@ -207,7 +207,7 @@ static int ipoib_stop(struct net_device *dev)
                        if (!(flags & IFF_UP))
                                continue;
 
-                       dev_change_flags(cpriv->dev, flags & ~IFF_UP);
+                       dev_change_flags(cpriv->dev, flags & ~IFF_UP, NULL);
                }
                up_read(&priv->vlan_rwsem);
        }
@@ -1823,7 +1823,7 @@ static void ipoib_parent_unregister_pre(struct net_device *ndev)
         * running ensures the it will not add more work.
         */
        rtnl_lock();
-       dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP);
+       dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP, NULL);
        rtnl_unlock();
 
        /* ipoib_event() cannot be running once this returns */
index 946b623..4ff3d98 100644 (file)
@@ -1124,7 +1124,9 @@ u8 iser_check_task_pi_status(struct iscsi_iser_task *iser_task,
                                         IB_MR_CHECK_SIG_STATUS, &mr_status);
                if (ret) {
                        pr_err("ib_check_mr_status failed, ret %d\n", ret);
-                       goto err;
+                       /* Not a lot we can do, return ambiguous guard error */
+                       *sector = 0;
+                       return 0x1;
                }
 
                if (mr_status.fail_status & IB_MR_CHECK_SIG_STATUS) {
@@ -1152,9 +1154,6 @@ u8 iser_check_task_pi_status(struct iscsi_iser_task *iser_task,
        }
 
        return 0;
-err:
-       /* Not alot we can do here, return ambiguous guard error */
-       return 0x1;
 }
 
 void iser_err_comp(struct ib_wc *wc, const char *type)
index d4b9db4..cfc8b94 100644 (file)
@@ -480,18 +480,18 @@ static const u8 xboxone_hori_init[] = {
 };
 
 /*
- * This packet is required for some of the PDP pads to start
+ * This packet is required for most (all?) of the PDP pads to start
  * sending input reports. These pads include: (0x0e6f:0x02ab),
- * (0x0e6f:0x02a4).
+ * (0x0e6f:0x02a4), (0x0e6f:0x02a6).
  */
 static const u8 xboxone_pdp_init1[] = {
        0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
 };
 
 /*
- * This packet is required for some of the PDP pads to start
+ * This packet is required for most (all?) of the PDP pads to start
  * sending input reports. These pads include: (0x0e6f:0x02ab),
- * (0x0e6f:0x02a4).
+ * (0x0e6f:0x02a4), (0x0e6f:0x02a6).
  */
 static const u8 xboxone_pdp_init2[] = {
        0x06, 0x20, 0x00, 0x02, 0x01, 0x00
@@ -527,12 +527,8 @@ static const struct xboxone_init_packet xboxone_init_packets[] = {
        XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_init),
        XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_init),
        XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init),
-       XBOXONE_INIT_PKT(0x0e6f, 0x02ab, xboxone_pdp_init1),
-       XBOXONE_INIT_PKT(0x0e6f, 0x02ab, xboxone_pdp_init2),
-       XBOXONE_INIT_PKT(0x0e6f, 0x02a4, xboxone_pdp_init1),
-       XBOXONE_INIT_PKT(0x0e6f, 0x02a4, xboxone_pdp_init2),
-       XBOXONE_INIT_PKT(0x0e6f, 0x02a6, xboxone_pdp_init1),
-       XBOXONE_INIT_PKT(0x0e6f, 0x02a6, xboxone_pdp_init2),
+       XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init1),
+       XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init2),
        XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init),
        XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init),
        XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init),
index 7e75835..850bb25 100644 (file)
@@ -841,7 +841,7 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra
        if (param[0] != 3) {
                param[0] = 2;
                if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
-               return 2;
+                       return 2;
        }
 
        ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MBR);
index 81be6f7..d560011 100644 (file)
@@ -493,7 +493,8 @@ static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev)
        for (i = 0; i < ARRAY_SIZE(cros_ec_keyb_bs); i++) {
                const struct cros_ec_bs_map *map = &cros_ec_keyb_bs[i];
 
-               if (buttons & BIT(map->bit))
+               if ((map->ev_type == EV_KEY && (buttons & BIT(map->bit))) ||
+                   (map->ev_type == EV_SW && (switches & BIT(map->bit))))
                        input_set_capability(idev, map->ev_type, map->code);
        }
 
index f51ae09..403452e 100644 (file)
@@ -407,7 +407,7 @@ matrix_keypad_parse_dt(struct device *dev)
        struct matrix_keypad_platform_data *pdata;
        struct device_node *np = dev->of_node;
        unsigned int *gpios;
-       int i, nrow, ncol;
+       int ret, i, nrow, ncol;
 
        if (!np) {
                dev_err(dev, "device lacks DT data\n");
@@ -452,12 +452,19 @@ matrix_keypad_parse_dt(struct device *dev)
                return ERR_PTR(-ENOMEM);
        }
 
-       for (i = 0; i < pdata->num_row_gpios; i++)
-               gpios[i] = of_get_named_gpio(np, "row-gpios", i);
+       for (i = 0; i < nrow; i++) {
+               ret = of_get_named_gpio(np, "row-gpios", i);
+               if (ret < 0)
+                       return ERR_PTR(ret);
+               gpios[i] = ret;
+       }
 
-       for (i = 0; i < pdata->num_col_gpios; i++)
-               gpios[pdata->num_row_gpios + i] =
-                       of_get_named_gpio(np, "col-gpios", i);
+       for (i = 0; i < ncol; i++) {
+               ret = of_get_named_gpio(np, "col-gpios", i);
+               if (ret < 0)
+                       return ERR_PTR(ret);
+               gpios[nrow + i] = ret;
+       }
 
        pdata->row_gpios = gpios;
        pdata->col_gpios = &gpios[pdata->num_row_gpios];
@@ -484,10 +491,8 @@ static int matrix_keypad_probe(struct platform_device *pdev)
        pdata = dev_get_platdata(&pdev->dev);
        if (!pdata) {
                pdata = matrix_keypad_parse_dt(&pdev->dev);
-               if (IS_ERR(pdata)) {
-                       dev_err(&pdev->dev, "no platform data defined\n");
+               if (IS_ERR(pdata))
                        return PTR_ERR(pdata);
-               }
        } else if (!pdata->keymap_data) {
                dev_err(&pdev->dev, "no keymap data defined\n");
                return -EINVAL;
index 4640634..a7dc286 100644 (file)
 
 /* OMAP4 values */
 #define OMAP4_VAL_IRQDISABLE           0x0
-#define OMAP4_VAL_DEBOUNCINGTIME       0x7
-#define OMAP4_VAL_PVT                  0x7
+
+/*
+ * Errata i689: If a key is released for a time shorter than debounce time,
+ * the keyboard will idle and never detect the key release. The workaround
+ * is to use at least a 12ms debounce time. See omap5432 TRM chapter
+ * "26.4.6.2 Keyboard Controller Timer" for more information.
+ */
+#define OMAP4_KEYPAD_PTV_DIV_128        0x6
+#define OMAP4_KEYPAD_DEBOUNCINGTIME_MS(dbms, ptv)     \
+       ((((dbms) * 1000) / ((1 << ((ptv) + 1)) * (1000000 / 32768))) - 1)
+#define OMAP4_VAL_DEBOUNCINGTIME_16MS                                  \
+       OMAP4_KEYPAD_DEBOUNCINGTIME_MS(16, OMAP4_KEYPAD_PTV_DIV_128)
 
 enum {
        KBD_REVISION_OMAP4 = 0,
@@ -181,9 +191,9 @@ static int omap4_keypad_open(struct input_dev *input)
 
        kbd_writel(keypad_data, OMAP4_KBD_CTRL,
                        OMAP4_DEF_CTRL_NOSOFTMODE |
-                       (OMAP4_VAL_PVT << OMAP4_DEF_CTRL_PTV_SHIFT));
+                       (OMAP4_KEYPAD_PTV_DIV_128 << OMAP4_DEF_CTRL_PTV_SHIFT));
        kbd_writel(keypad_data, OMAP4_KBD_DEBOUNCINGTIME,
-                       OMAP4_VAL_DEBOUNCINGTIME);
+                       OMAP4_VAL_DEBOUNCINGTIME_16MS);
        /* clear pending interrupts */
        kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
                         kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
index b0f9d19..a94b649 100644 (file)
@@ -1348,6 +1348,9 @@ static const struct acpi_device_id elan_acpi_id[] = {
        { "ELAN0618", 0 },
        { "ELAN061C", 0 },
        { "ELAN061D", 0 },
+       { "ELAN061E", 0 },
+       { "ELAN0620", 0 },
+       { "ELAN0621", 0 },
        { "ELAN0622", 0 },
        { "ELAN1000", 0 },
        { }
index 5e85f3c..2bd5bb1 100644 (file)
@@ -170,6 +170,7 @@ static const char * const smbus_pnp_ids[] = {
        "LEN0048", /* X1 Carbon 3 */
        "LEN0046", /* X250 */
        "LEN004a", /* W541 */
+       "LEN005b", /* P50 */
        "LEN0071", /* T480 */
        "LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */
        "LEN0073", /* X1 Carbon G5 (Elantech) */
@@ -177,6 +178,7 @@ static const char * const smbus_pnp_ids[] = {
        "LEN0096", /* X280 */
        "LEN0097", /* X280 -> ALPS trackpoint */
        "LEN200f", /* T450s */
+       "SYN3221", /* HP 15-ay000 */
        NULL
 };
 
index 47a0e81..a8b9be3 100644 (file)
@@ -177,7 +177,7 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev,
                 * state because the Enter-UP can trigger a wakeup at once.
                 */
                if (!(info & IS_BREAK))
-                       pm_wakeup_event(&hv_dev->device, 0);
+                       pm_wakeup_hard_event(&hv_dev->device);
 
                break;
 
index 02fb119..42d3fd7 100644 (file)
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Touch Screen driver for Renesas MIGO-R Platform
  *
  * Copyright (c) 2008 Magnus Damm
  * Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com>,
  *  Kenati Technologies Pvt Ltd.
- *
- * This file 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 file 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.
- *
- * You should have received a copy of the GNU General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #include <linux/module.h>
 #include <linux/kernel.h>
index b716739..11ff32c 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * ST1232 Touchscreen Controller Driver
  *
@@ -7,15 +8,6 @@
  * Using code from:
  *  - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c
  *     Copyright (C) 2007 Google, Inc.
- *
- * 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 <linux/delay.h>
@@ -295,4 +287,4 @@ module_i2c_driver(st1232_ts_driver);
 
 MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
 MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
index bb2cd29..d8f7000 100644 (file)
@@ -797,7 +797,8 @@ static int iommu_init_ga_log(struct amd_iommu *iommu)
        entry = iommu_virt_to_phys(iommu->ga_log) | GA_LOG_SIZE_512;
        memcpy_toio(iommu->mmio_base + MMIO_GA_LOG_BASE_OFFSET,
                    &entry, sizeof(entry));
-       entry = (iommu_virt_to_phys(iommu->ga_log) & 0xFFFFFFFFFFFFFULL) & ~7ULL;
+       entry = (iommu_virt_to_phys(iommu->ga_log_tail) &
+                (BIT_ULL(52)-1)) & ~7ULL;
        memcpy_toio(iommu->mmio_base + MMIO_GA_LOG_TAIL_OFFSET,
                    &entry, sizeof(entry));
        writel(0x00, iommu->mmio_base + MMIO_GA_HEAD_OFFSET);
index f3ccf02..41a4b88 100644 (file)
@@ -3075,7 +3075,7 @@ static int copy_context_table(struct intel_iommu *iommu,
                        }
 
                        if (old_ce)
-                               iounmap(old_ce);
+                               memunmap(old_ce);
 
                        ret = 0;
                        if (devfn < 0x80)
index db301ef..8871509 100644 (file)
@@ -595,7 +595,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
                        pr_err("%s: Page request without PASID: %08llx %08llx\n",
                               iommu->name, ((unsigned long long *)req)[0],
                               ((unsigned long long *)req)[1]);
-                       goto bad_req;
+                       goto no_pasid;
                }
 
                if (!svm || svm->pasid != req->pasid) {
index b98a031..ddf3a49 100644 (file)
@@ -498,6 +498,9 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain)
 
 static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain)
 {
+       if (!domain->mmu)
+               return;
+
        /*
         * Disable the context. Flush the TLB as required when modifying the
         * context registers.
index ea0e4c6..5b719b5 100644 (file)
@@ -274,7 +274,7 @@ hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type *bz, u_char *bdata, int count
        u_char *ptr, *ptr1, new_f2;
        struct sk_buff *skb;
        struct IsdnCardState *cs = bcs->cs;
-       int total, maxlen, new_z2;
+       int maxlen, new_z2;
        z_type *zp;
 
        if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
@@ -297,7 +297,6 @@ hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type *bz, u_char *bdata, int count
        } else if (!(skb = dev_alloc_skb(count - 3)))
                printk(KERN_WARNING "HFCPCI: receive out of memory\n");
        else {
-               total = count;
                count -= 3;
                ptr = skb_put(skb, count);
 
index 5936de7..6fc9383 100644 (file)
@@ -930,6 +930,10 @@ static int blocks_are_clean_separate_dirty(struct dm_cache_metadata *cmd,
        bool dirty_flag;
        *result = true;
 
+       if (from_cblock(cmd->cache_blocks) == 0)
+               /* Nothing to do */
+               return 0;
+
        r = dm_bitset_cursor_begin(&cmd->dirty_info, cmd->dirty_root,
                                   from_cblock(cmd->cache_blocks), &cmd->dirty_cursor);
        if (r) {
index 0bd8d49..dadd969 100644 (file)
@@ -195,7 +195,7 @@ static void throttle_unlock(struct throttle *t)
 struct dm_thin_new_mapping;
 
 /*
- * The pool runs in 4 modes.  Ordered in degraded order for comparisons.
+ * The pool runs in various modes.  Ordered in degraded order for comparisons.
  */
 enum pool_mode {
        PM_WRITE,               /* metadata may be changed */
@@ -282,9 +282,38 @@ struct pool {
        mempool_t mapping_pool;
 };
 
-static enum pool_mode get_pool_mode(struct pool *pool);
 static void metadata_operation_failed(struct pool *pool, const char *op, int r);
 
+static enum pool_mode get_pool_mode(struct pool *pool)
+{
+       return pool->pf.mode;
+}
+
+static void notify_of_pool_mode_change(struct pool *pool)
+{
+       const char *descs[] = {
+               "write",
+               "out-of-data-space",
+               "read-only",
+               "read-only",
+               "fail"
+       };
+       const char *extra_desc = NULL;
+       enum pool_mode mode = get_pool_mode(pool);
+
+       if (mode == PM_OUT_OF_DATA_SPACE) {
+               if (!pool->pf.error_if_no_space)
+                       extra_desc = " (queue IO)";
+               else
+                       extra_desc = " (error IO)";
+       }
+
+       dm_table_event(pool->ti->table);
+       DMINFO("%s: switching pool to %s%s mode",
+              dm_device_name(pool->pool_md),
+              descs[(int)mode], extra_desc ? : "");
+}
+
 /*
  * Target context for a pool.
  */
@@ -2351,8 +2380,6 @@ static void do_waker(struct work_struct *ws)
        queue_delayed_work(pool->wq, &pool->waker, COMMIT_PERIOD);
 }
 
-static void notify_of_pool_mode_change_to_oods(struct pool *pool);
-
 /*
  * We're holding onto IO to allow userland time to react.  After the
  * timeout either the pool will have been resized (and thus back in
@@ -2365,7 +2392,7 @@ static void do_no_space_timeout(struct work_struct *ws)
 
        if (get_pool_mode(pool) == PM_OUT_OF_DATA_SPACE && !pool->pf.error_if_no_space) {
                pool->pf.error_if_no_space = true;
-               notify_of_pool_mode_change_to_oods(pool);
+               notify_of_pool_mode_change(pool);
                error_retry_list_with_code(pool, BLK_STS_NOSPC);
        }
 }
@@ -2433,26 +2460,6 @@ static void noflush_work(struct thin_c *tc, void (*fn)(struct work_struct *))
 
 /*----------------------------------------------------------------*/
 
-static enum pool_mode get_pool_mode(struct pool *pool)
-{
-       return pool->pf.mode;
-}
-
-static void notify_of_pool_mode_change(struct pool *pool, const char *new_mode)
-{
-       dm_table_event(pool->ti->table);
-       DMINFO("%s: switching pool to %s mode",
-              dm_device_name(pool->pool_md), new_mode);
-}
-
-static void notify_of_pool_mode_change_to_oods(struct pool *pool)
-{
-       if (!pool->pf.error_if_no_space)
-               notify_of_pool_mode_change(pool, "out-of-data-space (queue IO)");
-       else
-               notify_of_pool_mode_change(pool, "out-of-data-space (error IO)");
-}
-
 static bool passdown_enabled(struct pool_c *pt)
 {
        return pt->adjusted_pf.discard_passdown;
@@ -2501,8 +2508,6 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
 
        switch (new_mode) {
        case PM_FAIL:
-               if (old_mode != new_mode)
-                       notify_of_pool_mode_change(pool, "failure");
                dm_pool_metadata_read_only(pool->pmd);
                pool->process_bio = process_bio_fail;
                pool->process_discard = process_bio_fail;
@@ -2516,8 +2521,6 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
 
        case PM_OUT_OF_METADATA_SPACE:
        case PM_READ_ONLY:
-               if (!is_read_only_pool_mode(old_mode))
-                       notify_of_pool_mode_change(pool, "read-only");
                dm_pool_metadata_read_only(pool->pmd);
                pool->process_bio = process_bio_read_only;
                pool->process_discard = process_bio_success;
@@ -2538,8 +2541,6 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
                 * alarming rate.  Adjust your low water mark if you're
                 * frequently seeing this mode.
                 */
-               if (old_mode != new_mode)
-                       notify_of_pool_mode_change_to_oods(pool);
                pool->out_of_data_space = true;
                pool->process_bio = process_bio_read_only;
                pool->process_discard = process_discard_bio;
@@ -2552,8 +2553,6 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
                break;
 
        case PM_WRITE:
-               if (old_mode != new_mode)
-                       notify_of_pool_mode_change(pool, "write");
                if (old_mode == PM_OUT_OF_DATA_SPACE)
                        cancel_delayed_work_sync(&pool->no_space_timeout);
                pool->out_of_data_space = false;
@@ -2573,6 +2572,9 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
         * doesn't cause an unexpected mode transition on resume.
         */
        pt->adjusted_pf.mode = new_mode;
+
+       if (old_mode != new_mode)
+               notify_of_pool_mode_change(pool);
 }
 
 static void abort_transaction(struct pool *pool)
@@ -4023,7 +4025,7 @@ static struct target_type pool_target = {
        .name = "thin-pool",
        .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
                    DM_TARGET_IMMUTABLE,
-       .version = {1, 20, 0},
+       .version = {1, 21, 0},
        .module = THIS_MODULE,
        .ctr = pool_ctr,
        .dtr = pool_dtr,
@@ -4397,7 +4399,7 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
 
 static struct target_type thin_target = {
        .name = "thin",
-       .version = {1, 20, 0},
+       .version = {1, 21, 0},
        .module = THIS_MODULE,
        .ctr = thin_ctr,
        .dtr = thin_dtr,
index 981154e..6af5bab 100644 (file)
@@ -20,7 +20,6 @@ struct dmz_bioctx {
        struct dm_zone          *zone;
        struct bio              *bio;
        refcount_t              ref;
-       blk_status_t            status;
 };
 
 /*
@@ -78,65 +77,66 @@ static inline void dmz_bio_endio(struct bio *bio, blk_status_t status)
 {
        struct dmz_bioctx *bioctx = dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
 
-       if (bioctx->status == BLK_STS_OK && status != BLK_STS_OK)
-               bioctx->status = status;
-       bio_endio(bio);
+       if (status != BLK_STS_OK && bio->bi_status == BLK_STS_OK)
+               bio->bi_status = status;
+
+       if (refcount_dec_and_test(&bioctx->ref)) {
+               struct dm_zone *zone = bioctx->zone;
+
+               if (zone) {
+                       if (bio->bi_status != BLK_STS_OK &&
+                           bio_op(bio) == REQ_OP_WRITE &&
+                           dmz_is_seq(zone))
+                               set_bit(DMZ_SEQ_WRITE_ERR, &zone->flags);
+                       dmz_deactivate_zone(zone);
+               }
+               bio_endio(bio);
+       }
 }
 
 /*
- * Partial clone read BIO completion callback. This terminates the
+ * Completion callback for an internally cloned target BIO. This terminates the
  * target BIO when there are no more references to its context.
  */
-static void dmz_read_bio_end_io(struct bio *bio)
+static void dmz_clone_endio(struct bio *clone)
 {
-       struct dmz_bioctx *bioctx = bio->bi_private;
-       blk_status_t status = bio->bi_status;
+       struct dmz_bioctx *bioctx = clone->bi_private;
+       blk_status_t status = clone->bi_status;
 
-       bio_put(bio);
+       bio_put(clone);
        dmz_bio_endio(bioctx->bio, status);
 }
 
 /*
- * Issue a BIO to a zone. The BIO may only partially process the
+ * Issue a clone of a target BIO. The clone may only partially process the
  * original target BIO.
  */
-static int dmz_submit_read_bio(struct dmz_target *dmz, struct dm_zone *zone,
-                              struct bio *bio, sector_t chunk_block,
-                              unsigned int nr_blocks)
+static int dmz_submit_bio(struct dmz_target *dmz, struct dm_zone *zone,
+                         struct bio *bio, sector_t chunk_block,
+                         unsigned int nr_blocks)
 {
        struct dmz_bioctx *bioctx = dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
-       sector_t sector;
        struct bio *clone;
 
-       /* BIO remap sector */
-       sector = dmz_start_sect(dmz->metadata, zone) + dmz_blk2sect(chunk_block);
-
-       /* If the read is not partial, there is no need to clone the BIO */
-       if (nr_blocks == dmz_bio_blocks(bio)) {
-               /* Setup and submit the BIO */
-               bio->bi_iter.bi_sector = sector;
-               refcount_inc(&bioctx->ref);
-               generic_make_request(bio);
-               return 0;
-       }
-
-       /* Partial BIO: we need to clone the BIO */
        clone = bio_clone_fast(bio, GFP_NOIO, &dmz->bio_set);
        if (!clone)
                return -ENOMEM;
 
-       /* Setup the clone */
-       clone->bi_iter.bi_sector = sector;
+       bio_set_dev(clone, dmz->dev->bdev);
+       clone->bi_iter.bi_sector =
+               dmz_start_sect(dmz->metadata, zone) + dmz_blk2sect(chunk_block);
        clone->bi_iter.bi_size = dmz_blk2sect(nr_blocks) << SECTOR_SHIFT;
-       clone->bi_end_io = dmz_read_bio_end_io;
+       clone->bi_end_io = dmz_clone_endio;
        clone->bi_private = bioctx;
 
        bio_advance(bio, clone->bi_iter.bi_size);
 
-       /* Submit the clone */
        refcount_inc(&bioctx->ref);
        generic_make_request(clone);
 
+       if (bio_op(bio) == REQ_OP_WRITE && dmz_is_seq(zone))
+               zone->wp_block += nr_blocks;
+
        return 0;
 }
 
@@ -214,7 +214,7 @@ static int dmz_handle_read(struct dmz_target *dmz, struct dm_zone *zone,
                if (nr_blocks) {
                        /* Valid blocks found: read them */
                        nr_blocks = min_t(unsigned int, nr_blocks, end_block - chunk_block);
-                       ret = dmz_submit_read_bio(dmz, rzone, bio, chunk_block, nr_blocks);
+                       ret = dmz_submit_bio(dmz, rzone, bio, chunk_block, nr_blocks);
                        if (ret)
                                return ret;
                        chunk_block += nr_blocks;
@@ -228,25 +228,6 @@ static int dmz_handle_read(struct dmz_target *dmz, struct dm_zone *zone,
        return 0;
 }
 
-/*
- * Issue a write BIO to a zone.
- */
-static void dmz_submit_write_bio(struct dmz_target *dmz, struct dm_zone *zone,
-                                struct bio *bio, sector_t chunk_block,
-                                unsigned int nr_blocks)
-{
-       struct dmz_bioctx *bioctx = dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
-
-       /* Setup and submit the BIO */
-       bio_set_dev(bio, dmz->dev->bdev);
-       bio->bi_iter.bi_sector = dmz_start_sect(dmz->metadata, zone) + dmz_blk2sect(chunk_block);
-       refcount_inc(&bioctx->ref);
-       generic_make_request(bio);
-
-       if (dmz_is_seq(zone))
-               zone->wp_block += nr_blocks;
-}
-
 /*
  * Write blocks directly in a data zone, at the write pointer.
  * If a buffer zone is assigned, invalidate the blocks written
@@ -265,7 +246,9 @@ static int dmz_handle_direct_write(struct dmz_target *dmz,
                return -EROFS;
 
        /* Submit write */
-       dmz_submit_write_bio(dmz, zone, bio, chunk_block, nr_blocks);
+       ret = dmz_submit_bio(dmz, zone, bio, chunk_block, nr_blocks);
+       if (ret)
+               return ret;
 
        /*
         * Validate the blocks in the data zone and invalidate
@@ -301,7 +284,9 @@ static int dmz_handle_buffered_write(struct dmz_target *dmz,
                return -EROFS;
 
        /* Submit write */
-       dmz_submit_write_bio(dmz, bzone, bio, chunk_block, nr_blocks);
+       ret = dmz_submit_bio(dmz, bzone, bio, chunk_block, nr_blocks);
+       if (ret)
+               return ret;
 
        /*
         * Validate the blocks in the buffer zone
@@ -600,7 +585,6 @@ static int dmz_map(struct dm_target *ti, struct bio *bio)
        bioctx->zone = NULL;
        bioctx->bio = bio;
        refcount_set(&bioctx->ref, 1);
-       bioctx->status = BLK_STS_OK;
 
        /* Set the BIO pending in the flush list */
        if (!nr_sectors && bio_op(bio) == REQ_OP_WRITE) {
@@ -623,35 +607,6 @@ static int dmz_map(struct dm_target *ti, struct bio *bio)
        return DM_MAPIO_SUBMITTED;
 }
 
-/*
- * Completed target BIO processing.
- */
-static int dmz_end_io(struct dm_target *ti, struct bio *bio, blk_status_t *error)
-{
-       struct dmz_bioctx *bioctx = dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
-
-       if (bioctx->status == BLK_STS_OK && *error)
-               bioctx->status = *error;
-
-       if (!refcount_dec_and_test(&bioctx->ref))
-               return DM_ENDIO_INCOMPLETE;
-
-       /* Done */
-       bio->bi_status = bioctx->status;
-
-       if (bioctx->zone) {
-               struct dm_zone *zone = bioctx->zone;
-
-               if (*error && bio_op(bio) == REQ_OP_WRITE) {
-                       if (dmz_is_seq(zone))
-                               set_bit(DMZ_SEQ_WRITE_ERR, &zone->flags);
-               }
-               dmz_deactivate_zone(zone);
-       }
-
-       return DM_ENDIO_DONE;
-}
-
 /*
  * Get zoned device information.
  */
@@ -946,7 +901,6 @@ static struct target_type dmz_type = {
        .ctr             = dmz_ctr,
        .dtr             = dmz_dtr,
        .map             = dmz_map,
-       .end_io          = dmz_end_io,
        .io_hints        = dmz_io_hints,
        .prepare_ioctl   = dmz_prepare_ioctl,
        .postsuspend     = dmz_suspend,
index c510179..63a7c41 100644 (file)
@@ -1593,6 +1593,8 @@ static blk_qc_t __split_and_process_bio(struct mapped_device *md,
                return ret;
        }
 
+       blk_queue_split(md->queue, &bio);
+
        init_clone_info(&ci, md, map, bio);
 
        if (bio->bi_opf & REQ_PREFLUSH) {
index 8add62a..102eb35 100644 (file)
@@ -110,6 +110,19 @@ config MEDIA_CONTROLLER_DVB
 
          This is currently experimental.
 
+config MEDIA_CONTROLLER_REQUEST_API
+       bool "Enable Media controller Request API (EXPERIMENTAL)"
+       depends on MEDIA_CONTROLLER && STAGING_MEDIA
+       default n
+       ---help---
+         DO NOT ENABLE THIS OPTION UNLESS YOU KNOW WHAT YOU'RE DOING.
+
+         This option enables the Request API for the Media controller and V4L2
+         interfaces. It is currently needed by a few stateless codec drivers.
+
+         There is currently no intention to provide API or ABI stability for
+         this new API as of yet.
+
 #
 # Video4Linux support
 #      Only enables if one of the V4L2 types (ATV, webcam, radio) is selected
index 31d1f4a..65a933a 100644 (file)
@@ -807,7 +807,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
        }
 
        if (adap->transmit_queue_sz >= CEC_MAX_MSG_TX_QUEUE_SZ) {
-               dprintk(1, "%s: transmit queue full\n", __func__);
+               dprintk(2, "%s: transmit queue full\n", __func__);
                return -EBUSY;
        }
 
@@ -1180,6 +1180,8 @@ static int cec_config_log_addr(struct cec_adapter *adap,
 {
        struct cec_log_addrs *las = &adap->log_addrs;
        struct cec_msg msg = { };
+       const unsigned int max_retries = 2;
+       unsigned int i;
        int err;
 
        if (cec_has_log_addr(adap, log_addr))
@@ -1188,19 +1190,44 @@ static int cec_config_log_addr(struct cec_adapter *adap,
        /* Send poll message */
        msg.len = 1;
        msg.msg[0] = (log_addr << 4) | log_addr;
-       err = cec_transmit_msg_fh(adap, &msg, NULL, true);
 
-       /*
-        * While trying to poll the physical address was reset
-        * and the adapter was unconfigured, so bail out.
-        */
-       if (!adap->is_configuring)
-               return -EINTR;
+       for (i = 0; i < max_retries; i++) {
+               err = cec_transmit_msg_fh(adap, &msg, NULL, true);
 
-       if (err)
-               return err;
+               /*
+                * While trying to poll the physical address was reset
+                * and the adapter was unconfigured, so bail out.
+                */
+               if (!adap->is_configuring)
+                       return -EINTR;
+
+               if (err)
+                       return err;
 
-       if (msg.tx_status & CEC_TX_STATUS_OK)
+               /*
+                * The message was aborted due to a disconnect or
+                * unconfigure, just bail out.
+                */
+               if (msg.tx_status & CEC_TX_STATUS_ABORTED)
+                       return -EINTR;
+               if (msg.tx_status & CEC_TX_STATUS_OK)
+                       return 0;
+               if (msg.tx_status & CEC_TX_STATUS_NACK)
+                       break;
+               /*
+                * Retry up to max_retries times if the message was neither
+                * OKed or NACKed. This can happen due to e.g. a Lost
+                * Arbitration condition.
+                */
+       }
+
+       /*
+        * If we are unable to get an OK or a NACK after max_retries attempts
+        * (and note that each attempt already consists of four polls), then
+        * then we assume that something is really weird and that it is not a
+        * good idea to try and claim this logical address.
+        */
+       if (i == max_retries)
                return 0;
 
        /*
index 975ff56..8ff8722 100644 (file)
@@ -947,7 +947,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
        }
        atomic_dec(&q->owned_by_drv_count);
 
-       if (vb->req_obj.req) {
+       if (state != VB2_BUF_STATE_QUEUED && vb->req_obj.req) {
                /* This is not supported at the moment */
                WARN_ON(state == VB2_BUF_STATE_REQUEUEING);
                media_request_object_unbind(&vb->req_obj);
@@ -1359,8 +1359,12 @@ static void vb2_req_release(struct media_request_object *obj)
 {
        struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
 
-       if (vb->state == VB2_BUF_STATE_IN_REQUEST)
+       if (vb->state == VB2_BUF_STATE_IN_REQUEST) {
                vb->state = VB2_BUF_STATE_DEQUEUED;
+               if (vb->request)
+                       media_request_put(vb->request);
+               vb->request = NULL;
+       }
 }
 
 static const struct media_request_object_ops vb2_core_req_ops = {
@@ -1528,6 +1532,18 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
                        return ret;
 
                vb->state = VB2_BUF_STATE_IN_REQUEST;
+
+               /*
+                * Increment the refcount and store the request.
+                * The request refcount is decremented again when the
+                * buffer is dequeued. This is to prevent vb2_buffer_done()
+                * from freeing the request from interrupt context, which can
+                * happen if the application closed the request fd after
+                * queueing the request.
+                */
+               media_request_get(req);
+               vb->request = req;
+
                /* Fill buffer information for the userspace */
                if (pb) {
                        call_void_bufop(q, copy_timestamp, vb, pb);
@@ -1749,10 +1765,6 @@ static void __vb2_dqbuf(struct vb2_buffer *vb)
                        call_void_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv);
                        vb->planes[i].dbuf_mapped = 0;
                }
-       if (vb->req_obj.req) {
-               media_request_object_unbind(&vb->req_obj);
-               media_request_object_put(&vb->req_obj);
-       }
        call_void_bufop(q, init_buffer, vb);
 }
 
@@ -1797,6 +1809,14 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
        /* go back to dequeued state */
        __vb2_dqbuf(vb);
 
+       if (WARN_ON(vb->req_obj.req)) {
+               media_request_object_unbind(&vb->req_obj);
+               media_request_object_put(&vb->req_obj);
+       }
+       if (vb->request)
+               media_request_put(vb->request);
+       vb->request = NULL;
+
        dprintk(2, "dqbuf of buffer %d, with state %d\n",
                        vb->index, vb->state);
 
@@ -1903,6 +1923,14 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
                        vb->prepared = false;
                }
                __vb2_dqbuf(vb);
+
+               if (vb->req_obj.req) {
+                       media_request_object_unbind(&vb->req_obj);
+                       media_request_object_put(&vb->req_obj);
+               }
+               if (vb->request)
+                       media_request_put(vb->request);
+               vb->request = NULL;
        }
 }
 
@@ -1940,10 +1968,8 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
                if (ret)
                        return ret;
                ret = vb2_start_streaming(q);
-               if (ret) {
-                       __vb2_queue_cancel(q);
+               if (ret)
                        return ret;
-               }
        }
 
        q->streaming = 1;
index a17033a..1d35aea 100644 (file)
@@ -333,10 +333,10 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b
 }
 
 static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
-                                   struct v4l2_buffer *b,
-                                   const char *opname,
+                                   struct v4l2_buffer *b, bool is_prepare,
                                    struct media_request **p_req)
 {
+       const char *opname = is_prepare ? "prepare_buf" : "qbuf";
        struct media_request *req;
        struct vb2_v4l2_buffer *vbuf;
        struct vb2_buffer *vb;
@@ -378,6 +378,9 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md
                        return ret;
        }
 
+       if (is_prepare)
+               return 0;
+
        if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD)) {
                if (q->uses_requests) {
                        dprintk(1, "%s: queue uses requests\n", opname);
@@ -631,8 +634,10 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps)
                *caps |= V4L2_BUF_CAP_SUPPORTS_USERPTR;
        if (q->io_modes & VB2_DMABUF)
                *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF;
+#ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API
        if (q->supports_requests)
                *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS;
+#endif
 }
 
 int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
@@ -657,7 +662,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
        if (b->flags & V4L2_BUF_FLAG_REQUEST_FD)
                return -EINVAL;
 
-       ret = vb2_queue_or_prepare_buf(q, mdev, b, "prepare_buf", NULL);
+       ret = vb2_queue_or_prepare_buf(q, mdev, b, true, NULL);
 
        return ret ? ret : vb2_core_prepare_buf(q, b->index, b);
 }
@@ -729,7 +734,7 @@ int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev,
                return -EBUSY;
        }
 
-       ret = vb2_queue_or_prepare_buf(q, mdev, b, "qbuf", &req);
+       ret = vb2_queue_or_prepare_buf(q, mdev, b, false, &req);
        if (ret)
                return ret;
        ret = vb2_core_qbuf(q, b->index, b, req);
index 6d4b2ee..29836c1 100644 (file)
@@ -80,8 +80,8 @@ struct dvb_pll_desc {
 
 static const struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
        .name  = "Thomson dtt7579",
-       .min   = 177000000,
-       .max   = 858000000,
+       .min   = 177 * MHz,
+       .max   = 858 * MHz,
        .iffreq= 36166667,
        .sleepdata = (u8[]){ 2, 0xb4, 0x03 },
        .count = 4,
@@ -102,8 +102,8 @@ static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf)
 
 static const struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
        .name  = "Thomson dtt759x",
-       .min   = 177000000,
-       .max   = 896000000,
+       .min   = 177 * MHz,
+       .max   = 896 * MHz,
        .set   = thomson_dtt759x_bw,
        .iffreq= 36166667,
        .sleepdata = (u8[]){ 2, 0x84, 0x03 },
@@ -126,8 +126,8 @@ static void thomson_dtt7520x_bw(struct dvb_frontend *fe, u8 *buf)
 
 static const struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
        .name  = "Thomson dtt7520x",
-       .min   = 185000000,
-       .max   = 900000000,
+       .min   = 185 * MHz,
+       .max   = 900 * MHz,
        .set   = thomson_dtt7520x_bw,
        .iffreq = 36166667,
        .count = 7,
@@ -144,8 +144,8 @@ static const struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
 
 static const struct dvb_pll_desc dvb_pll_lg_z201 = {
        .name  = "LG z201",
-       .min   = 174000000,
-       .max   = 862000000,
+       .min   = 174 * MHz,
+       .max   = 862 * MHz,
        .iffreq= 36166667,
        .sleepdata = (u8[]){ 2, 0xbc, 0x03 },
        .count = 5,
@@ -160,8 +160,8 @@ static const struct dvb_pll_desc dvb_pll_lg_z201 = {
 
 static const struct dvb_pll_desc dvb_pll_unknown_1 = {
        .name  = "unknown 1", /* used by dntv live dvb-t */
-       .min   = 174000000,
-       .max   = 862000000,
+       .min   = 174 * MHz,
+       .max   = 862 * MHz,
        .iffreq= 36166667,
        .count = 9,
        .entries = {
@@ -182,8 +182,8 @@ static const struct dvb_pll_desc dvb_pll_unknown_1 = {
  */
 static const struct dvb_pll_desc dvb_pll_tua6010xs = {
        .name  = "Infineon TUA6010XS",
-       .min   =  44250000,
-       .max   = 858000000,
+       .min   = 44250 * kHz,
+       .max   = 858 * MHz,
        .iffreq= 36125000,
        .count = 3,
        .entries = {
@@ -196,8 +196,8 @@ static const struct dvb_pll_desc dvb_pll_tua6010xs = {
 /* Panasonic env57h1xd5 (some Philips PLL ?) */
 static const struct dvb_pll_desc dvb_pll_env57h1xd5 = {
        .name  = "Panasonic ENV57H1XD5",
-       .min   =  44250000,
-       .max   = 858000000,
+       .min   = 44250 * kHz,
+       .max   = 858 * MHz,
        .iffreq= 36125000,
        .count = 4,
        .entries = {
@@ -220,8 +220,8 @@ static void tda665x_bw(struct dvb_frontend *fe, u8 *buf)
 
 static const struct dvb_pll_desc dvb_pll_tda665x = {
        .name  = "Philips TDA6650/TDA6651",
-       .min   =  44250000,
-       .max   = 858000000,
+       .min   = 44250 * kHz,
+       .max   = 858 * MHz,
        .set   = tda665x_bw,
        .iffreq= 36166667,
        .initdata = (u8[]){ 4, 0x0b, 0xf5, 0x85, 0xab },
@@ -254,8 +254,8 @@ static void tua6034_bw(struct dvb_frontend *fe, u8 *buf)
 
 static const struct dvb_pll_desc dvb_pll_tua6034 = {
        .name  = "Infineon TUA6034",
-       .min   =  44250000,
-       .max   = 858000000,
+       .min   = 44250 * kHz,
+       .max   = 858 * MHz,
        .iffreq= 36166667,
        .count = 3,
        .set   = tua6034_bw,
@@ -278,8 +278,8 @@ static void tded4_bw(struct dvb_frontend *fe, u8 *buf)
 
 static const struct dvb_pll_desc dvb_pll_tded4 = {
        .name = "ALPS TDED4",
-       .min = 47000000,
-       .max = 863000000,
+       .min =  47 * MHz,
+       .max = 863 * MHz,
        .iffreq= 36166667,
        .set   = tded4_bw,
        .count = 4,
@@ -296,8 +296,8 @@ static const struct dvb_pll_desc dvb_pll_tded4 = {
  */
 static const struct dvb_pll_desc dvb_pll_tdhu2 = {
        .name = "ALPS TDHU2",
-       .min = 54000000,
-       .max = 864000000,
+       .min =  54 * MHz,
+       .max = 864 * MHz,
        .iffreq= 44000000,
        .count = 4,
        .entries = {
@@ -313,8 +313,8 @@ static const struct dvb_pll_desc dvb_pll_tdhu2 = {
  */
 static const struct dvb_pll_desc dvb_pll_samsung_tbmv = {
        .name = "Samsung TBMV30111IN / TBMV30712IN1",
-       .min = 54000000,
-       .max = 860000000,
+       .min =  54 * MHz,
+       .max = 860 * MHz,
        .iffreq= 44000000,
        .count = 6,
        .entries = {
@@ -332,8 +332,8 @@ static const struct dvb_pll_desc dvb_pll_samsung_tbmv = {
  */
 static const struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
        .name  = "Philips SD1878",
-       .min   =  950000,
-       .max   = 2150000,
+       .min   =  950 * MHz,
+       .max   = 2150 * MHz,
        .iffreq= 249, /* zero-IF, offset 249 is to round up */
        .count = 4,
        .entries = {
@@ -398,8 +398,8 @@ static void opera1_bw(struct dvb_frontend *fe, u8 *buf)
 
 static const struct dvb_pll_desc dvb_pll_opera1 = {
        .name  = "Opera Tuner",
-       .min   =  900000,
-       .max   = 2250000,
+       .min   =  900 * MHz,
+       .max   = 2250 * MHz,
        .initdata = (u8[]){ 4, 0x08, 0xe5, 0xe1, 0x00 },
        .initdata2 = (u8[]){ 4, 0x08, 0xe5, 0xe5, 0x00 },
        .iffreq= 0,
@@ -445,8 +445,8 @@ static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf)
 /* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */
 static const struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
        .name   = "Samsung DTOS403IH102A",
-       .min    =  44250000,
-       .max    = 858000000,
+       .min    = 44250 * kHz,
+       .max    = 858 * MHz,
        .iffreq =  36125000,
        .count  = 8,
        .set    = samsung_dtos403ih102a_set,
@@ -465,8 +465,8 @@ static const struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
 /* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */
 static const struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
        .name   = "Samsung TDTC9251DH0",
-       .min    =  48000000,
-       .max    = 863000000,
+       .min    =  48 * MHz,
+       .max    = 863 * MHz,
        .iffreq =  36166667,
        .count  = 3,
        .entries = {
@@ -479,8 +479,8 @@ static const struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
 /* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */
 static const struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
        .name = "Samsung TBDU18132",
-       .min    =  950000,
-       .max    = 2150000, /* guesses */
+       .min    =  950 * MHz,
+       .max    = 2150 * MHz, /* guesses */
        .iffreq = 0,
        .count = 2,
        .entries = {
@@ -500,8 +500,8 @@ static const struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
 /* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */
 static const struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
        .name = "Samsung TBMU24112",
-       .min    =  950000,
-       .max    = 2150000, /* guesses */
+       .min    =  950 * MHz,
+       .max    = 2150 * MHz, /* guesses */
        .iffreq = 0,
        .count = 2,
        .entries = {
@@ -521,8 +521,8 @@ static const struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
  * 822 - 862   1  *  0   0   1   0   0   0   0x88 */
 static const struct dvb_pll_desc dvb_pll_alps_tdee4 = {
        .name = "ALPS TDEE4",
-       .min    =  47000000,
-       .max    = 862000000,
+       .min    =  47 * MHz,
+       .max    = 862 * MHz,
        .iffreq =  36125000,
        .count = 4,
        .entries = {
@@ -537,8 +537,8 @@ static const struct dvb_pll_desc dvb_pll_alps_tdee4 = {
 /* CP cur. 50uA, AGC takeover: 103dBuV, PORT3 on */
 static const struct dvb_pll_desc dvb_pll_tua6034_friio = {
        .name   = "Infineon TUA6034 ISDB-T (Friio)",
-       .min    =  90000000,
-       .max    = 770000000,
+       .min    =  90 * MHz,
+       .max    = 770 * MHz,
        .iffreq =  57000000,
        .initdata = (u8[]){ 4, 0x9a, 0x50, 0xb2, 0x08 },
        .sleepdata = (u8[]){ 4, 0x9a, 0x70, 0xb3, 0x0b },
@@ -553,8 +553,8 @@ static const struct dvb_pll_desc dvb_pll_tua6034_friio = {
 /* Philips TDA6651 ISDB-T, used in Earthsoft PT1 */
 static const struct dvb_pll_desc dvb_pll_tda665x_earth_pt1 = {
        .name   = "Philips TDA6651 ISDB-T (EarthSoft PT1)",
-       .min    =  90000000,
-       .max    = 770000000,
+       .min    =  90 * MHz,
+       .max    = 770 * MHz,
        .iffreq =  57000000,
        .initdata = (u8[]){ 5, 0x0e, 0x7f, 0xc1, 0x80, 0x80 },
        .count = 10,
@@ -610,9 +610,6 @@ static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf,
        u32 div;
        int i;
 
-       if (frequency && (frequency < desc->min || frequency > desc->max))
-               return -EINVAL;
-
        for (i = 0; i < desc->count; i++) {
                if (frequency > desc->entries[i].limit)
                        continue;
@@ -799,7 +796,6 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
        struct dvb_pll_priv *priv = NULL;
        int ret;
        const struct dvb_pll_desc *desc;
-       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 
        b1 = kmalloc(1, GFP_KERNEL);
        if (!b1)
@@ -845,18 +841,12 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
 
        strncpy(fe->ops.tuner_ops.info.name, desc->name,
                sizeof(fe->ops.tuner_ops.info.name));
-       switch (c->delivery_system) {
-       case SYS_DVBS:
-       case SYS_DVBS2:
-       case SYS_TURBO:
-       case SYS_ISDBS:
-               fe->ops.tuner_ops.info.frequency_min_hz = desc->min * kHz;
-               fe->ops.tuner_ops.info.frequency_max_hz = desc->max * kHz;
-               break;
-       default:
-               fe->ops.tuner_ops.info.frequency_min_hz = desc->min;
-               fe->ops.tuner_ops.info.frequency_max_hz = desc->max;
-       }
+
+       fe->ops.tuner_ops.info.frequency_min_hz = desc->min;
+       fe->ops.tuner_ops.info.frequency_max_hz = desc->max;
+
+       dprintk("%s tuner, frequency range: %u...%u\n",
+               desc->name, desc->min, desc->max);
 
        if (!desc->initdata)
                fe->ops.tuner_ops.init = NULL;
index ca5d929..41d470d 100644 (file)
@@ -1918,7 +1918,6 @@ static int tc358743_probe_of(struct tc358743_state *state)
        ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint);
        if (ret) {
                dev_err(dev, "failed to parse endpoint\n");
-               ret = ret;
                goto put_node;
        }
 
index bed2437..b8ec886 100644 (file)
@@ -381,10 +381,14 @@ static long media_device_get_topology(struct media_device *mdev, void *arg)
 static long media_device_request_alloc(struct media_device *mdev,
                                       int *alloc_fd)
 {
+#ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API
        if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue)
                return -ENOTTY;
 
        return media_request_alloc(mdev, alloc_fd);
+#else
+       return -ENOTTY;
+#endif
 }
 
 static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd)
index 4e9db1f..c71a34a 100644 (file)
@@ -238,6 +238,9 @@ static const struct file_operations request_fops = {
        .owner = THIS_MODULE,
        .poll = media_request_poll,
        .unlocked_ioctl = media_request_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = media_request_ioctl,
+#endif /* CONFIG_COMPAT */
        .release = media_request_close,
 };
 
index 452eb9b..447baae 100644 (file)
@@ -1844,14 +1844,12 @@ fail_mutex_destroy:
 static void cio2_pci_remove(struct pci_dev *pci_dev)
 {
        struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
-       unsigned int i;
 
+       media_device_unregister(&cio2->media_dev);
        cio2_notifier_exit(cio2);
+       cio2_queues_exit(cio2);
        cio2_fbpt_exit_dummy(cio2);
-       for (i = 0; i < CIO2_QUEUES; i++)
-               cio2_queue_exit(cio2, &cio2->queue[i]);
        v4l2_device_unregister(&cio2->v4l2_dev);
-       media_device_unregister(&cio2->media_dev);
        media_device_cleanup(&cio2->media_dev);
        mutex_destroy(&cio2->lock);
 }
index 77fb798..13f2828 100644 (file)
@@ -1587,6 +1587,8 @@ static void isp_pm_complete(struct device *dev)
 
 static void isp_unregister_entities(struct isp_device *isp)
 {
+       media_device_unregister(&isp->media_dev);
+
        omap3isp_csi2_unregister_entities(&isp->isp_csi2a);
        omap3isp_ccp2_unregister_entities(&isp->isp_ccp2);
        omap3isp_ccdc_unregister_entities(&isp->isp_ccdc);
@@ -1597,7 +1599,6 @@ static void isp_unregister_entities(struct isp_device *isp)
        omap3isp_stat_unregister_entities(&isp->isp_hist);
 
        v4l2_device_unregister(&isp->v4l2_dev);
-       media_device_unregister(&isp->media_dev);
        media_device_cleanup(&isp->media_dev);
 }
 
index 1eb9132..13fb69c 100644 (file)
@@ -42,7 +42,7 @@ MODULE_PARM_DESC(debug, " activates debug info");
 #define MAX_WIDTH              4096U
 #define MIN_WIDTH              640U
 #define MAX_HEIGHT             2160U
-#define MIN_HEIGHT             480U
+#define MIN_HEIGHT             360U
 
 #define dprintk(dev, fmt, arg...) \
        v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
@@ -304,7 +304,8 @@ restart:
                for (; p < p_out + sz; p++) {
                        u32 copy;
 
-                       p = memchr(p, magic[ctx->comp_magic_cnt], sz);
+                       p = memchr(p, magic[ctx->comp_magic_cnt],
+                                  p_out + sz - p);
                        if (!p) {
                                ctx->comp_magic_cnt = 0;
                                break;
@@ -996,11 +997,18 @@ static int vicodec_start_streaming(struct vb2_queue *q,
 
        q_data->sequence = 0;
 
-       if (!V4L2_TYPE_IS_OUTPUT(q->type))
+       if (!V4L2_TYPE_IS_OUTPUT(q->type)) {
+               if (!ctx->is_enc) {
+                       state->width = q_data->width;
+                       state->height = q_data->height;
+               }
                return 0;
+       }
 
-       state->width = q_data->width;
-       state->height = q_data->height;
+       if (ctx->is_enc) {
+               state->width = q_data->width;
+               state->height = q_data->height;
+       }
        state->ref_frame.width = state->ref_frame.height = 0;
        state->ref_frame.luma = kvmalloc(size + 2 * size / chroma_div,
                                         GFP_KERNEL);
index af150a0..d82db73 100644 (file)
@@ -1009,7 +1009,7 @@ static const struct v4l2_m2m_ops m2m_ops = {
 
 static const struct media_device_ops m2m_media_ops = {
        .req_validate = vb2_request_validate,
-       .req_queue = vb2_m2m_request_queue,
+       .req_queue = v4l2_m2m_request_queue,
 };
 
 static int vim2m_probe(struct platform_device *pdev)
index dcdc80e..9acc709 100644 (file)
@@ -276,8 +276,6 @@ static int sdr_cap_start_streaming(struct vb2_queue *vq, unsigned count)
 
                list_for_each_entry_safe(buf, tmp, &dev->sdr_cap_active, list) {
                        list_del(&buf->list);
-                       v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
-                                                  &dev->ctrl_hdl_sdr_cap);
                        vb2_buffer_done(&buf->vb.vb2_buf,
                                        VB2_BUF_STATE_QUEUED);
                }
index 903cebe..d666271 100644 (file)
@@ -204,8 +204,6 @@ static int vbi_cap_start_streaming(struct vb2_queue *vq, unsigned count)
 
                list_for_each_entry_safe(buf, tmp, &dev->vbi_cap_active, list) {
                        list_del(&buf->list);
-                       v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
-                                                  &dev->ctrl_hdl_vbi_cap);
                        vb2_buffer_done(&buf->vb.vb2_buf,
                                        VB2_BUF_STATE_QUEUED);
                }
index 9357c07..cd56476 100644 (file)
@@ -96,8 +96,6 @@ static int vbi_out_start_streaming(struct vb2_queue *vq, unsigned count)
 
                list_for_each_entry_safe(buf, tmp, &dev->vbi_out_active, list) {
                        list_del(&buf->list);
-                       v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
-                                                  &dev->ctrl_hdl_vbi_out);
                        vb2_buffer_done(&buf->vb.vb2_buf,
                                        VB2_BUF_STATE_QUEUED);
                }
index 9c8e8be..673772c 100644 (file)
@@ -243,8 +243,6 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count)
 
                list_for_each_entry_safe(buf, tmp, &dev->vid_cap_active, list) {
                        list_del(&buf->list);
-                       v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
-                                                  &dev->ctrl_hdl_vid_cap);
                        vb2_buffer_done(&buf->vb.vb2_buf,
                                        VB2_BUF_STATE_QUEUED);
                }
index aaf13f0..628eae1 100644 (file)
@@ -162,8 +162,6 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count)
 
                list_for_each_entry_safe(buf, tmp, &dev->vid_out_active, list) {
                        list_del(&buf->list);
-                       v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
-                                                  &dev->ctrl_hdl_vid_out);
                        vb2_buffer_done(&buf->vb.vb2_buf,
                                        VB2_BUF_STATE_QUEUED);
                }
index 0b18f0b..8b0a263 100644 (file)
@@ -95,7 +95,7 @@ static void lif_configure_stream(struct vsp1_entity *entity,
        format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config,
                                            LIF_PAD_SOURCE);
 
-       switch (entity->vsp1->version & VI6_IP_VERSION_SOC_MASK) {
+       switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) {
        case VI6_IP_VERSION_MODEL_VSPD_GEN2:
        case VI6_IP_VERSION_MODEL_VSPD_V2H:
                hbth = 1536;
index 8b97fd1..390a722 100644 (file)
@@ -59,6 +59,28 @@ static const struct bpf_func_proto rc_keydown_proto = {
        .arg4_type = ARG_ANYTHING,
 };
 
+BPF_CALL_3(bpf_rc_pointer_rel, u32*, sample, s32, rel_x, s32, rel_y)
+{
+       struct ir_raw_event_ctrl *ctrl;
+
+       ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample);
+
+       input_report_rel(ctrl->dev->input_dev, REL_X, rel_x);
+       input_report_rel(ctrl->dev->input_dev, REL_Y, rel_y);
+       input_sync(ctrl->dev->input_dev);
+
+       return 0;
+}
+
+static const struct bpf_func_proto rc_pointer_rel_proto = {
+       .func      = bpf_rc_pointer_rel,
+       .gpl_only  = true,
+       .ret_type  = RET_INTEGER,
+       .arg1_type = ARG_PTR_TO_CTX,
+       .arg2_type = ARG_ANYTHING,
+       .arg3_type = ARG_ANYTHING,
+};
+
 static const struct bpf_func_proto *
 lirc_mode2_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
@@ -67,6 +89,8 @@ lirc_mode2_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &rc_repeat_proto;
        case BPF_FUNC_rc_keydown:
                return &rc_keydown_proto;
+       case BPF_FUNC_rc_pointer_rel:
+               return &rc_pointer_rel_proto;
        case BPF_FUNC_map_lookup_elem:
                return &bpf_map_lookup_elem_proto;
        case BPF_FUNC_map_update_elem:
index fce9d6f..3137f5d 100644 (file)
@@ -426,10 +426,10 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
 
        /* append the packet to the frame buffer */
        if (len > 0) {
-               if (gspca_dev->image_len + len > gspca_dev->pixfmt.sizeimage) {
+               if (gspca_dev->image_len + len > PAGE_ALIGN(gspca_dev->pixfmt.sizeimage)) {
                        gspca_err(gspca_dev, "frame overflow %d > %d\n",
                                  gspca_dev->image_len + len,
-                                 gspca_dev->pixfmt.sizeimage);
+                                 PAGE_ALIGN(gspca_dev->pixfmt.sizeimage));
                        packet_type = DISCARD_PACKET;
                } else {
 /* !! image is NULL only when last pkt is LAST or DISCARD
@@ -1297,18 +1297,19 @@ static int gspca_queue_setup(struct vb2_queue *vq,
                             unsigned int sizes[], struct device *alloc_devs[])
 {
        struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq);
+       unsigned int size = PAGE_ALIGN(gspca_dev->pixfmt.sizeimage);
 
        if (*nplanes)
-               return sizes[0] < gspca_dev->pixfmt.sizeimage ? -EINVAL : 0;
+               return sizes[0] < size ? -EINVAL : 0;
        *nplanes = 1;
-       sizes[0] = gspca_dev->pixfmt.sizeimage;
+       sizes[0] = size;
        return 0;
 }
 
 static int gspca_buffer_prepare(struct vb2_buffer *vb)
 {
        struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue);
-       unsigned long size = gspca_dev->pixfmt.sizeimage;
+       unsigned long size = PAGE_ALIGN(gspca_dev->pixfmt.sizeimage);
 
        if (vb2_plane_size(vb, 0) < size) {
                gspca_err(gspca_dev, "buffer too small (%lu < %lu)\n",
index 6e37950..10b8d94 100644 (file)
@@ -1563,7 +1563,7 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
        u64 offset;
        s64 val;
 
-       switch (ctrl->type) {
+       switch ((u32)ctrl->type) {
        case V4L2_CTRL_TYPE_INTEGER:
                return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl);
        case V4L2_CTRL_TYPE_INTEGER64:
@@ -1664,6 +1664,11 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
                    p_mpeg2_slice_params->forward_ref_index >= VIDEO_MAX_FRAME)
                        return -EINVAL;
 
+               if (p_mpeg2_slice_params->pad ||
+                   p_mpeg2_slice_params->picture.pad ||
+                   p_mpeg2_slice_params->sequence.pad)
+                       return -EINVAL;
+
                return 0;
 
        case V4L2_CTRL_TYPE_MPEG2_QUANTIZATION:
@@ -2227,7 +2232,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
        is_array = nr_of_dims > 0;
 
        /* Prefill elem_size for all types handled by std_type_ops */
-       switch (type) {
+       switch ((u32)type) {
        case V4L2_CTRL_TYPE_INTEGER64:
                elem_size = sizeof(s64);
                break;
index a3ef1f5..481e3c6 100644 (file)
@@ -193,6 +193,22 @@ int v4l2_event_pending(struct v4l2_fh *fh)
 }
 EXPORT_SYMBOL_GPL(v4l2_event_pending);
 
+static void __v4l2_event_unsubscribe(struct v4l2_subscribed_event *sev)
+{
+       struct v4l2_fh *fh = sev->fh;
+       unsigned int i;
+
+       lockdep_assert_held(&fh->subscribe_lock);
+       assert_spin_locked(&fh->vdev->fh_lock);
+
+       /* Remove any pending events for this subscription */
+       for (i = 0; i < sev->in_use; i++) {
+               list_del(&sev->events[sev_pos(sev, i)].list);
+               fh->navailable--;
+       }
+       list_del(&sev->list);
+}
+
 int v4l2_event_subscribe(struct v4l2_fh *fh,
                         const struct v4l2_event_subscription *sub, unsigned elems,
                         const struct v4l2_subscribed_event_ops *ops)
@@ -224,27 +240,23 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
 
        spin_lock_irqsave(&fh->vdev->fh_lock, flags);
        found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
+       if (!found_ev)
+               list_add(&sev->list, &fh->subscribed);
        spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 
        if (found_ev) {
                /* Already listening */
                kvfree(sev);
-               goto out_unlock;
-       }
-
-       if (sev->ops && sev->ops->add) {
+       } else if (sev->ops && sev->ops->add) {
                ret = sev->ops->add(sev, elems);
                if (ret) {
+                       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+                       __v4l2_event_unsubscribe(sev);
+                       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
                        kvfree(sev);
-                       goto out_unlock;
                }
        }
 
-       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-       list_add(&sev->list, &fh->subscribed);
-       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-
-out_unlock:
        mutex_unlock(&fh->subscribe_lock);
 
        return ret;
@@ -279,7 +291,6 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
 {
        struct v4l2_subscribed_event *sev;
        unsigned long flags;
-       int i;
 
        if (sub->type == V4L2_EVENT_ALL) {
                v4l2_event_unsubscribe_all(fh);
@@ -291,14 +302,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
        spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 
        sev = v4l2_event_subscribed(fh, sub->type, sub->id);
-       if (sev != NULL) {
-               /* Remove any pending events for this subscription */
-               for (i = 0; i < sev->in_use; i++) {
-                       list_del(&sev->events[sev_pos(sev, i)].list);
-                       fh->navailable--;
-               }
-               list_del(&sev->list);
-       }
+       if (sev != NULL)
+               __v4l2_event_unsubscribe(sev);
 
        spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 
index d7806db..1ed2465 100644 (file)
@@ -953,7 +953,7 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx,
 }
 EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
 
-void vb2_m2m_request_queue(struct media_request *req)
+void v4l2_m2m_request_queue(struct media_request *req)
 {
        struct media_request_object *obj, *obj_safe;
        struct v4l2_m2m_ctx *m2m_ctx = NULL;
@@ -997,7 +997,7 @@ void vb2_m2m_request_queue(struct media_request *req)
        if (m2m_ctx)
                v4l2_m2m_try_schedule(m2m_ctx);
 }
-EXPORT_SYMBOL_GPL(vb2_m2m_request_queue);
+EXPORT_SYMBOL_GPL(v4l2_m2m_request_queue);
 
 /* Videobuf2 ioctl helpers */
 
index 8f9d696..b99a194 100644 (file)
@@ -263,6 +263,11 @@ static const struct file_operations fops = {
 #endif
 };
 
+static void cros_ec_class_release(struct device *dev)
+{
+       kfree(to_cros_ec_dev(dev));
+}
+
 static void cros_ec_sensors_register(struct cros_ec_dev *ec)
 {
        /*
@@ -395,7 +400,7 @@ static int ec_device_probe(struct platform_device *pdev)
        int retval = -ENOMEM;
        struct device *dev = &pdev->dev;
        struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
-       struct cros_ec_dev *ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
+       struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
 
        if (!ec)
                return retval;
@@ -417,6 +422,7 @@ static int ec_device_probe(struct platform_device *pdev)
        ec->class_dev.devt = MKDEV(ec_major, pdev->id);
        ec->class_dev.class = &cros_class;
        ec->class_dev.parent = dev;
+       ec->class_dev.release = cros_ec_class_release;
 
        retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
        if (retval) {
index b2a0340..d8e3cc2 100644 (file)
@@ -132,7 +132,7 @@ static const struct of_device_id atmel_ssc_dt_ids[] = {
 MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids);
 #endif
 
-static inline const struct atmel_ssc_platform_data * __init
+static inline const struct atmel_ssc_platform_data *
        atmel_ssc_get_driver_data(struct platform_device *pdev)
 {
        if (pdev->dev.of_node) {
index c824329..0e4193c 100644 (file)
@@ -416,7 +416,7 @@ static int scif_create_remote_lookup(struct scif_dev *remote_dev,
                if (err)
                        goto error_window;
                err = scif_map_page(&window->num_pages_lookup.lookup[j],
-                                   vmalloc_dma_phys ?
+                                   vmalloc_num_pages ?
                                    vmalloc_to_page(&window->num_pages[i]) :
                                    virt_to_page(&window->num_pages[i]),
                                    remote_dev);
index 3633202..6b212c8 100644 (file)
@@ -129,6 +129,16 @@ static u64 vop_get_features(struct virtio_device *vdev)
        return features;
 }
 
+static void vop_transport_features(struct virtio_device *vdev)
+{
+       /*
+        * Packed ring isn't enabled on virtio_vop for now,
+        * because virtio_vop uses vring_new_virtqueue() which
+        * creates virtio rings on preallocated memory.
+        */
+       __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED);
+}
+
 static int vop_finalize_features(struct virtio_device *vdev)
 {
        unsigned int i, bits;
@@ -141,6 +151,9 @@ static int vop_finalize_features(struct virtio_device *vdev)
        /* Give virtio_ring a chance to accept features. */
        vring_transport_features(vdev);
 
+       /* Give virtio_vop a chance to accept features. */
+       vop_transport_features(vdev);
+
        memset_io(out_features, 0, feature_len);
        bits = min_t(unsigned, feature_len,
                     sizeof(vdev->features)) * 8;
index 313da31..1540a77 100644 (file)
@@ -27,6 +27,9 @@
 #include <linux/delay.h>
 #include <linux/bitops.h>
 #include <asm/uv/uv_hub.h>
+
+#include <linux/nospec.h>
+
 #include "gru.h"
 #include "grutables.h"
 #include "gruhandles.h"
@@ -196,6 +199,7 @@ int gru_dump_chiplet_request(unsigned long arg)
        /* Currently, only dump by gid is implemented */
        if (req.gid >= gru_max_gids)
                return -EINVAL;
+       req.gid = array_index_nospec(req.gid, gru_max_gids);
 
        gru = GID_TO_GRU(req.gid);
        ubuf = req.buf;
index c35b5b0..1119348 100644 (file)
@@ -472,7 +472,7 @@ out:
 static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
                               struct mmc_blk_ioc_data *idata)
 {
-       struct mmc_command cmd = {};
+       struct mmc_command cmd = {}, sbc = {};
        struct mmc_data data = {};
        struct mmc_request mrq = {};
        struct scatterlist sg;
@@ -550,10 +550,15 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
        }
 
        if (idata->rpmb) {
-               err = mmc_set_blockcount(card, data.blocks,
-                       idata->ic.write_flag & (1 << 31));
-               if (err)
-                       return err;
+               sbc.opcode = MMC_SET_BLOCK_COUNT;
+               /*
+                * We don't do any blockcount validation because the max size
+                * may be increased by a future standard. We just copy the
+                * 'Reliable Write' bit here.
+                */
+               sbc.arg = data.blocks | (idata->ic.write_flag & BIT(31));
+               sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
+               mrq.sbc = &sbc;
        }
 
        if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) &&
index bc1bd2c..55997cf 100644 (file)
@@ -30,6 +30,7 @@
 #include "pwrseq.h"
 
 #define DEFAULT_CMD6_TIMEOUT_MS        500
+#define MIN_CACHE_EN_TIMEOUT_MS 1600
 
 static const unsigned int tran_exp[] = {
        10000,          100000,         1000000,        10000000,
@@ -526,8 +527,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
                        card->cid.year += 16;
 
                /* check whether the eMMC card supports BKOPS */
-               if (!mmc_card_broken_hpi(card) &&
-                   ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
+               if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
                        card->ext_csd.bkops = 1;
                        card->ext_csd.man_bkops_en =
                                        (ext_csd[EXT_CSD_BKOPS_EN] &
@@ -1782,20 +1782,26 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                if (err) {
                        pr_warn("%s: Enabling HPI failed\n",
                                mmc_hostname(card->host));
+                       card->ext_csd.hpi_en = 0;
                        err = 0;
-               } else
+               } else {
                        card->ext_csd.hpi_en = 1;
+               }
        }
 
        /*
-        * If cache size is higher than 0, this indicates
-        * the existence of cache and it can be turned on.
+        * If cache size is higher than 0, this indicates the existence of cache
+        * and it can be turned on. Note that some eMMCs from Micron has been
+        * reported to need ~800 ms timeout, while enabling the cache after
+        * sudden power failure tests. Let's extend the timeout to a minimum of
+        * DEFAULT_CACHE_EN_TIMEOUT_MS and do it for all cards.
         */
-       if (!mmc_card_broken_hpi(card) &&
-           card->ext_csd.cache_size > 0) {
+       if (card->ext_csd.cache_size > 0) {
+               unsigned int timeout_ms = MIN_CACHE_EN_TIMEOUT_MS;
+
+               timeout_ms = max(card->ext_csd.generic_cmd6_time, timeout_ms);
                err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                               EXT_CSD_CACHE_CTRL, 1,
-                               card->ext_csd.generic_cmd6_time);
+                               EXT_CSD_CACHE_CTRL, 1, timeout_ms);
                if (err && err != -EBADMSG)
                        goto free_card;
 
index adf3268..c60a762 100644 (file)
@@ -104,6 +104,7 @@ struct mmc_omap_slot {
        unsigned int            vdd;
        u16                     saved_con;
        u16                     bus_mode;
+       u16                     power_mode;
        unsigned int            fclk_freq;
 
        struct tasklet_struct   cover_tasklet;
@@ -1157,7 +1158,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        struct mmc_omap_slot *slot = mmc_priv(mmc);
        struct mmc_omap_host *host = slot->host;
        int i, dsor;
-       int clk_enabled;
+       int clk_enabled, init_stream;
 
        mmc_omap_select_slot(slot, 0);
 
@@ -1167,6 +1168,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                slot->vdd = ios->vdd;
 
        clk_enabled = 0;
+       init_stream = 0;
        switch (ios->power_mode) {
        case MMC_POWER_OFF:
                mmc_omap_set_power(slot, 0, ios->vdd);
@@ -1174,13 +1176,17 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        case MMC_POWER_UP:
                /* Cannot touch dsor yet, just power up MMC */
                mmc_omap_set_power(slot, 1, ios->vdd);
+               slot->power_mode = ios->power_mode;
                goto exit;
        case MMC_POWER_ON:
                mmc_omap_fclk_enable(host, 1);
                clk_enabled = 1;
                dsor |= 1 << 11;
+               if (slot->power_mode != MMC_POWER_ON)
+                       init_stream = 1;
                break;
        }
+       slot->power_mode = ios->power_mode;
 
        if (slot->bus_mode != ios->bus_mode) {
                if (slot->pdata->set_bus_mode != NULL)
@@ -1196,7 +1202,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        for (i = 0; i < 2; i++)
                OMAP_MMC_WRITE(host, CON, dsor);
        slot->saved_con = dsor;
-       if (ios->power_mode == MMC_POWER_ON) {
+       if (init_stream) {
                /* worst case at 400kHz, 80 cycles makes 200 microsecs */
                int usecs = 250;
 
@@ -1234,6 +1240,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
        slot->host = host;
        slot->mmc = mmc;
        slot->id = id;
+       slot->power_mode = MMC_POWER_UNDEFINED;
        slot->pdata = &host->pdata->slots[id];
 
        host->slots[id] = slot;
index 467d889..3f4ea8f 100644 (file)
@@ -1909,7 +1909,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
        mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */
        mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */
        mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
-       mmc->max_seg_size = mmc->max_req_size;
 
        mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
                     MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE | MMC_CAP_CMD23;
@@ -1939,6 +1938,17 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
                goto err_irq;
        }
 
+       /*
+        * Limit the maximum segment size to the lower of the request size
+        * and the DMA engine device segment size limits.  In reality, with
+        * 32-bit transfers, the DMA engine can do longer segments than this
+        * but there is no way to represent that in the DMA model - if we
+        * increase this figure here, we get warnings from the DMA API debug.
+        */
+       mmc->max_seg_size = min3(mmc->max_req_size,
+                       dma_get_max_seg_size(host->rx_chan->device->dev),
+                       dma_get_max_seg_size(host->tx_chan->device->dev));
+
        /* Request IRQ for MMC operations */
        ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,
                        mmc_hostname(mmc), host);
index 88347ce..d264391 100644 (file)
@@ -288,9 +288,9 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
        struct device *dev = omap_host->dev;
        struct mmc_ios *ios = &mmc->ios;
        u32 start_window = 0, max_window = 0;
+       bool dcrc_was_enabled = false;
        u8 cur_match, prev_match = 0;
        u32 length = 0, max_len = 0;
-       u32 ier = host->ier;
        u32 phase_delay = 0;
        int ret = 0;
        u32 reg;
@@ -317,9 +317,10 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
         * during the tuning procedure. So disable it during the
         * tuning procedure.
         */
-       ier &= ~SDHCI_INT_DATA_CRC;
-       sdhci_writel(host, ier, SDHCI_INT_ENABLE);
-       sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+       if (host->ier & SDHCI_INT_DATA_CRC) {
+               host->ier &= ~SDHCI_INT_DATA_CRC;
+               dcrc_was_enabled = true;
+       }
 
        while (phase_delay <= MAX_PHASE_DELAY) {
                sdhci_omap_set_dll(omap_host, phase_delay);
@@ -366,6 +367,9 @@ tuning_error:
 
 ret:
        sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+       /* Reenable forbidden interrupt */
+       if (dcrc_was_enabled)
+               host->ier |= SDHCI_INT_DATA_CRC;
        sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
        sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
        return ret;
index 7bfd366..c4115ba 100644 (file)
@@ -12,6 +12,7 @@
  *     - JMicron (hardware and technical support)
  */
 
+#include <linux/bitfield.h>
 #include <linux/string.h>
 #include <linux/delay.h>
 #include <linux/highmem.h>
@@ -462,6 +463,9 @@ struct intel_host {
        u32     dsm_fns;
        int     drv_strength;
        bool    d3_retune;
+       bool    rpm_retune_ok;
+       u32     glk_rx_ctrl1;
+       u32     glk_tun_val;
 };
 
 static const guid_t intel_dsm_guid =
@@ -791,6 +795,77 @@ cleanup:
        return ret;
 }
 
+#ifdef CONFIG_PM
+#define GLK_RX_CTRL1   0x834
+#define GLK_TUN_VAL    0x840
+#define GLK_PATH_PLL   GENMASK(13, 8)
+#define GLK_DLY                GENMASK(6, 0)
+/* Workaround firmware failing to restore the tuning value */
+static void glk_rpm_retune_wa(struct sdhci_pci_chip *chip, bool susp)
+{
+       struct sdhci_pci_slot *slot = chip->slots[0];
+       struct intel_host *intel_host = sdhci_pci_priv(slot);
+       struct sdhci_host *host = slot->host;
+       u32 glk_rx_ctrl1;
+       u32 glk_tun_val;
+       u32 dly;
+
+       if (intel_host->rpm_retune_ok || !mmc_can_retune(host->mmc))
+               return;
+
+       glk_rx_ctrl1 = sdhci_readl(host, GLK_RX_CTRL1);
+       glk_tun_val = sdhci_readl(host, GLK_TUN_VAL);
+
+       if (susp) {
+               intel_host->glk_rx_ctrl1 = glk_rx_ctrl1;
+               intel_host->glk_tun_val = glk_tun_val;
+               return;
+       }
+
+       if (!intel_host->glk_tun_val)
+               return;
+
+       if (glk_rx_ctrl1 != intel_host->glk_rx_ctrl1) {
+               intel_host->rpm_retune_ok = true;
+               return;
+       }
+
+       dly = FIELD_PREP(GLK_DLY, FIELD_GET(GLK_PATH_PLL, glk_rx_ctrl1) +
+                                 (intel_host->glk_tun_val << 1));
+       if (dly == FIELD_GET(GLK_DLY, glk_rx_ctrl1))
+               return;
+
+       glk_rx_ctrl1 = (glk_rx_ctrl1 & ~GLK_DLY) | dly;
+       sdhci_writel(host, glk_rx_ctrl1, GLK_RX_CTRL1);
+
+       intel_host->rpm_retune_ok = true;
+       chip->rpm_retune = true;
+       mmc_retune_needed(host->mmc);
+       pr_info("%s: Requiring re-tune after rpm resume", mmc_hostname(host->mmc));
+}
+
+static void glk_rpm_retune_chk(struct sdhci_pci_chip *chip, bool susp)
+{
+       if (chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC &&
+           !chip->rpm_retune)
+               glk_rpm_retune_wa(chip, susp);
+}
+
+static int glk_runtime_suspend(struct sdhci_pci_chip *chip)
+{
+       glk_rpm_retune_chk(chip, true);
+
+       return sdhci_cqhci_runtime_suspend(chip);
+}
+
+static int glk_runtime_resume(struct sdhci_pci_chip *chip)
+{
+       glk_rpm_retune_chk(chip, false);
+
+       return sdhci_cqhci_runtime_resume(chip);
+}
+#endif
+
 #ifdef CONFIG_ACPI
 static int ni_set_max_freq(struct sdhci_pci_slot *slot)
 {
@@ -879,8 +954,8 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = {
        .resume                 = sdhci_cqhci_resume,
 #endif
 #ifdef CONFIG_PM
-       .runtime_suspend        = sdhci_cqhci_runtime_suspend,
-       .runtime_resume         = sdhci_cqhci_runtime_resume,
+       .runtime_suspend        = glk_runtime_suspend,
+       .runtime_resume         = glk_runtime_resume,
 #endif
        .quirks                 = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
        .quirks2                = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
@@ -1762,8 +1837,13 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
                device_init_wakeup(&pdev->dev, true);
 
        if (slot->cd_idx >= 0) {
-               ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx,
+               ret = mmc_gpiod_request_cd(host->mmc, "cd", slot->cd_idx,
                                           slot->cd_override_level, 0, NULL);
+               if (ret && ret != -EPROBE_DEFER)
+                       ret = mmc_gpiod_request_cd(host->mmc, NULL,
+                                                  slot->cd_idx,
+                                                  slot->cd_override_level,
+                                                  0, NULL);
                if (ret == -EPROBE_DEFER)
                        goto remove;
 
index 7b95d08..e6ace31 100644 (file)
@@ -510,25 +510,25 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
 
        err = device_property_read_u32(host->mmc->parent,
                        "nvidia,pad-autocal-pull-up-offset-3v3-timeout",
-                       &autocal->pull_up_3v3);
+                       &autocal->pull_up_3v3_timeout);
        if (err)
                autocal->pull_up_3v3_timeout = 0;
 
        err = device_property_read_u32(host->mmc->parent,
                        "nvidia,pad-autocal-pull-down-offset-3v3-timeout",
-                       &autocal->pull_down_3v3);
+                       &autocal->pull_down_3v3_timeout);
        if (err)
                autocal->pull_down_3v3_timeout = 0;
 
        err = device_property_read_u32(host->mmc->parent,
                        "nvidia,pad-autocal-pull-up-offset-1v8-timeout",
-                       &autocal->pull_up_1v8);
+                       &autocal->pull_up_1v8_timeout);
        if (err)
                autocal->pull_up_1v8_timeout = 0;
 
        err = device_property_read_u32(host->mmc->parent,
                        "nvidia,pad-autocal-pull-down-offset-1v8-timeout",
-                       &autocal->pull_down_1v8);
+                       &autocal->pull_down_1v8_timeout);
        if (err)
                autocal->pull_down_1v8_timeout = 0;
 
index 99bdae5..df05352 100644 (file)
@@ -127,12 +127,12 @@ static void sdhci_do_enable_v4_mode(struct sdhci_host *host)
 {
        u16 ctrl2;
 
-       ctrl2 = sdhci_readb(host, SDHCI_HOST_CONTROL2);
+       ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
        if (ctrl2 & SDHCI_CTRL_V4_MODE)
                return;
 
        ctrl2 |= SDHCI_CTRL_V4_MODE;
-       sdhci_writeb(host, ctrl2, SDHCI_HOST_CONTROL);
+       sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
 }
 
 /*
@@ -216,8 +216,12 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
        timeout = ktime_add_ms(ktime_get(), 100);
 
        /* hw clears the bit when it's done */
-       while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
-               if (ktime_after(ktime_get(), timeout)) {
+       while (1) {
+               bool timedout = ktime_after(ktime_get(), timeout);
+
+               if (!(sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask))
+                       break;
+               if (timedout) {
                        pr_err("%s: Reset 0x%x never completed.\n",
                                mmc_hostname(host->mmc), (int)mask);
                        sdhci_dumpregs(host);
@@ -1608,9 +1612,13 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk)
 
        /* Wait max 20 ms */
        timeout = ktime_add_ms(ktime_get(), 20);
-       while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
-               & SDHCI_CLOCK_INT_STABLE)) {
-               if (ktime_after(ktime_get(), timeout)) {
+       while (1) {
+               bool timedout = ktime_after(ktime_get(), timeout);
+
+               clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+               if (clk & SDHCI_CLOCK_INT_STABLE)
+                       break;
+               if (timedout) {
                        pr_err("%s: Internal clock never stabilised.\n",
                               mmc_hostname(host->mmc));
                        sdhci_dumpregs(host);
index 56cde38..044adf9 100644 (file)
@@ -27,7 +27,8 @@ int nanddev_bbt_init(struct nand_device *nand)
        unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
                                           BITS_PER_LONG);
 
-       nand->bbt.cache = kzalloc(nwords, GFP_KERNEL);
+       nand->bbt.cache = kcalloc(nwords, sizeof(*nand->bbt.cache),
+                                 GFP_KERNEL);
        if (!nand->bbt.cache)
                return -ENOMEM;
 
index fb33f6b..ad72049 100644 (file)
@@ -2032,8 +2032,7 @@ atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc)
        int ret;
 
        nand_np = dev->of_node;
-       nfc_np = of_find_compatible_node(dev->of_node, NULL,
-                                        "atmel,sama5d3-nfc");
+       nfc_np = of_get_compatible_child(dev->of_node, "atmel,sama5d3-nfc");
        if (!nfc_np) {
                dev_err(dev, "Could not find device node for sama5d3-nfc\n");
                return -ENODEV;
@@ -2447,15 +2446,19 @@ static int atmel_nand_controller_probe(struct platform_device *pdev)
        }
 
        if (caps->legacy_of_bindings) {
+               struct device_node *nfc_node;
                u32 ale_offs = 21;
 
                /*
                 * If we are parsing legacy DT props and the DT contains a
                 * valid NFC node, forward the request to the sama5 logic.
                 */
-               if (of_find_compatible_node(pdev->dev.of_node, NULL,
-                                           "atmel,sama5d3-nfc"))
+               nfc_node = of_get_compatible_child(pdev->dev.of_node,
+                                                  "atmel,sama5d3-nfc");
+               if (nfc_node) {
                        caps = &atmel_sama5_nand_caps;
+                       of_node_put(nfc_node);
+               }
 
                /*
                 * Even if the compatible says we are dealing with an
index ef75dfa..699d3cf 100644 (file)
 #define        NAND_VERSION_MINOR_SHIFT        16
 
 /* NAND OP_CMDs */
-#define        PAGE_READ                       0x2
-#define        PAGE_READ_WITH_ECC              0x3
-#define        PAGE_READ_WITH_ECC_SPARE        0x4
-#define        PROGRAM_PAGE                    0x6
-#define        PAGE_PROGRAM_WITH_ECC           0x7
-#define        PROGRAM_PAGE_SPARE              0x9
-#define        BLOCK_ERASE                     0xa
-#define        FETCH_ID                        0xb
-#define        RESET_DEVICE                    0xd
+#define        OP_PAGE_READ                    0x2
+#define        OP_PAGE_READ_WITH_ECC           0x3
+#define        OP_PAGE_READ_WITH_ECC_SPARE     0x4
+#define        OP_PROGRAM_PAGE                 0x6
+#define        OP_PAGE_PROGRAM_WITH_ECC        0x7
+#define        OP_PROGRAM_PAGE_SPARE           0x9
+#define        OP_BLOCK_ERASE                  0xa
+#define        OP_FETCH_ID                     0xb
+#define        OP_RESET_DEVICE                 0xd
 
 /* Default Value for NAND_DEV_CMD_VLD */
 #define NAND_DEV_CMD_VLD_VAL           (READ_START_VLD | WRITE_START_VLD | \
@@ -692,11 +692,11 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read)
 
        if (read) {
                if (host->use_ecc)
-                       cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
+                       cmd = OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
                else
-                       cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
+                       cmd = OP_PAGE_READ | PAGE_ACC | LAST_PAGE;
        } else {
-                       cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
+               cmd = OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
        }
 
        if (host->use_ecc) {
@@ -1170,7 +1170,7 @@ static int nandc_param(struct qcom_nand_host *host)
         * in use. we configure the controller to perform a raw read of 512
         * bytes to read onfi params
         */
-       nandc_set_reg(nandc, NAND_FLASH_CMD, PAGE_READ | PAGE_ACC | LAST_PAGE);
+       nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ | PAGE_ACC | LAST_PAGE);
        nandc_set_reg(nandc, NAND_ADDR0, 0);
        nandc_set_reg(nandc, NAND_ADDR1, 0);
        nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE
@@ -1224,7 +1224,7 @@ static int erase_block(struct qcom_nand_host *host, int page_addr)
        struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
 
        nandc_set_reg(nandc, NAND_FLASH_CMD,
-                     BLOCK_ERASE | PAGE_ACC | LAST_PAGE);
+                     OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE);
        nandc_set_reg(nandc, NAND_ADDR0, page_addr);
        nandc_set_reg(nandc, NAND_ADDR1, 0);
        nandc_set_reg(nandc, NAND_DEV0_CFG0,
@@ -1255,7 +1255,7 @@ static int read_id(struct qcom_nand_host *host, int column)
        if (column == -1)
                return 0;
 
-       nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID);
+       nandc_set_reg(nandc, NAND_FLASH_CMD, OP_FETCH_ID);
        nandc_set_reg(nandc, NAND_ADDR0, column);
        nandc_set_reg(nandc, NAND_ADDR1, 0);
        nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT,
@@ -1276,7 +1276,7 @@ static int reset(struct qcom_nand_host *host)
        struct nand_chip *chip = &host->chip;
        struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
 
-       nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE);
+       nandc_set_reg(nandc, NAND_FLASH_CMD, OP_RESET_DEVICE);
        nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
 
        write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
index d846428..04cedd3 100644 (file)
@@ -644,9 +644,23 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr,
                ndelay(cqspi->wr_delay);
 
        while (remaining > 0) {
+               size_t write_words, mod_bytes;
+
                write_bytes = remaining > page_size ? page_size : remaining;
-               iowrite32_rep(cqspi->ahb_base, txbuf,
-                             DIV_ROUND_UP(write_bytes, 4));
+               write_words = write_bytes / 4;
+               mod_bytes = write_bytes % 4;
+               /* Write 4 bytes at a time then single bytes. */
+               if (write_words) {
+                       iowrite32_rep(cqspi->ahb_base, txbuf, write_words);
+                       txbuf += (write_words * 4);
+               }
+               if (mod_bytes) {
+                       unsigned int temp = 0xFFFFFFFF;
+
+                       memcpy(&temp, txbuf, mod_bytes);
+                       iowrite32(temp, cqspi->ahb_base);
+                       txbuf += mod_bytes;
+               }
 
                if (!wait_for_completion_timeout(&cqspi->transfer_complete,
                                        msecs_to_jiffies(CQSPI_TIMEOUT_MS))) {
@@ -655,7 +669,6 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr,
                        goto failwr;
                }
 
-               txbuf += write_bytes;
                remaining -= write_bytes;
 
                if (remaining > 0)
index 3e54e31..1fdd283 100644 (file)
@@ -2156,7 +2156,7 @@ spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
  * @nor:       pointer to a 'struct spi_nor'
  * @addr:      offset in the serial flash memory
  * @len:       number of bytes to read
- * @buf:       buffer where the data is copied into
+ * @buf:       buffer where the data is copied into (dma-safe memory)
  *
  * Return: 0 on success, -errno otherwise.
  */
@@ -2521,6 +2521,34 @@ static int spi_nor_map_cmp_erase_type(const void *l, const void *r)
        return left->size - right->size;
 }
 
+/**
+ * spi_nor_sort_erase_mask() - sort erase mask
+ * @map:       the erase map of the SPI NOR
+ * @erase_mask:        the erase type mask to be sorted
+ *
+ * Replicate the sort done for the map's erase types in BFPT: sort the erase
+ * mask in ascending order with the smallest erase type size starting from
+ * BIT(0) in the sorted erase mask.
+ *
+ * Return: sorted erase mask.
+ */
+static u8 spi_nor_sort_erase_mask(struct spi_nor_erase_map *map, u8 erase_mask)
+{
+       struct spi_nor_erase_type *erase_type = map->erase_type;
+       int i;
+       u8 sorted_erase_mask = 0;
+
+       if (!erase_mask)
+               return 0;
+
+       /* Replicate the sort done for the map's erase types. */
+       for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
+               if (erase_type[i].size && erase_mask & BIT(erase_type[i].idx))
+                       sorted_erase_mask |= BIT(i);
+
+       return sorted_erase_mask;
+}
+
 /**
  * spi_nor_regions_sort_erase_types() - sort erase types in each region
  * @map:       the erase map of the SPI NOR
@@ -2536,19 +2564,13 @@ static int spi_nor_map_cmp_erase_type(const void *l, const void *r)
 static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map)
 {
        struct spi_nor_erase_region *region = map->regions;
-       struct spi_nor_erase_type *erase_type = map->erase_type;
-       int i;
        u8 region_erase_mask, sorted_erase_mask;
 
        while (region) {
                region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
 
-               /* Replicate the sort done for the map's erase types. */
-               sorted_erase_mask = 0;
-               for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
-                       if (erase_type[i].size &&
-                           region_erase_mask & BIT(erase_type[i].idx))
-                               sorted_erase_mask |= BIT(i);
+               sorted_erase_mask = spi_nor_sort_erase_mask(map,
+                                                           region_erase_mask);
 
                /* Overwrite erase mask. */
                region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) |
@@ -2855,52 +2877,84 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
  * spi_nor_get_map_in_use() - get the configuration map in use
  * @nor:       pointer to a 'struct spi_nor'
  * @smpt:      pointer to the sector map parameter table
+ * @smpt_len:  sector map parameter table length
+ *
+ * Return: pointer to the map in use, ERR_PTR(-errno) otherwise.
  */
-static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
+static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
+                                        u8 smpt_len)
 {
-       const u32 *ret = NULL;
-       u32 i, addr;
+       const u32 *ret;
+       u8 *buf;
+       u32 addr;
        int err;
+       u8 i;
        u8 addr_width, read_opcode, read_dummy;
-       u8 read_data_mask, data_byte, map_id;
+       u8 read_data_mask, map_id;
+
+       /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */
+       buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return ERR_PTR(-ENOMEM);
 
        addr_width = nor->addr_width;
        read_dummy = nor->read_dummy;
        read_opcode = nor->read_opcode;
 
        map_id = 0;
-       i = 0;
        /* Determine if there are any optional Detection Command Descriptors */
-       while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
+       for (i = 0; i < smpt_len; i += 2) {
+               if (smpt[i] & SMPT_DESC_TYPE_MAP)
+                       break;
+
                read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
                nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
                nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
                nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
                addr = smpt[i + 1];
 
-               err = spi_nor_read_raw(nor, addr, 1, &data_byte);
-               if (err)
+               err = spi_nor_read_raw(nor, addr, 1, buf);
+               if (err) {
+                       ret = ERR_PTR(err);
                        goto out;
+               }
 
                /*
                 * Build an index value that is used to select the Sector Map
                 * Configuration that is currently in use.
                 */
-               map_id = map_id << 1 | !!(data_byte & read_data_mask);
-               i = i + 2;
+               map_id = map_id << 1 | !!(*buf & read_data_mask);
        }
 
-       /* Find the matching configuration map */
-       while (SMPT_MAP_ID(smpt[i]) != map_id) {
+       /*
+        * If command descriptors are provided, they always precede map
+        * descriptors in the table. There is no need to start the iteration
+        * over smpt array all over again.
+        *
+        * Find the matching configuration map.
+        */
+       ret = ERR_PTR(-EINVAL);
+       while (i < smpt_len) {
+               if (SMPT_MAP_ID(smpt[i]) == map_id) {
+                       ret = smpt + i;
+                       break;
+               }
+
+               /*
+                * If there are no more configuration map descriptors and no
+                * configuration ID matched the configuration identifier, the
+                * sector address map is unknown.
+                */
                if (smpt[i] & SMPT_DESC_END)
-                       goto out;
+                       break;
+
                /* increment the table index to the next map */
                i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
        }
 
-       ret = smpt + i;
        /* fall through */
 out:
+       kfree(buf);
        nor->addr_width = addr_width;
        nor->read_dummy = read_dummy;
        nor->read_opcode = read_opcode;
@@ -2941,12 +2995,13 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
                                              const u32 *smpt)
 {
        struct spi_nor_erase_map *map = &nor->erase_map;
-       const struct spi_nor_erase_type *erase = map->erase_type;
+       struct spi_nor_erase_type *erase = map->erase_type;
        struct spi_nor_erase_region *region;
        u64 offset;
        u32 region_count;
        int i, j;
-       u8 erase_type;
+       u8 uniform_erase_type, save_uniform_erase_type;
+       u8 erase_type, regions_erase_type;
 
        region_count = SMPT_MAP_REGION_COUNT(*smpt);
        /*
@@ -2959,7 +3014,8 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
                return -ENOMEM;
        map->regions = region;
 
-       map->uniform_erase_type = 0xff;
+       uniform_erase_type = 0xff;
+       regions_erase_type = 0;
        offset = 0;
        /* Populate regions. */
        for (i = 0; i < region_count; i++) {
@@ -2974,12 +3030,40 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
                 * Save the erase types that are supported in all regions and
                 * can erase the entire flash memory.
                 */
-               map->uniform_erase_type &= erase_type;
+               uniform_erase_type &= erase_type;
+
+               /*
+                * regions_erase_type mask will indicate all the erase types
+                * supported in this configuration map.
+                */
+               regions_erase_type |= erase_type;
 
                offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) +
                         region[i].size;
        }
 
+       save_uniform_erase_type = map->uniform_erase_type;
+       map->uniform_erase_type = spi_nor_sort_erase_mask(map,
+                                                         uniform_erase_type);
+
+       if (!regions_erase_type) {
+               /*
+                * Roll back to the previous uniform_erase_type mask, SMPT is
+                * broken.
+                */
+               map->uniform_erase_type = save_uniform_erase_type;
+               return -EINVAL;
+       }
+
+       /*
+        * BFPT advertises all the erase types supported by all the possible
+        * map configurations. Mask out the erase types that are not supported
+        * by the current map configuration.
+        */
+       for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
+               if (!(regions_erase_type & BIT(erase[i].idx)))
+                       spi_nor_set_erase_type(&erase[i], 0, 0xFF);
+
        spi_nor_region_mark_end(&region[i - 1]);
 
        return 0;
@@ -3020,9 +3104,9 @@ static int spi_nor_parse_smpt(struct spi_nor *nor,
        for (i = 0; i < smpt_header->length; i++)
                smpt[i] = le32_to_cpu(smpt[i]);
 
-       sector_map = spi_nor_get_map_in_use(nor, smpt);
-       if (!sector_map) {
-               ret = -EINVAL;
+       sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length);
+       if (IS_ERR(sector_map)) {
+               ret = PTR_ERR(sector_map);
                goto out;
        }
 
@@ -3125,7 +3209,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
        if (err)
                goto exit;
 
-       /* Parse other parameter headers. */
+       /* Parse optional parameter tables. */
        for (i = 0; i < header.nph; i++) {
                param_header = &param_headers[i];
 
@@ -3138,8 +3222,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
                        break;
                }
 
-               if (err)
-                       goto exit;
+               if (err) {
+                       dev_warn(dev, "Failed to parse optional parameter table: %04x\n",
+                                SFDP_PARAM_HEADER_ID(param_header));
+                       /*
+                        * Let's not drop all information we extracted so far
+                        * if optional table parsers fail. In case of failing,
+                        * each optional parser is responsible to roll back to
+                        * the previously known spi_nor data.
+                        */
+                       err = 0;
+               }
        }
 
 exit:
index d037751..6371958 100644 (file)
@@ -397,10 +397,10 @@ config NET_SB1000
 
          At present this driver only compiles as a module, so say M here if
          you have this card. The module will be called sb1000. Then read
-         <file:Documentation/networking/README.sb1000> for information on how
-         to use this module, as it needs special ppp scripts for establishing
-         a connection. Further documentation and the necessary scripts can be
-         found at:
+         <file:Documentation/networking/device_drivers/sb1000.txt> for
+         information on how to use this module, as it needs special ppp
+         scripts for establishing a connection. Further documentation
+         and the necessary scripts can be found at:
 
          <http://www.jacksonville.net/~fventuri/>
          <http://home.adelphia.net/~siglercm/sb1000.html>
index f43fb2f..7c46d9f 100644 (file)
@@ -1220,7 +1220,7 @@ static void ad_churn_machine(struct port *port)
                port->sm_churn_partner_state = AD_CHURN_MONITOR;
                port->sm_churn_actor_timer_counter =
                        __ad_timer_to_ticks(AD_ACTOR_CHURN_TIMER, 0);
-                port->sm_churn_partner_timer_counter =
+               port->sm_churn_partner_timer_counter =
                         __ad_timer_to_ticks(AD_PARTNER_CHURN_TIMER, 0);
                return;
        }
@@ -2086,6 +2086,9 @@ void bond_3ad_unbind_slave(struct slave *slave)
                   aggregator->aggregator_identifier);
 
        /* Tell the partner that this port is not suitable for aggregation */
+       port->actor_oper_port_state &= ~AD_STATE_SYNCHRONIZATION;
+       port->actor_oper_port_state &= ~AD_STATE_COLLECTING;
+       port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING;
        port->actor_oper_port_state &= ~AD_STATE_AGGREGATION;
        __update_lacpdu_from_port(port);
        ad_lacpdu_send(port);
@@ -2125,7 +2128,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
                                if ((new_aggregator->lag_ports == port) &&
                                    new_aggregator->is_active) {
                                        netdev_info(bond->dev, "Removing an active aggregator\n");
-                                        select_new_active_agg = 1;
+                                       select_new_active_agg = 1;
                                }
 
                                new_aggregator->is_individual = aggregator->is_individual;
index e82108c..9431127 100644 (file)
@@ -1031,7 +1031,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[],
         */
        memcpy(ss.__data, addr, len);
        ss.ss_family = dev->type;
-       if (dev_set_mac_address(dev, (struct sockaddr *)&ss)) {
+       if (dev_set_mac_address(dev, (struct sockaddr *)&ss, NULL)) {
                netdev_err(slave->bond->dev, "dev_set_mac_address of dev %s failed! ALB mode requires that the base driver support setting the hw address also when the network device's interface is open\n",
                           dev->name);
                return -EOPNOTSUPP;
@@ -1250,7 +1250,7 @@ static int alb_set_mac_address(struct bonding *bond, void *addr)
                bond_hw_addr_copy(tmp_addr, slave->dev->dev_addr,
                                  slave->dev->addr_len);
 
-               res = dev_set_mac_address(slave->dev, addr);
+               res = dev_set_mac_address(slave->dev, addr, NULL);
 
                /* restore net_device's hw address */
                bond_hw_addr_copy(slave->dev->dev_addr, tmp_addr,
@@ -1273,7 +1273,7 @@ unwind:
                bond_hw_addr_copy(tmp_addr, rollback_slave->dev->dev_addr,
                                  rollback_slave->dev->addr_len);
                dev_set_mac_address(rollback_slave->dev,
-                                   (struct sockaddr *)&ss);
+                                   (struct sockaddr *)&ss, NULL);
                bond_hw_addr_copy(rollback_slave->dev->dev_addr, tmp_addr,
                                  rollback_slave->dev->addr_len);
        }
@@ -1732,7 +1732,8 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
                                  bond->dev->addr_len);
                ss.ss_family = bond->dev->type;
                /* we don't care if it can't change its mac, best effort */
-               dev_set_mac_address(new_slave->dev, (struct sockaddr *)&ss);
+               dev_set_mac_address(new_slave->dev, (struct sockaddr *)&ss,
+                                   NULL);
 
                bond_hw_addr_copy(new_slave->dev->dev_addr, tmp_addr,
                                  new_slave->dev->addr_len);
index 3868e1a..1360f1f 100644 (file)
@@ -45,19 +45,7 @@ static int bond_debug_rlb_hash_show(struct seq_file *m, void *v)
 
        return 0;
 }
-
-static int bond_debug_rlb_hash_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, bond_debug_rlb_hash_show, inode->i_private);
-}
-
-static const struct file_operations bond_debug_rlb_hash_fops = {
-       .owner          = THIS_MODULE,
-       .open           = bond_debug_rlb_hash_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(bond_debug_rlb_hash);
 
 void bond_debug_register(struct bonding *bond)
 {
index 333387f..a9d597f 100644 (file)
@@ -609,14 +609,21 @@ static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active,
  *
  * Should be called with RTNL held.
  */
-static void bond_set_dev_addr(struct net_device *bond_dev,
-                             struct net_device *slave_dev)
+static int bond_set_dev_addr(struct net_device *bond_dev,
+                            struct net_device *slave_dev)
 {
+       int err;
+
        netdev_dbg(bond_dev, "bond_dev=%p slave_dev=%p slave_dev->name=%s slave_dev->addr_len=%d\n",
                   bond_dev, slave_dev, slave_dev->name, slave_dev->addr_len);
+       err = dev_pre_changeaddr_notify(bond_dev, slave_dev->dev_addr, NULL);
+       if (err)
+               return err;
+
        memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
        bond_dev->addr_assign_type = NET_ADDR_STOLEN;
        call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev);
+       return 0;
 }
 
 static struct slave *bond_get_old_active(struct bonding *bond,
@@ -652,8 +659,12 @@ static void bond_do_fail_over_mac(struct bonding *bond,
 
        switch (bond->params.fail_over_mac) {
        case BOND_FOM_ACTIVE:
-               if (new_active)
-                       bond_set_dev_addr(bond->dev, new_active->dev);
+               if (new_active) {
+                       rv = bond_set_dev_addr(bond->dev, new_active->dev);
+                       if (rv)
+                               netdev_err(bond->dev, "Error %d setting MAC of slave %s\n",
+                                          -rv, bond->dev->name);
+               }
                break;
        case BOND_FOM_FOLLOW:
                /* if new_active && old_active, swap them
@@ -680,7 +691,7 @@ static void bond_do_fail_over_mac(struct bonding *bond,
                }
 
                rv = dev_set_mac_address(new_active->dev,
-                                        (struct sockaddr *)&ss);
+                                        (struct sockaddr *)&ss, NULL);
                if (rv) {
                        netdev_err(bond->dev, "Error %d setting MAC of slave %s\n",
                                   -rv, new_active->dev->name);
@@ -695,7 +706,7 @@ static void bond_do_fail_over_mac(struct bonding *bond,
                ss.ss_family = old_active->dev->type;
 
                rv = dev_set_mac_address(old_active->dev,
-                                        (struct sockaddr *)&ss);
+                                        (struct sockaddr *)&ss, NULL);
                if (rv)
                        netdev_err(bond->dev, "Error %d setting MAC of slave %s\n",
                                   -rv, new_active->dev->name);
@@ -1489,8 +1500,11 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
         * address to be the same as the slave's.
         */
        if (!bond_has_slaves(bond) &&
-           bond->dev->addr_assign_type == NET_ADDR_RANDOM)
-               bond_set_dev_addr(bond->dev, slave_dev);
+           bond->dev->addr_assign_type == NET_ADDR_RANDOM) {
+               res = bond_set_dev_addr(bond->dev, slave_dev);
+               if (res)
+                       goto err_undo_flags;
+       }
 
        new_slave = bond_alloc_slave(bond);
        if (!new_slave) {
@@ -1527,7 +1541,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
                 */
                memcpy(ss.__data, bond_dev->dev_addr, bond_dev->addr_len);
                ss.ss_family = slave_dev->type;
-               res = dev_set_mac_address(slave_dev, (struct sockaddr *)&ss);
+               res = dev_set_mac_address(slave_dev, (struct sockaddr *)&ss,
+                                         extack);
                if (res) {
                        netdev_dbg(bond_dev, "Error %d calling set_mac_address\n", res);
                        goto err_restore_mtu;
@@ -1538,7 +1553,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
        slave_dev->flags |= IFF_SLAVE;
 
        /* open the slave since the application closed it */
-       res = dev_open(slave_dev);
+       res = dev_open(slave_dev, extack);
        if (res) {
                netdev_dbg(bond_dev, "Opening slave %s failed\n", slave_dev->name);
                goto err_restore_mac;
@@ -1818,7 +1833,7 @@ err_restore_mac:
                bond_hw_addr_copy(ss.__data, new_slave->perm_hwaddr,
                                  new_slave->dev->addr_len);
                ss.ss_family = slave_dev->type;
-               dev_set_mac_address(slave_dev, (struct sockaddr *)&ss);
+               dev_set_mac_address(slave_dev, (struct sockaddr *)&ss, NULL);
        }
 
 err_restore_mtu:
@@ -1999,7 +2014,7 @@ static int __bond_release_one(struct net_device *bond_dev,
                bond_hw_addr_copy(ss.__data, slave->perm_hwaddr,
                                  slave->dev->addr_len);
                ss.ss_family = slave_dev->type;
-               dev_set_mac_address(slave_dev, (struct sockaddr *)&ss);
+               dev_set_mac_address(slave_dev, (struct sockaddr *)&ss, NULL);
        }
 
        if (unregister)
@@ -3544,8 +3559,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
                break;
        case BOND_SETHWADDR_OLD:
        case SIOCBONDSETHWADDR:
-               bond_set_dev_addr(bond_dev, slave_dev);
-               res = 0;
+               res = bond_set_dev_addr(bond_dev, slave_dev);
                break;
        case BOND_CHANGE_ACTIVE_OLD:
        case SIOCBONDCHANGEACTIVE:
@@ -3732,7 +3746,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
 
        bond_for_each_slave(bond, slave, iter) {
                netdev_dbg(bond_dev, "slave %p %s\n", slave, slave->dev->name);
-               res = dev_set_mac_address(slave->dev, addr);
+               res = dev_set_mac_address(slave->dev, addr, NULL);
                if (res) {
                        /* TODO: consider downing the slave
                         * and retry ?
@@ -3761,7 +3775,7 @@ unwind:
                        break;
 
                tmp_res = dev_set_mac_address(rollback_slave->dev,
-                                             (struct sockaddr *)&tmp_ss);
+                                             (struct sockaddr *)&tmp_ss, NULL);
                if (tmp_res) {
                        netdev_dbg(bond_dev, "unwind err %d dev %s\n",
                                   tmp_res, rollback_slave->dev->name);
index 7cdd0ce..e0f0ad7 100644 (file)
@@ -96,7 +96,7 @@ config CAN_AT91
 
 config CAN_FLEXCAN
        tristate "Support for Freescale FLEXCAN based chips"
-       depends on ARM || PPC
+       depends on OF && HAS_IOMEM
        ---help---
          Say Y here if you want to support for Freescale FlexCAN.
 
index 4916357..3b3f88f 100644 (file)
@@ -477,6 +477,34 @@ void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(can_put_echo_skb);
 
+struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       struct sk_buff *skb = priv->echo_skb[idx];
+       struct canfd_frame *cf;
+
+       if (idx >= priv->echo_skb_max) {
+               netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n",
+                          __func__, idx, priv->echo_skb_max);
+               return NULL;
+       }
+
+       if (!skb) {
+               netdev_err(dev, "%s: BUG! Trying to echo non existing skb: can_priv::echo_skb[%u]\n",
+                          __func__, idx);
+               return NULL;
+       }
+
+       /* Using "struct canfd_frame::len" for the frame
+        * length is supported on both CAN and CANFD frames.
+        */
+       cf = (struct canfd_frame *)skb->data;
+       *len_ptr = cf->len;
+       priv->echo_skb[idx] = NULL;
+
+       return skb;
+}
+
 /*
  * Get the skb from the stack and loop it back locally
  *
@@ -486,22 +514,16 @@ EXPORT_SYMBOL_GPL(can_put_echo_skb);
  */
 unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx)
 {
-       struct can_priv *priv = netdev_priv(dev);
-
-       BUG_ON(idx >= priv->echo_skb_max);
-
-       if (priv->echo_skb[idx]) {
-               struct sk_buff *skb = priv->echo_skb[idx];
-               struct can_frame *cf = (struct can_frame *)skb->data;
-               u8 dlc = cf->can_dlc;
+       struct sk_buff *skb;
+       u8 len;
 
-               netif_rx(priv->echo_skb[idx]);
-               priv->echo_skb[idx] = NULL;
+       skb = __can_get_echo_skb(dev, idx, &len);
+       if (!skb)
+               return 0;
 
-               return dlc;
-       }
+       netif_rx(skb);
 
-       return 0;
+       return len;
 }
 EXPORT_SYMBOL_GPL(can_get_echo_skb);
 
index 8e972ef..0f36eaf 100644 (file)
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
 
 #define DRV_NAME                       "flexcan"
 
        (FLEXCAN_ESR_ERR_BUS | FLEXCAN_ESR_ERR_STATE)
 #define FLEXCAN_ESR_ALL_INT \
        (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | \
-        FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT)
+        FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT | \
+        FLEXCAN_ESR_WAK_INT)
 
 /* FLEXCAN interrupt flag register (IFLAG) bits */
 /* Errata ERR005829 step7: Reserve first valid MB */
-#define FLEXCAN_TX_MB_RESERVED_OFF_FIFO        8
-#define FLEXCAN_TX_MB_OFF_FIFO         9
+#define FLEXCAN_TX_MB_RESERVED_OFF_FIFO                8
 #define FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP   0
-#define FLEXCAN_TX_MB_OFF_TIMESTAMP            1
-#define FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST      (FLEXCAN_TX_MB_OFF_TIMESTAMP + 1)
-#define FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST       63
-#define FLEXCAN_IFLAG_MB(x)            BIT(x)
+#define FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST      (FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP + 1)
+#define FLEXCAN_IFLAG_MB(x)            BIT((x) & 0x1f)
 #define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7)
 #define FLEXCAN_IFLAG_RX_FIFO_WARN     BIT(6)
 #define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE        BIT(5)
 #define FLEXCAN_QUIRK_USE_OFF_TIMESTAMP        BIT(5) /* Use timestamp based offloading */
 #define FLEXCAN_QUIRK_BROKEN_PERR_STATE        BIT(6) /* No interrupt for error passive */
 #define FLEXCAN_QUIRK_DEFAULT_BIG_ENDIAN       BIT(7) /* default to BE register access */
+#define FLEXCAN_QUIRK_SETUP_STOP_MODE          BIT(8) /* Setup stop mode to support wakeup */
 
 /* Structure of the message buffer */
 struct flexcan_mb {
        u32 can_ctrl;
        u32 can_id;
-       u32 data[2];
+       u32 data[];
 };
 
 /* Structure of the hardware registers */
@@ -224,7 +225,7 @@ struct flexcan_regs {
        u32 rxfgmask;           /* 0x48 */
        u32 rxfir;              /* 0x4c */
        u32 _reserved3[12];     /* 0x50 */
-       struct flexcan_mb mb[64];       /* 0x80 */
+       u8 mb[2][512];          /* 0x80 */
        /* FIFO-mode:
         *                      MB
         * 0x080...0x08f        0       RX message buffer
@@ -254,6 +255,14 @@ struct flexcan_devtype_data {
        u32 quirks;             /* quirks needed for different IP cores */
 };
 
+struct flexcan_stop_mode {
+       struct regmap *gpr;
+       u8 req_gpr;
+       u8 req_bit;
+       u8 ack_gpr;
+       u8 ack_bit;
+};
+
 struct flexcan_priv {
        struct can_priv can;
        struct can_rx_offload offload;
@@ -262,6 +271,8 @@ struct flexcan_priv {
        struct flexcan_mb __iomem *tx_mb;
        struct flexcan_mb __iomem *tx_mb_reserved;
        u8 tx_mb_idx;
+       u8 mb_count;
+       u8 mb_size;
        u32 reg_ctrl_default;
        u32 reg_imask1_default;
        u32 reg_imask2_default;
@@ -270,6 +281,7 @@ struct flexcan_priv {
        struct clk *clk_per;
        const struct flexcan_devtype_data *devtype_data;
        struct regulator *reg_xceiver;
+       struct flexcan_stop_mode stm;
 
        /* Read and Write APIs */
        u32 (*read)(void __iomem *addr);
@@ -293,7 +305,8 @@ static const struct flexcan_devtype_data fsl_imx28_devtype_data = {
 
 static const struct flexcan_devtype_data fsl_imx6q_devtype_data = {
        .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
-               FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE,
+               FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE |
+               FLEXCAN_QUIRK_SETUP_STOP_MODE,
 };
 
 static const struct flexcan_devtype_data fsl_vf610_devtype_data = {
@@ -353,6 +366,68 @@ static inline void flexcan_write_le(u32 val, void __iomem *addr)
        iowrite32(val, addr);
 }
 
+static struct flexcan_mb __iomem *flexcan_get_mb(const struct flexcan_priv *priv,
+                                                u8 mb_index)
+{
+       u8 bank_size;
+       bool bank;
+
+       if (WARN_ON(mb_index >= priv->mb_count))
+               return NULL;
+
+       bank_size = sizeof(priv->regs->mb[0]) / priv->mb_size;
+
+       bank = mb_index >= bank_size;
+       if (bank)
+               mb_index -= bank_size;
+
+       return (struct flexcan_mb __iomem *)
+               (&priv->regs->mb[bank][priv->mb_size * mb_index]);
+}
+
+static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable)
+{
+       struct flexcan_regs __iomem *regs = priv->regs;
+       u32 reg_mcr;
+
+       reg_mcr = priv->read(&regs->mcr);
+
+       if (enable)
+               reg_mcr |= FLEXCAN_MCR_WAK_MSK;
+       else
+               reg_mcr &= ~FLEXCAN_MCR_WAK_MSK;
+
+       priv->write(reg_mcr, &regs->mcr);
+}
+
+static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv)
+{
+       struct flexcan_regs __iomem *regs = priv->regs;
+       u32 reg_mcr;
+
+       reg_mcr = priv->read(&regs->mcr);
+       reg_mcr |= FLEXCAN_MCR_SLF_WAK;
+       priv->write(reg_mcr, &regs->mcr);
+
+       /* enable stop request */
+       regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+                          1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
+}
+
+static inline void flexcan_exit_stop_mode(struct flexcan_priv *priv)
+{
+       struct flexcan_regs __iomem *regs = priv->regs;
+       u32 reg_mcr;
+
+       /* remove stop request */
+       regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+                          1 << priv->stm.req_bit, 0);
+
+       reg_mcr = priv->read(&regs->mcr);
+       reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
+       priv->write(reg_mcr, &regs->mcr);
+}
+
 static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv)
 {
        struct flexcan_regs __iomem *regs = priv->regs;
@@ -519,6 +594,7 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
        u32 can_id;
        u32 data;
        u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | (cf->can_dlc << 16);
+       int i;
 
        if (can_dropped_invalid_skb(dev, skb))
                return NETDEV_TX_OK;
@@ -535,13 +611,9 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
        if (cf->can_id & CAN_RTR_FLAG)
                ctrl |= FLEXCAN_MB_CNT_RTR;
 
-       if (cf->can_dlc > 0) {
-               data = be32_to_cpup((__be32 *)&cf->data[0]);
-               priv->write(data, &priv->tx_mb->data[0]);
-       }
-       if (cf->can_dlc > 4) {
-               data = be32_to_cpup((__be32 *)&cf->data[4]);
-               priv->write(data, &priv->tx_mb->data[1]);
+       for (i = 0; i < cf->can_dlc; i += sizeof(u32)) {
+               data = be32_to_cpup((__be32 *)&cf->data[i]);
+               priv->write(data, &priv->tx_mb->data[i / sizeof(u32)]);
        }
 
        can_put_echo_skb(skb, dev, 0);
@@ -553,9 +625,9 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
         * Write twice INACTIVE(0x8) code to first MB.
         */
        priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                     &priv->tx_mb_reserved->can_ctrl);
+                   &priv->tx_mb_reserved->can_ctrl);
        priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                     &priv->tx_mb_reserved->can_ctrl);
+                   &priv->tx_mb_reserved->can_ctrl);
 
        return NETDEV_TX_OK;
 }
@@ -563,9 +635,13 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
 static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr)
 {
        struct flexcan_priv *priv = netdev_priv(dev);
+       struct flexcan_regs __iomem *regs = priv->regs;
        struct sk_buff *skb;
        struct can_frame *cf;
        bool rx_errors = false, tx_errors = false;
+       u32 timestamp;
+
+       timestamp = priv->read(&regs->timer) << 16;
 
        skb = alloc_can_err_skb(dev, &cf);
        if (unlikely(!skb))
@@ -612,17 +688,21 @@ static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr)
        if (tx_errors)
                dev->stats.tx_errors++;
 
-       can_rx_offload_irq_queue_err_skb(&priv->offload, skb);
+       can_rx_offload_queue_sorted(&priv->offload, skb, timestamp);
 }
 
 static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
 {
        struct flexcan_priv *priv = netdev_priv(dev);
+       struct flexcan_regs __iomem *regs = priv->regs;
        struct sk_buff *skb;
        struct can_frame *cf;
        enum can_state new_state, rx_state, tx_state;
        int flt;
        struct can_berr_counter bec;
+       u32 timestamp;
+
+       timestamp = priv->read(&regs->timer) << 16;
 
        flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK;
        if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) {
@@ -652,7 +732,7 @@ static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
        if (unlikely(new_state == CAN_STATE_BUS_OFF))
                can_bus_off(dev);
 
-       can_rx_offload_irq_queue_err_skb(&priv->offload, skb);
+       can_rx_offload_queue_sorted(&priv->offload, skb, timestamp);
 }
 
 static inline struct flexcan_priv *rx_offload_to_priv(struct can_rx_offload *offload)
@@ -666,8 +746,11 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload,
 {
        struct flexcan_priv *priv = rx_offload_to_priv(offload);
        struct flexcan_regs __iomem *regs = priv->regs;
-       struct flexcan_mb __iomem *mb = &regs->mb[n];
+       struct flexcan_mb __iomem *mb;
        u32 reg_ctrl, reg_id, reg_iflag1;
+       int i;
+
+       mb = flexcan_get_mb(priv, n);
 
        if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
                u32 code;
@@ -708,8 +791,10 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload,
                cf->can_id |= CAN_RTR_FLAG;
        cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf);
 
-       *(__be32 *)(cf->data + 0) = cpu_to_be32(priv->read(&mb->data[0]));
-       *(__be32 *)(cf->data + 4) = cpu_to_be32(priv->read(&mb->data[1]));
+       for (i = 0; i < cf->can_dlc; i += sizeof(u32)) {
+               __be32 data = cpu_to_be32(priv->read(&mb->data[i / sizeof(u32)]));
+               *(__be32 *)(cf->data + i) = data;
+       }
 
        /* mark as read */
        if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
@@ -720,9 +805,14 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload,
                        priv->write(BIT(n - 32), &regs->iflag2);
        } else {
                priv->write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->iflag1);
-               priv->read(&regs->timer);
        }
 
+       /* Read the Free Running Timer. It is optional but recommended
+        * to unlock Mailbox as soon as possible and make it available
+        * for reception.
+        */
+       priv->read(&regs->timer);
+
        return 1;
 }
 
@@ -732,9 +822,9 @@ static inline u64 flexcan_read_reg_iflag_rx(struct flexcan_priv *priv)
        struct flexcan_regs __iomem *regs = priv->regs;
        u32 iflag1, iflag2;
 
-       iflag2 = priv->read(&regs->iflag2) & priv->reg_imask2_default;
-       iflag1 = priv->read(&regs->iflag1) & priv->reg_imask1_default &
+       iflag2 = priv->read(&regs->iflag2) & priv->reg_imask2_default &
                ~FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
+       iflag1 = priv->read(&regs->iflag1) & priv->reg_imask1_default;
 
        return (u64)iflag2 << 32 | iflag1;
 }
@@ -746,11 +836,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
        struct flexcan_priv *priv = netdev_priv(dev);
        struct flexcan_regs __iomem *regs = priv->regs;
        irqreturn_t handled = IRQ_NONE;
-       u32 reg_iflag1, reg_esr;
+       u32 reg_iflag2, reg_esr;
        enum can_state last_state = priv->can.state;
 
-       reg_iflag1 = priv->read(&regs->iflag1);
-
        /* reception interrupt */
        if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
                u64 reg_iflag;
@@ -764,6 +852,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
                                break;
                }
        } else {
+               u32 reg_iflag1;
+
+               reg_iflag1 = priv->read(&regs->iflag1);
                if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) {
                        handled = IRQ_HANDLED;
                        can_rx_offload_irq_offload_fifo(&priv->offload);
@@ -779,17 +870,22 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
                }
        }
 
+       reg_iflag2 = priv->read(&regs->iflag2);
+
        /* transmission complete interrupt */
-       if (reg_iflag1 & FLEXCAN_IFLAG_MB(priv->tx_mb_idx)) {
+       if (reg_iflag2 & FLEXCAN_IFLAG_MB(priv->tx_mb_idx)) {
+               u32 reg_ctrl = priv->read(&priv->tx_mb->can_ctrl);
+
                handled = IRQ_HANDLED;
-               stats->tx_bytes += can_get_echo_skb(dev, 0);
+               stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload,
+                                                              0, reg_ctrl << 16);
                stats->tx_packets++;
                can_led_event(dev, CAN_LED_EVENT_TX);
 
                /* after sending a RTR frame MB is in RX mode */
                priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
                            &priv->tx_mb->can_ctrl);
-               priv->write(FLEXCAN_IFLAG_MB(priv->tx_mb_idx), &regs->iflag1);
+               priv->write(FLEXCAN_IFLAG_MB(priv->tx_mb_idx), &regs->iflag2);
                netif_wake_queue(dev);
        }
 
@@ -804,7 +900,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
        /* state change interrupt or broken error state quirk fix is enabled */
        if ((reg_esr & FLEXCAN_ESR_ERR_STATE) ||
            (priv->devtype_data->quirks & (FLEXCAN_QUIRK_BROKEN_WERR_STATE |
-                                          FLEXCAN_QUIRK_BROKEN_PERR_STATE)))
+                                          FLEXCAN_QUIRK_BROKEN_PERR_STATE)))
                flexcan_irq_state(dev, reg_esr);
 
        /* bus error IRQ - handle if bus error reporting is activated */
@@ -902,6 +998,7 @@ static int flexcan_chip_start(struct net_device *dev)
        struct flexcan_regs __iomem *regs = priv->regs;
        u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr;
        int err, i;
+       struct flexcan_mb __iomem *mb;
 
        /* enable module */
        err = flexcan_chip_enable(priv);
@@ -918,11 +1015,9 @@ static int flexcan_chip_start(struct net_device *dev)
        /* MCR
         *
         * enable freeze
-        * enable fifo
         * halt now
         * only supervisor access
         * enable warning int
-        * disable local echo
         * enable individual RX masking
         * choose format C
         * set max mailbox number
@@ -930,16 +1025,37 @@ static int flexcan_chip_start(struct net_device *dev)
        reg_mcr = priv->read(&regs->mcr);
        reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
        reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV |
-               FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_IRMQ |
-               FLEXCAN_MCR_IDAM_C;
+               FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_IRMQ | FLEXCAN_MCR_IDAM_C |
+               FLEXCAN_MCR_MAXMB(priv->tx_mb_idx);
 
-       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
+       /* MCR
+        *
+        * FIFO:
+        * - disable for timestamp mode
+        * - enable for FIFO mode
+        */
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
                reg_mcr &= ~FLEXCAN_MCR_FEN;
-               reg_mcr |= FLEXCAN_MCR_MAXMB(priv->offload.mb_last);
-       } else {
-               reg_mcr |= FLEXCAN_MCR_FEN |
-                       FLEXCAN_MCR_MAXMB(priv->tx_mb_idx);
-       }
+       else
+               reg_mcr |= FLEXCAN_MCR_FEN;
+
+       /* MCR
+        *
+        * NOTE: In loopback mode, the CAN_MCR[SRXDIS] cannot be
+        *       asserted because this will impede the self reception
+        *       of a transmitted message. This is not documented in
+        *       earlier versions of flexcan block guide.
+        *
+        * Self Reception:
+        * - enable Self Reception for loopback mode
+        *   (by clearing "Self Reception Disable" bit)
+        * - disable for normal operation
+        */
+       if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+               reg_mcr &= ~FLEXCAN_MCR_SRX_DIS;
+       else
+               reg_mcr |= FLEXCAN_MCR_SRX_DIS;
+
        netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
        priv->write(reg_mcr, &regs->mcr);
 
@@ -982,16 +1098,19 @@ static int flexcan_chip_start(struct net_device *dev)
                priv->write(reg_ctrl2, &regs->ctrl2);
        }
 
-       /* clear and invalidate all mailboxes first */
-       for (i = priv->tx_mb_idx; i < ARRAY_SIZE(regs->mb); i++) {
-               priv->write(FLEXCAN_MB_CODE_RX_INACTIVE,
-                           &regs->mb[i].can_ctrl);
-       }
-
        if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
-               for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++)
+               for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++) {
+                       mb = flexcan_get_mb(priv, i);
                        priv->write(FLEXCAN_MB_CODE_RX_EMPTY,
-                                   &regs->mb[i].can_ctrl);
+                                   &mb->can_ctrl);
+               }
+       } else {
+               /* clear and invalidate unused mailboxes first */
+               for (i = FLEXCAN_TX_MB_RESERVED_OFF_FIFO; i <= priv->mb_count; i++) {
+                       mb = flexcan_get_mb(priv, i);
+                       priv->write(FLEXCAN_MB_CODE_RX_INACTIVE,
+                                   &mb->can_ctrl);
+               }
        }
 
        /* Errata ERR005829: mark first TX mailbox as INACTIVE */
@@ -1011,7 +1130,7 @@ static int flexcan_chip_start(struct net_device *dev)
                priv->write(0x0, &regs->rxfgmask);
 
        /* clear acceptance filters */
-       for (i = 0; i < ARRAY_SIZE(regs->mb); i++)
+       for (i = 0; i < priv->mb_count; i++)
                priv->write(0, &regs->rximr[i]);
 
        /* On Vybrid, disable memory error detection interrupts
@@ -1112,10 +1231,49 @@ static int flexcan_open(struct net_device *dev)
        if (err)
                goto out_close;
 
+       priv->mb_size = sizeof(struct flexcan_mb) + CAN_MAX_DLEN;
+       priv->mb_count = (sizeof(priv->regs->mb[0]) / priv->mb_size) +
+                        (sizeof(priv->regs->mb[1]) / priv->mb_size);
+
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
+               priv->tx_mb_reserved =
+                       flexcan_get_mb(priv, FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP);
+       else
+               priv->tx_mb_reserved =
+                       flexcan_get_mb(priv, FLEXCAN_TX_MB_RESERVED_OFF_FIFO);
+       priv->tx_mb_idx = priv->mb_count - 1;
+       priv->tx_mb = flexcan_get_mb(priv, priv->tx_mb_idx);
+
+       priv->reg_imask1_default = 0;
+       priv->reg_imask2_default = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
+
+       priv->offload.mailbox_read = flexcan_mailbox_read;
+
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
+               u64 imask;
+
+               priv->offload.mb_first = FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST;
+               priv->offload.mb_last = priv->mb_count - 2;
+
+               imask = GENMASK_ULL(priv->offload.mb_last,
+                                   priv->offload.mb_first);
+               priv->reg_imask1_default |= imask;
+               priv->reg_imask2_default |= imask >> 32;
+
+               err = can_rx_offload_add_timestamp(dev, &priv->offload);
+       } else {
+               priv->reg_imask1_default |= FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
+                       FLEXCAN_IFLAG_RX_FIFO_AVAILABLE;
+               err = can_rx_offload_add_fifo(dev, &priv->offload,
+                                             FLEXCAN_NAPI_WEIGHT);
+       }
+       if (err)
+               goto out_free_irq;
+
        /* start chip and queuing */
        err = flexcan_chip_start(dev);
        if (err)
-               goto out_free_irq;
+               goto out_offload_del;
 
        can_led_event(dev, CAN_LED_EVENT_OPEN);
 
@@ -1124,6 +1282,8 @@ static int flexcan_open(struct net_device *dev)
 
        return 0;
 
+ out_offload_del:
+       can_rx_offload_del(&priv->offload);
  out_free_irq:
        free_irq(dev->irq, dev);
  out_close:
@@ -1144,6 +1304,7 @@ static int flexcan_close(struct net_device *dev)
        can_rx_offload_disable(&priv->offload);
        flexcan_chip_stop(dev);
 
+       can_rx_offload_del(&priv->offload);
        free_irq(dev->irq, dev);
        clk_disable_unprepare(priv->clk_per);
        clk_disable_unprepare(priv->clk_ipg);
@@ -1244,6 +1405,59 @@ static void unregister_flexcandev(struct net_device *dev)
        unregister_candev(dev);
 }
 
+static int flexcan_setup_stop_mode(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *gpr_np;
+       struct flexcan_priv *priv;
+       phandle phandle;
+       u32 out_val[5];
+       int ret;
+
+       if (!np)
+               return -EINVAL;
+
+       /* stop mode property format is:
+        * <&gpr req_gpr req_bit ack_gpr ack_bit>.
+        */
+       ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val,
+                                        ARRAY_SIZE(out_val));
+       if (ret) {
+               dev_dbg(&pdev->dev, "no stop-mode property\n");
+               return ret;
+       }
+       phandle = *out_val;
+
+       gpr_np = of_find_node_by_phandle(phandle);
+       if (!gpr_np) {
+               dev_dbg(&pdev->dev, "could not find gpr node by phandle\n");
+               return PTR_ERR(gpr_np);
+       }
+
+       priv = netdev_priv(dev);
+       priv->stm.gpr = syscon_node_to_regmap(gpr_np);
+       of_node_put(gpr_np);
+       if (IS_ERR(priv->stm.gpr)) {
+               dev_dbg(&pdev->dev, "could not find gpr regmap\n");
+               return PTR_ERR(priv->stm.gpr);
+       }
+
+       priv->stm.req_gpr = out_val[1];
+       priv->stm.req_bit = out_val[2];
+       priv->stm.ack_gpr = out_val[3];
+       priv->stm.ack_bit = out_val[4];
+
+       dev_dbg(&pdev->dev,
+               "gpr %s req_gpr=0x02%x req_bit=%u ack_gpr=0x02%x ack_bit=%u\n",
+               gpr_np->full_name, priv->stm.req_gpr, priv->stm.req_bit,
+               priv->stm.ack_gpr, priv->stm.ack_bit);
+
+       device_set_wakeup_capable(&pdev->dev, true);
+
+       return 0;
+}
+
 static const struct of_device_id flexcan_of_match[] = {
        { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
        { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
@@ -1355,39 +1569,6 @@ static int flexcan_probe(struct platform_device *pdev)
        priv->devtype_data = devtype_data;
        priv->reg_xceiver = reg_xceiver;
 
-       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
-               priv->tx_mb_idx = FLEXCAN_TX_MB_OFF_TIMESTAMP;
-               priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP];
-       } else {
-               priv->tx_mb_idx = FLEXCAN_TX_MB_OFF_FIFO;
-               priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_FIFO];
-       }
-       priv->tx_mb = &regs->mb[priv->tx_mb_idx];
-
-       priv->reg_imask1_default = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
-       priv->reg_imask2_default = 0;
-
-       priv->offload.mailbox_read = flexcan_mailbox_read;
-
-       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
-               u64 imask;
-
-               priv->offload.mb_first = FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST;
-               priv->offload.mb_last = FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST;
-
-               imask = GENMASK_ULL(priv->offload.mb_last, priv->offload.mb_first);
-               priv->reg_imask1_default |= imask;
-               priv->reg_imask2_default |= imask >> 32;
-
-               err = can_rx_offload_add_timestamp(dev, &priv->offload);
-       } else {
-               priv->reg_imask1_default |= FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
-                       FLEXCAN_IFLAG_RX_FIFO_AVAILABLE;
-               err = can_rx_offload_add_fifo(dev, &priv->offload, FLEXCAN_NAPI_WEIGHT);
-       }
-       if (err)
-               goto failed_offload;
-
        err = register_flexcandev(dev);
        if (err) {
                dev_err(&pdev->dev, "registering netdev failed\n");
@@ -1396,12 +1577,17 @@ static int flexcan_probe(struct platform_device *pdev)
 
        devm_can_led_init(dev);
 
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE) {
+               err = flexcan_setup_stop_mode(pdev);
+               if (err)
+                       dev_dbg(&pdev->dev, "failed to setup stop-mode\n");
+       }
+
        dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n",
                 priv->regs, dev->irq);
 
        return 0;
 
- failed_offload:
  failed_register:
        free_candev(dev);
        return err;
@@ -1410,10 +1596,8 @@ static int flexcan_probe(struct platform_device *pdev)
 static int flexcan_remove(struct platform_device *pdev)
 {
        struct net_device *dev = platform_get_drvdata(pdev);
-       struct flexcan_priv *priv = netdev_priv(dev);
 
        unregister_flexcandev(dev);
-       can_rx_offload_del(&priv->offload);
        free_candev(dev);
 
        return 0;
@@ -1426,9 +1610,17 @@ static int __maybe_unused flexcan_suspend(struct device *device)
        int err;
 
        if (netif_running(dev)) {
-               err = flexcan_chip_disable(priv);
-               if (err)
-                       return err;
+               /* if wakeup is enabled, enter stop mode
+                * else enter disabled mode.
+                */
+               if (device_may_wakeup(device)) {
+                       enable_irq_wake(dev->irq);
+                       flexcan_enter_stop_mode(priv);
+               } else {
+                       err = flexcan_chip_disable(priv);
+                       if (err)
+                               return err;
+               }
                netif_stop_queue(dev);
                netif_device_detach(dev);
        }
@@ -1447,14 +1639,45 @@ static int __maybe_unused flexcan_resume(struct device *device)
        if (netif_running(dev)) {
                netif_device_attach(dev);
                netif_start_queue(dev);
-               err = flexcan_chip_enable(priv);
-               if (err)
-                       return err;
+               if (device_may_wakeup(device)) {
+                       disable_irq_wake(dev->irq);
+               } else {
+                       err = flexcan_chip_enable(priv);
+                       if (err)
+                               return err;
+               }
+       }
+       return 0;
+}
+
+static int __maybe_unused flexcan_noirq_suspend(struct device *device)
+{
+       struct net_device *dev = dev_get_drvdata(device);
+       struct flexcan_priv *priv = netdev_priv(dev);
+
+       if (netif_running(dev) && device_may_wakeup(device))
+               flexcan_enable_wakeup_irq(priv, true);
+
+       return 0;
+}
+
+static int __maybe_unused flexcan_noirq_resume(struct device *device)
+{
+       struct net_device *dev = dev_get_drvdata(device);
+       struct flexcan_priv *priv = netdev_priv(dev);
+
+       if (netif_running(dev) && device_may_wakeup(device)) {
+               flexcan_enable_wakeup_irq(priv, false);
+               flexcan_exit_stop_mode(priv);
        }
+
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume);
+static const struct dev_pm_ops flexcan_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(flexcan_suspend, flexcan_resume)
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(flexcan_noirq_suspend, flexcan_noirq_resume)
+};
 
 static struct platform_driver flexcan_driver = {
        .driver = {
index 7b03a3a..bd5a8fc 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
 config CAN_RCAR
        tristate "Renesas R-Car CAN controller"
        depends on ARCH_RENESAS || ARM
index 08de36a..c9185b0 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
 #
 #  Makefile for the Renesas R-Car CAN & CAN FD controller drivers
 #
index 11662f4..13e6629 100644 (file)
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
 /* Renesas R-Car CAN device driver
  *
  * Copyright (C) 2013 Cogent Embedded, Inc. <source@cogentembedded.com>
  * Copyright (C) 2013 Renesas Solutions Corp.
- *
- * 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 <linux/module.h>
@@ -24,6 +20,9 @@
 
 #define RCAR_CAN_DRV_NAME      "rcar_can"
 
+#define RCAR_SUPPORTED_CLOCKS  (BIT(CLKR_CLKP1) | BIT(CLKR_CLKP2) | \
+                                BIT(CLKR_CLKEXT))
+
 /* Mailbox configuration:
  * mailbox 60 - 63 - Rx FIFO mailboxes
  * mailbox 56 - 59 - Tx FIFO mailboxes
@@ -789,7 +788,7 @@ static int rcar_can_probe(struct platform_device *pdev)
                goto fail_clk;
        }
 
-       if (clock_select >= ARRAY_SIZE(clock_names)) {
+       if (!(BIT(clock_select) & RCAR_SUPPORTED_CLOCKS)) {
                err = -EINVAL;
                dev_err(&pdev->dev, "invalid CAN clock selected\n");
                goto fail_clk;
index 602c19e..0541000 100644 (file)
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
 /* Renesas R-Car CAN FD device driver
  *
  * Copyright (C) 2015 Renesas Electronics Corp.
- *
- * 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.
  */
 
 /* The R-Car CAN FD controller can operate in either one of the below two modes
index c7d0502..2ce4fa8 100644 (file)
@@ -211,7 +211,54 @@ int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload)
 }
 EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_fifo);
 
-int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_buff *skb)
+int can_rx_offload_queue_sorted(struct can_rx_offload *offload,
+                               struct sk_buff *skb, u32 timestamp)
+{
+       struct can_rx_offload_cb *cb;
+       unsigned long flags;
+
+       if (skb_queue_len(&offload->skb_queue) >
+           offload->skb_queue_len_max)
+               return -ENOMEM;
+
+       cb = can_rx_offload_get_cb(skb);
+       cb->timestamp = timestamp;
+
+       spin_lock_irqsave(&offload->skb_queue.lock, flags);
+       __skb_queue_add_sort(&offload->skb_queue, skb, can_rx_offload_compare);
+       spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
+
+       can_rx_offload_schedule(offload);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_queue_sorted);
+
+unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
+                                        unsigned int idx, u32 timestamp)
+{
+       struct net_device *dev = offload->dev;
+       struct net_device_stats *stats = &dev->stats;
+       struct sk_buff *skb;
+       u8 len;
+       int err;
+
+       skb = __can_get_echo_skb(dev, idx, &len);
+       if (!skb)
+               return 0;
+
+       err = can_rx_offload_queue_sorted(offload, skb, timestamp);
+       if (err) {
+               stats->rx_errors++;
+               stats->tx_fifo_errors++;
+       }
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb);
+
+int can_rx_offload_queue_tail(struct can_rx_offload *offload,
+                             struct sk_buff *skb)
 {
        if (skb_queue_len(&offload->skb_queue) >
            offload->skb_queue_len_max)
@@ -222,7 +269,7 @@ int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_b
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(can_rx_offload_irq_queue_err_skb);
+EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail);
 
 static int can_rx_offload_init_queue(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight)
 {
index 1e65cb6..f6dc899 100644 (file)
@@ -88,6 +88,7 @@ config CAN_PLX_PCI
           - TEWS TECHNOLOGIES TPMC810 card (http://www.tews.com/)
           - IXXAT Automation PC-I 04/PCI card (http://www.ixxat.com/)
           - Connect Tech Inc. CANpro/104-Plus Opto (CRG001) card (http://www.connecttech.com)
+          - ASEM CAN raw - 2 isolated CAN channels (www.asem.it)
 
 config CAN_TSCAN1
        tristate "TS-CAN1 PC104 boards"
index f8ff25c..9bcdefe 100644 (file)
@@ -46,7 +46,8 @@ MODULE_SUPPORTED_DEVICE("Adlink PCI-7841/cPCI-7841, "
                        "esd CAN-PCIe/2000, "
                        "Connect Tech Inc. CANpro/104-Plus Opto (CRG001), "
                        "IXXAT PC-I 04/PCI, "
-                       "ELCUS CAN-200-PCI")
+                       "ELCUS CAN-200-PCI, "
+                       "ASEM DUAL CAN-RAW")
 MODULE_LICENSE("GPL v2");
 
 #define PLX_PCI_MAX_CHAN 2
@@ -70,7 +71,9 @@ struct plx_pci_card {
                                         */
 
 #define PLX_LINT1_EN   0x1             /* Local interrupt 1 enable */
+#define PLX_LINT1_POL  (1 << 1)        /* Local interrupt 1 polarity */
 #define PLX_LINT2_EN   (1 << 3)        /* Local interrupt 2 enable */
+#define PLX_LINT2_POL  (1 << 4)        /* Local interrupt 2 polarity */
 #define PLX_PCI_INT_EN (1 << 6)        /* PCI Interrupt Enable */
 #define PLX_PCI_RESET  (1 << 30)       /* PCI Adapter Software Reset */
 
@@ -92,6 +95,9 @@ struct plx_pci_card {
  */
 #define PLX_PCI_OCR    (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
 
+/* OCR setting for ASEM Dual CAN raw */
+#define ASEM_PCI_OCR   0xfe
+
 /*
  * In the CDR register, you should set CBP to 1.
  * You will probably also want to set the clock divider value to 7
@@ -145,10 +151,20 @@ struct plx_pci_card {
 #define MOXA_PCI_VENDOR_ID             0x1393
 #define MOXA_PCI_DEVICE_ID             0x0100
 
+#define ASEM_RAW_CAN_VENDOR_ID         0x10b5
+#define ASEM_RAW_CAN_DEVICE_ID         0x9030
+#define ASEM_RAW_CAN_SUB_VENDOR_ID     0x3000
+#define ASEM_RAW_CAN_SUB_DEVICE_ID     0x1001
+#define ASEM_RAW_CAN_SUB_DEVICE_ID_BIS 0x1002
+#define ASEM_RAW_CAN_RST_REGISTER      0x54
+#define ASEM_RAW_CAN_RST_MASK_CAN1     0x20
+#define ASEM_RAW_CAN_RST_MASK_CAN2     0x04
+
 static void plx_pci_reset_common(struct pci_dev *pdev);
 static void plx9056_pci_reset_common(struct pci_dev *pdev);
 static void plx_pci_reset_marathon_pci(struct pci_dev *pdev);
 static void plx_pci_reset_marathon_pcie(struct pci_dev *pdev);
+static void plx_pci_reset_asem_dual_can_raw(struct pci_dev *pdev);
 
 struct plx_pci_channel_map {
        u32 bar;
@@ -269,6 +285,14 @@ static struct plx_pci_card_info plx_pci_card_info_moxa = {
         /* based on PLX9052 */
 };
 
+static struct plx_pci_card_info plx_pci_card_info_asem_dual_can = {
+       "ASEM Dual CAN raw PCI", 2,
+       PLX_PCI_CAN_CLOCK, ASEM_PCI_OCR, PLX_PCI_CDR,
+       {0, 0x00, 0x00}, { {2, 0x00, 0x00}, {4, 0x00, 0x00} },
+       &plx_pci_reset_asem_dual_can_raw
+       /* based on PLX9030 */
+};
+
 static const struct pci_device_id plx_pci_tbl[] = {
        {
                /* Adlink PCI-7841/cPCI-7841 */
@@ -375,6 +399,20 @@ static const struct pci_device_id plx_pci_tbl[] = {
                0, 0,
                (kernel_ulong_t)&plx_pci_card_info_moxa
        },
+       {
+               /* ASEM Dual CAN raw */
+               ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,
+               ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID,
+               0, 0,
+               (kernel_ulong_t)&plx_pci_card_info_asem_dual_can
+       },
+       {
+               /* ASEM Dual CAN raw -new model */
+               ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,
+               ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID_BIS,
+               0, 0,
+               (kernel_ulong_t)&plx_pci_card_info_asem_dual_can
+       },
        { 0,}
 };
 MODULE_DEVICE_TABLE(pci, plx_pci_tbl);
@@ -524,6 +562,31 @@ static void plx_pci_reset_marathon_pcie(struct pci_dev *pdev)
        }
 }
 
+/* Special reset function for ASEM Dual CAN raw card */
+static void plx_pci_reset_asem_dual_can_raw(struct pci_dev *pdev)
+{
+       void __iomem *bar0_addr;
+       u8 tmpval;
+
+       plx_pci_reset_common(pdev);
+
+       bar0_addr = pci_iomap(pdev, 0, 0);
+       if (!bar0_addr) {
+               dev_err(&pdev->dev, "Failed to remap reset space 0 (BAR0)\n");
+               return;
+       }
+
+       /* reset the two SJA1000 chips */
+       tmpval = ioread8(bar0_addr + ASEM_RAW_CAN_RST_REGISTER);
+       tmpval &= ~(ASEM_RAW_CAN_RST_MASK_CAN1 | ASEM_RAW_CAN_RST_MASK_CAN2);
+       iowrite8(tmpval, bar0_addr + ASEM_RAW_CAN_RST_REGISTER);
+       usleep_range(300, 400);
+       tmpval |= ASEM_RAW_CAN_RST_MASK_CAN1 | ASEM_RAW_CAN_RST_MASK_CAN2;
+       iowrite8(tmpval, bar0_addr + ASEM_RAW_CAN_RST_REGISTER);
+       usleep_range(300, 400);
+       pci_iounmap(pdev, bar0_addr);
+}
+
 static void plx_pci_del_card(struct pci_dev *pdev)
 {
        struct plx_pci_card *card = pci_get_drvdata(pdev);
index 53e320c..ddaf462 100644 (file)
@@ -760,7 +760,7 @@ static int hi3110_open(struct net_device *net)
 {
        struct hi3110_priv *priv = netdev_priv(net);
        struct spi_device *spi = priv->spi;
-       unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_RISING;
+       unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_HIGH;
        int ret;
 
        ret = open_candev(net);
index b939a4c..c89c7d4 100644 (file)
@@ -528,7 +528,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
                        context = &priv->tx_contexts[i];
 
                        context->echo_index = i;
-                       can_put_echo_skb(skb, netdev, context->echo_index);
                        ++priv->active_tx_contexts;
                        if (priv->active_tx_contexts >= (int)dev->max_tx_urbs)
                                netif_stop_queue(netdev);
@@ -553,7 +552,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
                dev_kfree_skb(skb);
                spin_lock_irqsave(&priv->tx_contexts_lock, flags);
 
-               can_free_echo_skb(netdev, context->echo_index);
                context->echo_index = dev->max_tx_urbs;
                --priv->active_tx_contexts;
                netif_wake_queue(netdev);
@@ -564,6 +562,8 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
 
        context->priv = priv;
 
+       can_put_echo_skb(skb, netdev, context->echo_index);
+
        usb_fill_bulk_urb(urb, dev->udev,
                          usb_sndbulkpipe(dev->udev,
                                          dev->bulk_out->bEndpointAddress),
index c084bae..5fc0be5 100644 (file)
@@ -1019,6 +1019,11 @@ kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv,
                                        new_state : CAN_STATE_ERROR_ACTIVE;
 
                        can_change_state(netdev, cf, tx_state, rx_state);
+
+                       if (priv->can.restart_ms &&
+                           old_state >= CAN_STATE_BUS_OFF &&
+                           new_state < CAN_STATE_BUS_OFF)
+                               cf->can_id |= CAN_ERR_RESTARTED;
                }
 
                if (new_state == CAN_STATE_BUS_OFF) {
@@ -1028,11 +1033,6 @@ kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv,
 
                        can_bus_off(netdev);
                }
-
-               if (priv->can.restart_ms &&
-                   old_state >= CAN_STATE_BUS_OFF &&
-                   new_state < CAN_STATE_BUS_OFF)
-                       cf->can_id |= CAN_ERR_RESTARTED;
        }
 
        if (!skb) {
index 0678a38..04aac3b 100644 (file)
 #include <linux/slab.h>
 #include <linux/usb.h>
 
-#include <linux/can.h>
-#include <linux/can/dev.h>
-#include <linux/can/error.h>
-
 #define UCAN_DRIVER_NAME "ucan"
 #define UCAN_MAX_RX_URBS 8
 /* the CAN controller needs a while to enable/disable the bus */
@@ -719,7 +715,7 @@ static void ucan_read_bulk_callback(struct urb *urb)
                                  up->in_ep_size,
                                  urb->transfer_buffer,
                                  urb->transfer_dma);
-               netdev_dbg(up->netdev, "not resumbmitting urb; status: %d\n",
+               netdev_dbg(up->netdev, "not resubmitting urb; status: %d\n",
                           urb->status);
                return;
        default:
@@ -1575,11 +1571,8 @@ err_firmware_needs_update:
 /* disconnect the device */
 static void ucan_disconnect(struct usb_interface *intf)
 {
-       struct usb_device *udev;
        struct ucan_priv *up = usb_get_intfdata(intf);
 
-       udev = interface_to_usbdev(intf);
-
        usb_set_intfdata(intf, NULL);
 
        if (up) {
index 045f084..97d0933 100644 (file)
@@ -63,6 +63,7 @@ enum xcan_reg {
        XCAN_FSR_OFFSET         = 0x00E8, /* RX FIFO Status */
        XCAN_TXMSG_BASE_OFFSET  = 0x0100, /* TX Message Space */
        XCAN_RXMSG_BASE_OFFSET  = 0x1100, /* RX Message Space */
+       XCAN_RXMSG_2_BASE_OFFSET        = 0x2100, /* RX Message Space */
 };
 
 #define XCAN_FRAME_ID_OFFSET(frame_base)       ((frame_base) + 0x00)
@@ -75,6 +76,8 @@ enum xcan_reg {
                                         XCAN_CANFD_FRAME_SIZE * (n))
 #define XCAN_RXMSG_FRAME_OFFSET(n)     (XCAN_RXMSG_BASE_OFFSET + \
                                         XCAN_CANFD_FRAME_SIZE * (n))
+#define XCAN_RXMSG_2_FRAME_OFFSET(n)   (XCAN_RXMSG_2_BASE_OFFSET + \
+                                        XCAN_CANFD_FRAME_SIZE * (n))
 
 /* the single TX mailbox used by this driver on CAN FD HW */
 #define XCAN_TX_MAILBOX_IDX            0
@@ -152,6 +155,7 @@ enum xcan_reg {
  * instead of the regular FIFO at 0x50
  */
 #define XCAN_FLAG_RX_FIFO_MULTI        0x0010
+#define XCAN_FLAG_CANFD_2      0x0020
 
 struct xcan_devtype_data {
        unsigned int flags;
@@ -221,6 +225,18 @@ static const struct can_bittiming_const xcan_bittiming_const_canfd = {
        .brp_inc = 1,
 };
 
+static const struct can_bittiming_const xcan_bittiming_const_canfd2 = {
+       .name = DRIVER_NAME,
+       .tseg1_min = 1,
+       .tseg1_max = 256,
+       .tseg2_min = 1,
+       .tseg2_max = 128,
+       .sjw_max = 128,
+       .brp_min = 1,
+       .brp_max = 256,
+       .brp_inc = 1,
+};
+
 /**
  * xcan_write_reg_le - Write a value to the device register little endian
  * @priv:      Driver private data structure
@@ -612,7 +628,7 @@ static int xcan_start_xmit_mailbox(struct sk_buff *skb, struct net_device *ndev)
  *
  * Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY when the tx queue is full
  */
-static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
        struct xcan_priv *priv = netdev_priv(ndev);
        int ret;
@@ -973,7 +989,10 @@ static int xcan_rx_fifo_get_next_frame(struct xcan_priv *priv)
                if (!(fsr & XCAN_FSR_FL_MASK))
                        return -ENOENT;
 
-               offset = XCAN_RXMSG_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
+               if (priv->devtype.flags & XCAN_FLAG_CANFD_2)
+                       offset = XCAN_RXMSG_2_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
+               else
+                       offset = XCAN_RXMSG_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
 
        } else {
                /* check if RX FIFO is empty */
@@ -1430,11 +1449,24 @@ static const struct xcan_devtype_data xcan_canfd_data = {
        .bus_clk_name = "s_axi_aclk",
 };
 
+static const struct xcan_devtype_data xcan_canfd2_data = {
+       .flags = XCAN_FLAG_EXT_FILTERS |
+                XCAN_FLAG_RXMNF |
+                XCAN_FLAG_TX_MAILBOXES |
+                XCAN_FLAG_CANFD_2 |
+                XCAN_FLAG_RX_FIFO_MULTI,
+       .bittiming_const = &xcan_bittiming_const_canfd2,
+       .btr_ts2_shift = XCAN_BTR_TS2_SHIFT_CANFD,
+       .btr_sjw_shift = XCAN_BTR_SJW_SHIFT_CANFD,
+       .bus_clk_name = "s_axi_aclk",
+};
+
 /* Match table for OF platform binding */
 static const struct of_device_id xcan_of_match[] = {
        { .compatible = "xlnx,zynq-can-1.0", .data = &xcan_zynq_data },
        { .compatible = "xlnx,axi-can-1.00.a", .data = &xcan_axi_data },
        { .compatible = "xlnx,canfd-1.0", .data = &xcan_canfd_data },
+       { .compatible = "xlnx,canfd-2.0", .data = &xcan_canfd2_data },
        { /* end of list */ },
 };
 MODULE_DEVICE_TABLE(of, xcan_of_match);
index a8b8f59..bea29fd 100644 (file)
@@ -1,12 +1,16 @@
-menuconfig MICROCHIP_KSZ
-       tristate "Microchip KSZ series switch support"
+config NET_DSA_MICROCHIP_KSZ_COMMON
+       tristate
+
+menuconfig NET_DSA_MICROCHIP_KSZ9477
+       tristate "Microchip KSZ9477 series switch support"
        depends on NET_DSA
-       select NET_DSA_TAG_KSZ
+       select NET_DSA_TAG_KSZ9477
+       select NET_DSA_MICROCHIP_KSZ_COMMON
        help
-         This driver adds support for Microchip KSZ switch chips.
+         This driver adds support for Microchip KSZ9477 switch chips.
 
-config MICROCHIP_KSZ_SPI_DRIVER
-       tristate "KSZ series SPI connected switch driver"
-       depends on MICROCHIP_KSZ && SPI
+config NET_DSA_MICROCHIP_KSZ9477_SPI
+       tristate "KSZ9477 series SPI connected switch driver"
+       depends on NET_DSA_MICROCHIP_KSZ9477 && SPI
        help
          Select to enable support for registering switches configured through SPI.
index ed335e2..3142c18 100644 (file)
@@ -1,2 +1,3 @@
-obj-$(CONFIG_MICROCHIP_KSZ)            += ksz_common.o
-obj-$(CONFIG_MICROCHIP_KSZ_SPI_DRIVER) += ksz_spi.o
+obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON)     += ksz_common.o
+obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477)                += ksz9477.o
+obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI)    += ksz9477_spi.o
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
new file mode 100644 (file)
index 0000000..89ed059
--- /dev/null
@@ -0,0 +1,1316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip KSZ9477 switch driver main logic
+ *
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_data/microchip-ksz.h>
+#include <linux/phy.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <net/dsa.h>
+#include <net/switchdev.h>
+
+#include "ksz_priv.h"
+#include "ksz_common.h"
+#include "ksz9477_reg.h"
+
+static const struct {
+       int index;
+       char string[ETH_GSTRING_LEN];
+} ksz9477_mib_names[TOTAL_SWITCH_COUNTER_NUM] = {
+       { 0x00, "rx_hi" },
+       { 0x01, "rx_undersize" },
+       { 0x02, "rx_fragments" },
+       { 0x03, "rx_oversize" },
+       { 0x04, "rx_jabbers" },
+       { 0x05, "rx_symbol_err" },
+       { 0x06, "rx_crc_err" },
+       { 0x07, "rx_align_err" },
+       { 0x08, "rx_mac_ctrl" },
+       { 0x09, "rx_pause" },
+       { 0x0A, "rx_bcast" },
+       { 0x0B, "rx_mcast" },
+       { 0x0C, "rx_ucast" },
+       { 0x0D, "rx_64_or_less" },
+       { 0x0E, "rx_65_127" },
+       { 0x0F, "rx_128_255" },
+       { 0x10, "rx_256_511" },
+       { 0x11, "rx_512_1023" },
+       { 0x12, "rx_1024_1522" },
+       { 0x13, "rx_1523_2000" },
+       { 0x14, "rx_2001" },
+       { 0x15, "tx_hi" },
+       { 0x16, "tx_late_col" },
+       { 0x17, "tx_pause" },
+       { 0x18, "tx_bcast" },
+       { 0x19, "tx_mcast" },
+       { 0x1A, "tx_ucast" },
+       { 0x1B, "tx_deferred" },
+       { 0x1C, "tx_total_col" },
+       { 0x1D, "tx_exc_col" },
+       { 0x1E, "tx_single_col" },
+       { 0x1F, "tx_mult_col" },
+       { 0x80, "rx_total" },
+       { 0x81, "tx_total" },
+       { 0x82, "rx_discards" },
+       { 0x83, "tx_discards" },
+};
+
+static void ksz9477_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool set)
+{
+       u32 data;
+
+       ksz_read32(dev, addr, &data);
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+       ksz_write32(dev, addr, data);
+}
+
+static void ksz9477_port_cfg32(struct ksz_device *dev, int port, int offset,
+                              u32 bits, bool set)
+{
+       u32 addr;
+       u32 data;
+
+       addr = PORT_CTRL_ADDR(port, offset);
+       ksz_read32(dev, addr, &data);
+
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+
+       ksz_write32(dev, addr, data);
+}
+
+static int ksz9477_wait_vlan_ctrl_ready(struct ksz_device *dev, u32 waiton,
+                                       int timeout)
+{
+       u8 data;
+
+       do {
+               ksz_read8(dev, REG_SW_VLAN_CTRL, &data);
+               if (!(data & waiton))
+                       break;
+               usleep_range(1, 10);
+       } while (timeout-- > 0);
+
+       if (timeout <= 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ksz9477_get_vlan_table(struct ksz_device *dev, u16 vid,
+                                 u32 *vlan_table)
+{
+       int ret;
+
+       mutex_lock(&dev->vlan_mutex);
+
+       ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
+       ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_READ | VLAN_START);
+
+       /* wait to be cleared */
+       ret = ksz9477_wait_vlan_ctrl_ready(dev, VLAN_START, 1000);
+       if (ret < 0) {
+               dev_dbg(dev->dev, "Failed to read vlan table\n");
+               goto exit;
+       }
+
+       ksz_read32(dev, REG_SW_VLAN_ENTRY__4, &vlan_table[0]);
+       ksz_read32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, &vlan_table[1]);
+       ksz_read32(dev, REG_SW_VLAN_ENTRY_PORTS__4, &vlan_table[2]);
+
+       ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
+
+exit:
+       mutex_unlock(&dev->vlan_mutex);
+
+       return ret;
+}
+
+static int ksz9477_set_vlan_table(struct ksz_device *dev, u16 vid,
+                                 u32 *vlan_table)
+{
+       int ret;
+
+       mutex_lock(&dev->vlan_mutex);
+
+       ksz_write32(dev, REG_SW_VLAN_ENTRY__4, vlan_table[0]);
+       ksz_write32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, vlan_table[1]);
+       ksz_write32(dev, REG_SW_VLAN_ENTRY_PORTS__4, vlan_table[2]);
+
+       ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
+       ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_START | VLAN_WRITE);
+
+       /* wait to be cleared */
+       ret = ksz9477_wait_vlan_ctrl_ready(dev, VLAN_START, 1000);
+       if (ret < 0) {
+               dev_dbg(dev->dev, "Failed to write vlan table\n");
+               goto exit;
+       }
+
+       ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
+
+       /* update vlan cache table */
+       dev->vlan_cache[vid].table[0] = vlan_table[0];
+       dev->vlan_cache[vid].table[1] = vlan_table[1];
+       dev->vlan_cache[vid].table[2] = vlan_table[2];
+
+exit:
+       mutex_unlock(&dev->vlan_mutex);
+
+       return ret;
+}
+
+static void ksz9477_read_table(struct ksz_device *dev, u32 *table)
+{
+       ksz_read32(dev, REG_SW_ALU_VAL_A, &table[0]);
+       ksz_read32(dev, REG_SW_ALU_VAL_B, &table[1]);
+       ksz_read32(dev, REG_SW_ALU_VAL_C, &table[2]);
+       ksz_read32(dev, REG_SW_ALU_VAL_D, &table[3]);
+}
+
+static void ksz9477_write_table(struct ksz_device *dev, u32 *table)
+{
+       ksz_write32(dev, REG_SW_ALU_VAL_A, table[0]);
+       ksz_write32(dev, REG_SW_ALU_VAL_B, table[1]);
+       ksz_write32(dev, REG_SW_ALU_VAL_C, table[2]);
+       ksz_write32(dev, REG_SW_ALU_VAL_D, table[3]);
+}
+
+static int ksz9477_wait_alu_ready(struct ksz_device *dev, u32 waiton,
+                                 int timeout)
+{
+       u32 data;
+
+       do {
+               ksz_read32(dev, REG_SW_ALU_CTRL__4, &data);
+               if (!(data & waiton))
+                       break;
+               usleep_range(1, 10);
+       } while (timeout-- > 0);
+
+       if (timeout <= 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ksz9477_wait_alu_sta_ready(struct ksz_device *dev, u32 waiton,
+                                     int timeout)
+{
+       u32 data;
+
+       do {
+               ksz_read32(dev, REG_SW_ALU_STAT_CTRL__4, &data);
+               if (!(data & waiton))
+                       break;
+               usleep_range(1, 10);
+       } while (timeout-- > 0);
+
+       if (timeout <= 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ksz9477_reset_switch(struct ksz_device *dev)
+{
+       u8 data8;
+       u16 data16;
+       u32 data32;
+
+       /* reset switch */
+       ksz_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
+
+       /* turn off SPI DO Edge select */
+       ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);
+       data8 &= ~SPI_AUTO_EDGE_DETECTION;
+       ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);
+
+       /* default configuration */
+       ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
+       data8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING |
+             SW_SRC_ADDR_FILTER | SW_FLUSH_STP_TABLE | SW_FLUSH_MSTP_TABLE;
+       ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+
+       /* disable interrupts */
+       ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
+       ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0x7F);
+       ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
+
+       /* set broadcast storm protection 10% rate */
+       ksz_read16(dev, REG_SW_MAC_CTRL_2, &data16);
+       data16 &= ~BROADCAST_STORM_RATE;
+       data16 |= (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
+       ksz_write16(dev, REG_SW_MAC_CTRL_2, data16);
+
+       return 0;
+}
+
+static enum dsa_tag_protocol ksz9477_get_tag_protocol(struct dsa_switch *ds,
+                                                     int port)
+{
+       return DSA_TAG_PROTO_KSZ9477;
+}
+
+static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg)
+{
+       struct ksz_device *dev = ds->priv;
+       u16 val = 0xffff;
+
+       /* No real PHY after this. Simulate the PHY.
+        * A fixed PHY can be setup in the device tree, but this function is
+        * still called for that port during initialization.
+        * For RGMII PHY there is no way to access it so the fixed PHY should
+        * be used.  For SGMII PHY the supporting code will be added later.
+        */
+       if (addr >= dev->phy_port_cnt) {
+               struct ksz_port *p = &dev->ports[addr];
+
+               switch (reg) {
+               case MII_BMCR:
+                       val = 0x1140;
+                       break;
+               case MII_BMSR:
+                       val = 0x796d;
+                       break;
+               case MII_PHYSID1:
+                       val = 0x0022;
+                       break;
+               case MII_PHYSID2:
+                       val = 0x1631;
+                       break;
+               case MII_ADVERTISE:
+                       val = 0x05e1;
+                       break;
+               case MII_LPA:
+                       val = 0xc5e1;
+                       break;
+               case MII_CTRL1000:
+                       val = 0x0700;
+                       break;
+               case MII_STAT1000:
+                       if (p->phydev.speed == SPEED_1000)
+                               val = 0x3800;
+                       else
+                               val = 0;
+                       break;
+               }
+       } else {
+               ksz_pread16(dev, addr, 0x100 + (reg << 1), &val);
+       }
+
+       return val;
+}
+
+static int ksz9477_phy_write16(struct dsa_switch *ds, int addr, int reg,
+                              u16 val)
+{
+       struct ksz_device *dev = ds->priv;
+
+       /* No real PHY after this. */
+       if (addr >= dev->phy_port_cnt)
+               return 0;
+       ksz_pwrite16(dev, addr, 0x100 + (reg << 1), val);
+
+       return 0;
+}
+
+static void ksz9477_get_strings(struct dsa_switch *ds, int port,
+                               u32 stringset, uint8_t *buf)
+{
+       int i;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
+               memcpy(buf + i * ETH_GSTRING_LEN, ksz9477_mib_names[i].string,
+                      ETH_GSTRING_LEN);
+       }
+}
+
+static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port,
+                                 uint64_t *buf)
+{
+       struct ksz_device *dev = ds->priv;
+       int i;
+       u32 data;
+       int timeout;
+
+       mutex_lock(&dev->stats_mutex);
+
+       for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
+               data = MIB_COUNTER_READ;
+               data |= ((ksz9477_mib_names[i].index & 0xFF) <<
+                       MIB_COUNTER_INDEX_S);
+               ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data);
+
+               timeout = 1000;
+               do {
+                       ksz_pread32(dev, port, REG_PORT_MIB_CTRL_STAT__4,
+                                   &data);
+                       usleep_range(1, 10);
+                       if (!(data & MIB_COUNTER_READ))
+                               break;
+               } while (timeout-- > 0);
+
+               /* failed to read MIB. get out of loop */
+               if (!timeout) {
+                       dev_dbg(dev->dev, "Failed to get MIB\n");
+                       break;
+               }
+
+               /* count resets upon read */
+               ksz_pread32(dev, port, REG_PORT_MIB_DATA, &data);
+
+               dev->mib_value[i] += (uint64_t)data;
+               buf[i] = dev->mib_value[i];
+       }
+
+       mutex_unlock(&dev->stats_mutex);
+}
+
+static void ksz9477_cfg_port_member(struct ksz_device *dev, int port,
+                                   u8 member)
+{
+       ksz_pwrite32(dev, port, REG_PORT_VLAN_MEMBERSHIP__4, member);
+       dev->ports[port].member = member;
+}
+
+static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port,
+                                      u8 state)
+{
+       struct ksz_device *dev = ds->priv;
+       struct ksz_port *p = &dev->ports[port];
+       u8 data;
+       int member = -1;
+
+       ksz_pread8(dev, port, P_STP_CTRL, &data);
+       data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+
+       switch (state) {
+       case BR_STATE_DISABLED:
+               data |= PORT_LEARN_DISABLE;
+               if (port != dev->cpu_port)
+                       member = 0;
+               break;
+       case BR_STATE_LISTENING:
+               data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+               if (port != dev->cpu_port &&
+                   p->stp_state == BR_STATE_DISABLED)
+                       member = dev->host_mask | p->vid_member;
+               break;
+       case BR_STATE_LEARNING:
+               data |= PORT_RX_ENABLE;
+               break;
+       case BR_STATE_FORWARDING:
+               data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
+
+               /* This function is also used internally. */
+               if (port == dev->cpu_port)
+                       break;
+
+               member = dev->host_mask | p->vid_member;
+
+               /* Port is a member of a bridge. */
+               if (dev->br_member & (1 << port)) {
+                       dev->member |= (1 << port);
+                       member = dev->member;
+               }
+               break;
+       case BR_STATE_BLOCKING:
+               data |= PORT_LEARN_DISABLE;
+               if (port != dev->cpu_port &&
+                   p->stp_state == BR_STATE_DISABLED)
+                       member = dev->host_mask | p->vid_member;
+               break;
+       default:
+               dev_err(ds->dev, "invalid STP state: %d\n", state);
+               return;
+       }
+
+       ksz_pwrite8(dev, port, P_STP_CTRL, data);
+       p->stp_state = state;
+       if (data & PORT_RX_ENABLE)
+               dev->rx_ports |= (1 << port);
+       else
+               dev->rx_ports &= ~(1 << port);
+       if (data & PORT_TX_ENABLE)
+               dev->tx_ports |= (1 << port);
+       else
+               dev->tx_ports &= ~(1 << port);
+
+       /* Port membership may share register with STP state. */
+       if (member >= 0 && member != p->member)
+               ksz9477_cfg_port_member(dev, port, (u8)member);
+
+       /* Check if forwarding needs to be updated. */
+       if (state != BR_STATE_FORWARDING) {
+               if (dev->br_member & (1 << port))
+                       dev->member &= ~(1 << 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.
+        */
+}
+
+static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port)
+{
+       u8 data;
+
+       ksz_read8(dev, REG_SW_LUE_CTRL_2, &data);
+       data &= ~(SW_FLUSH_OPTION_M << SW_FLUSH_OPTION_S);
+       data |= (SW_FLUSH_OPTION_DYN_MAC << SW_FLUSH_OPTION_S);
+       ksz_write8(dev, REG_SW_LUE_CTRL_2, data);
+       if (port < dev->mib_port_cnt) {
+               /* flush individual port */
+               ksz_pread8(dev, port, P_STP_CTRL, &data);
+               if (!(data & PORT_LEARN_DISABLE))
+                       ksz_pwrite8(dev, port, P_STP_CTRL,
+                                   data | PORT_LEARN_DISABLE);
+               ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true);
+               ksz_pwrite8(dev, port, P_STP_CTRL, data);
+       } else {
+               /* flush all */
+               ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_STP_TABLE, true);
+       }
+}
+
+static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port,
+                                      bool flag)
+{
+       struct ksz_device *dev = ds->priv;
+
+       if (flag) {
+               ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
+                            PORT_VLAN_LOOKUP_VID_0, true);
+               ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, true);
+       } else {
+               ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, false);
+               ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
+                            PORT_VLAN_LOOKUP_VID_0, false);
+       }
+
+       return 0;
+}
+
+static void ksz9477_port_vlan_add(struct dsa_switch *ds, int port,
+                                 const struct switchdev_obj_port_vlan *vlan)
+{
+       struct ksz_device *dev = ds->priv;
+       u32 vlan_table[3];
+       u16 vid;
+       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+
+       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+               if (ksz9477_get_vlan_table(dev, vid, vlan_table)) {
+                       dev_dbg(dev->dev, "Failed to get vlan table\n");
+                       return;
+               }
+
+               vlan_table[0] = VLAN_VALID | (vid & VLAN_FID_M);
+               if (untagged)
+                       vlan_table[1] |= BIT(port);
+               else
+                       vlan_table[1] &= ~BIT(port);
+               vlan_table[1] &= ~(BIT(dev->cpu_port));
+
+               vlan_table[2] |= BIT(port) | BIT(dev->cpu_port);
+
+               if (ksz9477_set_vlan_table(dev, vid, vlan_table)) {
+                       dev_dbg(dev->dev, "Failed to set vlan table\n");
+                       return;
+               }
+
+               /* change PVID */
+               if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
+                       ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vid);
+       }
+}
+
+static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port,
+                                const struct switchdev_obj_port_vlan *vlan)
+{
+       struct ksz_device *dev = ds->priv;
+       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       u32 vlan_table[3];
+       u16 vid;
+       u16 pvid;
+
+       ksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid);
+       pvid = pvid & 0xFFF;
+
+       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+               if (ksz9477_get_vlan_table(dev, vid, vlan_table)) {
+                       dev_dbg(dev->dev, "Failed to get vlan table\n");
+                       return -ETIMEDOUT;
+               }
+
+               vlan_table[2] &= ~BIT(port);
+
+               if (pvid == vid)
+                       pvid = 1;
+
+               if (untagged)
+                       vlan_table[1] &= ~BIT(port);
+
+               if (ksz9477_set_vlan_table(dev, vid, vlan_table)) {
+                       dev_dbg(dev->dev, "Failed to set vlan table\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
+       ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid);
+
+       return 0;
+}
+
+static int ksz9477_port_fdb_add(struct dsa_switch *ds, int port,
+                               const unsigned char *addr, u16 vid)
+{
+       struct ksz_device *dev = ds->priv;
+       u32 alu_table[4];
+       u32 data;
+       int ret = 0;
+
+       mutex_lock(&dev->alu_mutex);
+
+       /* find any entry with mac & vid */
+       data = vid << ALU_FID_INDEX_S;
+       data |= ((addr[0] << 8) | addr[1]);
+       ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
+
+       data = ((addr[2] << 24) | (addr[3] << 16));
+       data |= ((addr[4] << 8) | addr[5]);
+       ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
+
+       /* start read operation */
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
+
+       /* wait to be finished */
+       ret = ksz9477_wait_alu_ready(dev, ALU_START, 1000);
+       if (ret < 0) {
+               dev_dbg(dev->dev, "Failed to read ALU\n");
+               goto exit;
+       }
+
+       /* read ALU entry */
+       ksz9477_read_table(dev, alu_table);
+
+       /* update ALU entry */
+       alu_table[0] = ALU_V_STATIC_VALID;
+       alu_table[1] |= BIT(port);
+       if (vid)
+               alu_table[1] |= ALU_V_USE_FID;
+       alu_table[2] = (vid << ALU_V_FID_S);
+       alu_table[2] |= ((addr[0] << 8) | addr[1]);
+       alu_table[3] = ((addr[2] << 24) | (addr[3] << 16));
+       alu_table[3] |= ((addr[4] << 8) | addr[5]);
+
+       ksz9477_write_table(dev, alu_table);
+
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
+
+       /* wait to be finished */
+       ret = ksz9477_wait_alu_ready(dev, ALU_START, 1000);
+       if (ret < 0)
+               dev_dbg(dev->dev, "Failed to write ALU\n");
+
+exit:
+       mutex_unlock(&dev->alu_mutex);
+
+       return ret;
+}
+
+static int ksz9477_port_fdb_del(struct dsa_switch *ds, int port,
+                               const unsigned char *addr, u16 vid)
+{
+       struct ksz_device *dev = ds->priv;
+       u32 alu_table[4];
+       u32 data;
+       int ret = 0;
+
+       mutex_lock(&dev->alu_mutex);
+
+       /* read any entry with mac & vid */
+       data = vid << ALU_FID_INDEX_S;
+       data |= ((addr[0] << 8) | addr[1]);
+       ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
+
+       data = ((addr[2] << 24) | (addr[3] << 16));
+       data |= ((addr[4] << 8) | addr[5]);
+       ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
+
+       /* start read operation */
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
+
+       /* wait to be finished */
+       ret = ksz9477_wait_alu_ready(dev, ALU_START, 1000);
+       if (ret < 0) {
+               dev_dbg(dev->dev, "Failed to read ALU\n");
+               goto exit;
+       }
+
+       ksz_read32(dev, REG_SW_ALU_VAL_A, &alu_table[0]);
+       if (alu_table[0] & ALU_V_STATIC_VALID) {
+               ksz_read32(dev, REG_SW_ALU_VAL_B, &alu_table[1]);
+               ksz_read32(dev, REG_SW_ALU_VAL_C, &alu_table[2]);
+               ksz_read32(dev, REG_SW_ALU_VAL_D, &alu_table[3]);
+
+               /* clear forwarding port */
+               alu_table[2] &= ~BIT(port);
+
+               /* if there is no port to forward, clear table */
+               if ((alu_table[2] & ALU_V_PORT_MAP) == 0) {
+                       alu_table[0] = 0;
+                       alu_table[1] = 0;
+                       alu_table[2] = 0;
+                       alu_table[3] = 0;
+               }
+       } else {
+               alu_table[0] = 0;
+               alu_table[1] = 0;
+               alu_table[2] = 0;
+               alu_table[3] = 0;
+       }
+
+       ksz9477_write_table(dev, alu_table);
+
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
+
+       /* wait to be finished */
+       ret = ksz9477_wait_alu_ready(dev, ALU_START, 1000);
+       if (ret < 0)
+               dev_dbg(dev->dev, "Failed to write ALU\n");
+
+exit:
+       mutex_unlock(&dev->alu_mutex);
+
+       return ret;
+}
+
+static void ksz9477_convert_alu(struct alu_struct *alu, u32 *alu_table)
+{
+       alu->is_static = !!(alu_table[0] & ALU_V_STATIC_VALID);
+       alu->is_src_filter = !!(alu_table[0] & ALU_V_SRC_FILTER);
+       alu->is_dst_filter = !!(alu_table[0] & ALU_V_DST_FILTER);
+       alu->prio_age = (alu_table[0] >> ALU_V_PRIO_AGE_CNT_S) &
+                       ALU_V_PRIO_AGE_CNT_M;
+       alu->mstp = alu_table[0] & ALU_V_MSTP_M;
+
+       alu->is_override = !!(alu_table[1] & ALU_V_OVERRIDE);
+       alu->is_use_fid = !!(alu_table[1] & ALU_V_USE_FID);
+       alu->port_forward = alu_table[1] & ALU_V_PORT_MAP;
+
+       alu->fid = (alu_table[2] >> ALU_V_FID_S) & ALU_V_FID_M;
+
+       alu->mac[0] = (alu_table[2] >> 8) & 0xFF;
+       alu->mac[1] = alu_table[2] & 0xFF;
+       alu->mac[2] = (alu_table[3] >> 24) & 0xFF;
+       alu->mac[3] = (alu_table[3] >> 16) & 0xFF;
+       alu->mac[4] = (alu_table[3] >> 8) & 0xFF;
+       alu->mac[5] = alu_table[3] & 0xFF;
+}
+
+static int ksz9477_port_fdb_dump(struct dsa_switch *ds, int port,
+                                dsa_fdb_dump_cb_t *cb, void *data)
+{
+       struct ksz_device *dev = ds->priv;
+       int ret = 0;
+       u32 ksz_data;
+       u32 alu_table[4];
+       struct alu_struct alu;
+       int timeout;
+
+       mutex_lock(&dev->alu_mutex);
+
+       /* start ALU search */
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_START | ALU_SEARCH);
+
+       do {
+               timeout = 1000;
+               do {
+                       ksz_read32(dev, REG_SW_ALU_CTRL__4, &ksz_data);
+                       if ((ksz_data & ALU_VALID) || !(ksz_data & ALU_START))
+                               break;
+                       usleep_range(1, 10);
+               } while (timeout-- > 0);
+
+               if (!timeout) {
+                       dev_dbg(dev->dev, "Failed to search ALU\n");
+                       ret = -ETIMEDOUT;
+                       goto exit;
+               }
+
+               /* read ALU table */
+               ksz9477_read_table(dev, alu_table);
+
+               ksz9477_convert_alu(&alu, alu_table);
+
+               if (alu.port_forward & BIT(port)) {
+                       ret = cb(alu.mac, alu.fid, alu.is_static, data);
+                       if (ret)
+                               goto exit;
+               }
+       } while (ksz_data & ALU_START);
+
+exit:
+
+       /* stop ALU search */
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, 0);
+
+       mutex_unlock(&dev->alu_mutex);
+
+       return ret;
+}
+
+static void ksz9477_port_mdb_add(struct dsa_switch *ds, int port,
+                                const struct switchdev_obj_port_mdb *mdb)
+{
+       struct ksz_device *dev = ds->priv;
+       u32 static_table[4];
+       u32 data;
+       int index;
+       u32 mac_hi, mac_lo;
+
+       mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
+       mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
+       mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
+
+       mutex_lock(&dev->alu_mutex);
+
+       for (index = 0; index < dev->num_statics; index++) {
+               /* find empty slot first */
+               data = (index << ALU_STAT_INDEX_S) |
+                       ALU_STAT_READ | ALU_STAT_START;
+               ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+               /* wait to be finished */
+               if (ksz9477_wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0) {
+                       dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+                       goto exit;
+               }
+
+               /* read ALU static table */
+               ksz9477_read_table(dev, static_table);
+
+               if (static_table[0] & ALU_V_STATIC_VALID) {
+                       /* check this has same vid & mac address */
+                       if (((static_table[2] >> ALU_V_FID_S) == mdb->vid) &&
+                           ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
+                           static_table[3] == mac_lo) {
+                               /* found matching one */
+                               break;
+                       }
+               } else {
+                       /* found empty one */
+                       break;
+               }
+       }
+
+       /* no available entry */
+       if (index == dev->num_statics)
+               goto exit;
+
+       /* add entry */
+       static_table[0] = ALU_V_STATIC_VALID;
+       static_table[1] |= BIT(port);
+       if (mdb->vid)
+               static_table[1] |= ALU_V_USE_FID;
+       static_table[2] = (mdb->vid << ALU_V_FID_S);
+       static_table[2] |= mac_hi;
+       static_table[3] = mac_lo;
+
+       ksz9477_write_table(dev, static_table);
+
+       data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
+       ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+       /* wait to be finished */
+       if (ksz9477_wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0)
+               dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+
+exit:
+       mutex_unlock(&dev->alu_mutex);
+}
+
+static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port,
+                               const struct switchdev_obj_port_mdb *mdb)
+{
+       struct ksz_device *dev = ds->priv;
+       u32 static_table[4];
+       u32 data;
+       int index;
+       int ret = 0;
+       u32 mac_hi, mac_lo;
+
+       mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
+       mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
+       mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
+
+       mutex_lock(&dev->alu_mutex);
+
+       for (index = 0; index < dev->num_statics; index++) {
+               /* find empty slot first */
+               data = (index << ALU_STAT_INDEX_S) |
+                       ALU_STAT_READ | ALU_STAT_START;
+               ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+               /* wait to be finished */
+               ret = ksz9477_wait_alu_sta_ready(dev, ALU_STAT_START, 1000);
+               if (ret < 0) {
+                       dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+                       goto exit;
+               }
+
+               /* read ALU static table */
+               ksz9477_read_table(dev, static_table);
+
+               if (static_table[0] & ALU_V_STATIC_VALID) {
+                       /* check this has same vid & mac address */
+
+                       if (((static_table[2] >> ALU_V_FID_S) == mdb->vid) &&
+                           ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
+                           static_table[3] == mac_lo) {
+                               /* found matching one */
+                               break;
+                       }
+               }
+       }
+
+       /* no available entry */
+       if (index == dev->num_statics)
+               goto exit;
+
+       /* clear port */
+       static_table[1] &= ~BIT(port);
+
+       if ((static_table[1] & ALU_V_PORT_MAP) == 0) {
+               /* delete entry */
+               static_table[0] = 0;
+               static_table[1] = 0;
+               static_table[2] = 0;
+               static_table[3] = 0;
+       }
+
+       ksz9477_write_table(dev, static_table);
+
+       data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
+       ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+       /* wait to be finished */
+       ret = ksz9477_wait_alu_sta_ready(dev, ALU_STAT_START, 1000);
+       if (ret < 0)
+               dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+
+exit:
+       mutex_unlock(&dev->alu_mutex);
+
+       return ret;
+}
+
+static int ksz9477_port_mirror_add(struct dsa_switch *ds, int port,
+                                  struct dsa_mall_mirror_tc_entry *mirror,
+                                  bool ingress)
+{
+       struct ksz_device *dev = ds->priv;
+
+       if (ingress)
+               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
+       else
+               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
+
+       ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
+
+       /* configure mirror port */
+       ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+                    PORT_MIRROR_SNIFFER, true);
+
+       ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
+
+       return 0;
+}
+
+static void ksz9477_port_mirror_del(struct dsa_switch *ds, int port,
+                                   struct dsa_mall_mirror_tc_entry *mirror)
+{
+       struct ksz_device *dev = ds->priv;
+       u8 data;
+
+       if (mirror->ingress)
+               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
+       else
+               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
+
+       ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
+
+       if (!(data & (PORT_MIRROR_RX | PORT_MIRROR_TX)))
+               ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+                            PORT_MIRROR_SNIFFER, false);
+}
+
+static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
+{
+       u8 data8;
+       u8 member;
+       u16 data16;
+       struct ksz_port *p = &dev->ports[port];
+
+       /* enable tag tail for host port */
+       if (cpu_port)
+               ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_TAIL_TAG_ENABLE,
+                            true);
+
+       ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, false);
+
+       /* set back pressure */
+       ksz_port_cfg(dev, port, REG_PORT_MAC_CTRL_1, PORT_BACK_PRESSURE, true);
+
+       /* enable broadcast storm limit */
+       ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
+
+       /* disable DiffServ priority */
+       ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_PRIO_ENABLE, false);
+
+       /* replace priority */
+       ksz_port_cfg(dev, port, REG_PORT_MRI_MAC_CTRL, PORT_USER_PRIO_CEILING,
+                    false);
+       ksz9477_port_cfg32(dev, port, REG_PORT_MTI_QUEUE_CTRL_0__4,
+                          MTI_PVID_REPLACE, false);
+
+       /* enable 802.1p priority */
+       ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true);
+
+       if (port < dev->phy_port_cnt) {
+               /* do not force flow control */
+               ksz_port_cfg(dev, port, REG_PORT_CTRL_0,
+                            PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL,
+                            false);
+
+       } else {
+               /* force flow control */
+               ksz_port_cfg(dev, port, REG_PORT_CTRL_0,
+                            PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL,
+                            true);
+
+               /* configure MAC to 1G & RGMII mode */
+               ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
+               data8 &= ~PORT_MII_NOT_1GBIT;
+               data8 &= ~PORT_MII_SEL_M;
+               switch (dev->interface) {
+               case PHY_INTERFACE_MODE_MII:
+                       data8 |= PORT_MII_NOT_1GBIT;
+                       data8 |= PORT_MII_SEL;
+                       p->phydev.speed = SPEED_100;
+                       break;
+               case PHY_INTERFACE_MODE_RMII:
+                       data8 |= PORT_MII_NOT_1GBIT;
+                       data8 |= PORT_RMII_SEL;
+                       p->phydev.speed = SPEED_100;
+                       break;
+               case PHY_INTERFACE_MODE_GMII:
+                       data8 |= PORT_GMII_SEL;
+                       p->phydev.speed = SPEED_1000;
+                       break;
+               default:
+                       data8 &= ~PORT_RGMII_ID_IG_ENABLE;
+                       data8 &= ~PORT_RGMII_ID_EG_ENABLE;
+                       if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+                           dev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+                               data8 |= PORT_RGMII_ID_IG_ENABLE;
+                       if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+                           dev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+                               data8 |= PORT_RGMII_ID_EG_ENABLE;
+                       data8 |= PORT_RGMII_SEL;
+                       p->phydev.speed = SPEED_1000;
+                       break;
+               }
+               ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8);
+               p->phydev.duplex = 1;
+       }
+       if (cpu_port) {
+               member = dev->port_mask;
+               dev->on_ports = dev->host_mask;
+               dev->live_ports = dev->host_mask;
+       } else {
+               member = dev->host_mask | p->vid_member;
+               dev->on_ports |= (1 << port);
+
+               /* Link was detected before port is enabled. */
+               if (p->phydev.link)
+                       dev->live_ports |= (1 << port);
+       }
+       ksz9477_cfg_port_member(dev, port, member);
+
+       /* clear pending interrupts */
+       if (port < dev->phy_port_cnt)
+               ksz_pread16(dev, port, REG_PORT_PHY_INT_ENABLE, &data16);
+}
+
+static void ksz9477_config_cpu_port(struct dsa_switch *ds)
+{
+       struct ksz_device *dev = ds->priv;
+       struct ksz_port *p;
+       int i;
+
+       ds->num_ports = dev->port_cnt;
+
+       for (i = 0; i < dev->port_cnt; i++) {
+               if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {
+                       dev->cpu_port = i;
+                       dev->host_mask = (1 << dev->cpu_port);
+                       dev->port_mask |= dev->host_mask;
+
+                       /* enable cpu port */
+                       ksz9477_port_setup(dev, i, true);
+                       p = &dev->ports[dev->cpu_port];
+                       p->vid_member = dev->port_mask;
+                       p->on = 1;
+               }
+       }
+
+       dev->member = dev->host_mask;
+
+       for (i = 0; i < dev->mib_port_cnt; i++) {
+               if (i == dev->cpu_port)
+                       continue;
+               p = &dev->ports[i];
+
+               /* Initialize to non-zero so that ksz_cfg_port_member() will
+                * be called.
+                */
+               p->vid_member = (1 << i);
+               p->member = dev->port_mask;
+               ksz9477_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+               p->on = 1;
+               if (i < dev->phy_port_cnt)
+                       p->phy = 1;
+               if (dev->chip_id == 0x00947700 && i == 6) {
+                       p->sgmii = 1;
+
+                       /* SGMII PHY detection code is not implemented yet. */
+                       p->phy = 0;
+               }
+       }
+}
+
+static int ksz9477_setup(struct dsa_switch *ds)
+{
+       struct ksz_device *dev = ds->priv;
+       int ret = 0;
+
+       dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
+                                      dev->num_vlans, GFP_KERNEL);
+       if (!dev->vlan_cache)
+               return -ENOMEM;
+
+       ret = ksz9477_reset_switch(dev);
+       if (ret) {
+               dev_err(ds->dev, "failed to reset switch\n");
+               return ret;
+       }
+
+       /* Required for port partitioning. */
+       ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY,
+                     true);
+
+       /* accept packet up to 2000bytes */
+       ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true);
+
+       ksz9477_config_cpu_port(ds);
+
+       ksz_cfg(dev, REG_SW_MAC_CTRL_1, MULTICAST_STORM_DISABLE, true);
+
+       /* queue based egress rate limit */
+       ksz_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true);
+
+       /* start switch */
+       ksz_cfg(dev, REG_SW_OPERATION, SW_START, true);
+
+       return 0;
+}
+
+static const struct dsa_switch_ops ksz9477_switch_ops = {
+       .get_tag_protocol       = ksz9477_get_tag_protocol,
+       .setup                  = ksz9477_setup,
+       .phy_read               = ksz9477_phy_read16,
+       .phy_write              = ksz9477_phy_write16,
+       .port_enable            = ksz_enable_port,
+       .port_disable           = ksz_disable_port,
+       .get_strings            = ksz9477_get_strings,
+       .get_ethtool_stats      = ksz_get_ethtool_stats,
+       .get_sset_count         = ksz_sset_count,
+       .port_bridge_join       = ksz_port_bridge_join,
+       .port_bridge_leave      = ksz_port_bridge_leave,
+       .port_stp_state_set     = ksz9477_port_stp_state_set,
+       .port_fast_age          = ksz_port_fast_age,
+       .port_vlan_filtering    = ksz9477_port_vlan_filtering,
+       .port_vlan_prepare      = ksz_port_vlan_prepare,
+       .port_vlan_add          = ksz9477_port_vlan_add,
+       .port_vlan_del          = ksz9477_port_vlan_del,
+       .port_fdb_dump          = ksz9477_port_fdb_dump,
+       .port_fdb_add           = ksz9477_port_fdb_add,
+       .port_fdb_del           = ksz9477_port_fdb_del,
+       .port_mdb_prepare       = ksz_port_mdb_prepare,
+       .port_mdb_add           = ksz9477_port_mdb_add,
+       .port_mdb_del           = ksz9477_port_mdb_del,
+       .port_mirror_add        = ksz9477_port_mirror_add,
+       .port_mirror_del        = ksz9477_port_mirror_del,
+};
+
+static u32 ksz9477_get_port_addr(int port, int offset)
+{
+       return PORT_CTRL_ADDR(port, offset);
+}
+
+static int ksz9477_switch_detect(struct ksz_device *dev)
+{
+       u8 data8;
+       u32 id32;
+       int ret;
+
+       /* turn off SPI DO Edge select */
+       ret = ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);
+       if (ret)
+               return ret;
+
+       data8 &= ~SPI_AUTO_EDGE_DETECTION;
+       ret = ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);
+       if (ret)
+               return ret;
+
+       /* read chip id */
+       ret = ksz_read32(dev, REG_CHIP_ID0__1, &id32);
+       if (ret)
+               return ret;
+
+       /* Number of ports can be reduced depending on chip. */
+       dev->mib_port_cnt = TOTAL_PORT_NUM;
+       dev->phy_port_cnt = 5;
+
+       dev->chip_id = id32;
+
+       return 0;
+}
+
+struct ksz_chip_data {
+       u32 chip_id;
+       const char *dev_name;
+       int num_vlans;
+       int num_alus;
+       int num_statics;
+       int cpu_ports;
+       int port_cnt;
+};
+
+static const struct ksz_chip_data ksz9477_switch_chips[] = {
+       {
+               .chip_id = 0x00947700,
+               .dev_name = "KSZ9477",
+               .num_vlans = 4096,
+               .num_alus = 4096,
+               .num_statics = 16,
+               .cpu_ports = 0x7F,      /* can be configured as cpu port */
+               .port_cnt = 7,          /* total physical port count */
+       },
+       {
+               .chip_id = 0x00989700,
+               .dev_name = "KSZ9897",
+               .num_vlans = 4096,
+               .num_alus = 4096,
+               .num_statics = 16,
+               .cpu_ports = 0x7F,      /* can be configured as cpu port */
+               .port_cnt = 7,          /* total physical port count */
+       },
+};
+
+static int ksz9477_switch_init(struct ksz_device *dev)
+{
+       int i;
+
+       dev->ds->ops = &ksz9477_switch_ops;
+
+       for (i = 0; i < ARRAY_SIZE(ksz9477_switch_chips); i++) {
+               const struct ksz_chip_data *chip = &ksz9477_switch_chips[i];
+
+               if (dev->chip_id == chip->chip_id) {
+                       dev->name = chip->dev_name;
+                       dev->num_vlans = chip->num_vlans;
+                       dev->num_alus = chip->num_alus;
+                       dev->num_statics = chip->num_statics;
+                       dev->port_cnt = chip->port_cnt;
+                       dev->cpu_ports = chip->cpu_ports;
+
+                       break;
+               }
+       }
+
+       /* no switch found */
+       if (!dev->port_cnt)
+               return -ENODEV;
+
+       dev->port_mask = (1 << dev->port_cnt) - 1;
+
+       dev->reg_mib_cnt = SWITCH_COUNTER_NUM;
+       dev->mib_cnt = TOTAL_SWITCH_COUNTER_NUM;
+
+       i = dev->mib_port_cnt;
+       dev->ports = devm_kzalloc(dev->dev, sizeof(struct ksz_port) * i,
+                                 GFP_KERNEL);
+       if (!dev->ports)
+               return -ENOMEM;
+       for (i = 0; i < dev->mib_port_cnt; i++) {
+               dev->ports[i].mib.counters =
+                       devm_kzalloc(dev->dev,
+                                    sizeof(u64) *
+                                    (TOTAL_SWITCH_COUNTER_NUM + 1),
+                                    GFP_KERNEL);
+               if (!dev->ports[i].mib.counters)
+                       return -ENOMEM;
+       }
+       dev->interface = PHY_INTERFACE_MODE_RGMII_TXID;
+
+       return 0;
+}
+
+static void ksz9477_switch_exit(struct ksz_device *dev)
+{
+       ksz9477_reset_switch(dev);
+}
+
+static const struct ksz_dev_ops ksz9477_dev_ops = {
+       .get_port_addr = ksz9477_get_port_addr,
+       .cfg_port_member = ksz9477_cfg_port_member,
+       .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table,
+       .port_setup = ksz9477_port_setup,
+       .shutdown = ksz9477_reset_switch,
+       .detect = ksz9477_switch_detect,
+       .init = ksz9477_switch_init,
+       .exit = ksz9477_switch_exit,
+};
+
+int ksz9477_switch_register(struct ksz_device *dev)
+{
+       return ksz_switch_register(dev, &ksz9477_dev_ops);
+}
+EXPORT_SYMBOL(ksz9477_switch_register);
+
+MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>");
+MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch DSA Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/microchip/ksz9477_reg.h b/drivers/net/dsa/microchip/ksz9477_reg.h
new file mode 100644 (file)
index 0000000..2938e89
--- /dev/null
@@ -0,0 +1,1665 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Microchip KSZ9477 register definitions
+ *
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
+ */
+
+#ifndef __KSZ9477_REGS_H
+#define __KSZ9477_REGS_H
+
+#define KS_PRIO_M                      0x7
+#define KS_PRIO_S                      4
+
+/* 0 - Operation */
+#define REG_CHIP_ID0__1                        0x0000
+
+#define REG_CHIP_ID1__1                        0x0001
+
+#define FAMILY_ID                      0x95
+#define FAMILY_ID_94                   0x94
+#define FAMILY_ID_95                   0x95
+#define FAMILY_ID_85                   0x85
+#define FAMILY_ID_98                   0x98
+#define FAMILY_ID_88                   0x88
+
+#define REG_CHIP_ID2__1                        0x0002
+
+#define CHIP_ID_63                     0x63
+#define CHIP_ID_66                     0x66
+#define CHIP_ID_67                     0x67
+#define CHIP_ID_77                     0x77
+#define CHIP_ID_93                     0x93
+#define CHIP_ID_96                     0x96
+#define CHIP_ID_97                     0x97
+
+#define REG_CHIP_ID3__1                        0x0003
+
+#define SWITCH_REVISION_M              0x0F
+#define SWITCH_REVISION_S              4
+#define SWITCH_RESET                   0x01
+
+#define REG_SW_PME_CTRL                        0x0006
+
+#define PME_ENABLE                     BIT(1)
+#define PME_POLARITY                   BIT(0)
+
+#define REG_GLOBAL_OPTIONS             0x000F
+
+#define SW_GIGABIT_ABLE                        BIT(6)
+#define SW_REDUNDANCY_ABLE             BIT(5)
+#define SW_AVB_ABLE                    BIT(4)
+#define SW_9567_RL_5_2                 0xC
+#define SW_9477_SL_5_2                 0xD
+
+#define SW_9896_GL_5_1                 0xB
+#define SW_9896_RL_5_1                 0x8
+#define SW_9896_SL_5_1                 0x9
+
+#define SW_9895_GL_4_1                 0x7
+#define SW_9895_RL_4_1                 0x4
+#define SW_9895_SL_4_1                 0x5
+
+#define SW_9896_RL_4_2                 0x6
+
+#define SW_9893_RL_2_1                 0x0
+#define SW_9893_SL_2_1                 0x1
+#define SW_9893_GL_2_1                 0x3
+
+#define SW_QW_ABLE                     BIT(5)
+#define SW_9893_RN_2_1                 0xC
+
+#define REG_SW_INT_STATUS__4           0x0010
+#define REG_SW_INT_MASK__4             0x0014
+
+#define LUE_INT                                BIT(31)
+#define TRIG_TS_INT                    BIT(30)
+#define APB_TIMEOUT_INT                        BIT(29)
+
+#define SWITCH_INT_MASK                        (TRIG_TS_INT | APB_TIMEOUT_INT)
+
+#define REG_SW_PORT_INT_STATUS__4      0x0018
+#define REG_SW_PORT_INT_MASK__4                0x001C
+#define REG_SW_PHY_INT_STATUS          0x0020
+#define REG_SW_PHY_INT_ENABLE          0x0024
+
+/* 1 - Global */
+#define REG_SW_GLOBAL_SERIAL_CTRL_0    0x0100
+#define SW_SPARE_REG_2                 BIT(7)
+#define SW_SPARE_REG_1                 BIT(6)
+#define SW_SPARE_REG_0                 BIT(5)
+#define SW_BIG_ENDIAN                  BIT(4)
+#define SPI_AUTO_EDGE_DETECTION                BIT(1)
+#define SPI_CLOCK_OUT_RISING_EDGE      BIT(0)
+
+#define REG_SW_GLOBAL_OUTPUT_CTRL__1   0x0103
+#define SW_ENABLE_REFCLKO              BIT(1)
+#define SW_REFCLKO_IS_125MHZ           BIT(0)
+
+#define REG_SW_IBA__4                  0x0104
+
+#define SW_IBA_ENABLE                  BIT(31)
+#define SW_IBA_DA_MATCH                        BIT(30)
+#define SW_IBA_INIT                    BIT(29)
+#define SW_IBA_QID_M                   0xF
+#define SW_IBA_QID_S                   22
+#define SW_IBA_PORT_M                  0x2F
+#define SW_IBA_PORT_S                  16
+#define SW_IBA_FRAME_TPID_M            0xFFFF
+
+#define REG_SW_APB_TIMEOUT_ADDR__4     0x0108
+
+#define APB_TIMEOUT_ACKNOWLEDGE                BIT(31)
+
+#define REG_SW_IBA_SYNC__1             0x010C
+
+#define REG_SW_IO_STRENGTH__1          0x010D
+#define SW_DRIVE_STRENGTH_M            0x7
+#define SW_DRIVE_STRENGTH_2MA          0
+#define SW_DRIVE_STRENGTH_4MA          1
+#define SW_DRIVE_STRENGTH_8MA          2
+#define SW_DRIVE_STRENGTH_12MA         3
+#define SW_DRIVE_STRENGTH_16MA         4
+#define SW_DRIVE_STRENGTH_20MA         5
+#define SW_DRIVE_STRENGTH_24MA         6
+#define SW_DRIVE_STRENGTH_28MA         7
+#define SW_HI_SPEED_DRIVE_STRENGTH_S   4
+#define SW_LO_SPEED_DRIVE_STRENGTH_S   0
+
+#define REG_SW_IBA_STATUS__4           0x0110
+
+#define SW_IBA_REQ                     BIT(31)
+#define SW_IBA_RESP                    BIT(30)
+#define SW_IBA_DA_MISMATCH             BIT(14)
+#define SW_IBA_FMT_MISMATCH            BIT(13)
+#define SW_IBA_CODE_ERROR              BIT(12)
+#define SW_IBA_CMD_ERROR               BIT(11)
+#define SW_IBA_CMD_LOC_M               (BIT(6) - 1)
+
+#define REG_SW_IBA_STATES__4           0x0114
+
+#define SW_IBA_BUF_STATE_S             30
+#define SW_IBA_CMD_STATE_S             28
+#define SW_IBA_RESP_STATE_S            26
+#define SW_IBA_STATE_M                 0x3
+#define SW_IBA_PACKET_SIZE_M           0x7F
+#define SW_IBA_PACKET_SIZE_S           16
+#define SW_IBA_FMT_ID_M                        0xFFFF
+
+#define REG_SW_IBA_RESULT__4           0x0118
+
+#define SW_IBA_SIZE_S                  24
+
+#define SW_IBA_RETRY_CNT_M             (BIT(5) - 1)
+
+/* 2 - PHY */
+#define REG_SW_POWER_MANAGEMENT_CTRL   0x0201
+
+#define SW_PLL_POWER_DOWN              BIT(5)
+#define SW_POWER_DOWN_MODE             0x3
+#define SW_ENERGY_DETECTION            1
+#define SW_SOFT_POWER_DOWN             2
+#define SW_POWER_SAVING                        3
+
+/* 3 - Operation Control */
+#define REG_SW_OPERATION               0x0300
+
+#define SW_DOUBLE_TAG                  BIT(7)
+#define SW_RESET                       BIT(1)
+#define SW_START                       BIT(0)
+
+#define REG_SW_MAC_ADDR_0              0x0302
+#define REG_SW_MAC_ADDR_1              0x0303
+#define REG_SW_MAC_ADDR_2              0x0304
+#define REG_SW_MAC_ADDR_3              0x0305
+#define REG_SW_MAC_ADDR_4              0x0306
+#define REG_SW_MAC_ADDR_5              0x0307
+
+#define REG_SW_MTU__2                  0x0308
+
+#define REG_SW_ISP_TPID__2             0x030A
+
+#define REG_SW_HSR_TPID__2             0x030C
+
+#define REG_AVB_STRATEGY__2            0x030E
+
+#define SW_SHAPING_CREDIT_ACCT         BIT(1)
+#define SW_POLICING_CREDIT_ACCT                BIT(0)
+
+#define REG_SW_LUE_CTRL_0              0x0310
+
+#define SW_VLAN_ENABLE                 BIT(7)
+#define SW_DROP_INVALID_VID            BIT(6)
+#define SW_AGE_CNT_M                   0x7
+#define SW_AGE_CNT_S                   3
+#define SW_RESV_MCAST_ENABLE           BIT(2)
+#define SW_HASH_OPTION_M               0x03
+#define SW_HASH_OPTION_CRC             1
+#define SW_HASH_OPTION_XOR             2
+#define SW_HASH_OPTION_DIRECT          3
+
+#define REG_SW_LUE_CTRL_1              0x0311
+
+#define UNICAST_LEARN_DISABLE          BIT(7)
+#define SW_SRC_ADDR_FILTER             BIT(6)
+#define SW_FLUSH_STP_TABLE             BIT(5)
+#define SW_FLUSH_MSTP_TABLE            BIT(4)
+#define SW_FWD_MCAST_SRC_ADDR          BIT(3)
+#define SW_AGING_ENABLE                        BIT(2)
+#define SW_FAST_AGING                  BIT(1)
+#define SW_LINK_AUTO_AGING             BIT(0)
+
+#define REG_SW_LUE_CTRL_2              0x0312
+
+#define SW_TRAP_DOUBLE_TAG             BIT(6)
+#define SW_EGRESS_VLAN_FILTER_DYN      BIT(5)
+#define SW_EGRESS_VLAN_FILTER_STA      BIT(4)
+#define SW_FLUSH_OPTION_M              0x3
+#define SW_FLUSH_OPTION_S              2
+#define SW_FLUSH_OPTION_DYN_MAC                1
+#define SW_FLUSH_OPTION_STA_MAC                2
+#define SW_FLUSH_OPTION_BOTH           3
+#define SW_PRIO_M                      0x3
+#define SW_PRIO_DA                     0
+#define SW_PRIO_SA                     1
+#define SW_PRIO_HIGHEST_DA_SA          2
+#define SW_PRIO_LOWEST_DA_SA           3
+
+#define REG_SW_LUE_CTRL_3              0x0313
+
+#define REG_SW_LUE_INT_STATUS          0x0314
+#define REG_SW_LUE_INT_ENABLE          0x0315
+
+#define LEARN_FAIL_INT                 BIT(2)
+#define ALMOST_FULL_INT                        BIT(1)
+#define WRITE_FAIL_INT                 BIT(0)
+
+#define REG_SW_LUE_INDEX_0__2          0x0316
+
+#define ENTRY_INDEX_M                  0x0FFF
+
+#define REG_SW_LUE_INDEX_1__2          0x0318
+
+#define FAIL_INDEX_M                   0x03FF
+
+#define REG_SW_LUE_INDEX_2__2          0x031A
+
+#define REG_SW_LUE_UNK_UCAST_CTRL__4   0x0320
+
+#define SW_UNK_UCAST_ENABLE            BIT(31)
+
+#define REG_SW_LUE_UNK_MCAST_CTRL__4   0x0324
+
+#define SW_UNK_MCAST_ENABLE            BIT(31)
+
+#define REG_SW_LUE_UNK_VID_CTRL__4     0x0328
+
+#define SW_UNK_VID_ENABLE              BIT(31)
+
+#define REG_SW_MAC_CTRL_0              0x0330
+
+#define SW_NEW_BACKOFF                 BIT(7)
+#define SW_CHECK_LENGTH                        BIT(3)
+#define SW_PAUSE_UNH_MODE              BIT(1)
+#define SW_AGGR_BACKOFF                        BIT(0)
+
+#define REG_SW_MAC_CTRL_1              0x0331
+
+#define MULTICAST_STORM_DISABLE                BIT(6)
+#define SW_BACK_PRESSURE               BIT(5)
+#define FAIR_FLOW_CTRL                 BIT(4)
+#define NO_EXC_COLLISION_DROP          BIT(3)
+#define SW_JUMBO_PACKET                        BIT(2)
+#define SW_LEGAL_PACKET_DISABLE                BIT(1)
+#define SW_PASS_SHORT_FRAME            BIT(0)
+
+#define REG_SW_MAC_CTRL_2              0x0332
+
+#define SW_REPLACE_VID                 BIT(3)
+#define BROADCAST_STORM_RATE_HI                0x07
+
+#define REG_SW_MAC_CTRL_3              0x0333
+
+#define BROADCAST_STORM_RATE_LO                0xFF
+#define BROADCAST_STORM_RATE           0x07FF
+
+#define REG_SW_MAC_CTRL_4              0x0334
+
+#define SW_PASS_PAUSE                  BIT(3)
+
+#define REG_SW_MAC_CTRL_5              0x0335
+
+#define SW_OUT_RATE_LIMIT_QUEUE_BASED  BIT(3)
+
+#define REG_SW_MAC_CTRL_6              0x0336
+
+#define SW_MIB_COUNTER_FLUSH           BIT(7)
+#define SW_MIB_COUNTER_FREEZE          BIT(6)
+
+#define REG_SW_MAC_802_1P_MAP_0                0x0338
+#define REG_SW_MAC_802_1P_MAP_1                0x0339
+#define REG_SW_MAC_802_1P_MAP_2                0x033A
+#define REG_SW_MAC_802_1P_MAP_3                0x033B
+
+#define SW_802_1P_MAP_M                        KS_PRIO_M
+#define SW_802_1P_MAP_S                        KS_PRIO_S
+
+#define REG_SW_MAC_ISP_CTRL            0x033C
+
+#define REG_SW_MAC_TOS_CTRL            0x033E
+
+#define SW_TOS_DSCP_REMARK             BIT(1)
+#define SW_TOS_DSCP_REMAP              BIT(0)
+
+#define REG_SW_MAC_TOS_PRIO_0          0x0340
+#define REG_SW_MAC_TOS_PRIO_1          0x0341
+#define REG_SW_MAC_TOS_PRIO_2          0x0342
+#define REG_SW_MAC_TOS_PRIO_3          0x0343
+#define REG_SW_MAC_TOS_PRIO_4          0x0344
+#define REG_SW_MAC_TOS_PRIO_5          0x0345
+#define REG_SW_MAC_TOS_PRIO_6          0x0346
+#define REG_SW_MAC_TOS_PRIO_7          0x0347
+#define REG_SW_MAC_TOS_PRIO_8          0x0348
+#define REG_SW_MAC_TOS_PRIO_9          0x0349
+#define REG_SW_MAC_TOS_PRIO_10         0x034A
+#define REG_SW_MAC_TOS_PRIO_11         0x034B
+#define REG_SW_MAC_TOS_PRIO_12         0x034C
+#define REG_SW_MAC_TOS_PRIO_13         0x034D
+#define REG_SW_MAC_TOS_PRIO_14         0x034E
+#define REG_SW_MAC_TOS_PRIO_15         0x034F
+#define REG_SW_MAC_TOS_PRIO_16         0x0350
+#define REG_SW_MAC_TOS_PRIO_17         0x0351
+#define REG_SW_MAC_TOS_PRIO_18         0x0352
+#define REG_SW_MAC_TOS_PRIO_19         0x0353
+#define REG_SW_MAC_TOS_PRIO_20         0x0354
+#define REG_SW_MAC_TOS_PRIO_21         0x0355
+#define REG_SW_MAC_TOS_PRIO_22         0x0356
+#define REG_SW_MAC_TOS_PRIO_23         0x0357
+#define REG_SW_MAC_TOS_PRIO_24         0x0358
+#define REG_SW_MAC_TOS_PRIO_25         0x0359
+#define REG_SW_MAC_TOS_PRIO_26         0x035A
+#define REG_SW_MAC_TOS_PRIO_27         0x035B
+#define REG_SW_MAC_TOS_PRIO_28         0x035C
+#define REG_SW_MAC_TOS_PRIO_29         0x035D
+#define REG_SW_MAC_TOS_PRIO_30         0x035E
+#define REG_SW_MAC_TOS_PRIO_31         0x035F
+
+#define REG_SW_MRI_CTRL_0              0x0370
+
+#define SW_IGMP_SNOOP                  BIT(6)
+#define SW_IPV6_MLD_OPTION             BIT(3)
+#define SW_IPV6_MLD_SNOOP              BIT(2)
+#define SW_MIRROR_RX_TX                        BIT(0)
+
+#define REG_SW_CLASS_D_IP_CTRL__4      0x0374
+
+#define SW_CLASS_D_IP_ENABLE           BIT(31)
+
+#define REG_SW_MRI_CTRL_8              0x0378
+
+#define SW_NO_COLOR_S                  6
+#define SW_RED_COLOR_S                 4
+#define SW_YELLOW_COLOR_S              2
+#define SW_GREEN_COLOR_S               0
+#define SW_COLOR_M                     0x3
+
+#define REG_SW_QM_CTRL__4              0x0390
+
+#define PRIO_SCHEME_SELECT_M           KS_PRIO_M
+#define PRIO_SCHEME_SELECT_S           6
+#define PRIO_MAP_3_HI                  0
+#define PRIO_MAP_2_HI                  2
+#define PRIO_MAP_0_LO                  3
+#define UNICAST_VLAN_BOUNDARY          BIT(1)
+
+#define REG_SW_EEE_QM_CTRL__2          0x03C0
+
+#define REG_SW_EEE_TXQ_WAIT_TIME__2    0x03C2
+
+/* 4 - */
+#define REG_SW_VLAN_ENTRY__4           0x0400
+
+#define VLAN_VALID                     BIT(31)
+#define VLAN_FORWARD_OPTION            BIT(27)
+#define VLAN_PRIO_M                    KS_PRIO_M
+#define VLAN_PRIO_S                    24
+#define VLAN_MSTP_M                    0x7
+#define VLAN_MSTP_S                    12
+#define VLAN_FID_M                     0x7F
+
+#define REG_SW_VLAN_ENTRY_UNTAG__4     0x0404
+#define REG_SW_VLAN_ENTRY_PORTS__4     0x0408
+
+#define REG_SW_VLAN_ENTRY_INDEX__2     0x040C
+
+#define VLAN_INDEX_M                   0x0FFF
+
+#define REG_SW_VLAN_CTRL               0x040E
+
+#define VLAN_START                     BIT(7)
+#define VLAN_ACTION                    0x3
+#define VLAN_WRITE                     1
+#define VLAN_READ                      2
+#define VLAN_CLEAR                     3
+
+#define REG_SW_ALU_INDEX_0             0x0410
+
+#define ALU_FID_INDEX_S                        16
+#define ALU_MAC_ADDR_HI                        0xFFFF
+
+#define REG_SW_ALU_INDEX_1             0x0414
+
+#define ALU_DIRECT_INDEX_M             (BIT(12) - 1)
+
+#define REG_SW_ALU_CTRL__4             0x0418
+
+#define ALU_VALID_CNT_M                        (BIT(14) - 1)
+#define ALU_VALID_CNT_S                        16
+#define ALU_START                      BIT(7)
+#define ALU_VALID                      BIT(6)
+#define ALU_DIRECT                     BIT(2)
+#define ALU_ACTION                     0x3
+#define ALU_WRITE                      1
+#define ALU_READ                       2
+#define ALU_SEARCH                     3
+
+#define REG_SW_ALU_STAT_CTRL__4                0x041C
+
+#define ALU_STAT_INDEX_M               (BIT(4) - 1)
+#define ALU_STAT_INDEX_S               16
+#define ALU_RESV_MCAST_INDEX_M         (BIT(6) - 1)
+#define ALU_STAT_START                 BIT(7)
+#define ALU_RESV_MCAST_ADDR            BIT(1)
+#define ALU_STAT_READ                  BIT(0)
+
+#define REG_SW_ALU_VAL_A               0x0420
+
+#define ALU_V_STATIC_VALID             BIT(31)
+#define ALU_V_SRC_FILTER               BIT(30)
+#define ALU_V_DST_FILTER               BIT(29)
+#define ALU_V_PRIO_AGE_CNT_M           (BIT(3) - 1)
+#define ALU_V_PRIO_AGE_CNT_S           26
+#define ALU_V_MSTP_M                   0x7
+
+#define REG_SW_ALU_VAL_B               0x0424
+
+#define ALU_V_OVERRIDE                 BIT(31)
+#define ALU_V_USE_FID                  BIT(30)
+#define ALU_V_PORT_MAP                 (BIT(24) - 1)
+
+#define REG_SW_ALU_VAL_C               0x0428
+
+#define ALU_V_FID_M                    (BIT(16) - 1)
+#define ALU_V_FID_S                    16
+#define ALU_V_MAC_ADDR_HI              0xFFFF
+
+#define REG_SW_ALU_VAL_D               0x042C
+
+#define REG_HSR_ALU_INDEX_0            0x0440
+
+#define REG_HSR_ALU_INDEX_1            0x0444
+
+#define HSR_DST_MAC_INDEX_LO_S         16
+#define HSR_SRC_MAC_INDEX_HI           0xFFFF
+
+#define REG_HSR_ALU_INDEX_2            0x0448
+
+#define HSR_INDEX_MAX                  BIT(9)
+#define HSR_DIRECT_INDEX_M             (HSR_INDEX_MAX - 1)
+
+#define REG_HSR_ALU_INDEX_3            0x044C
+
+#define HSR_PATH_INDEX_M               (BIT(4) - 1)
+
+#define REG_HSR_ALU_CTRL__4            0x0450
+
+#define HSR_VALID_CNT_M                        (BIT(14) - 1)
+#define HSR_VALID_CNT_S                        16
+#define HSR_START                      BIT(7)
+#define HSR_VALID                      BIT(6)
+#define HSR_SEARCH_END                 BIT(5)
+#define HSR_DIRECT                     BIT(2)
+#define HSR_ACTION                     0x3
+#define HSR_WRITE                      1
+#define HSR_READ                       2
+#define HSR_SEARCH                     3
+
+#define REG_HSR_ALU_VAL_A              0x0454
+
+#define HSR_V_STATIC_VALID             BIT(31)
+#define HSR_V_AGE_CNT_M                        (BIT(3) - 1)
+#define HSR_V_AGE_CNT_S                        26
+#define HSR_V_PATH_ID_M                        (BIT(4) - 1)
+
+#define REG_HSR_ALU_VAL_B              0x0458
+
+#define REG_HSR_ALU_VAL_C              0x045C
+
+#define HSR_V_DST_MAC_ADDR_LO_S                16
+#define HSR_V_SRC_MAC_ADDR_HI          0xFFFF
+
+#define REG_HSR_ALU_VAL_D              0x0460
+
+#define REG_HSR_ALU_VAL_E              0x0464
+
+#define HSR_V_START_SEQ_1_S            16
+#define HSR_V_START_SEQ_2_S            0
+
+#define REG_HSR_ALU_VAL_F              0x0468
+
+#define HSR_V_EXP_SEQ_1_S              16
+#define HSR_V_EXP_SEQ_2_S              0
+
+#define REG_HSR_ALU_VAL_G              0x046C
+
+#define HSR_V_SEQ_CNT_1_S              16
+#define HSR_V_SEQ_CNT_2_S              0
+
+#define HSR_V_SEQ_M                    (BIT(16) - 1)
+
+/* 5 - PTP Clock */
+#define REG_PTP_CLK_CTRL               0x0500
+
+#define PTP_STEP_ADJ                   BIT(6)
+#define PTP_STEP_DIR                   BIT(5)
+#define PTP_READ_TIME                  BIT(4)
+#define PTP_LOAD_TIME                  BIT(3)
+#define PTP_CLK_ADJ_ENABLE             BIT(2)
+#define PTP_CLK_ENABLE                 BIT(1)
+#define PTP_CLK_RESET                  BIT(0)
+
+#define REG_PTP_RTC_SUB_NANOSEC__2     0x0502
+
+#define PTP_RTC_SUB_NANOSEC_M          0x0007
+
+#define REG_PTP_RTC_NANOSEC            0x0504
+#define REG_PTP_RTC_NANOSEC_H          0x0504
+#define REG_PTP_RTC_NANOSEC_L          0x0506
+
+#define REG_PTP_RTC_SEC                        0x0508
+#define REG_PTP_RTC_SEC_H              0x0508
+#define REG_PTP_RTC_SEC_L              0x050A
+
+#define REG_PTP_SUBNANOSEC_RATE                0x050C
+#define REG_PTP_SUBNANOSEC_RATE_H      0x050C
+
+#define PTP_RATE_DIR                   BIT(31)
+#define PTP_TMP_RATE_ENABLE            BIT(30)
+
+#define REG_PTP_SUBNANOSEC_RATE_L      0x050E
+
+#define REG_PTP_RATE_DURATION          0x0510
+#define REG_PTP_RATE_DURATION_H                0x0510
+#define REG_PTP_RATE_DURATION_L                0x0512
+
+#define REG_PTP_MSG_CONF1              0x0514
+
+#define PTP_802_1AS                    BIT(7)
+#define PTP_ENABLE                     BIT(6)
+#define PTP_ETH_ENABLE                 BIT(5)
+#define PTP_IPV4_UDP_ENABLE            BIT(4)
+#define PTP_IPV6_UDP_ENABLE            BIT(3)
+#define PTP_TC_P2P                     BIT(2)
+#define PTP_MASTER                     BIT(1)
+#define PTP_1STEP                      BIT(0)
+
+#define REG_PTP_MSG_CONF2              0x0516
+
+#define PTP_UNICAST_ENABLE             BIT(12)
+#define PTP_ALTERNATE_MASTER           BIT(11)
+#define PTP_ALL_HIGH_PRIO              BIT(10)
+#define PTP_SYNC_CHECK                 BIT(9)
+#define PTP_DELAY_CHECK                        BIT(8)
+#define PTP_PDELAY_CHECK               BIT(7)
+#define PTP_DROP_SYNC_DELAY_REQ                BIT(5)
+#define PTP_DOMAIN_CHECK               BIT(4)
+#define PTP_UDP_CHECKSUM               BIT(2)
+
+#define REG_PTP_DOMAIN_VERSION         0x0518
+#define PTP_VERSION_M                  0xFF00
+#define PTP_DOMAIN_M                   0x00FF
+
+#define REG_PTP_UNIT_INDEX__4          0x0520
+
+#define PTP_UNIT_M                     0xF
+
+#define PTP_GPIO_INDEX_S               16
+#define PTP_TSI_INDEX_S                        8
+#define PTP_TOU_INDEX_S                        0
+
+#define REG_PTP_TRIG_STATUS__4         0x0524
+
+#define TRIG_ERROR_S                   16
+#define TRIG_DONE_S                    0
+
+#define REG_PTP_INT_STATUS__4          0x0528
+
+#define TRIG_INT_S                     16
+#define TS_INT_S                       0
+
+#define TRIG_UNIT_M                    0x7
+#define TS_UNIT_M                      0x3
+
+#define REG_PTP_CTRL_STAT__4           0x052C
+
+#define GPIO_IN                                BIT(7)
+#define GPIO_OUT                       BIT(6)
+#define TS_INT_ENABLE                  BIT(5)
+#define TRIG_ACTIVE                    BIT(4)
+#define TRIG_ENABLE                    BIT(3)
+#define TRIG_RESET                     BIT(2)
+#define TS_ENABLE                      BIT(1)
+#define TS_RESET                       BIT(0)
+
+#define GPIO_CTRL_M                    (GPIO_IN | GPIO_OUT)
+
+#define TRIG_CTRL_M                    \
+       (TRIG_ACTIVE | TRIG_ENABLE | TRIG_RESET)
+
+#define TS_CTRL_M                      \
+       (TS_INT_ENABLE | TS_ENABLE | TS_RESET)
+
+#define REG_TRIG_TARGET_NANOSEC                0x0530
+#define REG_TRIG_TARGET_SEC            0x0534
+
+#define REG_TRIG_CTRL__4               0x0538
+
+#define TRIG_CASCADE_ENABLE            BIT(31)
+#define TRIG_CASCADE_TAIL              BIT(30)
+#define TRIG_CASCADE_UPS_M             0xF
+#define TRIG_CASCADE_UPS_S             26
+#define TRIG_NOW                       BIT(25)
+#define TRIG_NOTIFY                    BIT(24)
+#define TRIG_EDGE                      BIT(23)
+#define TRIG_PATTERN_S                 20
+#define TRIG_PATTERN_M                 0x7
+#define TRIG_NEG_EDGE                  0
+#define TRIG_POS_EDGE                  1
+#define TRIG_NEG_PULSE                 2
+#define TRIG_POS_PULSE                 3
+#define TRIG_NEG_PERIOD                        4
+#define TRIG_POS_PERIOD                        5
+#define TRIG_REG_OUTPUT                        6
+#define TRIG_GPO_S                     16
+#define TRIG_GPO_M                     0xF
+#define TRIG_CASCADE_ITERATE_CNT_M     0xFFFF
+
+#define REG_TRIG_CYCLE_WIDTH           0x053C
+
+#define REG_TRIG_CYCLE_CNT             0x0540
+
+#define TRIG_CYCLE_CNT_M               0xFFFF
+#define TRIG_CYCLE_CNT_S               16
+#define TRIG_BIT_PATTERN_M             0xFFFF
+
+#define REG_TRIG_ITERATE_TIME          0x0544
+
+#define REG_TRIG_PULSE_WIDTH__4                0x0548
+
+#define TRIG_PULSE_WIDTH_M             0x00FFFFFF
+
+#define REG_TS_CTRL_STAT__4            0x0550
+
+#define TS_EVENT_DETECT_M              0xF
+#define TS_EVENT_DETECT_S              17
+#define TS_EVENT_OVERFLOW              BIT(16)
+#define TS_GPI_M                       0xF
+#define TS_GPI_S                       8
+#define TS_DETECT_RISE                 BIT(7)
+#define TS_DETECT_FALL                 BIT(6)
+#define TS_DETECT_S                    6
+#define TS_CASCADE_TAIL                        BIT(5)
+#define TS_CASCADE_UPS_M               0xF
+#define TS_CASCADE_UPS_S               1
+#define TS_CASCADE_ENABLE              BIT(0)
+
+#define DETECT_RISE                    (TS_DETECT_RISE >> TS_DETECT_S)
+#define DETECT_FALL                    (TS_DETECT_FALL >> TS_DETECT_S)
+
+#define REG_TS_EVENT_0_NANOSEC         0x0554
+#define REG_TS_EVENT_0_SEC             0x0558
+#define REG_TS_EVENT_0_SUB_NANOSEC     0x055C
+
+#define REG_TS_EVENT_1_NANOSEC         0x0560
+#define REG_TS_EVENT_1_SEC             0x0564
+#define REG_TS_EVENT_1_SUB_NANOSEC     0x0568
+
+#define REG_TS_EVENT_2_NANOSEC         0x056C
+#define REG_TS_EVENT_2_SEC             0x0570
+#define REG_TS_EVENT_2_SUB_NANOSEC     0x0574
+
+#define REG_TS_EVENT_3_NANOSEC         0x0578
+#define REG_TS_EVENT_3_SEC             0x057C
+#define REG_TS_EVENT_3_SUB_NANOSEC     0x0580
+
+#define REG_TS_EVENT_4_NANOSEC         0x0584
+#define REG_TS_EVENT_4_SEC             0x0588
+#define REG_TS_EVENT_4_SUB_NANOSEC     0x058C
+
+#define REG_TS_EVENT_5_NANOSEC         0x0590
+#define REG_TS_EVENT_5_SEC             0x0594
+#define REG_TS_EVENT_5_SUB_NANOSEC     0x0598
+
+#define REG_TS_EVENT_6_NANOSEC         0x059C
+#define REG_TS_EVENT_6_SEC             0x05A0
+#define REG_TS_EVENT_6_SUB_NANOSEC     0x05A4
+
+#define REG_TS_EVENT_7_NANOSEC         0x05A8
+#define REG_TS_EVENT_7_SEC             0x05AC
+#define REG_TS_EVENT_7_SUB_NANOSEC     0x05B0
+
+#define TS_EVENT_EDGE_M                        0x1
+#define TS_EVENT_EDGE_S                        30
+#define TS_EVENT_NANOSEC_M             (BIT(30) - 1)
+
+#define TS_EVENT_SUB_NANOSEC_M         0x7
+
+#define TS_EVENT_SAMPLE                        \
+       (REG_TS_EVENT_1_NANOSEC - REG_TS_EVENT_0_NANOSEC)
+
+#define PORT_CTRL_ADDR(port, addr)     ((addr) | (((port) + 1) << 12))
+
+#define REG_GLOBAL_RR_INDEX__1         0x0600
+
+/* DLR */
+#define REG_DLR_SRC_PORT__4            0x0604
+
+#define DLR_SRC_PORT_UNICAST           BIT(31)
+#define DLR_SRC_PORT_M                 0x3
+#define DLR_SRC_PORT_BOTH              0
+#define DLR_SRC_PORT_EACH              1
+
+#define REG_DLR_IP_ADDR__4             0x0608
+
+#define REG_DLR_CTRL__1                        0x0610
+
+#define DLR_RESET_SEQ_ID               BIT(3)
+#define DLR_BACKUP_AUTO_ON             BIT(2)
+#define DLR_BEACON_TX_ENABLE           BIT(1)
+#define DLR_ASSIST_ENABLE              BIT(0)
+
+#define REG_DLR_STATE__1               0x0611
+
+#define DLR_NODE_STATE_M               0x3
+#define DLR_NODE_STATE_S               1
+#define DLR_NODE_STATE_IDLE            0
+#define DLR_NODE_STATE_FAULT           1
+#define DLR_NODE_STATE_NORMAL          2
+#define DLR_RING_STATE_FAULT           0
+#define DLR_RING_STATE_NORMAL          1
+
+#define REG_DLR_PRECEDENCE__1          0x0612
+
+#define REG_DLR_BEACON_INTERVAL__4     0x0614
+
+#define REG_DLR_BEACON_TIMEOUT__4      0x0618
+
+#define REG_DLR_TIMEOUT_WINDOW__4      0x061C
+
+#define DLR_TIMEOUT_WINDOW_M           (BIT(22) - 1)
+
+#define REG_DLR_VLAN_ID__2             0x0620
+
+#define DLR_VLAN_ID_M                  (BIT(12) - 1)
+
+#define REG_DLR_DEST_ADDR_0            0x0622
+#define REG_DLR_DEST_ADDR_1            0x0623
+#define REG_DLR_DEST_ADDR_2            0x0624
+#define REG_DLR_DEST_ADDR_3            0x0625
+#define REG_DLR_DEST_ADDR_4            0x0626
+#define REG_DLR_DEST_ADDR_5            0x0627
+
+#define REG_DLR_PORT_MAP__4            0x0628
+
+#define REG_DLR_CLASS__1               0x062C
+
+#define DLR_FRAME_QID_M                        0x3
+
+/* HSR */
+#define REG_HSR_PORT_MAP__4            0x0640
+
+#define REG_HSR_ALU_CTRL_0__1          0x0644
+
+#define HSR_DUPLICATE_DISCARD          BIT(7)
+#define HSR_NODE_UNICAST               BIT(6)
+#define HSR_AGE_CNT_DEFAULT_M          0x7
+#define HSR_AGE_CNT_DEFAULT_S          3
+#define HSR_LEARN_MCAST_DISABLE                BIT(2)
+#define HSR_HASH_OPTION_M              0x3
+#define HSR_HASH_DISABLE               0
+#define HSR_HASH_UPPER_BITS            1
+#define HSR_HASH_LOWER_BITS            2
+#define HSR_HASH_XOR_BOTH_BITS         3
+
+#define REG_HSR_ALU_CTRL_1__1          0x0645
+
+#define HSR_LEARN_UCAST_DISABLE                BIT(7)
+#define HSR_FLUSH_TABLE                        BIT(5)
+#define HSR_PROC_MCAST_SRC             BIT(3)
+#define HSR_AGING_ENABLE               BIT(2)
+
+#define REG_HSR_ALU_CTRL_2__2          0x0646
+
+#define REG_HSR_ALU_AGE_PERIOD__4      0x0648
+
+#define REG_HSR_ALU_INT_STATUS__1      0x064C
+#define REG_HSR_ALU_INT_MASK__1                0x064D
+
+#define HSR_WINDOW_OVERFLOW_INT                BIT(3)
+#define HSR_LEARN_FAIL_INT             BIT(2)
+#define HSR_ALMOST_FULL_INT            BIT(1)
+#define HSR_WRITE_FAIL_INT             BIT(0)
+
+#define REG_HSR_ALU_ENTRY_0__2         0x0650
+
+#define HSR_ENTRY_INDEX_M              (BIT(10) - 1)
+#define HSR_FAIL_INDEX_M               (BIT(8) - 1)
+
+#define REG_HSR_ALU_ENTRY_1__2         0x0652
+
+#define HSR_FAIL_LEARN_INDEX_M         (BIT(8) - 1)
+
+#define REG_HSR_ALU_ENTRY_3__2         0x0654
+
+#define HSR_CPU_ACCESS_ENTRY_INDEX_M   (BIT(8) - 1)
+
+/* 0 - Operation */
+#define REG_PORT_DEFAULT_VID           0x0000
+
+#define REG_PORT_CUSTOM_VID            0x0002
+#define REG_PORT_AVB_SR_1_VID          0x0004
+#define REG_PORT_AVB_SR_2_VID          0x0006
+
+#define REG_PORT_AVB_SR_1_TYPE         0x0008
+#define REG_PORT_AVB_SR_2_TYPE         0x000A
+
+#define REG_PORT_PME_STATUS            0x0013
+#define REG_PORT_PME_CTRL              0x0017
+
+#define PME_WOL_MAGICPKT               BIT(2)
+#define PME_WOL_LINKUP                 BIT(1)
+#define PME_WOL_ENERGY                 BIT(0)
+
+#define REG_PORT_INT_STATUS            0x001B
+#define REG_PORT_INT_MASK              0x001F
+
+#define PORT_SGMII_INT                 BIT(3)
+#define PORT_PTP_INT                   BIT(2)
+#define PORT_PHY_INT                   BIT(1)
+#define PORT_ACL_INT                   BIT(0)
+
+#define PORT_INT_MASK                  \
+       (PORT_SGMII_INT | PORT_PTP_INT | PORT_PHY_INT | PORT_ACL_INT)
+
+#define REG_PORT_CTRL_0                        0x0020
+
+#define PORT_MAC_LOOPBACK              BIT(7)
+#define PORT_FORCE_TX_FLOW_CTRL                BIT(4)
+#define PORT_FORCE_RX_FLOW_CTRL                BIT(3)
+#define PORT_TAIL_TAG_ENABLE           BIT(2)
+#define PORT_QUEUE_SPLIT_ENABLE                0x3
+
+#define REG_PORT_CTRL_1                        0x0021
+
+#define PORT_SRP_ENABLE                        0x3
+
+#define REG_PORT_STATUS_0              0x0030
+
+#define PORT_INTF_SPEED_M              0x3
+#define PORT_INTF_SPEED_S              3
+#define PORT_INTF_FULL_DUPLEX          BIT(2)
+#define PORT_TX_FLOW_CTRL              BIT(1)
+#define PORT_RX_FLOW_CTRL              BIT(0)
+
+#define REG_PORT_STATUS_1              0x0034
+
+/* 1 - PHY */
+#define REG_PORT_PHY_CTRL              0x0100
+
+#define PORT_PHY_RESET                 BIT(15)
+#define PORT_PHY_LOOPBACK              BIT(14)
+#define PORT_SPEED_100MBIT             BIT(13)
+#define PORT_AUTO_NEG_ENABLE           BIT(12)
+#define PORT_POWER_DOWN                        BIT(11)
+#define PORT_ISOLATE                   BIT(10)
+#define PORT_AUTO_NEG_RESTART          BIT(9)
+#define PORT_FULL_DUPLEX               BIT(8)
+#define PORT_COLLISION_TEST            BIT(7)
+#define PORT_SPEED_1000MBIT            BIT(6)
+
+#define REG_PORT_PHY_STATUS            0x0102
+
+#define PORT_100BT4_CAPABLE            BIT(15)
+#define PORT_100BTX_FD_CAPABLE         BIT(14)
+#define PORT_100BTX_CAPABLE            BIT(13)
+#define PORT_10BT_FD_CAPABLE           BIT(12)
+#define PORT_10BT_CAPABLE              BIT(11)
+#define PORT_EXTENDED_STATUS           BIT(8)
+#define PORT_MII_SUPPRESS_CAPABLE      BIT(6)
+#define PORT_AUTO_NEG_ACKNOWLEDGE      BIT(5)
+#define PORT_REMOTE_FAULT              BIT(4)
+#define PORT_AUTO_NEG_CAPABLE          BIT(3)
+#define PORT_LINK_STATUS               BIT(2)
+#define PORT_JABBER_DETECT             BIT(1)
+#define PORT_EXTENDED_CAPABILITY       BIT(0)
+
+#define REG_PORT_PHY_ID_HI             0x0104
+#define REG_PORT_PHY_ID_LO             0x0106
+
+#define KSZ9477_ID_HI                  0x0022
+#define KSZ9477_ID_LO                  0x1622
+
+#define REG_PORT_PHY_AUTO_NEGOTIATION  0x0108
+
+#define PORT_AUTO_NEG_NEXT_PAGE                BIT(15)
+#define PORT_AUTO_NEG_REMOTE_FAULT     BIT(13)
+#define PORT_AUTO_NEG_ASYM_PAUSE       BIT(11)
+#define PORT_AUTO_NEG_SYM_PAUSE                BIT(10)
+#define PORT_AUTO_NEG_100BT4           BIT(9)
+#define PORT_AUTO_NEG_100BTX_FD                BIT(8)
+#define PORT_AUTO_NEG_100BTX           BIT(7)
+#define PORT_AUTO_NEG_10BT_FD          BIT(6)
+#define PORT_AUTO_NEG_10BT             BIT(5)
+#define PORT_AUTO_NEG_SELECTOR         0x001F
+#define PORT_AUTO_NEG_802_3            0x0001
+
+#define PORT_AUTO_NEG_PAUSE            \
+       (PORT_AUTO_NEG_ASYM_PAUSE | PORT_AUTO_NEG_SYM_PAUSE)
+
+#define REG_PORT_PHY_REMOTE_CAPABILITY 0x010A
+
+#define PORT_REMOTE_NEXT_PAGE          BIT(15)
+#define PORT_REMOTE_ACKNOWLEDGE                BIT(14)
+#define PORT_REMOTE_REMOTE_FAULT       BIT(13)
+#define PORT_REMOTE_ASYM_PAUSE         BIT(11)
+#define PORT_REMOTE_SYM_PAUSE          BIT(10)
+#define PORT_REMOTE_100BTX_FD          BIT(8)
+#define PORT_REMOTE_100BTX             BIT(7)
+#define PORT_REMOTE_10BT_FD            BIT(6)
+#define PORT_REMOTE_10BT               BIT(5)
+
+#define REG_PORT_PHY_1000_CTRL         0x0112
+
+#define PORT_AUTO_NEG_MANUAL           BIT(12)
+#define PORT_AUTO_NEG_MASTER           BIT(11)
+#define PORT_AUTO_NEG_MASTER_PREFERRED BIT(10)
+#define PORT_AUTO_NEG_1000BT_FD                BIT(9)
+#define PORT_AUTO_NEG_1000BT           BIT(8)
+
+#define REG_PORT_PHY_1000_STATUS       0x0114
+
+#define PORT_MASTER_FAULT              BIT(15)
+#define PORT_LOCAL_MASTER              BIT(14)
+#define PORT_LOCAL_RX_OK               BIT(13)
+#define PORT_REMOTE_RX_OK              BIT(12)
+#define PORT_REMOTE_1000BT_FD          BIT(11)
+#define PORT_REMOTE_1000BT             BIT(10)
+#define PORT_REMOTE_IDLE_CNT_M         0x0F
+
+#define PORT_PHY_1000_STATIC_STATUS    \
+       (PORT_LOCAL_RX_OK |             \
+       PORT_REMOTE_RX_OK |             \
+       PORT_REMOTE_1000BT_FD |         \
+       PORT_REMOTE_1000BT)
+
+#define REG_PORT_PHY_MMD_SETUP         0x011A
+
+#define PORT_MMD_OP_MODE_M             0x3
+#define PORT_MMD_OP_MODE_S             14
+#define PORT_MMD_OP_INDEX              0
+#define PORT_MMD_OP_DATA_NO_INCR       1
+#define PORT_MMD_OP_DATA_INCR_RW       2
+#define PORT_MMD_OP_DATA_INCR_W                3
+#define PORT_MMD_DEVICE_ID_M           0x1F
+
+#define MMD_SETUP(mode, dev)           \
+       (((u16)(mode) << PORT_MMD_OP_MODE_S) | (dev))
+
+#define REG_PORT_PHY_MMD_INDEX_DATA    0x011C
+
+#define MMD_DEVICE_ID_DSP              1
+
+#define MMD_DSP_SQI_CHAN_A             0xAC
+#define MMD_DSP_SQI_CHAN_B             0xAD
+#define MMD_DSP_SQI_CHAN_C             0xAE
+#define MMD_DSP_SQI_CHAN_D             0xAF
+
+#define DSP_SQI_ERR_DETECTED           BIT(15)
+#define DSP_SQI_AVG_ERR                        0x7FFF
+
+#define MMD_DEVICE_ID_COMMON           2
+
+#define MMD_DEVICE_ID_EEE_ADV          7
+
+#define MMD_EEE_ADV                    0x3C
+#define EEE_ADV_100MBIT                        BIT(1)
+#define EEE_ADV_1GBIT                  BIT(2)
+
+#define MMD_EEE_LP_ADV                 0x3D
+#define MMD_EEE_MSG_CODE               0x3F
+
+#define MMD_DEVICE_ID_AFED             0x1C
+
+#define REG_PORT_PHY_EXTENDED_STATUS   0x011E
+
+#define PORT_100BTX_FD_ABLE            BIT(15)
+#define PORT_100BTX_ABLE               BIT(14)
+#define PORT_10BT_FD_ABLE              BIT(13)
+#define PORT_10BT_ABLE                 BIT(12)
+
+#define REG_PORT_SGMII_ADDR__4         0x0200
+#define PORT_SGMII_AUTO_INCR           BIT(23)
+#define PORT_SGMII_DEVICE_ID_M         0x1F
+#define PORT_SGMII_DEVICE_ID_S         16
+#define PORT_SGMII_ADDR_M              (BIT(21) - 1)
+
+#define REG_PORT_SGMII_DATA__4         0x0204
+#define PORT_SGMII_DATA_M              (BIT(16) - 1)
+
+#define MMD_DEVICE_ID_PMA              0x01
+#define MMD_DEVICE_ID_PCS              0x03
+#define MMD_DEVICE_ID_PHY_XS           0x04
+#define MMD_DEVICE_ID_DTE_XS           0x05
+#define MMD_DEVICE_ID_AN               0x07
+#define MMD_DEVICE_ID_VENDOR_CTRL      0x1E
+#define MMD_DEVICE_ID_VENDOR_MII       0x1F
+
+#define SR_MII                         MMD_DEVICE_ID_VENDOR_MII
+
+#define MMD_SR_MII_CTRL                        0x0000
+
+#define SR_MII_RESET                   BIT(15)
+#define SR_MII_LOOPBACK                        BIT(14)
+#define SR_MII_SPEED_100MBIT           BIT(13)
+#define SR_MII_AUTO_NEG_ENABLE         BIT(12)
+#define SR_MII_POWER_DOWN              BIT(11)
+#define SR_MII_AUTO_NEG_RESTART                BIT(9)
+#define SR_MII_FULL_DUPLEX             BIT(8)
+#define SR_MII_SPEED_1000MBIT          BIT(6)
+
+#define MMD_SR_MII_STATUS              0x0001
+#define MMD_SR_MII_ID_1                        0x0002
+#define MMD_SR_MII_ID_2                        0x0003
+#define MMD_SR_MII_AUTO_NEGOTIATION    0x0004
+
+#define SR_MII_AUTO_NEG_NEXT_PAGE      BIT(15)
+#define SR_MII_AUTO_NEG_REMOTE_FAULT_M 0x3
+#define SR_MII_AUTO_NEG_REMOTE_FAULT_S 12
+#define SR_MII_AUTO_NEG_NO_ERROR       0
+#define SR_MII_AUTO_NEG_OFFLINE                1
+#define SR_MII_AUTO_NEG_LINK_FAILURE   2
+#define SR_MII_AUTO_NEG_ERROR          3
+#define SR_MII_AUTO_NEG_PAUSE_M                0x3
+#define SR_MII_AUTO_NEG_PAUSE_S                7
+#define SR_MII_AUTO_NEG_NO_PAUSE       0
+#define SR_MII_AUTO_NEG_ASYM_PAUSE_TX  1
+#define SR_MII_AUTO_NEG_SYM_PAUSE      2
+#define SR_MII_AUTO_NEG_ASYM_PAUSE_RX  3
+#define SR_MII_AUTO_NEG_HALF_DUPLEX    BIT(6)
+#define SR_MII_AUTO_NEG_FULL_DUPLEX    BIT(5)
+
+#define MMD_SR_MII_REMOTE_CAPABILITY   0x0005
+#define MMD_SR_MII_AUTO_NEG_EXP                0x0006
+#define MMD_SR_MII_AUTO_NEG_EXT                0x000F
+
+#define MMD_SR_MII_DIGITAL_CTRL_1      0x8000
+
+#define MMD_SR_MII_AUTO_NEG_CTRL       0x8001
+
+#define SR_MII_8_BIT                   BIT(8)
+#define SR_MII_SGMII_LINK_UP           BIT(4)
+#define SR_MII_TX_CFG_PHY_MASTER       BIT(3)
+#define SR_MII_PCS_MODE_M              0x3
+#define SR_MII_PCS_MODE_S              1
+#define SR_MII_PCS_SGMII               2
+#define SR_MII_AUTO_NEG_COMPLETE_INTR  BIT(0)
+
+#define MMD_SR_MII_AUTO_NEG_STATUS     0x8002
+
+#define SR_MII_STAT_LINK_UP            BIT(4)
+#define SR_MII_STAT_M                  0x3
+#define SR_MII_STAT_S                  2
+#define SR_MII_STAT_10_MBPS            0
+#define SR_MII_STAT_100_MBPS           1
+#define SR_MII_STAT_1000_MBPS          2
+#define SR_MII_STAT_FULL_DUPLEX                BIT(1)
+
+#define MMD_SR_MII_PHY_CTRL            0x80A0
+
+#define SR_MII_PHY_LANE_SEL_M          0xF
+#define SR_MII_PHY_LANE_SEL_S          8
+#define SR_MII_PHY_WRITE               BIT(1)
+#define SR_MII_PHY_START_BUSY          BIT(0)
+
+#define MMD_SR_MII_PHY_ADDR            0x80A1
+
+#define SR_MII_PHY_ADDR_M              (BIT(16) - 1)
+
+#define MMD_SR_MII_PHY_DATA            0x80A2
+
+#define SR_MII_PHY_DATA_M              (BIT(16) - 1)
+
+#define SR_MII_PHY_JTAG_CHIP_ID_HI     0x000C
+#define SR_MII_PHY_JTAG_CHIP_ID_LO     0x000D
+
+#define REG_PORT_PHY_REMOTE_LB_LED     0x0122
+
+#define PORT_REMOTE_LOOPBACK           BIT(8)
+#define PORT_LED_SELECT                        (3 << 6)
+#define PORT_LED_CTRL                  (3 << 4)
+#define PORT_LED_CTRL_TEST             BIT(3)
+#define PORT_10BT_PREAMBLE             BIT(2)
+#define PORT_LINK_MD_10BT_ENABLE       BIT(1)
+#define PORT_LINK_MD_PASS              BIT(0)
+
+#define REG_PORT_PHY_LINK_MD           0x0124
+
+#define PORT_START_CABLE_DIAG          BIT(15)
+#define PORT_TX_DISABLE                        BIT(14)
+#define PORT_CABLE_DIAG_PAIR_M         0x3
+#define PORT_CABLE_DIAG_PAIR_S         12
+#define PORT_CABLE_DIAG_SELECT_M       0x3
+#define PORT_CABLE_DIAG_SELECT_S       10
+#define PORT_CABLE_DIAG_RESULT_M       0x3
+#define PORT_CABLE_DIAG_RESULT_S       8
+#define PORT_CABLE_STAT_NORMAL         0
+#define PORT_CABLE_STAT_OPEN           1
+#define PORT_CABLE_STAT_SHORT          2
+#define PORT_CABLE_STAT_FAILED         3
+#define PORT_CABLE_FAULT_COUNTER       0x00FF
+
+#define REG_PORT_PHY_PMA_STATUS                0x0126
+
+#define PORT_1000_LINK_GOOD            BIT(1)
+#define PORT_100_LINK_GOOD             BIT(0)
+
+#define REG_PORT_PHY_DIGITAL_STATUS    0x0128
+
+#define PORT_LINK_DETECT               BIT(14)
+#define PORT_SIGNAL_DETECT             BIT(13)
+#define PORT_PHY_STAT_MDI              BIT(12)
+#define PORT_PHY_STAT_MASTER           BIT(11)
+
+#define REG_PORT_PHY_RXER_COUNTER      0x012A
+
+#define REG_PORT_PHY_INT_ENABLE                0x0136
+#define REG_PORT_PHY_INT_STATUS                0x0137
+
+#define JABBER_INT                     BIT(7)
+#define RX_ERR_INT                     BIT(6)
+#define PAGE_RX_INT                    BIT(5)
+#define PARALLEL_DETECT_FAULT_INT      BIT(4)
+#define LINK_PARTNER_ACK_INT           BIT(3)
+#define LINK_DOWN_INT                  BIT(2)
+#define REMOTE_FAULT_INT               BIT(1)
+#define LINK_UP_INT                    BIT(0)
+
+#define REG_PORT_PHY_DIGITAL_DEBUG_1   0x0138
+
+#define PORT_REG_CLK_SPEED_25_MHZ      BIT(14)
+#define PORT_PHY_FORCE_MDI             BIT(7)
+#define PORT_PHY_AUTO_MDIX_DISABLE     BIT(6)
+
+/* Same as PORT_PHY_LOOPBACK */
+#define PORT_PHY_PCS_LOOPBACK          BIT(0)
+
+#define REG_PORT_PHY_DIGITAL_DEBUG_2   0x013A
+
+#define REG_PORT_PHY_DIGITAL_DEBUG_3   0x013C
+
+#define PORT_100BT_FIXED_LATENCY       BIT(15)
+
+#define REG_PORT_PHY_PHY_CTRL          0x013E
+
+#define PORT_INT_PIN_HIGH              BIT(14)
+#define PORT_ENABLE_JABBER             BIT(9)
+#define PORT_STAT_SPEED_1000MBIT       BIT(6)
+#define PORT_STAT_SPEED_100MBIT                BIT(5)
+#define PORT_STAT_SPEED_10MBIT         BIT(4)
+#define PORT_STAT_FULL_DUPLEX          BIT(3)
+
+/* Same as PORT_PHY_STAT_MASTER */
+#define PORT_STAT_MASTER               BIT(2)
+#define PORT_RESET                     BIT(1)
+#define PORT_LINK_STATUS_FAIL          BIT(0)
+
+/* 3 - xMII */
+#define REG_PORT_XMII_CTRL_0           0x0300
+
+#define PORT_SGMII_SEL                 BIT(7)
+#define PORT_MII_FULL_DUPLEX           BIT(6)
+#define PORT_MII_100MBIT               BIT(4)
+#define PORT_GRXC_ENABLE               BIT(0)
+
+#define REG_PORT_XMII_CTRL_1           0x0301
+
+#define PORT_RMII_CLK_SEL              BIT(7)
+/* S1 */
+#define PORT_MII_1000MBIT_S1           BIT(6)
+/* S2 */
+#define PORT_MII_NOT_1GBIT             BIT(6)
+#define PORT_MII_SEL_EDGE              BIT(5)
+#define PORT_RGMII_ID_IG_ENABLE                BIT(4)
+#define PORT_RGMII_ID_EG_ENABLE                BIT(3)
+#define PORT_MII_MAC_MODE              BIT(2)
+#define PORT_MII_SEL_M                 0x3
+/* S1 */
+#define PORT_MII_SEL_S1                        0x0
+#define PORT_RMII_SEL_S1               0x1
+#define PORT_GMII_SEL_S1               0x2
+#define PORT_RGMII_SEL_S1              0x3
+/* S2 */
+#define PORT_RGMII_SEL                 0x0
+#define PORT_RMII_SEL                  0x1
+#define PORT_GMII_SEL                  0x2
+#define PORT_MII_SEL                   0x3
+
+/* 4 - MAC */
+#define REG_PORT_MAC_CTRL_0            0x0400
+
+#define PORT_BROADCAST_STORM           BIT(1)
+#define PORT_JUMBO_FRAME               BIT(0)
+
+#define REG_PORT_MAC_CTRL_1            0x0401
+
+#define PORT_BACK_PRESSURE             BIT(3)
+#define PORT_PASS_ALL                  BIT(0)
+
+#define REG_PORT_MAC_CTRL_2            0x0402
+
+#define PORT_100BT_EEE_DISABLE         BIT(7)
+#define PORT_1000BT_EEE_DISABLE                BIT(6)
+
+#define REG_PORT_MAC_IN_RATE_LIMIT     0x0403
+
+#define PORT_IN_PORT_BASED_S           6
+#define PORT_RATE_PACKET_BASED_S       5
+#define PORT_IN_FLOW_CTRL_S            4
+#define PORT_COUNT_IFG_S               1
+#define PORT_COUNT_PREAMBLE_S          0
+#define PORT_IN_PORT_BASED             BIT(6)
+#define PORT_IN_PACKET_BASED           BIT(5)
+#define PORT_IN_FLOW_CTRL              BIT(4)
+#define PORT_IN_LIMIT_MODE_M           0x3
+#define PORT_IN_LIMIT_MODE_S           2
+#define PORT_IN_ALL                    0
+#define PORT_IN_UNICAST                        1
+#define PORT_IN_MULTICAST              2
+#define PORT_IN_BROADCAST              3
+#define PORT_COUNT_IFG                 BIT(1)
+#define PORT_COUNT_PREAMBLE            BIT(0)
+
+#define REG_PORT_IN_RATE_0             0x0410
+#define REG_PORT_IN_RATE_1             0x0411
+#define REG_PORT_IN_RATE_2             0x0412
+#define REG_PORT_IN_RATE_3             0x0413
+#define REG_PORT_IN_RATE_4             0x0414
+#define REG_PORT_IN_RATE_5             0x0415
+#define REG_PORT_IN_RATE_6             0x0416
+#define REG_PORT_IN_RATE_7             0x0417
+
+#define REG_PORT_OUT_RATE_0            0x0420
+#define REG_PORT_OUT_RATE_1            0x0421
+#define REG_PORT_OUT_RATE_2            0x0422
+#define REG_PORT_OUT_RATE_3            0x0423
+
+#define PORT_RATE_LIMIT_M              (BIT(7) - 1)
+
+/* 5 - MIB Counters */
+#define REG_PORT_MIB_CTRL_STAT__4      0x0500
+
+#define MIB_COUNTER_OVERFLOW           BIT(31)
+#define MIB_COUNTER_VALID              BIT(30)
+#define MIB_COUNTER_READ               BIT(25)
+#define MIB_COUNTER_FLUSH_FREEZE       BIT(24)
+#define MIB_COUNTER_INDEX_M            (BIT(8) - 1)
+#define MIB_COUNTER_INDEX_S            16
+#define MIB_COUNTER_DATA_HI_M          0xF
+
+#define REG_PORT_MIB_DATA              0x0504
+
+/* 6 - ACL */
+#define REG_PORT_ACL_0                 0x0600
+
+#define ACL_FIRST_RULE_M               0xF
+
+#define REG_PORT_ACL_1                 0x0601
+
+#define ACL_MODE_M                     0x3
+#define ACL_MODE_S                     4
+#define ACL_MODE_DISABLE               0
+#define ACL_MODE_LAYER_2               1
+#define ACL_MODE_LAYER_3               2
+#define ACL_MODE_LAYER_4               3
+#define ACL_ENABLE_M                   0x3
+#define ACL_ENABLE_S                   2
+#define ACL_ENABLE_2_COUNT             0
+#define ACL_ENABLE_2_TYPE              1
+#define ACL_ENABLE_2_MAC               2
+#define ACL_ENABLE_2_BOTH              3
+#define ACL_ENABLE_3_IP                        1
+#define ACL_ENABLE_3_SRC_DST_COMP      2
+#define ACL_ENABLE_4_PROTOCOL          0
+#define ACL_ENABLE_4_TCP_PORT_COMP     1
+#define ACL_ENABLE_4_UDP_PORT_COMP     2
+#define ACL_ENABLE_4_TCP_SEQN_COMP     3
+#define ACL_SRC                                BIT(1)
+#define ACL_EQUAL                      BIT(0)
+
+#define REG_PORT_ACL_2                 0x0602
+#define REG_PORT_ACL_3                 0x0603
+
+#define ACL_MAX_PORT                   0xFFFF
+
+#define REG_PORT_ACL_4                 0x0604
+#define REG_PORT_ACL_5                 0x0605
+
+#define ACL_MIN_PORT                   0xFFFF
+#define ACL_IP_ADDR                    0xFFFFFFFF
+#define ACL_TCP_SEQNUM                 0xFFFFFFFF
+
+#define REG_PORT_ACL_6                 0x0606
+
+#define ACL_RESERVED                   0xF8
+#define ACL_PORT_MODE_M                        0x3
+#define ACL_PORT_MODE_S                        1
+#define ACL_PORT_MODE_DISABLE          0
+#define ACL_PORT_MODE_EITHER           1
+#define ACL_PORT_MODE_IN_RANGE         2
+#define ACL_PORT_MODE_OUT_OF_RANGE     3
+
+#define REG_PORT_ACL_7                 0x0607
+
+#define ACL_TCP_FLAG_ENABLE            BIT(0)
+
+#define REG_PORT_ACL_8                 0x0608
+
+#define ACL_TCP_FLAG_M                 0xFF
+
+#define REG_PORT_ACL_9                 0x0609
+
+#define ACL_TCP_FLAG                   0xFF
+#define ACL_ETH_TYPE                   0xFFFF
+#define ACL_IP_M                       0xFFFFFFFF
+
+#define REG_PORT_ACL_A                 0x060A
+
+#define ACL_PRIO_MODE_M                        0x3
+#define ACL_PRIO_MODE_S                        6
+#define ACL_PRIO_MODE_DISABLE          0
+#define ACL_PRIO_MODE_HIGHER           1
+#define ACL_PRIO_MODE_LOWER            2
+#define ACL_PRIO_MODE_REPLACE          3
+#define ACL_PRIO_M                     KS_PRIO_M
+#define ACL_PRIO_S                     3
+#define ACL_VLAN_PRIO_REPLACE          BIT(2)
+#define ACL_VLAN_PRIO_M                        KS_PRIO_M
+#define ACL_VLAN_PRIO_HI_M             0x3
+
+#define REG_PORT_ACL_B                 0x060B
+
+#define ACL_VLAN_PRIO_LO_M             0x8
+#define ACL_VLAN_PRIO_S                        7
+#define ACL_MAP_MODE_M                 0x3
+#define ACL_MAP_MODE_S                 5
+#define ACL_MAP_MODE_DISABLE           0
+#define ACL_MAP_MODE_OR                        1
+#define ACL_MAP_MODE_AND               2
+#define ACL_MAP_MODE_REPLACE           3
+
+#define ACL_CNT_M                      (BIT(11) - 1)
+#define ACL_CNT_S                      5
+
+#define REG_PORT_ACL_C                 0x060C
+
+#define REG_PORT_ACL_D                 0x060D
+#define ACL_MSEC_UNIT                  BIT(6)
+#define ACL_INTR_MODE                  BIT(5)
+#define ACL_PORT_MAP                   0x7F
+
+#define REG_PORT_ACL_E                 0x060E
+#define REG_PORT_ACL_F                 0x060F
+
+#define REG_PORT_ACL_BYTE_EN_MSB       0x0610
+#define REG_PORT_ACL_BYTE_EN_LSB       0x0611
+
+#define ACL_ACTION_START               0xA
+#define ACL_ACTION_LEN                 4
+#define ACL_INTR_CNT_START             0xD
+#define ACL_RULESET_START              0xE
+#define ACL_RULESET_LEN                        2
+#define ACL_TABLE_LEN                  16
+
+#define ACL_ACTION_ENABLE              0x003C
+#define ACL_MATCH_ENABLE               0x7FC3
+#define ACL_RULESET_ENABLE             0x8003
+#define ACL_BYTE_ENABLE                        0xFFFF
+
+#define REG_PORT_ACL_CTRL_0            0x0612
+
+#define PORT_ACL_WRITE_DONE            BIT(6)
+#define PORT_ACL_READ_DONE             BIT(5)
+#define PORT_ACL_WRITE                 BIT(4)
+#define PORT_ACL_INDEX_M               0xF
+
+#define REG_PORT_ACL_CTRL_1            0x0613
+
+/* 8 - Classification and Policing */
+#define REG_PORT_MRI_MIRROR_CTRL       0x0800
+
+#define PORT_MIRROR_RX                 BIT(6)
+#define PORT_MIRROR_TX                 BIT(5)
+#define PORT_MIRROR_SNIFFER            BIT(1)
+
+#define REG_PORT_MRI_PRIO_CTRL         0x0801
+
+#define PORT_HIGHEST_PRIO              BIT(7)
+#define PORT_OR_PRIO                   BIT(6)
+#define PORT_MAC_PRIO_ENABLE           BIT(4)
+#define PORT_VLAN_PRIO_ENABLE          BIT(3)
+#define PORT_802_1P_PRIO_ENABLE                BIT(2)
+#define PORT_DIFFSERV_PRIO_ENABLE      BIT(1)
+#define PORT_ACL_PRIO_ENABLE           BIT(0)
+
+#define REG_PORT_MRI_MAC_CTRL          0x0802
+
+#define PORT_USER_PRIO_CEILING         BIT(7)
+#define PORT_DROP_NON_VLAN             BIT(4)
+#define PORT_DROP_TAG                  BIT(3)
+#define PORT_BASED_PRIO_M              KS_PRIO_M
+#define PORT_BASED_PRIO_S              0
+
+#define REG_PORT_MRI_AUTHEN_CTRL       0x0803
+
+#define PORT_ACL_ENABLE                        BIT(2)
+#define PORT_AUTHEN_MODE               0x3
+#define PORT_AUTHEN_PASS               0
+#define PORT_AUTHEN_BLOCK              1
+#define PORT_AUTHEN_TRAP               2
+
+#define REG_PORT_MRI_INDEX__4          0x0804
+
+#define MRI_INDEX_P_M                  0x7
+#define MRI_INDEX_P_S                  16
+#define MRI_INDEX_Q_M                  0x3
+#define MRI_INDEX_Q_S                  0
+
+#define REG_PORT_MRI_TC_MAP__4         0x0808
+
+#define PORT_TC_MAP_M                  0xf
+#define PORT_TC_MAP_S                  4
+
+#define REG_PORT_MRI_POLICE_CTRL__4    0x080C
+
+#define POLICE_DROP_ALL                        BIT(10)
+#define POLICE_PACKET_TYPE_M           0x3
+#define POLICE_PACKET_TYPE_S           8
+#define POLICE_PACKET_DROPPED          0
+#define POLICE_PACKET_GREEN            1
+#define POLICE_PACKET_YELLOW           2
+#define POLICE_PACKET_RED              3
+#define PORT_BASED_POLICING            BIT(7)
+#define NON_DSCP_COLOR_M               0x3
+#define NON_DSCP_COLOR_S               5
+#define COLOR_MARK_ENABLE              BIT(4)
+#define COLOR_REMAP_ENABLE             BIT(3)
+#define POLICE_DROP_SRP                        BIT(2)
+#define POLICE_COLOR_NOT_AWARE         BIT(1)
+#define POLICE_ENABLE                  BIT(0)
+
+#define REG_PORT_POLICE_COLOR_0__4     0x0810
+#define REG_PORT_POLICE_COLOR_1__4     0x0814
+#define REG_PORT_POLICE_COLOR_2__4     0x0818
+#define REG_PORT_POLICE_COLOR_3__4     0x081C
+
+#define POLICE_COLOR_MAP_S             2
+#define POLICE_COLOR_MAP_M             (BIT(POLICE_COLOR_MAP_S) - 1)
+
+#define REG_PORT_POLICE_RATE__4                0x0820
+
+#define POLICE_CIR_S                   16
+#define POLICE_PIR_S                   0
+
+#define REG_PORT_POLICE_BURST_SIZE__4  0x0824
+
+#define POLICE_BURST_SIZE_M            0x3FFF
+#define POLICE_CBS_S                   16
+#define POLICE_PBS_S                   0
+
+#define REG_PORT_WRED_PM_CTRL_0__4     0x0830
+
+#define WRED_PM_CTRL_M                 (BIT(11) - 1)
+
+#define WRED_PM_MAX_THRESHOLD_S                16
+#define WRED_PM_MIN_THRESHOLD_S                0
+
+#define REG_PORT_WRED_PM_CTRL_1__4     0x0834
+
+#define WRED_PM_MULTIPLIER_S           16
+#define WRED_PM_AVG_QUEUE_SIZE_S       0
+
+#define REG_PORT_WRED_QUEUE_CTRL_0__4  0x0840
+#define REG_PORT_WRED_QUEUE_CTRL_1__4  0x0844
+
+#define REG_PORT_WRED_QUEUE_PMON__4    0x0848
+
+#define WRED_RANDOM_DROP_ENABLE                BIT(31)
+#define WRED_PMON_FLUSH                        BIT(30)
+#define WRED_DROP_GYR_DISABLE          BIT(29)
+#define WRED_DROP_YR_DISABLE           BIT(28)
+#define WRED_DROP_R_DISABLE            BIT(27)
+#define WRED_DROP_ALL                  BIT(26)
+#define WRED_PMON_M                    (BIT(24) - 1)
+
+/* 9 - Shaping */
+
+#define REG_PORT_MTI_QUEUE_INDEX__4    0x0900
+
+#define REG_PORT_MTI_QUEUE_CTRL_0__4   0x0904
+
+#define MTI_PVID_REPLACE               BIT(0)
+
+#define REG_PORT_MTI_QUEUE_CTRL_0      0x0914
+
+#define MTI_SCHEDULE_MODE_M            0x3
+#define MTI_SCHEDULE_MODE_S            6
+#define MTI_SCHEDULE_STRICT_PRIO       0
+#define MTI_SCHEDULE_WRR               2
+#define MTI_SHAPING_M                  0x3
+#define MTI_SHAPING_S                  4
+#define MTI_SHAPING_OFF                        0
+#define MTI_SHAPING_SRP                        1
+#define MTI_SHAPING_TIME_AWARE         2
+
+#define REG_PORT_MTI_QUEUE_CTRL_1      0x0915
+
+#define MTI_TX_RATIO_M                 (BIT(7) - 1)
+
+#define REG_PORT_MTI_QUEUE_CTRL_2__2   0x0916
+#define REG_PORT_MTI_HI_WATER_MARK     0x0916
+#define REG_PORT_MTI_QUEUE_CTRL_3__2   0x0918
+#define REG_PORT_MTI_LO_WATER_MARK     0x0918
+#define REG_PORT_MTI_QUEUE_CTRL_4__2   0x091A
+#define REG_PORT_MTI_CREDIT_INCREMENT  0x091A
+
+/* A - QM */
+
+#define REG_PORT_QM_CTRL__4            0x0A00
+
+#define PORT_QM_DROP_PRIO_M            0x3
+
+#define REG_PORT_VLAN_MEMBERSHIP__4    0x0A04
+
+#define REG_PORT_QM_QUEUE_INDEX__4     0x0A08
+
+#define PORT_QM_QUEUE_INDEX_S          24
+#define PORT_QM_BURST_SIZE_S           16
+#define PORT_QM_MIN_RESV_SPACE_M       (BIT(11) - 1)
+
+#define REG_PORT_QM_WATER_MARK__4      0x0A0C
+
+#define PORT_QM_HI_WATER_MARK_S                16
+#define PORT_QM_LO_WATER_MARK_S                0
+#define PORT_QM_WATER_MARK_M           (BIT(11) - 1)
+
+#define REG_PORT_QM_TX_CNT_0__4                0x0A10
+
+#define PORT_QM_TX_CNT_USED_S          0
+#define PORT_QM_TX_CNT_M               (BIT(11) - 1)
+
+#define REG_PORT_QM_TX_CNT_1__4                0x0A14
+
+#define PORT_QM_TX_CNT_CALCULATED_S    16
+#define PORT_QM_TX_CNT_AVAIL_S         0
+
+/* B - LUE */
+#define REG_PORT_LUE_CTRL              0x0B00
+
+#define PORT_VLAN_LOOKUP_VID_0         BIT(7)
+#define PORT_INGRESS_FILTER            BIT(6)
+#define PORT_DISCARD_NON_VID           BIT(5)
+#define PORT_MAC_BASED_802_1X          BIT(4)
+#define PORT_SRC_ADDR_FILTER           BIT(3)
+
+#define REG_PORT_LUE_MSTP_INDEX                0x0B01
+
+#define REG_PORT_LUE_MSTP_STATE                0x0B04
+
+#define PORT_TX_ENABLE                 BIT(2)
+#define PORT_RX_ENABLE                 BIT(1)
+#define PORT_LEARN_DISABLE             BIT(0)
+
+/* C - PTP */
+
+#define REG_PTP_PORT_RX_DELAY__2       0x0C00
+#define REG_PTP_PORT_TX_DELAY__2       0x0C02
+#define REG_PTP_PORT_ASYM_DELAY__2     0x0C04
+
+#define REG_PTP_PORT_XDELAY_TS         0x0C08
+#define REG_PTP_PORT_XDELAY_TS_H       0x0C08
+#define REG_PTP_PORT_XDELAY_TS_L       0x0C0A
+
+#define REG_PTP_PORT_SYNC_TS           0x0C0C
+#define REG_PTP_PORT_SYNC_TS_H         0x0C0C
+#define REG_PTP_PORT_SYNC_TS_L         0x0C0E
+
+#define REG_PTP_PORT_PDRESP_TS         0x0C10
+#define REG_PTP_PORT_PDRESP_TS_H       0x0C10
+#define REG_PTP_PORT_PDRESP_TS_L       0x0C12
+
+#define REG_PTP_PORT_TX_INT_STATUS__2  0x0C14
+#define REG_PTP_PORT_TX_INT_ENABLE__2  0x0C16
+
+#define PTP_PORT_SYNC_INT              BIT(15)
+#define PTP_PORT_XDELAY_REQ_INT                BIT(14)
+#define PTP_PORT_PDELAY_RESP_INT       BIT(13)
+
+#define REG_PTP_PORT_LINK_DELAY__4     0x0C18
+
+#define PRIO_QUEUES                    4
+#define RX_PRIO_QUEUES                 8
+
+#define KS_PRIO_IN_REG                 2
+
+#define TOTAL_PORT_NUM                 7
+
+#define KSZ9477_COUNTER_NUM            0x20
+#define TOTAL_KSZ9477_COUNTER_NUM      (KSZ9477_COUNTER_NUM + 2 + 2)
+
+#define SWITCH_COUNTER_NUM             KSZ9477_COUNTER_NUM
+#define TOTAL_SWITCH_COUNTER_NUM       TOTAL_KSZ9477_COUNTER_NUM
+
+#define P_BCAST_STORM_CTRL             REG_PORT_MAC_CTRL_0
+#define P_PRIO_CTRL                    REG_PORT_MRI_PRIO_CTRL
+#define P_MIRROR_CTRL                  REG_PORT_MRI_MIRROR_CTRL
+#define P_STP_CTRL                     REG_PORT_LUE_MSTP_STATE
+#define P_PHY_CTRL                     REG_PORT_PHY_CTRL
+#define P_NEG_RESTART_CTRL             REG_PORT_PHY_CTRL
+#define P_LINK_STATUS                  REG_PORT_PHY_STATUS
+#define P_SPEED_STATUS                 REG_PORT_PHY_PHY_CTRL
+#define P_RATE_LIMIT_CTRL              REG_PORT_MAC_IN_RATE_LIMIT
+
+#define S_LINK_AGING_CTRL              REG_SW_LUE_CTRL_1
+#define S_MIRROR_CTRL                  REG_SW_MRI_CTRL_0
+#define S_REPLACE_VID_CTRL             REG_SW_MAC_CTRL_2
+#define S_802_1P_PRIO_CTRL             REG_SW_MAC_802_1P_MAP_0
+#define S_TOS_PRIO_CTRL                        REG_SW_MAC_TOS_PRIO_0
+#define S_FLUSH_TABLE_CTRL             REG_SW_LUE_CTRL_1
+
+#define SW_FLUSH_DYN_MAC_TABLE         SW_FLUSH_MSTP_TABLE
+
+#define MAX_TIMESTAMP_UNIT             2
+#define MAX_TRIG_UNIT                  3
+#define MAX_TIMESTAMP_EVENT_UNIT       8
+#define MAX_GPIO                       4
+
+#define PTP_TRIG_UNIT_M                        (BIT(MAX_TRIG_UNIT) - 1)
+#define PTP_TS_UNIT_M                  (BIT(MAX_TIMESTAMP_UNIT) - 1)
+
+/* Driver set switch broadcast storm protection at 10% rate. */
+#define BROADCAST_STORM_PROT_RATE      10
+
+/* 148,800 frames * 67 ms / 100 */
+#define BROADCAST_STORM_VALUE          9969
+
+#endif /* KSZ9477_REGS_H */
diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c
new file mode 100644 (file)
index 0000000..d757ba1
--- /dev/null
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip KSZ9477 series register access through SPI
+ *
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "ksz_priv.h"
+#include "ksz_spi.h"
+
+/* SPI frame opcodes */
+#define KS_SPIOP_RD                    3
+#define KS_SPIOP_WR                    2
+
+#define SPI_ADDR_SHIFT                 24
+#define SPI_ADDR_MASK                  (BIT(SPI_ADDR_SHIFT) - 1)
+#define SPI_TURNAROUND_SHIFT           5
+
+/* Enough to read all switch port registers. */
+#define SPI_TX_BUF_LEN                 0x100
+
+static int ksz9477_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val,
+                               unsigned int len)
+{
+       u32 txbuf;
+       int ret;
+
+       txbuf = reg & SPI_ADDR_MASK;
+       txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT;
+       txbuf <<= SPI_TURNAROUND_SHIFT;
+       txbuf = cpu_to_be32(txbuf);
+
+       ret = spi_write_then_read(spi, &txbuf, 4, val, len);
+       return ret;
+}
+
+static int ksz9477_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val,
+                                unsigned int len)
+{
+       u32 *txbuf = (u32 *)val;
+
+       *txbuf = reg & SPI_ADDR_MASK;
+       *txbuf |= (KS_SPIOP_WR << SPI_ADDR_SHIFT);
+       *txbuf <<= SPI_TURNAROUND_SHIFT;
+       *txbuf = cpu_to_be32(*txbuf);
+
+       return spi_write(spi, txbuf, 4 + len);
+}
+
+static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
+                       unsigned int len)
+{
+       struct spi_device *spi = dev->priv;
+
+       return ksz9477_spi_read_reg(spi, reg, data, len);
+}
+
+static int ksz_spi_write(struct ksz_device *dev, u32 reg, void *data,
+                        unsigned int len)
+{
+       struct spi_device *spi = dev->priv;
+
+       if (len > SPI_TX_BUF_LEN)
+               len = SPI_TX_BUF_LEN;
+       memcpy(&dev->txbuf[4], data, len);
+       return ksz9477_spi_write_reg(spi, reg, dev->txbuf, len);
+}
+
+static int ksz_spi_read24(struct ksz_device *dev, u32 reg, u32 *val)
+{
+       int ret;
+
+       *val = 0;
+       ret = ksz_spi_read(dev, reg, (u8 *)val, 3);
+       if (!ret) {
+               *val = be32_to_cpu(*val);
+               /* convert to 24bit */
+               *val >>= 8;
+       }
+
+       return ret;
+}
+
+static int ksz_spi_write24(struct ksz_device *dev, u32 reg, u32 value)
+{
+       /* make it to big endian 24bit from MSB */
+       value <<= 8;
+       value = cpu_to_be32(value);
+       return ksz_spi_write(dev, reg, &value, 3);
+}
+
+static const struct ksz_io_ops ksz9477_spi_ops = {
+       .read8 = ksz_spi_read8,
+       .read16 = ksz_spi_read16,
+       .read24 = ksz_spi_read24,
+       .read32 = ksz_spi_read32,
+       .write8 = ksz_spi_write8,
+       .write16 = ksz_spi_write16,
+       .write24 = ksz_spi_write24,
+       .write32 = ksz_spi_write32,
+       .get = ksz_spi_get,
+       .set = ksz_spi_set,
+};
+
+static int ksz9477_spi_probe(struct spi_device *spi)
+{
+       struct ksz_device *dev;
+       int ret;
+
+       dev = ksz_switch_alloc(&spi->dev, &ksz9477_spi_ops, spi);
+       if (!dev)
+               return -ENOMEM;
+
+       if (spi->dev.platform_data)
+               dev->pdata = spi->dev.platform_data;
+
+       dev->txbuf = devm_kzalloc(dev->dev, 4 + SPI_TX_BUF_LEN, GFP_KERNEL);
+
+       ret = ksz9477_switch_register(dev);
+
+       /* Main DSA driver may not be started yet. */
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, dev);
+
+       return 0;
+}
+
+static int ksz9477_spi_remove(struct spi_device *spi)
+{
+       struct ksz_device *dev = spi_get_drvdata(spi);
+
+       if (dev)
+               ksz_switch_remove(dev);
+
+       return 0;
+}
+
+static void ksz9477_spi_shutdown(struct spi_device *spi)
+{
+       struct ksz_device *dev = spi_get_drvdata(spi);
+
+       if (dev && dev->dev_ops->shutdown)
+               dev->dev_ops->shutdown(dev);
+}
+
+static const struct of_device_id ksz9477_dt_ids[] = {
+       { .compatible = "microchip,ksz9477" },
+       { .compatible = "microchip,ksz9897" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ksz9477_dt_ids);
+
+static struct spi_driver ksz9477_spi_driver = {
+       .driver = {
+               .name   = "ksz9477-switch",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(ksz9477_dt_ids),
+       },
+       .probe  = ksz9477_spi_probe,
+       .remove = ksz9477_spi_remove,
+       .shutdown = ksz9477_spi_shutdown,
+};
+
+module_spi_driver(ksz9477_spi_driver);
+
+MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>");
+MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch SPI access Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/microchip/ksz_9477_reg.h b/drivers/net/dsa/microchip/ksz_9477_reg.h
deleted file mode 100644 (file)
index 6aa6752..0000000
+++ /dev/null
@@ -1,1676 +0,0 @@
-/*
- * Microchip KSZ9477 register definitions
- *
- * Copyright (C) 2017
- *
- * 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.
- */
-
-#ifndef __KSZ9477_REGS_H
-#define __KSZ9477_REGS_H
-
-#define KS_PRIO_M                      0x7
-#define KS_PRIO_S                      4
-
-/* 0 - Operation */
-#define REG_CHIP_ID0__1                        0x0000
-
-#define REG_CHIP_ID1__1                        0x0001
-
-#define FAMILY_ID                      0x95
-#define FAMILY_ID_94                   0x94
-#define FAMILY_ID_95                   0x95
-#define FAMILY_ID_85                   0x85
-#define FAMILY_ID_98                   0x98
-#define FAMILY_ID_88                   0x88
-
-#define REG_CHIP_ID2__1                        0x0002
-
-#define CHIP_ID_63                     0x63
-#define CHIP_ID_66                     0x66
-#define CHIP_ID_67                     0x67
-#define CHIP_ID_77                     0x77
-#define CHIP_ID_93                     0x93
-#define CHIP_ID_96                     0x96
-#define CHIP_ID_97                     0x97
-
-#define REG_CHIP_ID3__1                        0x0003
-
-#define SWITCH_REVISION_M              0x0F
-#define SWITCH_REVISION_S              4
-#define SWITCH_RESET                   0x01
-
-#define REG_SW_PME_CTRL                        0x0006
-
-#define PME_ENABLE                     BIT(1)
-#define PME_POLARITY                   BIT(0)
-
-#define REG_GLOBAL_OPTIONS             0x000F
-
-#define SW_GIGABIT_ABLE                        BIT(6)
-#define SW_REDUNDANCY_ABLE             BIT(5)
-#define SW_AVB_ABLE                    BIT(4)
-#define SW_9567_RL_5_2                 0xC
-#define SW_9477_SL_5_2                 0xD
-
-#define SW_9896_GL_5_1                 0xB
-#define SW_9896_RL_5_1                 0x8
-#define SW_9896_SL_5_1                 0x9
-
-#define SW_9895_GL_4_1                 0x7
-#define SW_9895_RL_4_1                 0x4
-#define SW_9895_SL_4_1                 0x5
-
-#define SW_9896_RL_4_2                 0x6
-
-#define SW_9893_RL_2_1                 0x0
-#define SW_9893_SL_2_1                 0x1
-#define SW_9893_GL_2_1                 0x3
-
-#define SW_QW_ABLE                     BIT(5)
-#define SW_9893_RN_2_1                 0xC
-
-#define REG_SW_INT_STATUS__4           0x0010
-#define REG_SW_INT_MASK__4             0x0014
-
-#define LUE_INT                                BIT(31)
-#define TRIG_TS_INT                    BIT(30)
-#define APB_TIMEOUT_INT                        BIT(29)
-
-#define SWITCH_INT_MASK                        (TRIG_TS_INT | APB_TIMEOUT_INT)
-
-#define REG_SW_PORT_INT_STATUS__4      0x0018
-#define REG_SW_PORT_INT_MASK__4                0x001C
-#define REG_SW_PHY_INT_STATUS          0x0020
-#define REG_SW_PHY_INT_ENABLE          0x0024
-
-/* 1 - Global */
-#define REG_SW_GLOBAL_SERIAL_CTRL_0    0x0100
-#define SW_SPARE_REG_2                 BIT(7)
-#define SW_SPARE_REG_1                 BIT(6)
-#define SW_SPARE_REG_0                 BIT(5)
-#define SW_BIG_ENDIAN                  BIT(4)
-#define SPI_AUTO_EDGE_DETECTION                BIT(1)
-#define SPI_CLOCK_OUT_RISING_EDGE      BIT(0)
-
-#define REG_SW_GLOBAL_OUTPUT_CTRL__1   0x0103
-#define SW_ENABLE_REFCLKO              BIT(1)
-#define SW_REFCLKO_IS_125MHZ           BIT(0)
-
-#define REG_SW_IBA__4                  0x0104
-
-#define SW_IBA_ENABLE                  BIT(31)
-#define SW_IBA_DA_MATCH                        BIT(30)
-#define SW_IBA_INIT                    BIT(29)
-#define SW_IBA_QID_M                   0xF
-#define SW_IBA_QID_S                   22
-#define SW_IBA_PORT_M                  0x2F
-#define SW_IBA_PORT_S                  16
-#define SW_IBA_FRAME_TPID_M            0xFFFF
-
-#define REG_SW_APB_TIMEOUT_ADDR__4     0x0108
-
-#define APB_TIMEOUT_ACKNOWLEDGE                BIT(31)
-
-#define REG_SW_IBA_SYNC__1             0x010C
-
-#define REG_SW_IO_STRENGTH__1          0x010D
-#define SW_DRIVE_STRENGTH_M            0x7
-#define SW_DRIVE_STRENGTH_2MA          0
-#define SW_DRIVE_STRENGTH_4MA          1
-#define SW_DRIVE_STRENGTH_8MA          2
-#define SW_DRIVE_STRENGTH_12MA         3
-#define SW_DRIVE_STRENGTH_16MA         4
-#define SW_DRIVE_STRENGTH_20MA         5
-#define SW_DRIVE_STRENGTH_24MA         6
-#define SW_DRIVE_STRENGTH_28MA         7
-#define SW_HI_SPEED_DRIVE_STRENGTH_S   4
-#define SW_LO_SPEED_DRIVE_STRENGTH_S   0
-
-#define REG_SW_IBA_STATUS__4           0x0110
-
-#define SW_IBA_REQ                     BIT(31)
-#define SW_IBA_RESP                    BIT(30)
-#define SW_IBA_DA_MISMATCH             BIT(14)
-#define SW_IBA_FMT_MISMATCH            BIT(13)
-#define SW_IBA_CODE_ERROR              BIT(12)
-#define SW_IBA_CMD_ERROR               BIT(11)
-#define SW_IBA_CMD_LOC_M               (BIT(6) - 1)
-
-#define REG_SW_IBA_STATES__4           0x0114
-
-#define SW_IBA_BUF_STATE_S             30
-#define SW_IBA_CMD_STATE_S             28
-#define SW_IBA_RESP_STATE_S            26
-#define SW_IBA_STATE_M                 0x3
-#define SW_IBA_PACKET_SIZE_M           0x7F
-#define SW_IBA_PACKET_SIZE_S           16
-#define SW_IBA_FMT_ID_M                        0xFFFF
-
-#define REG_SW_IBA_RESULT__4           0x0118
-
-#define SW_IBA_SIZE_S                  24
-
-#define SW_IBA_RETRY_CNT_M             (BIT(5) - 1)
-
-/* 2 - PHY */
-#define REG_SW_POWER_MANAGEMENT_CTRL   0x0201
-
-#define SW_PLL_POWER_DOWN              BIT(5)
-#define SW_POWER_DOWN_MODE             0x3
-#define SW_ENERGY_DETECTION            1
-#define SW_SOFT_POWER_DOWN             2
-#define SW_POWER_SAVING                        3
-
-/* 3 - Operation Control */
-#define REG_SW_OPERATION               0x0300
-
-#define SW_DOUBLE_TAG                  BIT(7)
-#define SW_RESET                       BIT(1)
-#define SW_START                       BIT(0)
-
-#define REG_SW_MAC_ADDR_0              0x0302
-#define REG_SW_MAC_ADDR_1              0x0303
-#define REG_SW_MAC_ADDR_2              0x0304
-#define REG_SW_MAC_ADDR_3              0x0305
-#define REG_SW_MAC_ADDR_4              0x0306
-#define REG_SW_MAC_ADDR_5              0x0307
-
-#define REG_SW_MTU__2                  0x0308
-
-#define REG_SW_ISP_TPID__2             0x030A
-
-#define REG_SW_HSR_TPID__2             0x030C
-
-#define REG_AVB_STRATEGY__2            0x030E
-
-#define SW_SHAPING_CREDIT_ACCT         BIT(1)
-#define SW_POLICING_CREDIT_ACCT                BIT(0)
-
-#define REG_SW_LUE_CTRL_0              0x0310
-
-#define SW_VLAN_ENABLE                 BIT(7)
-#define SW_DROP_INVALID_VID            BIT(6)
-#define SW_AGE_CNT_M                   0x7
-#define SW_AGE_CNT_S                   3
-#define SW_RESV_MCAST_ENABLE           BIT(2)
-#define SW_HASH_OPTION_M               0x03
-#define SW_HASH_OPTION_CRC             1
-#define SW_HASH_OPTION_XOR             2
-#define SW_HASH_OPTION_DIRECT          3
-
-#define REG_SW_LUE_CTRL_1              0x0311
-
-#define UNICAST_LEARN_DISABLE          BIT(7)
-#define SW_SRC_ADDR_FILTER             BIT(6)
-#define SW_FLUSH_STP_TABLE             BIT(5)
-#define SW_FLUSH_MSTP_TABLE            BIT(4)
-#define SW_FWD_MCAST_SRC_ADDR          BIT(3)
-#define SW_AGING_ENABLE                        BIT(2)
-#define SW_FAST_AGING                  BIT(1)
-#define SW_LINK_AUTO_AGING             BIT(0)
-
-#define REG_SW_LUE_CTRL_2              0x0312
-
-#define SW_TRAP_DOUBLE_TAG             BIT(6)
-#define SW_EGRESS_VLAN_FILTER_DYN      BIT(5)
-#define SW_EGRESS_VLAN_FILTER_STA      BIT(4)
-#define SW_FLUSH_OPTION_M              0x3
-#define SW_FLUSH_OPTION_S              2
-#define SW_FLUSH_OPTION_DYN_MAC                1
-#define SW_FLUSH_OPTION_STA_MAC                2
-#define SW_FLUSH_OPTION_BOTH           3
-#define SW_PRIO_M                      0x3
-#define SW_PRIO_DA                     0
-#define SW_PRIO_SA                     1
-#define SW_PRIO_HIGHEST_DA_SA          2
-#define SW_PRIO_LOWEST_DA_SA           3
-
-#define REG_SW_LUE_CTRL_3              0x0313
-
-#define REG_SW_LUE_INT_STATUS          0x0314
-#define REG_SW_LUE_INT_ENABLE          0x0315
-
-#define LEARN_FAIL_INT                 BIT(2)
-#define ALMOST_FULL_INT                        BIT(1)
-#define WRITE_FAIL_INT                 BIT(0)
-
-#define REG_SW_LUE_INDEX_0__2          0x0316
-
-#define ENTRY_INDEX_M                  0x0FFF
-
-#define REG_SW_LUE_INDEX_1__2          0x0318
-
-#define FAIL_INDEX_M                   0x03FF
-
-#define REG_SW_LUE_INDEX_2__2          0x031A
-
-#define REG_SW_LUE_UNK_UCAST_CTRL__4   0x0320
-
-#define SW_UNK_UCAST_ENABLE            BIT(31)
-
-#define REG_SW_LUE_UNK_MCAST_CTRL__4   0x0324
-
-#define SW_UNK_MCAST_ENABLE            BIT(31)
-
-#define REG_SW_LUE_UNK_VID_CTRL__4     0x0328
-
-#define SW_UNK_VID_ENABLE              BIT(31)
-
-#define REG_SW_MAC_CTRL_0              0x0330
-
-#define SW_NEW_BACKOFF                 BIT(7)
-#define SW_CHECK_LENGTH                        BIT(3)
-#define SW_PAUSE_UNH_MODE              BIT(1)
-#define SW_AGGR_BACKOFF                        BIT(0)
-
-#define REG_SW_MAC_CTRL_1              0x0331
-
-#define MULTICAST_STORM_DISABLE                BIT(6)
-#define SW_BACK_PRESSURE               BIT(5)
-#define FAIR_FLOW_CTRL                 BIT(4)
-#define NO_EXC_COLLISION_DROP          BIT(3)
-#define SW_JUMBO_PACKET                        BIT(2)
-#define SW_LEGAL_PACKET_DISABLE                BIT(1)
-#define SW_PASS_SHORT_FRAME            BIT(0)
-
-#define REG_SW_MAC_CTRL_2              0x0332
-
-#define SW_REPLACE_VID                 BIT(3)
-#define BROADCAST_STORM_RATE_HI                0x07
-
-#define REG_SW_MAC_CTRL_3              0x0333
-
-#define BROADCAST_STORM_RATE_LO                0xFF
-#define BROADCAST_STORM_RATE           0x07FF
-
-#define REG_SW_MAC_CTRL_4              0x0334
-
-#define SW_PASS_PAUSE                  BIT(3)
-
-#define REG_SW_MAC_CTRL_5              0x0335
-
-#define SW_OUT_RATE_LIMIT_QUEUE_BASED  BIT(3)
-
-#define REG_SW_MAC_CTRL_6              0x0336
-
-#define SW_MIB_COUNTER_FLUSH           BIT(7)
-#define SW_MIB_COUNTER_FREEZE          BIT(6)
-
-#define REG_SW_MAC_802_1P_MAP_0                0x0338
-#define REG_SW_MAC_802_1P_MAP_1                0x0339
-#define REG_SW_MAC_802_1P_MAP_2                0x033A
-#define REG_SW_MAC_802_1P_MAP_3                0x033B
-
-#define SW_802_1P_MAP_M                        KS_PRIO_M
-#define SW_802_1P_MAP_S                        KS_PRIO_S
-
-#define REG_SW_MAC_ISP_CTRL            0x033C
-
-#define REG_SW_MAC_TOS_CTRL            0x033E
-
-#define SW_TOS_DSCP_REMARK             BIT(1)
-#define SW_TOS_DSCP_REMAP              BIT(0)
-
-#define REG_SW_MAC_TOS_PRIO_0          0x0340
-#define REG_SW_MAC_TOS_PRIO_1          0x0341
-#define REG_SW_MAC_TOS_PRIO_2          0x0342
-#define REG_SW_MAC_TOS_PRIO_3          0x0343
-#define REG_SW_MAC_TOS_PRIO_4          0x0344
-#define REG_SW_MAC_TOS_PRIO_5          0x0345
-#define REG_SW_MAC_TOS_PRIO_6          0x0346
-#define REG_SW_MAC_TOS_PRIO_7          0x0347
-#define REG_SW_MAC_TOS_PRIO_8          0x0348
-#define REG_SW_MAC_TOS_PRIO_9          0x0349
-#define REG_SW_MAC_TOS_PRIO_10         0x034A
-#define REG_SW_MAC_TOS_PRIO_11         0x034B
-#define REG_SW_MAC_TOS_PRIO_12         0x034C
-#define REG_SW_MAC_TOS_PRIO_13         0x034D
-#define REG_SW_MAC_TOS_PRIO_14         0x034E
-#define REG_SW_MAC_TOS_PRIO_15         0x034F
-#define REG_SW_MAC_TOS_PRIO_16         0x0350
-#define REG_SW_MAC_TOS_PRIO_17         0x0351
-#define REG_SW_MAC_TOS_PRIO_18         0x0352
-#define REG_SW_MAC_TOS_PRIO_19         0x0353
-#define REG_SW_MAC_TOS_PRIO_20         0x0354
-#define REG_SW_MAC_TOS_PRIO_21         0x0355
-#define REG_SW_MAC_TOS_PRIO_22         0x0356
-#define REG_SW_MAC_TOS_PRIO_23         0x0357
-#define REG_SW_MAC_TOS_PRIO_24         0x0358
-#define REG_SW_MAC_TOS_PRIO_25         0x0359
-#define REG_SW_MAC_TOS_PRIO_26         0x035A
-#define REG_SW_MAC_TOS_PRIO_27         0x035B
-#define REG_SW_MAC_TOS_PRIO_28         0x035C
-#define REG_SW_MAC_TOS_PRIO_29         0x035D
-#define REG_SW_MAC_TOS_PRIO_30         0x035E
-#define REG_SW_MAC_TOS_PRIO_31         0x035F
-
-#define REG_SW_MRI_CTRL_0              0x0370
-
-#define SW_IGMP_SNOOP                  BIT(6)
-#define SW_IPV6_MLD_OPTION             BIT(3)
-#define SW_IPV6_MLD_SNOOP              BIT(2)
-#define SW_MIRROR_RX_TX                        BIT(0)
-
-#define REG_SW_CLASS_D_IP_CTRL__4      0x0374
-
-#define SW_CLASS_D_IP_ENABLE           BIT(31)
-
-#define REG_SW_MRI_CTRL_8              0x0378
-
-#define SW_NO_COLOR_S                  6
-#define SW_RED_COLOR_S                 4
-#define SW_YELLOW_COLOR_S              2
-#define SW_GREEN_COLOR_S               0
-#define SW_COLOR_M                     0x3
-
-#define REG_SW_QM_CTRL__4              0x0390
-
-#define PRIO_SCHEME_SELECT_M           KS_PRIO_M
-#define PRIO_SCHEME_SELECT_S           6
-#define PRIO_MAP_3_HI                  0
-#define PRIO_MAP_2_HI                  2
-#define PRIO_MAP_0_LO                  3
-#define UNICAST_VLAN_BOUNDARY          BIT(1)
-
-#define REG_SW_EEE_QM_CTRL__2          0x03C0
-
-#define REG_SW_EEE_TXQ_WAIT_TIME__2    0x03C2
-
-/* 4 - */
-#define REG_SW_VLAN_ENTRY__4           0x0400
-
-#define VLAN_VALID                     BIT(31)
-#define VLAN_FORWARD_OPTION            BIT(27)
-#define VLAN_PRIO_M                    KS_PRIO_M
-#define VLAN_PRIO_S                    24
-#define VLAN_MSTP_M                    0x7
-#define VLAN_MSTP_S                    12
-#define VLAN_FID_M                     0x7F
-
-#define REG_SW_VLAN_ENTRY_UNTAG__4     0x0404
-#define REG_SW_VLAN_ENTRY_PORTS__4     0x0408
-
-#define REG_SW_VLAN_ENTRY_INDEX__2     0x040C
-
-#define VLAN_INDEX_M                   0x0FFF
-
-#define REG_SW_VLAN_CTRL               0x040E
-
-#define VLAN_START                     BIT(7)
-#define VLAN_ACTION                    0x3
-#define VLAN_WRITE                     1
-#define VLAN_READ                      2
-#define VLAN_CLEAR                     3
-
-#define REG_SW_ALU_INDEX_0             0x0410
-
-#define ALU_FID_INDEX_S                        16
-#define ALU_MAC_ADDR_HI                        0xFFFF
-
-#define REG_SW_ALU_INDEX_1             0x0414
-
-#define ALU_DIRECT_INDEX_M             (BIT(12) - 1)
-
-#define REG_SW_ALU_CTRL__4             0x0418
-
-#define ALU_VALID_CNT_M                        (BIT(14) - 1)
-#define ALU_VALID_CNT_S                        16
-#define ALU_START                      BIT(7)
-#define ALU_VALID                      BIT(6)
-#define ALU_DIRECT                     BIT(2)
-#define ALU_ACTION                     0x3
-#define ALU_WRITE                      1
-#define ALU_READ                       2
-#define ALU_SEARCH                     3
-
-#define REG_SW_ALU_STAT_CTRL__4                0x041C
-
-#define ALU_STAT_INDEX_M               (BIT(4) - 1)
-#define ALU_STAT_INDEX_S               16
-#define ALU_RESV_MCAST_INDEX_M         (BIT(6) - 1)
-#define ALU_STAT_START                 BIT(7)
-#define ALU_RESV_MCAST_ADDR            BIT(1)
-#define ALU_STAT_READ                  BIT(0)
-
-#define REG_SW_ALU_VAL_A               0x0420
-
-#define ALU_V_STATIC_VALID             BIT(31)
-#define ALU_V_SRC_FILTER               BIT(30)
-#define ALU_V_DST_FILTER               BIT(29)
-#define ALU_V_PRIO_AGE_CNT_M           (BIT(3) - 1)
-#define ALU_V_PRIO_AGE_CNT_S           26
-#define ALU_V_MSTP_M                   0x7
-
-#define REG_SW_ALU_VAL_B               0x0424
-
-#define ALU_V_OVERRIDE                 BIT(31)
-#define ALU_V_USE_FID                  BIT(30)
-#define ALU_V_PORT_MAP                 (BIT(24) - 1)
-
-#define REG_SW_ALU_VAL_C               0x0428
-
-#define ALU_V_FID_M                    (BIT(16) - 1)
-#define ALU_V_FID_S                    16
-#define ALU_V_MAC_ADDR_HI              0xFFFF
-
-#define REG_SW_ALU_VAL_D               0x042C
-
-#define REG_HSR_ALU_INDEX_0            0x0440
-
-#define REG_HSR_ALU_INDEX_1            0x0444
-
-#define HSR_DST_MAC_INDEX_LO_S         16
-#define HSR_SRC_MAC_INDEX_HI           0xFFFF
-
-#define REG_HSR_ALU_INDEX_2            0x0448
-
-#define HSR_INDEX_MAX                  BIT(9)
-#define HSR_DIRECT_INDEX_M             (HSR_INDEX_MAX - 1)
-
-#define REG_HSR_ALU_INDEX_3            0x044C
-
-#define HSR_PATH_INDEX_M               (BIT(4) - 1)
-
-#define REG_HSR_ALU_CTRL__4            0x0450
-
-#define HSR_VALID_CNT_M                        (BIT(14) - 1)
-#define HSR_VALID_CNT_S                        16
-#define HSR_START                      BIT(7)
-#define HSR_VALID                      BIT(6)
-#define HSR_SEARCH_END                 BIT(5)
-#define HSR_DIRECT                     BIT(2)
-#define HSR_ACTION                     0x3
-#define HSR_WRITE                      1
-#define HSR_READ                       2
-#define HSR_SEARCH                     3
-
-#define REG_HSR_ALU_VAL_A              0x0454
-
-#define HSR_V_STATIC_VALID             BIT(31)
-#define HSR_V_AGE_CNT_M                        (BIT(3) - 1)
-#define HSR_V_AGE_CNT_S                        26
-#define HSR_V_PATH_ID_M                        (BIT(4) - 1)
-
-#define REG_HSR_ALU_VAL_B              0x0458
-
-#define REG_HSR_ALU_VAL_C              0x045C
-
-#define HSR_V_DST_MAC_ADDR_LO_S                16
-#define HSR_V_SRC_MAC_ADDR_HI          0xFFFF
-
-#define REG_HSR_ALU_VAL_D              0x0460
-
-#define REG_HSR_ALU_VAL_E              0x0464
-
-#define HSR_V_START_SEQ_1_S            16
-#define HSR_V_START_SEQ_2_S            0
-
-#define REG_HSR_ALU_VAL_F              0x0468
-
-#define HSR_V_EXP_SEQ_1_S              16
-#define HSR_V_EXP_SEQ_2_S              0
-
-#define REG_HSR_ALU_VAL_G              0x046C
-
-#define HSR_V_SEQ_CNT_1_S              16
-#define HSR_V_SEQ_CNT_2_S              0
-
-#define HSR_V_SEQ_M                    (BIT(16) - 1)
-
-/* 5 - PTP Clock */
-#define REG_PTP_CLK_CTRL               0x0500
-
-#define PTP_STEP_ADJ                   BIT(6)
-#define PTP_STEP_DIR                   BIT(5)
-#define PTP_READ_TIME                  BIT(4)
-#define PTP_LOAD_TIME                  BIT(3)
-#define PTP_CLK_ADJ_ENABLE             BIT(2)
-#define PTP_CLK_ENABLE                 BIT(1)
-#define PTP_CLK_RESET                  BIT(0)
-
-#define REG_PTP_RTC_SUB_NANOSEC__2     0x0502
-
-#define PTP_RTC_SUB_NANOSEC_M          0x0007
-
-#define REG_PTP_RTC_NANOSEC            0x0504
-#define REG_PTP_RTC_NANOSEC_H          0x0504
-#define REG_PTP_RTC_NANOSEC_L          0x0506
-
-#define REG_PTP_RTC_SEC                        0x0508
-#define REG_PTP_RTC_SEC_H              0x0508
-#define REG_PTP_RTC_SEC_L              0x050A
-
-#define REG_PTP_SUBNANOSEC_RATE                0x050C
-#define REG_PTP_SUBNANOSEC_RATE_H      0x050C
-
-#define PTP_RATE_DIR                   BIT(31)
-#define PTP_TMP_RATE_ENABLE            BIT(30)
-
-#define REG_PTP_SUBNANOSEC_RATE_L      0x050E
-
-#define REG_PTP_RATE_DURATION          0x0510
-#define REG_PTP_RATE_DURATION_H                0x0510
-#define REG_PTP_RATE_DURATION_L                0x0512
-
-#define REG_PTP_MSG_CONF1              0x0514
-
-#define PTP_802_1AS                    BIT(7)
-#define PTP_ENABLE                     BIT(6)
-#define PTP_ETH_ENABLE                 BIT(5)
-#define PTP_IPV4_UDP_ENABLE            BIT(4)
-#define PTP_IPV6_UDP_ENABLE            BIT(3)
-#define PTP_TC_P2P                     BIT(2)
-#define PTP_MASTER                     BIT(1)
-#define PTP_1STEP                      BIT(0)
-
-#define REG_PTP_MSG_CONF2              0x0516
-
-#define PTP_UNICAST_ENABLE             BIT(12)
-#define PTP_ALTERNATE_MASTER           BIT(11)
-#define PTP_ALL_HIGH_PRIO              BIT(10)
-#define PTP_SYNC_CHECK                 BIT(9)
-#define PTP_DELAY_CHECK                        BIT(8)
-#define PTP_PDELAY_CHECK               BIT(7)
-#define PTP_DROP_SYNC_DELAY_REQ                BIT(5)
-#define PTP_DOMAIN_CHECK               BIT(4)
-#define PTP_UDP_CHECKSUM               BIT(2)
-
-#define REG_PTP_DOMAIN_VERSION         0x0518
-#define PTP_VERSION_M                  0xFF00
-#define PTP_DOMAIN_M                   0x00FF
-
-#define REG_PTP_UNIT_INDEX__4          0x0520
-
-#define PTP_UNIT_M                     0xF
-
-#define PTP_GPIO_INDEX_S               16
-#define PTP_TSI_INDEX_S                        8
-#define PTP_TOU_INDEX_S                        0
-
-#define REG_PTP_TRIG_STATUS__4         0x0524
-
-#define TRIG_ERROR_S                   16
-#define TRIG_DONE_S                    0
-
-#define REG_PTP_INT_STATUS__4          0x0528
-
-#define TRIG_INT_S                     16
-#define TS_INT_S                       0
-
-#define TRIG_UNIT_M                    0x7
-#define TS_UNIT_M                      0x3
-
-#define REG_PTP_CTRL_STAT__4           0x052C
-
-#define GPIO_IN                                BIT(7)
-#define GPIO_OUT                       BIT(6)
-#define TS_INT_ENABLE                  BIT(5)
-#define TRIG_ACTIVE                    BIT(4)
-#define TRIG_ENABLE                    BIT(3)
-#define TRIG_RESET                     BIT(2)
-#define TS_ENABLE                      BIT(1)
-#define TS_RESET                       BIT(0)
-
-#define GPIO_CTRL_M                    (GPIO_IN | GPIO_OUT)
-
-#define TRIG_CTRL_M                    \
-       (TRIG_ACTIVE | TRIG_ENABLE | TRIG_RESET)
-
-#define TS_CTRL_M                      \
-       (TS_INT_ENABLE | TS_ENABLE | TS_RESET)
-
-#define REG_TRIG_TARGET_NANOSEC                0x0530
-#define REG_TRIG_TARGET_SEC            0x0534
-
-#define REG_TRIG_CTRL__4               0x0538
-
-#define TRIG_CASCADE_ENABLE            BIT(31)
-#define TRIG_CASCADE_TAIL              BIT(30)
-#define TRIG_CASCADE_UPS_M             0xF
-#define TRIG_CASCADE_UPS_S             26
-#define TRIG_NOW                       BIT(25)
-#define TRIG_NOTIFY                    BIT(24)
-#define TRIG_EDGE                      BIT(23)
-#define TRIG_PATTERN_S                 20
-#define TRIG_PATTERN_M                 0x7
-#define TRIG_NEG_EDGE                  0
-#define TRIG_POS_EDGE                  1
-#define TRIG_NEG_PULSE                 2
-#define TRIG_POS_PULSE                 3
-#define TRIG_NEG_PERIOD                        4
-#define TRIG_POS_PERIOD                        5
-#define TRIG_REG_OUTPUT                        6
-#define TRIG_GPO_S                     16
-#define TRIG_GPO_M                     0xF
-#define TRIG_CASCADE_ITERATE_CNT_M     0xFFFF
-
-#define REG_TRIG_CYCLE_WIDTH           0x053C
-
-#define REG_TRIG_CYCLE_CNT             0x0540
-
-#define TRIG_CYCLE_CNT_M               0xFFFF
-#define TRIG_CYCLE_CNT_S               16
-#define TRIG_BIT_PATTERN_M             0xFFFF
-
-#define REG_TRIG_ITERATE_TIME          0x0544
-
-#define REG_TRIG_PULSE_WIDTH__4                0x0548
-
-#define TRIG_PULSE_WIDTH_M             0x00FFFFFF
-
-#define REG_TS_CTRL_STAT__4            0x0550
-
-#define TS_EVENT_DETECT_M              0xF
-#define TS_EVENT_DETECT_S              17
-#define TS_EVENT_OVERFLOW              BIT(16)
-#define TS_GPI_M                       0xF
-#define TS_GPI_S                       8
-#define TS_DETECT_RISE                 BIT(7)
-#define TS_DETECT_FALL                 BIT(6)
-#define TS_DETECT_S                    6
-#define TS_CASCADE_TAIL                        BIT(5)
-#define TS_CASCADE_UPS_M               0xF
-#define TS_CASCADE_UPS_S               1
-#define TS_CASCADE_ENABLE              BIT(0)
-
-#define DETECT_RISE                    (TS_DETECT_RISE >> TS_DETECT_S)
-#define DETECT_FALL                    (TS_DETECT_FALL >> TS_DETECT_S)
-
-#define REG_TS_EVENT_0_NANOSEC         0x0554
-#define REG_TS_EVENT_0_SEC             0x0558
-#define REG_TS_EVENT_0_SUB_NANOSEC     0x055C
-
-#define REG_TS_EVENT_1_NANOSEC         0x0560
-#define REG_TS_EVENT_1_SEC             0x0564
-#define REG_TS_EVENT_1_SUB_NANOSEC     0x0568
-
-#define REG_TS_EVENT_2_NANOSEC         0x056C
-#define REG_TS_EVENT_2_SEC             0x0570
-#define REG_TS_EVENT_2_SUB_NANOSEC     0x0574
-
-#define REG_TS_EVENT_3_NANOSEC         0x0578
-#define REG_TS_EVENT_3_SEC             0x057C
-#define REG_TS_EVENT_3_SUB_NANOSEC     0x0580
-
-#define REG_TS_EVENT_4_NANOSEC         0x0584
-#define REG_TS_EVENT_4_SEC             0x0588
-#define REG_TS_EVENT_4_SUB_NANOSEC     0x058C
-
-#define REG_TS_EVENT_5_NANOSEC         0x0590
-#define REG_TS_EVENT_5_SEC             0x0594
-#define REG_TS_EVENT_5_SUB_NANOSEC     0x0598
-
-#define REG_TS_EVENT_6_NANOSEC         0x059C
-#define REG_TS_EVENT_6_SEC             0x05A0
-#define REG_TS_EVENT_6_SUB_NANOSEC     0x05A4
-
-#define REG_TS_EVENT_7_NANOSEC         0x05A8
-#define REG_TS_EVENT_7_SEC             0x05AC
-#define REG_TS_EVENT_7_SUB_NANOSEC     0x05B0
-
-#define TS_EVENT_EDGE_M                        0x1
-#define TS_EVENT_EDGE_S                        30
-#define TS_EVENT_NANOSEC_M             (BIT(30) - 1)
-
-#define TS_EVENT_SUB_NANOSEC_M         0x7
-
-#define TS_EVENT_SAMPLE                        \
-       (REG_TS_EVENT_1_NANOSEC - REG_TS_EVENT_0_NANOSEC)
-
-#define PORT_CTRL_ADDR(port, addr)     ((addr) | (((port) + 1) << 12))
-
-#define REG_GLOBAL_RR_INDEX__1         0x0600
-
-/* DLR */
-#define REG_DLR_SRC_PORT__4            0x0604
-
-#define DLR_SRC_PORT_UNICAST           BIT(31)
-#define DLR_SRC_PORT_M                 0x3
-#define DLR_SRC_PORT_BOTH              0
-#define DLR_SRC_PORT_EACH              1
-
-#define REG_DLR_IP_ADDR__4             0x0608
-
-#define REG_DLR_CTRL__1                        0x0610
-
-#define DLR_RESET_SEQ_ID               BIT(3)
-#define DLR_BACKUP_AUTO_ON             BIT(2)
-#define DLR_BEACON_TX_ENABLE           BIT(1)
-#define DLR_ASSIST_ENABLE              BIT(0)
-
-#define REG_DLR_STATE__1               0x0611
-
-#define DLR_NODE_STATE_M               0x3
-#define DLR_NODE_STATE_S               1
-#define DLR_NODE_STATE_IDLE            0
-#define DLR_NODE_STATE_FAULT           1
-#define DLR_NODE_STATE_NORMAL          2
-#define DLR_RING_STATE_FAULT           0
-#define DLR_RING_STATE_NORMAL          1
-
-#define REG_DLR_PRECEDENCE__1          0x0612
-
-#define REG_DLR_BEACON_INTERVAL__4     0x0614
-
-#define REG_DLR_BEACON_TIMEOUT__4      0x0618
-
-#define REG_DLR_TIMEOUT_WINDOW__4      0x061C
-
-#define DLR_TIMEOUT_WINDOW_M           (BIT(22) - 1)
-
-#define REG_DLR_VLAN_ID__2             0x0620
-
-#define DLR_VLAN_ID_M                  (BIT(12) - 1)
-
-#define REG_DLR_DEST_ADDR_0            0x0622
-#define REG_DLR_DEST_ADDR_1            0x0623
-#define REG_DLR_DEST_ADDR_2            0x0624
-#define REG_DLR_DEST_ADDR_3            0x0625
-#define REG_DLR_DEST_ADDR_4            0x0626
-#define REG_DLR_DEST_ADDR_5            0x0627
-
-#define REG_DLR_PORT_MAP__4            0x0628
-
-#define REG_DLR_CLASS__1               0x062C
-
-#define DLR_FRAME_QID_M                        0x3
-
-/* HSR */
-#define REG_HSR_PORT_MAP__4            0x0640
-
-#define REG_HSR_ALU_CTRL_0__1          0x0644
-
-#define HSR_DUPLICATE_DISCARD          BIT(7)
-#define HSR_NODE_UNICAST               BIT(6)
-#define HSR_AGE_CNT_DEFAULT_M          0x7
-#define HSR_AGE_CNT_DEFAULT_S          3
-#define HSR_LEARN_MCAST_DISABLE                BIT(2)
-#define HSR_HASH_OPTION_M              0x3
-#define HSR_HASH_DISABLE               0
-#define HSR_HASH_UPPER_BITS            1
-#define HSR_HASH_LOWER_BITS            2
-#define HSR_HASH_XOR_BOTH_BITS         3
-
-#define REG_HSR_ALU_CTRL_1__1          0x0645
-
-#define HSR_LEARN_UCAST_DISABLE                BIT(7)
-#define HSR_FLUSH_TABLE                        BIT(5)
-#define HSR_PROC_MCAST_SRC             BIT(3)
-#define HSR_AGING_ENABLE               BIT(2)
-
-#define REG_HSR_ALU_CTRL_2__2          0x0646
-
-#define REG_HSR_ALU_AGE_PERIOD__4      0x0648
-
-#define REG_HSR_ALU_INT_STATUS__1      0x064C
-#define REG_HSR_ALU_INT_MASK__1                0x064D
-
-#define HSR_WINDOW_OVERFLOW_INT                BIT(3)
-#define HSR_LEARN_FAIL_INT             BIT(2)
-#define HSR_ALMOST_FULL_INT            BIT(1)
-#define HSR_WRITE_FAIL_INT             BIT(0)
-
-#define REG_HSR_ALU_ENTRY_0__2         0x0650
-
-#define HSR_ENTRY_INDEX_M              (BIT(10) - 1)
-#define HSR_FAIL_INDEX_M               (BIT(8) - 1)
-
-#define REG_HSR_ALU_ENTRY_1__2         0x0652
-
-#define HSR_FAIL_LEARN_INDEX_M         (BIT(8) - 1)
-
-#define REG_HSR_ALU_ENTRY_3__2         0x0654
-
-#define HSR_CPU_ACCESS_ENTRY_INDEX_M   (BIT(8) - 1)
-
-/* 0 - Operation */
-#define REG_PORT_DEFAULT_VID           0x0000
-
-#define REG_PORT_CUSTOM_VID            0x0002
-#define REG_PORT_AVB_SR_1_VID          0x0004
-#define REG_PORT_AVB_SR_2_VID          0x0006
-
-#define REG_PORT_AVB_SR_1_TYPE         0x0008
-#define REG_PORT_AVB_SR_2_TYPE         0x000A
-
-#define REG_PORT_PME_STATUS            0x0013
-#define REG_PORT_PME_CTRL              0x0017
-
-#define PME_WOL_MAGICPKT               BIT(2)
-#define PME_WOL_LINKUP                 BIT(1)
-#define PME_WOL_ENERGY                 BIT(0)
-
-#define REG_PORT_INT_STATUS            0x001B
-#define REG_PORT_INT_MASK              0x001F
-
-#define PORT_SGMII_INT                 BIT(3)
-#define PORT_PTP_INT                   BIT(2)
-#define PORT_PHY_INT                   BIT(1)
-#define PORT_ACL_INT                   BIT(0)
-
-#define PORT_INT_MASK                  \
-       (PORT_SGMII_INT | PORT_PTP_INT | PORT_PHY_INT | PORT_ACL_INT)
-
-#define REG_PORT_CTRL_0                        0x0020
-
-#define PORT_MAC_LOOPBACK              BIT(7)
-#define PORT_FORCE_TX_FLOW_CTRL                BIT(4)
-#define PORT_FORCE_RX_FLOW_CTRL                BIT(3)
-#define PORT_TAIL_TAG_ENABLE           BIT(2)
-#define PORT_QUEUE_SPLIT_ENABLE                0x3
-
-#define REG_PORT_CTRL_1                        0x0021
-
-#define PORT_SRP_ENABLE                        0x3
-
-#define REG_PORT_STATUS_0              0x0030
-
-#define PORT_INTF_SPEED_M              0x3
-#define PORT_INTF_SPEED_S              3
-#define PORT_INTF_FULL_DUPLEX          BIT(2)
-#define PORT_TX_FLOW_CTRL              BIT(1)
-#define PORT_RX_FLOW_CTRL              BIT(0)
-
-#define REG_PORT_STATUS_1              0x0034
-
-/* 1 - PHY */
-#define REG_PORT_PHY_CTRL              0x0100
-
-#define PORT_PHY_RESET                 BIT(15)
-#define PORT_PHY_LOOPBACK              BIT(14)
-#define PORT_SPEED_100MBIT             BIT(13)
-#define PORT_AUTO_NEG_ENABLE           BIT(12)
-#define PORT_POWER_DOWN                        BIT(11)
-#define PORT_ISOLATE                   BIT(10)
-#define PORT_AUTO_NEG_RESTART          BIT(9)
-#define PORT_FULL_DUPLEX               BIT(8)
-#define PORT_COLLISION_TEST            BIT(7)
-#define PORT_SPEED_1000MBIT            BIT(6)
-
-#define REG_PORT_PHY_STATUS            0x0102
-
-#define PORT_100BT4_CAPABLE            BIT(15)
-#define PORT_100BTX_FD_CAPABLE         BIT(14)
-#define PORT_100BTX_CAPABLE            BIT(13)
-#define PORT_10BT_FD_CAPABLE           BIT(12)
-#define PORT_10BT_CAPABLE              BIT(11)
-#define PORT_EXTENDED_STATUS           BIT(8)
-#define PORT_MII_SUPPRESS_CAPABLE      BIT(6)
-#define PORT_AUTO_NEG_ACKNOWLEDGE      BIT(5)
-#define PORT_REMOTE_FAULT              BIT(4)
-#define PORT_AUTO_NEG_CAPABLE          BIT(3)
-#define PORT_LINK_STATUS               BIT(2)
-#define PORT_JABBER_DETECT             BIT(1)
-#define PORT_EXTENDED_CAPABILITY       BIT(0)
-
-#define REG_PORT_PHY_ID_HI             0x0104
-#define REG_PORT_PHY_ID_LO             0x0106
-
-#define KSZ9477_ID_HI                  0x0022
-#define KSZ9477_ID_LO                  0x1622
-
-#define REG_PORT_PHY_AUTO_NEGOTIATION  0x0108
-
-#define PORT_AUTO_NEG_NEXT_PAGE                BIT(15)
-#define PORT_AUTO_NEG_REMOTE_FAULT     BIT(13)
-#define PORT_AUTO_NEG_ASYM_PAUSE       BIT(11)
-#define PORT_AUTO_NEG_SYM_PAUSE                BIT(10)
-#define PORT_AUTO_NEG_100BT4           BIT(9)
-#define PORT_AUTO_NEG_100BTX_FD                BIT(8)
-#define PORT_AUTO_NEG_100BTX           BIT(7)
-#define PORT_AUTO_NEG_10BT_FD          BIT(6)
-#define PORT_AUTO_NEG_10BT             BIT(5)
-#define PORT_AUTO_NEG_SELECTOR         0x001F
-#define PORT_AUTO_NEG_802_3            0x0001
-
-#define PORT_AUTO_NEG_PAUSE            \
-       (PORT_AUTO_NEG_ASYM_PAUSE | PORT_AUTO_NEG_SYM_PAUSE)
-
-#define REG_PORT_PHY_REMOTE_CAPABILITY 0x010A
-
-#define PORT_REMOTE_NEXT_PAGE          BIT(15)
-#define PORT_REMOTE_ACKNOWLEDGE                BIT(14)
-#define PORT_REMOTE_REMOTE_FAULT       BIT(13)
-#define PORT_REMOTE_ASYM_PAUSE         BIT(11)
-#define PORT_REMOTE_SYM_PAUSE          BIT(10)
-#define PORT_REMOTE_100BTX_FD          BIT(8)
-#define PORT_REMOTE_100BTX             BIT(7)
-#define PORT_REMOTE_10BT_FD            BIT(6)
-#define PORT_REMOTE_10BT               BIT(5)
-
-#define REG_PORT_PHY_1000_CTRL         0x0112
-
-#define PORT_AUTO_NEG_MANUAL           BIT(12)
-#define PORT_AUTO_NEG_MASTER           BIT(11)
-#define PORT_AUTO_NEG_MASTER_PREFERRED BIT(10)
-#define PORT_AUTO_NEG_1000BT_FD                BIT(9)
-#define PORT_AUTO_NEG_1000BT           BIT(8)
-
-#define REG_PORT_PHY_1000_STATUS       0x0114
-
-#define PORT_MASTER_FAULT              BIT(15)
-#define PORT_LOCAL_MASTER              BIT(14)
-#define PORT_LOCAL_RX_OK               BIT(13)
-#define PORT_REMOTE_RX_OK              BIT(12)
-#define PORT_REMOTE_1000BT_FD          BIT(11)
-#define PORT_REMOTE_1000BT             BIT(10)
-#define PORT_REMOTE_IDLE_CNT_M         0x0F
-
-#define PORT_PHY_1000_STATIC_STATUS    \
-       (PORT_LOCAL_RX_OK |             \
-       PORT_REMOTE_RX_OK |             \
-       PORT_REMOTE_1000BT_FD |         \
-       PORT_REMOTE_1000BT)
-
-#define REG_PORT_PHY_MMD_SETUP         0x011A
-
-#define PORT_MMD_OP_MODE_M             0x3
-#define PORT_MMD_OP_MODE_S             14
-#define PORT_MMD_OP_INDEX              0
-#define PORT_MMD_OP_DATA_NO_INCR       1
-#define PORT_MMD_OP_DATA_INCR_RW       2
-#define PORT_MMD_OP_DATA_INCR_W                3
-#define PORT_MMD_DEVICE_ID_M           0x1F
-
-#define MMD_SETUP(mode, dev)           \
-       (((u16)(mode) << PORT_MMD_OP_MODE_S) | (dev))
-
-#define REG_PORT_PHY_MMD_INDEX_DATA    0x011C
-
-#define MMD_DEVICE_ID_DSP              1
-
-#define MMD_DSP_SQI_CHAN_A             0xAC
-#define MMD_DSP_SQI_CHAN_B             0xAD
-#define MMD_DSP_SQI_CHAN_C             0xAE
-#define MMD_DSP_SQI_CHAN_D             0xAF
-
-#define DSP_SQI_ERR_DETECTED           BIT(15)
-#define DSP_SQI_AVG_ERR                        0x7FFF
-
-#define MMD_DEVICE_ID_COMMON           2
-
-#define MMD_DEVICE_ID_EEE_ADV          7
-
-#define MMD_EEE_ADV                    0x3C
-#define EEE_ADV_100MBIT                        BIT(1)
-#define EEE_ADV_1GBIT                  BIT(2)
-
-#define MMD_EEE_LP_ADV                 0x3D
-#define MMD_EEE_MSG_CODE               0x3F
-
-#define MMD_DEVICE_ID_AFED             0x1C
-
-#define REG_PORT_PHY_EXTENDED_STATUS   0x011E
-
-#define PORT_100BTX_FD_ABLE            BIT(15)
-#define PORT_100BTX_ABLE               BIT(14)
-#define PORT_10BT_FD_ABLE              BIT(13)
-#define PORT_10BT_ABLE                 BIT(12)
-
-#define REG_PORT_SGMII_ADDR__4         0x0200
-#define PORT_SGMII_AUTO_INCR           BIT(23)
-#define PORT_SGMII_DEVICE_ID_M         0x1F
-#define PORT_SGMII_DEVICE_ID_S         16
-#define PORT_SGMII_ADDR_M              (BIT(21) - 1)
-
-#define REG_PORT_SGMII_DATA__4         0x0204
-#define PORT_SGMII_DATA_M              (BIT(16) - 1)
-
-#define MMD_DEVICE_ID_PMA              0x01
-#define MMD_DEVICE_ID_PCS              0x03
-#define MMD_DEVICE_ID_PHY_XS           0x04
-#define MMD_DEVICE_ID_DTE_XS           0x05
-#define MMD_DEVICE_ID_AN               0x07
-#define MMD_DEVICE_ID_VENDOR_CTRL      0x1E
-#define MMD_DEVICE_ID_VENDOR_MII       0x1F
-
-#define SR_MII                         MMD_DEVICE_ID_VENDOR_MII
-
-#define MMD_SR_MII_CTRL                        0x0000
-
-#define SR_MII_RESET                   BIT(15)
-#define SR_MII_LOOPBACK                        BIT(14)
-#define SR_MII_SPEED_100MBIT           BIT(13)
-#define SR_MII_AUTO_NEG_ENABLE         BIT(12)
-#define SR_MII_POWER_DOWN              BIT(11)
-#define SR_MII_AUTO_NEG_RESTART                BIT(9)
-#define SR_MII_FULL_DUPLEX             BIT(8)
-#define SR_MII_SPEED_1000MBIT          BIT(6)
-
-#define MMD_SR_MII_STATUS              0x0001
-#define MMD_SR_MII_ID_1                        0x0002
-#define MMD_SR_MII_ID_2                        0x0003
-#define MMD_SR_MII_AUTO_NEGOTIATION    0x0004
-
-#define SR_MII_AUTO_NEG_NEXT_PAGE      BIT(15)
-#define SR_MII_AUTO_NEG_REMOTE_FAULT_M 0x3
-#define SR_MII_AUTO_NEG_REMOTE_FAULT_S 12
-#define SR_MII_AUTO_NEG_NO_ERROR       0
-#define SR_MII_AUTO_NEG_OFFLINE                1
-#define SR_MII_AUTO_NEG_LINK_FAILURE   2
-#define SR_MII_AUTO_NEG_ERROR          3
-#define SR_MII_AUTO_NEG_PAUSE_M                0x3
-#define SR_MII_AUTO_NEG_PAUSE_S                7
-#define SR_MII_AUTO_NEG_NO_PAUSE       0
-#define SR_MII_AUTO_NEG_ASYM_PAUSE_TX  1
-#define SR_MII_AUTO_NEG_SYM_PAUSE      2
-#define SR_MII_AUTO_NEG_ASYM_PAUSE_RX  3
-#define SR_MII_AUTO_NEG_HALF_DUPLEX    BIT(6)
-#define SR_MII_AUTO_NEG_FULL_DUPLEX    BIT(5)
-
-#define MMD_SR_MII_REMOTE_CAPABILITY   0x0005
-#define MMD_SR_MII_AUTO_NEG_EXP                0x0006
-#define MMD_SR_MII_AUTO_NEG_EXT                0x000F
-
-#define MMD_SR_MII_DIGITAL_CTRL_1      0x8000
-
-#define MMD_SR_MII_AUTO_NEG_CTRL       0x8001
-
-#define SR_MII_8_BIT                   BIT(8)
-#define SR_MII_SGMII_LINK_UP           BIT(4)
-#define SR_MII_TX_CFG_PHY_MASTER       BIT(3)
-#define SR_MII_PCS_MODE_M              0x3
-#define SR_MII_PCS_MODE_S              1
-#define SR_MII_PCS_SGMII               2
-#define SR_MII_AUTO_NEG_COMPLETE_INTR  BIT(0)
-
-#define MMD_SR_MII_AUTO_NEG_STATUS     0x8002
-
-#define SR_MII_STAT_LINK_UP            BIT(4)
-#define SR_MII_STAT_M                  0x3
-#define SR_MII_STAT_S                  2
-#define SR_MII_STAT_10_MBPS            0
-#define SR_MII_STAT_100_MBPS           1
-#define SR_MII_STAT_1000_MBPS          2
-#define SR_MII_STAT_FULL_DUPLEX                BIT(1)
-
-#define MMD_SR_MII_PHY_CTRL            0x80A0
-
-#define SR_MII_PHY_LANE_SEL_M          0xF
-#define SR_MII_PHY_LANE_SEL_S          8
-#define SR_MII_PHY_WRITE               BIT(1)
-#define SR_MII_PHY_START_BUSY          BIT(0)
-
-#define MMD_SR_MII_PHY_ADDR            0x80A1
-
-#define SR_MII_PHY_ADDR_M              (BIT(16) - 1)
-
-#define MMD_SR_MII_PHY_DATA            0x80A2
-
-#define SR_MII_PHY_DATA_M              (BIT(16) - 1)
-
-#define SR_MII_PHY_JTAG_CHIP_ID_HI     0x000C
-#define SR_MII_PHY_JTAG_CHIP_ID_LO     0x000D
-
-#define REG_PORT_PHY_REMOTE_LB_LED     0x0122
-
-#define PORT_REMOTE_LOOPBACK           BIT(8)
-#define PORT_LED_SELECT                        (3 << 6)
-#define PORT_LED_CTRL                  (3 << 4)
-#define PORT_LED_CTRL_TEST             BIT(3)
-#define PORT_10BT_PREAMBLE             BIT(2)
-#define PORT_LINK_MD_10BT_ENABLE       BIT(1)
-#define PORT_LINK_MD_PASS              BIT(0)
-
-#define REG_PORT_PHY_LINK_MD           0x0124
-
-#define PORT_START_CABLE_DIAG          BIT(15)
-#define PORT_TX_DISABLE                        BIT(14)
-#define PORT_CABLE_DIAG_PAIR_M         0x3
-#define PORT_CABLE_DIAG_PAIR_S         12
-#define PORT_CABLE_DIAG_SELECT_M       0x3
-#define PORT_CABLE_DIAG_SELECT_S       10
-#define PORT_CABLE_DIAG_RESULT_M       0x3
-#define PORT_CABLE_DIAG_RESULT_S       8
-#define PORT_CABLE_STAT_NORMAL         0
-#define PORT_CABLE_STAT_OPEN           1
-#define PORT_CABLE_STAT_SHORT          2
-#define PORT_CABLE_STAT_FAILED         3
-#define PORT_CABLE_FAULT_COUNTER       0x00FF
-
-#define REG_PORT_PHY_PMA_STATUS                0x0126
-
-#define PORT_1000_LINK_GOOD            BIT(1)
-#define PORT_100_LINK_GOOD             BIT(0)
-
-#define REG_PORT_PHY_DIGITAL_STATUS    0x0128
-
-#define PORT_LINK_DETECT               BIT(14)
-#define PORT_SIGNAL_DETECT             BIT(13)
-#define PORT_PHY_STAT_MDI              BIT(12)
-#define PORT_PHY_STAT_MASTER           BIT(11)
-
-#define REG_PORT_PHY_RXER_COUNTER      0x012A
-
-#define REG_PORT_PHY_INT_ENABLE                0x0136
-#define REG_PORT_PHY_INT_STATUS                0x0137
-
-#define JABBER_INT                     BIT(7)
-#define RX_ERR_INT                     BIT(6)
-#define PAGE_RX_INT                    BIT(5)
-#define PARALLEL_DETECT_FAULT_INT      BIT(4)
-#define LINK_PARTNER_ACK_INT           BIT(3)
-#define LINK_DOWN_INT                  BIT(2)
-#define REMOTE_FAULT_INT               BIT(1)
-#define LINK_UP_INT                    BIT(0)
-
-#define REG_PORT_PHY_DIGITAL_DEBUG_1   0x0138
-
-#define PORT_REG_CLK_SPEED_25_MHZ      BIT(14)
-#define PORT_PHY_FORCE_MDI             BIT(7)
-#define PORT_PHY_AUTO_MDIX_DISABLE     BIT(6)
-
-/* Same as PORT_PHY_LOOPBACK */
-#define PORT_PHY_PCS_LOOPBACK          BIT(0)
-
-#define REG_PORT_PHY_DIGITAL_DEBUG_2   0x013A
-
-#define REG_PORT_PHY_DIGITAL_DEBUG_3   0x013C
-
-#define PORT_100BT_FIXED_LATENCY       BIT(15)
-
-#define REG_PORT_PHY_PHY_CTRL          0x013E
-
-#define PORT_INT_PIN_HIGH              BIT(14)
-#define PORT_ENABLE_JABBER             BIT(9)
-#define PORT_STAT_SPEED_1000MBIT       BIT(6)
-#define PORT_STAT_SPEED_100MBIT                BIT(5)
-#define PORT_STAT_SPEED_10MBIT         BIT(4)
-#define PORT_STAT_FULL_DUPLEX          BIT(3)
-
-/* Same as PORT_PHY_STAT_MASTER */
-#define PORT_STAT_MASTER               BIT(2)
-#define PORT_RESET                     BIT(1)
-#define PORT_LINK_STATUS_FAIL          BIT(0)
-
-/* 3 - xMII */
-#define REG_PORT_XMII_CTRL_0           0x0300
-
-#define PORT_SGMII_SEL                 BIT(7)
-#define PORT_MII_FULL_DUPLEX           BIT(6)
-#define PORT_MII_100MBIT               BIT(4)
-#define PORT_GRXC_ENABLE               BIT(0)
-
-#define REG_PORT_XMII_CTRL_1           0x0301
-
-#define PORT_RMII_CLK_SEL              BIT(7)
-/* S1 */
-#define PORT_MII_1000MBIT_S1           BIT(6)
-/* S2 */
-#define PORT_MII_NOT_1GBIT             BIT(6)
-#define PORT_MII_SEL_EDGE              BIT(5)
-#define PORT_RGMII_ID_IG_ENABLE                BIT(4)
-#define PORT_RGMII_ID_EG_ENABLE                BIT(3)
-#define PORT_MII_MAC_MODE              BIT(2)
-#define PORT_MII_SEL_M                 0x3
-/* S1 */
-#define PORT_MII_SEL_S1                        0x0
-#define PORT_RMII_SEL_S1               0x1
-#define PORT_GMII_SEL_S1               0x2
-#define PORT_RGMII_SEL_S1              0x3
-/* S2 */
-#define PORT_RGMII_SEL                 0x0
-#define PORT_RMII_SEL                  0x1
-#define PORT_GMII_SEL                  0x2
-#define PORT_MII_SEL                   0x3
-
-/* 4 - MAC */
-#define REG_PORT_MAC_CTRL_0            0x0400
-
-#define PORT_BROADCAST_STORM           BIT(1)
-#define PORT_JUMBO_FRAME               BIT(0)
-
-#define REG_PORT_MAC_CTRL_1            0x0401
-
-#define PORT_BACK_PRESSURE             BIT(3)
-#define PORT_PASS_ALL                  BIT(0)
-
-#define REG_PORT_MAC_CTRL_2            0x0402
-
-#define PORT_100BT_EEE_DISABLE         BIT(7)
-#define PORT_1000BT_EEE_DISABLE                BIT(6)
-
-#define REG_PORT_MAC_IN_RATE_LIMIT     0x0403
-
-#define PORT_IN_PORT_BASED_S           6
-#define PORT_RATE_PACKET_BASED_S       5
-#define PORT_IN_FLOW_CTRL_S            4
-#define PORT_COUNT_IFG_S               1
-#define PORT_COUNT_PREAMBLE_S          0
-#define PORT_IN_PORT_BASED             BIT(6)
-#define PORT_IN_PACKET_BASED           BIT(5)
-#define PORT_IN_FLOW_CTRL              BIT(4)
-#define PORT_IN_LIMIT_MODE_M           0x3
-#define PORT_IN_LIMIT_MODE_S           2
-#define PORT_IN_ALL                    0
-#define PORT_IN_UNICAST                        1
-#define PORT_IN_MULTICAST              2
-#define PORT_IN_BROADCAST              3
-#define PORT_COUNT_IFG                 BIT(1)
-#define PORT_COUNT_PREAMBLE            BIT(0)
-
-#define REG_PORT_IN_RATE_0             0x0410
-#define REG_PORT_IN_RATE_1             0x0411
-#define REG_PORT_IN_RATE_2             0x0412
-#define REG_PORT_IN_RATE_3             0x0413
-#define REG_PORT_IN_RATE_4             0x0414
-#define REG_PORT_IN_RATE_5             0x0415
-#define REG_PORT_IN_RATE_6             0x0416
-#define REG_PORT_IN_RATE_7             0x0417
-
-#define REG_PORT_OUT_RATE_0            0x0420
-#define REG_PORT_OUT_RATE_1            0x0421
-#define REG_PORT_OUT_RATE_2            0x0422
-#define REG_PORT_OUT_RATE_3            0x0423
-
-#define PORT_RATE_LIMIT_M              (BIT(7) - 1)
-
-/* 5 - MIB Counters */
-#define REG_PORT_MIB_CTRL_STAT__4      0x0500
-
-#define MIB_COUNTER_OVERFLOW           BIT(31)
-#define MIB_COUNTER_VALID              BIT(30)
-#define MIB_COUNTER_READ               BIT(25)
-#define MIB_COUNTER_FLUSH_FREEZE       BIT(24)
-#define MIB_COUNTER_INDEX_M            (BIT(8) - 1)
-#define MIB_COUNTER_INDEX_S            16
-#define MIB_COUNTER_DATA_HI_M          0xF
-
-#define REG_PORT_MIB_DATA              0x0504
-
-/* 6 - ACL */
-#define REG_PORT_ACL_0                 0x0600
-
-#define ACL_FIRST_RULE_M               0xF
-
-#define REG_PORT_ACL_1                 0x0601
-
-#define ACL_MODE_M                     0x3
-#define ACL_MODE_S                     4
-#define ACL_MODE_DISABLE               0
-#define ACL_MODE_LAYER_2               1
-#define ACL_MODE_LAYER_3               2
-#define ACL_MODE_LAYER_4               3
-#define ACL_ENABLE_M                   0x3
-#define ACL_ENABLE_S                   2
-#define ACL_ENABLE_2_COUNT             0
-#define ACL_ENABLE_2_TYPE              1
-#define ACL_ENABLE_2_MAC               2
-#define ACL_ENABLE_2_BOTH              3
-#define ACL_ENABLE_3_IP                        1
-#define ACL_ENABLE_3_SRC_DST_COMP      2
-#define ACL_ENABLE_4_PROTOCOL          0
-#define ACL_ENABLE_4_TCP_PORT_COMP     1
-#define ACL_ENABLE_4_UDP_PORT_COMP     2
-#define ACL_ENABLE_4_TCP_SEQN_COMP     3
-#define ACL_SRC                                BIT(1)
-#define ACL_EQUAL                      BIT(0)
-
-#define REG_PORT_ACL_2                 0x0602
-#define REG_PORT_ACL_3                 0x0603
-
-#define ACL_MAX_PORT                   0xFFFF
-
-#define REG_PORT_ACL_4                 0x0604
-#define REG_PORT_ACL_5                 0x0605
-
-#define ACL_MIN_PORT                   0xFFFF
-#define ACL_IP_ADDR                    0xFFFFFFFF
-#define ACL_TCP_SEQNUM                 0xFFFFFFFF
-
-#define REG_PORT_ACL_6                 0x0606
-
-#define ACL_RESERVED                   0xF8
-#define ACL_PORT_MODE_M                        0x3
-#define ACL_PORT_MODE_S                        1
-#define ACL_PORT_MODE_DISABLE          0
-#define ACL_PORT_MODE_EITHER           1
-#define ACL_PORT_MODE_IN_RANGE         2
-#define ACL_PORT_MODE_OUT_OF_RANGE     3
-
-#define REG_PORT_ACL_7                 0x0607
-
-#define ACL_TCP_FLAG_ENABLE            BIT(0)
-
-#define REG_PORT_ACL_8                 0x0608
-
-#define ACL_TCP_FLAG_M                 0xFF
-
-#define REG_PORT_ACL_9                 0x0609
-
-#define ACL_TCP_FLAG                   0xFF
-#define ACL_ETH_TYPE                   0xFFFF
-#define ACL_IP_M                       0xFFFFFFFF
-
-#define REG_PORT_ACL_A                 0x060A
-
-#define ACL_PRIO_MODE_M                        0x3
-#define ACL_PRIO_MODE_S                        6
-#define ACL_PRIO_MODE_DISABLE          0
-#define ACL_PRIO_MODE_HIGHER           1
-#define ACL_PRIO_MODE_LOWER            2
-#define ACL_PRIO_MODE_REPLACE          3
-#define ACL_PRIO_M                     KS_PRIO_M
-#define ACL_PRIO_S                     3
-#define ACL_VLAN_PRIO_REPLACE          BIT(2)
-#define ACL_VLAN_PRIO_M                        KS_PRIO_M
-#define ACL_VLAN_PRIO_HI_M             0x3
-
-#define REG_PORT_ACL_B                 0x060B
-
-#define ACL_VLAN_PRIO_LO_M             0x8
-#define ACL_VLAN_PRIO_S                        7
-#define ACL_MAP_MODE_M                 0x3
-#define ACL_MAP_MODE_S                 5
-#define ACL_MAP_MODE_DISABLE           0
-#define ACL_MAP_MODE_OR                        1
-#define ACL_MAP_MODE_AND               2
-#define ACL_MAP_MODE_REPLACE           3
-
-#define ACL_CNT_M                      (BIT(11) - 1)
-#define ACL_CNT_S                      5
-
-#define REG_PORT_ACL_C                 0x060C
-
-#define REG_PORT_ACL_D                 0x060D
-#define ACL_MSEC_UNIT                  BIT(6)
-#define ACL_INTR_MODE                  BIT(5)
-#define ACL_PORT_MAP                   0x7F
-
-#define REG_PORT_ACL_E                 0x060E
-#define REG_PORT_ACL_F                 0x060F
-
-#define REG_PORT_ACL_BYTE_EN_MSB       0x0610
-#define REG_PORT_ACL_BYTE_EN_LSB       0x0611
-
-#define ACL_ACTION_START               0xA
-#define ACL_ACTION_LEN                 4
-#define ACL_INTR_CNT_START             0xD
-#define ACL_RULESET_START              0xE
-#define ACL_RULESET_LEN                        2
-#define ACL_TABLE_LEN                  16
-
-#define ACL_ACTION_ENABLE              0x003C
-#define ACL_MATCH_ENABLE               0x7FC3
-#define ACL_RULESET_ENABLE             0x8003
-#define ACL_BYTE_ENABLE                        0xFFFF
-
-#define REG_PORT_ACL_CTRL_0            0x0612
-
-#define PORT_ACL_WRITE_DONE            BIT(6)
-#define PORT_ACL_READ_DONE             BIT(5)
-#define PORT_ACL_WRITE                 BIT(4)
-#define PORT_ACL_INDEX_M               0xF
-
-#define REG_PORT_ACL_CTRL_1            0x0613
-
-/* 8 - Classification and Policing */
-#define REG_PORT_MRI_MIRROR_CTRL       0x0800
-
-#define PORT_MIRROR_RX                 BIT(6)
-#define PORT_MIRROR_TX                 BIT(5)
-#define PORT_MIRROR_SNIFFER            BIT(1)
-
-#define REG_PORT_MRI_PRIO_CTRL         0x0801
-
-#define PORT_HIGHEST_PRIO              BIT(7)
-#define PORT_OR_PRIO                   BIT(6)
-#define PORT_MAC_PRIO_ENABLE           BIT(4)
-#define PORT_VLAN_PRIO_ENABLE          BIT(3)
-#define PORT_802_1P_PRIO_ENABLE                BIT(2)
-#define PORT_DIFFSERV_PRIO_ENABLE      BIT(1)
-#define PORT_ACL_PRIO_ENABLE           BIT(0)
-
-#define REG_PORT_MRI_MAC_CTRL          0x0802
-
-#define PORT_USER_PRIO_CEILING         BIT(7)
-#define PORT_DROP_NON_VLAN             BIT(4)
-#define PORT_DROP_TAG                  BIT(3)
-#define PORT_BASED_PRIO_M              KS_PRIO_M
-#define PORT_BASED_PRIO_S              0
-
-#define REG_PORT_MRI_AUTHEN_CTRL       0x0803
-
-#define PORT_ACL_ENABLE                        BIT(2)
-#define PORT_AUTHEN_MODE               0x3
-#define PORT_AUTHEN_PASS               0
-#define PORT_AUTHEN_BLOCK              1
-#define PORT_AUTHEN_TRAP               2
-
-#define REG_PORT_MRI_INDEX__4          0x0804
-
-#define MRI_INDEX_P_M                  0x7
-#define MRI_INDEX_P_S                  16
-#define MRI_INDEX_Q_M                  0x3
-#define MRI_INDEX_Q_S                  0
-
-#define REG_PORT_MRI_TC_MAP__4         0x0808
-
-#define PORT_TC_MAP_M                  0xf
-#define PORT_TC_MAP_S                  4
-
-#define REG_PORT_MRI_POLICE_CTRL__4    0x080C
-
-#define POLICE_DROP_ALL                        BIT(10)
-#define POLICE_PACKET_TYPE_M           0x3
-#define POLICE_PACKET_TYPE_S           8
-#define POLICE_PACKET_DROPPED          0
-#define POLICE_PACKET_GREEN            1
-#define POLICE_PACKET_YELLOW           2
-#define POLICE_PACKET_RED              3
-#define PORT_BASED_POLICING            BIT(7)
-#define NON_DSCP_COLOR_M               0x3
-#define NON_DSCP_COLOR_S               5
-#define COLOR_MARK_ENABLE              BIT(4)
-#define COLOR_REMAP_ENABLE             BIT(3)
-#define POLICE_DROP_SRP                        BIT(2)
-#define POLICE_COLOR_NOT_AWARE         BIT(1)
-#define POLICE_ENABLE                  BIT(0)
-
-#define REG_PORT_POLICE_COLOR_0__4     0x0810
-#define REG_PORT_POLICE_COLOR_1__4     0x0814
-#define REG_PORT_POLICE_COLOR_2__4     0x0818
-#define REG_PORT_POLICE_COLOR_3__4     0x081C
-
-#define POLICE_COLOR_MAP_S             2
-#define POLICE_COLOR_MAP_M             (BIT(POLICE_COLOR_MAP_S) - 1)
-
-#define REG_PORT_POLICE_RATE__4                0x0820
-
-#define POLICE_CIR_S                   16
-#define POLICE_PIR_S                   0
-
-#define REG_PORT_POLICE_BURST_SIZE__4  0x0824
-
-#define POLICE_BURST_SIZE_M            0x3FFF
-#define POLICE_CBS_S                   16
-#define POLICE_PBS_S                   0
-
-#define REG_PORT_WRED_PM_CTRL_0__4     0x0830
-
-#define WRED_PM_CTRL_M                 (BIT(11) - 1)
-
-#define WRED_PM_MAX_THRESHOLD_S                16
-#define WRED_PM_MIN_THRESHOLD_S                0
-
-#define REG_PORT_WRED_PM_CTRL_1__4     0x0834
-
-#define WRED_PM_MULTIPLIER_S           16
-#define WRED_PM_AVG_QUEUE_SIZE_S       0
-
-#define REG_PORT_WRED_QUEUE_CTRL_0__4  0x0840
-#define REG_PORT_WRED_QUEUE_CTRL_1__4  0x0844
-
-#define REG_PORT_WRED_QUEUE_PMON__4    0x0848
-
-#define WRED_RANDOM_DROP_ENABLE                BIT(31)
-#define WRED_PMON_FLUSH                        BIT(30)
-#define WRED_DROP_GYR_DISABLE          BIT(29)
-#define WRED_DROP_YR_DISABLE           BIT(28)
-#define WRED_DROP_R_DISABLE            BIT(27)
-#define WRED_DROP_ALL                  BIT(26)
-#define WRED_PMON_M                    (BIT(24) - 1)
-
-/* 9 - Shaping */
-
-#define REG_PORT_MTI_QUEUE_INDEX__4    0x0900
-
-#define REG_PORT_MTI_QUEUE_CTRL_0__4   0x0904
-
-#define MTI_PVID_REPLACE               BIT(0)
-
-#define REG_PORT_MTI_QUEUE_CTRL_0      0x0914
-
-#define MTI_SCHEDULE_MODE_M            0x3
-#define MTI_SCHEDULE_MODE_S            6
-#define MTI_SCHEDULE_STRICT_PRIO       0
-#define MTI_SCHEDULE_WRR               2
-#define MTI_SHAPING_M                  0x3
-#define MTI_SHAPING_S                  4
-#define MTI_SHAPING_OFF                        0
-#define MTI_SHAPING_SRP                        1
-#define MTI_SHAPING_TIME_AWARE         2
-
-#define REG_PORT_MTI_QUEUE_CTRL_1      0x0915
-
-#define MTI_TX_RATIO_M                 (BIT(7) - 1)
-
-#define REG_PORT_MTI_QUEUE_CTRL_2__2   0x0916
-#define REG_PORT_MTI_HI_WATER_MARK     0x0916
-#define REG_PORT_MTI_QUEUE_CTRL_3__2   0x0918
-#define REG_PORT_MTI_LO_WATER_MARK     0x0918
-#define REG_PORT_MTI_QUEUE_CTRL_4__2   0x091A
-#define REG_PORT_MTI_CREDIT_INCREMENT  0x091A
-
-/* A - QM */
-
-#define REG_PORT_QM_CTRL__4            0x0A00
-
-#define PORT_QM_DROP_PRIO_M            0x3
-
-#define REG_PORT_VLAN_MEMBERSHIP__4    0x0A04
-
-#define REG_PORT_QM_QUEUE_INDEX__4     0x0A08
-
-#define PORT_QM_QUEUE_INDEX_S          24
-#define PORT_QM_BURST_SIZE_S           16
-#define PORT_QM_MIN_RESV_SPACE_M       (BIT(11) - 1)
-
-#define REG_PORT_QM_WATER_MARK__4      0x0A0C
-
-#define PORT_QM_HI_WATER_MARK_S                16
-#define PORT_QM_LO_WATER_MARK_S                0
-#define PORT_QM_WATER_MARK_M           (BIT(11) - 1)
-
-#define REG_PORT_QM_TX_CNT_0__4                0x0A10
-
-#define PORT_QM_TX_CNT_USED_S          0
-#define PORT_QM_TX_CNT_M               (BIT(11) - 1)
-
-#define REG_PORT_QM_TX_CNT_1__4                0x0A14
-
-#define PORT_QM_TX_CNT_CALCULATED_S    16
-#define PORT_QM_TX_CNT_AVAIL_S         0
-
-/* B - LUE */
-#define REG_PORT_LUE_CTRL              0x0B00
-
-#define PORT_VLAN_LOOKUP_VID_0         BIT(7)
-#define PORT_INGRESS_FILTER            BIT(6)
-#define PORT_DISCARD_NON_VID           BIT(5)
-#define PORT_MAC_BASED_802_1X          BIT(4)
-#define PORT_SRC_ADDR_FILTER           BIT(3)
-
-#define REG_PORT_LUE_MSTP_INDEX                0x0B01
-
-#define REG_PORT_LUE_MSTP_STATE                0x0B04
-
-#define PORT_TX_ENABLE                 BIT(2)
-#define PORT_RX_ENABLE                 BIT(1)
-#define PORT_LEARN_DISABLE             BIT(0)
-
-/* C - PTP */
-
-#define REG_PTP_PORT_RX_DELAY__2       0x0C00
-#define REG_PTP_PORT_TX_DELAY__2       0x0C02
-#define REG_PTP_PORT_ASYM_DELAY__2     0x0C04
-
-#define REG_PTP_PORT_XDELAY_TS         0x0C08
-#define REG_PTP_PORT_XDELAY_TS_H       0x0C08
-#define REG_PTP_PORT_XDELAY_TS_L       0x0C0A
-
-#define REG_PTP_PORT_SYNC_TS           0x0C0C
-#define REG_PTP_PORT_SYNC_TS_H         0x0C0C
-#define REG_PTP_PORT_SYNC_TS_L         0x0C0E
-
-#define REG_PTP_PORT_PDRESP_TS         0x0C10
-#define REG_PTP_PORT_PDRESP_TS_H       0x0C10
-#define REG_PTP_PORT_PDRESP_TS_L       0x0C12
-
-#define REG_PTP_PORT_TX_INT_STATUS__2  0x0C14
-#define REG_PTP_PORT_TX_INT_ENABLE__2  0x0C16
-
-#define PTP_PORT_SYNC_INT              BIT(15)
-#define PTP_PORT_XDELAY_REQ_INT                BIT(14)
-#define PTP_PORT_PDELAY_RESP_INT       BIT(13)
-
-#define REG_PTP_PORT_LINK_DELAY__4     0x0C18
-
-#define PRIO_QUEUES                    4
-#define RX_PRIO_QUEUES                 8
-
-#define KS_PRIO_IN_REG                 2
-
-#define TOTAL_PORT_NUM                 7
-
-#define KSZ9477_COUNTER_NUM            0x20
-#define TOTAL_KSZ9477_COUNTER_NUM      (KSZ9477_COUNTER_NUM + 2 + 2)
-
-#define SWITCH_COUNTER_NUM             KSZ9477_COUNTER_NUM
-#define TOTAL_SWITCH_COUNTER_NUM       TOTAL_KSZ9477_COUNTER_NUM
-
-#define P_BCAST_STORM_CTRL             REG_PORT_MAC_CTRL_0
-#define P_PRIO_CTRL                    REG_PORT_MRI_PRIO_CTRL
-#define P_MIRROR_CTRL                  REG_PORT_MRI_MIRROR_CTRL
-#define P_STP_CTRL                     REG_PORT_LUE_MSTP_STATE
-#define P_PHY_CTRL                     REG_PORT_PHY_CTRL
-#define P_NEG_RESTART_CTRL             REG_PORT_PHY_CTRL
-#define P_LINK_STATUS                  REG_PORT_PHY_STATUS
-#define P_SPEED_STATUS                 REG_PORT_PHY_PHY_CTRL
-#define P_RATE_LIMIT_CTRL              REG_PORT_MAC_IN_RATE_LIMIT
-
-#define S_LINK_AGING_CTRL              REG_SW_LUE_CTRL_1
-#define S_MIRROR_CTRL                  REG_SW_MRI_CTRL_0
-#define S_REPLACE_VID_CTRL             REG_SW_MAC_CTRL_2
-#define S_802_1P_PRIO_CTRL             REG_SW_MAC_802_1P_MAP_0
-#define S_TOS_PRIO_CTRL                        REG_SW_MAC_TOS_PRIO_0
-#define S_FLUSH_TABLE_CTRL             REG_SW_LUE_CTRL_1
-
-#define SW_FLUSH_DYN_MAC_TABLE         SW_FLUSH_MSTP_TABLE
-
-#define MAX_TIMESTAMP_UNIT             2
-#define MAX_TRIG_UNIT                  3
-#define MAX_TIMESTAMP_EVENT_UNIT       8
-#define MAX_GPIO                       4
-
-#define PTP_TRIG_UNIT_M                        (BIT(MAX_TRIG_UNIT) - 1)
-#define PTP_TS_UNIT_M                  (BIT(MAX_TIMESTAMP_UNIT) - 1)
-
-/* Driver set switch broadcast storm protection at 10% rate. */
-#define BROADCAST_STORM_PROT_RATE      10
-
-/* 148,800 frames * 67 ms / 100 */
-#define BROADCAST_STORM_VALUE          9969
-
-#endif /* KSZ9477_REGS_H */
index 86b6464..3b12e2d 100644 (file)
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Microchip switch driver main logic
  *
- * Copyright (C) 2017
- *
- * 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.
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
  */
 
 #include <linux/delay.h>
 #include <linux/export.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_data/microchip-ksz.h>
 #include <linux/phy.h>
 #include <linux/etherdevice.h>
 #include <linux/if_bridge.h>
+#include <linux/of_gpio.h>
+#include <linux/of_net.h>
 #include <net/dsa.h>
 #include <net/switchdev.h>
 
 #include "ksz_priv.h"
 
-static const struct {
-       int index;
-       char string[ETH_GSTRING_LEN];
-} mib_names[TOTAL_SWITCH_COUNTER_NUM] = {
-       { 0x00, "rx_hi" },
-       { 0x01, "rx_undersize" },
-       { 0x02, "rx_fragments" },
-       { 0x03, "rx_oversize" },
-       { 0x04, "rx_jabbers" },
-       { 0x05, "rx_symbol_err" },
-       { 0x06, "rx_crc_err" },
-       { 0x07, "rx_align_err" },
-       { 0x08, "rx_mac_ctrl" },
-       { 0x09, "rx_pause" },
-       { 0x0A, "rx_bcast" },
-       { 0x0B, "rx_mcast" },
-       { 0x0C, "rx_ucast" },
-       { 0x0D, "rx_64_or_less" },
-       { 0x0E, "rx_65_127" },
-       { 0x0F, "rx_128_255" },
-       { 0x10, "rx_256_511" },
-       { 0x11, "rx_512_1023" },
-       { 0x12, "rx_1024_1522" },
-       { 0x13, "rx_1523_2000" },
-       { 0x14, "rx_2001" },
-       { 0x15, "tx_hi" },
-       { 0x16, "tx_late_col" },
-       { 0x17, "tx_pause" },
-       { 0x18, "tx_bcast" },
-       { 0x19, "tx_mcast" },
-       { 0x1A, "tx_ucast" },
-       { 0x1B, "tx_deferred" },
-       { 0x1C, "tx_total_col" },
-       { 0x1D, "tx_exc_col" },
-       { 0x1E, "tx_single_col" },
-       { 0x1F, "tx_mult_col" },
-       { 0x80, "rx_total" },
-       { 0x81, "tx_total" },
-       { 0x82, "rx_discards" },
-       { 0x83, "tx_discards" },
-};
-
-static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
-{
-       u8 data;
-
-       ksz_read8(dev, addr, &data);
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-       ksz_write8(dev, addr, data);
-}
-
-static void ksz_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool set)
-{
-       u32 data;
-
-       ksz_read32(dev, addr, &data);
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-       ksz_write32(dev, addr, data);
-}
-
-static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
-                        bool set)
-{
-       u32 addr;
-       u8 data;
-
-       addr = PORT_CTRL_ADDR(port, offset);
-       ksz_read8(dev, addr, &data);
-
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-
-       ksz_write8(dev, addr, data);
-}
-
-static void ksz_port_cfg32(struct ksz_device *dev, int port, int offset,
-                          u32 bits, bool set)
-{
-       u32 addr;
-       u32 data;
-
-       addr = PORT_CTRL_ADDR(port, offset);
-       ksz_read32(dev, addr, &data);
-
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-
-       ksz_write32(dev, addr, data);
-}
-
-static int wait_vlan_ctrl_ready(struct ksz_device *dev, u32 waiton, int timeout)
-{
-       u8 data;
-
-       do {
-               ksz_read8(dev, REG_SW_VLAN_CTRL, &data);
-               if (!(data & waiton))
-                       break;
-               usleep_range(1, 10);
-       } while (timeout-- > 0);
-
-       if (timeout <= 0)
-               return -ETIMEDOUT;
-
-       return 0;
-}
-
-static int get_vlan_table(struct dsa_switch *ds, u16 vid, u32 *vlan_table)
-{
-       struct ksz_device *dev = ds->priv;
-       int ret;
-
-       mutex_lock(&dev->vlan_mutex);
-
-       ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
-       ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_READ | VLAN_START);
-
-       /* wait to be cleared */
-       ret = wait_vlan_ctrl_ready(dev, VLAN_START, 1000);
-       if (ret < 0) {
-               dev_dbg(dev->dev, "Failed to read vlan table\n");
-               goto exit;
-       }
-
-       ksz_read32(dev, REG_SW_VLAN_ENTRY__4, &vlan_table[0]);
-       ksz_read32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, &vlan_table[1]);
-       ksz_read32(dev, REG_SW_VLAN_ENTRY_PORTS__4, &vlan_table[2]);
-
-       ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
-
-exit:
-       mutex_unlock(&dev->vlan_mutex);
-
-       return ret;
-}
-
-static int set_vlan_table(struct dsa_switch *ds, u16 vid, u32 *vlan_table)
-{
-       struct ksz_device *dev = ds->priv;
-       int ret;
-
-       mutex_lock(&dev->vlan_mutex);
-
-       ksz_write32(dev, REG_SW_VLAN_ENTRY__4, vlan_table[0]);
-       ksz_write32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, vlan_table[1]);
-       ksz_write32(dev, REG_SW_VLAN_ENTRY_PORTS__4, vlan_table[2]);
-
-       ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
-       ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_START | VLAN_WRITE);
-
-       /* wait to be cleared */
-       ret = wait_vlan_ctrl_ready(dev, VLAN_START, 1000);
-       if (ret < 0) {
-               dev_dbg(dev->dev, "Failed to write vlan table\n");
-               goto exit;
-       }
-
-       ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
-
-       /* update vlan cache table */
-       dev->vlan_cache[vid].table[0] = vlan_table[0];
-       dev->vlan_cache[vid].table[1] = vlan_table[1];
-       dev->vlan_cache[vid].table[2] = vlan_table[2];
-
-exit:
-       mutex_unlock(&dev->vlan_mutex);
-
-       return ret;
-}
-
-static void read_table(struct dsa_switch *ds, u32 *table)
-{
-       struct ksz_device *dev = ds->priv;
-
-       ksz_read32(dev, REG_SW_ALU_VAL_A, &table[0]);
-       ksz_read32(dev, REG_SW_ALU_VAL_B, &table[1]);
-       ksz_read32(dev, REG_SW_ALU_VAL_C, &table[2]);
-       ksz_read32(dev, REG_SW_ALU_VAL_D, &table[3]);
-}
-
-static void write_table(struct dsa_switch *ds, u32 *table)
-{
-       struct ksz_device *dev = ds->priv;
-
-       ksz_write32(dev, REG_SW_ALU_VAL_A, table[0]);
-       ksz_write32(dev, REG_SW_ALU_VAL_B, table[1]);
-       ksz_write32(dev, REG_SW_ALU_VAL_C, table[2]);
-       ksz_write32(dev, REG_SW_ALU_VAL_D, table[3]);
-}
-
-static int wait_alu_ready(struct ksz_device *dev, u32 waiton, int timeout)
-{
-       u32 data;
-
-       do {
-               ksz_read32(dev, REG_SW_ALU_CTRL__4, &data);
-               if (!(data & waiton))
-                       break;
-               usleep_range(1, 10);
-       } while (timeout-- > 0);
-
-       if (timeout <= 0)
-               return -ETIMEDOUT;
-
-       return 0;
-}
-
-static int wait_alu_sta_ready(struct ksz_device *dev, u32 waiton, int timeout)
-{
-       u32 data;
-
-       do {
-               ksz_read32(dev, REG_SW_ALU_STAT_CTRL__4, &data);
-               if (!(data & waiton))
-                       break;
-               usleep_range(1, 10);
-       } while (timeout-- > 0);
-
-       if (timeout <= 0)
-               return -ETIMEDOUT;
-
-       return 0;
-}
-
-static int ksz_reset_switch(struct dsa_switch *ds)
+void ksz_update_port_member(struct ksz_device *dev, int port)
 {
-       struct ksz_device *dev = ds->priv;
-       u8 data8;
-       u16 data16;
-       u32 data32;
-
-       /* reset switch */
-       ksz_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
-
-       /* turn off SPI DO Edge select */
-       ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);
-       data8 &= ~SPI_AUTO_EDGE_DETECTION;
-       ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);
-
-       /* default configuration */
-       ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
-       data8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING |
-             SW_SRC_ADDR_FILTER | SW_FLUSH_STP_TABLE | SW_FLUSH_MSTP_TABLE;
-       ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
-
-       /* disable interrupts */
-       ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
-       ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0x7F);
-       ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
-
-       /* set broadcast storm protection 10% rate */
-       ksz_read16(dev, REG_SW_MAC_CTRL_2, &data16);
-       data16 &= ~BROADCAST_STORM_RATE;
-       data16 |= (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
-       ksz_write16(dev, REG_SW_MAC_CTRL_2, data16);
-
-       return 0;
-}
-
-static void port_setup(struct ksz_device *dev, int port, bool cpu_port)
-{
-       u8 data8;
-       u16 data16;
-
-       /* enable tag tail for host port */
-       if (cpu_port)
-               ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_TAIL_TAG_ENABLE,
-                            true);
-
-       ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, false);
-
-       /* set back pressure */
-       ksz_port_cfg(dev, port, REG_PORT_MAC_CTRL_1, PORT_BACK_PRESSURE, true);
-
-       /* set flow control */
-       ksz_port_cfg(dev, port, REG_PORT_CTRL_0,
-                    PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL, true);
-
-       /* enable broadcast storm limit */
-       ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
-
-       /* disable DiffServ priority */
-       ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_PRIO_ENABLE, false);
-
-       /* replace priority */
-       ksz_port_cfg(dev, port, REG_PORT_MRI_MAC_CTRL, PORT_USER_PRIO_CEILING,
-                    false);
-       ksz_port_cfg32(dev, port, REG_PORT_MTI_QUEUE_CTRL_0__4,
-                      MTI_PVID_REPLACE, false);
-
-       /* enable 802.1p priority */
-       ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true);
-
-       /* configure MAC to 1G & RGMII mode */
-       ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
-       data8 |= PORT_RGMII_ID_EG_ENABLE;
-       data8 &= ~PORT_MII_NOT_1GBIT;
-       data8 &= ~PORT_MII_SEL_M;
-       data8 |= PORT_RGMII_SEL;
-       ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8);
-
-       /* clear pending interrupts */
-       ksz_pread16(dev, port, REG_PORT_PHY_INT_ENABLE, &data16);
-}
-
-static void ksz_config_cpu_port(struct dsa_switch *ds)
-{
-       struct ksz_device *dev = ds->priv;
+       struct ksz_port *p;
        int i;
 
-       ds->num_ports = dev->port_cnt;
-
-       for (i = 0; i < ds->num_ports; i++) {
-               if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {
-                       dev->cpu_port = i;
-
-                       /* enable cpu port */
-                       port_setup(dev, i, true);
-               }
-       }
-}
-
-static int ksz_setup(struct dsa_switch *ds)
-{
-       struct ksz_device *dev = ds->priv;
-       int ret = 0;
-
-       dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
-                                      dev->num_vlans, GFP_KERNEL);
-       if (!dev->vlan_cache)
-               return -ENOMEM;
-
-       ret = ksz_reset_switch(ds);
-       if (ret) {
-               dev_err(ds->dev, "failed to reset switch\n");
-               return ret;
+       for (i = 0; i < dev->port_cnt; i++) {
+               if (i == port || i == dev->cpu_port)
+                       continue;
+               p = &dev->ports[i];
+               if (!(dev->member & (1 << i)))
+                       continue;
+
+               /* Port is a member of the bridge and is forwarding. */
+               if (p->stp_state == BR_STATE_FORWARDING &&
+                   p->member != dev->member)
+                       dev->dev_ops->cfg_port_member(dev, i, dev->member);
        }
-
-       /* accept packet up to 2000bytes */
-       ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true);
-
-       ksz_config_cpu_port(ds);
-
-       ksz_cfg(dev, REG_SW_MAC_CTRL_1, MULTICAST_STORM_DISABLE, true);
-
-       /* queue based egress rate limit */
-       ksz_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true);
-
-       /* start switch */
-       ksz_cfg(dev, REG_SW_OPERATION, SW_START, true);
-
-       return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_update_port_member);
 
-static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds,
-                                                 int port)
-{
-       return DSA_TAG_PROTO_KSZ;
-}
-
-static int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg)
+int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg)
 {
        struct ksz_device *dev = ds->priv;
-       u16 val = 0;
+       u16 val = 0xffff;
 
-       ksz_pread16(dev, addr, 0x100 + (reg << 1), &val);
+       dev->dev_ops->r_phy(dev, addr, reg, &val);
 
        return val;
 }
+EXPORT_SYMBOL_GPL(ksz_phy_read16);
 
-static int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val)
+int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val)
 {
        struct ksz_device *dev = ds->priv;
 
-       ksz_pwrite16(dev, addr, 0x100 + (reg << 1), val);
+       dev->dev_ops->w_phy(dev, addr, reg, val);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_phy_write16);
 
-static int ksz_enable_port(struct dsa_switch *ds, int port,
-                          struct phy_device *phy)
+int ksz_sset_count(struct dsa_switch *ds, int port, int sset)
 {
        struct ksz_device *dev = ds->priv;
 
-       /* setup slave port */
-       port_setup(dev, port, false);
-
-       return 0;
-}
-
-static void ksz_disable_port(struct dsa_switch *ds, int port,
-                            struct phy_device *phy)
-{
-       struct ksz_device *dev = ds->priv;
-
-       /* there is no port disable */
-       ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, true);
-}
-
-static int ksz_sset_count(struct dsa_switch *ds, int port, int sset)
-{
        if (sset != ETH_SS_STATS)
                return 0;
 
-       return TOTAL_SWITCH_COUNTER_NUM;
+       return dev->mib_cnt;
 }
+EXPORT_SYMBOL_GPL(ksz_sset_count);
 
-static void ksz_get_strings(struct dsa_switch *ds, int port,
-                           u32 stringset, uint8_t *buf)
-{
-       int i;
-
-       if (stringset != ETH_SS_STATS)
-               return;
-
-       for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
-               memcpy(buf + i * ETH_GSTRING_LEN, mib_names[i].string,
-                      ETH_GSTRING_LEN);
-       }
-}
-
-static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port,
-                                 uint64_t *buf)
+int ksz_port_bridge_join(struct dsa_switch *ds, int port,
+                        struct net_device *br)
 {
        struct ksz_device *dev = ds->priv;
-       int i;
-       u32 data;
-       int timeout;
-
-       mutex_lock(&dev->stats_mutex);
-
-       for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
-               data = MIB_COUNTER_READ;
-               data |= ((mib_names[i].index & 0xFF) << MIB_COUNTER_INDEX_S);
-               ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data);
-
-               timeout = 1000;
-               do {
-                       ksz_pread32(dev, port, REG_PORT_MIB_CTRL_STAT__4,
-                                   &data);
-                       usleep_range(1, 10);
-                       if (!(data & MIB_COUNTER_READ))
-                               break;
-               } while (timeout-- > 0);
-
-               /* failed to read MIB. get out of loop */
-               if (!timeout) {
-                       dev_dbg(dev->dev, "Failed to get MIB\n");
-                       break;
-               }
 
-               /* count resets upon read */
-               ksz_pread32(dev, port, REG_PORT_MIB_DATA, &data);
+       dev->br_member |= (1 << port);
 
-               dev->mib_value[i] += (uint64_t)data;
-               buf[i] = dev->mib_value[i];
-       }
-
-       mutex_unlock(&dev->stats_mutex);
-}
-
-static void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
-{
-       struct ksz_device *dev = ds->priv;
-       u8 data;
-
-       ksz_pread8(dev, port, P_STP_CTRL, &data);
-       data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
-
-       switch (state) {
-       case BR_STATE_DISABLED:
-               data |= PORT_LEARN_DISABLE;
-               break;
-       case BR_STATE_LISTENING:
-               data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
-               break;
-       case BR_STATE_LEARNING:
-               data |= PORT_RX_ENABLE;
-               break;
-       case BR_STATE_FORWARDING:
-               data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
-               break;
-       case BR_STATE_BLOCKING:
-               data |= PORT_LEARN_DISABLE;
-               break;
-       default:
-               dev_err(ds->dev, "invalid STP state: %d\n", state);
-               return;
-       }
+       /* port_stp_state_set() will be called after to put the port in
+        * appropriate state so there is no need to do anything.
+        */
 
-       ksz_pwrite8(dev, port, P_STP_CTRL, data);
+       return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_port_bridge_join);
 
-static void ksz_port_fast_age(struct dsa_switch *ds, int port)
+void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
+                          struct net_device *br)
 {
        struct ksz_device *dev = ds->priv;
-       u8 data8;
 
-       ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
-       data8 |= SW_FAST_AGING;
-       ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+       dev->br_member &= ~(1 << port);
+       dev->member &= ~(1 << port);
 
-       data8 &= ~SW_FAST_AGING;
-       ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+       /* port_stp_state_set() will be called after to put the port in
+        * forwarding state so there is no need to do anything.
+        */
 }
+EXPORT_SYMBOL_GPL(ksz_port_bridge_leave);
 
-static int ksz_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag)
+void ksz_port_fast_age(struct dsa_switch *ds, int port)
 {
        struct ksz_device *dev = ds->priv;
 
-       if (flag) {
-               ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
-                            PORT_VLAN_LOOKUP_VID_0, true);
-               ksz_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, true);
-               ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, true);
-       } else {
-               ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, false);
-               ksz_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, false);
-               ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
-                            PORT_VLAN_LOOKUP_VID_0, false);
-       }
-
-       return 0;
+       dev->dev_ops->flush_dyn_mac_table(dev, port);
 }
+EXPORT_SYMBOL_GPL(ksz_port_fast_age);
 
-static int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,
-                                const struct switchdev_obj_port_vlan *vlan)
+int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,
+                         const struct switchdev_obj_port_vlan *vlan)
 {
        /* nothing needed */
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_port_vlan_prepare);
 
-static void ksz_port_vlan_add(struct dsa_switch *ds, int port,
-                             const struct switchdev_obj_port_vlan *vlan)
-{
-       struct ksz_device *dev = ds->priv;
-       u32 vlan_table[3];
-       u16 vid;
-       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               if (get_vlan_table(ds, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to get vlan table\n");
-                       return;
-               }
-
-               vlan_table[0] = VLAN_VALID | (vid & VLAN_FID_M);
-               if (untagged)
-                       vlan_table[1] |= BIT(port);
-               else
-                       vlan_table[1] &= ~BIT(port);
-               vlan_table[1] &= ~(BIT(dev->cpu_port));
-
-               vlan_table[2] |= BIT(port) | BIT(dev->cpu_port);
-
-               if (set_vlan_table(ds, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to set vlan table\n");
-                       return;
-               }
-
-               /* change PVID */
-               if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
-                       ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vid);
-       }
-}
-
-static int ksz_port_vlan_del(struct dsa_switch *ds, int port,
-                            const struct switchdev_obj_port_vlan *vlan)
-{
-       struct ksz_device *dev = ds->priv;
-       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
-       u32 vlan_table[3];
-       u16 vid;
-       u16 pvid;
-
-       ksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid);
-       pvid = pvid & 0xFFF;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               if (get_vlan_table(ds, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to get vlan table\n");
-                       return -ETIMEDOUT;
-               }
-
-               vlan_table[2] &= ~BIT(port);
-
-               if (pvid == vid)
-                       pvid = 1;
-
-               if (untagged)
-                       vlan_table[1] &= ~BIT(port);
-
-               if (set_vlan_table(ds, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to set vlan table\n");
-                       return -ETIMEDOUT;
-               }
-       }
-
-       ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid);
-
-       return 0;
-}
-
-struct alu_struct {
-       /* entry 1 */
-       u8      is_static:1;
-       u8      is_src_filter:1;
-       u8      is_dst_filter:1;
-       u8      prio_age:3;
-       u32     _reserv_0_1:23;
-       u8      mstp:3;
-       /* entry 2 */
-       u8      is_override:1;
-       u8      is_use_fid:1;
-       u32     _reserv_1_1:23;
-       u8      port_forward:7;
-       /* entry 3 & 4*/
-       u32     _reserv_2_1:9;
-       u8      fid:7;
-       u8      mac[ETH_ALEN];
-};
-
-static int ksz_port_fdb_add(struct dsa_switch *ds, int port,
-                           const unsigned char *addr, u16 vid)
-{
-       struct ksz_device *dev = ds->priv;
-       u32 alu_table[4];
-       u32 data;
-       int ret = 0;
-
-       mutex_lock(&dev->alu_mutex);
-
-       /* find any entry with mac & vid */
-       data = vid << ALU_FID_INDEX_S;
-       data |= ((addr[0] << 8) | addr[1]);
-       ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
-
-       data = ((addr[2] << 24) | (addr[3] << 16));
-       data |= ((addr[4] << 8) | addr[5]);
-       ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
-
-       /* start read operation */
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
-
-       /* wait to be finished */
-       ret = wait_alu_ready(dev, ALU_START, 1000);
-       if (ret < 0) {
-               dev_dbg(dev->dev, "Failed to read ALU\n");
-               goto exit;
-       }
-
-       /* read ALU entry */
-       read_table(ds, alu_table);
-
-       /* update ALU entry */
-       alu_table[0] = ALU_V_STATIC_VALID;
-       alu_table[1] |= BIT(port);
-       if (vid)
-               alu_table[1] |= ALU_V_USE_FID;
-       alu_table[2] = (vid << ALU_V_FID_S);
-       alu_table[2] |= ((addr[0] << 8) | addr[1]);
-       alu_table[3] = ((addr[2] << 24) | (addr[3] << 16));
-       alu_table[3] |= ((addr[4] << 8) | addr[5]);
-
-       write_table(ds, alu_table);
-
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
-
-       /* wait to be finished */
-       ret = wait_alu_ready(dev, ALU_START, 1000);
-       if (ret < 0)
-               dev_dbg(dev->dev, "Failed to write ALU\n");
-
-exit:
-       mutex_unlock(&dev->alu_mutex);
-
-       return ret;
-}
-
-static int ksz_port_fdb_del(struct dsa_switch *ds, int port,
-                           const unsigned char *addr, u16 vid)
+int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
+                     void *data)
 {
        struct ksz_device *dev = ds->priv;
-       u32 alu_table[4];
-       u32 data;
        int ret = 0;
-
-       mutex_lock(&dev->alu_mutex);
-
-       /* read any entry with mac & vid */
-       data = vid << ALU_FID_INDEX_S;
-       data |= ((addr[0] << 8) | addr[1]);
-       ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
-
-       data = ((addr[2] << 24) | (addr[3] << 16));
-       data |= ((addr[4] << 8) | addr[5]);
-       ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
-
-       /* start read operation */
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
-
-       /* wait to be finished */
-       ret = wait_alu_ready(dev, ALU_START, 1000);
-       if (ret < 0) {
-               dev_dbg(dev->dev, "Failed to read ALU\n");
-               goto exit;
-       }
-
-       ksz_read32(dev, REG_SW_ALU_VAL_A, &alu_table[0]);
-       if (alu_table[0] & ALU_V_STATIC_VALID) {
-               ksz_read32(dev, REG_SW_ALU_VAL_B, &alu_table[1]);
-               ksz_read32(dev, REG_SW_ALU_VAL_C, &alu_table[2]);
-               ksz_read32(dev, REG_SW_ALU_VAL_D, &alu_table[3]);
-
-               /* clear forwarding port */
-               alu_table[2] &= ~BIT(port);
-
-               /* if there is no port to forward, clear table */
-               if ((alu_table[2] & ALU_V_PORT_MAP) == 0) {
-                       alu_table[0] = 0;
-                       alu_table[1] = 0;
-                       alu_table[2] = 0;
-                       alu_table[3] = 0;
-               }
-       } else {
-               alu_table[0] = 0;
-               alu_table[1] = 0;
-               alu_table[2] = 0;
-               alu_table[3] = 0;
-       }
-
-       write_table(ds, alu_table);
-
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
-
-       /* wait to be finished */
-       ret = wait_alu_ready(dev, ALU_START, 1000);
-       if (ret < 0)
-               dev_dbg(dev->dev, "Failed to write ALU\n");
-
-exit:
-       mutex_unlock(&dev->alu_mutex);
-
-       return ret;
-}
-
-static void convert_alu(struct alu_struct *alu, u32 *alu_table)
-{
-       alu->is_static = !!(alu_table[0] & ALU_V_STATIC_VALID);
-       alu->is_src_filter = !!(alu_table[0] & ALU_V_SRC_FILTER);
-       alu->is_dst_filter = !!(alu_table[0] & ALU_V_DST_FILTER);
-       alu->prio_age = (alu_table[0] >> ALU_V_PRIO_AGE_CNT_S) &
-                       ALU_V_PRIO_AGE_CNT_M;
-       alu->mstp = alu_table[0] & ALU_V_MSTP_M;
-
-       alu->is_override = !!(alu_table[1] & ALU_V_OVERRIDE);
-       alu->is_use_fid = !!(alu_table[1] & ALU_V_USE_FID);
-       alu->port_forward = alu_table[1] & ALU_V_PORT_MAP;
-
-       alu->fid = (alu_table[2] >> ALU_V_FID_S) & ALU_V_FID_M;
-
-       alu->mac[0] = (alu_table[2] >> 8) & 0xFF;
-       alu->mac[1] = alu_table[2] & 0xFF;
-       alu->mac[2] = (alu_table[3] >> 24) & 0xFF;
-       alu->mac[3] = (alu_table[3] >> 16) & 0xFF;
-       alu->mac[4] = (alu_table[3] >> 8) & 0xFF;
-       alu->mac[5] = alu_table[3] & 0xFF;
-}
-
-static int ksz_port_fdb_dump(struct dsa_switch *ds, int port,
-                            dsa_fdb_dump_cb_t *cb, void *data)
-{
-       struct ksz_device *dev = ds->priv;
-       int ret = 0;
-       u32 ksz_data;
-       u32 alu_table[4];
+       u16 i = 0;
+       u16 entries = 0;
+       u8 timestamp = 0;
+       u8 fid;
+       u8 member;
        struct alu_struct alu;
-       int timeout;
-
-       mutex_lock(&dev->alu_mutex);
-
-       /* start ALU search */
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_START | ALU_SEARCH);
 
        do {
-               timeout = 1000;
-               do {
-                       ksz_read32(dev, REG_SW_ALU_CTRL__4, &ksz_data);
-                       if ((ksz_data & ALU_VALID) || !(ksz_data & ALU_START))
-                               break;
-                       usleep_range(1, 10);
-               } while (timeout-- > 0);
-
-               if (!timeout) {
-                       dev_dbg(dev->dev, "Failed to search ALU\n");
-                       ret = -ETIMEDOUT;
-                       goto exit;
-               }
-
-               /* read ALU table */
-               read_table(ds, alu_table);
-
-               convert_alu(&alu, alu_table);
-
-               if (alu.port_forward & BIT(port)) {
+               alu.is_static = false;
+               ret = dev->dev_ops->r_dyn_mac_table(dev, i, alu.mac, &fid,
+                                                   &member, &timestamp,
+                                                   &entries);
+               if (!ret && (member & BIT(port))) {
                        ret = cb(alu.mac, alu.fid, alu.is_static, data);
                        if (ret)
-                               goto exit;
+                               break;
                }
-       } while (ksz_data & ALU_START);
-
-exit:
-
-       /* stop ALU search */
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, 0);
-
-       mutex_unlock(&dev->alu_mutex);
+               i++;
+       } while (i < entries);
+       if (i >= entries)
+               ret = 0;
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(ksz_port_fdb_dump);
 
-static int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,
-                               const struct switchdev_obj_port_mdb *mdb)
+int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,
+                        const struct switchdev_obj_port_mdb *mdb)
 {
        /* nothing to do */
        return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_port_mdb_prepare);
 
-static void ksz_port_mdb_add(struct dsa_switch *ds, int port,
-                            const struct switchdev_obj_port_mdb *mdb)
+void ksz_port_mdb_add(struct dsa_switch *ds, int port,
+                     const struct switchdev_obj_port_mdb *mdb)
 {
        struct ksz_device *dev = ds->priv;
-       u32 static_table[4];
-       u32 data;
+       struct alu_struct alu;
        int index;
-       u32 mac_hi, mac_lo;
-
-       mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
-       mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
-       mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
-
-       mutex_lock(&dev->alu_mutex);
+       int empty = 0;
 
+       alu.port_forward = 0;
        for (index = 0; index < dev->num_statics; index++) {
-               /* find empty slot first */
-               data = (index << ALU_STAT_INDEX_S) |
-                       ALU_STAT_READ | ALU_STAT_START;
-               ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
-
-               /* wait to be finished */
-               if (wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0) {
-                       dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
-                       goto exit;
-               }
-
-               /* read ALU static table */
-               read_table(ds, static_table);
-
-               if (static_table[0] & ALU_V_STATIC_VALID) {
-                       /* check this has same vid & mac address */
-                       if (((static_table[2] >> ALU_V_FID_S) == (mdb->vid)) &&
-                           ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
-                           (static_table[3] == mac_lo)) {
-                               /* found matching one */
+               if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) {
+                       /* Found one already in static MAC table. */
+                       if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) &&
+                           alu.fid == mdb->vid)
                                break;
-                       }
-               } else {
-                       /* found empty one */
-                       break;
+               /* Remember the first empty entry. */
+               } else if (!empty) {
+                       empty = index + 1;
                }
        }
 
        /* no available entry */
-       if (index == dev->num_statics)
-               goto exit;
+       if (index == dev->num_statics && !empty)
+               return;
 
        /* add entry */
-       static_table[0] = ALU_V_STATIC_VALID;
-       static_table[1] |= BIT(port);
-       if (mdb->vid)
-               static_table[1] |= ALU_V_USE_FID;
-       static_table[2] = (mdb->vid << ALU_V_FID_S);
-       static_table[2] |= mac_hi;
-       static_table[3] = mac_lo;
-
-       write_table(ds, static_table);
-
-       data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
-       ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
-
-       /* wait to be finished */
-       if (wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0)
-               dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+       if (index == dev->num_statics) {
+               index = empty - 1;
+               memset(&alu, 0, sizeof(alu));
+               memcpy(alu.mac, mdb->addr, ETH_ALEN);
+               alu.is_static = true;
+       }
+       alu.port_forward |= BIT(port);
+       if (mdb->vid) {
+               alu.is_use_fid = true;
 
-exit:
-       mutex_unlock(&dev->alu_mutex);
+               /* Need a way to map VID to FID. */
+               alu.fid = mdb->vid;
+       }
+       dev->dev_ops->w_sta_mac_table(dev, index, &alu);
 }
+EXPORT_SYMBOL_GPL(ksz_port_mdb_add);
 
-static int ksz_port_mdb_del(struct dsa_switch *ds, int port,
-                           const struct switchdev_obj_port_mdb *mdb)
+int ksz_port_mdb_del(struct dsa_switch *ds, int port,
+                    const struct switchdev_obj_port_mdb *mdb)
 {
        struct ksz_device *dev = ds->priv;
-       u32 static_table[4];
-       u32 data;
+       struct alu_struct alu;
        int index;
        int ret = 0;
-       u32 mac_hi, mac_lo;
-
-       mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
-       mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
-       mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
-
-       mutex_lock(&dev->alu_mutex);
 
        for (index = 0; index < dev->num_statics; index++) {
-               /* find empty slot first */
-               data = (index << ALU_STAT_INDEX_S) |
-                       ALU_STAT_READ | ALU_STAT_START;
-               ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
-
-               /* wait to be finished */
-               ret = wait_alu_sta_ready(dev, ALU_STAT_START, 1000);
-               if (ret < 0) {
-                       dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
-                       goto exit;
-               }
-
-               /* read ALU static table */
-               read_table(ds, static_table);
-
-               if (static_table[0] & ALU_V_STATIC_VALID) {
-                       /* check this has same vid & mac address */
-
-                       if (((static_table[2] >> ALU_V_FID_S) == (mdb->vid)) &&
-                           ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
-                           (static_table[3] == mac_lo)) {
-                               /* found matching one */
+               if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) {
+                       /* Found one already in static MAC table. */
+                       if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) &&
+                           alu.fid == mdb->vid)
                                break;
-                       }
                }
        }
 
        /* no available entry */
-       if (index == dev->num_statics) {
-               ret = -EINVAL;
+       if (index == dev->num_statics)
                goto exit;
-       }
 
        /* clear port */
-       static_table[1] &= ~BIT(port);
-
-       if ((static_table[1] & ALU_V_PORT_MAP) == 0) {
-               /* delete entry */
-               static_table[0] = 0;
-               static_table[1] = 0;
-               static_table[2] = 0;
-               static_table[3] = 0;
-       }
-
-       write_table(ds, static_table);
-
-       data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
-       ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
-
-       /* wait to be finished */
-       ret = wait_alu_sta_ready(dev, ALU_STAT_START, 1000);
-       if (ret < 0)
-               dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+       alu.port_forward &= ~BIT(port);
+       if (!alu.port_forward)
+               alu.is_static = false;
+       dev->dev_ops->w_sta_mac_table(dev, index, &alu);
 
 exit:
-       mutex_unlock(&dev->alu_mutex);
-
        return ret;
 }
+EXPORT_SYMBOL_GPL(ksz_port_mdb_del);
 
-static int ksz_port_mirror_add(struct dsa_switch *ds, int port,
-                              struct dsa_mall_mirror_tc_entry *mirror,
-                              bool ingress)
+int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy)
 {
        struct ksz_device *dev = ds->priv;
 
-       if (ingress)
-               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
-       else
-               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
-
-       ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
-
-       /* configure mirror port */
-       ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
-                    PORT_MIRROR_SNIFFER, true);
+       /* setup slave port */
+       dev->dev_ops->port_setup(dev, port, false);
 
-       ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
+       /* port_stp_state_set() will be called after to enable the port so
+        * there is no need to do anything.
+        */
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_enable_port);
 
-static void ksz_port_mirror_del(struct dsa_switch *ds, int port,
-                               struct dsa_mall_mirror_tc_entry *mirror)
+void ksz_disable_port(struct dsa_switch *ds, int port, struct phy_device *phy)
 {
        struct ksz_device *dev = ds->priv;
-       u8 data;
-
-       if (mirror->ingress)
-               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
-       else
-               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
 
-       ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
+       dev->on_ports &= ~(1 << port);
+       dev->live_ports &= ~(1 << port);
 
-       if (!(data & (PORT_MIRROR_RX | PORT_MIRROR_TX)))
-               ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
-                            PORT_MIRROR_SNIFFER, false);
-}
-
-static const struct dsa_switch_ops ksz_switch_ops = {
-       .get_tag_protocol       = ksz_get_tag_protocol,
-       .setup                  = ksz_setup,
-       .phy_read               = ksz_phy_read16,
-       .phy_write              = ksz_phy_write16,
-       .port_enable            = ksz_enable_port,
-       .port_disable           = ksz_disable_port,
-       .get_strings            = ksz_get_strings,
-       .get_ethtool_stats      = ksz_get_ethtool_stats,
-       .get_sset_count         = ksz_sset_count,
-       .port_stp_state_set     = ksz_port_stp_state_set,
-       .port_fast_age          = ksz_port_fast_age,
-       .port_vlan_filtering    = ksz_port_vlan_filtering,
-       .port_vlan_prepare      = ksz_port_vlan_prepare,
-       .port_vlan_add          = ksz_port_vlan_add,
-       .port_vlan_del          = ksz_port_vlan_del,
-       .port_fdb_dump          = ksz_port_fdb_dump,
-       .port_fdb_add           = ksz_port_fdb_add,
-       .port_fdb_del           = ksz_port_fdb_del,
-       .port_mdb_prepare       = ksz_port_mdb_prepare,
-       .port_mdb_add           = ksz_port_mdb_add,
-       .port_mdb_del           = ksz_port_mdb_del,
-       .port_mirror_add        = ksz_port_mirror_add,
-       .port_mirror_del        = ksz_port_mirror_del,
-};
-
-struct ksz_chip_data {
-       u32 chip_id;
-       const char *dev_name;
-       int num_vlans;
-       int num_alus;
-       int num_statics;
-       int cpu_ports;
-       int port_cnt;
-};
-
-static const struct ksz_chip_data ksz_switch_chips[] = {
-       {
-               .chip_id = 0x00947700,
-               .dev_name = "KSZ9477",
-               .num_vlans = 4096,
-               .num_alus = 4096,
-               .num_statics = 16,
-               .cpu_ports = 0x7F,      /* can be configured as cpu port */
-               .port_cnt = 7,          /* total physical port count */
-       },
-       {
-               .chip_id = 0x00989700,
-               .dev_name = "KSZ9897",
-               .num_vlans = 4096,
-               .num_alus = 4096,
-               .num_statics = 16,
-               .cpu_ports = 0x7F,      /* can be configured as cpu port */
-               .port_cnt = 7,          /* total physical port count */
-       },
-};
-
-static int ksz_switch_init(struct ksz_device *dev)
-{
-       int i;
-
-       dev->ds->ops = &ksz_switch_ops;
-
-       for (i = 0; i < ARRAY_SIZE(ksz_switch_chips); i++) {
-               const struct ksz_chip_data *chip = &ksz_switch_chips[i];
-
-               if (dev->chip_id == chip->chip_id) {
-                       dev->name = chip->dev_name;
-                       dev->num_vlans = chip->num_vlans;
-                       dev->num_alus = chip->num_alus;
-                       dev->num_statics = chip->num_statics;
-                       dev->port_cnt = chip->port_cnt;
-                       dev->cpu_ports = chip->cpu_ports;
-
-                       break;
-               }
-       }
-
-       /* no switch found */
-       if (!dev->port_cnt)
-               return -ENODEV;
-
-       return 0;
+       /* port_stp_state_set() will be called after to disable the port so
+        * there is no need to do anything.
+        */
 }
+EXPORT_SYMBOL_GPL(ksz_disable_port);
 
 struct ksz_device *ksz_switch_alloc(struct device *base,
                                    const struct ksz_io_ops *ops,
@@ -1167,59 +288,64 @@ struct ksz_device *ksz_switch_alloc(struct device *base,
 }
 EXPORT_SYMBOL(ksz_switch_alloc);
 
-int ksz_switch_detect(struct ksz_device *dev)
-{
-       u8 data8;
-       u32 id32;
-       int ret;
-
-       /* turn off SPI DO Edge select */
-       ret = ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);
-       if (ret)
-               return ret;
-
-       data8 &= ~SPI_AUTO_EDGE_DETECTION;
-       ret = ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);
-       if (ret)
-               return ret;
-
-       /* read chip id */
-       ret = ksz_read32(dev, REG_CHIP_ID0__1, &id32);
-       if (ret)
-               return ret;
-
-       dev->chip_id = id32;
-
-       return 0;
-}
-EXPORT_SYMBOL(ksz_switch_detect);
-
-int ksz_switch_register(struct ksz_device *dev)
+int ksz_switch_register(struct ksz_device *dev,
+                       const struct ksz_dev_ops *ops)
 {
        int ret;
 
        if (dev->pdata)
                dev->chip_id = dev->pdata->chip_id;
 
+       dev->reset_gpio = devm_gpiod_get_optional(dev->dev, "reset",
+                                                 GPIOD_OUT_LOW);
+       if (IS_ERR(dev->reset_gpio))
+               return PTR_ERR(dev->reset_gpio);
+
+       if (dev->reset_gpio) {
+               gpiod_set_value(dev->reset_gpio, 1);
+               mdelay(10);
+               gpiod_set_value(dev->reset_gpio, 0);
+       }
+
        mutex_init(&dev->reg_mutex);
        mutex_init(&dev->stats_mutex);
        mutex_init(&dev->alu_mutex);
        mutex_init(&dev->vlan_mutex);
 
-       if (ksz_switch_detect(dev))
+       dev->dev_ops = ops;
+
+       if (dev->dev_ops->detect(dev))
                return -EINVAL;
 
-       ret = ksz_switch_init(dev);
+       ret = dev->dev_ops->init(dev);
        if (ret)
                return ret;
 
-       return dsa_register_switch(dev->ds);
+       dev->interface = PHY_INTERFACE_MODE_MII;
+       if (dev->dev->of_node) {
+               ret = of_get_phy_mode(dev->dev->of_node);
+               if (ret >= 0)
+                       dev->interface = ret;
+       }
+
+       ret = dsa_register_switch(dev->ds);
+       if (ret) {
+               dev->dev_ops->exit(dev);
+               return ret;
+       }
+
+       return 0;
 }
 EXPORT_SYMBOL(ksz_switch_register);
 
 void ksz_switch_remove(struct ksz_device *dev)
 {
+       dev->dev_ops->exit(dev);
        dsa_unregister_switch(dev->ds);
+
+       if (dev->reset_gpio)
+               gpiod_set_value(dev->reset_gpio, 1);
+
 }
 EXPORT_SYMBOL(ksz_switch_remove);
 
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
new file mode 100644 (file)
index 0000000..2dd832d
--- /dev/null
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0
+ * Microchip switch driver common header
+ *
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
+ */
+
+#ifndef __KSZ_COMMON_H
+#define __KSZ_COMMON_H
+
+void ksz_update_port_member(struct ksz_device *dev, int port);
+
+/* Common DSA access functions */
+
+int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg);
+int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val);
+int ksz_sset_count(struct dsa_switch *ds, int port, int sset);
+int ksz_port_bridge_join(struct dsa_switch *ds, int port,
+                        struct net_device *br);
+void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
+                          struct net_device *br);
+void ksz_port_fast_age(struct dsa_switch *ds, int port);
+int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,
+                         const struct switchdev_obj_port_vlan *vlan);
+int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
+                     void *data);
+int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,
+                        const struct switchdev_obj_port_mdb *mdb);
+void ksz_port_mdb_add(struct dsa_switch *ds, int port,
+                     const struct switchdev_obj_port_mdb *mdb);
+int ksz_port_mdb_del(struct dsa_switch *ds, int port,
+                    const struct switchdev_obj_port_mdb *mdb);
+int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy);
+void ksz_disable_port(struct dsa_switch *ds, int port, struct phy_device *phy);
+
+/* Common register access functions */
+
+static inline int ksz_read8(struct ksz_device *dev, u32 reg, u8 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read8(dev, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_read16(struct ksz_device *dev, u32 reg, u16 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read16(dev, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_read24(struct ksz_device *dev, u32 reg, u32 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read24(dev, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_read32(struct ksz_device *dev, u32 reg, u32 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read32(dev, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_write8(struct ksz_device *dev, u32 reg, u8 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write8(dev, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_write16(struct ksz_device *dev, u32 reg, u16 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write16(dev, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_write24(struct ksz_device *dev, u32 reg, u32 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write24(dev, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_write32(struct ksz_device *dev, u32 reg, u32 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write32(dev, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_get(struct ksz_device *dev, u32 reg, void *data,
+                         size_t len)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->get(dev, reg, data, len);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_set(struct ksz_device *dev, u32 reg, void *data,
+                         size_t len)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->set(dev, reg, data, len);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline void ksz_pread8(struct ksz_device *dev, int port, int offset,
+                             u8 *data)
+{
+       ksz_read8(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static inline void ksz_pread16(struct ksz_device *dev, int port, int offset,
+                              u16 *data)
+{
+       ksz_read16(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static inline void ksz_pread32(struct ksz_device *dev, int port, int offset,
+                              u32 *data)
+{
+       ksz_read32(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static inline void ksz_pwrite8(struct ksz_device *dev, int port, int offset,
+                              u8 data)
+{
+       ksz_write8(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static inline void ksz_pwrite16(struct ksz_device *dev, int port, int offset,
+                               u16 data)
+{
+       ksz_write16(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static inline void ksz_pwrite32(struct ksz_device *dev, int port, int offset,
+                               u32 data)
+{
+       ksz_write32(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
+{
+       u8 data;
+
+       ksz_read8(dev, addr, &data);
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+       ksz_write8(dev, addr, data);
+}
+
+static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
+                        bool set)
+{
+       u32 addr;
+       u8 data;
+
+       addr = dev->dev_ops->get_port_addr(port, offset);
+       ksz_read8(dev, addr, &data);
+
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+
+       ksz_write8(dev, addr, data);
+}
+
+#endif
index 2a98dbd..60b4901 100644 (file)
@@ -1,19 +1,8 @@
-/*
- * Microchip KSZ series switch common definitions
- *
- * Copyright (C) 2017
+/* SPDX-License-Identifier: GPL-2.0
  *
- * 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.
+ * Microchip KSZ series switch common definitions
  *
- * 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.
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
  */
 
 #ifndef __KSZ_PRIV_H
@@ -25,7 +14,7 @@
 #include <linux/etherdevice.h>
 #include <net/dsa.h>
 
-#include "ksz_9477_reg.h"
+#include "ksz9477_reg.h"
 
 struct ksz_io_ops;
 
@@ -33,6 +22,27 @@ struct vlan_table {
        u32 table[3];
 };
 
+struct ksz_port_mib {
+       u8 cnt_ptr;
+       u64 *counters;
+};
+
+struct ksz_port {
+       u16 member;
+       u16 vid_member;
+       int stp_state;
+       struct phy_device phydev;
+
+       u32 on:1;                       /* port is not disabled by hardware */
+       u32 phy:1;                      /* port has a PHY */
+       u32 fiber:1;                    /* port is fiber */
+       u32 sgmii:1;                    /* port is SGMII */
+       u32 force:1;
+       u32 link_just_down:1;           /* link just goes down */
+
+       struct ksz_port_mib mib;
+};
+
 struct ksz_device {
        struct dsa_switch *ds;
        struct ksz_platform_data *pdata;
@@ -43,11 +53,14 @@ struct ksz_device {
        struct mutex alu_mutex;         /* ALU access */
        struct mutex vlan_mutex;        /* vlan access */
        const struct ksz_io_ops *ops;
+       const struct ksz_dev_ops *dev_ops;
 
        struct device *dev;
 
        void *priv;
 
+       struct gpio_desc *reset_gpio;   /* Optional reset GPIO */
+
        /* chip specific data */
        u32 chip_id;
        int num_vlans;
@@ -55,11 +68,37 @@ struct ksz_device {
        int num_statics;
        int cpu_port;                   /* port connected to CPU */
        int cpu_ports;                  /* port bitmap can be cpu port */
+       int phy_port_cnt;
        int port_cnt;
+       int reg_mib_cnt;
+       int mib_cnt;
+       int mib_port_cnt;
+       int last_port;                  /* ports after that not used */
+       phy_interface_t interface;
+       u32 regs_size;
 
        struct vlan_table *vlan_cache;
 
        u64 mib_value[TOTAL_SWITCH_COUNTER_NUM];
+
+       u8 *txbuf;
+
+       struct ksz_port *ports;
+       struct timer_list mib_read_timer;
+       struct work_struct mib_read;
+       unsigned long mib_read_interval;
+       u16 br_member;
+       u16 member;
+       u16 live_ports;
+       u16 on_ports;                   /* ports enabled by DSA */
+       u16 rx_ports;
+       u16 tx_ports;
+       u16 mirror_rx;
+       u16 mirror_tx;
+       u32 features;                   /* chip specific features */
+       u32 overrides;                  /* chip functions set by user */
+       u16 host_mask;
+       u16 port_mask;
 };
 
 struct ksz_io_ops {
@@ -71,140 +110,60 @@ struct ksz_io_ops {
        int (*write16)(struct ksz_device *dev, u32 reg, u16 value);
        int (*write24)(struct ksz_device *dev, u32 reg, u32 value);
        int (*write32)(struct ksz_device *dev, u32 reg, u32 value);
-       int (*phy_read16)(struct ksz_device *dev, int addr, int reg,
-                         u16 *value);
-       int (*phy_write16)(struct ksz_device *dev, int addr, int reg,
-                          u16 value);
+       int (*get)(struct ksz_device *dev, u32 reg, void *data, size_t len);
+       int (*set)(struct ksz_device *dev, u32 reg, void *data, size_t len);
+};
+
+struct alu_struct {
+       /* entry 1 */
+       u8      is_static:1;
+       u8      is_src_filter:1;
+       u8      is_dst_filter:1;
+       u8      prio_age:3;
+       u32     _reserv_0_1:23;
+       u8      mstp:3;
+       /* entry 2 */
+       u8      is_override:1;
+       u8      is_use_fid:1;
+       u32     _reserv_1_1:23;
+       u8      port_forward:7;
+       /* entry 3 & 4*/
+       u32     _reserv_2_1:9;
+       u8      fid:7;
+       u8      mac[ETH_ALEN];
+};
+
+struct ksz_dev_ops {
+       u32 (*get_port_addr)(int port, int offset);
+       void (*cfg_port_member)(struct ksz_device *dev, int port, u8 member);
+       void (*flush_dyn_mac_table)(struct ksz_device *dev, int port);
+       void (*port_setup)(struct ksz_device *dev, int port, bool cpu_port);
+       void (*r_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 *val);
+       void (*w_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 val);
+       int (*r_dyn_mac_table)(struct ksz_device *dev, u16 addr, u8 *mac_addr,
+                              u8 *fid, u8 *src_port, u8 *timestamp,
+                              u16 *entries);
+       int (*r_sta_mac_table)(struct ksz_device *dev, u16 addr,
+                              struct alu_struct *alu);
+       void (*w_sta_mac_table)(struct ksz_device *dev, u16 addr,
+                               struct alu_struct *alu);
+       void (*r_mib_cnt)(struct ksz_device *dev, int port, u16 addr,
+                         u64 *cnt);
+       void (*r_mib_pkt)(struct ksz_device *dev, int port, u16 addr,
+                         u64 *dropped, u64 *cnt);
+       void (*port_init_cnt)(struct ksz_device *dev, int port);
+       int (*shutdown)(struct ksz_device *dev);
+       int (*detect)(struct ksz_device *dev);
+       int (*init)(struct ksz_device *dev);
+       void (*exit)(struct ksz_device *dev);
 };
 
 struct ksz_device *ksz_switch_alloc(struct device *base,
                                    const struct ksz_io_ops *ops, void *priv);
-int ksz_switch_detect(struct ksz_device *dev);
-int ksz_switch_register(struct ksz_device *dev);
+int ksz_switch_register(struct ksz_device *dev,
+                       const struct ksz_dev_ops *ops);
 void ksz_switch_remove(struct ksz_device *dev);
 
-static inline int ksz_read8(struct ksz_device *dev, u32 reg, u8 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read8(dev, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_read16(struct ksz_device *dev, u32 reg, u16 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read16(dev, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_read24(struct ksz_device *dev, u32 reg, u32 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read24(dev, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_read32(struct ksz_device *dev, u32 reg, u32 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read32(dev, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_write8(struct ksz_device *dev, u32 reg, u8 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write8(dev, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_write16(struct ksz_device *dev, u32 reg, u16 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write16(dev, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_write24(struct ksz_device *dev, u32 reg, u32 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write24(dev, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_write32(struct ksz_device *dev, u32 reg, u32 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write32(dev, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline void ksz_pread8(struct ksz_device *dev, int port, int offset,
-                             u8 *data)
-{
-       ksz_read8(dev, PORT_CTRL_ADDR(port, offset), data);
-}
-
-static inline void ksz_pread16(struct ksz_device *dev, int port, int offset,
-                              u16 *data)
-{
-       ksz_read16(dev, PORT_CTRL_ADDR(port, offset), data);
-}
-
-static inline void ksz_pread32(struct ksz_device *dev, int port, int offset,
-                              u32 *data)
-{
-       ksz_read32(dev, PORT_CTRL_ADDR(port, offset), data);
-}
-
-static inline void ksz_pwrite8(struct ksz_device *dev, int port, int offset,
-                              u8 data)
-{
-       ksz_write8(dev, PORT_CTRL_ADDR(port, offset), data);
-}
-
-static inline void ksz_pwrite16(struct ksz_device *dev, int port, int offset,
-                               u16 data)
-{
-       ksz_write16(dev, PORT_CTRL_ADDR(port, offset), data);
-}
-
-static inline void ksz_pwrite32(struct ksz_device *dev, int port, int offset,
-                               u32 data)
-{
-       ksz_write32(dev, PORT_CTRL_ADDR(port, offset), data);
-}
+int ksz9477_switch_register(struct ksz_device *dev);
 
 #endif
diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c
deleted file mode 100644 (file)
index 8c1778b..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Microchip KSZ series register access through SPI
- *
- * Copyright (C) 2017
- *
- * 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.
- */
-
-#include <asm/unaligned.h>
-
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/spi/spi.h>
-
-#include "ksz_priv.h"
-
-/* SPI frame opcodes */
-#define KS_SPIOP_RD                    3
-#define KS_SPIOP_WR                    2
-
-#define SPI_ADDR_SHIFT                 24
-#define SPI_ADDR_MASK                  (BIT(SPI_ADDR_SHIFT) - 1)
-#define SPI_TURNAROUND_SHIFT           5
-
-static int ksz_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val,
-                           unsigned int len)
-{
-       u32 txbuf;
-       int ret;
-
-       txbuf = reg & SPI_ADDR_MASK;
-       txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT;
-       txbuf <<= SPI_TURNAROUND_SHIFT;
-       txbuf = cpu_to_be32(txbuf);
-
-       ret = spi_write_then_read(spi, &txbuf, 4, val, len);
-       return ret;
-}
-
-static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
-                       unsigned int len)
-{
-       struct spi_device *spi = dev->priv;
-
-       return ksz_spi_read_reg(spi, reg, data, len);
-}
-
-static int ksz_spi_read8(struct ksz_device *dev, u32 reg, u8 *val)
-{
-       return ksz_spi_read(dev, reg, val, 1);
-}
-
-static int ksz_spi_read16(struct ksz_device *dev, u32 reg, u16 *val)
-{
-       int ret = ksz_spi_read(dev, reg, (u8 *)val, 2);
-
-       if (!ret)
-               *val = be16_to_cpu(*val);
-
-       return ret;
-}
-
-static int ksz_spi_read24(struct ksz_device *dev, u32 reg, u32 *val)
-{
-       int ret;
-
-       *val = 0;
-       ret = ksz_spi_read(dev, reg, (u8 *)val, 3);
-       if (!ret) {
-               *val = be32_to_cpu(*val);
-               /* convert to 24bit */
-               *val >>= 8;
-       }
-
-       return ret;
-}
-
-static int ksz_spi_read32(struct ksz_device *dev, u32 reg, u32 *val)
-{
-       int ret = ksz_spi_read(dev, reg, (u8 *)val, 4);
-
-       if (!ret)
-               *val = be32_to_cpu(*val);
-
-       return ret;
-}
-
-static int ksz_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val,
-                            unsigned int len)
-{
-       u32 txbuf;
-       u8 data[12];
-       int i;
-
-       txbuf = reg & SPI_ADDR_MASK;
-       txbuf |= (KS_SPIOP_WR << SPI_ADDR_SHIFT);
-       txbuf <<= SPI_TURNAROUND_SHIFT;
-       txbuf = cpu_to_be32(txbuf);
-
-       data[0] = txbuf & 0xFF;
-       data[1] = (txbuf & 0xFF00) >> 8;
-       data[2] = (txbuf & 0xFF0000) >> 16;
-       data[3] = (txbuf & 0xFF000000) >> 24;
-       for (i = 0; i < len; i++)
-               data[i + 4] = val[i];
-
-       return spi_write(spi, &data, 4 + len);
-}
-
-static int ksz_spi_write8(struct ksz_device *dev, u32 reg, u8 value)
-{
-       struct spi_device *spi = dev->priv;
-
-       return ksz_spi_write_reg(spi, reg, &value, 1);
-}
-
-static int ksz_spi_write16(struct ksz_device *dev, u32 reg, u16 value)
-{
-       struct spi_device *spi = dev->priv;
-
-       value = cpu_to_be16(value);
-       return ksz_spi_write_reg(spi, reg, (u8 *)&value, 2);
-}
-
-static int ksz_spi_write24(struct ksz_device *dev, u32 reg, u32 value)
-{
-       struct spi_device *spi = dev->priv;
-
-       /* make it to big endian 24bit from MSB */
-       value <<= 8;
-       value = cpu_to_be32(value);
-       return ksz_spi_write_reg(spi, reg, (u8 *)&value, 3);
-}
-
-static int ksz_spi_write32(struct ksz_device *dev, u32 reg, u32 value)
-{
-       struct spi_device *spi = dev->priv;
-
-       value = cpu_to_be32(value);
-       return ksz_spi_write_reg(spi, reg, (u8 *)&value, 4);
-}
-
-static const struct ksz_io_ops ksz_spi_ops = {
-       .read8 = ksz_spi_read8,
-       .read16 = ksz_spi_read16,
-       .read24 = ksz_spi_read24,
-       .read32 = ksz_spi_read32,
-       .write8 = ksz_spi_write8,
-       .write16 = ksz_spi_write16,
-       .write24 = ksz_spi_write24,
-       .write32 = ksz_spi_write32,
-};
-
-static int ksz_spi_probe(struct spi_device *spi)
-{
-       struct ksz_device *dev;
-       int ret;
-
-       dev = ksz_switch_alloc(&spi->dev, &ksz_spi_ops, spi);
-       if (!dev)
-               return -ENOMEM;
-
-       if (spi->dev.platform_data)
-               dev->pdata = spi->dev.platform_data;
-
-       ret = ksz_switch_register(dev);
-       if (ret)
-               return ret;
-
-       spi_set_drvdata(spi, dev);
-
-       return 0;
-}
-
-static int ksz_spi_remove(struct spi_device *spi)
-{
-       struct ksz_device *dev = spi_get_drvdata(spi);
-
-       if (dev)
-               ksz_switch_remove(dev);
-
-       return 0;
-}
-
-static const struct of_device_id ksz_dt_ids[] = {
-       { .compatible = "microchip,ksz9477" },
-       { .compatible = "microchip,ksz9897" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, ksz_dt_ids);
-
-static struct spi_driver ksz_spi_driver = {
-       .driver = {
-               .name   = "ksz9477-switch",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(ksz_dt_ids),
-       },
-       .probe  = ksz_spi_probe,
-       .remove = ksz_spi_remove,
-};
-
-module_spi_driver(ksz_spi_driver);
-
-MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>");
-MODULE_DESCRIPTION("Microchip KSZ Series Switch SPI access Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/microchip/ksz_spi.h b/drivers/net/dsa/microchip/ksz_spi.h
new file mode 100644 (file)
index 0000000..427811b
--- /dev/null
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0
+ * Microchip KSZ series SPI access common header
+ *
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
+ *     Tristram Ha <Tristram.Ha@microchip.com>
+ */
+
+#ifndef __KSZ_SPI_H
+#define __KSZ_SPI_H
+
+/* Chip dependent SPI access */
+static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
+                       unsigned int len);
+static int ksz_spi_write(struct ksz_device *dev, u32 reg, void *data,
+                        unsigned int len);
+
+static int ksz_spi_read8(struct ksz_device *dev, u32 reg, u8 *val)
+{
+       return ksz_spi_read(dev, reg, val, 1);
+}
+
+static int ksz_spi_read16(struct ksz_device *dev, u32 reg, u16 *val)
+{
+       int ret = ksz_spi_read(dev, reg, (u8 *)val, 2);
+
+       if (!ret)
+               *val = be16_to_cpu(*val);
+
+       return ret;
+}
+
+static int ksz_spi_read32(struct ksz_device *dev, u32 reg, u32 *val)
+{
+       int ret = ksz_spi_read(dev, reg, (u8 *)val, 4);
+
+       if (!ret)
+               *val = be32_to_cpu(*val);
+
+       return ret;
+}
+
+static int ksz_spi_write8(struct ksz_device *dev, u32 reg, u8 value)
+{
+       return ksz_spi_write(dev, reg, &value, 1);
+}
+
+static int ksz_spi_write16(struct ksz_device *dev, u32 reg, u16 value)
+{
+       value = cpu_to_be16(value);
+       return ksz_spi_write(dev, reg, &value, 2);
+}
+
+static int ksz_spi_write32(struct ksz_device *dev, u32 reg, u32 value)
+{
+       value = cpu_to_be32(value);
+       return ksz_spi_write(dev, reg, &value, 4);
+}
+
+static int ksz_spi_get(struct ksz_device *dev, u32 reg, void *data, size_t len)
+{
+       return ksz_spi_read(dev, reg, data, len);
+}
+
+static int ksz_spi_set(struct ksz_device *dev, u32 reg, void *data, size_t len)
+{
+       return ksz_spi_write(dev, reg, data, len);
+}
+
+#endif
index 65f10fe..0b3e51f 100644 (file)
@@ -116,8 +116,7 @@ static int mv88e6060_switch_reset(struct dsa_switch *ds)
        /* Reset the switch. */
        REG_WRITE(REG_GLOBAL, GLOBAL_ATU_CONTROL,
                  GLOBAL_ATU_CONTROL_SWRESET |
-                 GLOBAL_ATU_CONTROL_ATUSIZE_1024 |
-                 GLOBAL_ATU_CONTROL_ATE_AGE_5MIN);
+                 GLOBAL_ATU_CONTROL_LEARNDIS);
 
        /* Wait up to one second for reset to complete. */
        timeout = jiffies + 1 * HZ;
@@ -142,13 +141,10 @@ static int mv88e6060_setup_global(struct dsa_switch *ds)
         */
        REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, GLOBAL_CONTROL_MAX_FRAME_1536);
 
-       /* Enable automatic address learning, set the address
-        * database size to 1024 entries, and set the default aging
-        * time to 5 minutes.
+       /* Disable automatic address learning.
         */
        REG_WRITE(REG_GLOBAL, GLOBAL_ATU_CONTROL,
-                 GLOBAL_ATU_CONTROL_ATUSIZE_1024 |
-                 GLOBAL_ATU_CONTROL_ATE_AGE_5MIN);
+                 GLOBAL_ATU_CONTROL_LEARNDIS);
 
        return 0;
 }
index fc0f508..8a517d8 100644 (file)
@@ -1124,7 +1124,7 @@ static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
        u16 *p = _p;
        int i;
 
-       regs->version = 0;
+       regs->version = chip->info->prod_num;
 
        memset(p, 0xff, 32 * sizeof(u16));
 
@@ -2524,11 +2524,22 @@ static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
        mutex_unlock(&chip->reg_lock);
 
        if (reg == MII_PHYSID2) {
-               /* Some internal PHYS don't have a model number.  Use
-                * the mv88e6390 family model number instead.
-                */
-               if (!(val & 0x3f0))
-                       val |= MV88E6XXX_PORT_SWITCH_ID_PROD_6390 >> 4;
+               /* Some internal PHYs don't have a model number. */
+               if (chip->info->family != MV88E6XXX_FAMILY_6165)
+                       /* Then there is the 6165 family. It gets is
+                        * PHYs correct. But it can also have two
+                        * SERDES interfaces in the PHY address
+                        * space. And these don't have a model
+                        * number. But they are not PHYs, so we don't
+                        * want to give them something a PHY driver
+                        * will recognise.
+                        *
+                        * Use the mv88e6390 family model number
+                        * instead, for anything which really could be
+                        * a PHY,
+                        */
+                       if (!(val & 0x3f0))
+                               val |= MV88E6XXX_PORT_SWITCH_ID_PROD_6390 >> 4;
        }
 
        return err ? err : val;
@@ -3467,6 +3478,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
        .stats_get_stats = mv88e6320_stats_get_stats,
        .set_cpu_port = mv88e6095_g1_set_cpu_port,
        .set_egress_port = mv88e6095_g1_set_egress_port,
+       .watchdog_ops = &mv88e6390_watchdog_ops,
        .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu,
        .pot_clear = mv88e6xxx_g2_pot_clear,
        .reset = mv88e6352_g1_reset,
@@ -3509,6 +3521,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
        .stats_get_stats = mv88e6320_stats_get_stats,
        .set_cpu_port = mv88e6095_g1_set_cpu_port,
        .set_egress_port = mv88e6095_g1_set_egress_port,
+       .watchdog_ops = &mv88e6390_watchdog_ops,
        .reset = mv88e6352_g1_reset,
        .vtu_getnext = mv88e6185_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
index 5bc1683..40f421d 100644 (file)
@@ -1151,7 +1151,7 @@ static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq,
 
        print_info = (vortex_debug > 1);
        if (print_info)
-               pr_info("See Documentation/networking/vortex.txt\n");
+               pr_info("See Documentation/networking/device_drivers/3com/vortex.txt\n");
 
        pr_info("%s: 3Com %s %s at %p.\n",
               print_name,
@@ -1956,7 +1956,7 @@ vortex_error(struct net_device *dev, int status)
                                   dev->name, tx_status);
                        if (tx_status == 0x82) {
                                pr_err("Probably a duplex mismatch.  See "
-                                               "Documentation/networking/vortex.txt\n");
+                                               "Documentation/networking/device_drivers/3com/vortex.txt\n");
                        }
                        dump_tx_ring(dev);
                }
index 5c3ef9f..0ac44ef 100644 (file)
@@ -75,8 +75,9 @@ config VORTEX
          "Hurricane" (3c555/3cSOHO)                           PCI
 
          If you have such a card, say Y here.  More specific information is in
-         <file:Documentation/networking/vortex.txt> and in the comments at
-         the beginning of <file:drivers/net/ethernet/3com/3c59x.c>.
+         <file:Documentation/networking/device_drivers/3com/vortex.txt> and
+         in the comments at the beginning of
+         <file:drivers/net/ethernet/3com/3c59x.c>.
 
          To compile this support as a module, choose M here.
 
index 18956e7..a70bb1b 100644 (file)
@@ -1848,6 +1848,8 @@ static void ena_down(struct ena_adapter *adapter)
                rc = ena_com_dev_reset(adapter->ena_dev, adapter->reset_reason);
                if (rc)
                        dev_err(&adapter->pdev->dev, "Device reset failed\n");
+               /* stop submitting admin commands on a device that was reset */
+               ena_com_set_admin_running_state(adapter->ena_dev, false);
        }
 
        ena_destroy_all_io_queues(adapter);
@@ -1914,6 +1916,9 @@ static int ena_close(struct net_device *netdev)
 
        netif_dbg(adapter, ifdown, netdev, "%s\n", __func__);
 
+       if (!test_bit(ENA_FLAG_DEVICE_RUNNING, &adapter->flags))
+               return 0;
+
        if (test_bit(ENA_FLAG_DEV_UP, &adapter->flags))
                ena_down(adapter);
 
@@ -2613,9 +2618,7 @@ static void ena_destroy_device(struct ena_adapter *adapter, bool graceful)
                ena_down(adapter);
 
        /* Stop the device from sending AENQ events (in case reset flag is set
-        *  and device is up, ena_close already reset the device
-        * In case the reset flag is set and the device is up, ena_down()
-        * already perform the reset, so it can be skipped.
+        *  and device is up, ena_down() already reset the device.
         */
        if (!(test_bit(ENA_FLAG_TRIGGER_RESET, &adapter->flags) && dev_up))
                ena_com_dev_reset(adapter->ena_dev, adapter->reset_reason);
@@ -2694,8 +2697,8 @@ err_device_destroy:
        ena_com_abort_admin_commands(ena_dev);
        ena_com_wait_for_abort_completion(ena_dev);
        ena_com_admin_destroy(ena_dev);
-       ena_com_mmio_reg_read_request_destroy(ena_dev);
        ena_com_dev_reset(ena_dev, ENA_REGS_RESET_DRIVER_INVALID_STATE);
+       ena_com_mmio_reg_read_request_destroy(ena_dev);
 err:
        clear_bit(ENA_FLAG_DEVICE_RUNNING, &adapter->flags);
        clear_bit(ENA_FLAG_ONGOING_RESET, &adapter->flags);
@@ -3452,6 +3455,8 @@ err_rss:
        ena_com_rss_destroy(ena_dev);
 err_free_msix:
        ena_com_dev_reset(ena_dev, ENA_REGS_RESET_INIT_ERR);
+       /* stop submitting admin commands on a device that was reset */
+       ena_com_set_admin_running_state(ena_dev, false);
        ena_free_mgmnt_irq(adapter);
        ena_disable_msix(adapter);
 err_worker_destroy:
@@ -3498,18 +3503,12 @@ static void ena_remove(struct pci_dev *pdev)
 
        cancel_work_sync(&adapter->reset_task);
 
-       unregister_netdev(netdev);
-
-       /* If the device is running then we want to make sure the device will be
-        * reset to make sure no more events will be issued by the device.
-        */
-       if (test_bit(ENA_FLAG_DEVICE_RUNNING, &adapter->flags))
-               set_bit(ENA_FLAG_TRIGGER_RESET, &adapter->flags);
-
        rtnl_lock();
        ena_destroy_device(adapter, true);
        rtnl_unlock();
 
+       unregister_netdev(netdev);
+
        free_netdev(netdev);
 
        ena_com_rss_destroy(ena_dev);
index 5218736..dc8b617 100644 (file)
@@ -45,7 +45,7 @@
 
 #define DRV_MODULE_VER_MAJOR   2
 #define DRV_MODULE_VER_MINOR   0
-#define DRV_MODULE_VER_SUBMINOR 1
+#define DRV_MODULE_VER_SUBMINOR 2
 
 #define DRV_MODULE_NAME                "ena"
 #ifndef DRV_MODULE_VERSION
index 7c1eb30..e833d1b 100644 (file)
@@ -940,11 +940,8 @@ static int au1000_open(struct net_device *dev)
                return retval;
        }
 
-       if (dev->phydev) {
-               /* cause the PHY state machine to schedule a link state check */
-               dev->phydev->state = PHY_CHANGELINK;
+       if (dev->phydev)
                phy_start(dev->phydev);
-       }
 
        netif_start_queue(dev);
 
index b4fc0ed..bd6589d 100644 (file)
@@ -1419,7 +1419,7 @@ static int sparc_lance_probe_one(struct platform_device *op,
 
                        prop = of_get_property(nd, "tpe-link-test?", NULL);
                        if (!prop)
-                               goto no_link_test;
+                               goto node_put;
 
                        if (strcmp(prop, "true")) {
                                printk(KERN_NOTICE "SunLance: warning: overriding option "
@@ -1428,6 +1428,8 @@ static int sparc_lance_probe_one(struct platform_device *op,
                                       "to ecd@skynet.be\n");
                                auxio_set_lte(AUXIO_LTE_ON);
                        }
+node_put:
+                       of_node_put(nd);
 no_link_test:
                        lp->auto_select = 1;
                        lp->tpe = 0;
@@ -1486,9 +1488,9 @@ static int sunlance_sbus_probe(struct platform_device *op)
        struct device_node *parent_dp = parent->dev.of_node;
        int err;
 
-       if (!strcmp(parent_dp->name, "ledma")) {
+       if (of_node_name_eq(parent_dp, "ledma")) {
                err = sparc_lance_probe_one(op, parent, NULL);
-       } else if (!strcmp(parent_dp->name, "lebuffer")) {
+       } else if (of_node_name_eq(parent_dp, "lebuffer")) {
                err = sparc_lance_probe_one(op, NULL, parent);
        } else
                err = sparc_lance_probe_one(op, NULL, NULL);
index 3b889ef..50dd6bf 100644 (file)
@@ -29,9 +29,6 @@
 #define RES_RING_CSR   1
 #define RES_RING_CMD   2
 
-static const struct of_device_id xgene_enet_of_match[];
-static const struct acpi_device_id xgene_enet_acpi_match[];
-
 static void xgene_enet_init_bufpool(struct xgene_enet_desc_ring *buf_pool)
 {
        struct xgene_enet_raw_desc16 *raw_desc;
index 686f6d8..4556630 100644 (file)
@@ -36,6 +36,7 @@ atlantic-objs := aq_main.o \
        aq_ring.o \
        aq_hw_utils.o \
        aq_ethtool.o \
+       aq_filters.o \
        hw_atl/hw_atl_a0.o \
        hw_atl/hw_atl_b0.o \
        hw_atl/hw_atl_utils.o \
index 91eb891..3944ce7 100644 (file)
@@ -12,7 +12,7 @@
 #ifndef AQ_CFG_H
 #define AQ_CFG_H
 
-#define AQ_CFG_VECS_DEF   4U
+#define AQ_CFG_VECS_DEF   8U
 #define AQ_CFG_TCS_DEF    1U
 
 #define AQ_CFG_TXDS_DEF    4096U
@@ -42,8 +42,8 @@
 #define AQ_CFG_IS_LRO_DEF           1U
 
 /* RSS */
-#define AQ_CFG_RSS_INDIRECTION_TABLE_MAX  128U
-#define AQ_CFG_RSS_HASHKEY_SIZE           320U
+#define AQ_CFG_RSS_INDIRECTION_TABLE_MAX  64U
+#define AQ_CFG_RSS_HASHKEY_SIZE           40U
 
 #define AQ_CFG_IS_RSS_DEF           1U
 #define AQ_CFG_NUM_RSS_QUEUES_DEF   AQ_CFG_VECS_DEF
index becb578..6b6d172 100644 (file)
@@ -14,7 +14,7 @@
 
 #include <linux/etherdevice.h>
 #include <linux/pci.h>
-
+#include <linux/if_vlan.h>
 #include "ver.h"
 #include "aq_cfg.h"
 #include "aq_utils.h"
index 99ef1da..38e87ee 100644 (file)
@@ -12,6 +12,7 @@
 #include "aq_ethtool.h"
 #include "aq_nic.h"
 #include "aq_vec.h"
+#include "aq_filters.h"
 
 static void aq_ethtool_get_regs(struct net_device *ndev,
                                struct ethtool_regs *regs, void *p)
@@ -201,6 +202,41 @@ static int aq_ethtool_get_rss(struct net_device *ndev, u32 *indir, u8 *key,
        return 0;
 }
 
+static int aq_ethtool_set_rss(struct net_device *netdev, const u32 *indir,
+                             const u8 *key, const u8 hfunc)
+{
+       struct aq_nic_s *aq_nic = netdev_priv(netdev);
+       struct aq_nic_cfg_s *cfg;
+       unsigned int i = 0U;
+       u32 rss_entries;
+       int err = 0;
+
+       cfg = aq_nic_get_cfg(aq_nic);
+       rss_entries = cfg->aq_rss.indirection_table_size;
+
+       /* We do not allow change in unsupported parameters */
+       if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+               return -EOPNOTSUPP;
+       /* Fill out the redirection table */
+       if (indir)
+               for (i = 0; i < rss_entries; i++)
+                       cfg->aq_rss.indirection_table[i] = indir[i];
+
+       /* Fill out the rss hash key */
+       if (key) {
+               memcpy(cfg->aq_rss.hash_secret_key, key,
+                      sizeof(cfg->aq_rss.hash_secret_key));
+               err = aq_nic->aq_hw_ops->hw_rss_hash_set(aq_nic->aq_hw,
+                       &cfg->aq_rss);
+               if (err)
+                       return err;
+       }
+
+       err = aq_nic->aq_hw_ops->hw_rss_set(aq_nic->aq_hw, &cfg->aq_rss);
+
+       return err;
+}
+
 static int aq_ethtool_get_rxnfc(struct net_device *ndev,
                                struct ethtool_rxnfc *cmd,
                                u32 *rule_locs)
@@ -213,7 +249,36 @@ static int aq_ethtool_get_rxnfc(struct net_device *ndev,
        case ETHTOOL_GRXRINGS:
                cmd->data = cfg->vecs;
                break;
+       case ETHTOOL_GRXCLSRLCNT:
+               cmd->rule_cnt = aq_get_rxnfc_count_all_rules(aq_nic);
+               break;
+       case ETHTOOL_GRXCLSRULE:
+               err = aq_get_rxnfc_rule(aq_nic, cmd);
+               break;
+       case ETHTOOL_GRXCLSRLALL:
+               err = aq_get_rxnfc_all_rules(aq_nic, cmd, rule_locs);
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
+static int aq_ethtool_set_rxnfc(struct net_device *ndev,
+                               struct ethtool_rxnfc *cmd)
+{
+       int err = 0;
+       struct aq_nic_s *aq_nic = netdev_priv(ndev);
 
+       switch (cmd->cmd) {
+       case ETHTOOL_SRXCLSRLINS:
+               err = aq_add_rxnfc_rule(aq_nic, cmd);
+               break;
+       case ETHTOOL_SRXCLSRLDEL:
+               err = aq_del_rxnfc_rule(aq_nic, cmd);
+               break;
        default:
                err = -EOPNOTSUPP;
                break;
@@ -495,7 +560,7 @@ static int aq_set_ringparam(struct net_device *ndev,
                }
        }
        if (ndev_running)
-               err = dev_open(ndev);
+               err = dev_open(ndev, NULL);
 
 err_exit:
        return err;
@@ -519,7 +584,9 @@ const struct ethtool_ops aq_ethtool_ops = {
        .set_pauseparam      = aq_ethtool_set_pauseparam,
        .get_rxfh_key_size   = aq_ethtool_get_rss_key_size,
        .get_rxfh            = aq_ethtool_get_rss,
+       .set_rxfh            = aq_ethtool_set_rss,
        .get_rxnfc           = aq_ethtool_get_rxnfc,
+       .set_rxnfc           = aq_ethtool_set_rxnfc,
        .get_sset_count      = aq_ethtool_get_sset_count,
        .get_ethtool_stats   = aq_ethtool_stats,
        .get_link_ksettings  = aq_ethtool_get_link_ksettings,
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_filters.c b/drivers/net/ethernet/aquantia/atlantic/aq_filters.c
new file mode 100644 (file)
index 0000000..18bc035
--- /dev/null
@@ -0,0 +1,876 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2014-2017 aQuantia Corporation. */
+
+/* File aq_filters.c: RX filters related functions. */
+
+#include "aq_filters.h"
+
+static bool __must_check
+aq_rule_is_approve(struct ethtool_rx_flow_spec *fsp)
+{
+       if (fsp->flow_type & FLOW_MAC_EXT)
+               return false;
+
+       switch (fsp->flow_type & ~FLOW_EXT) {
+       case ETHER_FLOW:
+       case TCP_V4_FLOW:
+       case UDP_V4_FLOW:
+       case SCTP_V4_FLOW:
+       case TCP_V6_FLOW:
+       case UDP_V6_FLOW:
+       case SCTP_V6_FLOW:
+       case IPV4_FLOW:
+       case IPV6_FLOW:
+               return true;
+       case IP_USER_FLOW:
+               switch (fsp->h_u.usr_ip4_spec.proto) {
+               case IPPROTO_TCP:
+               case IPPROTO_UDP:
+               case IPPROTO_SCTP:
+               case IPPROTO_IP:
+                       return true;
+               default:
+                       return false;
+                       }
+       case IPV6_USER_FLOW:
+               switch (fsp->h_u.usr_ip6_spec.l4_proto) {
+               case IPPROTO_TCP:
+               case IPPROTO_UDP:
+               case IPPROTO_SCTP:
+               case IPPROTO_IP:
+                       return true;
+               default:
+                       return false;
+                       }
+       default:
+               return false;
+       }
+
+       return false;
+}
+
+static bool __must_check
+aq_match_filter(struct ethtool_rx_flow_spec *fsp1,
+               struct ethtool_rx_flow_spec *fsp2)
+{
+       if (fsp1->flow_type != fsp2->flow_type ||
+           memcmp(&fsp1->h_u, &fsp2->h_u, sizeof(fsp2->h_u)) ||
+           memcmp(&fsp1->h_ext, &fsp2->h_ext, sizeof(fsp2->h_ext)) ||
+           memcmp(&fsp1->m_u, &fsp2->m_u, sizeof(fsp2->m_u)) ||
+           memcmp(&fsp1->m_ext, &fsp2->m_ext, sizeof(fsp2->m_ext)))
+               return false;
+
+       return true;
+}
+
+static bool __must_check
+aq_rule_already_exists(struct aq_nic_s *aq_nic,
+                      struct ethtool_rx_flow_spec *fsp)
+{
+       struct aq_rx_filter *rule;
+       struct hlist_node *aq_node2;
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               if (rule->aq_fsp.location == fsp->location)
+                       continue;
+               if (aq_match_filter(&rule->aq_fsp, fsp)) {
+                       netdev_err(aq_nic->ndev,
+                                  "ethtool: This filter is already set\n");
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static int aq_check_approve_fl3l4(struct aq_nic_s *aq_nic,
+                                 struct aq_hw_rx_fltrs_s *rx_fltrs,
+                                 struct ethtool_rx_flow_spec *fsp)
+{
+       if (fsp->location < AQ_RX_FIRST_LOC_FL3L4 ||
+           fsp->location > AQ_RX_LAST_LOC_FL3L4) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: location must be in range [%d, %d]",
+                          AQ_RX_FIRST_LOC_FL3L4,
+                          AQ_RX_LAST_LOC_FL3L4);
+               return -EINVAL;
+       }
+       if (rx_fltrs->fl3l4.is_ipv6 && rx_fltrs->fl3l4.active_ipv4) {
+               rx_fltrs->fl3l4.is_ipv6 = false;
+               netdev_err(aq_nic->ndev,
+                          "ethtool: mixing ipv4 and ipv6 is not allowed");
+               return -EINVAL;
+       } else if (!rx_fltrs->fl3l4.is_ipv6 && rx_fltrs->fl3l4.active_ipv6) {
+               rx_fltrs->fl3l4.is_ipv6 = true;
+               netdev_err(aq_nic->ndev,
+                          "ethtool: mixing ipv4 and ipv6 is not allowed");
+               return -EINVAL;
+       } else if (rx_fltrs->fl3l4.is_ipv6                    &&
+                  fsp->location != AQ_RX_FIRST_LOC_FL3L4 + 4 &&
+                  fsp->location != AQ_RX_FIRST_LOC_FL3L4) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: The specified location for ipv6 must be %d or %d",
+                          AQ_RX_FIRST_LOC_FL3L4, AQ_RX_FIRST_LOC_FL3L4 + 4);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __must_check
+aq_check_approve_fl2(struct aq_nic_s *aq_nic,
+                    struct aq_hw_rx_fltrs_s *rx_fltrs,
+                    struct ethtool_rx_flow_spec *fsp)
+{
+       if (fsp->location < AQ_RX_FIRST_LOC_FETHERT ||
+           fsp->location > AQ_RX_LAST_LOC_FETHERT) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: location must be in range [%d, %d]",
+                          AQ_RX_FIRST_LOC_FETHERT,
+                          AQ_RX_LAST_LOC_FETHERT);
+               return -EINVAL;
+       }
+
+       if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_PRIO_MASK &&
+           fsp->m_u.ether_spec.h_proto == 0U) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: proto (ether_type) parameter must be specified");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __must_check
+aq_check_approve_fvlan(struct aq_nic_s *aq_nic,
+                      struct aq_hw_rx_fltrs_s *rx_fltrs,
+                      struct ethtool_rx_flow_spec *fsp)
+{
+       if (fsp->location < AQ_RX_FIRST_LOC_FVLANID ||
+           fsp->location > AQ_RX_LAST_LOC_FVLANID) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: location must be in range [%d, %d]",
+                          AQ_RX_FIRST_LOC_FVLANID,
+                          AQ_RX_LAST_LOC_FVLANID);
+               return -EINVAL;
+       }
+
+       if ((aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) &&
+           (!test_bit(be16_to_cpu(fsp->h_ext.vlan_tci),
+                      aq_nic->active_vlans))) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: unknown vlan-id specified");
+               return -EINVAL;
+       }
+
+       if (fsp->ring_cookie > aq_nic->aq_nic_cfg.num_rss_queues) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: queue number must be in range [0, %d]",
+                          aq_nic->aq_nic_cfg.num_rss_queues - 1);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int __must_check
+aq_check_filter(struct aq_nic_s *aq_nic,
+               struct ethtool_rx_flow_spec *fsp)
+{
+       int err = 0;
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+
+       if (fsp->flow_type & FLOW_EXT) {
+               if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_VID_MASK) {
+                       err = aq_check_approve_fvlan(aq_nic, rx_fltrs, fsp);
+               } else if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_PRIO_MASK) {
+                       err = aq_check_approve_fl2(aq_nic, rx_fltrs, fsp);
+               } else {
+                       netdev_err(aq_nic->ndev,
+                                  "ethtool: invalid vlan mask 0x%x specified",
+                                  be16_to_cpu(fsp->m_ext.vlan_tci));
+                       err = -EINVAL;
+               }
+       } else {
+               switch (fsp->flow_type & ~FLOW_EXT) {
+               case ETHER_FLOW:
+                       err = aq_check_approve_fl2(aq_nic, rx_fltrs, fsp);
+                       break;
+               case TCP_V4_FLOW:
+               case UDP_V4_FLOW:
+               case SCTP_V4_FLOW:
+               case IPV4_FLOW:
+               case IP_USER_FLOW:
+                       rx_fltrs->fl3l4.is_ipv6 = false;
+                       err = aq_check_approve_fl3l4(aq_nic, rx_fltrs, fsp);
+                       break;
+               case TCP_V6_FLOW:
+               case UDP_V6_FLOW:
+               case SCTP_V6_FLOW:
+               case IPV6_FLOW:
+               case IPV6_USER_FLOW:
+                       rx_fltrs->fl3l4.is_ipv6 = true;
+                       err = aq_check_approve_fl3l4(aq_nic, rx_fltrs, fsp);
+                       break;
+               default:
+                       netdev_err(aq_nic->ndev,
+                                  "ethtool: unknown flow-type specified");
+                       err = -EINVAL;
+               }
+       }
+
+       return err;
+}
+
+static bool __must_check
+aq_rule_is_not_support(struct aq_nic_s *aq_nic,
+                      struct ethtool_rx_flow_spec *fsp)
+{
+       bool rule_is_not_support = false;
+
+       if (!(aq_nic->ndev->features & NETIF_F_NTUPLE)) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: Please, to enable the RX flow control:\n"
+                          "ethtool -K %s ntuple on\n", aq_nic->ndev->name);
+               rule_is_not_support = true;
+       } else if (!aq_rule_is_approve(fsp)) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: The specified flow type is not supported\n");
+               rule_is_not_support = true;
+       } else if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW &&
+                  (fsp->h_u.tcp_ip4_spec.tos ||
+                   fsp->h_u.tcp_ip6_spec.tclass)) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: The specified tos tclass are not supported\n");
+               rule_is_not_support = true;
+       } else if (fsp->flow_type & FLOW_MAC_EXT) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: MAC_EXT is not supported");
+               rule_is_not_support = true;
+       }
+
+       return rule_is_not_support;
+}
+
+static bool __must_check
+aq_rule_is_not_correct(struct aq_nic_s *aq_nic,
+                      struct ethtool_rx_flow_spec *fsp)
+{
+       bool rule_is_not_correct = false;
+
+       if (!aq_nic) {
+               rule_is_not_correct = true;
+       } else if (fsp->location > AQ_RX_MAX_RXNFC_LOC) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: The specified number %u rule is invalid\n",
+                          fsp->location);
+               rule_is_not_correct = true;
+       } else if (aq_check_filter(aq_nic, fsp)) {
+               rule_is_not_correct = true;
+       } else if (fsp->ring_cookie != RX_CLS_FLOW_DISC) {
+               if (fsp->ring_cookie >= aq_nic->aq_nic_cfg.num_rss_queues) {
+                       netdev_err(aq_nic->ndev,
+                                  "ethtool: The specified action is invalid.\n"
+                                  "Maximum allowable value action is %u.\n",
+                                  aq_nic->aq_nic_cfg.num_rss_queues - 1);
+                       rule_is_not_correct = true;
+               }
+       }
+
+       return rule_is_not_correct;
+}
+
+static int __must_check
+aq_check_rule(struct aq_nic_s *aq_nic,
+             struct ethtool_rx_flow_spec *fsp)
+{
+       int err = 0;
+
+       if (aq_rule_is_not_correct(aq_nic, fsp))
+               err = -EINVAL;
+       else if (aq_rule_is_not_support(aq_nic, fsp))
+               err = -EOPNOTSUPP;
+       else if (aq_rule_already_exists(aq_nic, fsp))
+               err = -EEXIST;
+
+       return err;
+}
+
+static void aq_set_data_fl2(struct aq_nic_s *aq_nic,
+                           struct aq_rx_filter *aq_rx_fltr,
+                           struct aq_rx_filter_l2 *data, bool add)
+{
+       const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp;
+
+       memset(data, 0, sizeof(*data));
+
+       data->location = fsp->location - AQ_RX_FIRST_LOC_FETHERT;
+
+       if (fsp->ring_cookie != RX_CLS_FLOW_DISC)
+               data->queue = fsp->ring_cookie;
+       else
+               data->queue = -1;
+
+       data->ethertype = be16_to_cpu(fsp->h_u.ether_spec.h_proto);
+       data->user_priority_en = be16_to_cpu(fsp->m_ext.vlan_tci)
+                                == VLAN_PRIO_MASK;
+       data->user_priority = (be16_to_cpu(fsp->h_ext.vlan_tci)
+                              & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+}
+
+static int aq_add_del_fether(struct aq_nic_s *aq_nic,
+                            struct aq_rx_filter *aq_rx_fltr, bool add)
+{
+       struct aq_rx_filter_l2 data;
+       struct aq_hw_s *aq_hw = aq_nic->aq_hw;
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+
+       aq_set_data_fl2(aq_nic, aq_rx_fltr, &data, add);
+
+       if (unlikely(!aq_hw_ops->hw_filter_l2_set))
+               return -EOPNOTSUPP;
+       if (unlikely(!aq_hw_ops->hw_filter_l2_clear))
+               return -EOPNOTSUPP;
+
+       if (add)
+               return aq_hw_ops->hw_filter_l2_set(aq_hw, &data);
+       else
+               return aq_hw_ops->hw_filter_l2_clear(aq_hw, &data);
+}
+
+static bool aq_fvlan_is_busy(struct aq_rx_filter_vlan *aq_vlans, int vlan)
+{
+       int i;
+
+       for (i = 0; i < AQ_VLAN_MAX_FILTERS; ++i) {
+               if (aq_vlans[i].enable &&
+                   aq_vlans[i].queue != AQ_RX_QUEUE_NOT_ASSIGNED &&
+                   aq_vlans[i].vlan_id == vlan) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+/* Function rebuilds array of vlan filters so that filters with assigned
+ * queue have a precedence over just vlans on the interface.
+ */
+static void aq_fvlan_rebuild(struct aq_nic_s *aq_nic,
+                            unsigned long *active_vlans,
+                            struct aq_rx_filter_vlan *aq_vlans)
+{
+       bool vlan_busy = false;
+       int vlan = -1;
+       int i;
+
+       for (i = 0; i < AQ_VLAN_MAX_FILTERS; ++i) {
+               if (aq_vlans[i].enable &&
+                   aq_vlans[i].queue != AQ_RX_QUEUE_NOT_ASSIGNED)
+                       continue;
+               do {
+                       vlan = find_next_bit(active_vlans,
+                                            VLAN_N_VID,
+                                            vlan + 1);
+                       if (vlan == VLAN_N_VID) {
+                               aq_vlans[i].enable = 0U;
+                               aq_vlans[i].queue = AQ_RX_QUEUE_NOT_ASSIGNED;
+                               aq_vlans[i].vlan_id = 0;
+                               continue;
+                       }
+
+                       vlan_busy = aq_fvlan_is_busy(aq_vlans, vlan);
+                       if (!vlan_busy) {
+                               aq_vlans[i].enable = 1U;
+                               aq_vlans[i].queue = AQ_RX_QUEUE_NOT_ASSIGNED;
+                               aq_vlans[i].vlan_id = vlan;
+                       }
+               } while (vlan_busy && vlan != VLAN_N_VID);
+       }
+}
+
+static int aq_set_data_fvlan(struct aq_nic_s *aq_nic,
+                            struct aq_rx_filter *aq_rx_fltr,
+                            struct aq_rx_filter_vlan *aq_vlans, bool add)
+{
+       const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp;
+       int location = fsp->location - AQ_RX_FIRST_LOC_FVLANID;
+       int i;
+
+       memset(&aq_vlans[location], 0, sizeof(aq_vlans[location]));
+
+       if (!add)
+               return 0;
+
+       /* remove vlan if it was in table without queue assignment */
+       for (i = 0; i < AQ_VLAN_MAX_FILTERS; ++i) {
+               if (aq_vlans[i].vlan_id ==
+                  (be16_to_cpu(fsp->h_ext.vlan_tci) & VLAN_VID_MASK)) {
+                       aq_vlans[i].enable = false;
+               }
+       }
+
+       aq_vlans[location].location = location;
+       aq_vlans[location].vlan_id = be16_to_cpu(fsp->h_ext.vlan_tci)
+                                    & VLAN_VID_MASK;
+       aq_vlans[location].queue = fsp->ring_cookie & 0x1FU;
+       aq_vlans[location].enable = 1U;
+
+       return 0;
+}
+
+int aq_del_fvlan_by_vlan(struct aq_nic_s *aq_nic, u16 vlan_id)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct aq_rx_filter *rule = NULL;
+       struct hlist_node *aq_node2;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               if (be16_to_cpu(rule->aq_fsp.h_ext.vlan_tci) == vlan_id)
+                       break;
+       }
+       if (rule && be16_to_cpu(rule->aq_fsp.h_ext.vlan_tci) == vlan_id) {
+               struct ethtool_rxnfc cmd;
+
+               cmd.fs.location = rule->aq_fsp.location;
+               return aq_del_rxnfc_rule(aq_nic, &cmd);
+       }
+
+       return -ENOENT;
+}
+
+static int aq_add_del_fvlan(struct aq_nic_s *aq_nic,
+                           struct aq_rx_filter *aq_rx_fltr, bool add)
+{
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+
+       if (unlikely(!aq_hw_ops->hw_filter_vlan_set))
+               return -EOPNOTSUPP;
+
+       aq_set_data_fvlan(aq_nic,
+                         aq_rx_fltr,
+                         aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans,
+                         add);
+
+       return aq_filters_vlans_update(aq_nic);
+}
+
+static int aq_set_data_fl3l4(struct aq_nic_s *aq_nic,
+                            struct aq_rx_filter *aq_rx_fltr,
+                            struct aq_rx_filter_l3l4 *data, bool add)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp;
+
+       memset(data, 0, sizeof(*data));
+
+       data->is_ipv6 = rx_fltrs->fl3l4.is_ipv6;
+       data->location = HW_ATL_GET_REG_LOCATION_FL3L4(fsp->location);
+
+       if (!add) {
+               if (!data->is_ipv6)
+                       rx_fltrs->fl3l4.active_ipv4 &= ~BIT(data->location);
+               else
+                       rx_fltrs->fl3l4.active_ipv6 &=
+                               ~BIT((data->location) / 4);
+
+               return 0;
+       }
+
+       data->cmd |= HW_ATL_RX_ENABLE_FLTR_L3L4;
+
+       switch (fsp->flow_type) {
+       case TCP_V4_FLOW:
+       case TCP_V6_FLOW:
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4;
+               break;
+       case UDP_V4_FLOW:
+       case UDP_V6_FLOW:
+               data->cmd |= HW_ATL_RX_UDP;
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4;
+               break;
+       case SCTP_V4_FLOW:
+       case SCTP_V6_FLOW:
+               data->cmd |= HW_ATL_RX_SCTP;
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4;
+               break;
+       default:
+               break;
+       }
+
+       if (!data->is_ipv6) {
+               data->ip_src[0] =
+                       ntohl(fsp->h_u.tcp_ip4_spec.ip4src);
+               data->ip_dst[0] =
+                       ntohl(fsp->h_u.tcp_ip4_spec.ip4dst);
+               rx_fltrs->fl3l4.active_ipv4 |= BIT(data->location);
+       } else {
+               int i;
+
+               rx_fltrs->fl3l4.active_ipv6 |= BIT((data->location) / 4);
+               for (i = 0; i < HW_ATL_RX_CNT_REG_ADDR_IPV6; ++i) {
+                       data->ip_dst[i] =
+                               ntohl(fsp->h_u.tcp_ip6_spec.ip6dst[i]);
+                       data->ip_src[i] =
+                               ntohl(fsp->h_u.tcp_ip6_spec.ip6src[i]);
+               }
+               data->cmd |= HW_ATL_RX_ENABLE_L3_IPV6;
+       }
+       if (fsp->flow_type != IP_USER_FLOW &&
+           fsp->flow_type != IPV6_USER_FLOW) {
+               if (!data->is_ipv6) {
+                       data->p_dst =
+                               ntohs(fsp->h_u.tcp_ip4_spec.pdst);
+                       data->p_src =
+                               ntohs(fsp->h_u.tcp_ip4_spec.psrc);
+               } else {
+                       data->p_dst =
+                               ntohs(fsp->h_u.tcp_ip6_spec.pdst);
+                       data->p_src =
+                               ntohs(fsp->h_u.tcp_ip6_spec.psrc);
+               }
+       }
+       if (data->ip_src[0] && !data->is_ipv6)
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3;
+       if (data->ip_dst[0] && !data->is_ipv6)
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3;
+       if (data->p_dst)
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4;
+       if (data->p_src)
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_SRC_PORT_L4;
+       if (fsp->ring_cookie != RX_CLS_FLOW_DISC) {
+               data->cmd |= HW_ATL_RX_HOST << HW_ATL_RX_ACTION_FL3F4_SHIFT;
+               data->cmd |= fsp->ring_cookie << HW_ATL_RX_QUEUE_FL3L4_SHIFT;
+               data->cmd |= HW_ATL_RX_ENABLE_QUEUE_L3L4;
+       } else {
+               data->cmd |= HW_ATL_RX_DISCARD << HW_ATL_RX_ACTION_FL3F4_SHIFT;
+       }
+
+       return 0;
+}
+
+static int aq_set_fl3l4(struct aq_hw_s *aq_hw,
+                       const struct aq_hw_ops *aq_hw_ops,
+                       struct aq_rx_filter_l3l4 *data)
+{
+       if (unlikely(!aq_hw_ops->hw_filter_l3l4_set))
+               return -EOPNOTSUPP;
+
+       return aq_hw_ops->hw_filter_l3l4_set(aq_hw, data);
+}
+
+static int aq_add_del_fl3l4(struct aq_nic_s *aq_nic,
+                           struct aq_rx_filter *aq_rx_fltr, bool add)
+{
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+       struct aq_hw_s *aq_hw = aq_nic->aq_hw;
+       struct aq_rx_filter_l3l4 data;
+
+       if (unlikely(aq_rx_fltr->aq_fsp.location < AQ_RX_FIRST_LOC_FL3L4 ||
+                    aq_rx_fltr->aq_fsp.location > AQ_RX_LAST_LOC_FL3L4  ||
+                    aq_set_data_fl3l4(aq_nic, aq_rx_fltr, &data, add)))
+               return -EINVAL;
+
+       return aq_set_fl3l4(aq_hw, aq_hw_ops, &data);
+}
+
+static int aq_add_del_rule(struct aq_nic_s *aq_nic,
+                          struct aq_rx_filter *aq_rx_fltr, bool add)
+{
+       int err = -EINVAL;
+
+       if (aq_rx_fltr->aq_fsp.flow_type & FLOW_EXT) {
+               if (be16_to_cpu(aq_rx_fltr->aq_fsp.m_ext.vlan_tci)
+                   == VLAN_VID_MASK) {
+                       aq_rx_fltr->type = aq_rx_filter_vlan;
+                       err = aq_add_del_fvlan(aq_nic, aq_rx_fltr, add);
+               } else if (be16_to_cpu(aq_rx_fltr->aq_fsp.m_ext.vlan_tci)
+                       == VLAN_PRIO_MASK) {
+                       aq_rx_fltr->type = aq_rx_filter_ethertype;
+                       err = aq_add_del_fether(aq_nic, aq_rx_fltr, add);
+               }
+       } else {
+               switch (aq_rx_fltr->aq_fsp.flow_type & ~FLOW_EXT) {
+               case ETHER_FLOW:
+                       aq_rx_fltr->type = aq_rx_filter_ethertype;
+                       err = aq_add_del_fether(aq_nic, aq_rx_fltr, add);
+                       break;
+               case TCP_V4_FLOW:
+               case UDP_V4_FLOW:
+               case SCTP_V4_FLOW:
+               case IP_USER_FLOW:
+               case TCP_V6_FLOW:
+               case UDP_V6_FLOW:
+               case SCTP_V6_FLOW:
+               case IPV6_USER_FLOW:
+                       aq_rx_fltr->type = aq_rx_filter_l3l4;
+                       err = aq_add_del_fl3l4(aq_nic, aq_rx_fltr, add);
+                       break;
+               default:
+                       err = -EINVAL;
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static int aq_update_table_filters(struct aq_nic_s *aq_nic,
+                                  struct aq_rx_filter *aq_rx_fltr, u16 index,
+                                  struct ethtool_rxnfc *cmd)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct aq_rx_filter *rule = NULL, *parent = NULL;
+       struct hlist_node *aq_node2;
+       int err = -EINVAL;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               if (rule->aq_fsp.location >= index)
+                       break;
+               parent = rule;
+       }
+
+       if (rule && rule->aq_fsp.location == index) {
+               err = aq_add_del_rule(aq_nic, rule, false);
+               hlist_del(&rule->aq_node);
+               kfree(rule);
+               --rx_fltrs->active_filters;
+       }
+
+       if (unlikely(!aq_rx_fltr))
+               return err;
+
+       INIT_HLIST_NODE(&aq_rx_fltr->aq_node);
+
+       if (parent)
+               hlist_add_behind(&aq_rx_fltr->aq_node, &parent->aq_node);
+       else
+               hlist_add_head(&aq_rx_fltr->aq_node, &rx_fltrs->filter_list);
+
+       ++rx_fltrs->active_filters;
+
+       return 0;
+}
+
+u16 aq_get_rxnfc_count_all_rules(struct aq_nic_s *aq_nic)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+
+       return rx_fltrs->active_filters;
+}
+
+struct aq_hw_rx_fltrs_s *aq_get_hw_rx_fltrs(struct aq_nic_s *aq_nic)
+{
+       return &aq_nic->aq_hw_rx_fltrs;
+}
+
+int aq_add_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct ethtool_rx_flow_spec *fsp =
+               (struct ethtool_rx_flow_spec *)&cmd->fs;
+       struct aq_rx_filter *aq_rx_fltr;
+       int err = 0;
+
+       err = aq_check_rule(aq_nic, fsp);
+       if (err)
+               goto err_exit;
+
+       aq_rx_fltr = kzalloc(sizeof(*aq_rx_fltr), GFP_KERNEL);
+       if (unlikely(!aq_rx_fltr)) {
+               err = -ENOMEM;
+               goto err_exit;
+       }
+
+       memcpy(&aq_rx_fltr->aq_fsp, fsp, sizeof(*fsp));
+
+       err = aq_update_table_filters(aq_nic, aq_rx_fltr, fsp->location, NULL);
+       if (unlikely(err))
+               goto err_free;
+
+       err = aq_add_del_rule(aq_nic, aq_rx_fltr, true);
+       if (unlikely(err)) {
+               hlist_del(&aq_rx_fltr->aq_node);
+               --rx_fltrs->active_filters;
+               goto err_free;
+       }
+
+       return 0;
+
+err_free:
+       kfree(aq_rx_fltr);
+err_exit:
+       return err;
+}
+
+int aq_del_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct aq_rx_filter *rule = NULL;
+       struct hlist_node *aq_node2;
+       int err = -EINVAL;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               if (rule->aq_fsp.location == cmd->fs.location)
+                       break;
+       }
+
+       if (rule && rule->aq_fsp.location == cmd->fs.location) {
+               err = aq_add_del_rule(aq_nic, rule, false);
+               hlist_del(&rule->aq_node);
+               kfree(rule);
+               --rx_fltrs->active_filters;
+       }
+       return err;
+}
+
+int aq_get_rxnfc_rule(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct ethtool_rx_flow_spec *fsp =
+                       (struct ethtool_rx_flow_spec *)&cmd->fs;
+       struct aq_rx_filter *rule = NULL;
+       struct hlist_node *aq_node2;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node)
+               if (fsp->location <= rule->aq_fsp.location)
+                       break;
+
+       if (unlikely(!rule || fsp->location != rule->aq_fsp.location))
+               return -EINVAL;
+
+       memcpy(fsp, &rule->aq_fsp, sizeof(*fsp));
+
+       return 0;
+}
+
+int aq_get_rxnfc_all_rules(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd,
+                          u32 *rule_locs)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct hlist_node *aq_node2;
+       struct aq_rx_filter *rule;
+       int count = 0;
+
+       cmd->data = aq_get_rxnfc_count_all_rules(aq_nic);
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               if (unlikely(count == cmd->rule_cnt))
+                       return -EMSGSIZE;
+
+               rule_locs[count++] = rule->aq_fsp.location;
+       }
+
+       cmd->rule_cnt = count;
+
+       return 0;
+}
+
+int aq_clear_rxnfc_all_rules(struct aq_nic_s *aq_nic)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct hlist_node *aq_node2;
+       struct aq_rx_filter *rule;
+       int err = 0;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               err = aq_add_del_rule(aq_nic, rule, false);
+               if (err)
+                       goto err_exit;
+               hlist_del(&rule->aq_node);
+               kfree(rule);
+               --rx_fltrs->active_filters;
+       }
+
+err_exit:
+       return err;
+}
+
+int aq_reapply_rxnfc_all_rules(struct aq_nic_s *aq_nic)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct hlist_node *aq_node2;
+       struct aq_rx_filter *rule;
+       int err = 0;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               err = aq_add_del_rule(aq_nic, rule, true);
+               if (err)
+                       goto err_exit;
+       }
+
+err_exit:
+       return err;
+}
+
+int aq_filters_vlans_update(struct aq_nic_s *aq_nic)
+{
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+       struct aq_hw_s *aq_hw = aq_nic->aq_hw;
+       int hweight = 0;
+       int err = 0;
+       int i;
+
+       if (unlikely(!aq_hw_ops->hw_filter_vlan_set))
+               return -EOPNOTSUPP;
+       if (unlikely(!aq_hw_ops->hw_filter_vlan_ctrl))
+               return -EOPNOTSUPP;
+
+       aq_fvlan_rebuild(aq_nic, aq_nic->active_vlans,
+                        aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans);
+
+       if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+               for (i = 0; i < BITS_TO_LONGS(VLAN_N_VID); i++)
+                       hweight += hweight_long(aq_nic->active_vlans[i]);
+
+               err = aq_hw_ops->hw_filter_vlan_ctrl(aq_hw, false);
+               if (err)
+                       return err;
+       }
+
+       err = aq_hw_ops->hw_filter_vlan_set(aq_hw,
+                                           aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans
+                                          );
+       if (err)
+               return err;
+
+       if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+               if (hweight < AQ_VLAN_MAX_FILTERS)
+                       err = aq_hw_ops->hw_filter_vlan_ctrl(aq_hw, true);
+               /* otherwise left in promiscue mode */
+       }
+
+       return err;
+}
+
+int aq_filters_vlan_offload_off(struct aq_nic_s *aq_nic)
+{
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+       struct aq_hw_s *aq_hw = aq_nic->aq_hw;
+       int err = 0;
+
+       memset(aq_nic->active_vlans, 0, sizeof(aq_nic->active_vlans));
+       aq_fvlan_rebuild(aq_nic, aq_nic->active_vlans,
+                        aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans);
+
+       if (unlikely(!aq_hw_ops->hw_filter_vlan_set))
+               return -EOPNOTSUPP;
+       if (unlikely(!aq_hw_ops->hw_filter_vlan_ctrl))
+               return -EOPNOTSUPP;
+
+       err = aq_hw_ops->hw_filter_vlan_ctrl(aq_hw, false);
+       if (err)
+               return err;
+       err = aq_hw_ops->hw_filter_vlan_set(aq_hw,
+                                           aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans
+                                          );
+       return err;
+}
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_filters.h b/drivers/net/ethernet/aquantia/atlantic/aq_filters.h
new file mode 100644 (file)
index 0000000..c6a08c6
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2014-2017 aQuantia Corporation. */
+
+/* File aq_filters.h: RX filters related functions. */
+
+#ifndef AQ_FILTERS_H
+#define AQ_FILTERS_H
+
+#include "aq_nic.h"
+
+enum aq_rx_filter_type {
+       aq_rx_filter_ethertype,
+       aq_rx_filter_vlan,
+       aq_rx_filter_l3l4
+};
+
+struct aq_rx_filter {
+       struct hlist_node aq_node;
+       enum aq_rx_filter_type type;
+       struct ethtool_rx_flow_spec aq_fsp;
+};
+
+u16 aq_get_rxnfc_count_all_rules(struct aq_nic_s *aq_nic);
+struct aq_hw_rx_fltrs_s *aq_get_hw_rx_fltrs(struct aq_nic_s *aq_nic);
+int aq_add_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd);
+int aq_del_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd);
+int aq_get_rxnfc_rule(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd);
+int aq_get_rxnfc_all_rules(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd,
+                          u32 *rule_locs);
+int aq_del_fvlan_by_vlan(struct aq_nic_s *aq_nic, u16 vlan_id);
+int aq_clear_rxnfc_all_rules(struct aq_nic_s *aq_nic);
+int aq_reapply_rxnfc_all_rules(struct aq_nic_s *aq_nic);
+int aq_filters_vlans_update(struct aq_nic_s *aq_nic);
+int aq_filters_vlan_offload_off(struct aq_nic_s *aq_nic);
+
+#endif /* AQ_FILTERS_H */
index a1e70da..81aab73 100644 (file)
 #include "aq_rss.h"
 #include "hw_atl/hw_atl_utils.h"
 
+#define AQ_RX_FIRST_LOC_FVLANID     0U
+#define AQ_RX_LAST_LOC_FVLANID    15U
+#define AQ_RX_FIRST_LOC_FETHERT    16U
+#define AQ_RX_LAST_LOC_FETHERT    31U
+#define AQ_RX_FIRST_LOC_FL3L4     32U
+#define AQ_RX_LAST_LOC_FL3L4      39U
+#define AQ_RX_MAX_RXNFC_LOC       AQ_RX_LAST_LOC_FL3L4
+#define AQ_VLAN_MAX_FILTERS   \
+                       (AQ_RX_LAST_LOC_FVLANID - AQ_RX_FIRST_LOC_FVLANID + 1U)
+#define AQ_RX_QUEUE_NOT_ASSIGNED   0xFFU
+
 /* NIC H/W capabilities */
 struct aq_hw_caps_s {
        u64 hw_features;
@@ -130,6 +141,7 @@ struct aq_hw_s {
 struct aq_ring_s;
 struct aq_ring_param_s;
 struct sk_buff;
+struct aq_rx_filter_l3l4;
 
 struct aq_hw_ops {
 
@@ -183,6 +195,23 @@ struct aq_hw_ops {
        int (*hw_packet_filter_set)(struct aq_hw_s *self,
                                    unsigned int packet_filter);
 
+       int (*hw_filter_l3l4_set)(struct aq_hw_s *self,
+                                 struct aq_rx_filter_l3l4 *data);
+
+       int (*hw_filter_l3l4_clear)(struct aq_hw_s *self,
+                                   struct aq_rx_filter_l3l4 *data);
+
+       int (*hw_filter_l2_set)(struct aq_hw_s *self,
+                               struct aq_rx_filter_l2 *data);
+
+       int (*hw_filter_l2_clear)(struct aq_hw_s *self,
+                                 struct aq_rx_filter_l2 *data);
+
+       int (*hw_filter_vlan_set)(struct aq_hw_s *self,
+                                 struct aq_rx_filter_vlan *aq_vlans);
+
+       int (*hw_filter_vlan_ctrl)(struct aq_hw_s *self, bool enable);
+
        int (*hw_multicast_list_set)(struct aq_hw_s *self,
                                     u8 ar_mac[AQ_HW_MULTICAST_ADDRESS_MAX]
                                     [ETH_ALEN],
index 7c07eef..2a11c1e 100644 (file)
@@ -13,6 +13,7 @@
 #include "aq_nic.h"
 #include "aq_pci_func.h"
 #include "aq_ethtool.h"
+#include "aq_filters.h"
 
 #include <linux/netdevice.h>
 #include <linux/module.h>
@@ -49,6 +50,11 @@ static int aq_ndev_open(struct net_device *ndev)
        err = aq_nic_init(aq_nic);
        if (err < 0)
                goto err_exit;
+
+       err = aq_reapply_rxnfc_all_rules(aq_nic);
+       if (err < 0)
+               goto err_exit;
+
        err = aq_nic_start(aq_nic);
        if (err < 0)
                goto err_exit;
@@ -101,6 +107,21 @@ static int aq_ndev_set_features(struct net_device *ndev,
        bool is_lro = false;
        int err = 0;
 
+       if (!(features & NETIF_F_NTUPLE)) {
+               if (aq_nic->ndev->features & NETIF_F_NTUPLE) {
+                       err = aq_clear_rxnfc_all_rules(aq_nic);
+                       if (unlikely(err))
+                               goto err_exit;
+               }
+       }
+       if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
+               if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+                       err = aq_filters_vlan_offload_off(aq_nic);
+                       if (unlikely(err))
+                               goto err_exit;
+               }
+       }
+
        aq_cfg->features = features;
 
        if (aq_cfg->aq_hw_caps->hw_features & NETIF_F_LRO) {
@@ -119,6 +140,7 @@ static int aq_ndev_set_features(struct net_device *ndev,
                err = aq_nic->aq_hw_ops->hw_set_offload(aq_nic->aq_hw,
                                                        aq_cfg);
 
+err_exit:
        return err;
 }
 
@@ -147,6 +169,35 @@ static void aq_ndev_set_multicast_settings(struct net_device *ndev)
        aq_nic_set_multicast_list(aq_nic, ndev);
 }
 
+static int aq_ndo_vlan_rx_add_vid(struct net_device *ndev, __be16 proto,
+                                 u16 vid)
+{
+       struct aq_nic_s *aq_nic = netdev_priv(ndev);
+
+       if (!aq_nic->aq_hw_ops->hw_filter_vlan_set)
+               return -EOPNOTSUPP;
+
+       set_bit(vid, aq_nic->active_vlans);
+
+       return aq_filters_vlans_update(aq_nic);
+}
+
+static int aq_ndo_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto,
+                                  u16 vid)
+{
+       struct aq_nic_s *aq_nic = netdev_priv(ndev);
+
+       if (!aq_nic->aq_hw_ops->hw_filter_vlan_set)
+               return -EOPNOTSUPP;
+
+       clear_bit(vid, aq_nic->active_vlans);
+
+       if (-ENOENT == aq_del_fvlan_by_vlan(aq_nic, vid))
+               return aq_filters_vlans_update(aq_nic);
+
+       return 0;
+}
+
 static const struct net_device_ops aq_ndev_ops = {
        .ndo_open = aq_ndev_open,
        .ndo_stop = aq_ndev_close,
@@ -154,5 +205,7 @@ static const struct net_device_ops aq_ndev_ops = {
        .ndo_set_rx_mode = aq_ndev_set_multicast_settings,
        .ndo_change_mtu = aq_ndev_change_mtu,
        .ndo_set_mac_address = aq_ndev_set_mac_address,
-       .ndo_set_features = aq_ndev_set_features
+       .ndo_set_features = aq_ndev_set_features,
+       .ndo_vlan_rx_add_vid = aq_ndo_vlan_rx_add_vid,
+       .ndo_vlan_rx_kill_vid = aq_ndo_vlan_rx_kill_vid,
 };
index 7abdc09..0147c03 100644 (file)
@@ -44,7 +44,7 @@ static void aq_nic_rss_init(struct aq_nic_s *self, unsigned int num_rss_queues)
        struct aq_rss_parameters *rss_params = &cfg->aq_rss;
        int i = 0;
 
-       static u8 rss_key[40] = {
+       static u8 rss_key[AQ_CFG_RSS_HASHKEY_SIZE] = {
                0x1e, 0xad, 0x71, 0x87, 0x65, 0xfc, 0x26, 0x7d,
                0x0d, 0x45, 0x67, 0x74, 0xcd, 0x06, 0x1a, 0x18,
                0xb6, 0xc1, 0xf0, 0xc7, 0xbb, 0x18, 0xbe, 0xf8,
@@ -84,10 +84,6 @@ void aq_nic_cfg_start(struct aq_nic_s *self)
 
        cfg->is_lro = AQ_CFG_IS_LRO_DEF;
 
-       cfg->vlan_id = 0U;
-
-       aq_nic_rss_init(self, cfg->num_rss_queues);
-
        /*descriptors */
        cfg->rxds = min(cfg->aq_hw_caps->rxds_max, AQ_CFG_RXDS_DEF);
        cfg->txds = min(cfg->aq_hw_caps->txds_max, AQ_CFG_TXDS_DEF);
@@ -108,6 +104,8 @@ void aq_nic_cfg_start(struct aq_nic_s *self)
 
        cfg->num_rss_queues = min(cfg->vecs, AQ_CFG_NUM_RSS_QUEUES_DEF);
 
+       aq_nic_rss_init(self, cfg->num_rss_queues);
+
        cfg->irq_type = aq_pci_func_get_irq_type(self);
 
        if ((cfg->irq_type == AQ_HW_IRQ_LEGACY) ||
index 44ec47a..8e34c1e 100644 (file)
@@ -35,7 +35,6 @@ struct aq_nic_cfg_s {
        u32 mtu;
        u32 flow_control;
        u32 link_speed_msk;
-       u32 vlan_id;
        u32 wol;
        u16 is_mc_list_enabled;
        u16 mc_list_count;
@@ -61,6 +60,23 @@ struct aq_nic_cfg_s {
 #define AQ_NIC_TCVEC2RING(_NIC_, _TC_, _VEC_) \
        ((_TC_) * AQ_CFG_TCS_MAX + (_VEC_))
 
+struct aq_hw_rx_fl2 {
+       struct aq_rx_filter_vlan aq_vlans[AQ_VLAN_MAX_FILTERS];
+};
+
+struct aq_hw_rx_fl3l4 {
+       u8   active_ipv4;
+       u8   active_ipv6:2;
+       u8 is_ipv6;
+};
+
+struct aq_hw_rx_fltrs_s {
+       struct hlist_head     filter_list;
+       u16                   active_filters;
+       struct aq_hw_rx_fl2   fl2;
+       struct aq_hw_rx_fl3l4 fl3l4;
+};
+
 struct aq_nic_s {
        atomic_t flags;
        struct aq_vec_s *aq_vec[AQ_CFG_VECS_MAX];
@@ -81,10 +97,13 @@ struct aq_nic_s {
                u32 count;
                u8 ar[AQ_HW_MULTICAST_ADDRESS_MAX][ETH_ALEN];
        } mc_list;
+       /* Bitmask of currently assigned vlans from linux */
+       unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
 
        struct pci_dev *pdev;
        unsigned int msix_entry_mask;
        u32 irqvecs;
+       struct aq_hw_rx_fltrs_s aq_hw_rx_fltrs;
 };
 
 static inline struct device *aq_nic_get_dev(struct aq_nic_s *self)
index 1d5d6b8..c8b44cd 100644 (file)
@@ -19,6 +19,7 @@
 #include "aq_pci_func.h"
 #include "hw_atl/hw_atl_a0.h"
 #include "hw_atl/hw_atl_b0.h"
+#include "aq_filters.h"
 
 static const struct pci_device_id aq_pci_tbl[] = {
        { PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_0001), },
@@ -309,6 +310,7 @@ static void aq_pci_remove(struct pci_dev *pdev)
        struct aq_nic_s *self = pci_get_drvdata(pdev);
 
        if (self->ndev) {
+               aq_clear_rxnfc_all_rules(self);
                if (self->ndev->reg_state == NETREG_REGISTERED)
                        unregister_netdev(self->ndev);
                aq_nic_free_vectors(self);
index f02592f..b58ca7c 100644 (file)
@@ -21,7 +21,7 @@
 
 #define DEFAULT_B0_BOARD_BASIC_CAPABILITIES \
        .is_64_dma = true,                \
-       .msix_irqs = 4U,                  \
+       .msix_irqs = 8U,                  \
        .irq_mask = ~0U,                  \
        .vecs = HW_ATL_B0_RSS_MAX,        \
        .tcs = HW_ATL_B0_TC_MAX,          \
@@ -41,7 +41,9 @@
                        NETIF_F_RXHASH |  \
                        NETIF_F_SG |      \
                        NETIF_F_TSO |     \
-                       NETIF_F_LRO,      \
+                       NETIF_F_LRO |     \
+                       NETIF_F_NTUPLE |  \
+                       NETIF_F_HW_VLAN_CTAG_FILTER, \
        .hw_priv_flags = IFF_UNICAST_FLT, \
        .flow_control = true,             \
        .mtu = HW_ATL_B0_MTU_JUMBO,       \
@@ -319,20 +321,11 @@ static int hw_atl_b0_hw_init_rx_path(struct aq_hw_s *self)
        hw_atl_rpf_vlan_outer_etht_set(self, 0x88A8U);
        hw_atl_rpf_vlan_inner_etht_set(self, 0x8100U);
 
-       if (cfg->vlan_id) {
-               hw_atl_rpf_vlan_flr_act_set(self, 1U, 0U);
-               hw_atl_rpf_vlan_id_flr_set(self, 0U, 0U);
-               hw_atl_rpf_vlan_flr_en_set(self, 0U, 0U);
+       hw_atl_rpf_vlan_prom_mode_en_set(self, 1);
 
-               hw_atl_rpf_vlan_accept_untagged_packets_set(self, 1U);
-               hw_atl_rpf_vlan_untagged_act_set(self, 1U);
-
-               hw_atl_rpf_vlan_flr_act_set(self, 1U, 1U);
-               hw_atl_rpf_vlan_id_flr_set(self, cfg->vlan_id, 0U);
-               hw_atl_rpf_vlan_flr_en_set(self, 1U, 1U);
-       } else {
-               hw_atl_rpf_vlan_prom_mode_en_set(self, 1);
-       }
+       // Always accept untagged packets
+       hw_atl_rpf_vlan_accept_untagged_packets_set(self, 1U);
+       hw_atl_rpf_vlan_untagged_act_set(self, 1U);
 
        /* Rx Interrupts */
        hw_atl_rdm_rx_desc_wr_wb_irq_en_set(self, 1U);
@@ -674,7 +667,7 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self,
 
                rx_stat = (0x0000003CU & rxd_wb->status) >> 2;
 
-               is_rx_check_sum_enabled = (rxd_wb->type) & (0x3U << 19);
+               is_rx_check_sum_enabled = (rxd_wb->type >> 19) & 0x3U;
 
                pkt_type = 0xFFU & (rxd_wb->type >> 4);
 
@@ -945,6 +938,142 @@ static int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self,
        return aq_hw_err_from_flags(self);
 }
 
+static int hw_atl_b0_hw_fl3l4_clear(struct aq_hw_s *self,
+                                   struct aq_rx_filter_l3l4 *data)
+{
+       u8 location = data->location;
+
+       if (!data->is_ipv6) {
+               hw_atl_rpfl3l4_cmd_clear(self, location);
+               hw_atl_rpf_l4_spd_set(self, 0U, location);
+               hw_atl_rpf_l4_dpd_set(self, 0U, location);
+               hw_atl_rpfl3l4_ipv4_src_addr_clear(self, location);
+               hw_atl_rpfl3l4_ipv4_dest_addr_clear(self, location);
+       } else {
+               int i;
+
+               for (i = 0; i < HW_ATL_RX_CNT_REG_ADDR_IPV6; ++i) {
+                       hw_atl_rpfl3l4_cmd_clear(self, location + i);
+                       hw_atl_rpf_l4_spd_set(self, 0U, location + i);
+                       hw_atl_rpf_l4_dpd_set(self, 0U, location + i);
+               }
+               hw_atl_rpfl3l4_ipv6_src_addr_clear(self, location);
+               hw_atl_rpfl3l4_ipv6_dest_addr_clear(self, location);
+       }
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_fl3l4_set(struct aq_hw_s *self,
+                                 struct aq_rx_filter_l3l4 *data)
+{
+       u8 location = data->location;
+
+       hw_atl_b0_hw_fl3l4_clear(self, data);
+
+       if (data->cmd) {
+               if (!data->is_ipv6) {
+                       hw_atl_rpfl3l4_ipv4_dest_addr_set(self,
+                                                         location,
+                                                         data->ip_dst[0]);
+                       hw_atl_rpfl3l4_ipv4_src_addr_set(self,
+                                                        location,
+                                                        data->ip_src[0]);
+               } else {
+                       hw_atl_rpfl3l4_ipv6_dest_addr_set(self,
+                                                         location,
+                                                         data->ip_dst);
+                       hw_atl_rpfl3l4_ipv6_src_addr_set(self,
+                                                        location,
+                                                        data->ip_src);
+               }
+       }
+       hw_atl_rpf_l4_dpd_set(self, data->p_dst, location);
+       hw_atl_rpf_l4_spd_set(self, data->p_src, location);
+       hw_atl_rpfl3l4_cmd_set(self, location, data->cmd);
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_fl2_set(struct aq_hw_s *self,
+                               struct aq_rx_filter_l2 *data)
+{
+       hw_atl_rpf_etht_flr_en_set(self, 1U, data->location);
+       hw_atl_rpf_etht_flr_set(self, data->ethertype, data->location);
+       hw_atl_rpf_etht_user_priority_en_set(self,
+                                            !!data->user_priority_en,
+                                            data->location);
+       if (data->user_priority_en)
+               hw_atl_rpf_etht_user_priority_set(self,
+                                                 data->user_priority,
+                                                 data->location);
+
+       if (data->queue < 0) {
+               hw_atl_rpf_etht_flr_act_set(self, 0U, data->location);
+               hw_atl_rpf_etht_rx_queue_en_set(self, 0U, data->location);
+       } else {
+               hw_atl_rpf_etht_flr_act_set(self, 1U, data->location);
+               hw_atl_rpf_etht_rx_queue_en_set(self, 1U, data->location);
+               hw_atl_rpf_etht_rx_queue_set(self, data->queue, data->location);
+       }
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_fl2_clear(struct aq_hw_s *self,
+                                 struct aq_rx_filter_l2 *data)
+{
+       hw_atl_rpf_etht_flr_en_set(self, 0U, data->location);
+       hw_atl_rpf_etht_flr_set(self, 0U, data->location);
+       hw_atl_rpf_etht_user_priority_en_set(self, 0U, data->location);
+
+       return aq_hw_err_from_flags(self);
+}
+
+/**
+ * @brief Set VLAN filter table
+ * @details Configure VLAN filter table to accept (and assign the queue) traffic
+ *  for the particular vlan ids.
+ * Note: use this function under vlan promisc mode not to lost the traffic
+ *
+ * @param aq_hw_s
+ * @param aq_rx_filter_vlan VLAN filter configuration
+ * @return 0 - OK, <0 - error
+ */
+static int hw_atl_b0_hw_vlan_set(struct aq_hw_s *self,
+                                struct aq_rx_filter_vlan *aq_vlans)
+{
+       int i;
+
+       for (i = 0; i < AQ_VLAN_MAX_FILTERS; i++) {
+               hw_atl_rpf_vlan_flr_en_set(self, 0U, i);
+               hw_atl_rpf_vlan_rxq_en_flr_set(self, 0U, i);
+               if (aq_vlans[i].enable) {
+                       hw_atl_rpf_vlan_id_flr_set(self,
+                                                  aq_vlans[i].vlan_id,
+                                                  i);
+                       hw_atl_rpf_vlan_flr_act_set(self, 1U, i);
+                       hw_atl_rpf_vlan_flr_en_set(self, 1U, i);
+                       if (aq_vlans[i].queue != 0xFF) {
+                               hw_atl_rpf_vlan_rxq_flr_set(self,
+                                                           aq_vlans[i].queue,
+                                                           i);
+                               hw_atl_rpf_vlan_rxq_en_flr_set(self, 1U, i);
+                       }
+               }
+       }
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_vlan_ctrl(struct aq_hw_s *self, bool enable)
+{
+       /* set promisc in case of disabing the vland filter */
+       hw_atl_rpf_vlan_prom_mode_en_set(self, !!!enable);
+
+       return aq_hw_err_from_flags(self);
+}
+
 const struct aq_hw_ops hw_atl_ops_b0 = {
        .hw_set_mac_address   = hw_atl_b0_hw_mac_addr_set,
        .hw_init              = hw_atl_b0_hw_init,
@@ -969,6 +1098,11 @@ const struct aq_hw_ops hw_atl_ops_b0 = {
        .hw_ring_rx_init             = hw_atl_b0_hw_ring_rx_init,
        .hw_ring_tx_init             = hw_atl_b0_hw_ring_tx_init,
        .hw_packet_filter_set        = hw_atl_b0_hw_packet_filter_set,
+       .hw_filter_l2_set            = hw_atl_b0_hw_fl2_set,
+       .hw_filter_l2_clear          = hw_atl_b0_hw_fl2_clear,
+       .hw_filter_l3l4_set          = hw_atl_b0_hw_fl3l4_set,
+       .hw_filter_vlan_set          = hw_atl_b0_hw_vlan_set,
+       .hw_filter_vlan_ctrl         = hw_atl_b0_hw_vlan_ctrl,
        .hw_multicast_list_set       = hw_atl_b0_hw_multicast_list_set,
        .hw_interrupt_moderation_set = hw_atl_b0_hw_interrupt_moderation_set,
        .hw_rss_set                  = hw_atl_b0_hw_rss_set,
index 5502ec5..939f77e 100644 (file)
@@ -898,6 +898,24 @@ void hw_atl_rpf_vlan_id_flr_set(struct aq_hw_s *aq_hw, u32 vlan_id_flr,
                            vlan_id_flr);
 }
 
+void hw_atl_rpf_vlan_rxq_en_flr_set(struct aq_hw_s *aq_hw, u32 vlan_rxq_en,
+                                   u32 filter)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_RPF_VL_RXQ_EN_F_ADR(filter),
+                           HW_ATL_RPF_VL_RXQ_EN_F_MSK,
+                           HW_ATL_RPF_VL_RXQ_EN_F_SHIFT,
+                           vlan_rxq_en);
+}
+
+void hw_atl_rpf_vlan_rxq_flr_set(struct aq_hw_s *aq_hw, u32 vlan_rxq,
+                                u32 filter)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_RPF_VL_RXQ_F_ADR(filter),
+                           HW_ATL_RPF_VL_RXQ_F_MSK,
+                           HW_ATL_RPF_VL_RXQ_F_SHIFT,
+                           vlan_rxq);
+};
+
 void hw_atl_rpf_etht_flr_en_set(struct aq_hw_s *aq_hw, u32 etht_flr_en,
                                u32 filter)
 {
@@ -965,6 +983,20 @@ void hw_atl_rpf_etht_flr_set(struct aq_hw_s *aq_hw, u32 etht_flr, u32 filter)
                            HW_ATL_RPF_ET_VALF_SHIFT, etht_flr);
 }
 
+void hw_atl_rpf_l4_spd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_RPF_L4_SPD_ADR(filter),
+                           HW_ATL_RPF_L4_SPD_MSK,
+                           HW_ATL_RPF_L4_SPD_SHIFT, val);
+}
+
+void hw_atl_rpf_l4_dpd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_RPF_L4_DPD_ADR(filter),
+                           HW_ATL_RPF_L4_DPD_MSK,
+                           HW_ATL_RPF_L4_DPD_SHIFT, val);
+}
+
 /* RPO: rx packet offload */
 void hw_atl_rpo_ipv4header_crc_offload_en_set(struct aq_hw_s *aq_hw,
                                              u32 ipv4header_crc_offload_en)
@@ -1476,3 +1508,80 @@ void hw_atl_mcp_up_force_intr_set(struct aq_hw_s *aq_hw, u32 up_force_intr)
                            HW_ATL_MCP_UP_FORCE_INTERRUPT_SHIFT,
                            up_force_intr);
 }
+
+void hw_atl_rpfl3l4_ipv4_dest_addr_clear(struct aq_hw_s *aq_hw, u8 location)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL_RPF_L3_DSTA_ADR(location), 0U);
+}
+
+void hw_atl_rpfl3l4_ipv4_src_addr_clear(struct aq_hw_s *aq_hw, u8 location)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL_RPF_L3_SRCA_ADR(location), 0U);
+}
+
+void hw_atl_rpfl3l4_cmd_clear(struct aq_hw_s *aq_hw, u8 location)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL_RPF_L3_REG_CTRL_ADR(location), 0U);
+}
+
+void hw_atl_rpfl3l4_ipv6_dest_addr_clear(struct aq_hw_s *aq_hw, u8 location)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i)
+               aq_hw_write_reg(aq_hw,
+                               HW_ATL_RPF_L3_DSTA_ADR(location + i),
+                               0U);
+}
+
+void hw_atl_rpfl3l4_ipv6_src_addr_clear(struct aq_hw_s *aq_hw, u8 location)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i)
+               aq_hw_write_reg(aq_hw,
+                               HW_ATL_RPF_L3_SRCA_ADR(location + i),
+                               0U);
+}
+
+void hw_atl_rpfl3l4_ipv4_dest_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                      u32 ipv4_dest)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL_RPF_L3_DSTA_ADR(location),
+                       ipv4_dest);
+}
+
+void hw_atl_rpfl3l4_ipv4_src_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                     u32 ipv4_src)
+{
+       aq_hw_write_reg(aq_hw,
+                       HW_ATL_RPF_L3_SRCA_ADR(location),
+                       ipv4_src);
+}
+
+void hw_atl_rpfl3l4_cmd_set(struct aq_hw_s *aq_hw, u8 location, u32 cmd)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL_RPF_L3_REG_CTRL_ADR(location), cmd);
+}
+
+void hw_atl_rpfl3l4_ipv6_src_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                     u32 *ipv6_src)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i)
+               aq_hw_write_reg(aq_hw,
+                               HW_ATL_RPF_L3_SRCA_ADR(location + i),
+                               ipv6_src[i]);
+}
+
+void hw_atl_rpfl3l4_ipv6_dest_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                      u32 *ipv6_dest)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i)
+               aq_hw_write_reg(aq_hw,
+                               HW_ATL_RPF_L3_DSTA_ADR(location + i),
+                               ipv6_dest[i]);
+}
index 41f2399..03c570d 100644 (file)
@@ -441,6 +441,14 @@ void hw_atl_rpf_vlan_flr_act_set(struct aq_hw_s *aq_hw, u32 vlan_filter_act,
 void hw_atl_rpf_vlan_id_flr_set(struct aq_hw_s *aq_hw, u32 vlan_id_flr,
                                u32 filter);
 
+/* Set VLAN RX queue assignment enable */
+void hw_atl_rpf_vlan_rxq_en_flr_set(struct aq_hw_s *aq_hw, u32 vlan_rxq_en,
+                                   u32 filter);
+
+/* Set VLAN RX queue */
+void hw_atl_rpf_vlan_rxq_flr_set(struct aq_hw_s *aq_hw, u32 vlan_rxq,
+                                u32 filter);
+
 /* set ethertype filter enable */
 void hw_atl_rpf_etht_flr_en_set(struct aq_hw_s *aq_hw, u32 etht_flr_en,
                                u32 filter);
@@ -475,6 +483,12 @@ void hw_atl_rpf_etht_flr_act_set(struct aq_hw_s *aq_hw, u32 etht_flr_act,
 /* set ethertype filter */
 void hw_atl_rpf_etht_flr_set(struct aq_hw_s *aq_hw, u32 etht_flr, u32 filter);
 
+/* set L4 source port */
+void hw_atl_rpf_l4_spd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter);
+
+/* set L4 destination port */
+void hw_atl_rpf_l4_dpd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter);
+
 /* rpo */
 
 /* set ipv4 header checksum offload enable */
@@ -704,4 +718,38 @@ void hw_atl_pci_pci_reg_res_dis_set(struct aq_hw_s *aq_hw, u32 pci_reg_res_dis);
 /* set uP Force Interrupt */
 void hw_atl_mcp_up_force_intr_set(struct aq_hw_s *aq_hw, u32 up_force_intr);
 
+/* clear ipv4 filter destination address */
+void hw_atl_rpfl3l4_ipv4_dest_addr_clear(struct aq_hw_s *aq_hw, u8 location);
+
+/* clear ipv4 filter source address */
+void hw_atl_rpfl3l4_ipv4_src_addr_clear(struct aq_hw_s *aq_hw, u8 location);
+
+/* clear command for filter l3-l4 */
+void hw_atl_rpfl3l4_cmd_clear(struct aq_hw_s *aq_hw, u8 location);
+
+/* clear ipv6 filter destination address */
+void hw_atl_rpfl3l4_ipv6_dest_addr_clear(struct aq_hw_s *aq_hw, u8 location);
+
+/* clear ipv6 filter source address */
+void hw_atl_rpfl3l4_ipv6_src_addr_clear(struct aq_hw_s *aq_hw, u8 location);
+
+/* set ipv4 filter destination address */
+void hw_atl_rpfl3l4_ipv4_dest_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                      u32 ipv4_dest);
+
+/* set ipv4 filter source address */
+void hw_atl_rpfl3l4_ipv4_src_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                     u32 ipv4_src);
+
+/* set command for filter l3-l4 */
+void hw_atl_rpfl3l4_cmd_set(struct aq_hw_s *aq_hw, u8 location, u32 cmd);
+
+/* set ipv6 filter source address */
+void hw_atl_rpfl3l4_ipv6_src_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                     u32 *ipv6_src);
+
+/* set ipv6 filter destination address */
+void hw_atl_rpfl3l4_ipv6_dest_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                      u32 *ipv6_dest);
+
 #endif /* HW_ATL_LLH_H */
index a715fa3..8470d92 100644 (file)
 /* Default value of bitfield vl_id{F}[B:0] */
 #define HW_ATL_RPF_VL_ID_F_DEFAULT 0x0
 
-/* RX et_en{F} Bitfield Definitions
- * Preprocessor definitions for the bitfield "et_en{F}".
+/* RX vl_rxq_en{F} Bitfield Definitions
+ * Preprocessor definitions for the bitfield "vl_rxq{F}".
  * Parameter: filter {F} | stride size 0x4 | range [0, 15]
- * PORT="pif_rpf_et_en_i[0]"
- */
-
-/* Register address for bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_ADR(filter) (0x00005300 + (filter) * 0x4)
-/* Bitmask for bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_MSK 0x80000000
-/* Inverted bitmask for bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_MSKN 0x7FFFFFFF
-/* Lower bit position of bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_SHIFT 31
-/* Width of bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_WIDTH 1
-/* Default value of bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_DEFAULT 0x0
+ * PORT="pif_rpf_vl_rxq_en_i"
+ */
+
+/* Register address for bitfield vl_rxq_en{F} */
+#define HW_ATL_RPF_VL_RXQ_EN_F_ADR(filter) (0x00005290 + (filter) * 0x4)
+/* Bitmask for bitfield vl_rxq_en{F} */
+#define HW_ATL_RPF_VL_RXQ_EN_F_MSK 0x10000000
+/* Inverted bitmask for bitfield vl_rxq_en{F}[ */
+#define HW_ATL_RPF_VL_RXQ_EN_F_MSKN 0xEFFFFFFF
+/* Lower bit position of bitfield vl_rxq_en{F} */
+#define HW_ATL_RPF_VL_RXQ_EN_F_SHIFT 28
+/* Width of bitfield vl_rxq_en{F} */
+#define HW_ATL_RPF_VL_RXQ_EN_F_WIDTH 1
+/* Default value of bitfield vl_rxq_en{F} */
+#define HW_ATL_RPF_VL_RXQ_EN_F_DEFAULT 0x0
+
+/* RX vl_rxq{F}[4:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "vl_rxq{F}[4:0]".
+ * Parameter: filter {F} | stride size 0x4 | range [0, 15]
+ * PORT="pif_rpf_vl_rxq0_i[4:0]"
+ */
+
+/* Register address for bitfield vl_rxq{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_ADR(filter) (0x00005290 + (filter) * 0x4)
+/* Bitmask for bitfield vl_rxq{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_MSK 0x01F00000
+/* Inverted bitmask for bitfield vl_rxq{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_MSKN 0xFE0FFFFF
+/* Lower bit position of bitfield vl_rxq{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_SHIFT 20
+/* Width of bitfield vl_rxw{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_WIDTH 5
+/* Default value of bitfield vl_rxq{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_DEFAULT 0x0
 
 /* rx et_en{f} bitfield definitions
  * preprocessor definitions for the bitfield "et_en{f}".
 /* default value of bitfield et_val{f}[f:0] */
 #define HW_ATL_RPF_ET_VALF_DEFAULT 0x0
 
+/* RX l4_sp{D}[F:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l4_sp{D}[F:0]".
+ * Parameter: srcport {D} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l4_sp0_i[15:0]"
+ */
+
+/* Register address for bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_ADR(srcport) (0x00005400u + (srcport) * 0x4)
+/* Bitmask for bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_MSK 0x0000FFFFu
+/* Inverted bitmask for bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_MSKN 0xFFFF0000u
+/* Lower bit position of bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_SHIFT 0
+/* Width of bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_WIDTH 16
+/* Default value of bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_DEFAULT 0x0
+
+/* RX l4_dp{D}[F:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l4_dp{D}[F:0]".
+ * Parameter: destport {D} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l4_dp0_i[15:0]"
+ */
+
+/* Register address for bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_ADR(destport) (0x00005420u + (destport) * 0x4)
+/* Bitmask for bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_MSK 0x0000FFFFu
+/* Inverted bitmask for bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_MSKN 0xFFFF0000u
+/* Lower bit position of bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_SHIFT 0
+/* Width of bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_WIDTH 16
+/* Default value of bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_DEFAULT 0x0
+
 /* rx ipv4_chk_en bitfield definitions
  * preprocessor definitions for the bitfield "ipv4_chk_en".
  * port="pif_rpo_ipv4_chk_en_i"
 /* default value of bitfield uP Force Interrupt */
 #define HW_ATL_MCP_UP_FORCE_INTERRUPT_DEFAULT 0x0
 
+#define HW_ATL_RX_CTRL_ADDR_BEGIN_FL3L4   0x00005380
+#define HW_ATL_RX_SRCA_ADDR_BEGIN_FL3L4   0x000053B0
+#define HW_ATL_RX_DESTA_ADDR_BEGIN_FL3L4  0x000053D0
+
+#define HW_ATL_RPF_L3_REG_CTRL_ADR(location) (0x00005380 + (location) * 0x4)
+
+/* RX rpf_l3_sa{D}[1F:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l3_sa{D}[1F:0]".
+ * Parameter: location {D} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l3_sa0_i[31:0]"
+ */
+
+/* Register address for bitfield pif_rpf_l3_sa0_i[31:0] */
+#define HW_ATL_RPF_L3_SRCA_ADR(location) (0x000053B0 + (location) * 0x4)
+/* Bitmask for bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_MSK 0xFFFFFFFFu
+/* Inverted bitmask for bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_MSKN 0xFFFFFFFFu
+/* Lower bit position of bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_SHIFT 0
+/* Width of bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_WIDTH 32
+/* Default value of bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_DEFAULT 0x0
+
+/* RX rpf_l3_da{D}[1F:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l3_da{D}[1F:0]".
+ * Parameter: location {D} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l3_da0_i[31:0]"
+ */
+
+ /* Register address for bitfield pif_rpf_l3_da0_i[31:0] */
+#define HW_ATL_RPF_L3_DSTA_ADR(location) (0x000053B0 + (location) * 0x4)
+/* Bitmask for bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_MSK 0xFFFFFFFFu
+/* Inverted bitmask for bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_MSKN 0xFFFFFFFFu
+/* Lower bit position of bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_SHIFT 0
+/* Width of bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_WIDTH 32
+/* Default value of bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_DEFAULT 0x0
+
 #endif /* HW_ATL_LLH_INTERNAL_H */
index 7def1cb..9b74a31 100644 (file)
@@ -263,6 +263,8 @@ int hw_atl_utils_soft_reset(struct aq_hw_s *self)
                AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR) &
                                HW_ATL_MPI_STATE_MSK) == MPI_DEINIT,
                               10, 1000U);
+               if (err)
+                       return err;
        }
 
        if (self->rbl_enabled)
@@ -454,8 +456,6 @@ int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
                               (fw.val =
                                aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR),
                                fw.tid), 1000U, 100U);
-               if (err < 0)
-                       goto err_exit;
 
                if (fw.len == 0xFFFFU) {
                        err = hw_atl_utils_fw_rpc_call(self, sw.len);
@@ -463,8 +463,6 @@ int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
                                goto err_exit;
                }
        } while (sw.tid != fw.tid || 0xFFFFU == fw.len);
-       if (err < 0)
-               goto err_exit;
 
        if (rpc) {
                if (fw.len) {
index 3613fca..48278e3 100644 (file)
@@ -240,6 +240,64 @@ struct __packed offload_info {
        u8 buf[0];
 };
 
+enum hw_atl_rx_action_with_traffic {
+       HW_ATL_RX_DISCARD,
+       HW_ATL_RX_HOST,
+};
+
+struct aq_rx_filter_vlan {
+       u8 enable;
+       u8 location;
+       u16 vlan_id;
+       u8 queue;
+};
+
+struct aq_rx_filter_l2 {
+       s8 queue;
+       u8 location;
+       u8 user_priority_en;
+       u8 user_priority;
+       u16 ethertype;
+};
+
+struct aq_rx_filter_l3l4 {
+       u32 cmd;
+       u8 location;
+       u32 ip_dst[4];
+       u32 ip_src[4];
+       u16 p_dst;
+       u16 p_src;
+       u8 is_ipv6;
+};
+
+enum hw_atl_rx_protocol_value_l3l4 {
+       HW_ATL_RX_TCP,
+       HW_ATL_RX_UDP,
+       HW_ATL_RX_SCTP,
+       HW_ATL_RX_ICMP
+};
+
+enum hw_atl_rx_ctrl_registers_l3l4 {
+       HW_ATL_RX_ENABLE_MNGMNT_QUEUE_L3L4 = BIT(22),
+       HW_ATL_RX_ENABLE_QUEUE_L3L4        = BIT(23),
+       HW_ATL_RX_ENABLE_ARP_FLTR_L3       = BIT(24),
+       HW_ATL_RX_ENABLE_CMP_PROT_L4       = BIT(25),
+       HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4  = BIT(26),
+       HW_ATL_RX_ENABLE_CMP_SRC_PORT_L4   = BIT(27),
+       HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3  = BIT(28),
+       HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3   = BIT(29),
+       HW_ATL_RX_ENABLE_L3_IPV6           = BIT(30),
+       HW_ATL_RX_ENABLE_FLTR_L3L4         = BIT(31)
+};
+
+#define HW_ATL_RX_QUEUE_FL3L4_SHIFT       8U
+#define HW_ATL_RX_ACTION_FL3F4_SHIFT      16U
+
+#define HW_ATL_RX_CNT_REG_ADDR_IPV6       4U
+
+#define HW_ATL_GET_REG_LOCATION_FL3L4(location) \
+       ((location) - AQ_RX_FIRST_LOC_FL3L4)
+
 #define HAL_ATLANTIC_UTILS_CHIP_MIPS         0x00000001U
 #define HAL_ATLANTIC_UTILS_CHIP_TPO2         0x00000002U
 #define HAL_ATLANTIC_UTILS_CHIP_RPF2         0x00000004U
index be15061..5cd3135 100644 (file)
@@ -1282,6 +1282,7 @@ enum sp_rtnl_flag {
        BNX2X_SP_RTNL_TX_STOP,
        BNX2X_SP_RTNL_GET_DRV_VERSION,
        BNX2X_SP_RTNL_CHANGE_UDP_PORT,
+       BNX2X_SP_RTNL_UPDATE_SVID,
 };
 
 enum bnx2x_iov_flag {
@@ -2191,6 +2192,13 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id,
 #define PMF_DMAE_C(bp)                 (BP_PORT(bp) * MAX_DMAE_C_PER_PORT + \
                                         E1HVN_MAX)
 
+/* Following is the DMAE channel number allocation for the clients.
+ *   MFW: OCBB/OCSD implementations use DMAE channels 14/15 respectively.
+ *   Driver: 0-3 and 8-11 (for PF dmae operations)
+ *           4 and 12 (for stats requests)
+ */
+#define BNX2X_FW_DMAE_C                 13 /* Channel for FW DMAE operations */
+
 /* PCIE link and speed */
 #define PCICFG_LINK_WIDTH              0x1f00000
 #define PCICFG_LINK_WIDTH_SHIFT                20
@@ -2513,6 +2521,7 @@ void bnx2x_update_mfw_dump(struct bnx2x *bp);
 void bnx2x_init_ptp(struct bnx2x *bp);
 int bnx2x_configure_ptp_filters(struct bnx2x *bp);
 void bnx2x_set_rx_ts(struct bnx2x *bp, struct sk_buff *skb);
+void bnx2x_register_phc(struct bnx2x *bp);
 
 #define BNX2X_MAX_PHC_DRIFT 31000000
 #define BNX2X_PTP_TX_TIMEOUT
index 686899d..ecb1bd7 100644 (file)
@@ -2842,6 +2842,7 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
        bnx2x_set_rx_mode_inner(bp);
 
        if (bp->flags & PTP_SUPPORTED) {
+               bnx2x_register_phc(bp);
                bnx2x_init_ptp(bp);
                bnx2x_configure_ptp_filters(bp);
        }
index a4a90b6..749d0ef 100644 (file)
@@ -1105,11 +1105,39 @@ static void bnx2x_get_drvinfo(struct net_device *dev,
                              struct ethtool_drvinfo *info)
 {
        struct bnx2x *bp = netdev_priv(dev);
+       char version[ETHTOOL_FWVERS_LEN];
+       int ext_dev_info_offset;
+       u32 mbi;
 
        strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
        strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
 
-       bnx2x_fill_fw_str(bp, info->fw_version, sizeof(info->fw_version));
+       memset(version, 0, sizeof(version));
+       snprintf(version, ETHTOOL_FWVERS_LEN, " storm %d.%d.%d.%d",
+                BCM_5710_FW_MAJOR_VERSION, BCM_5710_FW_MINOR_VERSION,
+                BCM_5710_FW_REVISION_VERSION, BCM_5710_FW_ENGINEERING_VERSION);
+       strlcat(info->version, version, sizeof(info->version));
+
+       if (SHMEM2_HAS(bp, extended_dev_info_shared_addr)) {
+               ext_dev_info_offset = SHMEM2_RD(bp,
+                                               extended_dev_info_shared_addr);
+               mbi = REG_RD(bp, ext_dev_info_offset +
+                            offsetof(struct extended_dev_info_shared_cfg,
+                                     mbi_version));
+               if (mbi) {
+                       memset(version, 0, sizeof(version));
+                       snprintf(version, ETHTOOL_FWVERS_LEN, "mbi %d.%d.%d ",
+                                (mbi & 0xff000000) >> 24,
+                                (mbi & 0x00ff0000) >> 16,
+                                (mbi & 0x0000ff00) >> 8);
+                       strlcpy(info->fw_version, version,
+                               sizeof(info->fw_version));
+               }
+       }
+
+       memset(version, 0, sizeof(version));
+       bnx2x_fill_fw_str(bp, version, ETHTOOL_FWVERS_LEN);
+       strlcat(info->fw_version, version, sizeof(info->fw_version));
 
        strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info));
 }
index f8b8103..d9057c8 100644 (file)
@@ -1140,6 +1140,11 @@ struct shm_dev_info {                            /* size */
 
 };
 
+struct extended_dev_info_shared_cfg {
+       u32 reserved[18];
+       u32 mbi_version;
+       u32 mbi_date;
+};
 
 #if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
        #error "Missing either LITTLE_ENDIAN or BIG_ENDIAN definition."
index 95309b2..b164f70 100644 (file)
@@ -2925,6 +2925,10 @@ static void bnx2x_handle_update_svid_cmd(struct bnx2x *bp)
        func_params.f_obj = &bp->func_obj;
        func_params.cmd = BNX2X_F_CMD_SWITCH_UPDATE;
 
+       /* Prepare parameters for function state transitions */
+       __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags);
+       __set_bit(RAMROD_RETRY, &func_params.ramrod_flags);
+
        if (IS_MF_UFP(bp) || IS_MF_BD(bp)) {
                int func = BP_ABS_FUNC(bp);
                u32 val;
@@ -4311,7 +4315,8 @@ static void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn)
                                bnx2x_handle_eee_event(bp);
 
                        if (val & DRV_STATUS_OEM_UPDATE_SVID)
-                               bnx2x_handle_update_svid_cmd(bp);
+                               bnx2x_schedule_sp_rtnl(bp,
+                                       BNX2X_SP_RTNL_UPDATE_SVID, 0);
 
                        if (bp->link_vars.periodic_flags &
                            PERIODIC_FLAGS_LINK_EVENT) {
@@ -7723,6 +7728,9 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
                REG_WR(bp, reg_addr, val);
        }
 
+       if (CHIP_IS_E3B0(bp))
+               bp->flags |= PTP_SUPPORTED;
+
        return 0;
 }
 
@@ -8472,6 +8480,7 @@ int bnx2x_set_vlan_one(struct bnx2x *bp, u16 vlan,
        /* Fill a user request section if needed */
        if (!test_bit(RAMROD_CONT, ramrod_flags)) {
                ramrod_param.user_req.u.vlan.vlan = vlan;
+               __set_bit(BNX2X_VLAN, &ramrod_param.user_req.vlan_mac_flags);
                /* Set the command: ADD or DEL */
                if (set)
                        ramrod_param.user_req.cmd = BNX2X_VLAN_MAC_ADD;
@@ -8492,6 +8501,27 @@ int bnx2x_set_vlan_one(struct bnx2x *bp, u16 vlan,
        return rc;
 }
 
+static int bnx2x_del_all_vlans(struct bnx2x *bp)
+{
+       struct bnx2x_vlan_mac_obj *vlan_obj = &bp->sp_objs[0].vlan_obj;
+       unsigned long ramrod_flags = 0, vlan_flags = 0;
+       struct bnx2x_vlan_entry *vlan;
+       int rc;
+
+       __set_bit(RAMROD_COMP_WAIT, &ramrod_flags);
+       __set_bit(BNX2X_VLAN, &vlan_flags);
+       rc = vlan_obj->delete_all(bp, vlan_obj, &vlan_flags, &ramrod_flags);
+       if (rc)
+               return rc;
+
+       /* Mark that hw forgot all entries */
+       list_for_each_entry(vlan, &bp->vlan_reg, link)
+               vlan->hw = false;
+       bp->vlan_cnt = 0;
+
+       return 0;
+}
+
 int bnx2x_del_all_macs(struct bnx2x *bp,
                       struct bnx2x_vlan_mac_obj *mac_obj,
                       int mac_type, bool wait_for_comp)
@@ -9330,6 +9360,11 @@ void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode, bool keep_link)
                BNX2X_ERR("Failed to schedule DEL commands for UC MACs list: %d\n",
                          rc);
 
+       /* Remove all currently configured VLANs */
+       rc = bnx2x_del_all_vlans(bp);
+       if (rc < 0)
+               BNX2X_ERR("Failed to delete all VLANs\n");
+
        /* Disable LLH */
        if (!CHIP_IS_E1(bp))
                REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 0);
@@ -9417,8 +9452,13 @@ unload_error:
         * function stop ramrod is sent, since as part of this ramrod FW access
         * PTP registers.
         */
-       if (bp->flags & PTP_SUPPORTED)
+       if (bp->flags & PTP_SUPPORTED) {
                bnx2x_stop_ptp(bp);
+               if (bp->ptp_clock) {
+                       ptp_clock_unregister(bp->ptp_clock);
+                       bp->ptp_clock = NULL;
+               }
+       }
 
        /* Disable HW interrupts, NAPI */
        bnx2x_netif_stop(bp, 1);
@@ -10359,6 +10399,9 @@ sp_rtnl_not_reset:
                               &bp->sp_rtnl_state))
                bnx2x_update_mng_version(bp);
 
+       if (test_and_clear_bit(BNX2X_SP_RTNL_UPDATE_SVID, &bp->sp_rtnl_state))
+               bnx2x_handle_update_svid_cmd(bp);
+
        if (test_and_clear_bit(BNX2X_SP_RTNL_CHANGE_UDP_PORT,
                               &bp->sp_rtnl_state)) {
                if (bnx2x_udp_port_update(bp)) {
@@ -11750,8 +11793,10 @@ static void bnx2x_get_fcoe_info(struct bnx2x *bp)
         * If maximum allowed number of connections is zero -
         * disable the feature.
         */
-       if (!bp->cnic_eth_dev.max_fcoe_conn)
+       if (!bp->cnic_eth_dev.max_fcoe_conn) {
                bp->flags |= NO_FCOE_FLAG;
+               eth_zero_addr(bp->fip_mac);
+       }
 }
 
 static void bnx2x_get_cnic_info(struct bnx2x *bp)
@@ -12494,9 +12539,6 @@ static int bnx2x_init_bp(struct bnx2x *bp)
 
        bp->dump_preset_idx = 1;
 
-       if (CHIP_IS_E3B0(bp))
-               bp->flags |= PTP_SUPPORTED;
-
        return rc;
 }
 
@@ -13024,13 +13066,6 @@ static void bnx2x_vlan_configure(struct bnx2x *bp, bool set_rx_mode)
 
 int bnx2x_vlan_reconfigure_vid(struct bnx2x *bp)
 {
-       struct bnx2x_vlan_entry *vlan;
-
-       /* The hw forgot all entries after reload */
-       list_for_each_entry(vlan, &bp->vlan_reg, link)
-               vlan->hw = false;
-       bp->vlan_cnt = 0;
-
        /* Don't set rx mode here. Our caller will do it. */
        bnx2x_vlan_configure(bp, false);
 
@@ -13895,7 +13930,7 @@ static int bnx2x_ptp_enable(struct ptp_clock_info *ptp,
        return -ENOTSUPP;
 }
 
-static void bnx2x_register_phc(struct bnx2x *bp)
+void bnx2x_register_phc(struct bnx2x *bp)
 {
        /* Fill the ptp_clock_info struct and register PTP clock*/
        bp->ptp_clock_info.owner = THIS_MODULE;
@@ -14097,8 +14132,6 @@ static int bnx2x_init_one(struct pci_dev *pdev,
               dev->base_addr, bp->pdev->irq, dev->dev_addr);
        pcie_print_link_status(bp->pdev);
 
-       bnx2x_register_phc(bp);
-
        if (!IS_MF_SD_STORAGE_PERSONALITY_ONLY(bp))
                bnx2x_set_os_driver_state(bp, OS_DRIVER_STATE_DISABLED);
 
@@ -14131,11 +14164,6 @@ static void __bnx2x_remove(struct pci_dev *pdev,
                           struct bnx2x *bp,
                           bool remove_netdev)
 {
-       if (bp->ptp_clock) {
-               ptp_clock_unregister(bp->ptp_clock);
-               bp->ptp_clock = NULL;
-       }
-
        /* Delete storage MAC address */
        if (!NO_FCOE(bp)) {
                rtnl_lock();
index 3f4d2c8..a9eaaf3 100644 (file)
@@ -6149,6 +6149,7 @@ static inline int bnx2x_func_send_start(struct bnx2x *bp,
        rdata->sd_vlan_tag      = cpu_to_le16(start_params->sd_vlan_tag);
        rdata->path_id          = BP_PATH(bp);
        rdata->network_cos_mode = start_params->network_cos_mode;
+       rdata->dmae_cmd_id      = BNX2X_FW_DMAE_C;
 
        rdata->vxlan_dst_port   = cpu_to_le16(start_params->vxlan_dst_port);
        rdata->geneve_dst_port  = cpu_to_le16(start_params->geneve_dst_port);
index 0bf2fd4..7a6e82d 100644 (file)
@@ -265,6 +265,7 @@ enum {
        BNX2X_ETH_MAC,
        BNX2X_ISCSI_ETH_MAC,
        BNX2X_NETQ_ETH_MAC,
+       BNX2X_VLAN,
        BNX2X_DONT_CONSUME_CAM_CREDIT,
        BNX2X_DONT_CONSUME_CAM_CREDIT_DEST,
 };
@@ -272,7 +273,8 @@ enum {
 #define BNX2X_VLAN_MAC_CMP_MASK        (1 << BNX2X_UC_LIST_MAC | \
                                 1 << BNX2X_ETH_MAC | \
                                 1 << BNX2X_ISCSI_ETH_MAC | \
-                                1 << BNX2X_NETQ_ETH_MAC)
+                                1 << BNX2X_NETQ_ETH_MAC | \
+                                1 << BNX2X_VLAN)
 #define BNX2X_VLAN_MAC_CMP_FLAGS(flags) \
        ((flags) & BNX2X_VLAN_MAC_CMP_MASK)
 
index dd85d79..3aa80da 100644 (file)
@@ -118,6 +118,7 @@ enum board_idx {
        NETXTREME_E_VF,
        NETXTREME_C_VF,
        NETXTREME_S_VF,
+       NETXTREME_E_P5_VF,
 };
 
 /* indexed by enum above */
@@ -160,6 +161,7 @@ static const struct {
        [NETXTREME_E_VF] = { "Broadcom NetXtreme-E Ethernet Virtual Function" },
        [NETXTREME_C_VF] = { "Broadcom NetXtreme-C Ethernet Virtual Function" },
        [NETXTREME_S_VF] = { "Broadcom NetXtreme-S Ethernet Virtual Function" },
+       [NETXTREME_E_P5_VF] = { "Broadcom BCM5750X NetXtreme-E Ethernet Virtual Function" },
 };
 
 static const struct pci_device_id bnxt_pci_tbl[] = {
@@ -210,6 +212,7 @@ static const struct pci_device_id bnxt_pci_tbl[] = {
        { PCI_VDEVICE(BROADCOM, 0x16dc), .driver_data = NETXTREME_E_VF },
        { PCI_VDEVICE(BROADCOM, 0x16e1), .driver_data = NETXTREME_C_VF },
        { PCI_VDEVICE(BROADCOM, 0x16e5), .driver_data = NETXTREME_C_VF },
+       { PCI_VDEVICE(BROADCOM, 0x1807), .driver_data = NETXTREME_E_P5_VF },
        { PCI_VDEVICE(BROADCOM, 0xd800), .driver_data = NETXTREME_S_VF },
 #endif
        { 0 }
@@ -237,7 +240,7 @@ static struct workqueue_struct *bnxt_pf_wq;
 static bool bnxt_vf_pciid(enum board_idx idx)
 {
        return (idx == NETXTREME_C_VF || idx == NETXTREME_E_VF ||
-               idx == NETXTREME_S_VF);
+               idx == NETXTREME_S_VF || idx == NETXTREME_E_P5_VF);
 }
 
 #define DB_CP_REARM_FLAGS      (DB_KEY_CP | DB_IDX_VALID)
@@ -1675,7 +1678,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
        } else {
                if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS) {
                        if (dev->features & NETIF_F_RXCSUM)
-                               cpr->rx_l4_csum_errors++;
+                               bnapi->cp_ring.rx_l4_csum_errors++;
                }
        }
 
@@ -1809,7 +1812,7 @@ static int bnxt_hwrm_handler(struct bnxt *bp, struct tx_cmp *txcmp)
        case CMPL_BASE_TYPE_HWRM_DONE:
                seq_id = le16_to_cpu(h_cmpl->sequence_id);
                if (seq_id == bp->hwrm_intr_seq_id)
-                       bp->hwrm_intr_seq_id = HWRM_SEQ_ID_INVALID;
+                       bp->hwrm_intr_seq_id = (u16)~bp->hwrm_intr_seq_id;
                else
                        netdev_err(bp->dev, "Invalid hwrm seq id %d\n", seq_id);
                break;
@@ -2372,7 +2375,11 @@ static void bnxt_free_ring(struct bnxt *bp, struct bnxt_ring_mem_info *rmem)
                rmem->pg_arr[i] = NULL;
        }
        if (rmem->pg_tbl) {
-               dma_free_coherent(&pdev->dev, rmem->nr_pages * 8,
+               size_t pg_tbl_size = rmem->nr_pages * 8;
+
+               if (rmem->flags & BNXT_RMEM_USE_FULL_PAGE_FLAG)
+                       pg_tbl_size = rmem->page_size;
+               dma_free_coherent(&pdev->dev, pg_tbl_size,
                                  rmem->pg_tbl, rmem->pg_tbl_map);
                rmem->pg_tbl = NULL;
        }
@@ -2390,9 +2397,12 @@ static int bnxt_alloc_ring(struct bnxt *bp, struct bnxt_ring_mem_info *rmem)
 
        if (rmem->flags & (BNXT_RMEM_VALID_PTE_FLAG | BNXT_RMEM_RING_PTE_FLAG))
                valid_bit = PTU_PTE_VALID;
-       if (rmem->nr_pages > 1) {
-               rmem->pg_tbl = dma_alloc_coherent(&pdev->dev,
-                                                 rmem->nr_pages * 8,
+       if ((rmem->nr_pages > 1 || rmem->depth > 0) && !rmem->pg_tbl) {
+               size_t pg_tbl_size = rmem->nr_pages * 8;
+
+               if (rmem->flags & BNXT_RMEM_USE_FULL_PAGE_FLAG)
+                       pg_tbl_size = rmem->page_size;
+               rmem->pg_tbl = dma_alloc_coherent(&pdev->dev, pg_tbl_size,
                                                  &rmem->pg_tbl_map,
                                                  GFP_KERNEL);
                if (!rmem->pg_tbl)
@@ -2409,7 +2419,7 @@ static int bnxt_alloc_ring(struct bnxt *bp, struct bnxt_ring_mem_info *rmem)
                if (!rmem->pg_arr[i])
                        return -ENOMEM;
 
-               if (rmem->nr_pages > 1) {
+               if (rmem->nr_pages > 1 || rmem->depth > 0) {
                        if (i == rmem->nr_pages - 2 &&
                            (rmem->flags & BNXT_RMEM_RING_PTE_FLAG))
                                extra_bits |= PTU_PTE_NEXT_TO_LAST;
@@ -3276,6 +3286,27 @@ static void bnxt_free_hwrm_resources(struct bnxt *bp)
                                  bp->hwrm_cmd_resp_dma_addr);
                bp->hwrm_cmd_resp_addr = NULL;
        }
+
+       if (bp->hwrm_cmd_kong_resp_addr) {
+               dma_free_coherent(&pdev->dev, PAGE_SIZE,
+                                 bp->hwrm_cmd_kong_resp_addr,
+                                 bp->hwrm_cmd_kong_resp_dma_addr);
+               bp->hwrm_cmd_kong_resp_addr = NULL;
+       }
+}
+
+static int bnxt_alloc_kong_hwrm_resources(struct bnxt *bp)
+{
+       struct pci_dev *pdev = bp->pdev;
+
+       bp->hwrm_cmd_kong_resp_addr =
+               dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
+                                  &bp->hwrm_cmd_kong_resp_dma_addr,
+                                  GFP_KERNEL);
+       if (!bp->hwrm_cmd_kong_resp_addr)
+               return -ENOMEM;
+
+       return 0;
 }
 
 static int bnxt_alloc_hwrm_resources(struct bnxt *bp)
@@ -3317,9 +3348,8 @@ static int bnxt_alloc_hwrm_short_cmd_req(struct bnxt *bp)
        return 0;
 }
 
-static void bnxt_free_stats(struct bnxt *bp)
+static void bnxt_free_port_stats(struct bnxt *bp)
 {
-       u32 size, i;
        struct pci_dev *pdev = bp->pdev;
 
        bp->flags &= ~BNXT_FLAG_PORT_STATS;
@@ -3345,6 +3375,12 @@ static void bnxt_free_stats(struct bnxt *bp)
                                  bp->hw_rx_port_stats_ext_map);
                bp->hw_rx_port_stats_ext = NULL;
        }
+}
+
+static void bnxt_free_ring_stats(struct bnxt *bp)
+{
+       struct pci_dev *pdev = bp->pdev;
+       int size, i;
 
        if (!bp->bnapi)
                return;
@@ -3384,6 +3420,9 @@ static int bnxt_alloc_stats(struct bnxt *bp)
        }
 
        if (BNXT_PF(bp) && bp->chip_num != CHIP_NUM_58700) {
+               if (bp->hw_rx_port_stats)
+                       goto alloc_ext_stats;
+
                bp->hw_port_stats_size = sizeof(struct rx_port_stats) +
                                         sizeof(struct tx_port_stats) + 1024;
 
@@ -3400,11 +3439,15 @@ static int bnxt_alloc_stats(struct bnxt *bp)
                                           sizeof(struct rx_port_stats) + 512;
                bp->flags |= BNXT_FLAG_PORT_STATS;
 
+alloc_ext_stats:
                /* Display extended statistics only if FW supports it */
                if (bp->hwrm_spec_code < 0x10804 ||
                    bp->hwrm_spec_code == 0x10900)
                        return 0;
 
+               if (bp->hw_rx_port_stats_ext)
+                       goto alloc_tx_ext_stats;
+
                bp->hw_rx_port_stats_ext =
                        dma_zalloc_coherent(&pdev->dev,
                                            sizeof(struct rx_port_stats_ext),
@@ -3413,6 +3456,10 @@ static int bnxt_alloc_stats(struct bnxt *bp)
                if (!bp->hw_rx_port_stats_ext)
                        return 0;
 
+alloc_tx_ext_stats:
+               if (bp->hw_tx_port_stats_ext)
+                       return 0;
+
                if (bp->hwrm_spec_code >= 0x10902) {
                        bp->hw_tx_port_stats_ext =
                                dma_zalloc_coherent(&pdev->dev,
@@ -3520,7 +3567,7 @@ static void bnxt_free_mem(struct bnxt *bp, bool irq_re_init)
        bnxt_free_cp_rings(bp);
        bnxt_free_ntp_fltrs(bp, irq_re_init);
        if (irq_re_init) {
-               bnxt_free_stats(bp);
+               bnxt_free_ring_stats(bp);
                bnxt_free_ring_grps(bp);
                bnxt_free_vnics(bp);
                kfree(bp->tx_ring_map);
@@ -3721,7 +3768,10 @@ void bnxt_hwrm_cmd_hdr_init(struct bnxt *bp, void *request, u16 req_type,
        req->req_type = cpu_to_le16(req_type);
        req->cmpl_ring = cpu_to_le16(cmpl_ring);
        req->target_id = cpu_to_le16(target_id);
-       req->resp_addr = cpu_to_le64(bp->hwrm_cmd_resp_dma_addr);
+       if (bnxt_kong_hwrm_message(bp, req))
+               req->resp_addr = cpu_to_le64(bp->hwrm_cmd_kong_resp_dma_addr);
+       else
+               req->resp_addr = cpu_to_le64(bp->hwrm_cmd_resp_dma_addr);
 }
 
 static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
@@ -3736,11 +3786,10 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
        struct hwrm_err_output *resp = bp->hwrm_cmd_resp_addr;
        u16 max_req_len = BNXT_HWRM_MAX_REQ_LEN;
        struct hwrm_short_input short_input = {0};
-
-       req->seq_id = cpu_to_le16(bp->hwrm_cmd_seq++);
-       memset(resp, 0, PAGE_SIZE);
-       cp_ring_id = le16_to_cpu(req->cmpl_ring);
-       intr_process = (cp_ring_id == INVALID_HW_RING_ID) ? 0 : 1;
+       u32 doorbell_offset = BNXT_GRCPF_REG_CHIMP_COMM_TRIGGER;
+       u8 *resp_addr = (u8 *)bp->hwrm_cmd_resp_addr;
+       u32 bar_offset = BNXT_GRCPF_REG_CHIMP_COMM;
+       u16 dst = BNXT_HWRM_CHNL_CHIMP;
 
        if (msg_len > BNXT_HWRM_MAX_REQ_LEN) {
                if (msg_len > bp->hwrm_max_ext_req_len ||
@@ -3748,6 +3797,23 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
                        return -EINVAL;
        }
 
+       if (bnxt_hwrm_kong_chnl(bp, req)) {
+               dst = BNXT_HWRM_CHNL_KONG;
+               bar_offset = BNXT_GRCPF_REG_KONG_COMM;
+               doorbell_offset = BNXT_GRCPF_REG_KONG_COMM_TRIGGER;
+               resp = bp->hwrm_cmd_kong_resp_addr;
+               resp_addr = (u8 *)bp->hwrm_cmd_kong_resp_addr;
+       }
+
+       memset(resp, 0, PAGE_SIZE);
+       cp_ring_id = le16_to_cpu(req->cmpl_ring);
+       intr_process = (cp_ring_id == INVALID_HW_RING_ID) ? 0 : 1;
+
+       req->seq_id = cpu_to_le16(bnxt_get_hwrm_seq_id(bp, dst));
+       /* currently supports only one outstanding message */
+       if (intr_process)
+               bp->hwrm_intr_seq_id = le16_to_cpu(req->seq_id);
+
        if ((bp->fw_cap & BNXT_FW_CAP_SHORT_CMD) ||
            msg_len > BNXT_HWRM_MAX_REQ_LEN) {
                void *short_cmd_req = bp->hwrm_short_cmd_req_addr;
@@ -3781,17 +3847,13 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
        }
 
        /* Write request msg to hwrm channel */
-       __iowrite32_copy(bp->bar0, data, msg_len / 4);
+       __iowrite32_copy(bp->bar0 + bar_offset, data, msg_len / 4);
 
        for (i = msg_len; i < max_req_len; i += 4)
-               writel(0, bp->bar0 + i);
-
-       /* currently supports only one outstanding message */
-       if (intr_process)
-               bp->hwrm_intr_seq_id = le16_to_cpu(req->seq_id);
+               writel(0, bp->bar0 + bar_offset + i);
 
        /* Ring channel doorbell */
-       writel(1, bp->bar0 + 0x100);
+       writel(1, bp->bar0 + doorbell_offset);
 
        if (!timeout)
                timeout = DFLT_HWRM_CMD_TIMEOUT;
@@ -3806,10 +3868,13 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
        tmo_count = HWRM_SHORT_TIMEOUT_COUNTER;
        timeout = timeout - HWRM_SHORT_MIN_TIMEOUT * HWRM_SHORT_TIMEOUT_COUNTER;
        tmo_count += DIV_ROUND_UP(timeout, HWRM_MIN_TIMEOUT);
-       resp_len = bp->hwrm_cmd_resp_addr + HWRM_RESP_LEN_OFFSET;
+       resp_len = (__le32 *)(resp_addr + HWRM_RESP_LEN_OFFSET);
+
        if (intr_process) {
+               u16 seq_id = bp->hwrm_intr_seq_id;
+
                /* Wait until hwrm response cmpl interrupt is processed */
-               while (bp->hwrm_intr_seq_id != HWRM_SEQ_ID_INVALID &&
+               while (bp->hwrm_intr_seq_id != (u16)~seq_id &&
                       i++ < tmo_count) {
                        /* on first few passes, just barely sleep */
                        if (i < HWRM_SHORT_TIMEOUT_COUNTER)
@@ -3820,14 +3885,14 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
                                             HWRM_MAX_TIMEOUT);
                }
 
-               if (bp->hwrm_intr_seq_id != HWRM_SEQ_ID_INVALID) {
+               if (bp->hwrm_intr_seq_id != (u16)~seq_id) {
                        netdev_err(bp->dev, "Resp cmpl intr err msg: 0x%x\n",
                                   le16_to_cpu(req->req_type));
                        return -1;
                }
                len = (le32_to_cpu(*resp_len) & HWRM_RESP_LEN_MASK) >>
                      HWRM_RESP_LEN_SFT;
-               valid = bp->hwrm_cmd_resp_addr + len - 1;
+               valid = resp_addr + len - 1;
        } else {
                int j;
 
@@ -3855,7 +3920,7 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
                }
 
                /* Last byte of resp contains valid bit */
-               valid = bp->hwrm_cmd_resp_addr + len - 1;
+               valid = resp_addr + len - 1;
                for (j = 0; j < HWRM_VALID_BIT_DELAY_USEC; j++) {
                        /* make sure we read from updated DMA memory */
                        dma_rmb();
@@ -3990,6 +4055,10 @@ static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp)
                        cpu_to_le32(FUNC_DRV_RGTR_REQ_ENABLES_VF_REQ_FWD);
        }
 
+       if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE)
+               req.flags |= cpu_to_le32(
+                       FUNC_DRV_RGTR_REQ_FLAGS_FLOW_HANDLE_64BIT_MODE);
+
        mutex_lock(&bp->hwrm_cmd_lock);
        rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
        if (rc)
@@ -4118,12 +4187,11 @@ static int bnxt_hwrm_cfa_ntuple_filter_free(struct bnxt *bp,
 static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp,
                                             struct bnxt_ntuple_filter *fltr)
 {
-       int rc = 0;
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[fltr->rxq + 1];
        struct hwrm_cfa_ntuple_filter_alloc_input req = {0};
-       struct hwrm_cfa_ntuple_filter_alloc_output *resp =
-               bp->hwrm_cmd_resp_addr;
+       struct hwrm_cfa_ntuple_filter_alloc_output *resp;
        struct flow_keys *keys = &fltr->fkeys;
-       struct bnxt_vnic_info *vnic = &bp->vnic_info[fltr->rxq + 1];
+       int rc = 0;
 
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_NTUPLE_FILTER_ALLOC, -1, -1);
        req.l2_filter_id = bp->vnic_info[0].fw_l2_filter_id[fltr->l2_fltr_idx];
@@ -4169,8 +4237,10 @@ static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp,
        req.dst_id = cpu_to_le16(vnic->fw_vnic_id);
        mutex_lock(&bp->hwrm_cmd_lock);
        rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
-       if (!rc)
+       if (!rc) {
+               resp = bnxt_get_hwrm_resp_addr(bp, &req);
                fltr->filter_id = resp->ntuple_filter_id;
+       }
        mutex_unlock(&bp->hwrm_cmd_lock);
        return rc;
 }
@@ -5161,7 +5231,7 @@ static int bnxt_hwrm_get_rings(struct bnxt *bp)
                hw_resc->resv_vnics = le16_to_cpu(resp->alloc_vnics);
                cp = le16_to_cpu(resp->alloc_cmpl_rings);
                stats = le16_to_cpu(resp->alloc_stat_ctx);
-               cp = min_t(u16, cp, stats);
+               hw_resc->resv_irqs = cp;
                if (bp->flags & BNXT_FLAG_CHIP_P5) {
                        int rx = hw_resc->resv_rx_rings;
                        int tx = hw_resc->resv_tx_rings;
@@ -5175,10 +5245,11 @@ static int bnxt_hwrm_get_rings(struct bnxt *bp)
                                hw_resc->resv_rx_rings = rx;
                                hw_resc->resv_tx_rings = tx;
                        }
-                       cp = le16_to_cpu(resp->alloc_msix);
+                       hw_resc->resv_irqs = le16_to_cpu(resp->alloc_msix);
                        hw_resc->resv_hw_ring_grps = rx;
                }
                hw_resc->resv_cp_rings = cp;
+               hw_resc->resv_stat_ctxs = stats;
        }
        mutex_unlock(&bp->hwrm_cmd_lock);
        return 0;
@@ -5208,7 +5279,7 @@ static bool bnxt_rfs_supported(struct bnxt *bp);
 static void
 __bnxt_hwrm_reserve_pf_rings(struct bnxt *bp, struct hwrm_func_cfg_input *req,
                             int tx_rings, int rx_rings, int ring_grps,
-                            int cp_rings, int vnics)
+                            int cp_rings, int stats, int vnics)
 {
        u32 enables = 0;
 
@@ -5250,7 +5321,7 @@ __bnxt_hwrm_reserve_pf_rings(struct bnxt *bp, struct hwrm_func_cfg_input *req,
                                req->num_rsscos_ctxs =
                                        cpu_to_le16(ring_grps + 1);
                }
-               req->num_stat_ctxs = req->num_cmpl_rings;
+               req->num_stat_ctxs = cpu_to_le16(stats);
                req->num_vnics = cpu_to_le16(vnics);
        }
        req->enables = cpu_to_le32(enables);
@@ -5260,7 +5331,7 @@ static void
 __bnxt_hwrm_reserve_vf_rings(struct bnxt *bp,
                             struct hwrm_func_vf_cfg_input *req, int tx_rings,
                             int rx_rings, int ring_grps, int cp_rings,
-                            int vnics)
+                            int stats, int vnics)
 {
        u32 enables = 0;
 
@@ -5293,7 +5364,7 @@ __bnxt_hwrm_reserve_vf_rings(struct bnxt *bp,
                req->num_hw_ring_grps = cpu_to_le16(ring_grps);
                req->num_rsscos_ctxs = cpu_to_le16(BNXT_VF_MAX_RSS_CTX);
        }
-       req->num_stat_ctxs = req->num_cmpl_rings;
+       req->num_stat_ctxs = cpu_to_le16(stats);
        req->num_vnics = cpu_to_le16(vnics);
 
        req->enables = cpu_to_le32(enables);
@@ -5301,13 +5372,13 @@ __bnxt_hwrm_reserve_vf_rings(struct bnxt *bp,
 
 static int
 bnxt_hwrm_reserve_pf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
-                          int ring_grps, int cp_rings, int vnics)
+                          int ring_grps, int cp_rings, int stats, int vnics)
 {
        struct hwrm_func_cfg_input req = {0};
        int rc;
 
        __bnxt_hwrm_reserve_pf_rings(bp, &req, tx_rings, rx_rings, ring_grps,
-                                    cp_rings, vnics);
+                                    cp_rings, stats, vnics);
        if (!req.enables)
                return 0;
 
@@ -5324,7 +5395,7 @@ bnxt_hwrm_reserve_pf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
 
 static int
 bnxt_hwrm_reserve_vf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
-                          int ring_grps, int cp_rings, int vnics)
+                          int ring_grps, int cp_rings, int stats, int vnics)
 {
        struct hwrm_func_vf_cfg_input req = {0};
        int rc;
@@ -5335,7 +5406,7 @@ bnxt_hwrm_reserve_vf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
        }
 
        __bnxt_hwrm_reserve_vf_rings(bp, &req, tx_rings, rx_rings, ring_grps,
-                                    cp_rings, vnics);
+                                    cp_rings, stats, vnics);
        rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
        if (rc)
                return -ENOMEM;
@@ -5345,15 +5416,17 @@ bnxt_hwrm_reserve_vf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
 }
 
 static int bnxt_hwrm_reserve_rings(struct bnxt *bp, int tx, int rx, int grp,
-                                  int cp, int vnic)
+                                  int cp, int stat, int vnic)
 {
        if (BNXT_PF(bp))
-               return bnxt_hwrm_reserve_pf_rings(bp, tx, rx, grp, cp, vnic);
+               return bnxt_hwrm_reserve_pf_rings(bp, tx, rx, grp, cp, stat,
+                                                 vnic);
        else
-               return bnxt_hwrm_reserve_vf_rings(bp, tx, rx, grp, cp, vnic);
+               return bnxt_hwrm_reserve_vf_rings(bp, tx, rx, grp, cp, stat,
+                                                 vnic);
 }
 
-static int bnxt_cp_rings_in_use(struct bnxt *bp)
+int bnxt_nq_rings_in_use(struct bnxt *bp)
 {
        int cp = bp->cp_nr_rings;
        int ulp_msix, ulp_base;
@@ -5368,11 +5441,28 @@ static int bnxt_cp_rings_in_use(struct bnxt *bp)
        return cp;
 }
 
+static int bnxt_cp_rings_in_use(struct bnxt *bp)
+{
+       int cp;
+
+       if (!(bp->flags & BNXT_FLAG_CHIP_P5))
+               return bnxt_nq_rings_in_use(bp);
+
+       cp = bp->tx_nr_rings + bp->rx_nr_rings;
+       return cp;
+}
+
+static int bnxt_get_func_stat_ctxs(struct bnxt *bp)
+{
+       return bp->cp_nr_rings + bnxt_get_ulp_stat_ctxs(bp);
+}
+
 static bool bnxt_need_reserve_rings(struct bnxt *bp)
 {
        struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
        int cp = bnxt_cp_rings_in_use(bp);
-       int rx = bp->rx_nr_rings;
+       int nq = bnxt_nq_rings_in_use(bp);
+       int rx = bp->rx_nr_rings, stat;
        int vnic = 1, grp = rx;
 
        if (bp->hwrm_spec_code < 0x10601)
@@ -5385,9 +5475,11 @@ static bool bnxt_need_reserve_rings(struct bnxt *bp)
                vnic = rx + 1;
        if (bp->flags & BNXT_FLAG_AGG_RINGS)
                rx <<= 1;
+       stat = bnxt_get_func_stat_ctxs(bp);
        if (BNXT_NEW_RM(bp) &&
            (hw_resc->resv_rx_rings != rx || hw_resc->resv_cp_rings != cp ||
-            hw_resc->resv_vnics != vnic ||
+            hw_resc->resv_irqs < nq || hw_resc->resv_vnics != vnic ||
+            hw_resc->resv_stat_ctxs != stat ||
             (hw_resc->resv_hw_ring_grps != grp &&
              !(bp->flags & BNXT_FLAG_CHIP_P5))))
                return true;
@@ -5397,12 +5489,12 @@ static bool bnxt_need_reserve_rings(struct bnxt *bp)
 static int __bnxt_reserve_rings(struct bnxt *bp)
 {
        struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
-       int cp = bnxt_cp_rings_in_use(bp);
+       int cp = bnxt_nq_rings_in_use(bp);
        int tx = bp->tx_nr_rings;
        int rx = bp->rx_nr_rings;
        int grp, rx_rings, rc;
+       int vnic = 1, stat;
        bool sh = false;
-       int vnic = 1;
 
        if (!bnxt_need_reserve_rings(bp))
                return 0;
@@ -5414,17 +5506,19 @@ static int __bnxt_reserve_rings(struct bnxt *bp)
        if (bp->flags & BNXT_FLAG_AGG_RINGS)
                rx <<= 1;
        grp = bp->rx_nr_rings;
+       stat = bnxt_get_func_stat_ctxs(bp);
 
-       rc = bnxt_hwrm_reserve_rings(bp, tx, rx, grp, cp, vnic);
+       rc = bnxt_hwrm_reserve_rings(bp, tx, rx, grp, cp, stat, vnic);
        if (rc)
                return rc;
 
        tx = hw_resc->resv_tx_rings;
        if (BNXT_NEW_RM(bp)) {
                rx = hw_resc->resv_rx_rings;
-               cp = hw_resc->resv_cp_rings;
+               cp = hw_resc->resv_irqs;
                grp = hw_resc->resv_hw_ring_grps;
                vnic = hw_resc->resv_vnics;
+               stat = hw_resc->resv_stat_ctxs;
        }
 
        rx_rings = rx;
@@ -5443,6 +5537,10 @@ static int __bnxt_reserve_rings(struct bnxt *bp)
                }
        }
        rx_rings = min_t(int, rx_rings, grp);
+       cp = min_t(int, cp, bp->cp_nr_rings);
+       if (stat > bnxt_get_ulp_stat_ctxs(bp))
+               stat -= bnxt_get_ulp_stat_ctxs(bp);
+       cp = min_t(int, cp, stat);
        rc = bnxt_trim_rings(bp, &rx_rings, &tx, cp, sh);
        if (bp->flags & BNXT_FLAG_AGG_RINGS)
                rx = rx_rings << 1;
@@ -5451,14 +5549,15 @@ static int __bnxt_reserve_rings(struct bnxt *bp)
        bp->rx_nr_rings = rx_rings;
        bp->cp_nr_rings = cp;
 
-       if (!tx || !rx || !cp || !grp || !vnic)
+       if (!tx || !rx || !cp || !grp || !vnic || !stat)
                return -ENOMEM;
 
        return rc;
 }
 
 static int bnxt_hwrm_check_vf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
-                                   int ring_grps, int cp_rings, int vnics)
+                                   int ring_grps, int cp_rings, int stats,
+                                   int vnics)
 {
        struct hwrm_func_vf_cfg_input req = {0};
        u32 flags;
@@ -5468,7 +5567,7 @@ static int bnxt_hwrm_check_vf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
                return 0;
 
        __bnxt_hwrm_reserve_vf_rings(bp, &req, tx_rings, rx_rings, ring_grps,
-                                    cp_rings, vnics);
+                                    cp_rings, stats, vnics);
        flags = FUNC_VF_CFG_REQ_FLAGS_TX_ASSETS_TEST |
                FUNC_VF_CFG_REQ_FLAGS_RX_ASSETS_TEST |
                FUNC_VF_CFG_REQ_FLAGS_CMPL_ASSETS_TEST |
@@ -5486,14 +5585,15 @@ static int bnxt_hwrm_check_vf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
 }
 
 static int bnxt_hwrm_check_pf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
-                                   int ring_grps, int cp_rings, int vnics)
+                                   int ring_grps, int cp_rings, int stats,
+                                   int vnics)
 {
        struct hwrm_func_cfg_input req = {0};
        u32 flags;
        int rc;
 
        __bnxt_hwrm_reserve_pf_rings(bp, &req, tx_rings, rx_rings, ring_grps,
-                                    cp_rings, vnics);
+                                    cp_rings, stats, vnics);
        flags = FUNC_CFG_REQ_FLAGS_TX_ASSETS_TEST;
        if (BNXT_NEW_RM(bp)) {
                flags |= FUNC_CFG_REQ_FLAGS_RX_ASSETS_TEST |
@@ -5514,17 +5614,19 @@ static int bnxt_hwrm_check_pf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
 }
 
 static int bnxt_hwrm_check_rings(struct bnxt *bp, int tx_rings, int rx_rings,
-                                int ring_grps, int cp_rings, int vnics)
+                                int ring_grps, int cp_rings, int stats,
+                                int vnics)
 {
        if (bp->hwrm_spec_code < 0x10801)
                return 0;
 
        if (BNXT_PF(bp))
                return bnxt_hwrm_check_pf_rings(bp, tx_rings, rx_rings,
-                                               ring_grps, cp_rings, vnics);
+                                               ring_grps, cp_rings, stats,
+                                               vnics);
 
        return bnxt_hwrm_check_vf_rings(bp, tx_rings, rx_rings, ring_grps,
-                                       cp_rings, vnics);
+                                       cp_rings, stats, vnics);
 }
 
 static void bnxt_hwrm_coal_params_qcaps(struct bnxt *bp)
@@ -5949,8 +6051,11 @@ static void bnxt_hwrm_set_pg_attr(struct bnxt_ring_mem_info *rmem, u8 *pg_attr,
                pg_size = 2 << 4;
 
        *pg_attr = pg_size;
-       if (rmem->nr_pages > 1) {
-               *pg_attr |= 1;
+       if (rmem->depth >= 1) {
+               if (rmem->depth == 2)
+                       *pg_attr |= 2;
+               else
+                       *pg_attr |= 1;
                *pg_dir = cpu_to_le64(rmem->pg_tbl_map);
        } else {
                *pg_dir = cpu_to_le64(rmem->dma_arr[0]);
@@ -6027,6 +6132,22 @@ static int bnxt_hwrm_func_backing_store_cfg(struct bnxt *bp, u32 enables)
                                      &req.stat_pg_size_stat_lvl,
                                      &req.stat_page_dir);
        }
+       if (enables & FUNC_BACKING_STORE_CFG_REQ_ENABLES_MRAV) {
+               ctx_pg = &ctx->mrav_mem;
+               req.mrav_num_entries = cpu_to_le32(ctx_pg->entries);
+               req.mrav_entry_size = cpu_to_le16(ctx->mrav_entry_size);
+               bnxt_hwrm_set_pg_attr(&ctx_pg->ring_mem,
+                                     &req.mrav_pg_size_mrav_lvl,
+                                     &req.mrav_page_dir);
+       }
+       if (enables & FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM) {
+               ctx_pg = &ctx->tim_mem;
+               req.tim_num_entries = cpu_to_le32(ctx_pg->entries);
+               req.tim_entry_size = cpu_to_le16(ctx->tim_entry_size);
+               bnxt_hwrm_set_pg_attr(&ctx_pg->ring_mem,
+                                     &req.tim_pg_size_tim_lvl,
+                                     &req.tim_page_dir);
+       }
        for (i = 0, num_entries = &req.tqm_sp_num_entries,
             pg_attr = &req.tqm_sp_pg_size_tqm_sp_lvl,
             pg_dir = &req.tqm_sp_page_dir,
@@ -6047,25 +6168,104 @@ static int bnxt_hwrm_func_backing_store_cfg(struct bnxt *bp, u32 enables)
 }
 
 static int bnxt_alloc_ctx_mem_blk(struct bnxt *bp,
-                                 struct bnxt_ctx_pg_info *ctx_pg, u32 mem_size)
+                                 struct bnxt_ctx_pg_info *ctx_pg)
 {
        struct bnxt_ring_mem_info *rmem = &ctx_pg->ring_mem;
 
-       if (!mem_size)
-               return 0;
-
-       rmem->nr_pages = DIV_ROUND_UP(mem_size, BNXT_PAGE_SIZE);
-       if (rmem->nr_pages > MAX_CTX_PAGES) {
-               rmem->nr_pages = 0;
-               return -EINVAL;
-       }
        rmem->page_size = BNXT_PAGE_SIZE;
        rmem->pg_arr = ctx_pg->ctx_pg_arr;
        rmem->dma_arr = ctx_pg->ctx_dma_arr;
        rmem->flags = BNXT_RMEM_VALID_PTE_FLAG;
+       if (rmem->depth >= 1)
+               rmem->flags |= BNXT_RMEM_USE_FULL_PAGE_FLAG;
        return bnxt_alloc_ring(bp, rmem);
 }
 
+static int bnxt_alloc_ctx_pg_tbls(struct bnxt *bp,
+                                 struct bnxt_ctx_pg_info *ctx_pg, u32 mem_size,
+                                 u8 depth)
+{
+       struct bnxt_ring_mem_info *rmem = &ctx_pg->ring_mem;
+       int rc;
+
+       if (!mem_size)
+               return 0;
+
+       ctx_pg->nr_pages = DIV_ROUND_UP(mem_size, BNXT_PAGE_SIZE);
+       if (ctx_pg->nr_pages > MAX_CTX_TOTAL_PAGES) {
+               ctx_pg->nr_pages = 0;
+               return -EINVAL;
+       }
+       if (ctx_pg->nr_pages > MAX_CTX_PAGES || depth > 1) {
+               int nr_tbls, i;
+
+               rmem->depth = 2;
+               ctx_pg->ctx_pg_tbl = kcalloc(MAX_CTX_PAGES, sizeof(ctx_pg),
+                                            GFP_KERNEL);
+               if (!ctx_pg->ctx_pg_tbl)
+                       return -ENOMEM;
+               nr_tbls = DIV_ROUND_UP(ctx_pg->nr_pages, MAX_CTX_PAGES);
+               rmem->nr_pages = nr_tbls;
+               rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg);
+               if (rc)
+                       return rc;
+               for (i = 0; i < nr_tbls; i++) {
+                       struct bnxt_ctx_pg_info *pg_tbl;
+
+                       pg_tbl = kzalloc(sizeof(*pg_tbl), GFP_KERNEL);
+                       if (!pg_tbl)
+                               return -ENOMEM;
+                       ctx_pg->ctx_pg_tbl[i] = pg_tbl;
+                       rmem = &pg_tbl->ring_mem;
+                       rmem->pg_tbl = ctx_pg->ctx_pg_arr[i];
+                       rmem->pg_tbl_map = ctx_pg->ctx_dma_arr[i];
+                       rmem->depth = 1;
+                       rmem->nr_pages = MAX_CTX_PAGES;
+                       if (i == (nr_tbls - 1))
+                               rmem->nr_pages = ctx_pg->nr_pages %
+                                                MAX_CTX_PAGES;
+                       rc = bnxt_alloc_ctx_mem_blk(bp, pg_tbl);
+                       if (rc)
+                               break;
+               }
+       } else {
+               rmem->nr_pages = DIV_ROUND_UP(mem_size, BNXT_PAGE_SIZE);
+               if (rmem->nr_pages > 1 || depth)
+                       rmem->depth = 1;
+               rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg);
+       }
+       return rc;
+}
+
+static void bnxt_free_ctx_pg_tbls(struct bnxt *bp,
+                                 struct bnxt_ctx_pg_info *ctx_pg)
+{
+       struct bnxt_ring_mem_info *rmem = &ctx_pg->ring_mem;
+
+       if (rmem->depth > 1 || ctx_pg->nr_pages > MAX_CTX_PAGES ||
+           ctx_pg->ctx_pg_tbl) {
+               int i, nr_tbls = rmem->nr_pages;
+
+               for (i = 0; i < nr_tbls; i++) {
+                       struct bnxt_ctx_pg_info *pg_tbl;
+                       struct bnxt_ring_mem_info *rmem2;
+
+                       pg_tbl = ctx_pg->ctx_pg_tbl[i];
+                       if (!pg_tbl)
+                               continue;
+                       rmem2 = &pg_tbl->ring_mem;
+                       bnxt_free_ring(bp, rmem2);
+                       ctx_pg->ctx_pg_arr[i] = NULL;
+                       kfree(pg_tbl);
+                       ctx_pg->ctx_pg_tbl[i] = NULL;
+               }
+               kfree(ctx_pg->ctx_pg_tbl);
+               ctx_pg->ctx_pg_tbl = NULL;
+       }
+       bnxt_free_ring(bp, rmem);
+       ctx_pg->nr_pages = 0;
+}
+
 static void bnxt_free_ctx_mem(struct bnxt *bp)
 {
        struct bnxt_ctx_mem_info *ctx = bp->ctx;
@@ -6076,16 +6276,18 @@ static void bnxt_free_ctx_mem(struct bnxt *bp)
 
        if (ctx->tqm_mem[0]) {
                for (i = 0; i < bp->max_q + 1; i++)
-                       bnxt_free_ring(bp, &ctx->tqm_mem[i]->ring_mem);
+                       bnxt_free_ctx_pg_tbls(bp, ctx->tqm_mem[i]);
                kfree(ctx->tqm_mem[0]);
                ctx->tqm_mem[0] = NULL;
        }
 
-       bnxt_free_ring(bp, &ctx->stat_mem.ring_mem);
-       bnxt_free_ring(bp, &ctx->vnic_mem.ring_mem);
-       bnxt_free_ring(bp, &ctx->cq_mem.ring_mem);
-       bnxt_free_ring(bp, &ctx->srq_mem.ring_mem);
-       bnxt_free_ring(bp, &ctx->qp_mem.ring_mem);
+       bnxt_free_ctx_pg_tbls(bp, &ctx->tim_mem);
+       bnxt_free_ctx_pg_tbls(bp, &ctx->mrav_mem);
+       bnxt_free_ctx_pg_tbls(bp, &ctx->stat_mem);
+       bnxt_free_ctx_pg_tbls(bp, &ctx->vnic_mem);
+       bnxt_free_ctx_pg_tbls(bp, &ctx->cq_mem);
+       bnxt_free_ctx_pg_tbls(bp, &ctx->srq_mem);
+       bnxt_free_ctx_pg_tbls(bp, &ctx->qp_mem);
        ctx->flags &= ~BNXT_CTX_FLAG_INITED;
 }
 
@@ -6094,6 +6296,9 @@ static int bnxt_alloc_ctx_mem(struct bnxt *bp)
        struct bnxt_ctx_pg_info *ctx_pg;
        struct bnxt_ctx_mem_info *ctx;
        u32 mem_size, ena, entries;
+       u32 extra_srqs = 0;
+       u32 extra_qps = 0;
+       u8 pg_lvl = 1;
        int i, rc;
 
        rc = bnxt_hwrm_func_backing_store_qcaps(bp);
@@ -6106,24 +6311,31 @@ static int bnxt_alloc_ctx_mem(struct bnxt *bp)
        if (!ctx || (ctx->flags & BNXT_CTX_FLAG_INITED))
                return 0;
 
+       if (bp->flags & BNXT_FLAG_ROCE_CAP) {
+               pg_lvl = 2;
+               extra_qps = 65536;
+               extra_srqs = 8192;
+       }
+
        ctx_pg = &ctx->qp_mem;
-       ctx_pg->entries = ctx->qp_min_qp1_entries + ctx->qp_max_l2_entries;
+       ctx_pg->entries = ctx->qp_min_qp1_entries + ctx->qp_max_l2_entries +
+                         extra_qps;
        mem_size = ctx->qp_entry_size * ctx_pg->entries;
-       rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size);
+       rc = bnxt_alloc_ctx_pg_tbls(bp, ctx_pg, mem_size, pg_lvl);
        if (rc)
                return rc;
 
        ctx_pg = &ctx->srq_mem;
-       ctx_pg->entries = ctx->srq_max_l2_entries;
+       ctx_pg->entries = ctx->srq_max_l2_entries + extra_srqs;
        mem_size = ctx->srq_entry_size * ctx_pg->entries;
-       rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size);
+       rc = bnxt_alloc_ctx_pg_tbls(bp, ctx_pg, mem_size, pg_lvl);
        if (rc)
                return rc;
 
        ctx_pg = &ctx->cq_mem;
-       ctx_pg->entries = ctx->cq_max_l2_entries;
+       ctx_pg->entries = ctx->cq_max_l2_entries + extra_qps * 2;
        mem_size = ctx->cq_entry_size * ctx_pg->entries;
-       rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size);
+       rc = bnxt_alloc_ctx_pg_tbls(bp, ctx_pg, mem_size, pg_lvl);
        if (rc)
                return rc;
 
@@ -6131,26 +6343,47 @@ static int bnxt_alloc_ctx_mem(struct bnxt *bp)
        ctx_pg->entries = ctx->vnic_max_vnic_entries +
                          ctx->vnic_max_ring_table_entries;
        mem_size = ctx->vnic_entry_size * ctx_pg->entries;
-       rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size);
+       rc = bnxt_alloc_ctx_pg_tbls(bp, ctx_pg, mem_size, 1);
        if (rc)
                return rc;
 
        ctx_pg = &ctx->stat_mem;
        ctx_pg->entries = ctx->stat_max_entries;
        mem_size = ctx->stat_entry_size * ctx_pg->entries;
-       rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size);
+       rc = bnxt_alloc_ctx_pg_tbls(bp, ctx_pg, mem_size, 1);
+       if (rc)
+               return rc;
+
+       ena = 0;
+       if (!(bp->flags & BNXT_FLAG_ROCE_CAP))
+               goto skip_rdma;
+
+       ctx_pg = &ctx->mrav_mem;
+       ctx_pg->entries = extra_qps * 4;
+       mem_size = ctx->mrav_entry_size * ctx_pg->entries;
+       rc = bnxt_alloc_ctx_pg_tbls(bp, ctx_pg, mem_size, 2);
+       if (rc)
+               return rc;
+       ena = FUNC_BACKING_STORE_CFG_REQ_ENABLES_MRAV;
+
+       ctx_pg = &ctx->tim_mem;
+       ctx_pg->entries = ctx->qp_mem.entries;
+       mem_size = ctx->tim_entry_size * ctx_pg->entries;
+       rc = bnxt_alloc_ctx_pg_tbls(bp, ctx_pg, mem_size, 1);
        if (rc)
                return rc;
+       ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM;
 
-       entries = ctx->qp_max_l2_entries;
+skip_rdma:
+       entries = ctx->qp_max_l2_entries + extra_qps;
        entries = roundup(entries, ctx->tqm_entries_multiple);
        entries = clamp_t(u32, entries, ctx->tqm_min_entries_per_ring,
                          ctx->tqm_max_entries_per_ring);
-       for (i = 0, ena = 0; i < bp->max_q + 1; i++) {
+       for (i = 0; i < bp->max_q + 1; i++) {
                ctx_pg = ctx->tqm_mem[i];
                ctx_pg->entries = entries;
                mem_size = ctx->tqm_entry_size * entries;
-               rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size);
+               rc = bnxt_alloc_ctx_pg_tbls(bp, ctx_pg, mem_size, 1);
                if (rc)
                        return rc;
                ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_SP << i;
@@ -6177,7 +6410,8 @@ int bnxt_hwrm_func_resc_qcaps(struct bnxt *bp, bool all)
        req.fid = cpu_to_le16(0xffff);
 
        mutex_lock(&bp->hwrm_cmd_lock);
-       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       rc = _hwrm_send_message_silent(bp, &req, sizeof(req),
+                                      HWRM_CMD_TIMEOUT);
        if (rc) {
                rc = -EIO;
                goto hwrm_func_resc_qcaps_exit;
@@ -6207,7 +6441,7 @@ int bnxt_hwrm_func_resc_qcaps(struct bnxt *bp, bool all)
        if (bp->flags & BNXT_FLAG_CHIP_P5) {
                u16 max_msix = le16_to_cpu(resp->max_msix);
 
-               hw_resc->max_irqs = min_t(u16, hw_resc->max_irqs, max_msix);
+               hw_resc->max_nqs = max_msix;
                hw_resc->max_hw_ring_grps = hw_resc->max_rx_rings;
        }
 
@@ -6292,6 +6526,8 @@ hwrm_func_qcaps_exit:
        return rc;
 }
 
+static int bnxt_hwrm_queue_qportcfg(struct bnxt *bp);
+
 static int bnxt_hwrm_func_qcaps(struct bnxt *bp)
 {
        int rc;
@@ -6299,6 +6535,11 @@ static int bnxt_hwrm_func_qcaps(struct bnxt *bp)
        rc = __bnxt_hwrm_func_qcaps(bp);
        if (rc)
                return rc;
+       rc = bnxt_hwrm_queue_qportcfg(bp);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm query qportcfg failure rc: %d\n", rc);
+               return rc;
+       }
        if (bp->hwrm_spec_code >= 0x10803) {
                rc = bnxt_alloc_ctx_mem(bp);
                if (rc)
@@ -6422,6 +6663,13 @@ static int bnxt_hwrm_ver_get(struct bnxt *bp)
            (dev_caps_cfg & VER_GET_RESP_DEV_CAPS_CFG_SHORT_CMD_REQUIRED))
                bp->fw_cap |= BNXT_FW_CAP_SHORT_CMD;
 
+       if (dev_caps_cfg & VER_GET_RESP_DEV_CAPS_CFG_KONG_MB_CHNL_SUPPORTED)
+               bp->fw_cap |= BNXT_FW_CAP_KONG_MB_CHNL;
+
+       if (dev_caps_cfg &
+           VER_GET_RESP_DEV_CAPS_CFG_FLOW_HANDLE_64BIT_SUPPORTED)
+               bp->fw_cap |= BNXT_FW_CAP_OVS_64BIT_HANDLE;
+
 hwrm_ver_get_exit:
        mutex_unlock(&bp->hwrm_cmd_lock);
        return rc;
@@ -6468,6 +6716,7 @@ static int bnxt_hwrm_port_qstats(struct bnxt *bp)
 static int bnxt_hwrm_port_qstats_ext(struct bnxt *bp)
 {
        struct hwrm_port_qstats_ext_output *resp = bp->hwrm_cmd_resp_addr;
+       struct hwrm_queue_pri2cos_qcfg_input req2 = {0};
        struct hwrm_port_qstats_ext_input req = {0};
        struct bnxt_pf_info *pf = &bp->pf;
        int rc;
@@ -6490,6 +6739,34 @@ static int bnxt_hwrm_port_qstats_ext(struct bnxt *bp)
                bp->fw_rx_stats_ext_size = 0;
                bp->fw_tx_stats_ext_size = 0;
        }
+       if (bp->fw_tx_stats_ext_size <=
+           offsetof(struct tx_port_stats_ext, pfc_pri0_tx_duration_us) / 8) {
+               mutex_unlock(&bp->hwrm_cmd_lock);
+               bp->pri2cos_valid = 0;
+               return rc;
+       }
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req2, HWRM_QUEUE_PRI2COS_QCFG, -1, -1);
+       req2.flags = cpu_to_le32(QUEUE_PRI2COS_QCFG_REQ_FLAGS_IVLAN);
+
+       rc = _hwrm_send_message(bp, &req2, sizeof(req2), HWRM_CMD_TIMEOUT);
+       if (!rc) {
+               struct hwrm_queue_pri2cos_qcfg_output *resp2;
+               u8 *pri2cos;
+               int i, j;
+
+               resp2 = bp->hwrm_cmd_resp_addr;
+               pri2cos = &resp2->pri0_cos_queue_id;
+               for (i = 0; i < 8; i++) {
+                       u8 queue_id = pri2cos[i];
+
+                       for (j = 0; j < bp->max_q; j++) {
+                               if (bp->q_ids[j] == queue_id)
+                                       bp->pri2cos[i] = j;
+                       }
+               }
+               bp->pri2cos_valid = 1;
+       }
        mutex_unlock(&bp->hwrm_cmd_lock);
        return rc;
 }
@@ -7014,25 +7291,28 @@ unsigned int bnxt_get_max_func_stat_ctxs(struct bnxt *bp)
        return bp->hw_resc.max_stat_ctxs;
 }
 
-void bnxt_set_max_func_stat_ctxs(struct bnxt *bp, unsigned int max)
-{
-       bp->hw_resc.max_stat_ctxs = max;
-}
-
 unsigned int bnxt_get_max_func_cp_rings(struct bnxt *bp)
 {
        return bp->hw_resc.max_cp_rings;
 }
 
-unsigned int bnxt_get_max_func_cp_rings_for_en(struct bnxt *bp)
+static unsigned int bnxt_get_max_func_cp_rings_for_en(struct bnxt *bp)
 {
-       return bp->hw_resc.max_cp_rings - bnxt_get_ulp_msix_num(bp);
+       unsigned int cp = bp->hw_resc.max_cp_rings;
+
+       if (!(bp->flags & BNXT_FLAG_CHIP_P5))
+               cp -= bnxt_get_ulp_msix_num(bp);
+
+       return cp;
 }
 
 static unsigned int bnxt_get_max_func_irqs(struct bnxt *bp)
 {
        struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
 
+       if (bp->flags & BNXT_FLAG_CHIP_P5)
+               return min_t(unsigned int, hw_resc->max_irqs, hw_resc->max_nqs);
+
        return min_t(unsigned int, hw_resc->max_irqs, hw_resc->max_cp_rings);
 }
 
@@ -7041,6 +7321,26 @@ static void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max_irqs)
        bp->hw_resc.max_irqs = max_irqs;
 }
 
+unsigned int bnxt_get_avail_cp_rings_for_en(struct bnxt *bp)
+{
+       unsigned int cp;
+
+       cp = bnxt_get_max_func_cp_rings_for_en(bp);
+       if (bp->flags & BNXT_FLAG_CHIP_P5)
+               return cp - bp->rx_nr_rings - bp->tx_nr_rings;
+       else
+               return cp - bp->cp_nr_rings;
+}
+
+unsigned int bnxt_get_avail_stat_ctxs_for_en(struct bnxt *bp)
+{
+       unsigned int stat;
+
+       stat = bnxt_get_max_func_stat_ctxs(bp) - bnxt_get_ulp_stat_ctxs(bp);
+       stat -= bp->cp_nr_rings;
+       return stat;
+}
+
 int bnxt_get_avail_msix(struct bnxt *bp, int num)
 {
        int max_cp = bnxt_get_max_func_cp_rings(bp);
@@ -7048,7 +7348,9 @@ int bnxt_get_avail_msix(struct bnxt *bp, int num)
        int total_req = bp->cp_nr_rings + num;
        int max_idx, avail_msix;
 
-       max_idx = min_t(int, bp->total_irqs, max_cp);
+       max_idx = bp->total_irqs;
+       if (!(bp->flags & BNXT_FLAG_CHIP_P5))
+               max_idx = min_t(int, bp->total_irqs, max_cp);
        avail_msix = max_idx - bp->cp_nr_rings;
        if (!BNXT_NEW_RM(bp) || avail_msix >= num)
                return avail_msix;
@@ -7066,7 +7368,7 @@ static int bnxt_get_num_msix(struct bnxt *bp)
        if (!BNXT_NEW_RM(bp))
                return bnxt_get_max_func_irqs(bp);
 
-       return bnxt_cp_rings_in_use(bp);
+       return bnxt_nq_rings_in_use(bp);
 }
 
 static int bnxt_init_msix(struct bnxt *bp)
@@ -7176,23 +7478,26 @@ static void bnxt_clear_int_mode(struct bnxt *bp)
 int bnxt_reserve_rings(struct bnxt *bp)
 {
        int tcs = netdev_get_num_tc(bp->dev);
+       bool reinit_irq = false;
        int rc;
 
        if (!bnxt_need_reserve_rings(bp))
                return 0;
 
-       rc = __bnxt_reserve_rings(bp);
-       if (rc) {
-               netdev_err(bp->dev, "ring reservation failure rc: %d\n", rc);
-               return rc;
-       }
        if (BNXT_NEW_RM(bp) && (bnxt_get_num_msix(bp) != bp->total_irqs)) {
                bnxt_ulp_irq_stop(bp);
                bnxt_clear_int_mode(bp);
-               rc = bnxt_init_int_mode(bp);
+               reinit_irq = true;
+       }
+       rc = __bnxt_reserve_rings(bp);
+       if (reinit_irq) {
+               if (!rc)
+                       rc = bnxt_init_int_mode(bp);
                bnxt_ulp_irq_restart(bp, rc);
-               if (rc)
-                       return rc;
+       }
+       if (rc) {
+               netdev_err(bp->dev, "ring reservation/IRQ init failure rc: %d\n", rc);
+               return rc;
        }
        if (tcs && (bp->tx_nr_rings_per_tc * tcs != bp->tx_nr_rings)) {
                netdev_err(bp->dev, "tx ring reservation failure\n");
@@ -7200,7 +7505,6 @@ int bnxt_reserve_rings(struct bnxt *bp)
                bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
                return -ENOMEM;
        }
-       bp->num_stat_ctxs = bp->cp_nr_rings;
        return 0;
 }
 
@@ -7794,6 +8098,8 @@ static int bnxt_hwrm_if_change(struct bnxt *bp, bool up)
 
                rc = bnxt_hwrm_func_resc_qcaps(bp, true);
                hw_resc->resv_cp_rings = 0;
+               hw_resc->resv_stat_ctxs = 0;
+               hw_resc->resv_irqs = 0;
                hw_resc->resv_tx_rings = 0;
                hw_resc->resv_rx_rings = 0;
                hw_resc->resv_hw_ring_grps = 0;
@@ -8232,6 +8538,9 @@ static bool bnxt_drv_busy(struct bnxt *bp)
                test_bit(BNXT_STATE_READ_STATS, &bp->state));
 }
 
+static void bnxt_get_ring_stats(struct bnxt *bp,
+                               struct rtnl_link_stats64 *stats);
+
 static void __bnxt_close_nic(struct bnxt *bp, bool irq_re_init,
                             bool link_re_init)
 {
@@ -8257,6 +8566,9 @@ static void __bnxt_close_nic(struct bnxt *bp, bool irq_re_init,
        del_timer_sync(&bp->timer);
        bnxt_free_skbs(bp);
 
+       /* Save ring stats before shutdown */
+       if (bp->bnapi)
+               bnxt_get_ring_stats(bp, &bp->net_stats_prev);
        if (irq_re_init) {
                bnxt_free_irq(bp);
                bnxt_del_napi(bp);
@@ -8318,23 +8630,12 @@ static int bnxt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        return -EOPNOTSUPP;
 }
 
-static void
-bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+static void bnxt_get_ring_stats(struct bnxt *bp,
+                               struct rtnl_link_stats64 *stats)
 {
-       u32 i;
-       struct bnxt *bp = netdev_priv(dev);
+       int i;
 
-       set_bit(BNXT_STATE_READ_STATS, &bp->state);
-       /* Make sure bnxt_close_nic() sees that we are reading stats before
-        * we check the BNXT_STATE_OPEN flag.
-        */
-       smp_mb__after_atomic();
-       if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
-               clear_bit(BNXT_STATE_READ_STATS, &bp->state);
-               return;
-       }
 
-       /* TODO check if we need to synchronize with bnxt_close path */
        for (i = 0; i < bp->cp_nr_rings; i++) {
                struct bnxt_napi *bnapi = bp->bnapi[i];
                struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
@@ -8363,6 +8664,40 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 
                stats->tx_dropped += le64_to_cpu(hw_stats->tx_drop_pkts);
        }
+}
+
+static void bnxt_add_prev_stats(struct bnxt *bp,
+                               struct rtnl_link_stats64 *stats)
+{
+       struct rtnl_link_stats64 *prev_stats = &bp->net_stats_prev;
+
+       stats->rx_packets += prev_stats->rx_packets;
+       stats->tx_packets += prev_stats->tx_packets;
+       stats->rx_bytes += prev_stats->rx_bytes;
+       stats->tx_bytes += prev_stats->tx_bytes;
+       stats->rx_missed_errors += prev_stats->rx_missed_errors;
+       stats->multicast += prev_stats->multicast;
+       stats->tx_dropped += prev_stats->tx_dropped;
+}
+
+static void
+bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       set_bit(BNXT_STATE_READ_STATS, &bp->state);
+       /* Make sure bnxt_close_nic() sees that we are reading stats before
+        * we check the BNXT_STATE_OPEN flag.
+        */
+       smp_mb__after_atomic();
+       if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
+               clear_bit(BNXT_STATE_READ_STATS, &bp->state);
+               *stats = bp->net_stats_prev;
+               return;
+       }
+
+       bnxt_get_ring_stats(bp, stats);
+       bnxt_add_prev_stats(bp, stats);
 
        if (bp->flags & BNXT_FLAG_PORT_STATS) {
                struct rx_port_stats *rx = bp->hw_rx_port_stats;
@@ -8598,12 +8933,12 @@ static bool bnxt_rfs_capable(struct bnxt *bp)
        if (vnics == bp->hw_resc.resv_vnics)
                return true;
 
-       bnxt_hwrm_reserve_rings(bp, 0, 0, 0, 0, vnics);
+       bnxt_hwrm_reserve_rings(bp, 0, 0, 0, 0, 0, vnics);
        if (vnics <= bp->hw_resc.resv_vnics)
                return true;
 
        netdev_warn(bp->dev, "Unable to reserve resources to support NTUPLE filters.\n");
-       bnxt_hwrm_reserve_rings(bp, 0, 0, 0, 0, 1);
+       bnxt_hwrm_reserve_rings(bp, 0, 0, 0, 0, 0, 1);
        return false;
 #else
        return false;
@@ -8714,6 +9049,26 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features)
        return rc;
 }
 
+static int bnxt_dbg_hwrm_ring_info_get(struct bnxt *bp, u8 ring_type,
+                                      u32 ring_id, u32 *prod, u32 *cons)
+{
+       struct hwrm_dbg_ring_info_get_output *resp = bp->hwrm_cmd_resp_addr;
+       struct hwrm_dbg_ring_info_get_input req = {0};
+       int rc;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_DBG_RING_INFO_GET, -1, -1);
+       req.ring_type = ring_type;
+       req.fw_ring_id = cpu_to_le32(ring_id);
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc) {
+               *prod = le32_to_cpu(resp->producer_index);
+               *cons = le32_to_cpu(resp->consumer_index);
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
 static void bnxt_dump_tx_sw_state(struct bnxt_napi *bnapi)
 {
        struct bnxt_tx_ring_info *txr = bnapi->tx_ring;
@@ -8821,6 +9176,11 @@ static void bnxt_timer(struct timer_list *t)
                        bnxt_queue_sp_work(bp);
                }
        }
+
+       if ((bp->flags & BNXT_FLAG_CHIP_P5) && netif_carrier_ok(dev)) {
+               set_bit(BNXT_RING_COAL_NOW_SP_EVENT, &bp->sp_event);
+               bnxt_queue_sp_work(bp);
+       }
 bnxt_restart_timer:
        mod_timer(&bp->timer, jiffies + bp->current_interval);
 }
@@ -8851,6 +9211,44 @@ static void bnxt_reset(struct bnxt *bp, bool silent)
        bnxt_rtnl_unlock_sp(bp);
 }
 
+static void bnxt_chk_missed_irq(struct bnxt *bp)
+{
+       int i;
+
+       if (!(bp->flags & BNXT_FLAG_CHIP_P5))
+               return;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr;
+               u32 fw_ring_id;
+               int j;
+
+               if (!bnapi)
+                       continue;
+
+               cpr = &bnapi->cp_ring;
+               for (j = 0; j < 2; j++) {
+                       struct bnxt_cp_ring_info *cpr2 = cpr->cp_ring_arr[j];
+                       u32 val[2];
+
+                       if (!cpr2 || cpr2->has_more_work ||
+                           !bnxt_has_work(bp, cpr2))
+                               continue;
+
+                       if (cpr2->cp_raw_cons != cpr2->last_cp_raw_cons) {
+                               cpr2->last_cp_raw_cons = cpr2->cp_raw_cons;
+                               continue;
+                       }
+                       fw_ring_id = cpr2->cp_ring_struct.fw_ring_id;
+                       bnxt_dbg_hwrm_ring_info_get(bp,
+                               DBG_RING_INFO_GET_REQ_RING_TYPE_L2_CMPL,
+                               fw_ring_id, &val[0], &val[1]);
+                       cpr->missed_irqs++;
+               }
+       }
+}
+
 static void bnxt_cfg_ntp_filters(struct bnxt *);
 
 static void bnxt_sp_task(struct work_struct *work)
@@ -8930,6 +9328,9 @@ static void bnxt_sp_task(struct work_struct *work)
        if (test_and_clear_bit(BNXT_FLOW_STATS_SP_EVENT, &bp->sp_event))
                bnxt_tc_flow_stats_work(bp);
 
+       if (test_and_clear_bit(BNXT_RING_COAL_NOW_SP_EVENT, &bp->sp_event))
+               bnxt_chk_missed_irq(bp);
+
        /* These functions below will clear BNXT_STATE_IN_SP_TASK.  They
         * must be the last functions to be called before exiting.
         */
@@ -8948,7 +9349,7 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
                     int tx_xdp)
 {
        int max_rx, max_tx, tx_sets = 1;
-       int tx_rings_needed;
+       int tx_rings_needed, stats;
        int rx_rings = rx;
        int cp, vnics, rc;
 
@@ -8973,10 +9374,13 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
        if (bp->flags & BNXT_FLAG_AGG_RINGS)
                rx_rings <<= 1;
        cp = sh ? max_t(int, tx_rings_needed, rx) : tx_rings_needed + rx;
-       if (BNXT_NEW_RM(bp))
+       stats = cp;
+       if (BNXT_NEW_RM(bp)) {
                cp += bnxt_get_ulp_msix_num(bp);
+               stats += bnxt_get_ulp_stat_ctxs(bp);
+       }
        return bnxt_hwrm_check_rings(bp, tx_rings_needed, rx_rings, rx, cp,
-                                    vnics);
+                                    stats, vnics);
 }
 
 static void bnxt_unmap_bars(struct bnxt *bp, struct pci_dev *pdev)
@@ -9012,7 +9416,7 @@ static void bnxt_init_dflt_coal(struct bnxt *bp)
         * 1 coal_buf x bufs_per_record = 1 completion record.
         */
        coal = &bp->rx_coal;
-       coal->coal_ticks = 14;
+       coal->coal_ticks = 10;
        coal->coal_bufs = 30;
        coal->coal_ticks_irq = 1;
        coal->coal_bufs_irq = 2;
@@ -9200,7 +9604,6 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc)
        bp->tx_nr_rings += bp->tx_nr_rings_xdp;
        bp->cp_nr_rings = sh ? max_t(int, bp->tx_nr_rings, bp->rx_nr_rings) :
                               bp->tx_nr_rings + bp->rx_nr_rings;
-       bp->num_stat_ctxs = bp->cp_nr_rings;
 
        if (netif_running(bp->dev))
                return bnxt_open_nic(bp, true, false);
@@ -9523,7 +9926,7 @@ static int bnxt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
 }
 
 static int bnxt_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
-                              u16 flags)
+                              u16 flags, struct netlink_ext_ack *extack)
 {
        struct bnxt *bp = netdev_priv(dev);
        struct nlattr *attr, *br_spec;
@@ -9666,6 +10069,7 @@ static void bnxt_remove_one(struct pci_dev *pdev)
        kfree(bp->ctx);
        bp->ctx = NULL;
        bnxt_cleanup_pci(bp);
+       bnxt_free_port_stats(bp);
        free_netdev(dev);
 }
 
@@ -9733,13 +10137,16 @@ static void _bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx,
                                int *max_cp)
 {
        struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
-       int max_ring_grps = 0;
+       int max_ring_grps = 0, max_irq;
 
        *max_tx = hw_resc->max_tx_rings;
        *max_rx = hw_resc->max_rx_rings;
-       *max_cp = min_t(int, bnxt_get_max_func_cp_rings_for_en(bp),
-                       hw_resc->max_irqs - bnxt_get_ulp_msix_num(bp));
-       *max_cp = min_t(int, *max_cp, hw_resc->max_stat_ctxs);
+       *max_cp = bnxt_get_max_func_cp_rings_for_en(bp);
+       max_irq = min_t(int, bnxt_get_max_func_irqs(bp) -
+                       bnxt_get_ulp_msix_num(bp),
+                       hw_resc->max_stat_ctxs - bnxt_get_ulp_stat_ctxs(bp));
+       if (!(bp->flags & BNXT_FLAG_CHIP_P5))
+               *max_cp = min_t(int, *max_cp, max_irq);
        max_ring_grps = hw_resc->max_hw_ring_grps;
        if (BNXT_CHIP_TYPE_NITRO_A0(bp) && BNXT_PF(bp)) {
                *max_cp -= 1;
@@ -9747,6 +10154,11 @@ static void _bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx,
        }
        if (bp->flags & BNXT_FLAG_AGG_RINGS)
                *max_rx >>= 1;
+       if (bp->flags & BNXT_FLAG_CHIP_P5) {
+               bnxt_trim_rings(bp, max_rx, max_tx, *max_cp, false);
+               /* On P5 chips, max_cp output param should be available NQs */
+               *max_cp = max_irq;
+       }
        *max_rx = min_t(int, *max_rx, max_ring_grps);
 }
 
@@ -9863,7 +10275,6 @@ static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh)
                        netdev_warn(bp->dev, "2nd rings reservation failed.\n");
                bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
        }
-       bp->num_stat_ctxs = bp->cp_nr_rings;
        if (BNXT_CHIP_TYPE_NITRO_A0(bp)) {
                bp->rx_nr_rings++;
                bp->cp_nr_rings++;
@@ -9997,6 +10408,12 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (rc)
                goto init_err_pci_clean;
 
+       if (bp->fw_cap & BNXT_FW_CAP_KONG_MB_CHNL) {
+               rc = bnxt_alloc_kong_hwrm_resources(bp);
+               if (rc)
+                       bp->fw_cap &= ~BNXT_FW_CAP_KONG_MB_CHNL;
+       }
+
        if ((bp->fw_cap & BNXT_FW_CAP_SHORT_CMD) ||
            bp->hwrm_max_ext_req_len > BNXT_HWRM_MAX_REQ_LEN) {
                rc = bnxt_alloc_hwrm_short_cmd_req(bp);
@@ -10087,6 +10504,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        bnxt_hwrm_func_qcfg(bp);
+       bnxt_hwrm_vnic_qcaps(bp);
        bnxt_hwrm_port_led_qcaps(bp);
        bnxt_ethtool_init(bp);
        bnxt_dcb_init(bp);
@@ -10120,7 +10538,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                                    VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6;
        }
 
-       bnxt_hwrm_vnic_qcaps(bp);
        if (bnxt_rfs_supported(bp)) {
                dev->hw_features |= NETIF_F_NTUPLE;
                if (bnxt_rfs_capable(bp)) {
index 498b373..a451796 100644 (file)
@@ -567,7 +567,6 @@ struct nqe_cn {
 #define HWRM_RESP_LEN_MASK             0xffff0000
 #define HWRM_RESP_LEN_SFT              16
 #define HWRM_RESP_VALID_MASK           0xff000000
-#define HWRM_SEQ_ID_INVALID            -1
 #define BNXT_HWRM_REQ_MAX_SIZE         128
 #define BNXT_HWRM_REQS_PER_PAGE                (BNXT_PAGE_SIZE /       \
                                         BNXT_HWRM_REQ_MAX_SIZE)
@@ -585,6 +584,9 @@ struct nqe_cn {
 
 #define HWRM_VALID_BIT_DELAY_USEC      20
 
+#define BNXT_HWRM_CHNL_CHIMP   0
+#define BNXT_HWRM_CHNL_KONG    1
+
 #define BNXT_RX_EVENT  1
 #define BNXT_AGG_EVENT 2
 #define BNXT_TX_EVENT  4
@@ -615,9 +617,12 @@ struct bnxt_sw_rx_agg_bd {
 struct bnxt_ring_mem_info {
        int                     nr_pages;
        int                     page_size;
-       u32                     flags;
+       u16                     flags;
 #define BNXT_RMEM_VALID_PTE_FLAG       1
 #define BNXT_RMEM_RING_PTE_FLAG                2
+#define BNXT_RMEM_USE_FULL_PAGE_FLAG   4
+
+       u16                     depth;
 
        void                    **pg_arr;
        dma_addr_t              *dma_arr;
@@ -798,6 +803,8 @@ struct bnxt_cp_ring_info {
        u8                      had_work_done:1;
        u8                      has_more_work:1;
 
+       u32                     last_cp_raw_cons;
+
        struct bnxt_coal        rx_ring_coal;
        u64                     rx_packets;
        u64                     rx_bytes;
@@ -816,6 +823,7 @@ struct bnxt_cp_ring_info {
        dma_addr_t              hw_stats_map;
        u32                     hw_stats_ctx_id;
        u64                     rx_l4_csum_errors;
+       u64                     missed_irqs;
 
        struct bnxt_ring_struct cp_ring_struct;
 
@@ -924,7 +932,10 @@ struct bnxt_hw_resc {
        u16     resv_vnics;
        u16     min_stat_ctxs;
        u16     max_stat_ctxs;
+       u16     resv_stat_ctxs;
+       u16     max_nqs;
        u16     max_irqs;
+       u16     resv_irqs;
 };
 
 #if defined(CONFIG_BNXT_SRIOV)
@@ -1107,9 +1118,14 @@ struct bnxt_test_info {
        char string[BNXT_MAX_TEST][ETH_GSTRING_LEN];
 };
 
-#define BNXT_GRCPF_REG_WINDOW_BASE_OUT 0x400
-#define BNXT_CAG_REG_LEGACY_INT_STATUS 0x4014
-#define BNXT_CAG_REG_BASE              0x300000
+#define BNXT_GRCPF_REG_CHIMP_COMM              0x0
+#define BNXT_GRCPF_REG_CHIMP_COMM_TRIGGER      0x100
+#define BNXT_GRCPF_REG_WINDOW_BASE_OUT         0x400
+#define BNXT_CAG_REG_LEGACY_INT_STATUS         0x4014
+#define BNXT_CAG_REG_BASE                      0x300000
+
+#define BNXT_GRCPF_REG_KONG_COMM               0xA00
+#define BNXT_GRCPF_REG_KONG_COMM_TRIGGER       0xB00
 
 struct bnxt_tc_flow_stats {
        u64             packets;
@@ -1177,12 +1193,15 @@ struct bnxt_vf_rep {
 #define PTU_PTE_NEXT_TO_LAST      0x4UL
 
 #define MAX_CTX_PAGES  (BNXT_PAGE_SIZE / 8)
+#define MAX_CTX_TOTAL_PAGES    (MAX_CTX_PAGES * MAX_CTX_PAGES)
 
 struct bnxt_ctx_pg_info {
        u32             entries;
+       u32             nr_pages;
        void            *ctx_pg_arr[MAX_CTX_PAGES];
        dma_addr_t      ctx_dma_arr[MAX_CTX_PAGES];
        struct bnxt_ring_mem_info ring_mem;
+       struct bnxt_ctx_pg_info **ctx_pg_tbl;
 };
 
 struct bnxt_ctx_mem_info {
@@ -1218,6 +1237,8 @@ struct bnxt_ctx_mem_info {
        struct bnxt_ctx_pg_info cq_mem;
        struct bnxt_ctx_pg_info vnic_mem;
        struct bnxt_ctx_pg_info stat_mem;
+       struct bnxt_ctx_pg_info mrav_mem;
+       struct bnxt_ctx_pg_info tim_mem;
        struct bnxt_ctx_pg_info *tqm_mem[9];
 };
 
@@ -1412,8 +1433,6 @@ struct bnxt {
        int                     cp_nr_pages;
        int                     cp_nr_rings;
 
-       int                     num_stat_ctxs;
-
        /* grp_info indexed by completion ring index */
        struct bnxt_ring_grp_info       *grp_info;
        struct bnxt_vnic_info   *vnic_info;
@@ -1453,21 +1472,27 @@ struct bnxt {
        u32                     msg_enable;
 
        u32                     fw_cap;
-       #define BNXT_FW_CAP_SHORT_CMD   0x00000001
-       #define BNXT_FW_CAP_LLDP_AGENT  0x00000002
-       #define BNXT_FW_CAP_DCBX_AGENT  0x00000004
-       #define BNXT_FW_CAP_NEW_RM      0x00000008
-       #define BNXT_FW_CAP_IF_CHANGE   0x00000010
+       #define BNXT_FW_CAP_SHORT_CMD                   0x00000001
+       #define BNXT_FW_CAP_LLDP_AGENT                  0x00000002
+       #define BNXT_FW_CAP_DCBX_AGENT                  0x00000004
+       #define BNXT_FW_CAP_NEW_RM                      0x00000008
+       #define BNXT_FW_CAP_IF_CHANGE                   0x00000010
+       #define BNXT_FW_CAP_KONG_MB_CHNL                0x00000080
+       #define BNXT_FW_CAP_OVS_64BIT_HANDLE            0x00000400
 
 #define BNXT_NEW_RM(bp)                ((bp)->fw_cap & BNXT_FW_CAP_NEW_RM)
        u32                     hwrm_spec_code;
        u16                     hwrm_cmd_seq;
-       u32                     hwrm_intr_seq_id;
+       u16                     hwrm_cmd_kong_seq;
+       u16                     hwrm_intr_seq_id;
        void                    *hwrm_short_cmd_req_addr;
        dma_addr_t              hwrm_short_cmd_req_dma_addr;
        void                    *hwrm_cmd_resp_addr;
        dma_addr_t              hwrm_cmd_resp_dma_addr;
+       void                    *hwrm_cmd_kong_resp_addr;
+       dma_addr_t              hwrm_cmd_kong_resp_dma_addr;
 
+       struct rtnl_link_stats64        net_stats_prev;
        struct rx_port_stats    *hw_rx_port_stats;
        struct tx_port_stats    *hw_tx_port_stats;
        struct rx_port_stats_ext        *hw_rx_port_stats_ext;
@@ -1479,6 +1504,8 @@ struct bnxt {
        int                     hw_port_stats_size;
        u16                     fw_rx_stats_ext_size;
        u16                     fw_tx_stats_ext_size;
+       u8                      pri2cos[8];
+       u8                      pri2cos_valid;
 
        u16                     hwrm_max_req_len;
        u16                     hwrm_max_ext_req_len;
@@ -1527,6 +1554,7 @@ struct bnxt {
 #define BNXT_LINK_SPEED_CHNG_SP_EVENT  14
 #define BNXT_FLOW_STATS_SP_EVENT       15
 #define BNXT_UPDATE_PHY_SP_EVENT       16
+#define BNXT_RING_COAL_NOW_SP_EVENT    17
 
        struct bnxt_hw_resc     hw_resc;
        struct bnxt_pf_info     pf;
@@ -1664,6 +1692,66 @@ static inline void bnxt_db_write(struct bnxt *bp, struct bnxt_db_info *db,
        }
 }
 
+static inline bool bnxt_cfa_hwrm_message(u16 req_type)
+{
+       switch (req_type) {
+       case HWRM_CFA_ENCAP_RECORD_ALLOC:
+       case HWRM_CFA_ENCAP_RECORD_FREE:
+       case HWRM_CFA_DECAP_FILTER_ALLOC:
+       case HWRM_CFA_DECAP_FILTER_FREE:
+       case HWRM_CFA_NTUPLE_FILTER_ALLOC:
+       case HWRM_CFA_NTUPLE_FILTER_FREE:
+       case HWRM_CFA_NTUPLE_FILTER_CFG:
+       case HWRM_CFA_EM_FLOW_ALLOC:
+       case HWRM_CFA_EM_FLOW_FREE:
+       case HWRM_CFA_EM_FLOW_CFG:
+       case HWRM_CFA_FLOW_ALLOC:
+       case HWRM_CFA_FLOW_FREE:
+       case HWRM_CFA_FLOW_INFO:
+       case HWRM_CFA_FLOW_FLUSH:
+       case HWRM_CFA_FLOW_STATS:
+       case HWRM_CFA_METER_PROFILE_ALLOC:
+       case HWRM_CFA_METER_PROFILE_FREE:
+       case HWRM_CFA_METER_PROFILE_CFG:
+       case HWRM_CFA_METER_INSTANCE_ALLOC:
+       case HWRM_CFA_METER_INSTANCE_FREE:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static inline bool bnxt_kong_hwrm_message(struct bnxt *bp, struct input *req)
+{
+       return (bp->fw_cap & BNXT_FW_CAP_KONG_MB_CHNL &&
+               bnxt_cfa_hwrm_message(le16_to_cpu(req->req_type)));
+}
+
+static inline bool bnxt_hwrm_kong_chnl(struct bnxt *bp, struct input *req)
+{
+       return (bp->fw_cap & BNXT_FW_CAP_KONG_MB_CHNL &&
+               req->resp_addr == cpu_to_le64(bp->hwrm_cmd_kong_resp_dma_addr));
+}
+
+static inline void *bnxt_get_hwrm_resp_addr(struct bnxt *bp, void *req)
+{
+       if (bnxt_hwrm_kong_chnl(bp, (struct input *)req))
+               return bp->hwrm_cmd_kong_resp_addr;
+       else
+               return bp->hwrm_cmd_resp_addr;
+}
+
+static inline u16 bnxt_get_hwrm_seq_id(struct bnxt *bp, u16 dst)
+{
+       u16 seq_id;
+
+       if (dst == BNXT_HWRM_CHNL_CHIMP)
+               seq_id = bp->hwrm_cmd_seq++;
+       else
+               seq_id = bp->hwrm_cmd_kong_seq++;
+       return seq_id;
+}
+
 extern const u16 bnxt_lhint_arr[];
 
 int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
@@ -1681,11 +1769,12 @@ int bnxt_hwrm_func_rgtr_async_events(struct bnxt *bp, unsigned long *bmap,
                                     int bmap_size);
 int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id);
 int __bnxt_hwrm_get_tx_rings(struct bnxt *bp, u16 fid, int *tx_rings);
+int bnxt_nq_rings_in_use(struct bnxt *bp);
 int bnxt_hwrm_set_coal(struct bnxt *);
 unsigned int bnxt_get_max_func_stat_ctxs(struct bnxt *bp);
-void bnxt_set_max_func_stat_ctxs(struct bnxt *bp, unsigned int max);
+unsigned int bnxt_get_avail_stat_ctxs_for_en(struct bnxt *bp);
 unsigned int bnxt_get_max_func_cp_rings(struct bnxt *bp);
-unsigned int bnxt_get_max_func_cp_rings_for_en(struct bnxt *bp);
+unsigned int bnxt_get_avail_cp_rings_for_en(struct bnxt *bp);
 int bnxt_get_avail_msix(struct bnxt *bp, int num);
 int bnxt_reserve_rings(struct bnxt *bp);
 void bnxt_tx_disable(struct bnxt *bp);
index a85d2be..15c7041 100644 (file)
@@ -471,7 +471,10 @@ static int bnxt_ets_validate(struct bnxt *bp, struct ieee_ets *ets, u8 *tc)
        if (total_ets_bw > 100)
                return -EINVAL;
 
-       *tc = max_tc + 1;
+       if (max_tc >= bp->max_tc)
+               *tc = bp->max_tc;
+       else
+               *tc = max_tc + 1;
        return 0;
 }
 
index 4807856..adabbe9 100644 (file)
@@ -137,7 +137,7 @@ reset_coalesce:
        return rc;
 }
 
-#define BNXT_NUM_STATS 21
+#define BNXT_NUM_STATS 22
 
 #define BNXT_RX_STATS_ENTRY(counter)   \
        { BNXT_RX_STATS_OFFSET(counter), __stringify(counter) }
@@ -207,6 +207,34 @@ reset_coalesce:
        BNXT_TX_STATS_EXT_COS_ENTRY(6),                         \
        BNXT_TX_STATS_EXT_COS_ENTRY(7)                          \
 
+#define BNXT_RX_STATS_PRI_ENTRY(counter, n)            \
+       { BNXT_RX_STATS_EXT_OFFSET(counter##_cos0),     \
+         __stringify(counter##_pri##n) }
+
+#define BNXT_TX_STATS_PRI_ENTRY(counter, n)            \
+       { BNXT_TX_STATS_EXT_OFFSET(counter##_cos0),     \
+         __stringify(counter##_pri##n) }
+
+#define BNXT_RX_STATS_PRI_ENTRIES(counter)             \
+       BNXT_RX_STATS_PRI_ENTRY(counter, 0),            \
+       BNXT_RX_STATS_PRI_ENTRY(counter, 1),            \
+       BNXT_RX_STATS_PRI_ENTRY(counter, 2),            \
+       BNXT_RX_STATS_PRI_ENTRY(counter, 3),            \
+       BNXT_RX_STATS_PRI_ENTRY(counter, 4),            \
+       BNXT_RX_STATS_PRI_ENTRY(counter, 5),            \
+       BNXT_RX_STATS_PRI_ENTRY(counter, 6),            \
+       BNXT_RX_STATS_PRI_ENTRY(counter, 7)
+
+#define BNXT_TX_STATS_PRI_ENTRIES(counter)             \
+       BNXT_TX_STATS_PRI_ENTRY(counter, 0),            \
+       BNXT_TX_STATS_PRI_ENTRY(counter, 1),            \
+       BNXT_TX_STATS_PRI_ENTRY(counter, 2),            \
+       BNXT_TX_STATS_PRI_ENTRY(counter, 3),            \
+       BNXT_TX_STATS_PRI_ENTRY(counter, 4),            \
+       BNXT_TX_STATS_PRI_ENTRY(counter, 5),            \
+       BNXT_TX_STATS_PRI_ENTRY(counter, 6),            \
+       BNXT_TX_STATS_PRI_ENTRY(counter, 7)
+
 enum {
        RX_TOTAL_DISCARDS,
        TX_TOTAL_DISCARDS,
@@ -327,8 +355,41 @@ static const struct {
        BNXT_TX_STATS_EXT_PFC_ENTRIES,
 };
 
+static const struct {
+       long base_off;
+       char string[ETH_GSTRING_LEN];
+} bnxt_rx_bytes_pri_arr[] = {
+       BNXT_RX_STATS_PRI_ENTRIES(rx_bytes),
+};
+
+static const struct {
+       long base_off;
+       char string[ETH_GSTRING_LEN];
+} bnxt_rx_pkts_pri_arr[] = {
+       BNXT_RX_STATS_PRI_ENTRIES(rx_packets),
+};
+
+static const struct {
+       long base_off;
+       char string[ETH_GSTRING_LEN];
+} bnxt_tx_bytes_pri_arr[] = {
+       BNXT_TX_STATS_PRI_ENTRIES(tx_bytes),
+};
+
+static const struct {
+       long base_off;
+       char string[ETH_GSTRING_LEN];
+} bnxt_tx_pkts_pri_arr[] = {
+       BNXT_TX_STATS_PRI_ENTRIES(tx_packets),
+};
+
 #define BNXT_NUM_SW_FUNC_STATS ARRAY_SIZE(bnxt_sw_func_stats)
 #define BNXT_NUM_PORT_STATS ARRAY_SIZE(bnxt_port_stats_arr)
+#define BNXT_NUM_STATS_PRI                     \
+       (ARRAY_SIZE(bnxt_rx_bytes_pri_arr) +    \
+        ARRAY_SIZE(bnxt_rx_pkts_pri_arr) +     \
+        ARRAY_SIZE(bnxt_tx_bytes_pri_arr) +    \
+        ARRAY_SIZE(bnxt_tx_pkts_pri_arr))
 
 static int bnxt_get_num_stats(struct bnxt *bp)
 {
@@ -339,9 +400,12 @@ static int bnxt_get_num_stats(struct bnxt *bp)
        if (bp->flags & BNXT_FLAG_PORT_STATS)
                num_stats += BNXT_NUM_PORT_STATS;
 
-       if (bp->flags & BNXT_FLAG_PORT_STATS_EXT)
+       if (bp->flags & BNXT_FLAG_PORT_STATS_EXT) {
                num_stats += bp->fw_rx_stats_ext_size +
                             bp->fw_tx_stats_ext_size;
+               if (bp->pri2cos_valid)
+                       num_stats += BNXT_NUM_STATS_PRI;
+       }
 
        return num_stats;
 }
@@ -369,8 +433,10 @@ static void bnxt_get_ethtool_stats(struct net_device *dev,
        struct bnxt *bp = netdev_priv(dev);
        u32 stat_fields = sizeof(struct ctx_hw_stats) / 8;
 
-       if (!bp->bnapi)
-               return;
+       if (!bp->bnapi) {
+               j += BNXT_NUM_STATS * bp->cp_nr_rings + BNXT_NUM_SW_FUNC_STATS;
+               goto skip_ring_stats;
+       }
 
        for (i = 0; i < BNXT_NUM_SW_FUNC_STATS; i++)
                bnxt_sw_func_stats[i].counter = 0;
@@ -384,6 +450,7 @@ static void bnxt_get_ethtool_stats(struct net_device *dev,
                for (k = 0; k < stat_fields; j++, k++)
                        buf[j] = le64_to_cpu(hw_stats[k]);
                buf[j++] = cpr->rx_l4_csum_errors;
+               buf[j++] = cpr->missed_irqs;
 
                bnxt_sw_func_stats[RX_TOTAL_DISCARDS].counter +=
                        le64_to_cpu(cpr->hw_stats->rx_discard_pkts);
@@ -394,6 +461,7 @@ static void bnxt_get_ethtool_stats(struct net_device *dev,
        for (i = 0; i < BNXT_NUM_SW_FUNC_STATS; i++, j++)
                buf[j] = bnxt_sw_func_stats[i].counter;
 
+skip_ring_stats:
        if (bp->flags & BNXT_FLAG_PORT_STATS) {
                __le64 *port_stats = (__le64 *)bp->hw_rx_port_stats;
 
@@ -414,6 +482,32 @@ static void bnxt_get_ethtool_stats(struct net_device *dev,
                        buf[j] = le64_to_cpu(*(tx_port_stats_ext +
                                        bnxt_tx_port_stats_ext_arr[i].offset));
                }
+               if (bp->pri2cos_valid) {
+                       for (i = 0; i < 8; i++, j++) {
+                               long n = bnxt_rx_bytes_pri_arr[i].base_off +
+                                        bp->pri2cos[i];
+
+                               buf[j] = le64_to_cpu(*(rx_port_stats_ext + n));
+                       }
+                       for (i = 0; i < 8; i++, j++) {
+                               long n = bnxt_rx_pkts_pri_arr[i].base_off +
+                                        bp->pri2cos[i];
+
+                               buf[j] = le64_to_cpu(*(rx_port_stats_ext + n));
+                       }
+                       for (i = 0; i < 8; i++, j++) {
+                               long n = bnxt_tx_bytes_pri_arr[i].base_off +
+                                        bp->pri2cos[i];
+
+                               buf[j] = le64_to_cpu(*(tx_port_stats_ext + n));
+                       }
+                       for (i = 0; i < 8; i++, j++) {
+                               long n = bnxt_tx_pkts_pri_arr[i].base_off +
+                                        bp->pri2cos[i];
+
+                               buf[j] = le64_to_cpu(*(tx_port_stats_ext + n));
+                       }
+               }
        }
 }
 
@@ -468,6 +562,8 @@ static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
                        buf += ETH_GSTRING_LEN;
                        sprintf(buf, "[%d]: rx_l4_csum_errors", i);
                        buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: missed_irqs", i);
+                       buf += ETH_GSTRING_LEN;
                }
                for (i = 0; i < BNXT_NUM_SW_FUNC_STATS; i++) {
                        strcpy(buf, bnxt_sw_func_stats[i].string);
@@ -490,6 +586,28 @@ static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
                                       bnxt_tx_port_stats_ext_arr[i].string);
                                buf += ETH_GSTRING_LEN;
                        }
+                       if (bp->pri2cos_valid) {
+                               for (i = 0; i < 8; i++) {
+                                       strcpy(buf,
+                                              bnxt_rx_bytes_pri_arr[i].string);
+                                       buf += ETH_GSTRING_LEN;
+                               }
+                               for (i = 0; i < 8; i++) {
+                                       strcpy(buf,
+                                              bnxt_rx_pkts_pri_arr[i].string);
+                                       buf += ETH_GSTRING_LEN;
+                               }
+                               for (i = 0; i < 8; i++) {
+                                       strcpy(buf,
+                                              bnxt_tx_bytes_pri_arr[i].string);
+                                       buf += ETH_GSTRING_LEN;
+                               }
+                               for (i = 0; i < 8; i++) {
+                                       strcpy(buf,
+                                              bnxt_tx_pkts_pri_arr[i].string);
+                                       buf += ETH_GSTRING_LEN;
+                               }
+                       }
                }
                break;
        case ETH_SS_TEST:
@@ -660,8 +778,6 @@ static int bnxt_set_channels(struct net_device *dev,
        bp->cp_nr_rings = sh ? max_t(int, bp->tx_nr_rings, bp->rx_nr_rings) :
                               bp->tx_nr_rings + bp->rx_nr_rings;
 
-       bp->num_stat_ctxs = bp->cp_nr_rings;
-
        /* After changing number of rx channels, update NTUPLE feature. */
        netdev_update_features(dev);
        if (netif_running(dev)) {
@@ -1523,14 +1639,22 @@ static int bnxt_flash_nvram(struct net_device *dev,
        rc = hwrm_send_message(bp, &req, sizeof(req), FLASH_NVRAM_TIMEOUT);
        dma_free_coherent(&bp->pdev->dev, data_len, kmem, dma_handle);
 
+       if (rc == HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED) {
+               netdev_info(dev,
+                           "PF does not have admin privileges to flash the device\n");
+               rc = -EACCES;
+       } else if (rc) {
+               rc = -EIO;
+       }
        return rc;
 }
 
 static int bnxt_firmware_reset(struct net_device *dev,
                               u16 dir_type)
 {
-       struct bnxt *bp = netdev_priv(dev);
        struct hwrm_fw_reset_input req = {0};
+       struct bnxt *bp = netdev_priv(dev);
+       int rc;
 
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FW_RESET, -1, -1);
 
@@ -1570,7 +1694,15 @@ static int bnxt_firmware_reset(struct net_device *dev,
                return -EINVAL;
        }
 
-       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (rc == HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED) {
+               netdev_info(dev,
+                           "PF does not have admin privileges to reset the device\n");
+               rc = -EACCES;
+       } else if (rc) {
+               rc = -EIO;
+       }
+       return rc;
 }
 
 static int bnxt_flash_firmware(struct net_device *dev,
@@ -1777,9 +1909,9 @@ static int bnxt_flash_package_from_file(struct net_device *dev,
        struct hwrm_nvm_install_update_output *resp = bp->hwrm_cmd_resp_addr;
        struct hwrm_nvm_install_update_input install = {0};
        const struct firmware *fw;
+       int rc, hwrm_err = 0;
        u32 item_len;
        u16 index;
-       int rc;
 
        bnxt_hwrm_fw_set_time(bp);
 
@@ -1822,15 +1954,16 @@ static int bnxt_flash_package_from_file(struct net_device *dev,
                        memcpy(kmem, fw->data, fw->size);
                        modify.host_src_addr = cpu_to_le64(dma_handle);
 
-                       rc = hwrm_send_message(bp, &modify, sizeof(modify),
-                                              FLASH_PACKAGE_TIMEOUT);
+                       hwrm_err = hwrm_send_message(bp, &modify,
+                                                    sizeof(modify),
+                                                    FLASH_PACKAGE_TIMEOUT);
                        dma_free_coherent(&bp->pdev->dev, fw->size, kmem,
                                          dma_handle);
                }
        }
        release_firmware(fw);
-       if (rc)
-               return rc;
+       if (rc || hwrm_err)
+               goto err_exit;
 
        if ((install_type & 0xffff) == 0)
                install_type >>= 16;
@@ -1838,12 +1971,10 @@ static int bnxt_flash_package_from_file(struct net_device *dev,
        install.install_type = cpu_to_le32(install_type);
 
        mutex_lock(&bp->hwrm_cmd_lock);
-       rc = _hwrm_send_message(bp, &install, sizeof(install),
-                               INSTALL_PACKAGE_TIMEOUT);
-       if (rc) {
-               rc = -EOPNOTSUPP;
+       hwrm_err = _hwrm_send_message(bp, &install, sizeof(install),
+                                     INSTALL_PACKAGE_TIMEOUT);
+       if (hwrm_err)
                goto flash_pkg_exit;
-       }
 
        if (resp->error_code) {
                u8 error_code = ((struct hwrm_err_output *)resp)->cmd_err;
@@ -1851,12 +1982,11 @@ static int bnxt_flash_package_from_file(struct net_device *dev,
                if (error_code == NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) {
                        install.flags |= cpu_to_le16(
                               NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG);
-                       rc = _hwrm_send_message(bp, &install, sizeof(install),
-                                               INSTALL_PACKAGE_TIMEOUT);
-                       if (rc) {
-                               rc = -EOPNOTSUPP;
+                       hwrm_err = _hwrm_send_message(bp, &install,
+                                                     sizeof(install),
+                                                     INSTALL_PACKAGE_TIMEOUT);
+                       if (hwrm_err)
                                goto flash_pkg_exit;
-                       }
                }
        }
 
@@ -1867,6 +1997,14 @@ static int bnxt_flash_package_from_file(struct net_device *dev,
        }
 flash_pkg_exit:
        mutex_unlock(&bp->hwrm_cmd_lock);
+err_exit:
+       if (hwrm_err == HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED) {
+               netdev_info(dev,
+                           "PF does not have admin privileges to flash the device\n");
+               rc = -EACCES;
+       } else if (hwrm_err) {
+               rc = -EOPNOTSUPP;
+       }
        return rc;
 }
 
@@ -2447,17 +2585,37 @@ static int bnxt_hwrm_mac_loopback(struct bnxt *bp, bool enable)
        return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
 }
 
+static int bnxt_query_force_speeds(struct bnxt *bp, u16 *force_speeds)
+{
+       struct hwrm_port_phy_qcaps_output *resp = bp->hwrm_cmd_resp_addr;
+       struct hwrm_port_phy_qcaps_input req = {0};
+       int rc;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_QCAPS, -1, -1);
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc)
+               *force_speeds = le16_to_cpu(resp->supported_speeds_force_mode);
+
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
 static int bnxt_disable_an_for_lpbk(struct bnxt *bp,
                                    struct hwrm_port_phy_cfg_input *req)
 {
        struct bnxt_link_info *link_info = &bp->link_info;
-       u16 fw_advertising = link_info->advertising;
+       u16 fw_advertising;
        u16 fw_speed;
        int rc;
 
        if (!link_info->autoneg)
                return 0;
 
+       rc = bnxt_query_force_speeds(bp, &fw_advertising);
+       if (rc)
+               return rc;
+
        fw_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_1GB;
        if (netif_carrier_ok(bp->dev))
                fw_speed = bp->link_info.link_speed;
@@ -2569,6 +2727,7 @@ static int bnxt_poll_loopback(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
 static int bnxt_run_loopback(struct bnxt *bp)
 {
        struct bnxt_tx_ring_info *txr = &bp->tx_ring[0];
+       struct bnxt_rx_ring_info *rxr = &bp->rx_ring[0];
        struct bnxt_cp_ring_info *cpr;
        int pkt_size, i = 0;
        struct sk_buff *skb;
@@ -2576,7 +2735,9 @@ static int bnxt_run_loopback(struct bnxt *bp)
        u8 *data;
        int rc;
 
-       cpr = &txr->bnapi->cp_ring;
+       cpr = &rxr->bnapi->cp_ring;
+       if (bp->flags & BNXT_FLAG_CHIP_P5)
+               cpr = cpr->cp_ring_arr[BNXT_RX_HDL];
        pkt_size = min(bp->dev->mtu + ETH_HLEN, bp->rx_copy_thresh);
        skb = netdev_alloc_skb(bp->dev, pkt_size);
        if (!skb)
@@ -2942,8 +3103,8 @@ bnxt_fill_coredump_record(struct bnxt *bp, struct bnxt_coredump_record *record,
        record->asic_state = 0;
        strlcpy(record->system_name, utsname()->nodename,
                sizeof(record->system_name));
-       record->year = cpu_to_le16(tm.tm_year);
-       record->month = cpu_to_le16(tm.tm_mon);
+       record->year = cpu_to_le16(tm.tm_year + 1900);
+       record->month = cpu_to_le16(tm.tm_mon + 1);
        record->day = cpu_to_le16(tm.tm_mday);
        record->hour = cpu_to_le16(tm.tm_hour);
        record->minute = cpu_to_le16(tm.tm_min);
index 5dd0860..f1aaac8 100644 (file)
@@ -194,6 +194,8 @@ struct cmd_nums {
        #define HWRM_STAT_CTX_QUERY                       0xb2UL
        #define HWRM_STAT_CTX_CLR_STATS                   0xb3UL
        #define HWRM_PORT_QSTATS_EXT                      0xb4UL
+       #define HWRM_PORT_PHY_MDIO_WRITE                  0xb5UL
+       #define HWRM_PORT_PHY_MDIO_READ                   0xb6UL
        #define HWRM_FW_RESET                             0xc0UL
        #define HWRM_FW_QSTATUS                           0xc1UL
        #define HWRM_FW_HEALTH_CHECK                      0xc2UL
@@ -213,6 +215,7 @@ struct cmd_nums {
        #define HWRM_WOL_FILTER_FREE                      0xf1UL
        #define HWRM_WOL_FILTER_QCFG                      0xf2UL
        #define HWRM_WOL_REASON_QCFG                      0xf3UL
+       #define HWRM_CFA_METER_QCAPS                      0xf4UL
        #define HWRM_CFA_METER_PROFILE_ALLOC              0xf5UL
        #define HWRM_CFA_METER_PROFILE_FREE               0xf6UL
        #define HWRM_CFA_METER_PROFILE_CFG                0xf7UL
@@ -239,6 +242,24 @@ struct cmd_nums {
        #define HWRM_FW_IPC_MSG                           0x110UL
        #define HWRM_CFA_REDIRECT_TUNNEL_TYPE_INFO        0x111UL
        #define HWRM_CFA_REDIRECT_QUERY_TUNNEL_TYPE       0x112UL
+       #define HWRM_CFA_FLOW_AGING_TIMER_RESET           0x113UL
+       #define HWRM_CFA_FLOW_AGING_CFG                   0x114UL
+       #define HWRM_CFA_FLOW_AGING_QCFG                  0x115UL
+       #define HWRM_CFA_FLOW_AGING_QCAPS                 0x116UL
+       #define HWRM_CFA_CTX_MEM_RGTR                     0x117UL
+       #define HWRM_CFA_CTX_MEM_UNRGTR                   0x118UL
+       #define HWRM_CFA_CTX_MEM_QCTX                     0x119UL
+       #define HWRM_CFA_CTX_MEM_QCAPS                    0x11aUL
+       #define HWRM_CFA_COUNTER_QCAPS                    0x11bUL
+       #define HWRM_CFA_COUNTER_CFG                      0x11cUL
+       #define HWRM_CFA_COUNTER_QCFG                     0x11dUL
+       #define HWRM_CFA_COUNTER_QSTATS                   0x11eUL
+       #define HWRM_CFA_TCP_FLAG_PROCESS_QCFG            0x11fUL
+       #define HWRM_CFA_EEM_QCAPS                        0x120UL
+       #define HWRM_CFA_EEM_CFG                          0x121UL
+       #define HWRM_CFA_EEM_QCFG                         0x122UL
+       #define HWRM_CFA_EEM_OP                           0x123UL
+       #define HWRM_CFA_ADV_FLOW_MGNT_QCAPS              0x124UL
        #define HWRM_ENGINE_CKV_HELLO                     0x12dUL
        #define HWRM_ENGINE_CKV_STATUS                    0x12eUL
        #define HWRM_ENGINE_CKV_CKEK_ADD                  0x12fUL
@@ -335,6 +356,8 @@ struct ret_codes {
        #define HWRM_ERR_CODE_UNSUPPORTED_TLV           0x7UL
        #define HWRM_ERR_CODE_NO_BUFFER                 0x8UL
        #define HWRM_ERR_CODE_UNSUPPORTED_OPTION_ERR    0x9UL
+       #define HWRM_ERR_CODE_HOT_RESET_PROGRESS        0xaUL
+       #define HWRM_ERR_CODE_HOT_RESET_FAIL            0xbUL
        #define HWRM_ERR_CODE_HWRM_ERROR                0xfUL
        #define HWRM_ERR_CODE_TLV_ENCAPSULATED_RESPONSE 0x8000UL
        #define HWRM_ERR_CODE_UNKNOWN_ERR               0xfffeUL
@@ -363,8 +386,8 @@ struct hwrm_err_output {
 #define HWRM_VERSION_MAJOR 1
 #define HWRM_VERSION_MINOR 10
 #define HWRM_VERSION_UPDATE 0
-#define HWRM_VERSION_RSVD 3
-#define HWRM_VERSION_STR "1.10.0.3"
+#define HWRM_VERSION_RSVD 33
+#define HWRM_VERSION_STR "1.10.0.33"
 
 /* hwrm_ver_get_input (size:192b/24B) */
 struct hwrm_ver_get_input {
@@ -411,6 +434,10 @@ struct hwrm_ver_get_output {
        #define VER_GET_RESP_DEV_CAPS_CFG_L2_FILTER_TYPES_ROCE_OR_L2_SUPPORTED     0x40UL
        #define VER_GET_RESP_DEV_CAPS_CFG_VIRTIO_VSWITCH_OFFLOAD_SUPPORTED         0x80UL
        #define VER_GET_RESP_DEV_CAPS_CFG_TRUSTED_VF_SUPPORTED                     0x100UL
+       #define VER_GET_RESP_DEV_CAPS_CFG_FLOW_AGING_SUPPORTED                     0x200UL
+       #define VER_GET_RESP_DEV_CAPS_CFG_ADV_FLOW_COUNTERS_SUPPORTED              0x400UL
+       #define VER_GET_RESP_DEV_CAPS_CFG_CFA_EEM_SUPPORTED                        0x800UL
+       #define VER_GET_RESP_DEV_CAPS_CFG_CFA_ADV_FLOW_MGNT_SUPPORTED              0x1000UL
        u8      roce_fw_maj_8b;
        u8      roce_fw_min_8b;
        u8      roce_fw_bld_8b;
@@ -465,14 +492,27 @@ struct hwrm_ver_get_output {
 /* eject_cmpl (size:128b/16B) */
 struct eject_cmpl {
        __le16  type;
-       #define EJECT_CMPL_TYPE_MASK      0x3fUL
-       #define EJECT_CMPL_TYPE_SFT       0
-       #define EJECT_CMPL_TYPE_STAT_EJECT  0x1aUL
-       #define EJECT_CMPL_TYPE_LAST       EJECT_CMPL_TYPE_STAT_EJECT
+       #define EJECT_CMPL_TYPE_MASK       0x3fUL
+       #define EJECT_CMPL_TYPE_SFT        0
+       #define EJECT_CMPL_TYPE_STAT_EJECT   0x1aUL
+       #define EJECT_CMPL_TYPE_LAST        EJECT_CMPL_TYPE_STAT_EJECT
+       #define EJECT_CMPL_FLAGS_MASK      0xffc0UL
+       #define EJECT_CMPL_FLAGS_SFT       6
+       #define EJECT_CMPL_FLAGS_ERROR      0x40UL
        __le16  len;
        __le32  opaque;
-       __le32  v;
-       #define EJECT_CMPL_V     0x1UL
+       __le16  v;
+       #define EJECT_CMPL_V                              0x1UL
+       #define EJECT_CMPL_ERRORS_MASK                    0xfffeUL
+       #define EJECT_CMPL_ERRORS_SFT                     1
+       #define EJECT_CMPL_ERRORS_BUFFER_ERROR_MASK        0xeUL
+       #define EJECT_CMPL_ERRORS_BUFFER_ERROR_SFT         1
+       #define EJECT_CMPL_ERRORS_BUFFER_ERROR_NO_BUFFER     (0x0UL << 1)
+       #define EJECT_CMPL_ERRORS_BUFFER_ERROR_DID_NOT_FIT   (0x1UL << 1)
+       #define EJECT_CMPL_ERRORS_BUFFER_ERROR_BAD_FORMAT    (0x3UL << 1)
+       #define EJECT_CMPL_ERRORS_BUFFER_ERROR_FLUSH         (0x5UL << 1)
+       #define EJECT_CMPL_ERRORS_BUFFER_ERROR_LAST         EJECT_CMPL_ERRORS_BUFFER_ERROR_FLUSH
+       __le16  reserved16;
        __le32  unused_2;
 };
 
@@ -552,6 +592,10 @@ struct hwrm_async_event_cmpl {
        #define ASYNC_EVENT_CMPL_EVENT_ID_LLFC_PFC_CHANGE            0x34UL
        #define ASYNC_EVENT_CMPL_EVENT_ID_DEFAULT_VNIC_CHANGE        0x35UL
        #define ASYNC_EVENT_CMPL_EVENT_ID_HW_FLOW_AGED               0x36UL
+       #define ASYNC_EVENT_CMPL_EVENT_ID_DEBUG_NOTIFICATION         0x37UL
+       #define ASYNC_EVENT_CMPL_EVENT_ID_EEM_CACHE_FLUSH_REQ        0x38UL
+       #define ASYNC_EVENT_CMPL_EVENT_ID_EEM_CACHE_FLUSH_DONE       0x39UL
+       #define ASYNC_EVENT_CMPL_EVENT_ID_FW_TRACE_MSG               0xfeUL
        #define ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR                 0xffUL
        #define ASYNC_EVENT_CMPL_EVENT_ID_LAST                      ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR
        __le32  event_data2;
@@ -647,6 +691,39 @@ struct hwrm_async_event_cmpl_link_speed_cfg_change {
        #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_DATA1_ILLEGAL_LINK_SPEED_CFG           0x20000UL
 };
 
+/* hwrm_async_event_cmpl_reset_notify (size:128b/16B) */
+struct hwrm_async_event_cmpl_reset_notify {
+       __le16  type;
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_TYPE_MASK            0x3fUL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_TYPE_SFT             0
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_TYPE_HWRM_ASYNC_EVENT  0x2eUL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_TYPE_LAST             ASYNC_EVENT_CMPL_RESET_NOTIFY_TYPE_HWRM_ASYNC_EVENT
+       __le16  event_id;
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_ID_RESET_NOTIFY 0x8UL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_ID_LAST        ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_ID_RESET_NOTIFY
+       __le32  event_data2;
+       u8      opaque_v;
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_V          0x1UL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_OPAQUE_MASK 0xfeUL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_OPAQUE_SFT 1
+       u8      timestamp_lo;
+       __le16  timestamp_hi;
+       __le32  event_data1;
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DRIVER_ACTION_MASK                  0xffUL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DRIVER_ACTION_SFT                   0
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DRIVER_ACTION_DRIVER_STOP_TX_QUEUE    0x1UL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DRIVER_ACTION_DRIVER_IFDOWN           0x2UL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DRIVER_ACTION_LAST                   ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DRIVER_ACTION_DRIVER_IFDOWN
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_MASK                    0xff00UL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_SFT                     8
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_MANAGEMENT_RESET_REQUEST  (0x1UL << 8)
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_EXCEPTION_FATAL        (0x2UL << 8)
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_EXCEPTION_NON_FATAL    (0x3UL << 8)
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_LAST                     ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_EXCEPTION_NON_FATAL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DELAY_IN_100MS_TICKS_MASK           0xffff0000UL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DELAY_IN_100MS_TICKS_SFT            16
+};
+
 /* hwrm_async_event_cmpl_vf_cfg_change (size:128b/16B) */
 struct hwrm_async_event_cmpl_vf_cfg_change {
        __le16  type;
@@ -672,6 +749,74 @@ struct hwrm_async_event_cmpl_vf_cfg_change {
        #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_DATA1_TRUSTED_VF_CFG_CHANGE     0x10UL
 };
 
+/* hwrm_async_event_cmpl_hw_flow_aged (size:128b/16B) */
+struct hwrm_async_event_cmpl_hw_flow_aged {
+       __le16  type;
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_TYPE_MASK            0x3fUL
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_TYPE_SFT             0
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_TYPE_HWRM_ASYNC_EVENT  0x2eUL
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_TYPE_LAST             ASYNC_EVENT_CMPL_HW_FLOW_AGED_TYPE_HWRM_ASYNC_EVENT
+       __le16  event_id;
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_EVENT_ID_HW_FLOW_AGED 0x36UL
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_EVENT_ID_LAST        ASYNC_EVENT_CMPL_HW_FLOW_AGED_EVENT_ID_HW_FLOW_AGED
+       __le32  event_data2;
+       u8      opaque_v;
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_V          0x1UL
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_OPAQUE_MASK 0xfeUL
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_OPAQUE_SFT 1
+       u8      timestamp_lo;
+       __le16  timestamp_hi;
+       __le32  event_data1;
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_EVENT_DATA1_FLOW_ID_MASK       0x7fffffffUL
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_EVENT_DATA1_FLOW_ID_SFT        0
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_EVENT_DATA1_FLOW_DIRECTION     0x80000000UL
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_EVENT_DATA1_FLOW_DIRECTION_RX    (0x0UL << 31)
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_EVENT_DATA1_FLOW_DIRECTION_TX    (0x1UL << 31)
+       #define ASYNC_EVENT_CMPL_HW_FLOW_AGED_EVENT_DATA1_FLOW_DIRECTION_LAST ASYNC_EVENT_CMPL_HW_FLOW_AGED_EVENT_DATA1_FLOW_DIRECTION_TX
+};
+
+/* hwrm_async_event_cmpl_eem_cache_flush_req (size:128b/16B) */
+struct hwrm_async_event_cmpl_eem_cache_flush_req {
+       __le16  type;
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_TYPE_MASK            0x3fUL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_TYPE_SFT             0
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_TYPE_HWRM_ASYNC_EVENT  0x2eUL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_TYPE_LAST             ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_TYPE_HWRM_ASYNC_EVENT
+       __le16  event_id;
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_EVENT_ID_EEM_CACHE_FLUSH_REQ 0x38UL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_EVENT_ID_LAST               ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_EVENT_ID_EEM_CACHE_FLUSH_REQ
+       __le32  event_data2;
+       u8      opaque_v;
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_V          0x1UL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_OPAQUE_MASK 0xfeUL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_REQ_OPAQUE_SFT 1
+       u8      timestamp_lo;
+       __le16  timestamp_hi;
+       __le32  event_data1;
+};
+
+/* hwrm_async_event_cmpl_eem_cache_flush_done (size:128b/16B) */
+struct hwrm_async_event_cmpl_eem_cache_flush_done {
+       __le16  type;
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_TYPE_MASK            0x3fUL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_TYPE_SFT             0
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_TYPE_HWRM_ASYNC_EVENT  0x2eUL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_TYPE_LAST             ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_TYPE_HWRM_ASYNC_EVENT
+       __le16  event_id;
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_EVENT_ID_EEM_CACHE_FLUSH_DONE 0x39UL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_EVENT_ID_LAST                ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_EVENT_ID_EEM_CACHE_FLUSH_DONE
+       __le32  event_data2;
+       u8      opaque_v;
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_V          0x1UL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_OPAQUE_MASK 0xfeUL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_OPAQUE_SFT 1
+       u8      timestamp_lo;
+       __le16  timestamp_hi;
+       __le32  event_data1;
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_EVENT_DATA1_FID_MASK 0xffffUL
+       #define ASYNC_EVENT_CMPL_EEM_CACHE_FLUSH_DONE_EVENT_DATA1_FID_SFT 0
+};
+
 /* hwrm_func_reset_input (size:192b/24B) */
 struct hwrm_func_reset_input {
        __le16  req_type;
@@ -867,6 +1012,8 @@ struct hwrm_func_qcaps_output {
        #define FUNC_QCAPS_RESP_FLAGS_ADMIN_PF_SUPPORTED              0x40000UL
        #define FUNC_QCAPS_RESP_FLAGS_LINK_ADMIN_STATUS_SUPPORTED     0x80000UL
        #define FUNC_QCAPS_RESP_FLAGS_WCB_PUSH_MODE                   0x100000UL
+       #define FUNC_QCAPS_RESP_FLAGS_DYNAMIC_TX_RING_ALLOC           0x200000UL
+       #define FUNC_QCAPS_RESP_FLAGS_HOT_RESET_CAPABLE               0x400000UL
        u8      mac_address[6];
        __le16  max_rsscos_ctx;
        __le16  max_cmpl_rings;
@@ -902,7 +1049,7 @@ struct hwrm_func_qcfg_input {
        u8      unused_0[6];
 };
 
-/* hwrm_func_qcfg_output (size:640b/80B) */
+/* hwrm_func_qcfg_output (size:704b/88B) */
 struct hwrm_func_qcfg_output {
        __le16  error_code;
        __le16  req_type;
@@ -919,6 +1066,7 @@ struct hwrm_func_qcfg_output {
        #define FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED        0x10UL
        #define FUNC_QCFG_RESP_FLAGS_MULTI_HOST                   0x20UL
        #define FUNC_QCFG_RESP_FLAGS_TRUSTED_VF                   0x40UL
+       #define FUNC_QCFG_RESP_FLAGS_SECURE_MODE_ENABLED          0x80UL
        u8      mac_address[6];
        __le16  pci_id;
        __le16  alloc_rsscos_ctx;
@@ -1000,7 +1148,11 @@ struct hwrm_func_qcfg_output {
        __le16  alloc_sp_tx_rings;
        __le16  alloc_stat_ctx;
        __le16  alloc_msix;
-       u8      unused_2[5];
+       __le16  registered_vfs;
+       u8      unused_1[3];
+       u8      always_1;
+       __le32  reset_addr_poll;
+       u8      unused_2[3];
        u8      valid;
 };
 
@@ -1031,6 +1183,7 @@ struct hwrm_func_cfg_input {
        #define FUNC_CFG_REQ_FLAGS_VNIC_ASSETS_TEST               0x80000UL
        #define FUNC_CFG_REQ_FLAGS_L2_CTX_ASSETS_TEST             0x100000UL
        #define FUNC_CFG_REQ_FLAGS_TRUSTED_VF_ENABLE              0x200000UL
+       #define FUNC_CFG_REQ_FLAGS_DYNAMIC_TX_RING_ALLOC          0x400000UL
        __le32  enables;
        #define FUNC_CFG_REQ_ENABLES_MTU                     0x1UL
        #define FUNC_CFG_REQ_ENABLES_MRU                     0x2UL
@@ -1235,6 +1388,7 @@ struct hwrm_func_drv_rgtr_input {
        #define FUNC_DRV_RGTR_REQ_FLAGS_FWD_NONE_MODE              0x2UL
        #define FUNC_DRV_RGTR_REQ_FLAGS_16BIT_VER_MODE             0x4UL
        #define FUNC_DRV_RGTR_REQ_FLAGS_FLOW_HANDLE_64BIT_MODE     0x8UL
+       #define FUNC_DRV_RGTR_REQ_FLAGS_HOT_RESET_SUPPORT          0x10UL
        __le32  enables;
        #define FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE             0x1UL
        #define FUNC_DRV_RGTR_REQ_ENABLES_VER                 0x2UL
@@ -1888,7 +2042,8 @@ struct hwrm_func_drv_if_change_output {
        __le16  seq_id;
        __le16  resp_len;
        __le32  flags;
-       #define FUNC_DRV_IF_CHANGE_RESP_FLAGS_RESC_CHANGE     0x1UL
+       #define FUNC_DRV_IF_CHANGE_RESP_FLAGS_RESC_CHANGE           0x1UL
+       #define FUNC_DRV_IF_CHANGE_RESP_FLAGS_HOT_FW_RESET_DONE     0x2UL
        u8      unused_0[3];
        u8      valid;
 };
@@ -2864,6 +3019,60 @@ struct hwrm_port_phy_i2c_read_output {
        u8      valid;
 };
 
+/* hwrm_port_phy_mdio_write_input (size:320b/40B) */
+struct hwrm_port_phy_mdio_write_input {
+       __le16  req_type;
+       __le16  cmpl_ring;
+       __le16  seq_id;
+       __le16  target_id;
+       __le64  resp_addr;
+       __le32  unused_0[2];
+       __le16  port_id;
+       u8      phy_addr;
+       u8      dev_addr;
+       __le16  reg_addr;
+       __le16  reg_data;
+       u8      cl45_mdio;
+       u8      unused_1[7];
+};
+
+/* hwrm_port_phy_mdio_write_output (size:128b/16B) */
+struct hwrm_port_phy_mdio_write_output {
+       __le16  error_code;
+       __le16  req_type;
+       __le16  seq_id;
+       __le16  resp_len;
+       u8      unused_0[7];
+       u8      valid;
+};
+
+/* hwrm_port_phy_mdio_read_input (size:256b/32B) */
+struct hwrm_port_phy_mdio_read_input {
+       __le16  req_type;
+       __le16  cmpl_ring;
+       __le16  seq_id;
+       __le16  target_id;
+       __le64  resp_addr;
+       __le32  unused_0[2];
+       __le16  port_id;
+       u8      phy_addr;
+       u8      dev_addr;
+       __le16  reg_addr;
+       u8      cl45_mdio;
+       u8      unused_1;
+};
+
+/* hwrm_port_phy_mdio_read_output (size:128b/16B) */
+struct hwrm_port_phy_mdio_read_output {
+       __le16  error_code;
+       __le16  req_type;
+       __le16  seq_id;
+       __le16  resp_len;
+       __le16  reg_data;
+       u8      unused_0[5];
+       u8      valid;
+};
+
 /* hwrm_port_led_cfg_input (size:512b/64B) */
 struct hwrm_port_led_cfg_input {
        __le16  req_type;
@@ -4869,6 +5078,10 @@ struct hwrm_ring_grp_free_output {
        u8      unused_0[7];
        u8      valid;
 };
+#define DEFAULT_FLOW_ID 0xFFFFFFFFUL
+#define ROCEV1_FLOW_ID 0xFFFFFFFEUL
+#define ROCEV2_FLOW_ID 0xFFFFFFFDUL
+#define ROCEV2_CNP_FLOW_ID 0xFFFFFFFCUL
 
 /* hwrm_cfa_l2_filter_alloc_input (size:768b/96B) */
 struct hwrm_cfa_l2_filter_alloc_input {
@@ -4937,20 +5150,21 @@ struct hwrm_cfa_l2_filter_alloc_input {
        u8      unused_3;
        __le32  src_id;
        u8      tunnel_type;
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL 0x0UL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN     0x1UL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE     0x2UL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE     0x3UL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP      0x4UL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE    0x5UL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS      0x6UL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT       0x7UL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE     0x8UL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4  0x9UL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1  0xaUL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE  0xbUL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL 0xffUL
-       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_LAST     CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL    0x0UL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN        0x1UL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE        0x2UL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE        0x3UL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP         0x4UL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE       0x5UL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS         0x6UL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT          0x7UL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE        0x8UL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4     0x9UL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1     0xaUL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE     0xbUL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL    0xffUL
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_LAST        CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
        u8      unused_4;
        __le16  dst_id;
        __le16  mirror_vnic_id;
@@ -5108,20 +5322,21 @@ struct hwrm_cfa_tunnel_filter_alloc_input {
        u8      l3_addr_type;
        u8      t_l3_addr_type;
        u8      tunnel_type;
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL 0x0UL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN     0x1UL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE     0x2UL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE     0x3UL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP      0x4UL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE    0x5UL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS      0x6UL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT       0x7UL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE     0x8UL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4  0x9UL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1  0xaUL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE  0xbUL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL 0xffUL
-       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_LAST     CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL    0x0UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN        0x1UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE        0x2UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE        0x3UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP         0x4UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE       0x5UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS         0x6UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT          0x7UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE        0x8UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4     0x9UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1     0xaUL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE     0xbUL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL    0xffUL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_LAST        CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
        u8      tunnel_flags;
        #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_FLAGS_TUN_FLAGS_OAM_CHECKSUM_EXPLHDR     0x1UL
        #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_FLAGS_TUN_FLAGS_CRITICAL_OPT_S1          0x2UL
@@ -5326,20 +5541,21 @@ struct hwrm_cfa_ntuple_filter_alloc_input {
        __le16  dst_id;
        __le16  mirror_vnic_id;
        u8      tunnel_type;
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL 0x0UL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN     0x1UL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE     0x2UL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE     0x3UL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP      0x4UL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE    0x5UL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS      0x6UL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT       0x7UL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE     0x8UL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4  0x9UL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1  0xaUL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE  0xbUL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL 0xffUL
-       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_LAST     CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL    0x0UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN        0x1UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE        0x2UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE        0x3UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP         0x4UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE       0x5UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS         0x6UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT          0x7UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE        0x8UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4     0x9UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1     0xaUL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE     0xbUL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL    0xffUL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_LAST        CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
        u8      pri_hint;
        #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_NO_PREFER 0x0UL
        #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_ABOVE     0x1UL
@@ -5459,20 +5675,21 @@ struct hwrm_cfa_decap_filter_alloc_input {
        #define CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID     0x10000UL
        __be32  tunnel_id;
        u8      tunnel_type;
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL 0x0UL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN     0x1UL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE     0x2UL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE     0x3UL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP      0x4UL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE    0x5UL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS      0x6UL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT       0x7UL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE     0x8UL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4  0x9UL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1  0xaUL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE  0xbUL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL 0xffUL
-       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_LAST     CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL    0x0UL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN        0x1UL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE        0x2UL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE        0x3UL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP         0x4UL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE       0x5UL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS         0x6UL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT          0x7UL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE        0x8UL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4     0x9UL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1     0xaUL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE     0xbUL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL    0xffUL
+       #define CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_LAST        CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
        u8      unused_0;
        __le16  unused_1;
        u8      src_macaddr[6];
@@ -5559,20 +5776,23 @@ struct hwrm_cfa_flow_alloc_input {
        #define CFA_FLOW_ALLOC_REQ_FLAGS_PATH_TX                0x40UL
        #define CFA_FLOW_ALLOC_REQ_FLAGS_PATH_RX                0x80UL
        #define CFA_FLOW_ALLOC_REQ_FLAGS_MATCH_VXLAN_IP_VNI     0x100UL
+       #define CFA_FLOW_ALLOC_REQ_FLAGS_VHOST_ID_USE_VLAN      0x200UL
        __le16  src_fid;
        __le32  tunnel_handle;
        __le16  action_flags;
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD                   0x1UL
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_RECYCLE               0x2UL
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP                  0x4UL
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_METER                 0x8UL
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL                0x10UL
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC               0x20UL
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST              0x40UL
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_IPV4_ADDRESS      0x80UL
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE     0x100UL
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TTL_DECREMENT         0x200UL
-       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL_IP             0x400UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD                    0x1UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_RECYCLE                0x2UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP                   0x4UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_METER                  0x8UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL                 0x10UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC                0x20UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST               0x40UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_IPV4_ADDRESS       0x80UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE      0x100UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TTL_DECREMENT          0x200UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL_IP              0x400UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FLOW_AGING_ENABLED     0x800UL
+       #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_PRI_HINT               0x1000UL
        __le16  dst_fid;
        __be16  l2_rewrite_vlan_tpid;
        __be16  l2_rewrite_vlan_tci;
@@ -5597,20 +5817,21 @@ struct hwrm_cfa_flow_alloc_input {
        __be16  l2_rewrite_smac[3];
        u8      ip_proto;
        u8      tunnel_type;
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL 0x0UL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_VXLAN     0x1UL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_NVGRE     0x2UL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_L2GRE     0x3UL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_IPIP      0x4UL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_GENEVE    0x5UL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_MPLS      0x6UL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_STT       0x7UL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_IPGRE     0x8UL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4  0x9UL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1  0xaUL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE  0xbUL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL 0xffUL
-       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_LAST     CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL    0x0UL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_VXLAN        0x1UL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_NVGRE        0x2UL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_L2GRE        0x3UL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_IPIP         0x4UL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_GENEVE       0x5UL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_MPLS         0x6UL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_STT          0x7UL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_IPGRE        0x8UL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4     0x9UL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1     0xaUL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE     0xbUL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL    0xffUL
+       #define CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_LAST        CFA_FLOW_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
 };
 
 /* hwrm_cfa_flow_alloc_output (size:256b/32B) */
@@ -5623,7 +5844,8 @@ struct hwrm_cfa_flow_alloc_output {
        u8      unused_0[2];
        __le32  flow_id;
        __le64  ext_flow_handle;
-       u8      unused_1[7];
+       __le32  flow_counter_id;
+       u8      unused_1[3];
        u8      valid;
 };
 
@@ -5651,6 +5873,46 @@ struct hwrm_cfa_flow_free_output {
        u8      valid;
 };
 
+/* hwrm_cfa_flow_info_input (size:256b/32B) */
+struct hwrm_cfa_flow_info_input {
+       __le16  req_type;
+       __le16  cmpl_ring;
+       __le16  seq_id;
+       __le16  target_id;
+       __le64  resp_addr;
+       __le16  flow_handle;
+       #define CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK       0xfffUL
+       #define CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_SFT        0
+       #define CFA_FLOW_INFO_REQ_FLOW_HANDLE_CNP_CNT        0x1000UL
+       #define CFA_FLOW_INFO_REQ_FLOW_HANDLE_ROCEV1_CNT     0x2000UL
+       #define CFA_FLOW_INFO_REQ_FLOW_HANDLE_ROCEV2_CNT     0x4000UL
+       #define CFA_FLOW_INFO_REQ_FLOW_HANDLE_DIR_RX         0x8000UL
+       u8      unused_0[6];
+       __le64  ext_flow_handle;
+};
+
+/* hwrm_cfa_flow_info_output (size:448b/56B) */
+struct hwrm_cfa_flow_info_output {
+       __le16  error_code;
+       __le16  req_type;
+       __le16  seq_id;
+       __le16  resp_len;
+       u8      flags;
+       u8      profile;
+       __le16  src_fid;
+       __le16  dst_fid;
+       __le16  l2_ctxt_id;
+       __le64  em_info;
+       __le64  tcam_info;
+       __le64  vfp_tcam_info;
+       __le16  ar_id;
+       __le16  flow_handle;
+       __le32  tunnel_handle;
+       __le16  flow_timer;
+       u8      unused_0[5];
+       u8      valid;
+};
+
 /* hwrm_cfa_flow_stats_input (size:640b/80B) */
 struct hwrm_cfa_flow_stats_input {
        __le16  req_type;
@@ -5757,6 +6019,128 @@ struct hwrm_cfa_vfr_free_output {
        u8      valid;
 };
 
+/* hwrm_cfa_eem_qcaps_input (size:192b/24B) */
+struct hwrm_cfa_eem_qcaps_input {
+       __le16  req_type;
+       __le16  cmpl_ring;
+       __le16  seq_id;
+       __le16  target_id;
+       __le64  resp_addr;
+       __le32  flags;
+       #define CFA_EEM_QCAPS_REQ_FLAGS_PATH_TX               0x1UL
+       #define CFA_EEM_QCAPS_REQ_FLAGS_PATH_RX               0x2UL
+       #define CFA_EEM_QCAPS_REQ_FLAGS_PREFERRED_OFFLOAD     0x4UL
+       __le32  unused_0;
+};
+
+/* hwrm_cfa_eem_qcaps_output (size:256b/32B) */
+struct hwrm_cfa_eem_qcaps_output {
+       __le16  error_code;
+       __le16  req_type;
+       __le16  seq_id;
+       __le16  resp_len;
+       __le32  flags;
+       #define CFA_EEM_QCAPS_RESP_FLAGS_PATH_TX     0x1UL
+       #define CFA_EEM_QCAPS_RESP_FLAGS_PATH_RX     0x2UL
+       __le32  unused_0;
+       __le32  supported;
+       #define CFA_EEM_QCAPS_RESP_SUPPORTED_KEY0_TABLE                       0x1UL
+       #define CFA_EEM_QCAPS_RESP_SUPPORTED_KEY1_TABLE                       0x2UL
+       #define CFA_EEM_QCAPS_RESP_SUPPORTED_EXTERNAL_RECORD_TABLE            0x4UL
+       #define CFA_EEM_QCAPS_RESP_SUPPORTED_EXTERNAL_FLOW_COUNTERS_TABLE     0x8UL
+       __le32  max_entries_supported;
+       __le16  key_entry_size;
+       __le16  record_entry_size;
+       __le16  efc_entry_size;
+       u8      unused_1;
+       u8      valid;
+};
+
+/* hwrm_cfa_eem_cfg_input (size:320b/40B) */
+struct hwrm_cfa_eem_cfg_input {
+       __le16  req_type;
+       __le16  cmpl_ring;
+       __le16  seq_id;
+       __le16  target_id;
+       __le64  resp_addr;
+       __le32  flags;
+       #define CFA_EEM_CFG_REQ_FLAGS_PATH_TX               0x1UL
+       #define CFA_EEM_CFG_REQ_FLAGS_PATH_RX               0x2UL
+       #define CFA_EEM_CFG_REQ_FLAGS_PREFERRED_OFFLOAD     0x4UL
+       __le32  unused_0;
+       __le32  num_entries;
+       __le32  unused_1;
+       __le16  key0_ctx_id;
+       __le16  key1_ctx_id;
+       __le16  record_ctx_id;
+       __le16  efc_ctx_id;
+};
+
+/* hwrm_cfa_eem_cfg_output (size:128b/16B) */
+struct hwrm_cfa_eem_cfg_output {
+       __le16  error_code;
+       __le16  req_type;
+       __le16  seq_id;
+       __le16  resp_len;
+       u8      unused_0[7];
+       u8      valid;
+};
+
+/* hwrm_cfa_eem_qcfg_input (size:192b/24B) */
+struct hwrm_cfa_eem_qcfg_input {
+       __le16  req_type;
+       __le16  cmpl_ring;
+       __le16  seq_id;
+       __le16  target_id;
+       __le64  resp_addr;
+       __le32  flags;
+       #define CFA_EEM_QCFG_REQ_FLAGS_PATH_TX     0x1UL
+       #define CFA_EEM_QCFG_REQ_FLAGS_PATH_RX     0x2UL
+       __le32  unused_0;
+};
+
+/* hwrm_cfa_eem_qcfg_output (size:128b/16B) */
+struct hwrm_cfa_eem_qcfg_output {
+       __le16  error_code;
+       __le16  req_type;
+       __le16  seq_id;
+       __le16  resp_len;
+       __le32  flags;
+       #define CFA_EEM_QCFG_RESP_FLAGS_PATH_TX               0x1UL
+       #define CFA_EEM_QCFG_RESP_FLAGS_PATH_RX               0x2UL
+       #define CFA_EEM_QCFG_RESP_FLAGS_PREFERRED_OFFLOAD     0x4UL
+       __le32  num_entries;
+};
+
+/* hwrm_cfa_eem_op_input (size:192b/24B) */
+struct hwrm_cfa_eem_op_input {
+       __le16  req_type;
+       __le16  cmpl_ring;
+       __le16  seq_id;
+       __le16  target_id;
+       __le64  resp_addr;
+       __le32  flags;
+       #define CFA_EEM_OP_REQ_FLAGS_PATH_TX     0x1UL
+       #define CFA_EEM_OP_REQ_FLAGS_PATH_RX     0x2UL
+       __le16  unused_0;
+       __le16  op;
+       #define CFA_EEM_OP_REQ_OP_RESERVED    0x0UL
+       #define CFA_EEM_OP_REQ_OP_EEM_DISABLE 0x1UL
+       #define CFA_EEM_OP_REQ_OP_EEM_ENABLE  0x2UL
+       #define CFA_EEM_OP_REQ_OP_EEM_CLEANUP 0x3UL
+       #define CFA_EEM_OP_REQ_OP_LAST       CFA_EEM_OP_REQ_OP_EEM_CLEANUP
+};
+
+/* hwrm_cfa_eem_op_output (size:128b/16B) */
+struct hwrm_cfa_eem_op_output {
+       __le16  error_code;
+       __le16  req_type;
+       __le16  seq_id;
+       __le16  resp_len;
+       u8      unused_0[7];
+       u8      valid;
+};
+
 /* hwrm_tunnel_dst_port_query_input (size:192b/24B) */
 struct hwrm_tunnel_dst_port_query_input {
        __le16  req_type;
@@ -5765,12 +6149,13 @@ struct hwrm_tunnel_dst_port_query_input {
        __le16  target_id;
        __le64  resp_addr;
        u8      tunnel_type;
-       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN    0x1UL
-       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_GENEVE   0x5UL
-       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN_V4 0x9UL
-       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPGRE_V1 0xaUL
-       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_L2_ETYPE 0xbUL
-       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_LAST    TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_L2_ETYPE
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN        0x1UL
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_GENEVE       0x5UL
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN_V4     0x9UL
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPGRE_V1     0xaUL
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_L2_ETYPE     0xbUL
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_LAST        TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN_GPE_V6
        u8      unused_0[7];
 };
 
@@ -5794,12 +6179,13 @@ struct hwrm_tunnel_dst_port_alloc_input {
        __le16  target_id;
        __le64  resp_addr;
        u8      tunnel_type;
-       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN    0x1UL
-       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_GENEVE   0x5UL
-       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4 0x9UL
-       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1 0xaUL
-       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE 0xbUL
-       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_LAST    TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN        0x1UL
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_GENEVE       0x5UL
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4     0x9UL
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1     0xaUL
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE     0xbUL
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_LAST        TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6
        u8      unused_0;
        __be16  tunnel_dst_port_val;
        u8      unused_1[4];
@@ -5824,12 +6210,13 @@ struct hwrm_tunnel_dst_port_free_input {
        __le16  target_id;
        __le64  resp_addr;
        u8      tunnel_type;
-       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN    0x1UL
-       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE   0x5UL
-       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN_V4 0x9UL
-       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPGRE_V1 0xaUL
-       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_L2_ETYPE 0xbUL
-       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_LAST    TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_L2_ETYPE
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN        0x1UL
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE       0x5UL
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN_V4     0x9UL
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPGRE_V1     0xaUL
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_L2_ETYPE     0xbUL
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_LAST        TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN_GPE_V6
        u8      unused_0;
        __le16  tunnel_dst_port_id;
        u8      unused_1[4];
@@ -6040,7 +6427,9 @@ struct hwrm_fw_reset_input {
        #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTIMMEDIATE 0x3UL
        #define FW_RESET_REQ_SELFRST_STATUS_LAST            FW_RESET_REQ_SELFRST_STATUS_SELFRSTIMMEDIATE
        u8      host_idx;
-       u8      unused_0[5];
+       u8      flags;
+       #define FW_RESET_REQ_FLAGS_RESET_GRACEFUL     0x1UL
+       u8      unused_0[4];
 };
 
 /* hwrm_fw_reset_output (size:128b/16B) */
@@ -6137,6 +6526,7 @@ struct hwrm_struct_hdr {
        #define STRUCT_HDR_STRUCT_ID_DCBX_FEATURE_STATE 0x422UL
        #define STRUCT_HDR_STRUCT_ID_LLDP_GENERIC       0x424UL
        #define STRUCT_HDR_STRUCT_ID_LLDP_DEVICE        0x426UL
+       #define STRUCT_HDR_STRUCT_ID_POWER_BKUP         0x427UL
        #define STRUCT_HDR_STRUCT_ID_AFM_OPAQUE         0x1UL
        #define STRUCT_HDR_STRUCT_ID_PORT_DESCRIPTION   0xaUL
        #define STRUCT_HDR_STRUCT_ID_RSS_V2             0x64UL
index 3962f6f..d80f5c9 100644 (file)
@@ -448,16 +448,22 @@ static int bnxt_hwrm_func_vf_resc_cfg(struct bnxt *bp, int num_vfs)
        u16 vf_stat_ctx, vf_vnics, vf_ring_grps;
        struct bnxt_pf_info *pf = &bp->pf;
        int i, rc = 0, min = 1;
+       u16 vf_msix = 0;
 
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_VF_RESOURCE_CFG, -1, -1);
 
-       vf_cp_rings = bnxt_get_max_func_cp_rings_for_en(bp) - bp->cp_nr_rings;
-       vf_stat_ctx = hw_resc->max_stat_ctxs - bp->num_stat_ctxs;
+       if (bp->flags & BNXT_FLAG_CHIP_P5) {
+               vf_msix = hw_resc->max_nqs - bnxt_nq_rings_in_use(bp);
+               vf_ring_grps = 0;
+       } else {
+               vf_ring_grps = hw_resc->max_hw_ring_grps - bp->rx_nr_rings;
+       }
+       vf_cp_rings = bnxt_get_avail_cp_rings_for_en(bp);
+       vf_stat_ctx = bnxt_get_avail_stat_ctxs_for_en(bp);
        if (bp->flags & BNXT_FLAG_AGG_RINGS)
                vf_rx_rings = hw_resc->max_rx_rings - bp->rx_nr_rings * 2;
        else
                vf_rx_rings = hw_resc->max_rx_rings - bp->rx_nr_rings;
-       vf_ring_grps = hw_resc->max_hw_ring_grps - bp->rx_nr_rings;
        vf_tx_rings = hw_resc->max_tx_rings - bp->tx_nr_rings;
        vf_vnics = hw_resc->max_vnics - bp->nr_vnics;
        vf_vnics = min_t(u16, vf_vnics, vf_rx_rings);
@@ -476,7 +482,8 @@ static int bnxt_hwrm_func_vf_resc_cfg(struct bnxt *bp, int num_vfs)
                req.min_l2_ctxs = cpu_to_le16(min);
                req.min_vnics = cpu_to_le16(min);
                req.min_stat_ctx = cpu_to_le16(min);
-               req.min_hw_ring_grps = cpu_to_le16(min);
+               if (!(bp->flags & BNXT_FLAG_CHIP_P5))
+                       req.min_hw_ring_grps = cpu_to_le16(min);
        } else {
                vf_cp_rings /= num_vfs;
                vf_tx_rings /= num_vfs;
@@ -500,6 +507,8 @@ static int bnxt_hwrm_func_vf_resc_cfg(struct bnxt *bp, int num_vfs)
        req.max_vnics = cpu_to_le16(vf_vnics);
        req.max_stat_ctx = cpu_to_le16(vf_stat_ctx);
        req.max_hw_ring_grps = cpu_to_le16(vf_ring_grps);
+       if (bp->flags & BNXT_FLAG_CHIP_P5)
+               req.max_msix = cpu_to_le16(vf_msix / num_vfs);
 
        mutex_lock(&bp->hwrm_cmd_lock);
        for (i = 0; i < num_vfs; i++) {
@@ -525,6 +534,8 @@ static int bnxt_hwrm_func_vf_resc_cfg(struct bnxt *bp, int num_vfs)
                hw_resc->max_rsscos_ctxs -= pf->active_vfs;
                hw_resc->max_stat_ctxs -= le16_to_cpu(req.min_stat_ctx) * n;
                hw_resc->max_vnics -= le16_to_cpu(req.min_vnics) * n;
+               if (bp->flags & BNXT_FLAG_CHIP_P5)
+                       hw_resc->max_irqs -= vf_msix * n;
 
                rc = pf->active_vfs;
        }
@@ -539,19 +550,16 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs)
        u32 rc = 0, mtu, i;
        u16 vf_tx_rings, vf_rx_rings, vf_cp_rings, vf_stat_ctx, vf_vnics;
        struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
-       u16 vf_ring_grps, max_stat_ctxs;
        struct hwrm_func_cfg_input req = {0};
        struct bnxt_pf_info *pf = &bp->pf;
        int total_vf_tx_rings = 0;
+       u16 vf_ring_grps;
 
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
 
-       max_stat_ctxs = hw_resc->max_stat_ctxs;
-
        /* Remaining rings are distributed equally amongs VF's for now */
-       vf_cp_rings = (bnxt_get_max_func_cp_rings_for_en(bp) -
-                      bp->cp_nr_rings) / num_vfs;
-       vf_stat_ctx = (max_stat_ctxs - bp->num_stat_ctxs) / num_vfs;
+       vf_cp_rings = bnxt_get_avail_cp_rings_for_en(bp) / num_vfs;
+       vf_stat_ctx = bnxt_get_avail_stat_ctxs_for_en(bp) / num_vfs;
        if (bp->flags & BNXT_FLAG_AGG_RINGS)
                vf_rx_rings = (hw_resc->max_rx_rings - bp->rx_nr_rings * 2) /
                              num_vfs;
@@ -644,8 +652,8 @@ static int bnxt_sriov_enable(struct bnxt *bp, int *num_vfs)
         */
        vfs_supported = *num_vfs;
 
-       avail_cp = bnxt_get_max_func_cp_rings_for_en(bp) - bp->cp_nr_rings;
-       avail_stat = hw_resc->max_stat_ctxs - bp->num_stat_ctxs;
+       avail_cp = bnxt_get_avail_cp_rings_for_en(bp);
+       avail_stat = bnxt_get_avail_stat_ctxs_for_en(bp);
        avail_cp = min_t(int, avail_cp, avail_stat);
 
        while (vfs_supported) {
index 749f63b..c683b5e 100644 (file)
@@ -337,18 +337,21 @@ static int bnxt_tc_parse_flow(struct bnxt *bp,
        return bnxt_tc_parse_actions(bp, &flow->actions, tc_flow_cmd->exts);
 }
 
-static int bnxt_hwrm_cfa_flow_free(struct bnxt *bp, __le16 flow_handle)
+static int bnxt_hwrm_cfa_flow_free(struct bnxt *bp,
+                                  struct bnxt_tc_flow_node *flow_node)
 {
        struct hwrm_cfa_flow_free_input req = { 0 };
        int rc;
 
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_FREE, -1, -1);
-       req.flow_handle = flow_handle;
+       if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE)
+               req.ext_flow_handle = flow_node->ext_flow_handle;
+       else
+               req.flow_handle = flow_node->flow_handle;
 
        rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
        if (rc)
-               netdev_info(bp->dev, "Error: %s: flow_handle=0x%x rc=%d",
-                           __func__, flow_handle, rc);
+               netdev_info(bp->dev, "%s: Error rc=%d", __func__, rc);
 
        if (rc)
                rc = -EIO;
@@ -418,13 +421,14 @@ static bool bits_set(void *key, int len)
 
 static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow,
                                    __le16 ref_flow_handle,
-                                   __le32 tunnel_handle, __le16 *flow_handle)
+                                   __le32 tunnel_handle,
+                                   struct bnxt_tc_flow_node *flow_node)
 {
-       struct hwrm_cfa_flow_alloc_output *resp = bp->hwrm_cmd_resp_addr;
        struct bnxt_tc_actions *actions = &flow->actions;
        struct bnxt_tc_l3_key *l3_mask = &flow->l3_mask;
        struct bnxt_tc_l3_key *l3_key = &flow->l3_key;
        struct hwrm_cfa_flow_alloc_input req = { 0 };
+       struct hwrm_cfa_flow_alloc_output *resp;
        u16 flow_flags = 0, action_flags = 0;
        int rc;
 
@@ -527,8 +531,23 @@ static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow,
 
        mutex_lock(&bp->hwrm_cmd_lock);
        rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
-       if (!rc)
-               *flow_handle = resp->flow_handle;
+       if (!rc) {
+               resp = bnxt_get_hwrm_resp_addr(bp, &req);
+               /* CFA_FLOW_ALLOC response interpretation:
+                *                  fw with          fw with
+                *                  16-bit           64-bit
+                *                  flow handle      flow handle
+                *                  ===========      ===========
+                * flow_handle      flow handle      flow context id
+                * ext_flow_handle  INVALID          flow handle
+                * flow_id          INVALID          flow counter id
+                */
+               flow_node->flow_handle = resp->flow_handle;
+               if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) {
+                       flow_node->ext_flow_handle = resp->ext_flow_handle;
+                       flow_node->flow_id = resp->flow_id;
+               }
+       }
        mutex_unlock(&bp->hwrm_cmd_lock);
 
        if (rc == HWRM_ERR_CODE_RESOURCE_ALLOC_ERROR)
@@ -544,9 +563,8 @@ static int hwrm_cfa_decap_filter_alloc(struct bnxt *bp,
                                       __le32 ref_decap_handle,
                                       __le32 *decap_filter_handle)
 {
-       struct hwrm_cfa_decap_filter_alloc_output *resp =
-                                               bp->hwrm_cmd_resp_addr;
        struct hwrm_cfa_decap_filter_alloc_input req = { 0 };
+       struct hwrm_cfa_decap_filter_alloc_output *resp;
        struct ip_tunnel_key *tun_key = &flow->tun_key;
        u32 enables = 0;
        int rc;
@@ -599,10 +617,12 @@ static int hwrm_cfa_decap_filter_alloc(struct bnxt *bp,
 
        mutex_lock(&bp->hwrm_cmd_lock);
        rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
-       if (!rc)
+       if (!rc) {
+               resp = bnxt_get_hwrm_resp_addr(bp, &req);
                *decap_filter_handle = resp->decap_filter_id;
-       else
+       } else {
                netdev_info(bp->dev, "%s: Error rc=%d", __func__, rc);
+       }
        mutex_unlock(&bp->hwrm_cmd_lock);
 
        if (rc)
@@ -633,9 +653,8 @@ static int hwrm_cfa_encap_record_alloc(struct bnxt *bp,
                                       struct bnxt_tc_l2_key *l2_info,
                                       __le32 *encap_record_handle)
 {
-       struct hwrm_cfa_encap_record_alloc_output *resp =
-                                               bp->hwrm_cmd_resp_addr;
        struct hwrm_cfa_encap_record_alloc_input req = { 0 };
+       struct hwrm_cfa_encap_record_alloc_output *resp;
        struct hwrm_cfa_encap_data_vxlan *encap =
                        (struct hwrm_cfa_encap_data_vxlan *)&req.encap_data;
        struct hwrm_vxlan_ipv4_hdr *encap_ipv4 =
@@ -667,10 +686,12 @@ static int hwrm_cfa_encap_record_alloc(struct bnxt *bp,
 
        mutex_lock(&bp->hwrm_cmd_lock);
        rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
-       if (!rc)
+       if (!rc) {
+               resp = bnxt_get_hwrm_resp_addr(bp, &req);
                *encap_record_handle = resp->encap_record_id;
-       else
+       } else {
                netdev_info(bp->dev, "%s: Error rc=%d", __func__, rc);
+       }
        mutex_unlock(&bp->hwrm_cmd_lock);
 
        if (rc)
@@ -1224,7 +1245,7 @@ static int __bnxt_tc_del_flow(struct bnxt *bp,
        int rc;
 
        /* send HWRM cmd to free the flow-id */
-       bnxt_hwrm_cfa_flow_free(bp, flow_node->flow_handle);
+       bnxt_hwrm_cfa_flow_free(bp, flow_node);
 
        mutex_lock(&tc_info->lock);
 
@@ -1246,6 +1267,12 @@ static int __bnxt_tc_del_flow(struct bnxt *bp,
        return 0;
 }
 
+static void bnxt_tc_set_flow_dir(struct bnxt *bp, struct bnxt_tc_flow *flow,
+                                u16 src_fid)
+{
+       flow->dir = (bp->pf.fw_fid == src_fid) ? BNXT_DIR_RX : BNXT_DIR_TX;
+}
+
 static void bnxt_tc_set_src_fid(struct bnxt *bp, struct bnxt_tc_flow *flow,
                                u16 src_fid)
 {
@@ -1293,6 +1320,9 @@ static int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid,
 
        bnxt_tc_set_src_fid(bp, flow, src_fid);
 
+       if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE)
+               bnxt_tc_set_flow_dir(bp, flow, src_fid);
+
        if (!bnxt_tc_can_offload(bp, flow)) {
                rc = -ENOSPC;
                goto free_node;
@@ -1320,7 +1350,7 @@ static int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid,
 
        /* send HWRM cmd to alloc the flow */
        rc = bnxt_hwrm_cfa_flow_alloc(bp, flow, ref_flow_handle,
-                                     tunnel_handle, &new_node->flow_handle);
+                                     tunnel_handle, new_node);
        if (rc)
                goto put_tunnel;
 
@@ -1336,7 +1366,7 @@ static int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid,
        return 0;
 
 hwrm_flow_free:
-       bnxt_hwrm_cfa_flow_free(bp, new_node->flow_handle);
+       bnxt_hwrm_cfa_flow_free(bp, new_node);
 put_tunnel:
        bnxt_tc_put_tunnel_handle(bp, flow, new_node);
 put_l2:
@@ -1397,13 +1427,40 @@ static int bnxt_tc_get_flow_stats(struct bnxt *bp,
        return 0;
 }
 
+static void bnxt_fill_cfa_stats_req(struct bnxt *bp,
+                                   struct bnxt_tc_flow_node *flow_node,
+                                   __le16 *flow_handle, __le32 *flow_id)
+{
+       u16 handle;
+
+       if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) {
+               *flow_id = flow_node->flow_id;
+
+               /* If flow_id is used to fetch flow stats then:
+                * 1. lower 12 bits of flow_handle must be set to all 1s.
+                * 2. 15th bit of flow_handle must specify the flow
+                *    direction (TX/RX).
+                */
+               if (flow_node->flow.dir == BNXT_DIR_RX)
+                       handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_DIR_RX |
+                                CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK;
+               else
+                       handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK;
+
+               *flow_handle = cpu_to_le16(handle);
+       } else {
+               *flow_handle = flow_node->flow_handle;
+       }
+}
+
 static int
 bnxt_hwrm_cfa_flow_stats_get(struct bnxt *bp, int num_flows,
                             struct bnxt_tc_stats_batch stats_batch[])
 {
-       struct hwrm_cfa_flow_stats_output *resp = bp->hwrm_cmd_resp_addr;
        struct hwrm_cfa_flow_stats_input req = { 0 };
+       struct hwrm_cfa_flow_stats_output *resp;
        __le16 *req_flow_handles = &req.flow_handle_0;
+       __le32 *req_flow_ids = &req.flow_id_0;
        int rc, i;
 
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_STATS, -1, -1);
@@ -1411,14 +1468,19 @@ bnxt_hwrm_cfa_flow_stats_get(struct bnxt *bp, int num_flows,
        for (i = 0; i < num_flows; i++) {
                struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node;
 
-               req_flow_handles[i] = flow_node->flow_handle;
+               bnxt_fill_cfa_stats_req(bp, flow_node,
+                                       &req_flow_handles[i], &req_flow_ids[i]);
        }
 
        mutex_lock(&bp->hwrm_cmd_lock);
        rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
        if (!rc) {
-               __le64 *resp_packets = &resp->packet_0;
-               __le64 *resp_bytes = &resp->byte_0;
+               __le64 *resp_packets;
+               __le64 *resp_bytes;
+
+               resp = bnxt_get_hwrm_resp_addr(bp, &req);
+               resp_packets = &resp->packet_0;
+               resp_bytes = &resp->byte_0;
 
                for (i = 0; i < num_flows; i++) {
                        stats_batch[i].hw_stats.packets =
index 97e09a8..8a09689 100644 (file)
@@ -98,6 +98,9 @@ struct bnxt_tc_flow {
 
        /* flow applicable to pkts ingressing on this fid */
        u16                             src_fid;
+       u8                              dir;
+#define BNXT_DIR_RX    1
+#define BNXT_DIR_TX    0
        struct bnxt_tc_l2_key           l2_key;
        struct bnxt_tc_l2_key           l2_mask;
        struct bnxt_tc_l3_key           l3_key;
@@ -170,7 +173,9 @@ struct bnxt_tc_flow_node {
 
        struct bnxt_tc_flow             flow;
 
+       __le64                          ext_flow_handle;
        __le16                          flow_handle;
+       __le32                          flow_id;
 
        /* L2 node in l2 hashtable that shares flow's l2 key */
        struct bnxt_tc_l2_node          *l2_node;
index beee612..ea45a9b 100644 (file)
@@ -43,12 +43,13 @@ static int bnxt_register_dev(struct bnxt_en_dev *edev, int ulp_id,
        if (ulp_id == BNXT_ROCE_ULP) {
                unsigned int max_stat_ctxs;
 
+               if (bp->flags & BNXT_FLAG_CHIP_P5)
+                       return -EOPNOTSUPP;
+
                max_stat_ctxs = bnxt_get_max_func_stat_ctxs(bp);
                if (max_stat_ctxs <= BNXT_MIN_ROCE_STAT_CTXS ||
-                   bp->num_stat_ctxs == max_stat_ctxs)
+                   bp->cp_nr_rings == max_stat_ctxs)
                        return -ENOMEM;
-               bnxt_set_max_func_stat_ctxs(bp, max_stat_ctxs -
-                                           BNXT_MIN_ROCE_STAT_CTXS);
        }
 
        atomic_set(&ulp->ref_count, 0);
@@ -79,14 +80,9 @@ static int bnxt_unregister_dev(struct bnxt_en_dev *edev, int ulp_id)
                netdev_err(bp->dev, "ulp id %d not registered\n", ulp_id);
                return -EINVAL;
        }
-       if (ulp_id == BNXT_ROCE_ULP) {
-               unsigned int max_stat_ctxs;
+       if (ulp_id == BNXT_ROCE_ULP && ulp->msix_requested)
+               edev->en_ops->bnxt_free_msix(edev, ulp_id);
 
-               max_stat_ctxs = bnxt_get_max_func_stat_ctxs(bp);
-               bnxt_set_max_func_stat_ctxs(bp, max_stat_ctxs + 1);
-               if (ulp->msix_requested)
-                       edev->en_ops->bnxt_free_msix(edev, ulp_id);
-       }
        if (ulp->max_async_event_id)
                bnxt_hwrm_func_rgtr_async_events(bp, NULL, 0);
 
@@ -165,7 +161,7 @@ static int bnxt_req_msix_vecs(struct bnxt_en_dev *edev, int ulp_id,
        if (BNXT_NEW_RM(bp)) {
                struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
 
-               avail_msix = hw_resc->resv_cp_rings - bp->cp_nr_rings;
+               avail_msix = hw_resc->resv_irqs - bp->cp_nr_rings;
                edev->ulp_tbl[ulp_id].msix_requested = avail_msix;
        }
        bnxt_fill_msix_vecs(bp, ent);
@@ -215,6 +211,14 @@ int bnxt_get_ulp_msix_base(struct bnxt *bp)
        return 0;
 }
 
+int bnxt_get_ulp_stat_ctxs(struct bnxt *bp)
+{
+       if (bnxt_ulp_registered(bp->edev, BNXT_ROCE_ULP))
+               return BNXT_MIN_ROCE_STAT_CTXS;
+
+       return 0;
+}
+
 static int bnxt_send_msg(struct bnxt_en_dev *edev, int ulp_id,
                         struct bnxt_fw_msg *fw_msg)
 {
index d9bea37..cd78453 100644 (file)
@@ -90,6 +90,7 @@ static inline bool bnxt_ulp_registered(struct bnxt_en_dev *edev, int ulp_id)
 
 int bnxt_get_ulp_msix_num(struct bnxt *bp);
 int bnxt_get_ulp_msix_base(struct bnxt *bp);
+int bnxt_get_ulp_stat_ctxs(struct bnxt *bp);
 void bnxt_ulp_stop(struct bnxt *bp);
 void bnxt_ulp_start(struct bnxt *bp);
 void bnxt_ulp_sriov_cfg(struct bnxt *bp, int num_vfs);
index bf6de02..0184ef6 100644 (file)
@@ -199,7 +199,6 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
        bp->tx_nr_rings_xdp = tx_xdp;
        bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc + tx_xdp;
        bp->cp_nr_rings = max_t(int, bp->tx_nr_rings, bp->rx_nr_rings);
-       bp->num_stat_ctxs = bp->cp_nr_rings;
        bnxt_set_tpa_flags(bp);
        bnxt_set_ring_params(bp);
 
index bf88749..983245c 100644 (file)
@@ -3612,36 +3612,6 @@ static int bcmgenet_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int bcmgenet_suspend(struct device *d)
-{
-       struct net_device *dev = dev_get_drvdata(d);
-       struct bcmgenet_priv *priv = netdev_priv(dev);
-       int ret = 0;
-
-       if (!netif_running(dev))
-               return 0;
-
-       netif_device_detach(dev);
-
-       bcmgenet_netif_stop(dev);
-
-       if (!device_may_wakeup(d))
-               phy_suspend(dev->phydev);
-
-       /* Prepare the device for Wake-on-LAN and switch to the slow clock */
-       if (device_may_wakeup(d) && priv->wolopts) {
-               ret = bcmgenet_power_down(priv, GENET_POWER_WOL_MAGIC);
-               clk_prepare_enable(priv->clk_wol);
-       } else if (priv->internal_phy) {
-               ret = bcmgenet_power_down(priv, GENET_POWER_PASSIVE);
-       }
-
-       /* Turn off the clocks */
-       clk_disable_unprepare(priv->clk);
-
-       return ret;
-}
-
 static int bcmgenet_resume(struct device *d)
 {
        struct net_device *dev = dev_get_drvdata(d);
@@ -3719,6 +3689,39 @@ out_clk_disable:
        clk_disable_unprepare(priv->clk);
        return ret;
 }
+
+static int bcmgenet_suspend(struct device *d)
+{
+       struct net_device *dev = dev_get_drvdata(d);
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       int ret = 0;
+
+       if (!netif_running(dev))
+               return 0;
+
+       netif_device_detach(dev);
+
+       bcmgenet_netif_stop(dev);
+
+       if (!device_may_wakeup(d))
+               phy_suspend(dev->phydev);
+
+       /* Prepare the device for Wake-on-LAN and switch to the slow clock */
+       if (device_may_wakeup(d) && priv->wolopts) {
+               ret = bcmgenet_power_down(priv, GENET_POWER_WOL_MAGIC);
+               clk_prepare_enable(priv->clk_wol);
+       } else if (priv->internal_phy) {
+               ret = bcmgenet_power_down(priv, GENET_POWER_PASSIVE);
+       }
+
+       /* Turn off the clocks */
+       clk_disable_unprepare(priv->clk);
+
+       if (ret)
+               bcmgenet_resume(d);
+
+       return ret;
+}
 #endif /* CONFIG_PM_SLEEP */
 
 static SIMPLE_DEV_PM_OPS(bcmgenet_pm_ops, bcmgenet_suspend, bcmgenet_resume);
index 2fbd027..57582ef 100644 (file)
@@ -186,6 +186,8 @@ void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
        }
 
        reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
+       if (!(reg & MPD_EN))
+               return; /* already powered up so skip the rest */
        reg &= ~MPD_EN;
        bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
 
index 79b881d..3b1397a 100644 (file)
 #include <uapi/linux/net_tstamp.h>
 #include <linux/ptp_clock_kernel.h>
 
-#ifdef CONFIG_SPARC
-#include <asm/idprom.h>
-#include <asm/prom.h>
-#endif
-
 #define BAR_0  0
 #define BAR_2  2
 
@@ -12439,6 +12434,7 @@ static int tg3_set_ringparam(struct net_device *dev, struct ethtool_ringparam *e
 {
        struct tg3 *tp = netdev_priv(dev);
        int i, irq_sync = 0, err = 0;
+       bool reset_phy = false;
 
        if ((ering->rx_pending > tp->rx_std_ring_mask) ||
            (ering->rx_jumbo_pending > tp->rx_jmb_ring_mask) ||
@@ -12470,7 +12466,13 @@ static int tg3_set_ringparam(struct net_device *dev, struct ethtool_ringparam *e
 
        if (netif_running(dev)) {
                tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
-               err = tg3_restart_hw(tp, false);
+               /* Reset PHY to avoid PHY lock up */
+               if (tg3_asic_rev(tp) == ASIC_REV_5717 ||
+                   tg3_asic_rev(tp) == ASIC_REV_5719 ||
+                   tg3_asic_rev(tp) == ASIC_REV_5720)
+                       reset_phy = true;
+
+               err = tg3_restart_hw(tp, reset_phy);
                if (!err)
                        tg3_netif_start(tp);
        }
@@ -12504,6 +12506,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
 {
        struct tg3 *tp = netdev_priv(dev);
        int err = 0;
+       bool reset_phy = false;
 
        if (tp->link_config.autoneg == AUTONEG_ENABLE)
                tg3_warn_mgmt_link_flap(tp);
@@ -12573,7 +12576,13 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
 
                if (netif_running(dev)) {
                        tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
-                       err = tg3_restart_hw(tp, false);
+                       /* Reset PHY to avoid PHY lock up */
+                       if (tg3_asic_rev(tp) == ASIC_REV_5717 ||
+                           tg3_asic_rev(tp) == ASIC_REV_5719 ||
+                           tg3_asic_rev(tp) == ASIC_REV_5720)
+                               reset_phy = true;
+
+                       err = tg3_restart_hw(tp, reset_phy);
                        if (!err)
                                tg3_netif_start(tp);
                }
@@ -16976,32 +16985,6 @@ static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent)
        return err;
 }
 
-#ifdef CONFIG_SPARC
-static int tg3_get_macaddr_sparc(struct tg3 *tp)
-{
-       struct net_device *dev = tp->dev;
-       struct pci_dev *pdev = tp->pdev;
-       struct device_node *dp = pci_device_to_OF_node(pdev);
-       const unsigned char *addr;
-       int len;
-
-       addr = of_get_property(dp, "local-mac-address", &len);
-       if (addr && len == ETH_ALEN) {
-               memcpy(dev->dev_addr, addr, ETH_ALEN);
-               return 0;
-       }
-       return -ENODEV;
-}
-
-static int tg3_get_default_macaddr_sparc(struct tg3 *tp)
-{
-       struct net_device *dev = tp->dev;
-
-       memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN);
-       return 0;
-}
-#endif
-
 static int tg3_get_device_address(struct tg3 *tp)
 {
        struct net_device *dev = tp->dev;
@@ -17009,10 +16992,8 @@ static int tg3_get_device_address(struct tg3 *tp)
        int addr_ok = 0;
        int err;
 
-#ifdef CONFIG_SPARC
-       if (!tg3_get_macaddr_sparc(tp))
+       if (!eth_platform_get_mac_address(&tp->pdev->dev, dev->dev_addr))
                return 0;
-#endif
 
        if (tg3_flag(tp, IS_SSB_CORE)) {
                err = ssb_gige_get_macaddr(tp->pdev, &dev->dev_addr[0]);
@@ -17074,13 +17055,8 @@ static int tg3_get_device_address(struct tg3 *tp)
                }
        }
 
-       if (!is_valid_ether_addr(&dev->dev_addr[0])) {
-#ifdef CONFIG_SPARC
-               if (!tg3_get_default_macaddr_sparc(tp))
-                       return 0;
-#endif
+       if (!is_valid_ether_addr(&dev->dev_addr[0]))
                return -EINVAL;
-       }
        return 0;
 }
 
index 1d86b4d..b126926 100644 (file)
@@ -61,7 +61,8 @@
 #define MACB_TX_ERR_FLAGS      (MACB_BIT(ISR_TUND)                     \
                                        | MACB_BIT(ISR_RLE)             \
                                        | MACB_BIT(TXERR))
-#define MACB_TX_INT_FLAGS      (MACB_TX_ERR_FLAGS | MACB_BIT(TCOMP))
+#define MACB_TX_INT_FLAGS      (MACB_TX_ERR_FLAGS | MACB_BIT(TCOMP)    \
+                                       | MACB_BIT(TXUBR))
 
 /* Max length of transmit frame must be a multiple of 8 bytes */
 #define MACB_TX_LEN_ALIGN      8
@@ -680,6 +681,11 @@ static void macb_set_addr(struct macb *bp, struct macb_dma_desc *desc, dma_addr_
        if (bp->hw_dma_cap & HW_DMA_CAP_64B) {
                desc_64 = macb_64b_desc(bp, desc);
                desc_64->addrh = upper_32_bits(addr);
+               /* The low bits of RX address contain the RX_USED bit, clearing
+                * of which allows packet RX. Make sure the high bits are also
+                * visible to HW at that point.
+                */
+               dma_wmb();
        }
 #endif
        desc->addr = lower_32_bits(addr);
@@ -928,14 +934,19 @@ static void gem_rx_refill(struct macb_queue *queue)
 
                        if (entry == bp->rx_ring_size - 1)
                                paddr |= MACB_BIT(RX_WRAP);
-                       macb_set_addr(bp, desc, paddr);
                        desc->ctrl = 0;
+                       /* Setting addr clears RX_USED and allows reception,
+                        * make sure ctrl is cleared first to avoid a race.
+                        */
+                       dma_wmb();
+                       macb_set_addr(bp, desc, paddr);
 
                        /* properly align Ethernet header */
                        skb_reserve(skb, NET_IP_ALIGN);
                } else {
-                       desc->addr &= ~MACB_BIT(RX_USED);
                        desc->ctrl = 0;
+                       dma_wmb();
+                       desc->addr &= ~MACB_BIT(RX_USED);
                }
        }
 
@@ -989,11 +1000,15 @@ static int gem_rx(struct macb_queue *queue, int budget)
 
                rxused = (desc->addr & MACB_BIT(RX_USED)) ? true : false;
                addr = macb_get_addr(bp, desc);
-               ctrl = desc->ctrl;
 
                if (!rxused)
                        break;
 
+               /* Ensure ctrl is at least as up-to-date as rxused */
+               dma_rmb();
+
+               ctrl = desc->ctrl;
+
                queue->rx_tail++;
                count++;
 
@@ -1168,11 +1183,14 @@ static int macb_rx(struct macb_queue *queue, int budget)
                /* Make hw descriptor updates visible to CPU */
                rmb();
 
-               ctrl = desc->ctrl;
-
                if (!(desc->addr & MACB_BIT(RX_USED)))
                        break;
 
+               /* Ensure ctrl is at least as up-to-date as addr */
+               dma_rmb();
+
+               ctrl = desc->ctrl;
+
                if (ctrl & MACB_BIT(RX_SOF)) {
                        if (first_frag != -1)
                                discard_partial_frame(queue, first_frag, tail);
@@ -1312,6 +1330,21 @@ static void macb_hresp_error_task(unsigned long data)
        netif_tx_start_all_queues(dev);
 }
 
+static void macb_tx_restart(struct macb_queue *queue)
+{
+       unsigned int head = queue->tx_head;
+       unsigned int tail = queue->tx_tail;
+       struct macb *bp = queue->bp;
+
+       if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+               queue_writel(queue, ISR, MACB_BIT(TXUBR));
+
+       if (head == tail)
+               return;
+
+       macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
+}
+
 static irqreturn_t macb_interrupt(int irq, void *dev_id)
 {
        struct macb_queue *queue = dev_id;
@@ -1369,6 +1402,9 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
                if (status & MACB_BIT(TCOMP))
                        macb_tx_interrupt(queue);
 
+               if (status & MACB_BIT(TXUBR))
+                       macb_tx_restart(queue);
+
                /* Link change detection isn't possible with RMII, so we'll
                 * add that if/when we get our hands on a full-blown MII PHY.
                 */
@@ -4055,7 +4091,7 @@ static int macb_probe(struct platform_device *pdev)
        if (mac) {
                ether_addr_copy(bp->dev->dev_addr, mac);
        } else {
-               err = of_get_nvmem_mac_address(np, bp->dev->dev_addr);
+               err = nvmem_get_mac_address(&pdev->dev, bp->dev->dev_addr);
                if (err) {
                        if (err == -EPROBE_DEFER)
                                goto err_out_free_netdev;
index cd5296b..a6dc47e 100644 (file)
@@ -319,6 +319,8 @@ int gem_ptp_txstamp(struct macb_queue *queue, struct sk_buff *skb,
        desc_ptp = macb_ptp_desc(queue->bp, desc);
        tx_timestamp = &queue->tx_timestamps[head];
        tx_timestamp->skb = skb;
+       /* ensure ts_1/ts_2 is loaded after ctrl (TX_USED check) */
+       dma_rmb();
        tx_timestamp->desc_ptp.ts_1 = desc_ptp->ts_1;
        tx_timestamp->desc_ptp.ts_2 = desc_ptp->ts_2;
        /* move head */
index 6aeb104..73632b8 100644 (file)
@@ -277,10 +277,6 @@ static int cavium_ptp_probe(struct pci_dev *pdev,
        writeq(clock_comp, clock->reg_base + PTP_CLOCK_COMP);
 
        clock->ptp_clock = ptp_clock_register(&clock->ptp_info, dev);
-       if (!clock->ptp_clock) {
-               err = -ENODEV;
-               goto error_stop;
-       }
        if (IS_ERR(clock->ptp_clock)) {
                err = PTR_ERR(clock->ptp_clock);
                goto error_stop;
index 4c3925a..abe5d0d 100644 (file)
@@ -111,7 +111,7 @@ static const char oct_stats_strings[][ETH_GSTRING_LEN] = {
        "mac_tx_one_collision",
        "mac_tx_multi_collision",
        "mac_tx_max_collision_fail",
-       "mac_tx_max_deferal_fail",
+       "mac_tx_max_deferral_fail",
        "mac_tx_fifo_err",
        "mac_tx_runts",
 
index ea9859e..de61060 100644 (file)
@@ -349,13 +349,15 @@ lio_vf_rep_packet_sent_callback(struct octeon_device *oct,
        struct octeon_soft_command *sc = (struct octeon_soft_command *)buf;
        struct sk_buff *skb = sc->ctxptr;
        struct net_device *ndev = skb->dev;
+       u32 iq_no;
 
        dma_unmap_single(&oct->pci_dev->dev, sc->dmadptr,
                         sc->datasize, DMA_TO_DEVICE);
        dev_kfree_skb_any(skb);
+       iq_no = sc->iq_no;
        octeon_free_soft_command(oct, sc);
 
-       if (octnet_iq_is_full(oct, sc->iq_no))
+       if (octnet_iq_is_full(oct, iq_no))
                return;
 
        if (netif_queue_stopped(ndev))
index 55af04f..6c8dcb6 100644 (file)
@@ -1441,6 +1441,9 @@ static void nic_remove(struct pci_dev *pdev)
 {
        struct nicpf *nic = pci_get_drvdata(pdev);
 
+       if (!nic)
+               return;
+
        if (nic->flags & NIC_SRIOV_ENABLED)
                pci_disable_sriov(pdev);
 
index 768f584..88f8a8f 100644 (file)
@@ -1784,6 +1784,7 @@ static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog)
        bool if_up = netif_running(nic->netdev);
        struct bpf_prog *old_prog;
        bool bpf_attached = false;
+       int ret = 0;
 
        /* For now just support only the usual MTU sized frames */
        if (prog && (dev->mtu > 1500)) {
@@ -1817,8 +1818,12 @@ static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog)
        if (nic->xdp_prog) {
                /* Attach BPF program */
                nic->xdp_prog = bpf_prog_add(nic->xdp_prog, nic->rx_queues - 1);
-               if (!IS_ERR(nic->xdp_prog))
+               if (!IS_ERR(nic->xdp_prog)) {
                        bpf_attached = true;
+               } else {
+                       ret = PTR_ERR(nic->xdp_prog);
+                       nic->xdp_prog = NULL;
+               }
        }
 
        /* Calculate Tx queues needed for XDP and network stack */
@@ -1830,7 +1835,7 @@ static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog)
                netif_trans_update(nic->netdev);
        }
 
-       return 0;
+       return ret;
 }
 
 static int nicvf_xdp(struct net_device *netdev, struct netdev_bpf *xdp)
index 187a249..fcaf18f 100644 (file)
@@ -585,10 +585,12 @@ static void nicvf_free_snd_queue(struct nicvf *nic, struct snd_queue *sq)
        if (!sq->dmem.base)
                return;
 
-       if (sq->tso_hdrs)
+       if (sq->tso_hdrs) {
                dma_free_coherent(&nic->pdev->dev,
                                  sq->dmem.q_len * TSO_HEADER_SIZE,
                                  sq->tso_hdrs, sq->tso_hdrs_phys);
+               sq->tso_hdrs = NULL;
+       }
 
        /* Free pending skbs in the queue */
        smp_rmb();
index 75c1c5e..e8001e9 100644 (file)
@@ -24,7 +24,8 @@ config CHELSIO_T1
        ---help---
          This driver supports Chelsio gigabit and 10-gigabit
          Ethernet cards. More information about adapter features and
-         performance tuning is in <file:Documentation/networking/cxgb.txt>.
+         performance tuning is in
+         <file:Documentation/networking/device_drivers/chelsio/cxgb.txt>.
 
          For general information about Chelsio and our products, visit
          our website at <http://www.chelsio.com>.
@@ -67,7 +68,6 @@ config CHELSIO_T3
 config CHELSIO_T4
        tristate "Chelsio Communications T4/T5/T6 Ethernet support"
        depends on PCI && (IPV6 || IPV6=n)
-       depends on THERMAL || !THERMAL
        select FW_LOADER
        select MDIO
        select ZLIB_DEFLATE
index 78e5d17..91d8a88 100644 (file)
@@ -12,6 +12,4 @@ cxgb4-objs := cxgb4_main.o l2t.o smt.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o
 cxgb4-$(CONFIG_CHELSIO_T4_DCB) +=  cxgb4_dcb.o
 cxgb4-$(CONFIG_CHELSIO_T4_FCOE) +=  cxgb4_fcoe.o
 cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
-ifdef CONFIG_THERMAL
-cxgb4-objs += cxgb4_thermal.o
-endif
+cxgb4-$(CONFIG_THERMAL) += cxgb4_thermal.o
index b16f4b3..2d1ca92 100644 (file)
@@ -404,6 +404,7 @@ struct adapter_params {
        bool fr_nsmr_tpte_wr_support;     /* FW support for FR_NSMR_TPTE_WR */
        u8 fw_caps_support;             /* 32-bit Port Capabilities */
        bool filter2_wr_support;        /* FW support for FILTER2_WR */
+       unsigned int viid_smt_extn_support:1; /* FW returns vin and smt index */
 
        /* MPS Buffer Group Map[per Port].  Bit i is set if buffer group i is
         * used by the Port
@@ -592,6 +593,13 @@ struct port_info {
        bool ptp_enable;
        struct sched_table *sched_tbl;
        u32 eth_flags;
+
+       /* viid and smt fields either returned by fw
+        * or decoded by parsing viid by driver.
+        */
+       u8 vin;
+       u8 vivld;
+       u8 smt_idx;
 };
 
 struct dentry;
@@ -1757,7 +1765,7 @@ int t4_cfg_pfvf(struct adapter *adap, unsigned int mbox, unsigned int pf,
                unsigned int nexact, unsigned int rcaps, unsigned int wxcaps);
 int t4_alloc_vi(struct adapter *adap, unsigned int mbox, unsigned int port,
                unsigned int pf, unsigned int vf, unsigned int nmac, u8 *mac,
-               unsigned int *rss_size);
+               unsigned int *rss_size, u8 *vivld, u8 *vin);
 int t4_free_vi(struct adapter *adap, unsigned int mbox,
               unsigned int pf, unsigned int vf,
               unsigned int viid);
@@ -1783,7 +1791,7 @@ int t4_free_mac_filt(struct adapter *adap, unsigned int mbox,
                     unsigned int viid, unsigned int naddr,
                     const u8 **addr, bool sleep_ok);
 int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
-                 int idx, const u8 *addr, bool persist, bool add_smt);
+                 int idx, const u8 *addr, bool persist, u8 *smt_idx);
 int t4_set_addr_hash(struct adapter *adap, unsigned int mbox, unsigned int viid,
                     bool ucast, u64 vec, bool sleep_ok);
 int t4_enable_vi_params(struct adapter *adap, unsigned int mbox,
index cab492e..b0ff9fa 100644 (file)
@@ -378,19 +378,7 @@ static int cim_qcfg_show(struct seq_file *seq, void *v)
                           QUEREMFLITS_G(p[2]) * 16);
        return 0;
 }
-
-static int cim_qcfg_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, cim_qcfg_show, inode->i_private);
-}
-
-static const struct file_operations cim_qcfg_fops = {
-       .owner   = THIS_MODULE,
-       .open    = cim_qcfg_open,
-       .read    = seq_read,
-       .llseek  = seq_lseek,
-       .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(cim_qcfg);
 
 static int cimq_show(struct seq_file *seq, void *v, int idx)
 {
@@ -860,8 +848,7 @@ static int tx_rate_show(struct seq_file *seq, void *v)
        }
        return 0;
 }
-
-DEFINE_SIMPLE_DEBUGFS_FILE(tx_rate);
+DEFINE_SHOW_ATTRIBUTE(tx_rate);
 
 static int cctrl_tbl_show(struct seq_file *seq, void *v)
 {
@@ -893,8 +880,7 @@ static int cctrl_tbl_show(struct seq_file *seq, void *v)
        kfree(incr);
        return 0;
 }
-
-DEFINE_SIMPLE_DEBUGFS_FILE(cctrl_tbl);
+DEFINE_SHOW_ATTRIBUTE(cctrl_tbl);
 
 /* Format a value in a unit that differs from the value's native unit by the
  * given factor.
@@ -955,8 +941,7 @@ static int clk_show(struct seq_file *seq, void *v)
 
        return 0;
 }
-
-DEFINE_SIMPLE_DEBUGFS_FILE(clk);
+DEFINE_SHOW_ATTRIBUTE(clk);
 
 /* Firmware Device Log dump. */
 static const char * const devlog_level_strings[] = {
@@ -1990,22 +1975,10 @@ static int sensors_show(struct seq_file *seq, void *v)
 
        return 0;
 }
-
-DEFINE_SIMPLE_DEBUGFS_FILE(sensors);
+DEFINE_SHOW_ATTRIBUTE(sensors);
 
 #if IS_ENABLED(CONFIG_IPV6)
-static int clip_tbl_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, clip_tbl_show, inode->i_private);
-}
-
-static const struct file_operations clip_tbl_debugfs_fops = {
-       .owner   = THIS_MODULE,
-       .open    = clip_tbl_open,
-       .read    = seq_read,
-       .llseek  = seq_lseek,
-       .release = single_release
-};
+DEFINE_SHOW_ATTRIBUTE(clip_tbl);
 #endif
 
 /*RSS Table.
@@ -2208,8 +2181,7 @@ static int rss_config_show(struct seq_file *seq, void *v)
 
        return 0;
 }
-
-DEFINE_SIMPLE_DEBUGFS_FILE(rss_config);
+DEFINE_SHOW_ATTRIBUTE(rss_config);
 
 /* RSS Secret Key.
  */
@@ -2628,19 +2600,7 @@ static int resources_show(struct seq_file *seq, void *v)
 
        return 0;
 }
-
-static int resources_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, resources_show, inode->i_private);
-}
-
-static const struct file_operations resources_debugfs_fops = {
-       .owner   = THIS_MODULE,
-       .open    = resources_open,
-       .read    = seq_read,
-       .llseek  = seq_lseek,
-       .release = seq_release,
-};
+DEFINE_SHOW_ATTRIBUTE(resources);
 
 /**
  * ethqset2pinfo - return port_info of an Ethernet Queue Set
@@ -3233,8 +3193,7 @@ static int tid_info_show(struct seq_file *seq, void *v)
                           t4_read_reg(adap, LE_DB_ACT_CNT_IPV6_A));
        return 0;
 }
-
-DEFINE_SIMPLE_DEBUGFS_FILE(tid_info);
+DEFINE_SHOW_ATTRIBUTE(tid_info);
 
 static void add_debugfs_mem(struct adapter *adap, const char *name,
                            unsigned int idx, unsigned int size_mb)
@@ -3364,21 +3323,9 @@ static int meminfo_show(struct seq_file *seq, void *v)
 
        return 0;
 }
+DEFINE_SHOW_ATTRIBUTE(meminfo);
 
-static int meminfo_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, meminfo_show, inode->i_private);
-}
-
-static const struct file_operations meminfo_fops = {
-       .owner   = THIS_MODULE,
-       .open    = meminfo_open,
-       .read    = seq_read,
-       .llseek  = seq_lseek,
-       .release = single_release,
-};
-
-static int chcr_show(struct seq_file *seq, void *v)
+static int chcr_stats_show(struct seq_file *seq, void *v)
 {
        struct adapter *adap = seq->private;
 
@@ -3399,20 +3346,7 @@ static int chcr_show(struct seq_file *seq, void *v)
                   atomic_read(&adap->chcr_stats.ipsec_cnt));
        return 0;
 }
-
-
-static int chcr_stats_open(struct inode *inode, struct file *file)
-{
-        return single_open(file, chcr_show, inode->i_private);
-}
-
-static const struct file_operations chcr_stats_debugfs_fops = {
-        .owner   = THIS_MODULE,
-        .open    = chcr_stats_open,
-        .read    = seq_read,
-        .llseek  = seq_lseek,
-        .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(chcr_stats);
 
 #define PRINT_ADAP_STATS(string, value) \
        seq_printf(seq, "%-25s %-20llu\n", (string), \
@@ -3573,8 +3507,7 @@ static int tp_stats_show(struct seq_file *seq, void *v)
 
        return 0;
 }
-
-DEFINE_SIMPLE_DEBUGFS_FILE(tp_stats);
+DEFINE_SHOW_ATTRIBUTE(tp_stats);
 
 /* Add an array of Debug FS files.
  */
@@ -3603,7 +3536,7 @@ int t4_setup_debugfs(struct adapter *adap)
                { "cim_pif_la", &cim_pif_la_fops, 0400, 0 },
                { "cim_ma_la", &cim_ma_la_fops, 0400, 0 },
                { "cim_qcfg", &cim_qcfg_fops, 0400, 0 },
-               { "clk", &clk_debugfs_fops, 0400, 0 },
+               { "clk", &clk_fops, 0400, 0 },
                { "devlog", &devlog_fops, 0400, 0 },
                { "mboxlog", &mboxlog_fops, 0400, 0 },
                { "mbox0", &mbox_debugfs_fops, 0600, 0 },
@@ -3621,11 +3554,11 @@ int t4_setup_debugfs(struct adapter *adap)
                { "l2t", &t4_l2t_fops, 0400, 0},
                { "mps_tcam", &mps_tcam_debugfs_fops, 0400, 0 },
                { "rss", &rss_debugfs_fops, 0400, 0 },
-               { "rss_config", &rss_config_debugfs_fops, 0400, 0 },
+               { "rss_config", &rss_config_fops, 0400, 0 },
                { "rss_key", &rss_key_debugfs_fops, 0400, 0 },
                { "rss_pf_config", &rss_pf_config_debugfs_fops, 0400, 0 },
                { "rss_vf_config", &rss_vf_config_debugfs_fops, 0400, 0 },
-               { "resources", &resources_debugfs_fops, 0400, 0 },
+               { "resources", &resources_fops, 0400, 0 },
 #ifdef CONFIG_CHELSIO_T4_DCB
                { "dcb_info", &dcb_info_debugfs_fops, 0400, 0 },
 #endif
@@ -3644,18 +3577,18 @@ int t4_setup_debugfs(struct adapter *adap)
                { "obq_ncsi", &cim_obq_fops, 0400, 5 },
                { "tp_la", &tp_la_fops, 0400, 0 },
                { "ulprx_la", &ulprx_la_fops, 0400, 0 },
-               { "sensors", &sensors_debugfs_fops, 0400, 0 },
+               { "sensors", &sensors_fops, 0400, 0 },
                { "pm_stats", &pm_stats_debugfs_fops, 0400, 0 },
-               { "tx_rate", &tx_rate_debugfs_fops, 0400, 0 },
-               { "cctrl", &cctrl_tbl_debugfs_fops, 0400, 0 },
+               { "tx_rate", &tx_rate_fops, 0400, 0 },
+               { "cctrl", &cctrl_tbl_fops, 0400, 0 },
 #if IS_ENABLED(CONFIG_IPV6)
-               { "clip_tbl", &clip_tbl_debugfs_fops, 0400, 0 },
+               { "clip_tbl", &clip_tbl_fops, 0400, 0 },
 #endif
-               { "tids", &tid_info_debugfs_fops, 0400, 0},
+               { "tids", &tid_info_fops, 0400, 0},
                { "blocked_fl", &blocked_fl_fops, 0600, 0 },
                { "meminfo", &meminfo_fops, 0400, 0 },
-               { "crypto", &chcr_stats_debugfs_fops, 0400, 0 },
-               { "tp_stats", &tp_stats_debugfs_fops, 0400, 0 },
+               { "crypto", &chcr_stats_fops, 0400, 0 },
+               { "tp_stats", &tp_stats_fops, 0400, 0 },
        };
 
        /* Debug FS nodes common to all T5 and later adapters.
index 23f43a0..ba95e13 100644 (file)
 
 #include <linux/export.h>
 
-#define DEFINE_SIMPLE_DEBUGFS_FILE(name) \
-static int name##_open(struct inode *inode, struct file *file) \
-{ \
-       return single_open(file, name##_show, inode->i_private); \
-} \
-static const struct file_operations name##_debugfs_fops = { \
-       .owner   = THIS_MODULE, \
-       .open    = name##_open, \
-       .read    = seq_read, \
-       .llseek  = seq_lseek, \
-       .release = single_release \
-}
-
 struct t4_debugfs_entry {
        const char *name;
        const struct file_operations *ops;
index 956e708..6ba9099 100644 (file)
@@ -453,7 +453,7 @@ static int link_start(struct net_device *dev)
        if (ret == 0) {
                ret = t4_change_mac(pi->adapter, mb, pi->viid,
                                    pi->xact_addr_filt, dev->dev_addr, true,
-                                   true);
+                                   &pi->smt_idx);
                if (ret >= 0) {
                        pi->xact_addr_filt = ret;
                        ret = 0;
@@ -1584,28 +1584,6 @@ unsigned int cxgb4_best_aligned_mtu(const unsigned short *mtus,
 }
 EXPORT_SYMBOL(cxgb4_best_aligned_mtu);
 
-/**
- *     cxgb4_tp_smt_idx - Get the Source Mac Table index for this VI
- *     @chip: chip type
- *     @viid: VI id of the given port
- *
- *     Return the SMT index for this VI.
- */
-unsigned int cxgb4_tp_smt_idx(enum chip_type chip, unsigned int viid)
-{
-       /* In T4/T5, SMT contains 256 SMAC entries organized in
-        * 128 rows of 2 entries each.
-        * In T6, SMT contains 256 SMAC entries in 256 rows.
-        * TODO: The below code needs to be updated when we add support
-        * for 256 VFs.
-        */
-       if (CHELSIO_CHIP_VERSION(chip) <= CHELSIO_T5)
-               return ((viid & 0x7f) << 1);
-       else
-               return (viid & 0x7f);
-}
-EXPORT_SYMBOL(cxgb4_tp_smt_idx);
-
 /**
  *     cxgb4_port_chan - get the HW channel of a port
  *     @dev: the net device for the port
@@ -2280,8 +2258,6 @@ static int cxgb_up(struct adapter *adap)
 #if IS_ENABLED(CONFIG_IPV6)
        update_clip(adap);
 #endif
-       /* Initialize hash mac addr list*/
-       INIT_LIST_HEAD(&adap->mac_hlist);
        return err;
 
  irq_err:
@@ -2295,8 +2271,6 @@ static int cxgb_up(struct adapter *adap)
 
 static void cxgb_down(struct adapter *adapter)
 {
-       struct hash_mac_addr *entry, *tmp;
-
        cancel_work_sync(&adapter->tid_release_task);
        cancel_work_sync(&adapter->db_full_task);
        cancel_work_sync(&adapter->db_drop_task);
@@ -2306,11 +2280,6 @@ static void cxgb_down(struct adapter *adapter)
        t4_sge_stop(adapter);
        t4_free_sge_resources(adapter);
 
-       list_for_each_entry_safe(entry, tmp, &adapter->mac_hlist, list) {
-               list_del(&entry->list);
-               kfree(entry);
-       }
-
        adapter->flags &= ~FULL_INIT_DONE;
 }
 
@@ -2677,7 +2646,7 @@ static void cxgb4_mgmt_fill_vf_station_mac_addr(struct adapter *adap)
 
        for (vf = 0, nvfs = pci_sriov_get_totalvfs(adap->pdev);
                vf < nvfs; vf++) {
-               macaddr[5] = adap->pf * 16 + vf;
+               macaddr[5] = adap->pf * nvfs + vf;
                ether_addr_copy(adap->vfinfo[vf].vf_mac_addr, macaddr);
        }
 }
@@ -2871,7 +2840,8 @@ static int cxgb_set_mac_addr(struct net_device *dev, void *p)
                return -EADDRNOTAVAIL;
 
        ret = t4_change_mac(pi->adapter, pi->adapter->pf, pi->viid,
-                           pi->xact_addr_filt, addr->sa_data, true, true);
+                           pi->xact_addr_filt, addr->sa_data, true,
+                           &pi->smt_idx);
        if (ret < 0)
                return ret;
 
@@ -4475,6 +4445,15 @@ static int adap_init0(struct adapter *adap)
                adap->params.filter2_wr_support = (ret == 0 && val[0] != 0);
        }
 
+       /* Check if FW supports returning vin and smt index.
+        * If this is not supported, driver will interpret
+        * these values from viid.
+        */
+       params[0] = FW_PARAM_DEV(OPAQUE_VIID_SMT_EXTN);
+       ret = t4_query_params(adap, adap->mbox, adap->pf, 0,
+                             1, params, val);
+       adap->params.viid_smt_extn_support = (ret == 0 && val[0] != 0);
+
        /*
         * Get device capabilities so we can determine what resources we need
         * to manage.
@@ -4785,14 +4764,26 @@ static pci_ers_result_t eeh_slot_reset(struct pci_dev *pdev)
                return PCI_ERS_RESULT_DISCONNECT;
 
        for_each_port(adap, i) {
-               struct port_info *p = adap2pinfo(adap, i);
+               struct port_info *pi = adap2pinfo(adap, i);
+               u8 vivld = 0, vin = 0;
 
-               ret = t4_alloc_vi(adap, adap->mbox, p->tx_chan, adap->pf, 0, 1,
-                                 NULL, NULL);
+               ret = t4_alloc_vi(adap, adap->mbox, pi->tx_chan, adap->pf, 0, 1,
+                                 NULL, NULL, &vivld, &vin);
                if (ret < 0)
                        return PCI_ERS_RESULT_DISCONNECT;
-               p->viid = ret;
-               p->xact_addr_filt = -1;
+               pi->viid = ret;
+               pi->xact_addr_filt = -1;
+               /* If fw supports returning the VIN as part of FW_VI_CMD,
+                * save the returned values.
+                */
+               if (adap->params.viid_smt_extn_support) {
+                       pi->vivld = vivld;
+                       pi->vin = vin;
+               } else {
+                       /* Retrieve the values from VIID */
+                       pi->vivld = FW_VIID_VIVLD_G(pi->viid);
+                       pi->vin = FW_VIID_VIN_G(pi->viid);
+               }
        }
 
        t4_load_mtus(adap, adap->params.mtus, adap->params.a_wnd,
@@ -5629,6 +5620,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                             (is_t5(adapter->params.chip) ? STATMODE_V(0) :
                              T6_STATMODE_V(0)));
 
+       /* Initialize hash mac addr list */
+       INIT_LIST_HEAD(&adapter->mac_hlist);
+
        for_each_port(adapter, i) {
                netdev = alloc_etherdev_mq(sizeof(struct port_info),
                                           MAX_ETH_QSETS);
@@ -5871,7 +5865,7 @@ fw_attach_fail:
        if (!is_t4(adapter->params.chip))
                cxgb4_ptp_init(adapter);
 
-       if (IS_ENABLED(CONFIG_THERMAL) &&
+       if (IS_REACHABLE(CONFIG_THERMAL) &&
            !is_t4(adapter->params.chip) && (adapter->flags & FW_OK))
                cxgb4_thermal_init(adapter);
 
@@ -5907,6 +5901,7 @@ fw_attach_fail:
 static void remove_one(struct pci_dev *pdev)
 {
        struct adapter *adapter = pci_get_drvdata(pdev);
+       struct hash_mac_addr *entry, *tmp;
 
        if (!adapter) {
                pci_release_regions(pdev);
@@ -5940,7 +5935,7 @@ static void remove_one(struct pci_dev *pdev)
 
                if (!is_t4(adapter->params.chip))
                        cxgb4_ptp_stop(adapter);
-               if (IS_ENABLED(CONFIG_THERMAL))
+               if (IS_REACHABLE(CONFIG_THERMAL))
                        cxgb4_thermal_remove(adapter);
 
                /* If we allocated filters, free up state associated with any
@@ -5956,6 +5951,12 @@ static void remove_one(struct pci_dev *pdev)
                if (adapter->num_uld || adapter->num_ofld_uld)
                        t4_uld_mem_free(adapter);
                free_some_resources(adapter);
+               list_for_each_entry_safe(entry, tmp, &adapter->mac_hlist,
+                                        list) {
+                       list_del(&entry->list);
+                       kfree(entry);
+               }
+
 #if IS_ENABLED(CONFIG_IPV6)
                t4_cleanup_clip_tbl(adapter);
 #endif
index 99022c0..4852feb 100644 (file)
@@ -495,14 +495,11 @@ u64 cxgb4_select_ntuple(struct net_device *dev,
                ntuple |= (u64)IPPROTO_TCP << tp->protocol_shift;
 
        if (tp->vnic_shift >= 0 && (tp->ingress_config & VNIC_F)) {
-               u32 viid = cxgb4_port_viid(dev);
-               u32 vf = FW_VIID_VIN_G(viid);
-               u32 pf = FW_VIID_PFN_G(viid);
-               u32 vld = FW_VIID_VIVLD_G(viid);
-
-               ntuple |= (u64)(FT_VNID_ID_VF_V(vf) |
-                               FT_VNID_ID_PF_V(pf) |
-                               FT_VNID_ID_VLD_V(vld)) << tp->vnic_shift;
+               struct port_info *pi = (struct port_info *)netdev_priv(dev);
+
+               ntuple |= (u64)(FT_VNID_ID_VF_V(pi->vin) |
+                               FT_VNID_ID_PF_V(adap->pf) |
+                               FT_VNID_ID_VLD_V(pi->vivld)) << tp->vnic_shift;
        }
 
        return ntuple;
index cb52394..e8c3429 100644 (file)
@@ -5880,7 +5880,6 @@ int t4_set_trace_filter(struct adapter *adap, const struct trace_params *tp,
 {
        int i, ofst = idx * 4;
        u32 data_reg, mask_reg, cfg;
-       u32 multitrc = TRCMULTIFILTER_F;
 
        if (!enable) {
                t4_write_reg(adap, MPS_TRC_FILTER_MATCH_CTL_A_A + ofst, 0);
@@ -5900,7 +5899,6 @@ int t4_set_trace_filter(struct adapter *adap, const struct trace_params *tp,
                 * maximum packet capture size of 9600 bytes is recommended.
                 * Also in this mode, only trace0 can be enabled and running.
                 */
-               multitrc = 0;
                if (tp->snap_len > 9600 || idx)
                        return -EINVAL;
        }
@@ -7141,21 +7139,10 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size,
                         unsigned int cache_line_size)
 {
        unsigned int page_shift = fls(page_size) - 1;
-       unsigned int sge_hps = page_shift - 10;
        unsigned int stat_len = cache_line_size > 64 ? 128 : 64;
        unsigned int fl_align = cache_line_size < 32 ? 32 : cache_line_size;
        unsigned int fl_align_log = fls(fl_align) - 1;
 
-       t4_write_reg(adap, SGE_HOST_PAGE_SIZE_A,
-                    HOSTPAGESIZEPF0_V(sge_hps) |
-                    HOSTPAGESIZEPF1_V(sge_hps) |
-                    HOSTPAGESIZEPF2_V(sge_hps) |
-                    HOSTPAGESIZEPF3_V(sge_hps) |
-                    HOSTPAGESIZEPF4_V(sge_hps) |
-                    HOSTPAGESIZEPF5_V(sge_hps) |
-                    HOSTPAGESIZEPF6_V(sge_hps) |
-                    HOSTPAGESIZEPF7_V(sge_hps));
-
        if (is_t4(adap->params.chip)) {
                t4_set_reg_field(adap, SGE_CONTROL_A,
                                 INGPADBOUNDARY_V(INGPADBOUNDARY_M) |
@@ -7488,7 +7475,7 @@ int t4_cfg_pfvf(struct adapter *adap, unsigned int mbox, unsigned int pf,
  */
 int t4_alloc_vi(struct adapter *adap, unsigned int mbox, unsigned int port,
                unsigned int pf, unsigned int vf, unsigned int nmac, u8 *mac,
-               unsigned int *rss_size)
+               unsigned int *rss_size, u8 *vivld, u8 *vin)
 {
        int ret;
        struct fw_vi_cmd c;
@@ -7523,6 +7510,13 @@ int t4_alloc_vi(struct adapter *adap, unsigned int mbox, unsigned int port,
        }
        if (rss_size)
                *rss_size = FW_VI_CMD_RSSSIZE_G(be16_to_cpu(c.rsssize_pkd));
+
+       if (vivld)
+               *vivld = FW_VI_CMD_VFVLD_G(be32_to_cpu(c.alloc_to_len16));
+
+       if (vin)
+               *vin = FW_VI_CMD_VIN_G(be32_to_cpu(c.alloc_to_len16));
+
        return FW_VI_CMD_VIID_G(be16_to_cpu(c.type_viid));
 }
 
@@ -7980,7 +7974,7 @@ int t4_free_mac_filt(struct adapter *adap, unsigned int mbox,
  *     MAC value.
  */
 int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
-                 int idx, const u8 *addr, bool persist, bool add_smt)
+                 int idx, const u8 *addr, bool persist, u8 *smt_idx)
 {
        int ret, mode;
        struct fw_vi_mac_cmd c;
@@ -7989,7 +7983,7 @@ int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
 
        if (idx < 0)                             /* new allocation */
                idx = persist ? FW_VI_MAC_ADD_PERSIST_MAC : FW_VI_MAC_ADD_MAC;
-       mode = add_smt ? FW_VI_MAC_SMT_AND_MPSTCAM : FW_VI_MAC_MPS_TCAM_ENTRY;
+       mode = smt_idx ? FW_VI_MAC_SMT_AND_MPSTCAM : FW_VI_MAC_MPS_TCAM_ENTRY;
 
        memset(&c, 0, sizeof(c));
        c.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_MAC_CMD) |
@@ -8006,6 +8000,23 @@ int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
                ret = FW_VI_MAC_CMD_IDX_G(be16_to_cpu(p->valid_to_idx));
                if (ret >= max_mac_addr)
                        ret = -ENOMEM;
+               if (smt_idx) {
+                       if (adap->params.viid_smt_extn_support) {
+                               *smt_idx = FW_VI_MAC_CMD_SMTID_G
+                                                   (be32_to_cpu(c.op_to_viid));
+                       } else {
+                               /* In T4/T5, SMT contains 256 SMAC entries
+                                * organized in 128 rows of 2 entries each.
+                                * In T6, SMT contains 256 SMAC entries in
+                                * 256 rows.
+                                */
+                               if (CHELSIO_CHIP_VERSION(adap->params.chip) <=
+                                                                    CHELSIO_T5)
+                                       *smt_idx = (viid & FW_VIID_VIN_M) << 1;
+                               else
+                                       *smt_idx = (viid & FW_VIID_VIN_M);
+                       }
+               }
        }
        return ret;
 }
@@ -8593,7 +8604,7 @@ int t4_get_link_params(struct port_info *pi, unsigned int *link_okp,
 {
        unsigned int fw_caps = pi->adapter->params.fw_caps_support;
        struct fw_port_cmd port_cmd;
-       unsigned int action, link_ok, speed, mtu;
+       unsigned int action, link_ok, mtu;
        fw_port_cap32_t linkattr;
        int ret;
 
@@ -8627,7 +8638,6 @@ int t4_get_link_params(struct port_info *pi, unsigned int *link_okp,
                mtu = FW_PORT_CMD_MTU32_G(
                        be32_to_cpu(port_cmd.u.info32.auxlinfo32_mtu32));
        }
-       speed = fwcap_to_speed(linkattr);
 
        *link_okp = link_ok;
        *speedp = fwcap_to_speed(linkattr);
@@ -9374,6 +9384,7 @@ int t4_init_portinfo(struct port_info *pi, int mbox,
        enum fw_port_type port_type;
        int mdio_addr;
        fw_port_cap32_t pcaps, acaps;
+       u8 vivld = 0, vin = 0;
        int ret;
 
        /* If we haven't yet determined whether we're talking to Firmware
@@ -9428,7 +9439,8 @@ int t4_init_portinfo(struct port_info *pi, int mbox,
                acaps = be32_to_cpu(cmd.u.info32.acaps32);
        }
 
-       ret = t4_alloc_vi(pi->adapter, mbox, port, pf, vf, 1, mac, &rss_size);
+       ret = t4_alloc_vi(pi->adapter, mbox, port, pf, vf, 1, mac, &rss_size,
+                         &vivld, &vin);
        if (ret < 0)
                return ret;
 
@@ -9437,6 +9449,18 @@ int t4_init_portinfo(struct port_info *pi, int mbox,
        pi->lport = port;
        pi->rss_size = rss_size;
 
+       /* If fw supports returning the VIN as part of FW_VI_CMD,
+        * save the returned values.
+        */
+       if (adapter->params.viid_smt_extn_support) {
+               pi->vivld = vivld;
+               pi->vin = vin;
+       } else {
+               /* Retrieve the values from VIID */
+               pi->vivld = FW_VIID_VIVLD_G(pi->viid);
+               pi->vin =  FW_VIID_VIN_G(pi->viid);
+       }
+
        pi->port_type = port_type;
        pi->mdio_addr = mdio_addr;
        pi->mod_type = FW_PORT_MOD_TYPE_NA;
index f152da1..c62a0c8 100644 (file)
@@ -1453,6 +1453,9 @@ struct cpl_tx_data {
 #define T6_TX_FORCE_V(x)       ((x) << T6_TX_FORCE_S)
 #define T6_TX_FORCE_F          T6_TX_FORCE_V(1U)
 
+#define TX_URG_S    16
+#define TX_URG_V(x) ((x) << TX_URG_S)
+
 #define TX_SHOVE_S    14
 #define TX_SHOVE_V(x) ((x) << TX_SHOVE_S)
 
index 57584ab..1d9b3e1 100644 (file)
@@ -1253,6 +1253,7 @@ enum fw_params_param_dev {
        FW_PARAMS_PARAM_DEV_HMA_SIZE    = 0x20,
        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,
 };
 
 /*
@@ -2109,6 +2110,19 @@ struct fw_vi_cmd {
 #define FW_VI_CMD_FREE_V(x)    ((x) << FW_VI_CMD_FREE_S)
 #define FW_VI_CMD_FREE_F       FW_VI_CMD_FREE_V(1U)
 
+#define FW_VI_CMD_VFVLD_S      24
+#define FW_VI_CMD_VFVLD_M      0x1
+#define FW_VI_CMD_VFVLD_V(x)   ((x) << FW_VI_CMD_VFVLD_S)
+#define FW_VI_CMD_VFVLD_G(x)   \
+       (((x) >> FW_VI_CMD_VFVLD_S) & FW_VI_CMD_VFVLD_M)
+#define FW_VI_CMD_VFVLD_F      FW_VI_CMD_VFVLD_V(1U)
+
+#define FW_VI_CMD_VIN_S                16
+#define FW_VI_CMD_VIN_M                0xff
+#define FW_VI_CMD_VIN_V(x)     ((x) << FW_VI_CMD_VIN_S)
+#define FW_VI_CMD_VIN_G(x)     \
+       (((x) >> FW_VI_CMD_VIN_S) & FW_VI_CMD_VIN_M)
+
 #define FW_VI_CMD_VIID_S       0
 #define FW_VI_CMD_VIID_M       0xfff
 #define FW_VI_CMD_VIID_V(x)    ((x) << FW_VI_CMD_VIID_S)
@@ -2182,6 +2196,12 @@ struct fw_vi_mac_cmd {
        } u;
 };
 
+#define FW_VI_MAC_CMD_SMTID_S          12
+#define FW_VI_MAC_CMD_SMTID_M          0xff
+#define FW_VI_MAC_CMD_SMTID_V(x)       ((x) << FW_VI_MAC_CMD_SMTID_S)
+#define FW_VI_MAC_CMD_SMTID_G(x)       \
+       (((x) >> FW_VI_MAC_CMD_SMTID_S) & FW_VI_MAC_CMD_SMTID_M)
+
 #define FW_VI_MAC_CMD_VIID_S   0
 #define FW_VI_MAC_CMD_VIID_V(x)        ((x) << FW_VI_MAC_CMD_VIID_S)
 
index 8ec503c..2fab87e 100644 (file)
@@ -723,9 +723,6 @@ static int adapter_up(struct adapter *adapter)
                if (adapter->flags & USING_MSIX)
                        name_msix_vecs(adapter);
 
-               /* Initialize hash mac addr list*/
-               INIT_LIST_HEAD(&adapter->mac_hlist);
-
                adapter->flags |= FULL_INIT_DONE;
        }
 
@@ -2326,19 +2323,7 @@ static int resources_show(struct seq_file *seq, void *v)
 
        return 0;
 }
-
-static int resources_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, resources_show, inode->i_private);
-}
-
-static const struct file_operations resources_proc_fops = {
-       .owner   = THIS_MODULE,
-       .open    = resources_open,
-       .read    = seq_read,
-       .llseek  = seq_lseek,
-       .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(resources);
 
 /*
  * Show Virtual Interfaces.
@@ -2422,7 +2407,7 @@ static struct cxgb4vf_debugfs_entry debugfs_files[] = {
        { "mboxlog",    0444, &mboxlog_fops },
        { "sge_qinfo",  0444, &sge_qinfo_debugfs_fops },
        { "sge_qstats", 0444, &sge_qstats_proc_fops },
-       { "resources",  0444, &resources_proc_fops },
+       { "resources",  0444, &resources_fops },
        { "interfaces", 0444, &interfaces_proc_fops },
 };
 
@@ -3038,6 +3023,9 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev,
        if (err)
                goto err_unmap_bar;
 
+       /* Initialize hash mac addr list */
+       INIT_LIST_HEAD(&adapter->mac_hlist);
+
        /*
         * Allocate our "adapter ports" and stitch everything together.
         */
index ec0b545..e9a0213 100644 (file)
@@ -23,7 +23,7 @@ config CS89x0
        ---help---
          Support for CS89x0 chipset based Ethernet cards. If you have a
          network (Ethernet) card of this type, say Y and read the file
-         <file:Documentation/networking/cs89x0.txt>.
+         <file:Documentation/networking/device_drivers/cirrus/cs89x0.txt>.
 
          To compile this driver as a module, choose M here. The module
          will be called cs89x0.
index f42f7a6..ebd5c2c 100644 (file)
@@ -241,7 +241,7 @@ static int enic_set_ringparam(struct net_device *netdev,
        }
        enic_init_vnic_resources(enic);
        if (running) {
-               err = dev_open(netdev);
+               err = dev_open(netdev, NULL);
                if (err)
                        goto err_out;
        }
index ceec467..949103d 100644 (file)
@@ -660,7 +660,7 @@ static void gmac_clean_txq(struct net_device *netdev, struct gmac_txq *txq,
 
                        u64_stats_update_begin(&port->tx_stats_syncp);
                        port->tx_frag_stats[nfrags]++;
-                       u64_stats_update_end(&port->ir_stats_syncp);
+                       u64_stats_update_end(&port->tx_stats_syncp);
                }
        }
 
index 1003201..264e9b4 100644 (file)
@@ -113,7 +113,7 @@ config DE4X5
          These include the DE425, DE434, DE435, DE450 and DE500 models.  If
          you have a network card of this type, say Y.  More specific
          information is contained in
-         <file:Documentation/networking/de4x5.txt>.
+         <file:Documentation/networking/device_drivers/dec/de4x5.txt>.
 
          To compile this driver as a module, choose M here. The module will
          be called de4x5.
@@ -137,7 +137,7 @@ config DM9102
          This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from
          Davicom (<http://www.davicom.com.tw/>).  If you have such a network
          (Ethernet) card, say Y.  Some information is contained in the file
-         <file:Documentation/networking/dmfe.txt>.
+         <file:Documentation/networking/device_drivers/dec/dmfe.txt>.
 
          To compile this driver as a module, choose M here. The module will
          be called dmfe.
index f0536b1..d8d423f 100644 (file)
@@ -1881,7 +1881,7 @@ Compile command:
 
 gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -c dl2k.c
 
-Read Documentation/networking/dl2k.txt for details.
+Read Documentation/networking/device_drivers/dlink/dl2k.txt for details.
 
 */
 
index 80b2bd3..852f5bf 100644 (file)
@@ -796,7 +796,7 @@ static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter,
        u16 vlan_tag;
 
        vlan_tag = skb_vlan_tag_get(skb);
-       vlan_prio = (vlan_tag & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+       vlan_prio = skb_vlan_tag_get_prio(skb);
        /* If vlan priority provided by OS is NOT in available bmap */
        if (!(adapter->vlan_prio_bmap & (1 << vlan_prio)))
                vlan_tag = (vlan_tag & ~VLAN_PRIO_MASK) |
@@ -4955,7 +4955,7 @@ fw_exit:
 }
 
 static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
-                                u16 flags)
+                                u16 flags, struct netlink_ext_ack *extack)
 {
        struct be_adapter *adapter = netdev_priv(dev);
        struct nlattr *attr, *br_spec;
index 570caeb..084f24d 100644 (file)
@@ -872,11 +872,10 @@ static irqreturn_t ftmac100_interrupt(int irq, void *dev_id)
        struct net_device *netdev = dev_id;
        struct ftmac100 *priv = netdev_priv(netdev);
 
-       if (likely(netif_running(netdev))) {
-               /* Disable interrupts for polling */
-               ftmac100_disable_all_int(priv);
+       /* Disable interrupts for polling */
+       ftmac100_disable_all_int(priv);
+       if (likely(netif_running(netdev)))
                napi_schedule(&priv->napi);
-       }
 
        return IRQ_HANDLED;
 }
index 9510c9d..f53090c 100644 (file)
@@ -51,9 +51,9 @@
 #include <linux/percpu.h>
 #include <linux/dma-mapping.h>
 #include <linux/sort.h>
+#include <linux/phy_fixed.h>
 #include <soc/fsl/bman.h>
 #include <soc/fsl/qman.h>
-
 #include "fman.h"
 #include "fman_port.h"
 #include "mac.h"
@@ -2616,6 +2616,7 @@ static const struct net_device_ops dpaa_ops = {
        .ndo_stop = dpaa_eth_stop,
        .ndo_tx_timeout = dpaa_tx_timeout,
        .ndo_get_stats64 = dpaa_get_stats64,
+       .ndo_change_carrier = fixed_phy_change_carrier,
        .ndo_set_mac_address = dpaa_set_mac_address,
        .ndo_validate_addr = eth_validate_addr,
        .ndo_set_rx_mode = dpaa_set_rx_mode,
index 13d6e22..6249711 100644 (file)
@@ -529,6 +529,75 @@ static int dpaa_get_ts_info(struct net_device *net_dev,
        return 0;
 }
 
+static int dpaa_get_coalesce(struct net_device *dev,
+                            struct ethtool_coalesce *c)
+{
+       struct qman_portal *portal;
+       u32 period;
+       u8 thresh;
+
+       portal = qman_get_affine_portal(smp_processor_id());
+       qman_portal_get_iperiod(portal, &period);
+       qman_dqrr_get_ithresh(portal, &thresh);
+
+       c->rx_coalesce_usecs = period;
+       c->rx_max_coalesced_frames = thresh;
+       c->use_adaptive_rx_coalesce = false;
+
+       return 0;
+}
+
+static int dpaa_set_coalesce(struct net_device *dev,
+                            struct ethtool_coalesce *c)
+{
+       const cpumask_t *cpus = qman_affine_cpus();
+       bool needs_revert[NR_CPUS] = {false};
+       struct qman_portal *portal;
+       u32 period, prev_period;
+       u8 thresh, prev_thresh;
+       int cpu, res;
+
+       if (c->use_adaptive_rx_coalesce)
+               return -EINVAL;
+
+       period = c->rx_coalesce_usecs;
+       thresh = c->rx_max_coalesced_frames;
+
+       /* save previous values */
+       portal = qman_get_affine_portal(smp_processor_id());
+       qman_portal_get_iperiod(portal, &prev_period);
+       qman_dqrr_get_ithresh(portal, &prev_thresh);
+
+       /* set new values */
+       for_each_cpu(cpu, cpus) {
+               portal = qman_get_affine_portal(cpu);
+               res = qman_portal_set_iperiod(portal, period);
+               if (res)
+                       goto revert_values;
+               res = qman_dqrr_set_ithresh(portal, thresh);
+               if (res) {
+                       qman_portal_set_iperiod(portal, prev_period);
+                       goto revert_values;
+               }
+               needs_revert[cpu] = true;
+       }
+
+       return 0;
+
+revert_values:
+       /* restore previous values */
+       for_each_cpu(cpu, cpus) {
+               if (!needs_revert[cpu])
+                       continue;
+               portal = qman_get_affine_portal(cpu);
+               /* previous values will not fail, ignore return value */
+               qman_portal_set_iperiod(portal, prev_period);
+               qman_dqrr_set_ithresh(portal, prev_thresh);
+       }
+
+       return res;
+}
+
 const struct ethtool_ops dpaa_ethtool_ops = {
        .get_drvinfo = dpaa_get_drvinfo,
        .get_msglevel = dpaa_get_msglevel,
@@ -545,4 +614,6 @@ const struct ethtool_ops dpaa_ethtool_ops = {
        .get_rxnfc = dpaa_get_rxnfc,
        .set_rxnfc = dpaa_set_rxnfc,
        .get_ts_info = dpaa_get_ts_info,
+       .get_coalesce = dpaa_get_coalesce,
+       .set_coalesce = dpaa_set_coalesce,
 };
index bdfb13b..1ca9a18 100644 (file)
@@ -13,7 +13,8 @@
 #include <linux/iommu.h>
 #include <linux/net_tstamp.h>
 #include <linux/fsl/mc.h>
-
+#include <linux/bpf.h>
+#include <linux/bpf_trace.h>
 #include <net/sock.h>
 
 #include "dpaa2-eth.h"
@@ -86,7 +87,7 @@ static void free_rx_fd(struct dpaa2_eth_priv *priv,
                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_FROM_DEVICE);
+                                DMA_BIDIRECTIONAL);
 
                skb_free_frag(sg_vaddr);
                if (dpaa2_sg_is_final(&sgt[i]))
@@ -144,7 +145,7 @@ static struct sk_buff *build_frag_skb(struct dpaa2_eth_priv *priv,
                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_FROM_DEVICE);
+                                DMA_BIDIRECTIONAL);
 
                sg_length = dpaa2_sg_get_len(sge);
 
@@ -199,12 +200,148 @@ static struct sk_buff *build_frag_skb(struct dpaa2_eth_priv *priv,
        return skb;
 }
 
+/* Free buffers acquired from the buffer pool or which were meant to
+ * be released in the pool
+ */
+static void free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, int count)
+{
+       struct device *dev = priv->net_dev->dev.parent;
+       void *vaddr;
+       int i;
+
+       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);
+       }
+}
+
+static void xdp_release_buf(struct dpaa2_eth_priv *priv,
+                           struct dpaa2_eth_channel *ch,
+                           dma_addr_t addr)
+{
+       int err;
+
+       ch->xdp.drop_bufs[ch->xdp.drop_cnt++] = addr;
+       if (ch->xdp.drop_cnt < DPAA2_ETH_BUFS_PER_CMD)
+               return;
+
+       while ((err = dpaa2_io_service_release(ch->dpio, priv->bpid,
+                                              ch->xdp.drop_bufs,
+                                              ch->xdp.drop_cnt)) == -EBUSY)
+               cpu_relax();
+
+       if (err) {
+               free_bufs(priv, ch->xdp.drop_bufs, ch->xdp.drop_cnt);
+               ch->buf_count -= ch->xdp.drop_cnt;
+       }
+
+       ch->xdp.drop_cnt = 0;
+}
+
+static int xdp_enqueue(struct dpaa2_eth_priv *priv, struct dpaa2_fd *fd,
+                      void *buf_start, u16 queue_id)
+{
+       struct dpaa2_eth_fq *fq;
+       struct dpaa2_faead *faead;
+       u32 ctrl, frc;
+       int i, err;
+
+       /* Mark the egress frame hardware annotation area as valid */
+       frc = dpaa2_fd_get_frc(fd);
+       dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV);
+       dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_ASAL);
+
+       /* Instruct hardware to release the FD buffer directly into
+        * the buffer pool once transmission is completed, instead of
+        * sending a Tx confirmation frame to us
+        */
+       ctrl = DPAA2_FAEAD_A4V | DPAA2_FAEAD_A2V | DPAA2_FAEAD_EBDDV;
+       faead = dpaa2_get_faead(buf_start, false);
+       faead->ctrl = cpu_to_le32(ctrl);
+       faead->conf_fqid = 0;
+
+       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);
+               if (err != -EBUSY)
+                       break;
+       }
+
+       return err;
+}
+
+static u32 run_xdp(struct dpaa2_eth_priv *priv,
+                  struct dpaa2_eth_channel *ch,
+                  struct dpaa2_eth_fq *rx_fq,
+                  struct dpaa2_fd *fd, void *vaddr)
+{
+       dma_addr_t addr = dpaa2_fd_get_addr(fd);
+       struct rtnl_link_stats64 *percpu_stats;
+       struct bpf_prog *xdp_prog;
+       struct xdp_buff xdp;
+       u32 xdp_act = XDP_PASS;
+       int err;
+
+       percpu_stats = this_cpu_ptr(priv->percpu_stats);
+
+       rcu_read_lock();
+
+       xdp_prog = READ_ONCE(ch->xdp.prog);
+       if (!xdp_prog)
+               goto out;
+
+       xdp.data = vaddr + dpaa2_fd_get_offset(fd);
+       xdp.data_end = xdp.data + dpaa2_fd_get_len(fd);
+       xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM;
+       xdp_set_data_meta_invalid(&xdp);
+
+       xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp);
+
+       /* xdp.data pointer may have changed */
+       dpaa2_fd_set_offset(fd, xdp.data - vaddr);
+       dpaa2_fd_set_len(fd, xdp.data_end - xdp.data);
+
+       switch (xdp_act) {
+       case XDP_PASS:
+               break;
+       case XDP_TX:
+               err = xdp_enqueue(priv, fd, vaddr, rx_fq->flowid);
+               if (err) {
+                       xdp_release_buf(priv, ch, addr);
+                       percpu_stats->tx_errors++;
+                       ch->stats.xdp_tx_err++;
+               } else {
+                       percpu_stats->tx_packets++;
+                       percpu_stats->tx_bytes += dpaa2_fd_get_len(fd);
+                       ch->stats.xdp_tx++;
+               }
+               break;
+       default:
+               bpf_warn_invalid_xdp_action(xdp_act);
+               /* fall through */
+       case XDP_ABORTED:
+               trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act);
+               /* fall through */
+       case XDP_DROP:
+               xdp_release_buf(priv, ch, addr);
+               ch->stats.xdp_drop++;
+               break;
+       }
+
+out:
+       rcu_read_unlock();
+       return xdp_act;
+}
+
 /* Main Rx frame processing routine */
 static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
                         struct dpaa2_eth_channel *ch,
                         const struct dpaa2_fd *fd,
-                        struct napi_struct *napi,
-                        u16 queue_id)
+                        struct dpaa2_eth_fq *fq)
 {
        dma_addr_t addr = dpaa2_fd_get_addr(fd);
        u8 fd_format = dpaa2_fd_get_format(fd);
@@ -216,12 +353,14 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
        struct dpaa2_fas *fas;
        void *buf_data;
        u32 status = 0;
+       u32 xdp_act;
 
        /* Tracing point */
        trace_dpaa2_rx_fd(priv->net_dev, fd);
 
        vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr);
-       dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, DMA_FROM_DEVICE);
+       dma_sync_single_for_cpu(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
+                               DMA_BIDIRECTIONAL);
 
        fas = dpaa2_get_fas(vaddr, false);
        prefetch(fas);
@@ -232,8 +371,21 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
        percpu_extras = this_cpu_ptr(priv->percpu_extras);
 
        if (fd_format == dpaa2_fd_single) {
+               xdp_act = run_xdp(priv, ch, fq, (struct dpaa2_fd *)fd, vaddr);
+               if (xdp_act != XDP_PASS) {
+                       percpu_stats->rx_packets++;
+                       percpu_stats->rx_bytes += dpaa2_fd_get_len(fd);
+                       return;
+               }
+
+               dma_unmap_single(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);
                skb = build_frag_skb(priv, ch, buf_data);
                skb_free_frag(vaddr);
                percpu_extras->rx_sg_frames++;
@@ -267,12 +419,12 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
        }
 
        skb->protocol = eth_type_trans(skb, priv->net_dev);
-       skb_record_rx_queue(skb, queue_id);
+       skb_record_rx_queue(skb, fq->flowid);
 
        percpu_stats->rx_packets++;
        percpu_stats->rx_bytes += dpaa2_fd_get_len(fd);
 
-       napi_gro_receive(napi, skb);
+       napi_gro_receive(&ch->napi, skb);
 
        return;
 
@@ -289,7 +441,7 @@ err_frame_format:
  * Observance of NAPI budget is not our concern, leaving that to the caller.
  */
 static int consume_frames(struct dpaa2_eth_channel *ch,
-                         enum dpaa2_eth_fq_type *type)
+                         struct dpaa2_eth_fq **src)
 {
        struct dpaa2_eth_priv *priv = ch->priv;
        struct dpaa2_eth_fq *fq = NULL;
@@ -312,7 +464,7 @@ static int consume_frames(struct dpaa2_eth_channel *ch,
                fd = dpaa2_dq_fd(dq);
                fq = (struct dpaa2_eth_fq *)(uintptr_t)dpaa2_dq_fqd_ctx(dq);
 
-               fq->consume(priv, ch, fd, &ch->napi, fq->flowid);
+               fq->consume(priv, ch, fd, fq);
                cleaned++;
        } while (!is_last);
 
@@ -320,13 +472,12 @@ static int consume_frames(struct dpaa2_eth_channel *ch,
                return 0;
 
        fq->stats.frames += cleaned;
-       ch->stats.frames += cleaned;
 
        /* A dequeue operation only pulls frames from a single queue
-        * into the store. Return the frame queue type as an out param.
+        * into the store. Return the frame queue as an out param.
         */
-       if (type)
-               *type = fq->type;
+       if (src)
+               *src = fq;
 
        return cleaned;
 }
@@ -571,8 +722,10 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
        struct rtnl_link_stats64 *percpu_stats;
        struct dpaa2_eth_drv_stats *percpu_extras;
        struct dpaa2_eth_fq *fq;
+       struct netdev_queue *nq;
        u16 queue_mapping;
        unsigned int needed_headroom;
+       u32 fd_len;
        int err, i;
 
        percpu_stats = this_cpu_ptr(priv->percpu_stats);
@@ -644,8 +797,12 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
                /* Clean up everything, including freeing the skb */
                free_tx_fd(priv, &fd);
        } else {
+               fd_len = dpaa2_fd_get_len(&fd);
                percpu_stats->tx_packets++;
-               percpu_stats->tx_bytes += dpaa2_fd_get_len(&fd);
+               percpu_stats->tx_bytes += fd_len;
+
+               nq = netdev_get_tx_queue(net_dev, queue_mapping);
+               netdev_tx_sent_queue(nq, fd_len);
        }
 
        return NETDEV_TX_OK;
@@ -661,11 +818,11 @@ err_alloc_headroom:
 static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv,
                              struct dpaa2_eth_channel *ch __always_unused,
                              const struct dpaa2_fd *fd,
-                             struct napi_struct *napi __always_unused,
-                             u16 queue_id __always_unused)
+                             struct dpaa2_eth_fq *fq)
 {
        struct rtnl_link_stats64 *percpu_stats;
        struct dpaa2_eth_drv_stats *percpu_extras;
+       u32 fd_len = dpaa2_fd_get_len(fd);
        u32 fd_errors;
 
        /* Tracing point */
@@ -673,7 +830,10 @@ static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv,
 
        percpu_extras = this_cpu_ptr(priv->percpu_extras);
        percpu_extras->tx_conf_frames++;
-       percpu_extras->tx_conf_bytes += dpaa2_fd_get_len(fd);
+       percpu_extras->tx_conf_bytes += fd_len;
+
+       fq->dq_frames++;
+       fq->dq_bytes += fd_len;
 
        /* Check frame errors in the FD field */
        fd_errors = dpaa2_fd_get_ctrl(fd) & DPAA2_FD_TX_ERR_MASK;
@@ -735,23 +895,6 @@ static int set_tx_csum(struct dpaa2_eth_priv *priv, bool enable)
        return 0;
 }
 
-/* Free buffers acquired from the buffer pool or which were meant to
- * be released in the pool
- */
-static void free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, int count)
-{
-       struct device *dev = priv->net_dev->dev.parent;
-       void *vaddr;
-       int i;
-
-       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_FROM_DEVICE);
-               skb_free_frag(vaddr);
-       }
-}
-
 /* Perform a single release command to add buffers
  * to the specified buffer pool
  */
@@ -775,7 +918,7 @@ static int add_bufs(struct dpaa2_eth_priv *priv,
                buf = PTR_ALIGN(buf, priv->rx_buf_align);
 
                addr = dma_map_single(dev, buf, DPAA2_ETH_RX_BUF_SIZE,
-                                     DMA_FROM_DEVICE);
+                                     DMA_BIDIRECTIONAL);
                if (unlikely(dma_mapping_error(dev, addr)))
                        goto err_map;
 
@@ -934,8 +1077,9 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget)
        struct dpaa2_eth_channel *ch;
        struct dpaa2_eth_priv *priv;
        int rx_cleaned = 0, txconf_cleaned = 0;
-       enum dpaa2_eth_fq_type type = 0;
-       int store_cleaned;
+       struct dpaa2_eth_fq *fq, *txc_fq = NULL;
+       struct netdev_queue *nq;
+       int store_cleaned, work_done;
        int err;
 
        ch = container_of(napi, struct dpaa2_eth_channel, napi);
@@ -949,18 +1093,25 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget)
                /* Refill pool if appropriate */
                refill_pool(priv, ch, priv->bpid);
 
-               store_cleaned = consume_frames(ch, &type);
-               if (type == DPAA2_RX_FQ)
+               store_cleaned = consume_frames(ch, &fq);
+               if (!store_cleaned)
+                       break;
+               if (fq->type == DPAA2_RX_FQ) {
                        rx_cleaned += store_cleaned;
-               else
+               } else {
                        txconf_cleaned += store_cleaned;
+                       /* We have a single Tx conf FQ on this channel */
+                       txc_fq = fq;
+               }
 
                /* If we either consumed the whole NAPI budget with Rx frames
                 * or we reached the Tx confirmations threshold, we're done.
                 */
                if (rx_cleaned >= budget ||
-                   txconf_cleaned >= DPAA2_ETH_TXCONF_PER_NAPI)
-                       return budget;
+                   txconf_cleaned >= DPAA2_ETH_TXCONF_PER_NAPI) {
+                       work_done = budget;
+                       goto out;
+               }
        } while (store_cleaned);
 
        /* We didn't consume the entire budget, so finish napi and
@@ -974,7 +1125,18 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget)
        WARN_ONCE(err, "CDAN notifications rearm failed on core %d",
                  ch->nctx.desired_cpu);
 
-       return max(rx_cleaned, 1);
+       work_done = max(rx_cleaned, 1);
+
+out:
+       if (txc_fq) {
+               nq = netdev_get_tx_queue(priv->net_dev, txc_fq->flowid);
+               netdev_tx_completed_queue(nq, txc_fq->dq_frames,
+                                         txc_fq->dq_bytes);
+               txc_fq->dq_frames = 0;
+               txc_fq->dq_bytes = 0;
+       }
+
+       return work_done;
 }
 
 static void enable_ch_napi(struct dpaa2_eth_priv *priv)
@@ -1400,6 +1562,174 @@ static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
        return -EINVAL;
 }
 
+static bool xdp_mtu_valid(struct dpaa2_eth_priv *priv, int mtu)
+{
+       int mfl, linear_mfl;
+
+       mfl = DPAA2_ETH_L2_MAX_FRM(mtu);
+       linear_mfl = DPAA2_ETH_RX_BUF_SIZE - DPAA2_ETH_RX_HWA_SIZE -
+                    dpaa2_eth_rx_head_room(priv) - XDP_PACKET_HEADROOM;
+
+       if (mfl > linear_mfl) {
+               netdev_warn(priv->net_dev, "Maximum MTU for XDP is %d\n",
+                           linear_mfl - VLAN_ETH_HLEN);
+               return false;
+       }
+
+       return true;
+}
+
+static int set_rx_mfl(struct dpaa2_eth_priv *priv, int mtu, bool has_xdp)
+{
+       int mfl, err;
+
+       /* We enforce a maximum Rx frame length based on MTU only if we have
+        * an XDP program attached (in order to avoid Rx S/G frames).
+        * Otherwise, we accept all incoming frames as long as they are not
+        * larger than maximum size supported in hardware
+        */
+       if (has_xdp)
+               mfl = DPAA2_ETH_L2_MAX_FRM(mtu);
+       else
+               mfl = DPAA2_ETH_MFL;
+
+       err = dpni_set_max_frame_length(priv->mc_io, 0, priv->mc_token, mfl);
+       if (err) {
+               netdev_err(priv->net_dev, "dpni_set_max_frame_length failed\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static int dpaa2_eth_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct dpaa2_eth_priv *priv = netdev_priv(dev);
+       int err;
+
+       if (!priv->xdp_prog)
+               goto out;
+
+       if (!xdp_mtu_valid(priv, new_mtu))
+               return -EINVAL;
+
+       err = set_rx_mfl(priv, new_mtu, true);
+       if (err)
+               return err;
+
+out:
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+static int update_rx_buffer_headroom(struct dpaa2_eth_priv *priv, bool has_xdp)
+{
+       struct dpni_buffer_layout buf_layout = {0};
+       int err;
+
+       err = dpni_get_buffer_layout(priv->mc_io, 0, priv->mc_token,
+                                    DPNI_QUEUE_RX, &buf_layout);
+       if (err) {
+               netdev_err(priv->net_dev, "dpni_get_buffer_layout failed\n");
+               return err;
+       }
+
+       /* Reserve extra headroom for XDP header size changes */
+       buf_layout.data_head_room = dpaa2_eth_rx_head_room(priv) +
+                                   (has_xdp ? XDP_PACKET_HEADROOM : 0);
+       buf_layout.options = DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM;
+       err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token,
+                                    DPNI_QUEUE_RX, &buf_layout);
+       if (err) {
+               netdev_err(priv->net_dev, "dpni_set_buffer_layout failed\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static int setup_xdp(struct net_device *dev, struct bpf_prog *prog)
+{
+       struct dpaa2_eth_priv *priv = netdev_priv(dev);
+       struct dpaa2_eth_channel *ch;
+       struct bpf_prog *old;
+       bool up, need_update;
+       int i, err;
+
+       if (prog && !xdp_mtu_valid(priv, dev->mtu))
+               return -EINVAL;
+
+       if (prog) {
+               prog = bpf_prog_add(prog, priv->num_channels);
+               if (IS_ERR(prog))
+                       return PTR_ERR(prog);
+       }
+
+       up = netif_running(dev);
+       need_update = (!!priv->xdp_prog != !!prog);
+
+       if (up)
+               dpaa2_eth_stop(dev);
+
+       /* While in xdp mode, enforce a maximum Rx frame size based on MTU.
+        * Also, when switching between xdp/non-xdp modes we need to reconfigure
+        * our Rx buffer layout. Buffer pool was drained on dpaa2_eth_stop,
+        * so we are sure no old format buffers will be used from now on.
+        */
+       if (need_update) {
+               err = set_rx_mfl(priv, dev->mtu, !!prog);
+               if (err)
+                       goto out_err;
+               err = update_rx_buffer_headroom(priv, !!prog);
+               if (err)
+                       goto out_err;
+       }
+
+       old = xchg(&priv->xdp_prog, prog);
+       if (old)
+               bpf_prog_put(old);
+
+       for (i = 0; i < priv->num_channels; i++) {
+               ch = priv->channel[i];
+               old = xchg(&ch->xdp.prog, prog);
+               if (old)
+                       bpf_prog_put(old);
+       }
+
+       if (up) {
+               err = dpaa2_eth_open(dev);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+
+out_err:
+       if (prog)
+               bpf_prog_sub(prog, priv->num_channels);
+       if (up)
+               dpaa2_eth_open(dev);
+
+       return err;
+}
+
+static int dpaa2_eth_xdp(struct net_device *dev, struct netdev_bpf *xdp)
+{
+       struct dpaa2_eth_priv *priv = netdev_priv(dev);
+
+       switch (xdp->command) {
+       case XDP_SETUP_PROG:
+               return setup_xdp(dev, xdp->prog);
+       case XDP_QUERY_PROG:
+               xdp->prog_id = priv->xdp_prog ? priv->xdp_prog->aux->id : 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static const struct net_device_ops dpaa2_eth_ops = {
        .ndo_open = dpaa2_eth_open,
        .ndo_start_xmit = dpaa2_eth_tx,
@@ -1409,6 +1739,8 @@ static const struct net_device_ops dpaa2_eth_ops = {
        .ndo_set_rx_mode = dpaa2_eth_set_rx_mode,
        .ndo_set_features = dpaa2_eth_set_features,
        .ndo_do_ioctl = dpaa2_eth_ioctl,
+       .ndo_change_mtu = dpaa2_eth_change_mtu,
+       .ndo_bpf = dpaa2_eth_xdp,
 };
 
 static void cdan_cb(struct dpaa2_io_notification_ctx *ctx)
@@ -1603,7 +1935,7 @@ static int setup_dpio(struct dpaa2_eth_priv *priv)
                /* Stop if we already have enough channels to accommodate all
                 * RX and TX conf queues
                 */
-               if (priv->num_channels == dpaa2_eth_queue_count(priv))
+               if (priv->num_channels == priv->dpni_attrs.num_queues)
                        break;
        }
 
index 452a8e9..69c965d 100644 (file)
@@ -139,7 +139,9 @@ struct dpaa2_faead {
 };
 
 #define DPAA2_FAEAD_A2V                        0x20000000
+#define DPAA2_FAEAD_A4V                        0x08000000
 #define DPAA2_FAEAD_UPDV               0x00001000
+#define DPAA2_FAEAD_EBDDV              0x00002000
 #define DPAA2_FAEAD_UPD                        0x00000010
 
 /* Accessors for the hardware annotation fields that we use */
@@ -243,12 +245,14 @@ struct dpaa2_eth_fq_stats {
 struct dpaa2_eth_ch_stats {
        /* Volatile dequeues retried due to portal busy */
        __u64 dequeue_portal_busy;
-       /* Number of CDANs; useful to estimate avg NAPI len */
-       __u64 cdan;
-       /* Number of frames received on queues from this channel */
-       __u64 frames;
        /* Pull errors */
        __u64 pull_err;
+       /* Number of CDANs; useful to estimate avg NAPI len */
+       __u64 cdan;
+       /* XDP counters */
+       __u64 xdp_drop;
+       __u64 xdp_tx;
+       __u64 xdp_tx_err;
 };
 
 /* Maximum number of queues associated with a DPNI */
@@ -271,17 +275,24 @@ struct dpaa2_eth_fq {
        u32 tx_qdbin;
        u16 flowid;
        int target_cpu;
+       u32 dq_frames;
+       u32 dq_bytes;
        struct dpaa2_eth_channel *channel;
        enum dpaa2_eth_fq_type type;
 
        void (*consume)(struct dpaa2_eth_priv *priv,
                        struct dpaa2_eth_channel *ch,
                        const struct dpaa2_fd *fd,
-                       struct napi_struct *napi,
-                       u16 queue_id);
+                       struct dpaa2_eth_fq *fq);
        struct dpaa2_eth_fq_stats stats;
 };
 
+struct dpaa2_eth_ch_xdp {
+       struct bpf_prog *prog;
+       u64 drop_bufs[DPAA2_ETH_BUFS_PER_CMD];
+       int drop_cnt;
+};
+
 struct dpaa2_eth_channel {
        struct dpaa2_io_notification_ctx nctx;
        struct fsl_mc_device *dpcon;
@@ -293,6 +304,7 @@ struct dpaa2_eth_channel {
        struct dpaa2_eth_priv *priv;
        int buf_count;
        struct dpaa2_eth_ch_stats stats;
+       struct dpaa2_eth_ch_xdp xdp;
 };
 
 struct dpaa2_eth_dist_fields {
@@ -352,6 +364,7 @@ struct dpaa2_eth_priv {
        u64 rx_hash_fields;
        struct dpaa2_eth_cls_rule *cls_rules;
        u8 rx_cls_enabled;
+       struct bpf_prog *xdp_prog;
 };
 
 #define DPAA2_RXH_SUPPORTED    (RXH_L2DA | RXH_VLAN | RXH_L3_PROTO \
@@ -434,9 +447,10 @@ static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv)
               DPAA2_ETH_RX_HWA_SIZE;
 }
 
+/* We have exactly one {Rx, Tx conf} queue per channel */
 static int dpaa2_eth_queue_count(struct dpaa2_eth_priv *priv)
 {
-       return priv->dpni_attrs.num_queues;
+       return priv->num_channels;
 }
 
 int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags);
index 26bd5a2..a7389e7 100644 (file)
@@ -45,6 +45,15 @@ static char dpaa2_ethtool_extras[][ETH_GSTRING_LEN] = {
        "[drv] dequeue portal busy",
        "[drv] channel pull errors",
        "[drv] cdan",
+       "[drv] xdp drop",
+       "[drv] xdp tx",
+       "[drv] xdp tx errors",
+       /* FQ stats */
+       "[qbman] rx pending frames",
+       "[qbman] rx pending bytes",
+       "[qbman] tx conf pending frames",
+       "[qbman] tx conf pending bytes",
+       "[qbman] buffer count",
 };
 
 #define DPAA2_ETH_NUM_EXTRA_STATS      ARRAY_SIZE(dpaa2_ethtool_extras)
@@ -174,8 +183,10 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
        int j, k, err;
        int num_cnt;
        union dpni_statistics dpni_stats;
-       u64 cdan = 0;
-       u64 portal_busy = 0, pull_err = 0;
+       u32 fcnt, bcnt;
+       u32 fcnt_rx_total = 0, fcnt_tx_total = 0;
+       u32 bcnt_rx_total = 0, bcnt_tx_total = 0;
+       u32 buf_cnt;
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
        struct dpaa2_eth_drv_stats *extras;
        struct dpaa2_eth_ch_stats *ch_stats;
@@ -212,16 +223,43 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
        }
        i += j;
 
-       for (j = 0; j < priv->num_channels; j++) {
-               ch_stats = &priv->channel[j]->stats;
-               cdan += ch_stats->cdan;
-               portal_busy += ch_stats->dequeue_portal_busy;
-               pull_err += ch_stats->pull_err;
+       /* Per-channel stats */
+       for (k = 0; k < priv->num_channels; k++) {
+               ch_stats = &priv->channel[k]->stats;
+               for (j = 0; j < sizeof(*ch_stats) / sizeof(__u64); j++)
+                       *((__u64 *)data + i + j) += *((__u64 *)ch_stats + j);
        }
+       i += j;
+
+       for (j = 0; j < priv->num_fqs; j++) {
+               /* Print FQ instantaneous counts */
+               err = dpaa2_io_query_fq_count(NULL, priv->fq[j].fqid,
+                                             &fcnt, &bcnt);
+               if (err) {
+                       netdev_warn(net_dev, "FQ query error %d", err);
+                       return;
+               }
 
-       *(data + i++) = portal_busy;
-       *(data + i++) = pull_err;
-       *(data + i++) = cdan;
+               if (priv->fq[j].type == DPAA2_TX_CONF_FQ) {
+                       fcnt_tx_total += fcnt;
+                       bcnt_tx_total += bcnt;
+               } else {
+                       fcnt_rx_total += fcnt;
+                       bcnt_rx_total += bcnt;
+               }
+       }
+
+       *(data + i++) = fcnt_rx_total;
+       *(data + i++) = bcnt_rx_total;
+       *(data + i++) = fcnt_tx_total;
+       *(data + i++) = bcnt_tx_total;
+
+       err = dpaa2_io_query_bp_count(NULL, priv->bpid, &buf_cnt);
+       if (err) {
+               netdev_warn(net_dev, "Buffer count query error %d\n", err);
+               return;
+       }
+       *(data + i++) = buf_cnt;
 }
 
 static int prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask,
index bf80855..f79e57f 100644 (file)
@@ -531,7 +531,6 @@ struct fec_enet_private {
 
        /* Phylib and MDIO interface */
        struct  mii_bus *mii_bus;
-       int     mii_timeout;
        uint    phy_speed;
        phy_interface_t phy_interface;
        struct device_node *phy_node;
index 6db69ba..ae0f88b 100644 (file)
@@ -1714,12 +1714,6 @@ static void fec_enet_adjust_link(struct net_device *ndev)
        struct phy_device *phy_dev = ndev->phydev;
        int status_change = 0;
 
-       /* Prevent a state halted on mii error */
-       if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
-               phy_dev->state = PHY_RESUMING;
-               return;
-       }
-
        /*
         * If the netdev is down, or is going down, we're not interested
         * in link state events, so just mark our idea of the link as down
@@ -1779,7 +1773,6 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
        if (ret < 0)
                return ret;
 
-       fep->mii_timeout = 0;
        reinit_completion(&fep->mdio_done);
 
        /* start a read op */
@@ -1791,7 +1784,6 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
        time_left = wait_for_completion_timeout(&fep->mdio_done,
                        usecs_to_jiffies(FEC_MII_TIMEOUT));
        if (time_left == 0) {
-               fep->mii_timeout = 1;
                netdev_err(fep->netdev, "MDIO read timeout\n");
                ret = -ETIMEDOUT;
                goto out;
@@ -1820,7 +1812,6 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
        else
                ret = 0;
 
-       fep->mii_timeout = 0;
        reinit_completion(&fep->mdio_done);
 
        /* start a write op */
@@ -1833,7 +1824,6 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
        time_left = wait_for_completion_timeout(&fep->mdio_done,
                        usecs_to_jiffies(FEC_MII_TIMEOUT));
        if (time_left == 0) {
-               fep->mii_timeout = 1;
                netdev_err(fep->netdev, "MDIO write timeout\n");
                ret  = -ETIMEDOUT;
        }
@@ -2001,8 +1991,6 @@ static int fec_enet_mii_init(struct platform_device *pdev)
                return -ENOENT;
        }
 
-       fep->mii_timeout = 0;
-
        /*
         * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
         *
index c415ac6..e80fedb 100644 (file)
@@ -2786,7 +2786,7 @@ static struct fman *read_dts_node(struct platform_device *of_dev)
        if (!muram_node) {
                dev_err(&of_dev->dev, "%s: could not find MURAM node\n",
                        __func__);
-               goto fman_node_put;
+               goto fman_free;
        }
 
        err = of_address_to_resource(muram_node, 0,
@@ -2795,11 +2795,10 @@ static struct fman *read_dts_node(struct platform_device *of_dev)
                of_node_put(muram_node);
                dev_err(&of_dev->dev, "%s: of_address_to_resource() = %d\n",
                        __func__, err);
-               goto fman_node_put;
+               goto fman_free;
        }
 
        of_node_put(muram_node);
-       of_node_put(fm_node);
 
        err = devm_request_irq(&of_dev->dev, irq, fman_irq, IRQF_SHARED,
                               "fman", fman);
index 82722d0..88a396f 100644 (file)
@@ -473,7 +473,7 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev)
 
        if (data->get_tbipa) {
                for_each_child_of_node(np, tbi) {
-                       if (strcmp(tbi->type, "tbi-phy") == 0) {
+                       if (of_node_is_type(tbi, "tbi-phy")) {
                                dev_dbg(&pdev->dev, "found TBI PHY node %pOFP\n",
                                        tbi);
                                break;
index 0e102c7..45fcc96 100644 (file)
@@ -500,6 +500,7 @@ static const struct net_device_ops gfar_netdev_ops = {
        .ndo_tx_timeout = gfar_timeout,
        .ndo_do_ioctl = gfar_ioctl,
        .ndo_get_stats = gfar_get_stats,
+       .ndo_change_carrier = fixed_phy_change_carrier,
        .ndo_set_mac_address = gfar_set_mac_addr,
        .ndo_validate_addr = eth_validate_addr,
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -720,7 +721,7 @@ static int gfar_of_group_count(struct device_node *np)
        int num = 0;
 
        for_each_available_child_of_node(np, child)
-               if (!of_node_cmp(child->name, "queue-group"))
+               if (of_node_name_eq(child, "queue-group"))
                        num++;
 
        return num;
@@ -838,7 +839,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
        /* Parse and initialize group specific information */
        if (priv->mode == MQ_MG_MODE) {
                for_each_available_child_of_node(np, child) {
-                       if (of_node_cmp(child->name, "queue-group"))
+                       if (!of_node_name_eq(child, "queue-group"))
                                continue;
 
                        err = gfar_parse_group(child, priv, model);
index 2e978cb..c3d539e 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/mii.h>
 #include <linux/phy.h>
+#include <linux/phy_fixed.h>
 #include <linux/workqueue.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
@@ -3676,6 +3677,7 @@ static const struct net_device_ops ucc_geth_netdev_ops = {
        .ndo_stop               = ucc_geth_close,
        .ndo_start_xmit         = ucc_geth_start_xmit,
        .ndo_validate_addr      = eth_validate_addr,
+       .ndo_change_carrier     = fixed_phy_change_carrier,
        .ndo_set_mac_address    = ucc_geth_set_mac_addr,
        .ndo_set_rx_mode        = ucc_geth_set_multi,
        .ndo_tx_timeout         = ucc_geth_timeout,
index 2515271..fee4664 100644 (file)
@@ -118,6 +118,7 @@ config HNS3_ENET
        tristate "Hisilicon HNS3 Ethernet Device Support"
        default m
        depends on 64BIT && PCI
+       depends on INET
        ---help---
          This selects the Ethernet Driver for Hisilicon Network Subsystem 3 for hip08
          family of SoCs. This module depends upon HNAE3 driver to access the HNAE3
index be268dc..f9a4e76 100644 (file)
@@ -915,10 +915,8 @@ static int hip04_mac_probe(struct platform_device *pdev)
        }
 
        ret = register_netdev(ndev);
-       if (ret) {
-               free_netdev(ndev);
+       if (ret)
                goto alloc_fail;
-       }
 
        return 0;
 
index b52029e..ad1779f 100644 (file)
@@ -379,6 +379,9 @@ static void hns_ae_stop(struct hnae_handle *handle)
 
        hns_ae_ring_enable_all(handle, 0);
 
+       /* clean rx fbd. */
+       hns_rcb_wait_fbd_clean(handle->qs, handle->q_num, RCB_INT_FLAG_RX);
+
        (void)hns_mac_vm_config_bc_en(mac_cb, 0, false);
 }
 
index aaf72c0..1790cda 100644 (file)
@@ -67,11 +67,14 @@ static void hns_gmac_enable(void *mac_drv, enum mac_commom_mode mode)
        struct mac_driver *drv = (struct mac_driver *)mac_drv;
 
        /*enable GE rX/tX */
-       if ((mode == MAC_COMM_MODE_TX) || (mode == MAC_COMM_MODE_RX_AND_TX))
+       if (mode == MAC_COMM_MODE_TX || mode == MAC_COMM_MODE_RX_AND_TX)
                dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_TX_EN_B, 1);
 
-       if ((mode == MAC_COMM_MODE_RX) || (mode == MAC_COMM_MODE_RX_AND_TX))
+       if (mode == MAC_COMM_MODE_RX || mode == MAC_COMM_MODE_RX_AND_TX) {
+               /* enable rx pcs */
+               dsaf_set_dev_bit(drv, GMAC_PCS_RX_EN_REG, 0, 0);
                dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 1);
+       }
 }
 
 static void hns_gmac_disable(void *mac_drv, enum mac_commom_mode mode)
@@ -79,11 +82,14 @@ static void hns_gmac_disable(void *mac_drv, enum mac_commom_mode mode)
        struct mac_driver *drv = (struct mac_driver *)mac_drv;
 
        /*disable GE rX/tX */
-       if ((mode == MAC_COMM_MODE_TX) || (mode == MAC_COMM_MODE_RX_AND_TX))
+       if (mode == MAC_COMM_MODE_TX || mode == MAC_COMM_MODE_RX_AND_TX)
                dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_TX_EN_B, 0);
 
-       if ((mode == MAC_COMM_MODE_RX) || (mode == MAC_COMM_MODE_RX_AND_TX))
+       if (mode == MAC_COMM_MODE_RX || mode == MAC_COMM_MODE_RX_AND_TX) {
+               /* disable rx pcs */
+               dsaf_set_dev_bit(drv, GMAC_PCS_RX_EN_REG, 0, 1);
                dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 0);
+       }
 }
 
 /* hns_gmac_get_en - get port enable
index 3613e40..a97228c 100644 (file)
@@ -778,6 +778,17 @@ static int hns_mac_register_phy(struct hns_mac_cb *mac_cb)
        return rc;
 }
 
+static void hns_mac_remove_phydev(struct hns_mac_cb *mac_cb)
+{
+       if (!to_acpi_device_node(mac_cb->fw_port) || !mac_cb->phy_dev)
+               return;
+
+       phy_device_remove(mac_cb->phy_dev);
+       phy_device_free(mac_cb->phy_dev);
+
+       mac_cb->phy_dev = NULL;
+}
+
 #define MAC_MEDIA_TYPE_MAX_LEN         16
 
 static const struct {
@@ -1117,7 +1128,11 @@ void hns_mac_uninit(struct dsaf_device *dsaf_dev)
        int max_port_num = hns_mac_get_max_port_num(dsaf_dev);
 
        for (i = 0; i < max_port_num; i++) {
+               if (!dsaf_dev->mac_cb[i])
+                       continue;
+
                dsaf_dev->misc_op->cpld_reset_led(dsaf_dev->mac_cb[i]);
+               hns_mac_remove_phydev(dsaf_dev->mac_cb[i]);
                dsaf_dev->mac_cb[i] = NULL;
        }
 }
index e557a4e..3b9e74b 100644 (file)
@@ -934,6 +934,62 @@ static void hns_dsaf_tcam_mc_cfg(
        spin_unlock_bh(&dsaf_dev->tcam_lock);
 }
 
+/**
+ * hns_dsaf_tcam_uc_cfg_vague - INT
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @address,
+ * @ptbl_tcam_data,
+ */
+static void hns_dsaf_tcam_uc_cfg_vague(struct dsaf_device *dsaf_dev,
+                                      u32 address,
+                                      struct dsaf_tbl_tcam_data *tcam_data,
+                                      struct dsaf_tbl_tcam_data *tcam_mask,
+                                      struct dsaf_tbl_tcam_ucast_cfg *tcam_uc)
+{
+       spin_lock_bh(&dsaf_dev->tcam_lock);
+       hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address);
+       hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, tcam_data);
+       hns_dsaf_tbl_tcam_ucast_cfg(dsaf_dev, tcam_uc);
+       hns_dsaf_tbl_tcam_match_cfg(dsaf_dev, tcam_mask);
+       hns_dsaf_tbl_tcam_data_ucast_pul(dsaf_dev);
+
+       /*Restore Match Data*/
+       tcam_mask->tbl_tcam_data_high = 0xffffffff;
+       tcam_mask->tbl_tcam_data_low = 0xffffffff;
+       hns_dsaf_tbl_tcam_match_cfg(dsaf_dev, tcam_mask);
+
+       spin_unlock_bh(&dsaf_dev->tcam_lock);
+}
+
+/**
+ * hns_dsaf_tcam_mc_cfg_vague - INT
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @address,
+ * @ptbl_tcam_data,
+ * @ptbl_tcam_mask
+ * @ptbl_tcam_mcast
+ */
+static void hns_dsaf_tcam_mc_cfg_vague(struct dsaf_device *dsaf_dev,
+                                      u32 address,
+                                      struct dsaf_tbl_tcam_data *tcam_data,
+                                      struct dsaf_tbl_tcam_data *tcam_mask,
+                                      struct dsaf_tbl_tcam_mcast_cfg *tcam_mc)
+{
+       spin_lock_bh(&dsaf_dev->tcam_lock);
+       hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address);
+       hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, tcam_data);
+       hns_dsaf_tbl_tcam_mcast_cfg(dsaf_dev, tcam_mc);
+       hns_dsaf_tbl_tcam_match_cfg(dsaf_dev, tcam_mask);
+       hns_dsaf_tbl_tcam_data_mcast_pul(dsaf_dev);
+
+       /*Restore Match Data*/
+       tcam_mask->tbl_tcam_data_high = 0xffffffff;
+       tcam_mask->tbl_tcam_data_low = 0xffffffff;
+       hns_dsaf_tbl_tcam_match_cfg(dsaf_dev, tcam_mask);
+
+       spin_unlock_bh(&dsaf_dev->tcam_lock);
+}
+
 /**
  * hns_dsaf_tcam_mc_invld - INT
  * @dsaf_id: dsa fabric id
@@ -1492,6 +1548,27 @@ static u16 hns_dsaf_find_empty_mac_entry(struct dsaf_device *dsaf_dev)
        return DSAF_INVALID_ENTRY_IDX;
 }
 
+/**
+ * hns_dsaf_find_empty_mac_entry_reverse
+ * search dsa fabric soft empty-entry from the end
+ * @dsaf_dev: dsa fabric device struct pointer
+ */
+static u16 hns_dsaf_find_empty_mac_entry_reverse(struct dsaf_device *dsaf_dev)
+{
+       struct dsaf_drv_priv *priv = hns_dsaf_dev_priv(dsaf_dev);
+       struct dsaf_drv_soft_mac_tbl *soft_mac_entry;
+       int i;
+
+       soft_mac_entry = priv->soft_mac_tbl + (DSAF_TCAM_SUM - 1);
+       for (i = (DSAF_TCAM_SUM - 1); i > 0; i--) {
+               /* search all entry from end to start.*/
+               if (soft_mac_entry->index == DSAF_INVALID_ENTRY_IDX)
+                       return i;
+               soft_mac_entry--;
+       }
+       return DSAF_INVALID_ENTRY_IDX;
+}
+
 /**
  * hns_dsaf_set_mac_key - set mac key
  * @dsaf_dev: dsa fabric device struct pointer
@@ -2166,9 +2243,9 @@ void hns_dsaf_update_stats(struct dsaf_device *dsaf_dev, u32 node_num)
                DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG + 0x80 * (u64)node_num);
 
        hw_stats->vlan_drop += dsaf_read_dev(dsaf_dev,
-               DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + 0x80 * (u64)node_num);
+               DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + 4 * (u64)node_num);
        hw_stats->stp_drop += dsaf_read_dev(dsaf_dev,
-               DSAF_INODE_IN_DATA_STP_DISC_0_REG + 0x80 * (u64)node_num);
+               DSAF_INODE_IN_DATA_STP_DISC_0_REG + 4 * (u64)node_num);
 
        /* pfc pause frame statistics stored in dsaf inode*/
        if ((node_num < DSAF_SERVICE_NW_NUM) && !is_ver1) {
@@ -2285,237 +2362,237 @@ void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data)
                                DSAF_INODE_BD_ORDER_STATUS_0_REG + j * 4);
                p[223 + i] = dsaf_read_dev(ddev,
                                DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + j * 4);
-               p[224 + i] = dsaf_read_dev(ddev,
+               p[226 + i] = dsaf_read_dev(ddev,
                                DSAF_INODE_IN_DATA_STP_DISC_0_REG + j * 4);
        }
 
-       p[227] = dsaf_read_dev(ddev, DSAF_INODE_GE_FC_EN_0_REG + port * 4);
+       p[229] = dsaf_read_dev(ddev, DSAF_INODE_GE_FC_EN_0_REG + port * 4);
 
        for (i = 0; i < DSAF_INODE_NUM / DSAF_COMM_CHN; i++) {
                j = i * DSAF_COMM_CHN + port;
-               p[228 + i] = dsaf_read_dev(ddev,
+               p[230 + i] = dsaf_read_dev(ddev,
                                DSAF_INODE_VC0_IN_PKT_NUM_0_REG + j * 4);
        }
 
-       p[231] = dsaf_read_dev(ddev,
-               DSAF_INODE_VC1_IN_PKT_NUM_0_REG + port * 4);
+       p[233] = dsaf_read_dev(ddev,
+               DSAF_INODE_VC1_IN_PKT_NUM_0_REG + port * 0x80);
 
        /* dsaf inode registers */
        for (i = 0; i < HNS_DSAF_SBM_NUM(ddev) / DSAF_COMM_CHN; i++) {
                j = i * DSAF_COMM_CHN + port;
-               p[232 + i] = dsaf_read_dev(ddev,
+               p[234 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_CFG_REG_0_REG + j * 0x80);
-               p[235 + i] = dsaf_read_dev(ddev,
+               p[237 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + j * 0x80);
-               p[238 + i] = dsaf_read_dev(ddev,
+               p[240 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_BP_CFG_1_REG_0_REG + j * 0x80);
-               p[241 + i] = dsaf_read_dev(ddev,
+               p[243 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + j * 0x80);
-               p[244 + i] = dsaf_read_dev(ddev,
+               p[246 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_FREE_CNT_0_0_REG + j * 0x80);
-               p[245 + i] = dsaf_read_dev(ddev,
+               p[249 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_FREE_CNT_1_0_REG + j * 0x80);
-               p[248 + i] = dsaf_read_dev(ddev,
+               p[252 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_BP_CNT_0_0_REG + j * 0x80);
-               p[251 + i] = dsaf_read_dev(ddev,
+               p[255 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_BP_CNT_1_0_REG + j * 0x80);
-               p[254 + i] = dsaf_read_dev(ddev,
+               p[258 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_BP_CNT_2_0_REG + j * 0x80);
-               p[257 + i] = dsaf_read_dev(ddev,
+               p[261 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_BP_CNT_3_0_REG + j * 0x80);
-               p[260 + i] = dsaf_read_dev(ddev,
+               p[264 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_INER_ST_0_REG + j * 0x80);
-               p[263 + i] = dsaf_read_dev(ddev,
+               p[267 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_MIB_REQ_FAILED_TC_0_REG + j * 0x80);
-               p[266 + i] = dsaf_read_dev(ddev,
+               p[270 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_INPORT_CNT_0_REG + j * 0x80);
-               p[269 + i] = dsaf_read_dev(ddev,
+               p[273 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_DROP_CNT_0_REG + j * 0x80);
-               p[272 + i] = dsaf_read_dev(ddev,
+               p[276 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_INF_OUTPORT_CNT_0_REG + j * 0x80);
-               p[275 + i] = dsaf_read_dev(ddev,
+               p[279 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_INPORT_TC0_CNT_0_REG + j * 0x80);
-               p[278 + i] = dsaf_read_dev(ddev,
+               p[282 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_INPORT_TC1_CNT_0_REG + j * 0x80);
-               p[281 + i] = dsaf_read_dev(ddev,
+               p[285 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_INPORT_TC2_CNT_0_REG + j * 0x80);
-               p[284 + i] = dsaf_read_dev(ddev,
+               p[288 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_INPORT_TC3_CNT_0_REG + j * 0x80);
-               p[287 + i] = dsaf_read_dev(ddev,
+               p[291 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_INPORT_TC4_CNT_0_REG + j * 0x80);
-               p[290 + i] = dsaf_read_dev(ddev,
+               p[294 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_INPORT_TC5_CNT_0_REG + j * 0x80);
-               p[293 + i] = dsaf_read_dev(ddev,
+               p[297 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_INPORT_TC6_CNT_0_REG + j * 0x80);
-               p[296 + i] = dsaf_read_dev(ddev,
+               p[300 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_INPORT_TC7_CNT_0_REG + j * 0x80);
-               p[299 + i] = dsaf_read_dev(ddev,
+               p[303 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_REQ_CNT_0_REG + j * 0x80);
-               p[302 + i] = dsaf_read_dev(ddev,
+               p[306 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_LNK_RELS_CNT_0_REG + j * 0x80);
-               p[305 + i] = dsaf_read_dev(ddev,
+               p[309 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_BP_CFG_3_REG_0_REG + j * 0x80);
-               p[308 + i] = dsaf_read_dev(ddev,
+               p[312 + i] = dsaf_read_dev(ddev,
                                DSAF_SBM_BP_CFG_4_REG_0_REG + j * 0x80);
        }
 
        /* dsaf onode registers */
        for (i = 0; i < DSAF_XOD_NUM; i++) {
-               p[311 + i] = dsaf_read_dev(ddev,
+               p[315 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_ETS_TSA_TC0_TC3_CFG_0_REG + i * 0x90);
-               p[319 + i] = dsaf_read_dev(ddev,
+               p[323 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_ETS_TSA_TC4_TC7_CFG_0_REG + i * 0x90);
-               p[327 + i] = dsaf_read_dev(ddev,
+               p[331 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_ETS_BW_TC0_TC3_CFG_0_REG + i * 0x90);
-               p[335 + i] = dsaf_read_dev(ddev,
+               p[339 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_ETS_BW_TC4_TC7_CFG_0_REG + i * 0x90);
-               p[343 + i] = dsaf_read_dev(ddev,
+               p[347 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_ETS_BW_OFFSET_CFG_0_REG + i * 0x90);
-               p[351 + i] = dsaf_read_dev(ddev,
+               p[355 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_ETS_TOKEN_CFG_0_REG + i * 0x90);
        }
 
-       p[359] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_0_0_REG + port * 0x90);
-       p[360] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_1_0_REG + port * 0x90);
-       p[361] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_2_0_REG + port * 0x90);
+       p[363] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_0_0_REG + port * 0x90);
+       p[364] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_1_0_REG + port * 0x90);
+       p[365] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_2_0_REG + port * 0x90);
 
        for (i = 0; i < DSAF_XOD_BIG_NUM / DSAF_COMM_CHN; i++) {
                j = i * DSAF_COMM_CHN + port;
-               p[362 + i] = dsaf_read_dev(ddev,
+               p[366 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_GNT_L_0_REG + j * 0x90);
-               p[365 + i] = dsaf_read_dev(ddev,
+               p[369 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_GNT_H_0_REG + j * 0x90);
-               p[368 + i] = dsaf_read_dev(ddev,
+               p[372 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_CONNECT_STATE_0_REG + j * 0x90);
-               p[371 + i] = dsaf_read_dev(ddev,
+               p[375 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_RCVPKT_CNT_0_REG + j * 0x90);
-               p[374 + i] = dsaf_read_dev(ddev,
+               p[378 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_RCVTC0_CNT_0_REG + j * 0x90);
-               p[377 + i] = dsaf_read_dev(ddev,
+               p[381 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_RCVTC1_CNT_0_REG + j * 0x90);
-               p[380 + i] = dsaf_read_dev(ddev,
+               p[384 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_RCVTC2_CNT_0_REG + j * 0x90);
-               p[383 + i] = dsaf_read_dev(ddev,
+               p[387 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_RCVTC3_CNT_0_REG + j * 0x90);
-               p[386 + i] = dsaf_read_dev(ddev,
+               p[390 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_RCVVC0_CNT_0_REG + j * 0x90);
-               p[389 + i] = dsaf_read_dev(ddev,
+               p[393 + i] = dsaf_read_dev(ddev,
                                DSAF_XOD_RCVVC1_CNT_0_REG + j * 0x90);
        }
 
-       p[392] = dsaf_read_dev(ddev,
+       p[396] = dsaf_read_dev(ddev,
                DSAF_XOD_XGE_RCVIN0_CNT_0_REG + port * 0x90);
-       p[393] = dsaf_read_dev(ddev,
+       p[397] = dsaf_read_dev(ddev,
                DSAF_XOD_XGE_RCVIN1_CNT_0_REG + port * 0x90);
-       p[394] = dsaf_read_dev(ddev,
+       p[398] = dsaf_read_dev(ddev,
                DSAF_XOD_XGE_RCVIN2_CNT_0_REG + port * 0x90);
-       p[395] = dsaf_read_dev(ddev,
+       p[399] = dsaf_read_dev(ddev,
                DSAF_XOD_XGE_RCVIN3_CNT_0_REG + port * 0x90);
-       p[396] = dsaf_read_dev(ddev,
+       p[400] = dsaf_read_dev(ddev,
                DSAF_XOD_XGE_RCVIN4_CNT_0_REG + port * 0x90);
-       p[397] = dsaf_read_dev(ddev,
+       p[401] = dsaf_read_dev(ddev,
                DSAF_XOD_XGE_RCVIN5_CNT_0_REG + port * 0x90);
-       p[398] = dsaf_read_dev(ddev,
+       p[402] = dsaf_read_dev(ddev,
                DSAF_XOD_XGE_RCVIN6_CNT_0_REG + port * 0x90);
-       p[399] = dsaf_read_dev(ddev,
+       p[403] = dsaf_read_dev(ddev,
                DSAF_XOD_XGE_RCVIN7_CNT_0_REG + port * 0x90);
-       p[400] = dsaf_read_dev(ddev,
+       p[404] = dsaf_read_dev(ddev,
                DSAF_XOD_PPE_RCVIN0_CNT_0_REG + port * 0x90);
-       p[401] = dsaf_read_dev(ddev,
+       p[405] = dsaf_read_dev(ddev,
                DSAF_XOD_PPE_RCVIN1_CNT_0_REG + port * 0x90);
-       p[402] = dsaf_read_dev(ddev,
+       p[406] = dsaf_read_dev(ddev,
                DSAF_XOD_ROCEE_RCVIN0_CNT_0_REG + port * 0x90);
-       p[403] = dsaf_read_dev(ddev,
+       p[407] = dsaf_read_dev(ddev,
                DSAF_XOD_ROCEE_RCVIN1_CNT_0_REG + port * 0x90);
-       p[404] = dsaf_read_dev(ddev,
+       p[408] = dsaf_read_dev(ddev,
                DSAF_XOD_FIFO_STATUS_0_REG + port * 0x90);
 
        /* dsaf voq registers */
        for (i = 0; i < DSAF_VOQ_NUM / DSAF_COMM_CHN; i++) {
                j = (i * DSAF_COMM_CHN + port) * 0x90;
-               p[405 + i] = dsaf_read_dev(ddev,
+               p[409 + i] = dsaf_read_dev(ddev,
                        DSAF_VOQ_ECC_INVERT_EN_0_REG + j);
-               p[408 + i] = dsaf_read_dev(ddev,
+               p[412 + i] = dsaf_read_dev(ddev,
                        DSAF_VOQ_SRAM_PKT_NUM_0_REG + j);
-               p[411 + i] = dsaf_read_dev(ddev, DSAF_VOQ_IN_PKT_NUM_0_REG + j);
-               p[414 + i] = dsaf_read_dev(ddev,
+               p[415 + i] = dsaf_read_dev(ddev, DSAF_VOQ_IN_PKT_NUM_0_REG + j);
+               p[418 + i] = dsaf_read_dev(ddev,
                        DSAF_VOQ_OUT_PKT_NUM_0_REG + j);
-               p[417 + i] = dsaf_read_dev(ddev,
+               p[421 + i] = dsaf_read_dev(ddev,
                        DSAF_VOQ_ECC_ERR_ADDR_0_REG + j);
-               p[420 + i] = dsaf_read_dev(ddev, DSAF_VOQ_BP_STATUS_0_REG + j);
-               p[423 + i] = dsaf_read_dev(ddev, DSAF_VOQ_SPUP_IDLE_0_REG + j);
-               p[426 + i] = dsaf_read_dev(ddev,
+               p[424 + i] = dsaf_read_dev(ddev, DSAF_VOQ_BP_STATUS_0_REG + j);
+               p[427 + i] = dsaf_read_dev(ddev, DSAF_VOQ_SPUP_IDLE_0_REG + j);
+               p[430 + i] = dsaf_read_dev(ddev,
                        DSAF_VOQ_XGE_XOD_REQ_0_0_REG + j);
-               p[429 + i] = dsaf_read_dev(ddev,
+               p[433 + i] = dsaf_read_dev(ddev,
                        DSAF_VOQ_XGE_XOD_REQ_1_0_REG + j);
-               p[432 + i] = dsaf_read_dev(ddev,
+               p[436 + i] = dsaf_read_dev(ddev,
                        DSAF_VOQ_PPE_XOD_REQ_0_REG + j);
-               p[435 + i] = dsaf_read_dev(ddev,
+               p[439 + i] = dsaf_read_dev(ddev,
                        DSAF_VOQ_ROCEE_XOD_REQ_0_REG + j);
-               p[438 + i] = dsaf_read_dev(ddev,
+               p[442 + i] = dsaf_read_dev(ddev,
                        DSAF_VOQ_BP_ALL_THRD_0_REG + j);
        }
 
        /* dsaf tbl registers */
-       p[441] = dsaf_read_dev(ddev, DSAF_TBL_CTRL_0_REG);
-       p[442] = dsaf_read_dev(ddev, DSAF_TBL_INT_MSK_0_REG);
-       p[443] = dsaf_read_dev(ddev, DSAF_TBL_INT_SRC_0_REG);
-       p[444] = dsaf_read_dev(ddev, DSAF_TBL_INT_STS_0_REG);
-       p[445] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_ADDR_0_REG);
-       p[446] = dsaf_read_dev(ddev, DSAF_TBL_LINE_ADDR_0_REG);
-       p[447] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_HIGH_0_REG);
-       p[448] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_LOW_0_REG);
-       p[449] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG);
-       p[450] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG);
-       p[451] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG);
-       p[452] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG);
-       p[453] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG);
-       p[454] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_UCAST_CFG_0_REG);
-       p[455] = dsaf_read_dev(ddev, DSAF_TBL_LIN_CFG_0_REG);
-       p[456] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG);
-       p[457] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_LOW_0_REG);
-       p[458] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA4_0_REG);
-       p[459] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA3_0_REG);
-       p[460] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA2_0_REG);
-       p[461] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA1_0_REG);
-       p[462] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA0_0_REG);
-       p[463] = dsaf_read_dev(ddev, DSAF_TBL_LIN_RDATA_0_REG);
+       p[445] = dsaf_read_dev(ddev, DSAF_TBL_CTRL_0_REG);
+       p[446] = dsaf_read_dev(ddev, DSAF_TBL_INT_MSK_0_REG);
+       p[447] = dsaf_read_dev(ddev, DSAF_TBL_INT_SRC_0_REG);
+       p[448] = dsaf_read_dev(ddev, DSAF_TBL_INT_STS_0_REG);
+       p[449] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_ADDR_0_REG);
+       p[450] = dsaf_read_dev(ddev, DSAF_TBL_LINE_ADDR_0_REG);
+       p[451] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_HIGH_0_REG);
+       p[452] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_LOW_0_REG);
+       p[453] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG);
+       p[454] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG);
+       p[455] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG);
+       p[456] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG);
+       p[457] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG);
+       p[458] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_UCAST_CFG_0_REG);
+       p[459] = dsaf_read_dev(ddev, DSAF_TBL_LIN_CFG_0_REG);
+       p[460] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG);
+       p[461] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_LOW_0_REG);
+       p[462] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA4_0_REG);
+       p[463] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA3_0_REG);
+       p[464] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA2_0_REG);
+       p[465] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA1_0_REG);
+       p[466] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA0_0_REG);
+       p[467] = dsaf_read_dev(ddev, DSAF_TBL_LIN_RDATA_0_REG);
 
        for (i = 0; i < DSAF_SW_PORT_NUM; i++) {
                j = i * 0x8;
-               p[464 + 2 * i] = dsaf_read_dev(ddev,
+               p[468 + 2 * i] = dsaf_read_dev(ddev,
                        DSAF_TBL_DA0_MIS_INFO1_0_REG + j);
-               p[465 + 2 * i] = dsaf_read_dev(ddev,
+               p[469 + 2 * i] = dsaf_read_dev(ddev,
                        DSAF_TBL_DA0_MIS_INFO0_0_REG + j);
        }
 
-       p[480] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO2_0_REG);
-       p[481] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO1_0_REG);
-       p[482] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO0_0_REG);
-       p[483] = dsaf_read_dev(ddev, DSAF_TBL_PUL_0_REG);
-       p[484] = dsaf_read_dev(ddev, DSAF_TBL_OLD_RSLT_0_REG);
-       p[485] = dsaf_read_dev(ddev, DSAF_TBL_OLD_SCAN_VAL_0_REG);
-       p[486] = dsaf_read_dev(ddev, DSAF_TBL_DFX_CTRL_0_REG);
-       p[487] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_0_REG);
-       p[488] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_2_0_REG);
-       p[489] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_I_0_REG);
-       p[490] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_O_0_REG);
-       p[491] = dsaf_read_dev(ddev, DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG);
+       p[484] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO2_0_REG);
+       p[485] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO1_0_REG);
+       p[486] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO0_0_REG);
+       p[487] = dsaf_read_dev(ddev, DSAF_TBL_PUL_0_REG);
+       p[488] = dsaf_read_dev(ddev, DSAF_TBL_OLD_RSLT_0_REG);
+       p[489] = dsaf_read_dev(ddev, DSAF_TBL_OLD_SCAN_VAL_0_REG);
+       p[490] = dsaf_read_dev(ddev, DSAF_TBL_DFX_CTRL_0_REG);
+       p[491] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_0_REG);
+       p[492] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_2_0_REG);
+       p[493] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_I_0_REG);
+       p[494] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_O_0_REG);
+       p[495] = dsaf_read_dev(ddev, DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG);
 
        /* dsaf other registers */
-       p[492] = dsaf_read_dev(ddev, DSAF_INODE_FIFO_WL_0_REG + port * 0x4);
-       p[493] = dsaf_read_dev(ddev, DSAF_ONODE_FIFO_WL_0_REG + port * 0x4);
-       p[494] = dsaf_read_dev(ddev, DSAF_XGE_GE_WORK_MODE_0_REG + port * 0x4);
-       p[495] = dsaf_read_dev(ddev,
+       p[496] = dsaf_read_dev(ddev, DSAF_INODE_FIFO_WL_0_REG + port * 0x4);
+       p[497] = dsaf_read_dev(ddev, DSAF_ONODE_FIFO_WL_0_REG + port * 0x4);
+       p[498] = dsaf_read_dev(ddev, DSAF_XGE_GE_WORK_MODE_0_REG + port * 0x4);
+       p[499] = dsaf_read_dev(ddev,
                DSAF_XGE_APP_RX_LINK_UP_0_REG + port * 0x4);
-       p[496] = dsaf_read_dev(ddev, DSAF_NETPORT_CTRL_SIG_0_REG + port * 0x4);
-       p[497] = dsaf_read_dev(ddev, DSAF_XGE_CTRL_SIG_CFG_0_REG + port * 0x4);
+       p[500] = dsaf_read_dev(ddev, DSAF_NETPORT_CTRL_SIG_0_REG + port * 0x4);
+       p[501] = dsaf_read_dev(ddev, DSAF_XGE_CTRL_SIG_CFG_0_REG + port * 0x4);
 
        if (!is_ver1)
-               p[498] = dsaf_read_dev(ddev, DSAF_PAUSE_CFG_REG + port * 0x4);
+               p[502] = dsaf_read_dev(ddev, DSAF_PAUSE_CFG_REG + port * 0x4);
 
        /* mark end of dsaf regs */
-       for (i = 499; i < 504; i++)
+       for (i = 503; i < 504; i++)
                p[i] = 0xdddddddd;
 }
 
@@ -2673,58 +2750,156 @@ int hns_dsaf_get_regs_count(void)
        return DSAF_DUMP_REGS_NUM;
 }
 
-/* Reserve the last TCAM entry for promisc support */
-#define dsaf_promisc_tcam_entry(port) \
-       (DSAF_TCAM_SUM - DSAFV2_MAC_FUZZY_TCAM_NUM + (port))
-void hns_dsaf_set_promisc_tcam(struct dsaf_device *dsaf_dev,
-                              u32 port, bool enable)
+static void set_promisc_tcam_enable(struct dsaf_device *dsaf_dev, u32 port)
 {
+       struct dsaf_tbl_tcam_ucast_cfg tbl_tcam_ucast = {0, 1, 0, 0, 0x80};
+       struct dsaf_tbl_tcam_data tbl_tcam_data_mc = {0x01000000, port};
+       struct dsaf_tbl_tcam_data tbl_tcam_mask_uc = {0x01000000, 0xf};
+       struct dsaf_tbl_tcam_mcast_cfg tbl_tcam_mcast = {0, 0, {0} };
        struct dsaf_drv_priv *priv = hns_dsaf_dev_priv(dsaf_dev);
-       struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl;
-       u16 entry_index;
-       struct dsaf_drv_tbl_tcam_key tbl_tcam_data, tbl_tcam_mask;
-       struct dsaf_tbl_tcam_mcast_cfg mac_data = {0};
+       struct dsaf_tbl_tcam_data tbl_tcam_data_uc = {0, port};
+       struct dsaf_drv_mac_single_dest_entry mask_entry;
+       struct dsaf_drv_tbl_tcam_key temp_key, mask_key;
+       struct dsaf_drv_soft_mac_tbl *soft_mac_entry;
+       u16 entry_index = DSAF_INVALID_ENTRY_IDX;
+       struct dsaf_drv_tbl_tcam_key mac_key;
+       struct hns_mac_cb *mac_cb;
+       u8 addr[ETH_ALEN] = {0};
+       u8 port_num;
+       u16 mskid;
+
+       /* promisc use vague table match with vlanid = 0 & macaddr = 0 */
+       hns_dsaf_set_mac_key(dsaf_dev, &mac_key, 0x00, port, addr);
+       entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key);
+       if (entry_index != DSAF_INVALID_ENTRY_IDX)
+               return;
+
+       /* put promisc tcam entry in the end. */
+       /* 1. set promisc unicast vague tcam entry. */
+       entry_index = hns_dsaf_find_empty_mac_entry_reverse(dsaf_dev);
+       if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+               dev_err(dsaf_dev->dev,
+                       "enable uc promisc failed (port:%#x)\n",
+                       port);
+               return;
+       }
+
+       mac_cb = dsaf_dev->mac_cb[port];
+       (void)hns_mac_get_inner_port_num(mac_cb, 0, &port_num);
+       tbl_tcam_ucast.tbl_ucast_out_port = port_num;
 
-       if ((AE_IS_VER1(dsaf_dev->dsaf_ver)) || HNS_DSAF_IS_DEBUG(dsaf_dev))
+       /* config uc vague table */
+       hns_dsaf_tcam_uc_cfg_vague(dsaf_dev, entry_index, &tbl_tcam_data_uc,
+                                  &tbl_tcam_mask_uc, &tbl_tcam_ucast);
+
+       /* update software entry */
+       soft_mac_entry = priv->soft_mac_tbl;
+       soft_mac_entry += entry_index;
+       soft_mac_entry->index = entry_index;
+       soft_mac_entry->tcam_key.high.val = mac_key.high.val;
+       soft_mac_entry->tcam_key.low.val = mac_key.low.val;
+       /* step back to the START for mc. */
+       soft_mac_entry = priv->soft_mac_tbl;
+
+       /* 2. set promisc multicast vague tcam entry. */
+       entry_index = hns_dsaf_find_empty_mac_entry_reverse(dsaf_dev);
+       if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+               dev_err(dsaf_dev->dev,
+                       "enable mc promisc failed (port:%#x)\n",
+                       port);
                return;
+       }
+
+       memset(&mask_entry, 0x0, sizeof(mask_entry));
+       memset(&mask_key, 0x0, sizeof(mask_key));
+       memset(&temp_key, 0x0, sizeof(temp_key));
+       mask_entry.addr[0] = 0x01;
+       hns_dsaf_set_mac_key(dsaf_dev, &mask_key, mask_entry.in_vlan_id,
+                            port, mask_entry.addr);
+       tbl_tcam_mcast.tbl_mcast_item_vld = 1;
+       tbl_tcam_mcast.tbl_mcast_old_en = 0;
 
-       /* find the tcam entry index for promisc */
-       entry_index = dsaf_promisc_tcam_entry(port);
-
-       memset(&tbl_tcam_data, 0, sizeof(tbl_tcam_data));
-       memset(&tbl_tcam_mask, 0, sizeof(tbl_tcam_mask));
-
-       /* config key mask */
-       if (enable) {
-               dsaf_set_field(tbl_tcam_data.low.bits.port_vlan,
-                              DSAF_TBL_TCAM_KEY_PORT_M,
-                              DSAF_TBL_TCAM_KEY_PORT_S, port);
-               dsaf_set_field(tbl_tcam_mask.low.bits.port_vlan,
-                              DSAF_TBL_TCAM_KEY_PORT_M,
-                              DSAF_TBL_TCAM_KEY_PORT_S, 0xf);
-
-               /* SUB_QID */
-               dsaf_set_bit(mac_data.tbl_mcast_port_msk[0],
-                            DSAF_SERVICE_NW_NUM, true);
-               mac_data.tbl_mcast_item_vld = true;     /* item_vld bit */
+       if (port < DSAF_SERVICE_NW_NUM) {
+               mskid = port;
+       } else if (port >= DSAF_BASE_INNER_PORT_NUM) {
+               mskid = port - DSAF_BASE_INNER_PORT_NUM + DSAF_SERVICE_NW_NUM;
        } else {
-               mac_data.tbl_mcast_item_vld = false;    /* item_vld bit */
+               dev_err(dsaf_dev->dev, "%s,pnum(%d)error,key(%#x:%#x)\n",
+                       dsaf_dev->ae_dev.name, port,
+                       mask_key.high.val, mask_key.low.val);
+               return;
        }
 
-       dev_dbg(dsaf_dev->dev,
-               "set_promisc_entry, %s Mac key(%#x:%#x) entry_index%d\n",
-               dsaf_dev->ae_dev.name, tbl_tcam_data.high.val,
-               tbl_tcam_data.low.val, entry_index);
+       dsaf_set_bit(tbl_tcam_mcast.tbl_mcast_port_msk[mskid / 32],
+                    mskid % 32, 1);
+       memcpy(&temp_key, &mask_key, sizeof(mask_key));
+       hns_dsaf_tcam_mc_cfg_vague(dsaf_dev, entry_index, &tbl_tcam_data_mc,
+                                  (struct dsaf_tbl_tcam_data *)(&mask_key),
+                                  &tbl_tcam_mcast);
+
+       /* update software entry */
+       soft_mac_entry += entry_index;
+       soft_mac_entry->index = entry_index;
+       soft_mac_entry->tcam_key.high.val = temp_key.high.val;
+       soft_mac_entry->tcam_key.low.val = temp_key.low.val;
+}
 
-       /* config promisc entry with mask */
-       hns_dsaf_tcam_mc_cfg(dsaf_dev, entry_index,
-                            (struct dsaf_tbl_tcam_data *)&tbl_tcam_data,
-                            (struct dsaf_tbl_tcam_data *)&tbl_tcam_mask,
-                            &mac_data);
+static void set_promisc_tcam_disable(struct dsaf_device *dsaf_dev, u32 port)
+{
+       struct dsaf_tbl_tcam_data tbl_tcam_data_mc = {0x01000000, port};
+       struct dsaf_tbl_tcam_ucast_cfg tbl_tcam_ucast = {0, 0, 0, 0, 0};
+       struct dsaf_tbl_tcam_mcast_cfg tbl_tcam_mcast = {0, 0, {0} };
+       struct dsaf_drv_priv *priv = hns_dsaf_dev_priv(dsaf_dev);
+       struct dsaf_tbl_tcam_data tbl_tcam_data_uc = {0, 0};
+       struct dsaf_tbl_tcam_data tbl_tcam_mask = {0, 0};
+       struct dsaf_drv_soft_mac_tbl *soft_mac_entry;
+       u16 entry_index = DSAF_INVALID_ENTRY_IDX;
+       struct dsaf_drv_tbl_tcam_key mac_key;
+       u8 addr[ETH_ALEN] = {0};
 
-       /* config software entry */
+       /* 1. delete uc vague tcam entry. */
+       /* promisc use vague table match with vlanid = 0 & macaddr = 0 */
+       hns_dsaf_set_mac_key(dsaf_dev, &mac_key, 0x00, port, addr);
+       entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key);
+
+       if (entry_index == DSAF_INVALID_ENTRY_IDX)
+               return;
+
+       /* config uc vague table */
+       hns_dsaf_tcam_uc_cfg_vague(dsaf_dev, entry_index, &tbl_tcam_data_uc,
+                                  &tbl_tcam_mask, &tbl_tcam_ucast);
+       /* update soft management table. */
+       soft_mac_entry = priv->soft_mac_tbl;
+       soft_mac_entry += entry_index;
+       soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX;
+       /* step back to the START for mc. */
+       soft_mac_entry = priv->soft_mac_tbl;
+
+       /* 2. delete mc vague tcam entry. */
+       addr[0] = 0x01;
+       memset(&mac_key, 0x0, sizeof(mac_key));
+       hns_dsaf_set_mac_key(dsaf_dev, &mac_key, 0x00, port, addr);
+       entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key);
+
+       if (entry_index == DSAF_INVALID_ENTRY_IDX)
+               return;
+
+       /* config mc vague table */
+       hns_dsaf_tcam_mc_cfg_vague(dsaf_dev, entry_index, &tbl_tcam_data_mc,
+                                  &tbl_tcam_mask, &tbl_tcam_mcast);
+       /* update soft management table. */
        soft_mac_entry += entry_index;
-       soft_mac_entry->index = enable ? entry_index : DSAF_INVALID_ENTRY_IDX;
+       soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX;
+}
+
+/* Reserve the last TCAM entry for promisc support */
+void hns_dsaf_set_promisc_tcam(struct dsaf_device *dsaf_dev,
+                              u32 port, bool enable)
+{
+       if (enable)
+               set_promisc_tcam_enable(dsaf_dev, port);
+       else
+               set_promisc_tcam_disable(dsaf_dev, port);
 }
 
 int hns_dsaf_wait_pkt_clean(struct dsaf_device *dsaf_dev, int port)
index 74d935d..b9733b0 100644 (file)
 #define DSAF_INODE_IN_DATA_STP_DISC_0_REG      0x1A50
 #define DSAF_INODE_GE_FC_EN_0_REG              0x1B00
 #define DSAF_INODE_VC0_IN_PKT_NUM_0_REG                0x1B50
-#define DSAF_INODE_VC1_IN_PKT_NUM_0_REG                0x1C00
+#define DSAF_INODE_VC1_IN_PKT_NUM_0_REG                0x103C
 #define DSAF_INODE_IN_PRIO_PAUSE_BASE_REG      0x1C00
 #define DSAF_INODE_IN_PRIO_PAUSE_BASE_OFFSET   0x100
 #define DSAF_INODE_IN_PRIO_PAUSE_OFFSET                0x50
 #define RCB_ECC_ERR_ADDR4_REG                  0x460
 #define RCB_ECC_ERR_ADDR5_REG                  0x464
 
-#define RCB_COM_SF_CFG_INTMASK_RING            0x480
-#define RCB_COM_SF_CFG_RING_STS                        0x484
-#define RCB_COM_SF_CFG_RING                    0x488
-#define RCB_COM_SF_CFG_INTMASK_BD              0x48C
-#define RCB_COM_SF_CFG_BD_RINT_STS             0x470
+#define RCB_COM_SF_CFG_INTMASK_RING            0x470
+#define RCB_COM_SF_CFG_RING_STS                        0x474
+#define RCB_COM_SF_CFG_RING                    0x478
+#define RCB_COM_SF_CFG_INTMASK_BD              0x47C
+#define RCB_COM_SF_CFG_BD_RINT_STS             0x480
 #define RCB_COM_RCB_RD_BD_BUSY                 0x490
 #define RCB_COM_RCB_FBD_CRT_EN                 0x494
 #define RCB_COM_AXI_WR_ERR_INTMASK             0x498
 #define GMAC_LD_LINK_COUNTER_REG               0x01D0UL
 #define GMAC_LOOP_REG                          0x01DCUL
 #define GMAC_RECV_CONTROL_REG                  0x01E0UL
+#define GMAC_PCS_RX_EN_REG                     0x01E4UL
 #define GMAC_VLAN_CODE_REG                     0x01E8UL
 #define GMAC_RX_OVERRUN_CNT_REG                        0x01ECUL
 #define GMAC_RX_LENGTHFIELD_ERR_CNT_REG                0x01F4UL
index c62378c..5748d3f 100644 (file)
@@ -1188,6 +1188,9 @@ int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h)
        if (h->phy_if == PHY_INTERFACE_MODE_XGMII)
                phy_dev->autoneg = false;
 
+       if (h->phy_if == PHY_INTERFACE_MODE_SGMII)
+               phy_stop(phy_dev);
+
        return 0;
 }
 
@@ -1283,6 +1286,22 @@ static int hns_nic_init_affinity_mask(int q_num, int ring_idx,
        return cpu;
 }
 
+static void hns_nic_free_irq(int q_num, struct hns_nic_priv *priv)
+{
+       int i;
+
+       for (i = 0; i < q_num * 2; i++) {
+               if (priv->ring_data[i].ring->irq_init_flag == RCB_IRQ_INITED) {
+                       irq_set_affinity_hint(priv->ring_data[i].ring->irq,
+                                             NULL);
+                       free_irq(priv->ring_data[i].ring->irq,
+                                &priv->ring_data[i]);
+                       priv->ring_data[i].ring->irq_init_flag =
+                               RCB_IRQ_NOT_INITED;
+               }
+       }
+}
+
 static int hns_nic_init_irq(struct hns_nic_priv *priv)
 {
        struct hnae_handle *h = priv->ae_handle;
@@ -1308,7 +1327,7 @@ static int hns_nic_init_irq(struct hns_nic_priv *priv)
                if (ret) {
                        netdev_err(priv->netdev, "request irq(%d) fail\n",
                                   rd->ring->irq);
-                       return ret;
+                       goto out_free_irq;
                }
                disable_irq(rd->ring->irq);
 
@@ -1323,6 +1342,10 @@ static int hns_nic_init_irq(struct hns_nic_priv *priv)
        }
 
        return 0;
+
+out_free_irq:
+       hns_nic_free_irq(h->q_num, priv);
+       return ret;
 }
 
 static int hns_nic_net_up(struct net_device *ndev)
@@ -1332,6 +1355,9 @@ static int hns_nic_net_up(struct net_device *ndev)
        int i, j;
        int ret;
 
+       if (!test_bit(NIC_STATE_DOWN, &priv->state))
+               return 0;
+
        ret = hns_nic_init_irq(priv);
        if (ret != 0) {
                netdev_err(ndev, "hns init irq failed! ret=%d\n", ret);
@@ -1367,6 +1393,7 @@ out_has_some_queues:
        for (j = i - 1; j >= 0; j--)
                hns_nic_ring_close(ndev, j);
 
+       hns_nic_free_irq(h->q_num, priv);
        set_bit(NIC_STATE_DOWN, &priv->state);
 
        return ret;
@@ -1484,11 +1511,19 @@ static int hns_nic_net_stop(struct net_device *ndev)
 }
 
 static void hns_tx_timeout_reset(struct hns_nic_priv *priv);
+#define HNS_TX_TIMEO_LIMIT (40 * HZ)
 static void hns_nic_net_timeout(struct net_device *ndev)
 {
        struct hns_nic_priv *priv = netdev_priv(ndev);
 
-       hns_tx_timeout_reset(priv);
+       if (ndev->watchdog_timeo < HNS_TX_TIMEO_LIMIT) {
+               ndev->watchdog_timeo *= 2;
+               netdev_info(ndev, "watchdog_timo changed to %d.\n",
+                           ndev->watchdog_timeo);
+       } else {
+               ndev->watchdog_timeo = HNS_NIC_TX_TIMEOUT;
+               hns_tx_timeout_reset(priv);
+       }
 }
 
 static int hns_nic_do_ioctl(struct net_device *netdev, struct ifreq *ifr,
@@ -2051,11 +2086,11 @@ static void hns_nic_service_task(struct work_struct *work)
                = container_of(work, struct hns_nic_priv, service_task);
        struct hnae_handle *h = priv->ae_handle;
 
+       hns_nic_reset_subtask(priv);
        hns_nic_update_link_status(priv->netdev);
        h->dev->ops->update_led_status(h);
        hns_nic_update_stats(priv->netdev);
 
-       hns_nic_reset_subtask(priv);
        hns_nic_service_event_complete(priv);
 }
 
@@ -2341,7 +2376,7 @@ static int hns_nic_dev_probe(struct platform_device *pdev)
        ndev->min_mtu = MAC_MIN_MTU;
        switch (priv->enet_ver) {
        case AE_VERSION_2:
-               ndev->features |= NETIF_F_TSO | NETIF_F_TSO6;
+               ndev->features |= NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_NTUPLE;
                ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                        NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |
                        NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6;
index 774beda..8e9b958 100644 (file)
@@ -624,7 +624,7 @@ static void hns_nic_self_test(struct net_device *ndev,
                clear_bit(NIC_STATE_TESTING, &priv->state);
 
                if (if_running)
-                       (void)dev_open(ndev);
+                       (void)dev_open(ndev, NULL);
        }
        /* Online tests aren't run; pass by default */
 
index 002534f..d01bf53 100644 (file)
@@ -9,6 +9,6 @@ obj-$(CONFIG_HNS3) += hns3vf/
 obj-$(CONFIG_HNS3) += hnae3.o
 
 obj-$(CONFIG_HNS3_ENET) += hns3.o
-hns3-objs = hns3_enet.o hns3_ethtool.o
+hns3-objs = hns3_enet.o hns3_ethtool.o hns3_debugfs.o
 
 hns3-$(CONFIG_HNS3_DCB) += hns3_dcbnl.o
index 3bb313c..691d121 100644 (file)
@@ -36,6 +36,10 @@ enum HCLGE_MBX_OPCODE {
        HCLGE_MBX_BIND_FUNC_QUEUE,      /* (VF -> PF) bind function and queue */
        HCLGE_MBX_GET_LINK_STATUS,      /* (VF -> PF) get link status */
        HCLGE_MBX_QUEUE_RESET,          /* (VF -> PF) reset queue */
+       HCLGE_MBX_KEEP_ALIVE,           /* (VF -> PF) send keep alive cmd */
+       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 */
 };
 
 /* below are per-VF mac-vlan subcodes */
index f69d39f..36eab37 100644 (file)
@@ -52,6 +52,7 @@
 #define HNAE3_UNIC_CLIENT_INITED_B             0x4
 #define HNAE3_ROCE_CLIENT_INITED_B             0x5
 #define HNAE3_DEV_SUPPORT_FD_B                 0x6
+#define HNAE3_DEV_SUPPORT_GRO_B                        0x7
 
 #define HNAE3_DEV_SUPPORT_ROCE_DCB_BITS (BIT(HNAE3_DEV_SUPPORT_DCB_B) |\
                BIT(HNAE3_DEV_SUPPORT_ROCE_B))
@@ -65,6 +66,9 @@
 #define hnae3_dev_fd_supported(hdev) \
        hnae3_get_bit((hdev)->ae_dev->flag, HNAE3_DEV_SUPPORT_FD_B)
 
+#define hnae3_dev_gro_supported(hdev) \
+       hnae3_get_bit((hdev)->ae_dev->flag, HNAE3_DEV_SUPPORT_GRO_B)
+
 #define ring_ptr_move_fw(ring, p) \
        ((ring)->p = ((ring)->p + 1) % (ring)->desc_num)
 #define ring_ptr_move_bw(ring, p) \
@@ -132,6 +136,7 @@ enum hnae3_reset_type {
        HNAE3_CORE_RESET,
        HNAE3_GLOBAL_RESET,
        HNAE3_IMP_RESET,
+       HNAE3_UNKNOWN_RESET,
        HNAE3_NONE_RESET,
 };
 
@@ -206,6 +211,10 @@ struct hnae3_ae_dev {
  *   Enable the hardware
  * stop()
  *   Disable the hardware
+ * start_client()
+ *   Inform the hclge that client has been started
+ * stop_client()
+ *   Inform the hclge that client has been stopped
  * get_status()
  *   Get the carrier state of the back channel of the handle, 1 for ok, 0 for
  *   non-ok
@@ -301,6 +310,8 @@ struct hnae3_ae_dev {
  *   Set vlan filter config of vf
  * enable_hw_strip_rxvtag()
  *   Enable/disable hardware strip vlan tag of packets received
+ * set_gro_en
+ *   Enable/disable HW GRO
  */
 struct hnae3_ae_ops {
        int (*init_ae_dev)(struct hnae3_ae_dev *ae_dev);
@@ -313,6 +324,8 @@ struct hnae3_ae_ops {
                                       struct hnae3_ae_dev *ae_dev);
        int (*start)(struct hnae3_handle *handle);
        void (*stop)(struct hnae3_handle *handle);
+       int (*client_start)(struct hnae3_handle *handle);
+       void (*client_stop)(struct hnae3_handle *handle);
        int (*get_status)(struct hnae3_handle *handle);
        void (*get_ksettings_an_result)(struct hnae3_handle *handle,
                                        u8 *auto_neg, u32 *speed, u8 *duplex);
@@ -441,10 +454,14 @@ struct hnae3_ae_ops {
                                struct ethtool_rxnfc *cmd, u32 *rule_locs);
        int (*restore_fd_rules)(struct hnae3_handle *handle);
        void (*enable_fd)(struct hnae3_handle *handle, bool enable);
-       pci_ers_result_t (*process_hw_error)(struct hnae3_ae_dev *ae_dev);
+       int (*dbg_run_cmd)(struct hnae3_handle *handle, char *cmd_buf);
+       pci_ers_result_t (*handle_hw_ras_error)(struct hnae3_ae_dev *ae_dev);
        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);
+       u16 (*get_global_queue_id)(struct hnae3_handle *handle, u16 queue_id);
+       void (*set_timer_task)(struct hnae3_handle *handle, bool enable);
 };
 
 struct hnae3_dcb_ops {
@@ -553,6 +570,7 @@ struct hnae3_handle {
        u32 numa_node_mask;     /* for multi-chip support */
 
        u8 netdev_flags;
+       struct dentry *hnae3_dbgfs;
 };
 
 #define hnae3_set_field(origin, mask, shift, val) \
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
new file mode 100644 (file)
index 0000000..0de543f
--- /dev/null
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2018-2019 Hisilicon Limited. */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+
+#include "hnae3.h"
+#include "hns3_enet.h"
+
+#define HNS3_DBG_READ_LEN 256
+
+static struct dentry *hns3_dbgfs_root;
+
+static int hns3_dbg_queue_info(struct hnae3_handle *h, char *cmd_buf)
+{
+       struct hns3_nic_priv *priv = h->priv;
+       struct hns3_nic_ring_data *ring_data;
+       struct hns3_enet_ring *ring;
+       u32 base_add_l, base_add_h;
+       u32 queue_num, queue_max;
+       u32 value, i = 0;
+       int cnt;
+
+       if (!priv->ring_data) {
+               dev_err(&h->pdev->dev, "ring_data is NULL\n");
+               return -EFAULT;
+       }
+
+       queue_max = h->kinfo.num_tqps;
+       cnt = kstrtouint(&cmd_buf[11], 0, &queue_num);
+       if (cnt)
+               queue_num = 0;
+       else
+               queue_max = queue_num + 1;
+
+       dev_info(&h->pdev->dev, "queue info\n");
+
+       if (queue_num >= h->kinfo.num_tqps) {
+               dev_err(&h->pdev->dev,
+                       "Queue number(%u) is out of range(%u)\n", queue_num,
+                       h->kinfo.num_tqps - 1);
+               return -EINVAL;
+       }
+
+       ring_data = priv->ring_data;
+       for (i = queue_num; i < queue_max; i++) {
+               /* Each cycle needs to determine whether the instance is reset,
+                * to prevent reference to invalid memory. And need to ensure
+                * that the following code is executed within 100ms.
+                */
+               if (!test_bit(HNS3_NIC_STATE_INITED, &priv->state) ||
+                   test_bit(HNS3_NIC_STATE_RESETTING, &priv->state))
+                       return -EPERM;
+
+               ring = ring_data[(u32)(i + h->kinfo.num_tqps)].ring;
+               base_add_h = readl_relaxed(ring->tqp->io_base +
+                                          HNS3_RING_RX_RING_BASEADDR_H_REG);
+               base_add_l = readl_relaxed(ring->tqp->io_base +
+                                          HNS3_RING_RX_RING_BASEADDR_L_REG);
+               dev_info(&h->pdev->dev, "RX(%d) BASE ADD: 0x%08x%08x\n", i,
+                        base_add_h, base_add_l);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_BD_NUM_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING BD NUM: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_BD_LEN_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING BD LEN: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_TAIL_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING TAIL: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_HEAD_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING HEAD: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_FBDNUM_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING FBDNUM: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_PKTNUM_RECORD_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING PKTNUM: %u\n", i, value);
+
+               ring = ring_data[i].ring;
+               base_add_h = readl_relaxed(ring->tqp->io_base +
+                                          HNS3_RING_TX_RING_BASEADDR_H_REG);
+               base_add_l = readl_relaxed(ring->tqp->io_base +
+                                          HNS3_RING_TX_RING_BASEADDR_L_REG);
+               dev_info(&h->pdev->dev, "TX(%d) BASE ADD: 0x%08x%08x\n", i,
+                        base_add_h, base_add_l);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_BD_NUM_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING BD NUM: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_TC_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING TC: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_TAIL_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING TAIL: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_HEAD_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING HEAD: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_FBDNUM_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING FBDNUM: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_OFFSET_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING OFFSET: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_PKTNUM_RECORD_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING PKTNUM: %u\n\n", i,
+                        value);
+       }
+
+       return 0;
+}
+
+static int hns3_dbg_queue_map(struct hnae3_handle *h)
+{
+       struct hns3_nic_priv *priv = h->priv;
+       struct hns3_nic_ring_data *ring_data;
+       int i;
+
+       if (!h->ae_algo->ops->get_global_queue_id)
+               return -EOPNOTSUPP;
+
+       dev_info(&h->pdev->dev, "map info for queue id and vector id\n");
+       dev_info(&h->pdev->dev,
+                "local queue id | global queue id | vector id\n");
+       for (i = 0; i < h->kinfo.num_tqps; i++) {
+               u16 global_qid;
+
+               global_qid = h->ae_algo->ops->get_global_queue_id(h, i);
+               ring_data = &priv->ring_data[i];
+               if (!ring_data || !ring_data->ring ||
+                   !ring_data->ring->tqp_vector)
+                       continue;
+
+               dev_info(&h->pdev->dev,
+                        "      %4d            %4d            %4d\n",
+                        i, global_qid,
+                        ring_data->ring->tqp_vector->vector_irq);
+       }
+
+       return 0;
+}
+
+static int hns3_dbg_bd_info(struct hnae3_handle *h, char *cmd_buf)
+{
+       struct hns3_nic_priv *priv = h->priv;
+       struct hns3_nic_ring_data *ring_data;
+       struct hns3_desc *rx_desc, *tx_desc;
+       struct device *dev = &h->pdev->dev;
+       struct hns3_enet_ring *ring;
+       u32 tx_index, rx_index;
+       u32 q_num, value;
+       int cnt;
+
+       cnt = sscanf(&cmd_buf[8], "%u %u", &q_num, &tx_index);
+       if (cnt == 2) {
+               rx_index = tx_index;
+       } else if (cnt != 1) {
+               dev_err(dev, "bd info: bad command string, cnt=%d\n", cnt);
+               return -EINVAL;
+       }
+
+       if (q_num >= h->kinfo.num_tqps) {
+               dev_err(dev, "Queue number(%u) is out of range(%u)\n", q_num,
+                       h->kinfo.num_tqps - 1);
+               return -EINVAL;
+       }
+
+       ring_data = priv->ring_data;
+       ring  = ring_data[q_num].ring;
+       value = readl_relaxed(ring->tqp->io_base + HNS3_RING_TX_RING_TAIL_REG);
+       tx_index = (cnt == 1) ? value : tx_index;
+
+       if (tx_index >= ring->desc_num) {
+               dev_err(dev, "bd index (%u) is out of range(%u)\n", tx_index,
+                       ring->desc_num - 1);
+               return -EINVAL;
+       }
+
+       tx_desc = &ring->desc[tx_index];
+       dev_info(dev, "TX Queue Num: %u, BD Index: %u\n", q_num, tx_index);
+       dev_info(dev, "(TX) addr: 0x%llx\n", tx_desc->addr);
+       dev_info(dev, "(TX)vlan_tag: %u\n", tx_desc->tx.vlan_tag);
+       dev_info(dev, "(TX)send_size: %u\n", tx_desc->tx.send_size);
+       dev_info(dev, "(TX)vlan_tso: %u\n", tx_desc->tx.type_cs_vlan_tso);
+       dev_info(dev, "(TX)l2_len: %u\n", tx_desc->tx.l2_len);
+       dev_info(dev, "(TX)l3_len: %u\n", tx_desc->tx.l3_len);
+       dev_info(dev, "(TX)l4_len: %u\n", tx_desc->tx.l4_len);
+       dev_info(dev, "(TX)vlan_tag: %u\n", tx_desc->tx.outer_vlan_tag);
+       dev_info(dev, "(TX)tv: %u\n", tx_desc->tx.tv);
+       dev_info(dev, "(TX)vlan_msec: %u\n", tx_desc->tx.ol_type_vlan_msec);
+       dev_info(dev, "(TX)ol2_len: %u\n", tx_desc->tx.ol2_len);
+       dev_info(dev, "(TX)ol3_len: %u\n", tx_desc->tx.ol3_len);
+       dev_info(dev, "(TX)ol4_len: %u\n", tx_desc->tx.ol4_len);
+       dev_info(dev, "(TX)paylen: %u\n", tx_desc->tx.paylen);
+       dev_info(dev, "(TX)vld_ra_ri: %u\n", tx_desc->tx.bdtp_fe_sc_vld_ra_ri);
+       dev_info(dev, "(TX)mss: %u\n", tx_desc->tx.mss);
+
+       ring  = ring_data[q_num + h->kinfo.num_tqps].ring;
+       value = readl_relaxed(ring->tqp->io_base + HNS3_RING_RX_RING_TAIL_REG);
+       rx_index = (cnt == 1) ? value : tx_index;
+       rx_desc  = &ring->desc[rx_index];
+
+       dev_info(dev, "RX Queue Num: %u, BD Index: %u\n", q_num, rx_index);
+       dev_info(dev, "(RX)addr: 0x%llx\n", rx_desc->addr);
+       dev_info(dev, "(RX)pkt_len: %u\n", rx_desc->rx.pkt_len);
+       dev_info(dev, "(RX)size: %u\n", rx_desc->rx.size);
+       dev_info(dev, "(RX)rss_hash: %u\n", rx_desc->rx.rss_hash);
+       dev_info(dev, "(RX)fd_id: %u\n", rx_desc->rx.fd_id);
+       dev_info(dev, "(RX)vlan_tag: %u\n", rx_desc->rx.vlan_tag);
+       dev_info(dev, "(RX)o_dm_vlan_id_fb: %u\n", rx_desc->rx.o_dm_vlan_id_fb);
+       dev_info(dev, "(RX)ot_vlan_tag: %u\n", rx_desc->rx.ot_vlan_tag);
+       dev_info(dev, "(RX)bd_base_info: %u\n", rx_desc->rx.bd_base_info);
+
+       return 0;
+}
+
+static void hns3_dbg_help(struct hnae3_handle *h)
+{
+#define HNS3_DBG_BUF_LEN 256
+
+       char printf_buf[HNS3_DBG_BUF_LEN];
+
+       dev_info(&h->pdev->dev, "available commands\n");
+       dev_info(&h->pdev->dev, "queue info [number]\n");
+       dev_info(&h->pdev->dev, "queue map\n");
+       dev_info(&h->pdev->dev, "bd info [q_num] <bd index>\n");
+       dev_info(&h->pdev->dev, "dump fd tcam\n");
+       dev_info(&h->pdev->dev, "dump tc\n");
+       dev_info(&h->pdev->dev, "dump tm map [q_num]\n");
+       dev_info(&h->pdev->dev, "dump tm\n");
+       dev_info(&h->pdev->dev, "dump qos pause cfg\n");
+       dev_info(&h->pdev->dev, "dump qos pri map\n");
+       dev_info(&h->pdev->dev, "dump qos buf cfg\n");
+       dev_info(&h->pdev->dev, "dump mng tbl\n");
+
+       memset(printf_buf, 0, HNS3_DBG_BUF_LEN);
+       strncat(printf_buf, "dump reg [[bios common] [ssu <prt_id>]",
+               HNS3_DBG_BUF_LEN - 1);
+       strncat(printf_buf + strlen(printf_buf),
+               " [igu egu <prt_id>] [rpu <tc_queue_num>]",
+               HNS3_DBG_BUF_LEN - strlen(printf_buf) - 1);
+       strncat(printf_buf + strlen(printf_buf),
+               " [rtc] [ppp] [rcb] [tqp <q_num>]]\n",
+               HNS3_DBG_BUF_LEN - strlen(printf_buf) - 1);
+       dev_info(&h->pdev->dev, "%s", printf_buf);
+
+       memset(printf_buf, 0, HNS3_DBG_BUF_LEN);
+       strncat(printf_buf, "dump reg dcb [port_id] [pri_id] [pg_id]",
+               HNS3_DBG_BUF_LEN - 1);
+       strncat(printf_buf + strlen(printf_buf), " [rq_id] [nq_id] [qset_id]\n",
+               HNS3_DBG_BUF_LEN - strlen(printf_buf) - 1);
+       dev_info(&h->pdev->dev, "%s", printf_buf);
+}
+
+static ssize_t hns3_dbg_cmd_read(struct file *filp, char __user *buffer,
+                                size_t count, loff_t *ppos)
+{
+       int uncopy_bytes;
+       char *buf;
+       int len;
+
+       if (*ppos != 0)
+               return 0;
+
+       if (count < HNS3_DBG_READ_LEN)
+               return -ENOSPC;
+
+       buf = kzalloc(HNS3_DBG_READ_LEN, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       len = snprintf(buf, HNS3_DBG_READ_LEN, "%s\n",
+                      "Please echo help to cmd to get help information");
+       uncopy_bytes = copy_to_user(buffer, buf, len);
+
+       kfree(buf);
+
+       if (uncopy_bytes)
+               return -EFAULT;
+
+       return (*ppos = len);
+}
+
+static ssize_t hns3_dbg_cmd_write(struct file *filp, const char __user *buffer,
+                                 size_t count, loff_t *ppos)
+{
+       struct hnae3_handle *handle = filp->private_data;
+       struct hns3_nic_priv *priv  = handle->priv;
+       char *cmd_buf, *cmd_buf_tmp;
+       int uncopied_bytes;
+       int ret = 0;
+
+       if (*ppos != 0)
+               return 0;
+
+       /* Judge if the instance is being reset. */
+       if (!test_bit(HNS3_NIC_STATE_INITED, &priv->state) ||
+           test_bit(HNS3_NIC_STATE_RESETTING, &priv->state))
+               return 0;
+
+       cmd_buf = kzalloc(count + 1, GFP_KERNEL);
+       if (!cmd_buf)
+               return count;
+
+       uncopied_bytes = copy_from_user(cmd_buf, buffer, count);
+       if (uncopied_bytes) {
+               kfree(cmd_buf);
+               return -EFAULT;
+       }
+
+       cmd_buf[count] = '\0';
+
+       cmd_buf_tmp = strchr(cmd_buf, '\n');
+       if (cmd_buf_tmp) {
+               *cmd_buf_tmp = '\0';
+               count = cmd_buf_tmp - cmd_buf + 1;
+       }
+
+       if (strncmp(cmd_buf, "help", 4) == 0)
+               hns3_dbg_help(handle);
+       else if (strncmp(cmd_buf, "queue info", 10) == 0)
+               ret = hns3_dbg_queue_info(handle, cmd_buf);
+       else if (strncmp(cmd_buf, "queue map", 9) == 0)
+               ret = hns3_dbg_queue_map(handle);
+       else if (strncmp(cmd_buf, "bd info", 7) == 0)
+               ret = hns3_dbg_bd_info(handle, cmd_buf);
+       else if (handle->ae_algo->ops->dbg_run_cmd)
+               ret = handle->ae_algo->ops->dbg_run_cmd(handle, cmd_buf);
+
+       if (ret)
+               hns3_dbg_help(handle);
+
+       kfree(cmd_buf);
+       cmd_buf = NULL;
+
+       return count;
+}
+
+static const struct file_operations hns3_dbg_cmd_fops = {
+       .owner = THIS_MODULE,
+       .open  = simple_open,
+       .read  = hns3_dbg_cmd_read,
+       .write = hns3_dbg_cmd_write,
+};
+
+void hns3_dbg_init(struct hnae3_handle *handle)
+{
+       const char *name = pci_name(handle->pdev);
+       struct dentry *pfile;
+
+       handle->hnae3_dbgfs = debugfs_create_dir(name, hns3_dbgfs_root);
+       if (!handle->hnae3_dbgfs)
+               return;
+
+       pfile = debugfs_create_file("cmd", 0600, handle->hnae3_dbgfs, handle,
+                                   &hns3_dbg_cmd_fops);
+       if (!pfile) {
+               debugfs_remove_recursive(handle->hnae3_dbgfs);
+               handle->hnae3_dbgfs = NULL;
+               dev_warn(&handle->pdev->dev, "create file for %s fail\n",
+                        name);
+       }
+}
+
+void hns3_dbg_uninit(struct hnae3_handle *handle)
+{
+       debugfs_remove_recursive(handle->hnae3_dbgfs);
+       handle->hnae3_dbgfs = NULL;
+}
+
+void hns3_dbg_register_debugfs(const char *debugfs_dir_name)
+{
+       hns3_dbgfs_root = debugfs_create_dir(debugfs_dir_name, NULL);
+       if (!hns3_dbgfs_root) {
+               pr_warn("Register debugfs for %s fail\n", debugfs_dir_name);
+               return;
+       }
+}
+
+void hns3_dbg_unregister_debugfs(void)
+{
+       debugfs_remove_recursive(hns3_dbgfs_root);
+       hns3_dbgfs_root = NULL;
+}
index 8d07ec6..d3b9aaf 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/vermagic.h>
 #include <net/gre.h>
 #include <net/pkt_cls.h>
+#include <net/tcp.h>
 #include <net/vxlan.h>
 
 #include "hnae3.h"
@@ -239,7 +240,6 @@ static void hns3_vector_gl_rl_init(struct hns3_enet_tqp_vector *tqp_vector,
        tqp_vector->tx_group.coal.int_gl = HNS3_INT_GL_50K;
        tqp_vector->rx_group.coal.int_gl = HNS3_INT_GL_50K;
 
-       tqp_vector->int_adapt_down = HNS3_INT_ADAPT_DOWN_START;
        tqp_vector->rx_group.coal.flow_level = HNS3_FLOW_LOW;
        tqp_vector->tx_group.coal.flow_level = HNS3_FLOW_LOW;
 }
@@ -379,6 +379,7 @@ out_start_err:
 
 static int hns3_nic_net_open(struct net_device *netdev)
 {
+       struct hns3_nic_priv *priv = netdev_priv(netdev);
        struct hnae3_handle *h = hns3_get_handle(netdev);
        struct hnae3_knic_private_info *kinfo;
        int i, ret;
@@ -405,6 +406,9 @@ static int hns3_nic_net_open(struct net_device *netdev)
                                       kinfo->prio_tc[i]);
        }
 
+       if (h->ae_algo->ops->set_timer_task)
+               h->ae_algo->ops->set_timer_task(priv->ae_handle, true);
+
        return 0;
 }
 
@@ -437,10 +441,14 @@ static void hns3_nic_net_down(struct net_device *netdev)
 static int hns3_nic_net_stop(struct net_device *netdev)
 {
        struct hns3_nic_priv *priv = netdev_priv(netdev);
+       struct hnae3_handle *h = hns3_get_handle(netdev);
 
        if (test_and_set_bit(HNS3_NIC_STATE_DOWN, &priv->state))
                return 0;
 
+       if (h->ae_algo->ops->set_timer_task)
+               h->ae_algo->ops->set_timer_task(priv->ae_handle, false);
+
        netif_tx_stop_all_queues(netdev);
        netif_carrier_off(netdev);
 
@@ -1345,6 +1353,15 @@ static int hns3_nic_set_features(struct net_device *netdev,
                        priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tx;
        }
 
+       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);
+               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)
@@ -1563,18 +1580,11 @@ static int hns3_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan,
 static int hns3_nic_change_mtu(struct net_device *netdev, int new_mtu)
 {
        struct hnae3_handle *h = hns3_get_handle(netdev);
-       bool if_running = netif_running(netdev);
        int ret;
 
        if (!h->ae_algo->ops->set_mtu)
                return -EOPNOTSUPP;
 
-       /* if this was called with netdev up then bring netdevice down */
-       if (if_running) {
-               (void)hns3_nic_net_stop(netdev);
-               msleep(100);
-       }
-
        ret = h->ae_algo->ops->set_mtu(h, new_mtu);
        if (ret)
                netdev_err(netdev, "failed to change MTU in hardware %d\n",
@@ -1582,10 +1592,6 @@ static int hns3_nic_change_mtu(struct net_device *netdev, int new_mtu)
        else
                netdev->mtu = new_mtu;
 
-       /* if the netdev was running earlier, bring it up again */
-       if (if_running && hns3_nic_net_open(netdev))
-               ret = -EINVAL;
-
        return ret;
 }
 
@@ -1714,8 +1720,10 @@ static void hns3_disable_sriov(struct pci_dev *pdev)
 static void hns3_get_dev_capability(struct pci_dev *pdev,
                                    struct hnae3_ae_dev *ae_dev)
 {
-       if (pdev->revision >= 0x21)
+       if (pdev->revision >= 0x21) {
                hnae3_set_bit(ae_dev->flag, HNAE3_DEV_SUPPORT_FD_B, 1);
+               hnae3_set_bit(ae_dev->flag, HNAE3_DEV_SUPPORT_GRO_B, 1);
+       }
 }
 
 /* hns3_probe - Device initialization routine
@@ -1827,8 +1835,8 @@ static pci_ers_result_t hns3_error_detected(struct pci_dev *pdev,
                return PCI_ERS_RESULT_NONE;
        }
 
-       if (ae_dev->ops->process_hw_error)
-               ret = ae_dev->ops->process_hw_error(ae_dev);
+       if (ae_dev->ops->handle_hw_ras_error)
+               ret = ae_dev->ops->handle_hw_ras_error(ae_dev);
        else
                return PCI_ERS_RESULT_NONE;
 
@@ -1927,7 +1935,9 @@ 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;
+               netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER |
+                       NETIF_F_GRO_HW;
+               netdev->features |= NETIF_F_GRO_HW;
 
                if (!(h->flags & HNAE3_SUPPORT_VF)) {
                        netdev->hw_features |= NETIF_F_NTUPLE;
@@ -2305,6 +2315,12 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
        if (!(netdev->features & NETIF_F_RXCSUM))
                return;
 
+       /* We MUST enable hardware checksum before enabling hardware GRO */
+       if (skb_shinfo(skb)->gso_size) {
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               return;
+       }
+
        /* check if hardware has done checksum */
        if (!hnae3_get_bit(bd_base_info, HNS3_RXD_L3L4P_B))
                return;
@@ -2348,6 +2364,9 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
 
 static void hns3_rx_skb(struct hns3_enet_ring *ring, struct sk_buff *skb)
 {
+       if (skb_has_frag_list(skb))
+               napi_gro_flush(&ring->tqp_vector->napi, false);
+
        napi_gro_receive(&ring->tqp_vector->napi, skb);
 }
 
@@ -2381,12 +2400,166 @@ static bool hns3_parse_vlan_tag(struct hns3_enet_ring *ring,
        }
 }
 
+static int hns3_alloc_skb(struct hns3_enet_ring *ring, int length,
+                         unsigned char *va)
+{
+#define HNS3_NEED_ADD_FRAG     1
+       struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_clean];
+       struct net_device *netdev = ring->tqp->handle->kinfo.netdev;
+       struct sk_buff *skb;
+
+       ring->skb = napi_alloc_skb(&ring->tqp_vector->napi, HNS3_RX_HEAD_SIZE);
+       skb = ring->skb;
+       if (unlikely(!skb)) {
+               netdev_err(netdev, "alloc rx skb fail\n");
+
+               u64_stats_update_begin(&ring->syncp);
+               ring->stats.sw_err_cnt++;
+               u64_stats_update_end(&ring->syncp);
+
+               return -ENOMEM;
+       }
+
+       prefetchw(skb->data);
+
+       ring->pending_buf = 1;
+       ring->frag_num = 0;
+       ring->tail_skb = NULL;
+       if (length <= HNS3_RX_HEAD_SIZE) {
+               memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long)));
+
+               /* We can reuse buffer as-is, just make sure it is local */
+               if (likely(page_to_nid(desc_cb->priv) == numa_node_id()))
+                       desc_cb->reuse_flag = 1;
+               else /* This page cannot be reused so discard it */
+                       put_page(desc_cb->priv);
+
+               ring_ptr_move_fw(ring, next_to_clean);
+               return 0;
+       }
+       u64_stats_update_begin(&ring->syncp);
+       ring->stats.seg_pkt_cnt++;
+       u64_stats_update_end(&ring->syncp);
+
+       ring->pull_len = eth_get_headlen(va, HNS3_RX_HEAD_SIZE);
+       __skb_put(skb, ring->pull_len);
+       hns3_nic_reuse_page(skb, ring->frag_num++, ring, ring->pull_len,
+                           desc_cb);
+       ring_ptr_move_fw(ring, next_to_clean);
+
+       return HNS3_NEED_ADD_FRAG;
+}
+
+static int hns3_add_frag(struct hns3_enet_ring *ring, struct hns3_desc *desc,
+                        struct sk_buff **out_skb, bool pending)
+{
+       struct sk_buff *skb = *out_skb;
+       struct sk_buff *head_skb = *out_skb;
+       struct sk_buff *new_skb;
+       struct hns3_desc_cb *desc_cb;
+       struct hns3_desc *pre_desc;
+       u32 bd_base_info;
+       int pre_bd;
+
+       /* if there is pending bd, the SW param next_to_clean has moved
+        * to next and the next is NULL
+        */
+       if (pending) {
+               pre_bd = (ring->next_to_clean - 1 + ring->desc_num) %
+                       ring->desc_num;
+               pre_desc = &ring->desc[pre_bd];
+               bd_base_info = le32_to_cpu(pre_desc->rx.bd_base_info);
+       } else {
+               bd_base_info = le32_to_cpu(desc->rx.bd_base_info);
+       }
+
+       while (!hnae3_get_bit(bd_base_info, HNS3_RXD_FE_B)) {
+               desc = &ring->desc[ring->next_to_clean];
+               desc_cb = &ring->desc_cb[ring->next_to_clean];
+               bd_base_info = le32_to_cpu(desc->rx.bd_base_info);
+               if (!hnae3_get_bit(bd_base_info, HNS3_RXD_VLD_B))
+                       return -ENXIO;
+
+               if (unlikely(ring->frag_num >= MAX_SKB_FRAGS)) {
+                       new_skb = napi_alloc_skb(&ring->tqp_vector->napi,
+                                                HNS3_RX_HEAD_SIZE);
+                       if (unlikely(!new_skb)) {
+                               netdev_err(ring->tqp->handle->kinfo.netdev,
+                                          "alloc rx skb frag fail\n");
+                               return -ENXIO;
+                       }
+                       ring->frag_num = 0;
+
+                       if (ring->tail_skb) {
+                               ring->tail_skb->next = new_skb;
+                               ring->tail_skb = new_skb;
+                       } else {
+                               skb_shinfo(skb)->frag_list = new_skb;
+                               ring->tail_skb = new_skb;
+                       }
+               }
+
+               if (ring->tail_skb) {
+                       head_skb->truesize += hnae3_buf_size(ring);
+                       head_skb->data_len += le16_to_cpu(desc->rx.size);
+                       head_skb->len += le16_to_cpu(desc->rx.size);
+                       skb = ring->tail_skb;
+               }
+
+               hns3_nic_reuse_page(skb, ring->frag_num++, ring, 0, desc_cb);
+               ring_ptr_move_fw(ring, next_to_clean);
+               ring->pending_buf++;
+       }
+
+       return 0;
+}
+
+static void hns3_set_gro_param(struct sk_buff *skb, u32 l234info,
+                              u32 bd_base_info)
+{
+       u16 gro_count;
+       u32 l3_type;
+
+       gro_count = hnae3_get_field(l234info, HNS3_RXD_GRO_COUNT_M,
+                                   HNS3_RXD_GRO_COUNT_S);
+       /* if there is no HW GRO, do not set gro params */
+       if (!gro_count)
+               return;
+
+       /* tcp_gro_complete() will copy NAPI_GRO_CB(skb)->count
+        * to skb_shinfo(skb)->gso_segs
+        */
+       NAPI_GRO_CB(skb)->count = gro_count;
+
+       l3_type = hnae3_get_field(l234info, HNS3_RXD_L3ID_M,
+                                 HNS3_RXD_L3ID_S);
+       if (l3_type == HNS3_L3_TYPE_IPV4)
+               skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+       else if (l3_type == HNS3_L3_TYPE_IPV6)
+               skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
+       else
+               return;
+
+       skb_shinfo(skb)->gso_size = hnae3_get_field(bd_base_info,
+                                                   HNS3_RXD_GRO_SIZE_M,
+                                                   HNS3_RXD_GRO_SIZE_S);
+       if (skb_shinfo(skb)->gso_size)
+               tcp_gro_complete(skb);
+}
+
 static void hns3_set_rx_skb_rss_type(struct hns3_enet_ring *ring,
                                     struct sk_buff *skb)
 {
-       struct hns3_desc *desc = &ring->desc[ring->next_to_clean];
        struct hnae3_handle *handle = ring->tqp->handle;
        enum pkt_hash_types rss_type;
+       struct hns3_desc *desc;
+       int last_bd;
+
+       /* When driver handle the rss type, ring->next_to_clean indicates the
+        * first descriptor of next packet, need -1 here.
+        */
+       last_bd = (ring->next_to_clean - 1 + ring->desc_num) % ring->desc_num;
+       desc = &ring->desc[last_bd];
 
        if (le32_to_cpu(desc->rx.rss_hash))
                rss_type = handle->kinfo.rss_type;
@@ -2397,18 +2570,16 @@ static void hns3_set_rx_skb_rss_type(struct hns3_enet_ring *ring,
 }
 
 static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
-                            struct sk_buff **out_skb, int *out_bnum)
+                            struct sk_buff **out_skb)
 {
        struct net_device *netdev = ring->tqp->handle->kinfo.netdev;
+       struct sk_buff *skb = ring->skb;
        struct hns3_desc_cb *desc_cb;
        struct hns3_desc *desc;
-       struct sk_buff *skb;
-       unsigned char *va;
        u32 bd_base_info;
-       int pull_len;
        u32 l234info;
        int length;
-       int bnum;
+       int ret;
 
        desc = &ring->desc[ring->next_to_clean];
        desc_cb = &ring->desc_cb[ring->next_to_clean];
@@ -2420,9 +2591,10 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
 
        /* Check valid BD */
        if (unlikely(!hnae3_get_bit(bd_base_info, HNS3_RXD_VLD_B)))
-               return -EFAULT;
+               return -ENXIO;
 
-       va = (unsigned char *)desc_cb->buf + desc_cb->page_offset;
+       if (!skb)
+               ring->va = (unsigned char *)desc_cb->buf + desc_cb->page_offset;
 
        /* Prefetch first cache line of first page
         * Idea is to cache few bytes of the header of the packet. Our L1 Cache
@@ -2431,62 +2603,42 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
         * lines. In such a case, single fetch would suffice to cache in the
         * relevant part of the header.
         */
-       prefetch(va);
+       prefetch(ring->va);
 #if L1_CACHE_BYTES < 128
-       prefetch(va + L1_CACHE_BYTES);
+       prefetch(ring->va + L1_CACHE_BYTES);
 #endif
 
-       skb = *out_skb = napi_alloc_skb(&ring->tqp_vector->napi,
-                                       HNS3_RX_HEAD_SIZE);
-       if (unlikely(!skb)) {
-               netdev_err(netdev, "alloc rx skb fail\n");
+       if (!skb) {
+               ret = hns3_alloc_skb(ring, length, ring->va);
+               *out_skb = skb = ring->skb;
 
-               u64_stats_update_begin(&ring->syncp);
-               ring->stats.sw_err_cnt++;
-               u64_stats_update_end(&ring->syncp);
-
-               return -ENOMEM;
-       }
-
-       prefetchw(skb->data);
-
-       bnum = 1;
-       if (length <= HNS3_RX_HEAD_SIZE) {
-               memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long)));
-
-               /* We can reuse buffer as-is, just make sure it is local */
-               if (likely(page_to_nid(desc_cb->priv) == numa_node_id()))
-                       desc_cb->reuse_flag = 1;
-               else /* This page cannot be reused so discard it */
-                       put_page(desc_cb->priv);
+               if (ret < 0) /* alloc buffer fail */
+                       return ret;
+               if (ret > 0) { /* need add frag */
+                       ret = hns3_add_frag(ring, desc, &skb, false);
+                       if (ret)
+                               return ret;
 
-               ring_ptr_move_fw(ring, next_to_clean);
+                       /* As the head data may be changed when GRO enable, copy
+                        * the head data in after other data rx completed
+                        */
+                       memcpy(skb->data, ring->va,
+                              ALIGN(ring->pull_len, sizeof(long)));
+               }
        } else {
-               u64_stats_update_begin(&ring->syncp);
-               ring->stats.seg_pkt_cnt++;
-               u64_stats_update_end(&ring->syncp);
-
-               pull_len = eth_get_headlen(va, HNS3_RX_HEAD_SIZE);
-
-               memcpy(__skb_put(skb, pull_len), va,
-                      ALIGN(pull_len, sizeof(long)));
-
-               hns3_nic_reuse_page(skb, 0, ring, pull_len, desc_cb);
-               ring_ptr_move_fw(ring, next_to_clean);
+               ret = hns3_add_frag(ring, desc, &skb, true);
+               if (ret)
+                       return ret;
 
-               while (!hnae3_get_bit(bd_base_info, HNS3_RXD_FE_B)) {
-                       desc = &ring->desc[ring->next_to_clean];
-                       desc_cb = &ring->desc_cb[ring->next_to_clean];
-                       bd_base_info = le32_to_cpu(desc->rx.bd_base_info);
-                       hns3_nic_reuse_page(skb, bnum, ring, 0, desc_cb);
-                       ring_ptr_move_fw(ring, next_to_clean);
-                       bnum++;
-               }
+               /* As the head data may be changed when GRO enable, copy
+                * the head data in after other data rx completed
+                */
+               memcpy(skb->data, ring->va,
+                      ALIGN(ring->pull_len, sizeof(long)));
        }
 
-       *out_bnum = bnum;
-
        l234info = le32_to_cpu(desc->rx.l234_info);
+       bd_base_info = le32_to_cpu(desc->rx.bd_base_info);
 
        /* Based on hw strategy, the tag offloaded will be stored at
         * ot_vlan_tag in two layer tag case, and stored at vlan_tag
@@ -2536,7 +2688,11 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
 
        ring->tqp_vector->rx_group.total_bytes += skb->len;
 
+       /* This is needed in order to enable forwarding support */
+       hns3_set_gro_param(skb, l234info, bd_base_info);
+
        hns3_rx_checksum(ring, skb, desc);
+       *out_skb = skb;
        hns3_set_rx_skb_rss_type(ring, skb);
 
        return 0;
@@ -2549,9 +2705,9 @@ int hns3_clean_rx_ring(
 #define RCB_NOF_ALLOC_RX_BUFF_ONCE 16
        struct net_device *netdev = ring->tqp->handle->kinfo.netdev;
        int recv_pkts, recv_bds, clean_count, err;
-       int unused_count = hns3_desc_unused(ring);
-       struct sk_buff *skb = NULL;
-       int num, bnum = 0;
+       int unused_count = hns3_desc_unused(ring) - ring->pending_buf;
+       struct sk_buff *skb = ring->skb;
+       int num;
 
        num = readl_relaxed(ring->tqp->io_base + HNS3_RING_RX_RING_FBDNUM_REG);
        rmb(); /* Make sure num taken effect before the other data is touched */
@@ -2565,24 +2721,32 @@ int hns3_clean_rx_ring(
                        hns3_nic_alloc_rx_buffers(ring,
                                                  clean_count + unused_count);
                        clean_count = 0;
-                       unused_count = hns3_desc_unused(ring);
+                       unused_count = hns3_desc_unused(ring) -
+                                       ring->pending_buf;
                }
 
                /* Poll one pkt */
-               err = hns3_handle_rx_bd(ring, &skb, &bnum);
+               err = hns3_handle_rx_bd(ring, &skb);
                if (unlikely(!skb)) /* This fault cannot be repaired */
                        goto out;
 
-               recv_bds += bnum;
-               clean_count += bnum;
-               if (unlikely(err)) {  /* Do jump the err */
-                       recv_pkts++;
+               if (err == -ENXIO) { /* Do not get FE for the packet */
+                       goto out;
+               } else if (unlikely(err)) {  /* Do jump the err */
+                       recv_bds += ring->pending_buf;
+                       clean_count += ring->pending_buf;
+                       ring->skb = NULL;
+                       ring->pending_buf = 0;
                        continue;
                }
 
                /* Do update ip stack process */
                skb->protocol = eth_type_trans(skb, netdev);
                rx_fn(ring, skb);
+               recv_bds += ring->pending_buf;
+               clean_count += ring->pending_buf;
+               ring->skb = NULL;
+               ring->pending_buf = 0;
 
                recv_pkts++;
        }
@@ -2696,10 +2860,10 @@ static void hns3_update_new_int_gl(struct hns3_enet_tqp_vector *tqp_vector)
        struct hns3_enet_ring_group *tx_group = &tqp_vector->tx_group;
        bool rx_update, tx_update;
 
-       if (tqp_vector->int_adapt_down > 0) {
-               tqp_vector->int_adapt_down--;
+       /* update param every 1000ms */
+       if (time_before(jiffies,
+                       tqp_vector->last_jiffies + msecs_to_jiffies(1000)))
                return;
-       }
 
        if (rx_group->coal.gl_adapt_enable) {
                rx_update = hns3_get_new_int_gl(rx_group);
@@ -2716,7 +2880,6 @@ static void hns3_update_new_int_gl(struct hns3_enet_tqp_vector *tqp_vector)
        }
 
        tqp_vector->last_jiffies = jiffies;
-       tqp_vector->int_adapt_down = HNS3_INT_ADAPT_DOWN_START;
 }
 
 static int hns3_nic_common_poll(struct napi_struct *napi, int budget)
@@ -2759,8 +2922,8 @@ static int hns3_nic_common_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
 
-       if (likely(!test_bit(HNS3_NIC_STATE_DOWN, &priv->state)) &&
-           napi_complete(napi)) {
+       if (napi_complete(napi) &&
+           likely(!test_bit(HNS3_NIC_STATE_DOWN, &priv->state))) {
                hns3_update_new_int_gl(tqp_vector);
                hns3_mask_vector_irq(tqp_vector, 1);
        }
@@ -2843,9 +3006,10 @@ err_free_chain:
        cur_chain = head->next;
        while (cur_chain) {
                chain = cur_chain->next;
-               devm_kfree(&pdev->dev, chain);
+               devm_kfree(&pdev->dev, cur_chain);
                cur_chain = chain;
        }
+       head->next = NULL;
 
        return -ENOMEM;
 }
@@ -2936,7 +3100,7 @@ static int hns3_nic_init_vector_data(struct hns3_nic_priv *priv)
                ret = hns3_get_vector_ring_chain(tqp_vector,
                                                 &vector_ring_chain);
                if (ret)
-                       return ret;
+                       goto map_ring_fail;
 
                ret = h->ae_algo->ops->map_ring_to_vector(h,
                        tqp_vector->vector_irq, &vector_ring_chain);
@@ -2961,6 +3125,8 @@ map_ring_fail:
 
 static int hns3_nic_alloc_vector_data(struct hns3_nic_priv *priv)
 {
+#define HNS3_VECTOR_PF_MAX_NUM         64
+
        struct hnae3_handle *h = priv->ae_handle;
        struct hns3_enet_tqp_vector *tqp_vector;
        struct hnae3_vector_info *vector;
@@ -2973,6 +3139,8 @@ static int hns3_nic_alloc_vector_data(struct hns3_nic_priv *priv)
        /* RSS size, cpu online and vector_num should be the same */
        /* Should consider 2p/4p later */
        vector_num = min_t(u16, num_online_cpus(), tqp_num);
+       vector_num = min_t(u16, vector_num, HNS3_VECTOR_PF_MAX_NUM);
+
        vector = devm_kcalloc(&pdev->dev, vector_num, sizeof(*vector),
                              GFP_KERNEL);
        if (!vector)
@@ -3030,12 +3198,12 @@ static int hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv)
 
                hns3_free_vector_ring_chain(tqp_vector, &vector_ring_chain);
 
-               if (priv->tqp_vector[i].irq_init_flag == HNS3_VECTOR_INITED) {
-                       (void)irq_set_affinity_hint(
-                               priv->tqp_vector[i].vector_irq,
-                                                   NULL);
-                       free_irq(priv->tqp_vector[i].vector_irq,
-                                &priv->tqp_vector[i]);
+               if (tqp_vector->irq_init_flag == HNS3_VECTOR_INITED) {
+                       irq_set_affinity_notifier(tqp_vector->vector_irq,
+                                                 NULL);
+                       irq_set_affinity_hint(tqp_vector->vector_irq, NULL);
+                       free_irq(tqp_vector->vector_irq, tqp_vector);
+                       tqp_vector->irq_init_flag = HNS3_VECTOR_NOT_INITED;
                }
 
                priv->ring_data[i].ring->irq_init_flag = HNS3_VECTOR_NOT_INITED;
@@ -3379,6 +3547,22 @@ static void hns3_nic_set_priv_ops(struct net_device *netdev)
                priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tx;
 }
 
+static int hns3_client_start(struct hnae3_handle *handle)
+{
+       if (!handle->ae_algo->ops->client_start)
+               return 0;
+
+       return handle->ae_algo->ops->client_start(handle);
+}
+
+static void hns3_client_stop(struct hnae3_handle *handle)
+{
+       if (!handle->ae_algo->ops->client_stop)
+               return;
+
+       handle->ae_algo->ops->client_stop(handle);
+}
+
 static int hns3_client_init(struct hnae3_handle *handle)
 {
        struct pci_dev *pdev = handle->pdev;
@@ -3446,10 +3630,18 @@ static int hns3_client_init(struct hnae3_handle *handle)
                goto out_reg_netdev_fail;
        }
 
+       ret = hns3_client_start(handle);
+       if (ret) {
+               dev_err(priv->dev, "hns3_client_start fail! ret=%d\n", ret);
+                       goto out_reg_netdev_fail;
+       }
+
        hns3_dcbnl_setup(handle);
 
-       /* MTU range: (ETH_MIN_MTU(kernel default) - 9706) */
-       netdev->max_mtu = HNS3_MAX_MTU - (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN);
+       hns3_dbg_init(handle);
+
+       /* MTU range: (ETH_MIN_MTU(kernel default) - 9702) */
+       netdev->max_mtu = HNS3_MAX_MTU;
 
        set_bit(HNS3_NIC_STATE_INITED, &priv->state);
 
@@ -3474,6 +3666,8 @@ static void hns3_client_uninit(struct hnae3_handle *handle, bool reset)
        struct hns3_nic_priv *priv = netdev_priv(netdev);
        int ret;
 
+       hns3_client_stop(handle);
+
        hns3_remove_hw_addr(netdev);
 
        if (netdev->reg_state != NETREG_UNINITIALIZED)
@@ -3502,6 +3696,8 @@ static void hns3_client_uninit(struct hnae3_handle *handle, bool reset)
 
        hns3_put_ring_config(priv);
 
+       hns3_dbg_uninit(handle);
+
        priv->ring_data = NULL;
 
 out_netdev_free:
@@ -4071,15 +4267,23 @@ static int __init hns3_init_module(void)
 
        INIT_LIST_HEAD(&client.node);
 
+       hns3_dbg_register_debugfs(hns3_driver_name);
+
        ret = hnae3_register_client(&client);
        if (ret)
-               return ret;
+               goto err_reg_client;
 
        ret = pci_register_driver(&hns3_driver);
        if (ret)
-               hnae3_unregister_client(&client);
+               goto err_reg_driver;
 
        return ret;
+
+err_reg_driver:
+       hnae3_unregister_client(&client);
+err_reg_client:
+       hns3_dbg_unregister_debugfs();
+       return ret;
 }
 module_init(hns3_init_module);
 
@@ -4091,6 +4295,7 @@ static void __exit hns3_exit_module(void)
 {
        pci_unregister_driver(&hns3_driver);
        hnae3_unregister_client(&client);
+       hns3_dbg_unregister_debugfs();
 }
 module_exit(hns3_exit_module);
 
index 10ff18a..e55995e 100644 (file)
@@ -76,7 +76,10 @@ enum hns3_nic_state {
 #define HNS3_RING_MAX_PENDING                  32768
 #define HNS3_RING_MIN_PENDING                  8
 #define HNS3_RING_BD_MULTIPLE                  8
-#define HNS3_MAX_MTU                           9728
+/* max frame size of mac */
+#define HNS3_MAC_MAX_FRAME                     9728
+#define HNS3_MAX_MTU \
+       (HNS3_MAC_MAX_FRAME - (ETH_HLEN + ETH_FCS_LEN + 2 * VLAN_HLEN))
 
 #define HNS3_BD_SIZE_512_TYPE                  0
 #define HNS3_BD_SIZE_1024_TYPE                 1
@@ -109,6 +112,10 @@ enum hns3_nic_state {
 #define HNS3_RXD_DOI_B                         21
 #define HNS3_RXD_OL3E_B                                22
 #define HNS3_RXD_OL4E_B                                23
+#define HNS3_RXD_GRO_COUNT_S                   24
+#define HNS3_RXD_GRO_COUNT_M                   (0x3f << HNS3_RXD_GRO_COUNT_S)
+#define HNS3_RXD_GRO_FIXID_B                   30
+#define HNS3_RXD_GRO_ECN_B                     31
 
 #define HNS3_RXD_ODMAC_S                       0
 #define HNS3_RXD_ODMAC_M                       (0x3 << HNS3_RXD_ODMAC_S)
@@ -135,9 +142,8 @@ enum hns3_nic_state {
 #define HNS3_RXD_TSIND_S                       12
 #define HNS3_RXD_TSIND_M                       (0x7 << HNS3_RXD_TSIND_S)
 #define HNS3_RXD_LKBK_B                                15
-#define HNS3_RXD_HDL_S                         16
-#define HNS3_RXD_HDL_M                         (0x7ff << HNS3_RXD_HDL_S)
-#define HNS3_RXD_HSIND_B                       31
+#define HNS3_RXD_GRO_SIZE_S                    16
+#define HNS3_RXD_GRO_SIZE_M                    (0x3ff << HNS3_RXD_GRO_SIZE_S)
 
 #define HNS3_TXD_L3T_S                         0
 #define HNS3_TXD_L3T_M                         (0x3 << HNS3_TXD_L3T_S)
@@ -401,11 +407,19 @@ struct hns3_enet_ring {
         */
        int next_to_clean;
 
+       int pull_len; /* head length for current packet */
+       u32 frag_num;
+       unsigned char *va; /* first buffer address for current packet */
+
        u32 flag;          /* ring attribute */
        int irq_init_flag;
 
        int numa_node;
        cpumask_t affinity_mask;
+
+       int pending_buf;
+       struct sk_buff *skb;
+       struct sk_buff *tail_skb;
 };
 
 struct hns_queue;
@@ -462,8 +476,6 @@ enum hns3_link_mode_bits {
 #define HNS3_INT_RL_MAX                        0x00EC
 #define HNS3_INT_RL_ENABLE_MASK                0x40
 
-#define HNS3_INT_ADAPT_DOWN_START      100
-
 struct hns3_enet_coalesce {
        u16 int_gl;
        u8 gl_adapt_enable;
@@ -498,8 +510,6 @@ struct hns3_enet_tqp_vector {
 
        char name[HNAE3_INT_NAME_LEN];
 
-       /* when 0 should adjust interrupt coalesce parameter */
-       u8 int_adapt_down;
        unsigned long last_jiffies;
 } ____cacheline_internodealigned_in_smp;
 
@@ -669,4 +679,8 @@ void hns3_dcbnl_setup(struct hnae3_handle *handle);
 static inline void hns3_dcbnl_setup(struct hnae3_handle *handle) {}
 #endif
 
+void hns3_dbg_init(struct hnae3_handle *handle);
+void hns3_dbg_uninit(struct hnae3_handle *handle);
+void hns3_dbg_register_debugfs(const char *debugfs_dir_name);
+void hns3_dbg_unregister_debugfs(void);
 #endif
index 4563638..e678b69 100644 (file)
@@ -821,7 +821,7 @@ static int hns3_set_ringparam(struct net_device *ndev,
        }
 
        if (if_running)
-               ret = dev_open(ndev);
+               ret = dev_open(ndev, NULL);
 
        return ret;
 }
index 580e817..fffe8c1 100644 (file)
@@ -6,6 +6,6 @@
 ccflags-y := -Idrivers/net/ethernet/hisilicon/hns3
 
 obj-$(CONFIG_HNS3_HCLGE) += hclge.o
-hclge-objs = hclge_main.o hclge_cmd.o hclge_mdio.o hclge_tm.o hclge_mbx.o hclge_err.o
+hclge-objs = hclge_main.o hclge_cmd.o hclge_mdio.o hclge_tm.o hclge_mbx.o hclge_err.o  hclge_debugfs.o
 
 hclge-$(CONFIG_HNS3_DCB) += hclge_dcb.o
index 872cd4b..f23042b 100644 (file)
@@ -86,11 +86,24 @@ enum hclge_opcode_type {
        HCLGE_OPC_QUERY_REG_NUM         = 0x0040,
        HCLGE_OPC_QUERY_32_BIT_REG      = 0x0041,
        HCLGE_OPC_QUERY_64_BIT_REG      = 0x0042,
+       HCLGE_OPC_DFX_BD_NUM            = 0x0043,
+       HCLGE_OPC_DFX_BIOS_COMMON_REG   = 0x0044,
+       HCLGE_OPC_DFX_SSU_REG_0         = 0x0045,
+       HCLGE_OPC_DFX_SSU_REG_1         = 0x0046,
+       HCLGE_OPC_DFX_IGU_EGU_REG       = 0x0047,
+       HCLGE_OPC_DFX_RPU_REG_0         = 0x0048,
+       HCLGE_OPC_DFX_RPU_REG_1         = 0x0049,
+       HCLGE_OPC_DFX_NCSI_REG          = 0x004A,
+       HCLGE_OPC_DFX_RTC_REG           = 0x004B,
+       HCLGE_OPC_DFX_PPP_REG           = 0x004C,
+       HCLGE_OPC_DFX_RCB_REG           = 0x004D,
+       HCLGE_OPC_DFX_TQP_REG           = 0x004E,
+       HCLGE_OPC_DFX_SSU_REG_2         = 0x004F,
+       HCLGE_OPC_DFX_QUERY_CHIP_CAP    = 0x0050,
 
        /* MAC command */
        HCLGE_OPC_CONFIG_MAC_MODE       = 0x0301,
        HCLGE_OPC_CONFIG_AN_MODE        = 0x0304,
-       HCLGE_OPC_QUERY_AN_RESULT       = 0x0306,
        HCLGE_OPC_QUERY_LINK_STATUS     = 0x0307,
        HCLGE_OPC_CONFIG_MAX_FRM_SIZE   = 0x0308,
        HCLGE_OPC_CONFIG_SPEED_DUP      = 0x0309,
@@ -126,6 +139,16 @@ enum hclge_opcode_type {
        HCLGE_OPC_TM_PRI_SCH_MODE_CFG   = 0x0813,
        HCLGE_OPC_TM_QS_SCH_MODE_CFG    = 0x0814,
        HCLGE_OPC_TM_BP_TO_QSET_MAPPING = 0x0815,
+       HCLGE_OPC_ETS_TC_WEIGHT         = 0x0843,
+       HCLGE_OPC_QSET_DFX_STS          = 0x0844,
+       HCLGE_OPC_PRI_DFX_STS           = 0x0845,
+       HCLGE_OPC_PG_DFX_STS            = 0x0846,
+       HCLGE_OPC_PORT_DFX_STS          = 0x0847,
+       HCLGE_OPC_SCH_NQ_CNT            = 0x0848,
+       HCLGE_OPC_SCH_RQ_CNT            = 0x0849,
+       HCLGE_OPC_TM_INTERNAL_STS       = 0x0850,
+       HCLGE_OPC_TM_INTERNAL_CNT       = 0x0851,
+       HCLGE_OPC_TM_INTERNAL_STS_1     = 0x0852,
 
        /* Packet buffer allocate commands */
        HCLGE_OPC_TX_BUFF_ALLOC         = 0x0901,
@@ -142,6 +165,7 @@ enum hclge_opcode_type {
        HCLGE_OPC_CFG_TX_QUEUE          = 0x0B01,
        HCLGE_OPC_QUERY_TX_POINTER      = 0x0B02,
        HCLGE_OPC_QUERY_TX_STATUS       = 0x0B03,
+       HCLGE_OPC_TQP_TX_QUEUE_TC       = 0x0B04,
        HCLGE_OPC_CFG_RX_QUEUE          = 0x0B11,
        HCLGE_OPC_QUERY_RX_POINTER      = 0x0B12,
        HCLGE_OPC_QUERY_RX_STATUS       = 0x0B13,
@@ -152,6 +176,7 @@ enum hclge_opcode_type {
 
        /* TSO command */
        HCLGE_OPC_TSO_GENERIC_CONFIG    = 0x0C01,
+       HCLGE_OPC_GRO_GENERIC_CONFIG    = 0x0C10,
 
        /* RSS commands */
        HCLGE_OPC_RSS_GENERIC_CONFIG    = 0x0D01,
@@ -210,27 +235,34 @@ enum hclge_opcode_type {
        /* Led command */
        HCLGE_OPC_LED_STATUS_CFG        = 0xB000,
 
+       /* SFP command */
+       HCLGE_OPC_SFP_GET_SPEED         = 0x7104,
+
        /* Error INT commands */
+       HCLGE_MAC_COMMON_INT_EN         = 0x030E,
        HCLGE_TM_SCH_ECC_INT_EN         = 0x0829,
-       HCLGE_TM_SCH_ECC_ERR_RINT_CMD   = 0x082d,
-       HCLGE_TM_SCH_ECC_ERR_RINT_CE    = 0x082f,
-       HCLGE_TM_SCH_ECC_ERR_RINT_NFE   = 0x0830,
-       HCLGE_TM_SCH_ECC_ERR_RINT_FE    = 0x0831,
-       HCLGE_TM_SCH_MBIT_ECC_INFO_CMD  = 0x0833,
+       HCLGE_SSU_ECC_INT_CMD           = 0x0989,
+       HCLGE_SSU_COMMON_INT_CMD        = 0x098C,
+       HCLGE_PPU_MPF_ECC_INT_CMD       = 0x0B40,
+       HCLGE_PPU_MPF_OTHER_INT_CMD     = 0x0B41,
+       HCLGE_PPU_PF_OTHER_INT_CMD      = 0x0B42,
        HCLGE_COMMON_ECC_INT_CFG        = 0x1505,
-       HCLGE_IGU_EGU_TNL_INT_QUERY     = 0x1802,
+       HCLGE_QUERY_RAS_INT_STS_BD_NUM  = 0x1510,
+       HCLGE_QUERY_CLEAR_MPF_RAS_INT   = 0x1511,
+       HCLGE_QUERY_CLEAR_PF_RAS_INT    = 0x1512,
+       HCLGE_QUERY_MSIX_INT_STS_BD_NUM = 0x1513,
+       HCLGE_QUERY_CLEAR_ALL_MPF_MSIX_INT      = 0x1514,
+       HCLGE_QUERY_CLEAR_ALL_PF_MSIX_INT       = 0x1515,
+       HCLGE_CONFIG_ROCEE_RAS_INT_EN   = 0x1580,
+       HCLGE_QUERY_CLEAR_ROCEE_RAS_INT = 0x1581,
+       HCLGE_ROCEE_PF_RAS_INT_CMD      = 0x1584,
        HCLGE_IGU_EGU_TNL_INT_EN        = 0x1803,
-       HCLGE_IGU_EGU_TNL_INT_CLR       = 0x1804,
-       HCLGE_IGU_COMMON_INT_QUERY      = 0x1805,
        HCLGE_IGU_COMMON_INT_EN         = 0x1806,
-       HCLGE_IGU_COMMON_INT_CLR        = 0x1807,
        HCLGE_TM_QCN_MEM_INT_CFG        = 0x1A14,
-       HCLGE_TM_QCN_MEM_INT_INFO_CMD   = 0x1A17,
        HCLGE_PPP_CMD0_INT_CMD          = 0x2100,
        HCLGE_PPP_CMD1_INT_CMD          = 0x2101,
-       HCLGE_NCSI_INT_QUERY            = 0x2400,
+       HCLGE_MAC_ETHERTYPE_IDX_RD      = 0x2105,
        HCLGE_NCSI_INT_EN               = 0x2401,
-       HCLGE_NCSI_INT_CLR              = 0x2402,
 };
 
 #define HCLGE_TQP_REG_OFFSET           0x80000
@@ -388,7 +420,9 @@ struct hclge_pf_res_cmd {
 #define HCLGE_PF_VEC_NUM_M             GENMASK(7, 0)
        __le16 pf_intr_vector_number;
        __le16 pf_own_fun_number;
-       __le32 rsv[3];
+       __le16 tx_buf_size;
+       __le16 dv_buf_size;
+       __le32 rsv[2];
 };
 
 #define HCLGE_CFG_OFFSET_S     0
@@ -542,20 +576,6 @@ struct hclge_config_mac_speed_dup_cmd {
        u8 rsv[22];
 };
 
-#define HCLGE_QUERY_SPEED_S            3
-#define HCLGE_QUERY_AN_B               0
-#define HCLGE_QUERY_DUPLEX_B           2
-
-#define HCLGE_QUERY_SPEED_M            GENMASK(4, 0)
-#define HCLGE_QUERY_AN_M               BIT(HCLGE_QUERY_AN_B)
-#define HCLGE_QUERY_DUPLEX_M           BIT(HCLGE_QUERY_DUPLEX_B)
-
-struct hclge_query_an_speed_dup_cmd {
-       u8 an_syn_dup_speed;
-       u8 pause;
-       u8 rsv[23];
-};
-
 #define HCLGE_RING_ID_MASK             GENMASK(9, 0)
 #define HCLGE_TQP_ENABLE_B             0
 
@@ -572,6 +592,11 @@ struct hclge_config_auto_neg_cmd {
        u8      rsv[20];
 };
 
+struct hclge_sfp_speed_cmd {
+       __le32  sfp_speed;
+       u32     rsv[5];
+};
+
 #define HCLGE_MAC_UPLINK_PORT          0x100
 
 struct hclge_config_max_frm_size_cmd {
@@ -746,6 +771,24 @@ struct hclge_cfg_tx_queue_pointer_cmd {
        u8 rsv[14];
 };
 
+#pragma pack(1)
+struct hclge_mac_ethertype_idx_rd_cmd {
+       u8      flags;
+       u8      resp_code;
+       __le16  vlan_tag;
+       u8      mac_add[6];
+       __le16  index;
+       __le16  ethter_type;
+       __le16  egress_port;
+       __le16  egress_queue;
+       __le16  rev0;
+       u8      i_port_bitmap;
+       u8      i_port_direction;
+       u8      rev1[2];
+};
+
+#pragma pack()
+
 #define HCLGE_TSO_MSS_MIN_S    0
 #define HCLGE_TSO_MSS_MIN_M    GENMASK(13, 0)
 
@@ -758,6 +801,12 @@ struct hclge_cfg_tso_status_cmd {
        u8 rsv[20];
 };
 
+#define HCLGE_GRO_EN_B         0
+struct hclge_cfg_gro_status_cmd {
+       __le16 gro_en;
+       u8 rsv[22];
+};
+
 #define HCLGE_TSO_MSS_MIN      256
 #define HCLGE_TSO_MSS_MAX      9668
 
@@ -792,6 +841,7 @@ struct hclge_serdes_lb_cmd {
 #define HCLGE_TOTAL_PKT_BUF            0x108000 /* 1.03125M bytes */
 #define HCLGE_DEFAULT_DV               0xA000   /* 40k byte */
 #define HCLGE_DEFAULT_NON_DCB_DV       0x7800  /* 30K byte */
+#define HCLGE_NON_DCB_ADDITIONAL_BUF   0x200   /* 512 byte */
 
 #define HCLGE_TYPE_CRQ                 0
 #define HCLGE_TYPE_CSQ                 1
index e72f724..f6323b2 100644 (file)
@@ -35,7 +35,9 @@ static int hclge_ieee_ets_to_tm_info(struct hclge_dev *hdev,
                }
        }
 
-       return hclge_tm_prio_tc_info_update(hdev, ets->prio_tc);
+       hclge_tm_prio_tc_info_update(hdev, ets->prio_tc);
+
+       return 0;
 }
 
 static void hclge_tm_info_to_ieee_ets(struct hclge_dev *hdev,
@@ -70,25 +72,61 @@ static int hclge_ieee_getets(struct hnae3_handle *h, struct ieee_ets *ets)
        return 0;
 }
 
+static int hclge_dcb_common_validate(struct hclge_dev *hdev, u8 num_tc,
+                                    u8 *prio_tc)
+{
+       int i;
+
+       if (num_tc > hdev->tc_max) {
+               dev_err(&hdev->pdev->dev,
+                       "tc num checking failed, %u > tc_max(%u)\n",
+                       num_tc, hdev->tc_max);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < HNAE3_MAX_USER_PRIO; i++) {
+               if (prio_tc[i] >= num_tc) {
+                       dev_err(&hdev->pdev->dev,
+                               "prio_tc[%u] checking failed, %u >= num_tc(%u)\n",
+                               i, prio_tc[i], num_tc);
+                       return -EINVAL;
+               }
+       }
+
+       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;
+               }
+       }
+
+       return 0;
+}
+
 static int hclge_ets_validate(struct hclge_dev *hdev, struct ieee_ets *ets,
                              u8 *tc, bool *changed)
 {
        bool has_ets_tc = false;
        u32 total_ets_bw = 0;
        u8 max_tc = 0;
+       int ret;
        u8 i;
 
-       for (i = 0; i < HNAE3_MAX_TC; i++) {
-               if (ets->prio_tc[i] >= hdev->tc_max ||
-                   i >= hdev->tc_max)
-                       return -EINVAL;
-
+       for (i = 0; i < HNAE3_MAX_USER_PRIO; i++) {
                if (ets->prio_tc[i] != hdev->tm_info.prio_tc[i])
                        *changed = true;
 
                if (ets->prio_tc[i] > max_tc)
                        max_tc = ets->prio_tc[i];
+       }
 
+       ret = hclge_dcb_common_validate(hdev, max_tc + 1, ets->prio_tc);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < HNAE3_MAX_TC; i++) {
                switch (ets->tc_tsa[i]) {
                case IEEE_8021QAZ_TSA_STRICT:
                        if (hdev->tm_info.tc_info[i].tc_sch_mode !=
@@ -184,9 +222,7 @@ static int hclge_ieee_setets(struct hnae3_handle *h, struct ieee_ets *ets)
        if (ret)
                return ret;
 
-       ret = hclge_tm_schd_info_update(hdev, num_tc);
-       if (ret)
-               return ret;
+       hclge_tm_schd_info_update(hdev, num_tc);
 
        ret = hclge_ieee_ets_to_tm_info(hdev, ets);
        if (ret)
@@ -305,20 +341,12 @@ static int hclge_setup_tc(struct hnae3_handle *h, u8 tc, u8 *prio_tc)
        if (hdev->flag & HCLGE_FLAG_DCB_ENABLE)
                return -EINVAL;
 
-       if (tc > hdev->tc_max) {
-               dev_err(&hdev->pdev->dev,
-                       "setup tc failed, tc(%u) > tc_max(%u)\n",
-                       tc, hdev->tc_max);
-               return -EINVAL;
-       }
-
-       ret = hclge_tm_schd_info_update(hdev, tc);
+       ret = hclge_dcb_common_validate(hdev, tc, prio_tc);
        if (ret)
-               return ret;
+               return -EINVAL;
 
-       ret = hclge_tm_prio_tc_info_update(hdev, prio_tc);
-       if (ret)
-               return ret;
+       hclge_tm_schd_info_update(hdev, tc);
+       hclge_tm_prio_tc_info_update(hdev, prio_tc);
 
        ret = hclge_tm_init_hw(hdev);
        if (ret)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c
new file mode 100644 (file)
index 0000000..26d8050
--- /dev/null
@@ -0,0 +1,933 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2018-2019 Hisilicon Limited. */
+
+#include <linux/device.h>
+
+#include "hclge_debugfs.h"
+#include "hclge_cmd.h"
+#include "hclge_main.h"
+#include "hclge_tm.h"
+#include "hnae3.h"
+
+static int hclge_dbg_get_dfx_bd_num(struct hclge_dev *hdev, int offset)
+{
+       struct hclge_desc desc[4];
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_DFX_BD_NUM, true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_DFX_BD_NUM, true);
+       desc[1].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_DFX_BD_NUM, true);
+       desc[2].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[3], HCLGE_OPC_DFX_BD_NUM, true);
+
+       ret = hclge_cmd_send(&hdev->hw, desc, 4);
+       if (ret != HCLGE_CMD_EXEC_SUCCESS) {
+               dev_err(&hdev->pdev->dev,
+                       "get dfx bdnum fail, status is %d.\n", ret);
+               return ret;
+       }
+
+       return (int)desc[offset / 6].data[offset % 6];
+}
+
+static int hclge_dbg_cmd_send(struct hclge_dev *hdev,
+                             struct hclge_desc *desc_src,
+                             int index, int bd_num,
+                             enum hclge_opcode_type cmd)
+{
+       struct hclge_desc *desc = desc_src;
+       int ret, i;
+
+       hclge_cmd_setup_basic_desc(desc, cmd, true);
+       desc->data[0] = cpu_to_le32(index);
+
+       for (i = 1; i < bd_num; i++) {
+               desc->flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+               desc++;
+               hclge_cmd_setup_basic_desc(desc, cmd, true);
+       }
+
+       ret = hclge_cmd_send(&hdev->hw, desc_src, bd_num);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "read reg cmd send fail, status is %d.\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static void hclge_dbg_dump_reg_common(struct hclge_dev *hdev,
+                                     struct hclge_dbg_dfx_message *dfx_message,
+                                     char *cmd_buf, int msg_num, int offset,
+                                     enum hclge_opcode_type cmd)
+{
+       struct hclge_desc *desc_src;
+       struct hclge_desc *desc;
+       int bd_num, buf_len;
+       int ret, i;
+       int index;
+       int max;
+
+       ret = kstrtouint(cmd_buf, 10, &index);
+       index = (ret != 0) ? 0 : index;
+
+       bd_num = hclge_dbg_get_dfx_bd_num(hdev, offset);
+       if (bd_num <= 0)
+               return;
+
+       buf_len  = sizeof(struct hclge_desc) * bd_num;
+       desc_src = kzalloc(buf_len, GFP_KERNEL);
+       if (!desc_src) {
+               dev_err(&hdev->pdev->dev, "call kzalloc failed\n");
+               return;
+       }
+
+       desc = desc_src;
+       ret  = hclge_dbg_cmd_send(hdev, desc, index, bd_num, cmd);
+       if (ret != HCLGE_CMD_EXEC_SUCCESS) {
+               kfree(desc_src);
+               return;
+       }
+
+       max = (bd_num * 6) <= msg_num ? (bd_num * 6) : msg_num;
+
+       desc = desc_src;
+       for (i = 0; i < max; i++) {
+               (((i / 6) > 0) && ((i % 6) == 0)) ? desc++ : desc;
+               if (dfx_message->flag)
+                       dev_info(&hdev->pdev->dev, "%s: 0x%x\n",
+                                dfx_message->message, desc->data[i % 6]);
+
+               dfx_message++;
+       }
+
+       kfree(desc_src);
+}
+
+static void hclge_dbg_dump_dcb(struct hclge_dev *hdev, char *cmd_buf)
+{
+       struct device *dev = &hdev->pdev->dev;
+       struct hclge_dbg_bitmap_cmd *bitmap;
+       int rq_id, pri_id, qset_id;
+       int port_id, nq_id, pg_id;
+       struct hclge_desc desc[2];
+
+       int cnt, ret;
+
+       cnt = sscanf(cmd_buf, "%i %i %i %i %i %i",
+                    &port_id, &pri_id, &pg_id, &rq_id, &nq_id, &qset_id);
+       if (cnt != 6) {
+               dev_err(&hdev->pdev->dev,
+                       "dump dcb: bad command parameter, cnt=%d\n", cnt);
+               return;
+       }
+
+       ret = hclge_dbg_cmd_send(hdev, desc, qset_id, 1,
+                                HCLGE_OPC_QSET_DFX_STS);
+       if (ret)
+               return;
+
+       bitmap = (struct hclge_dbg_bitmap_cmd *)&desc[0].data[1];
+       dev_info(dev, "roce_qset_mask: 0x%x\n", bitmap->bit0);
+       dev_info(dev, "nic_qs_mask: 0x%x\n", bitmap->bit1);
+       dev_info(dev, "qs_shaping_pass: 0x%x\n", bitmap->bit2);
+       dev_info(dev, "qs_bp_sts: 0x%x\n", bitmap->bit3);
+
+       ret = hclge_dbg_cmd_send(hdev, desc, pri_id, 1, HCLGE_OPC_PRI_DFX_STS);
+       if (ret)
+               return;
+
+       bitmap = (struct hclge_dbg_bitmap_cmd *)&desc[0].data[1];
+       dev_info(dev, "pri_mask: 0x%x\n", bitmap->bit0);
+       dev_info(dev, "pri_cshaping_pass: 0x%x\n", bitmap->bit1);
+       dev_info(dev, "pri_pshaping_pass: 0x%x\n", bitmap->bit2);
+
+       ret = hclge_dbg_cmd_send(hdev, desc, pg_id, 1, HCLGE_OPC_PG_DFX_STS);
+       if (ret)
+               return;
+
+       bitmap = (struct hclge_dbg_bitmap_cmd *)&desc[0].data[1];
+       dev_info(dev, "pg_mask: 0x%x\n", bitmap->bit0);
+       dev_info(dev, "pg_cshaping_pass: 0x%x\n", bitmap->bit1);
+       dev_info(dev, "pg_pshaping_pass: 0x%x\n", bitmap->bit2);
+
+       ret = hclge_dbg_cmd_send(hdev, desc, port_id, 1,
+                                HCLGE_OPC_PORT_DFX_STS);
+       if (ret)
+               return;
+
+       bitmap = (struct hclge_dbg_bitmap_cmd *)&desc[0].data[1];
+       dev_info(dev, "port_mask: 0x%x\n", bitmap->bit0);
+       dev_info(dev, "port_shaping_pass: 0x%x\n", bitmap->bit1);
+
+       ret = hclge_dbg_cmd_send(hdev, desc, nq_id, 1, HCLGE_OPC_SCH_NQ_CNT);
+       if (ret)
+               return;
+
+       dev_info(dev, "sch_nq_cnt: 0x%x\n", desc[0].data[1]);
+
+       ret = hclge_dbg_cmd_send(hdev, desc, nq_id, 1, HCLGE_OPC_SCH_RQ_CNT);
+       if (ret)
+               return;
+
+       dev_info(dev, "sch_rq_cnt: 0x%x\n", desc[0].data[1]);
+
+       ret = hclge_dbg_cmd_send(hdev, desc, 0, 2, HCLGE_OPC_TM_INTERNAL_STS);
+       if (ret)
+               return;
+
+       dev_info(dev, "pri_bp: 0x%x\n", desc[0].data[1]);
+       dev_info(dev, "fifo_dfx_info: 0x%x\n", desc[0].data[2]);
+       dev_info(dev, "sch_roce_fifo_afull_gap: 0x%x\n", desc[0].data[3]);
+       dev_info(dev, "tx_private_waterline: 0x%x\n", desc[0].data[4]);
+       dev_info(dev, "tm_bypass_en: 0x%x\n", desc[0].data[5]);
+       dev_info(dev, "SSU_TM_BYPASS_EN: 0x%x\n", desc[1].data[0]);
+       dev_info(dev, "SSU_RESERVE_CFG: 0x%x\n", desc[1].data[1]);
+
+       ret = hclge_dbg_cmd_send(hdev, desc, port_id, 1,
+                                HCLGE_OPC_TM_INTERNAL_CNT);
+       if (ret)
+               return;
+
+       dev_info(dev, "SCH_NIC_NUM: 0x%x\n", desc[0].data[1]);
+       dev_info(dev, "SCH_ROCE_NUM: 0x%x\n", desc[0].data[2]);
+
+       ret = hclge_dbg_cmd_send(hdev, desc, port_id, 1,
+                                HCLGE_OPC_TM_INTERNAL_STS_1);
+       if (ret)
+               return;
+
+       dev_info(dev, "TC_MAP_SEL: 0x%x\n", desc[0].data[1]);
+       dev_info(dev, "IGU_PFC_PRI_EN: 0x%x\n", desc[0].data[2]);
+       dev_info(dev, "MAC_PFC_PRI_EN: 0x%x\n", desc[0].data[3]);
+       dev_info(dev, "IGU_PRI_MAP_TC_CFG: 0x%x\n", desc[0].data[4]);
+       dev_info(dev, "IGU_TX_PRI_MAP_TC_CFG: 0x%x\n", desc[0].data[5]);
+}
+
+static void hclge_dbg_dump_reg_cmd(struct hclge_dev *hdev, char *cmd_buf)
+{
+       int msg_num;
+
+       if (strncmp(&cmd_buf[9], "bios common", 11) == 0) {
+               msg_num = sizeof(hclge_dbg_bios_common_reg) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_bios_common_reg,
+                                         &cmd_buf[21], msg_num,
+                                         HCLGE_DBG_DFX_BIOS_OFFSET,
+                                         HCLGE_OPC_DFX_BIOS_COMMON_REG);
+       } else if (strncmp(&cmd_buf[9], "ssu", 3) == 0) {
+               msg_num = sizeof(hclge_dbg_ssu_reg_0) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_ssu_reg_0,
+                                         &cmd_buf[13], msg_num,
+                                         HCLGE_DBG_DFX_SSU_0_OFFSET,
+                                         HCLGE_OPC_DFX_SSU_REG_0);
+
+               msg_num = sizeof(hclge_dbg_ssu_reg_1) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_ssu_reg_1,
+                                         &cmd_buf[13], msg_num,
+                                         HCLGE_DBG_DFX_SSU_1_OFFSET,
+                                         HCLGE_OPC_DFX_SSU_REG_1);
+
+               msg_num = sizeof(hclge_dbg_ssu_reg_2) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_ssu_reg_2,
+                                         &cmd_buf[13], msg_num,
+                                         HCLGE_DBG_DFX_SSU_2_OFFSET,
+                                         HCLGE_OPC_DFX_SSU_REG_2);
+       } else if (strncmp(&cmd_buf[9], "igu egu", 7) == 0) {
+               msg_num = sizeof(hclge_dbg_igu_egu_reg) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_igu_egu_reg,
+                                         &cmd_buf[17], msg_num,
+                                         HCLGE_DBG_DFX_IGU_OFFSET,
+                                         HCLGE_OPC_DFX_IGU_EGU_REG);
+       } else if (strncmp(&cmd_buf[9], "rpu", 3) == 0) {
+               msg_num = sizeof(hclge_dbg_rpu_reg_0) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_rpu_reg_0,
+                                         &cmd_buf[13], msg_num,
+                                         HCLGE_DBG_DFX_RPU_0_OFFSET,
+                                         HCLGE_OPC_DFX_RPU_REG_0);
+
+               msg_num = sizeof(hclge_dbg_rpu_reg_1) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_rpu_reg_1,
+                                         &cmd_buf[13], msg_num,
+                                         HCLGE_DBG_DFX_RPU_1_OFFSET,
+                                         HCLGE_OPC_DFX_RPU_REG_1);
+       } else if (strncmp(&cmd_buf[9], "ncsi", 4) == 0) {
+               msg_num = sizeof(hclge_dbg_ncsi_reg) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_ncsi_reg,
+                                         &cmd_buf[14], msg_num,
+                                         HCLGE_DBG_DFX_NCSI_OFFSET,
+                                         HCLGE_OPC_DFX_NCSI_REG);
+       } else if (strncmp(&cmd_buf[9], "rtc", 3) == 0) {
+               msg_num = sizeof(hclge_dbg_rtc_reg) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_rtc_reg,
+                                         &cmd_buf[13], msg_num,
+                                         HCLGE_DBG_DFX_RTC_OFFSET,
+                                         HCLGE_OPC_DFX_RTC_REG);
+       } else if (strncmp(&cmd_buf[9], "ppp", 3) == 0) {
+               msg_num = sizeof(hclge_dbg_ppp_reg) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_ppp_reg,
+                                         &cmd_buf[13], msg_num,
+                                         HCLGE_DBG_DFX_PPP_OFFSET,
+                                         HCLGE_OPC_DFX_PPP_REG);
+       } else if (strncmp(&cmd_buf[9], "rcb", 3) == 0) {
+               msg_num = sizeof(hclge_dbg_rcb_reg) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_rcb_reg,
+                                         &cmd_buf[13], msg_num,
+                                         HCLGE_DBG_DFX_RCB_OFFSET,
+                                         HCLGE_OPC_DFX_RCB_REG);
+       } else if (strncmp(&cmd_buf[9], "tqp", 3) == 0) {
+               msg_num = sizeof(hclge_dbg_tqp_reg) /
+                         sizeof(struct hclge_dbg_dfx_message);
+               hclge_dbg_dump_reg_common(hdev, hclge_dbg_tqp_reg,
+                                         &cmd_buf[13], msg_num,
+                                         HCLGE_DBG_DFX_TQP_OFFSET,
+                                         HCLGE_OPC_DFX_TQP_REG);
+       } else if (strncmp(&cmd_buf[9], "dcb", 3) == 0) {
+               hclge_dbg_dump_dcb(hdev, &cmd_buf[13]);
+       } else {
+               dev_info(&hdev->pdev->dev, "unknown command\n");
+               return;
+       }
+}
+
+static void hclge_title_idx_print(struct hclge_dev *hdev, bool flag, int index,
+                                 char *title_buf, char *true_buf,
+                                 char *false_buf)
+{
+       if (flag)
+               dev_info(&hdev->pdev->dev, "%s(%d): %s\n", title_buf, index,
+                        true_buf);
+       else
+               dev_info(&hdev->pdev->dev, "%s(%d): %s\n", title_buf, index,
+                        false_buf);
+}
+
+static void hclge_dbg_dump_tc(struct hclge_dev *hdev)
+{
+       struct hclge_ets_tc_weight_cmd *ets_weight;
+       struct hclge_desc desc;
+       int i, ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_ETS_TC_WEIGHT, true);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "dump tc fail, status is %d.\n", ret);
+               return;
+       }
+
+       ets_weight = (struct hclge_ets_tc_weight_cmd *)desc.data;
+
+       dev_info(&hdev->pdev->dev, "dump tc\n");
+       dev_info(&hdev->pdev->dev, "weight_offset: %u\n",
+                ets_weight->weight_offset);
+
+       for (i = 0; i < HNAE3_MAX_TC; i++)
+               hclge_title_idx_print(hdev, ets_weight->tc_weight[i], i,
+                                     "tc", "no sp mode", "sp mode");
+}
+
+static void hclge_dbg_dump_tm_pg(struct hclge_dev *hdev)
+{
+       struct hclge_port_shapping_cmd *port_shap_cfg_cmd;
+       struct hclge_bp_to_qs_map_cmd *bp_to_qs_map_cmd;
+       struct hclge_pg_shapping_cmd *pg_shap_cfg_cmd;
+       enum hclge_opcode_type cmd;
+       struct hclge_desc desc;
+       int ret;
+
+       cmd = HCLGE_OPC_TM_PG_C_SHAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       pg_shap_cfg_cmd = (struct hclge_pg_shapping_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PG_C pg_id: %u\n", pg_shap_cfg_cmd->pg_id);
+       dev_info(&hdev->pdev->dev, "PG_C pg_shapping: 0x%x\n",
+                pg_shap_cfg_cmd->pg_shapping_para);
+
+       cmd = HCLGE_OPC_TM_PG_P_SHAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       pg_shap_cfg_cmd = (struct hclge_pg_shapping_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PG_P pg_id: %u\n", pg_shap_cfg_cmd->pg_id);
+       dev_info(&hdev->pdev->dev, "PG_P pg_shapping: 0x%x\n",
+                pg_shap_cfg_cmd->pg_shapping_para);
+
+       cmd = HCLGE_OPC_TM_PORT_SHAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       port_shap_cfg_cmd = (struct hclge_port_shapping_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PORT port_shapping: 0x%x\n",
+                port_shap_cfg_cmd->port_shapping_para);
+
+       cmd = HCLGE_OPC_TM_PG_SCH_MODE_CFG;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "PG_SCH pg_id: %u\n", desc.data[0]);
+
+       cmd = HCLGE_OPC_TM_PRI_SCH_MODE_CFG;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "PRI_SCH pg_id: %u\n", desc.data[0]);
+
+       cmd = HCLGE_OPC_TM_QS_SCH_MODE_CFG;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "QS_SCH pg_id: %u\n", desc.data[0]);
+
+       cmd = HCLGE_OPC_TM_BP_TO_QSET_MAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       bp_to_qs_map_cmd = (struct hclge_bp_to_qs_map_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "BP_TO_QSET pg_id: %u\n",
+                bp_to_qs_map_cmd->tc_id);
+       dev_info(&hdev->pdev->dev, "BP_TO_QSET pg_shapping: 0x%x\n",
+                bp_to_qs_map_cmd->qs_group_id);
+       dev_info(&hdev->pdev->dev, "BP_TO_QSET qs_bit_map: 0x%x\n",
+                bp_to_qs_map_cmd->qs_bit_map);
+       return;
+
+err_tm_pg_cmd_send:
+       dev_err(&hdev->pdev->dev, "dump tm_pg fail(0x%x), status is %d\n",
+               cmd, ret);
+}
+
+static void hclge_dbg_dump_tm(struct hclge_dev *hdev)
+{
+       struct hclge_priority_weight_cmd *priority_weight;
+       struct hclge_pg_to_pri_link_cmd *pg_to_pri_map;
+       struct hclge_qs_to_pri_link_cmd *qs_to_pri_map;
+       struct hclge_nq_to_qs_link_cmd *nq_to_qs_map;
+       struct hclge_pri_shapping_cmd *shap_cfg_cmd;
+       struct hclge_pg_weight_cmd *pg_weight;
+       struct hclge_qs_weight_cmd *qs_weight;
+       enum hclge_opcode_type cmd;
+       struct hclge_desc desc;
+       int ret;
+
+       cmd = HCLGE_OPC_TM_PG_TO_PRI_LINK;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       pg_to_pri_map = (struct hclge_pg_to_pri_link_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "dump tm\n");
+       dev_info(&hdev->pdev->dev, "PG_TO_PRI gp_id: %u\n",
+                pg_to_pri_map->pg_id);
+       dev_info(&hdev->pdev->dev, "PG_TO_PRI map: 0x%x\n",
+                pg_to_pri_map->pri_bit_map);
+
+       cmd = HCLGE_OPC_TM_QS_TO_PRI_LINK;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       qs_to_pri_map = (struct hclge_qs_to_pri_link_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "QS_TO_PRI qs_id: %u\n",
+                qs_to_pri_map->qs_id);
+       dev_info(&hdev->pdev->dev, "QS_TO_PRI priority: %u\n",
+                qs_to_pri_map->priority);
+       dev_info(&hdev->pdev->dev, "QS_TO_PRI link_vld: %u\n",
+                qs_to_pri_map->link_vld);
+
+       cmd = HCLGE_OPC_TM_NQ_TO_QS_LINK;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       nq_to_qs_map = (struct hclge_nq_to_qs_link_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "NQ_TO_QS nq_id: %u\n", nq_to_qs_map->nq_id);
+       dev_info(&hdev->pdev->dev, "NQ_TO_QS qset_id: %u\n",
+                nq_to_qs_map->qset_id);
+
+       cmd = HCLGE_OPC_TM_PG_WEIGHT;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       pg_weight = (struct hclge_pg_weight_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PG pg_id: %u\n", pg_weight->pg_id);
+       dev_info(&hdev->pdev->dev, "PG dwrr: %u\n", pg_weight->dwrr);
+
+       cmd = HCLGE_OPC_TM_QS_WEIGHT;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       qs_weight = (struct hclge_qs_weight_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "QS qs_id: %u\n", qs_weight->qs_id);
+       dev_info(&hdev->pdev->dev, "QS dwrr: %u\n", qs_weight->dwrr);
+
+       cmd = HCLGE_OPC_TM_PRI_WEIGHT;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       priority_weight = (struct hclge_priority_weight_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PRI pri_id: %u\n", priority_weight->pri_id);
+       dev_info(&hdev->pdev->dev, "PRI dwrr: %u\n", priority_weight->dwrr);
+
+       cmd = HCLGE_OPC_TM_PRI_C_SHAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       shap_cfg_cmd = (struct hclge_pri_shapping_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PRI_C pri_id: %u\n", shap_cfg_cmd->pri_id);
+       dev_info(&hdev->pdev->dev, "PRI_C pri_shapping: 0x%x\n",
+                shap_cfg_cmd->pri_shapping_para);
+
+       cmd = HCLGE_OPC_TM_PRI_P_SHAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       shap_cfg_cmd = (struct hclge_pri_shapping_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PRI_P pri_id: %u\n", shap_cfg_cmd->pri_id);
+       dev_info(&hdev->pdev->dev, "PRI_P pri_shapping: 0x%x\n",
+                shap_cfg_cmd->pri_shapping_para);
+
+       hclge_dbg_dump_tm_pg(hdev);
+
+       return;
+
+err_tm_cmd_send:
+       dev_err(&hdev->pdev->dev, "dump tm fail(0x%x), status is %d\n",
+               cmd, ret);
+}
+
+static void hclge_dbg_dump_tm_map(struct hclge_dev *hdev, char *cmd_buf)
+{
+       struct hclge_bp_to_qs_map_cmd *bp_to_qs_map_cmd;
+       struct hclge_nq_to_qs_link_cmd *nq_to_qs_map;
+       struct hclge_qs_to_pri_link_cmd *map;
+       struct hclge_tqp_tx_queue_tc_cmd *tc;
+       enum hclge_opcode_type cmd;
+       struct hclge_desc desc;
+       int queue_id, group_id;
+       u32 qset_maping[32];
+       int tc_id, qset_id;
+       int pri_id, ret;
+       u32 i;
+
+       ret = kstrtouint(&cmd_buf[12], 10, &queue_id);
+       queue_id = (ret != 0) ? 0 : queue_id;
+
+       cmd = HCLGE_OPC_TM_NQ_TO_QS_LINK;
+       nq_to_qs_map = (struct hclge_nq_to_qs_link_cmd *)desc.data;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       nq_to_qs_map->nq_id = cpu_to_le16(queue_id);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_map_cmd_send;
+       qset_id = nq_to_qs_map->qset_id & 0x3FF;
+
+       cmd = HCLGE_OPC_TM_QS_TO_PRI_LINK;
+       map = (struct hclge_qs_to_pri_link_cmd *)desc.data;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       map->qs_id = cpu_to_le16(qset_id);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_map_cmd_send;
+       pri_id = map->priority;
+
+       cmd = HCLGE_OPC_TQP_TX_QUEUE_TC;
+       tc = (struct hclge_tqp_tx_queue_tc_cmd *)desc.data;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       tc->queue_id = cpu_to_le16(queue_id);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_map_cmd_send;
+       tc_id = tc->tc_id & 0x7;
+
+       dev_info(&hdev->pdev->dev, "queue_id | qset_id | pri_id | tc_id\n");
+       dev_info(&hdev->pdev->dev, "%04d     | %04d    | %02d     | %02d\n",
+                queue_id, qset_id, pri_id, tc_id);
+
+       cmd = HCLGE_OPC_TM_BP_TO_QSET_MAPPING;
+       bp_to_qs_map_cmd = (struct hclge_bp_to_qs_map_cmd *)desc.data;
+       for (group_id = 0; group_id < 32; group_id++) {
+               hclge_cmd_setup_basic_desc(&desc, cmd, true);
+               bp_to_qs_map_cmd->tc_id = tc_id;
+               bp_to_qs_map_cmd->qs_group_id = group_id;
+               ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+               if (ret)
+                       goto err_tm_map_cmd_send;
+
+               qset_maping[group_id] = bp_to_qs_map_cmd->qs_bit_map;
+       }
+
+       dev_info(&hdev->pdev->dev, "index | tm bp qset maping:\n");
+
+       i = 0;
+       for (group_id = 0; group_id < 4; group_id++) {
+               dev_info(&hdev->pdev->dev,
+                        "%04d  | %08x:%08x:%08x:%08x:%08x:%08x:%08x:%08x\n",
+                        group_id * 256, qset_maping[(u32)(i + 7)],
+                        qset_maping[(u32)(i + 6)], qset_maping[(u32)(i + 5)],
+                        qset_maping[(u32)(i + 4)], qset_maping[(u32)(i + 3)],
+                        qset_maping[(u32)(i + 2)], qset_maping[(u32)(i + 1)],
+                        qset_maping[i]);
+               i += 8;
+       }
+
+       return;
+
+err_tm_map_cmd_send:
+       dev_err(&hdev->pdev->dev, "dump tqp map fail(0x%x), status is %d\n",
+               cmd, ret);
+}
+
+static void hclge_dbg_dump_qos_pause_cfg(struct hclge_dev *hdev)
+{
+       struct hclge_cfg_pause_param_cmd *pause_param;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_MAC_PARA, true);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "dump checksum fail, status is %d.\n",
+                       ret);
+               return;
+       }
+
+       pause_param = (struct hclge_cfg_pause_param_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "dump qos pause cfg\n");
+       dev_info(&hdev->pdev->dev, "pause_trans_gap: 0x%x\n",
+                pause_param->pause_trans_gap);
+       dev_info(&hdev->pdev->dev, "pause_trans_time: 0x%x\n",
+                pause_param->pause_trans_time);
+}
+
+static void hclge_dbg_dump_qos_pri_map(struct hclge_dev *hdev)
+{
+       struct hclge_qos_pri_map_cmd *pri_map;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PRI_TO_TC_MAPPING, true);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "dump qos pri map fail, status is %d.\n", ret);
+               return;
+       }
+
+       pri_map = (struct hclge_qos_pri_map_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "dump qos pri map\n");
+       dev_info(&hdev->pdev->dev, "vlan_to_pri: 0x%x\n", pri_map->vlan_pri);
+       dev_info(&hdev->pdev->dev, "pri_0_to_tc: 0x%x\n", pri_map->pri0_tc);
+       dev_info(&hdev->pdev->dev, "pri_1_to_tc: 0x%x\n", pri_map->pri1_tc);
+       dev_info(&hdev->pdev->dev, "pri_2_to_tc: 0x%x\n", pri_map->pri2_tc);
+       dev_info(&hdev->pdev->dev, "pri_3_to_tc: 0x%x\n", pri_map->pri3_tc);
+       dev_info(&hdev->pdev->dev, "pri_4_to_tc: 0x%x\n", pri_map->pri4_tc);
+       dev_info(&hdev->pdev->dev, "pri_5_to_tc: 0x%x\n", pri_map->pri5_tc);
+       dev_info(&hdev->pdev->dev, "pri_6_to_tc: 0x%x\n", pri_map->pri6_tc);
+       dev_info(&hdev->pdev->dev, "pri_7_to_tc: 0x%x\n", pri_map->pri7_tc);
+}
+
+static void hclge_dbg_dump_qos_buf_cfg(struct hclge_dev *hdev)
+{
+       struct hclge_tx_buff_alloc_cmd *tx_buf_cmd;
+       struct hclge_rx_priv_buff_cmd *rx_buf_cmd;
+       struct hclge_rx_priv_wl_buf *rx_priv_wl;
+       struct hclge_rx_com_wl *rx_packet_cnt;
+       struct hclge_rx_com_thrd *rx_com_thrd;
+       struct hclge_rx_com_wl *rx_com_wl;
+       enum hclge_opcode_type cmd;
+       struct hclge_desc desc[2];
+       int i, ret;
+
+       cmd = HCLGE_OPC_TX_BUFF_ALLOC;
+       hclge_cmd_setup_basic_desc(desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 1);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       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++)
+               dev_info(&hdev->pdev->dev, "tx_packet_buf_tc_%d: 0x%x\n", i,
+                        tx_buf_cmd->tx_pkt_buff[i]);
+
+       cmd = HCLGE_OPC_RX_PRIV_BUFF_ALLOC;
+       hclge_cmd_setup_basic_desc(desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 1);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       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++)
+               dev_info(&hdev->pdev->dev, "rx_packet_buf_tc_%d: 0x%x\n", i,
+                        rx_buf_cmd->buf_num[i]);
+
+       dev_info(&hdev->pdev->dev, "rx_share_buf: 0x%x\n",
+                rx_buf_cmd->shared_buf);
+
+       cmd = HCLGE_OPC_RX_PRIV_WL_ALLOC;
+       hclge_cmd_setup_basic_desc(&desc[0], cmd, true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 2);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "\n");
+       rx_priv_wl = (struct hclge_rx_priv_wl_buf *)desc[0].data;
+       for (i = 0; i < HCLGE_TC_NUM_ONE_DESC; i++)
+               dev_info(&hdev->pdev->dev,
+                        "rx_priv_wl_tc_%d: high: 0x%x, low: 0x%x\n", i,
+                        rx_priv_wl->tc_wl[i].high, rx_priv_wl->tc_wl[i].low);
+
+       rx_priv_wl = (struct hclge_rx_priv_wl_buf *)desc[1].data;
+       for (i = 0; i < HCLGE_TC_NUM_ONE_DESC; i++)
+               dev_info(&hdev->pdev->dev,
+                        "rx_priv_wl_tc_%d: high: 0x%x, low: 0x%x\n", i + 4,
+                        rx_priv_wl->tc_wl[i].high, rx_priv_wl->tc_wl[i].low);
+
+       cmd = HCLGE_OPC_RX_COM_THRD_ALLOC;
+       hclge_cmd_setup_basic_desc(&desc[0], cmd, true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 2);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "\n");
+       rx_com_thrd = (struct hclge_rx_com_thrd *)desc[0].data;
+       for (i = 0; i < HCLGE_TC_NUM_ONE_DESC; i++)
+               dev_info(&hdev->pdev->dev,
+                        "rx_com_thrd_tc_%d: high: 0x%x, low: 0x%x\n", i,
+                        rx_com_thrd->com_thrd[i].high,
+                        rx_com_thrd->com_thrd[i].low);
+
+       rx_com_thrd = (struct hclge_rx_com_thrd *)desc[1].data;
+       for (i = 0; i < HCLGE_TC_NUM_ONE_DESC; i++)
+               dev_info(&hdev->pdev->dev,
+                        "rx_com_thrd_tc_%d: high: 0x%x, low: 0x%x\n", i + 4,
+                        rx_com_thrd->com_thrd[i].high,
+                        rx_com_thrd->com_thrd[i].low);
+
+       cmd = HCLGE_OPC_RX_COM_WL_ALLOC;
+       hclge_cmd_setup_basic_desc(desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 1);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       rx_com_wl = (struct hclge_rx_com_wl *)desc[0].data;
+       dev_info(&hdev->pdev->dev, "\n");
+       dev_info(&hdev->pdev->dev, "rx_com_wl: high: 0x%x, low: 0x%x\n",
+                rx_com_wl->com_wl.high, rx_com_wl->com_wl.low);
+
+       cmd = HCLGE_OPC_RX_GBL_PKT_CNT;
+       hclge_cmd_setup_basic_desc(desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 1);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       rx_packet_cnt = (struct hclge_rx_com_wl *)desc[0].data;
+       dev_info(&hdev->pdev->dev,
+                "rx_global_packet_cnt: high: 0x%x, low: 0x%x\n",
+                rx_packet_cnt->com_wl.high, rx_packet_cnt->com_wl.low);
+
+       return;
+
+err_qos_cmd_send:
+       dev_err(&hdev->pdev->dev,
+               "dump qos buf cfg fail(0x%x), status is %d\n", cmd, ret);
+}
+
+static void hclge_dbg_dump_mng_table(struct hclge_dev *hdev)
+{
+       struct hclge_mac_ethertype_idx_rd_cmd *req0;
+       char printf_buf[HCLGE_DBG_BUF_LEN];
+       struct hclge_desc desc;
+       int ret, i;
+
+       dev_info(&hdev->pdev->dev, "mng tab:\n");
+       memset(printf_buf, 0, HCLGE_DBG_BUF_LEN);
+       strncat(printf_buf,
+               "entry|mac_addr         |mask|ether|mask|vlan|mask",
+               HCLGE_DBG_BUF_LEN - 1);
+       strncat(printf_buf + strlen(printf_buf),
+               "|i_map|i_dir|e_type|pf_id|vf_id|q_id|drop\n",
+               HCLGE_DBG_BUF_LEN - strlen(printf_buf) - 1);
+
+       dev_info(&hdev->pdev->dev, "%s", printf_buf);
+
+       for (i = 0; i < HCLGE_DBG_MNG_TBL_MAX; i++) {
+               hclge_cmd_setup_basic_desc(&desc, HCLGE_MAC_ETHERTYPE_IDX_RD,
+                                          true);
+               req0 = (struct hclge_mac_ethertype_idx_rd_cmd *)&desc.data;
+               req0->index = cpu_to_le16(i);
+
+               ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+               if (ret) {
+                       dev_err(&hdev->pdev->dev,
+                               "call hclge_cmd_send fail, ret = %d\n", ret);
+                       return;
+               }
+
+               if (!req0->resp_code)
+                       continue;
+
+               memset(printf_buf, 0, HCLGE_DBG_BUF_LEN);
+               snprintf(printf_buf, HCLGE_DBG_BUF_LEN,
+                        "%02u   |%02x:%02x:%02x:%02x:%02x:%02x|",
+                        req0->index, req0->mac_add[0], req0->mac_add[1],
+                        req0->mac_add[2], req0->mac_add[3], req0->mac_add[4],
+                        req0->mac_add[5]);
+
+               snprintf(printf_buf + strlen(printf_buf),
+                        HCLGE_DBG_BUF_LEN - strlen(printf_buf),
+                        "%x   |%04x |%x   |%04x|%x   |%02x   |%02x   |",
+                        !!(req0->flags & HCLGE_DBG_MNG_MAC_MASK_B),
+                        req0->ethter_type,
+                        !!(req0->flags & HCLGE_DBG_MNG_ETHER_MASK_B),
+                        req0->vlan_tag & HCLGE_DBG_MNG_VLAN_TAG,
+                        !!(req0->flags & HCLGE_DBG_MNG_VLAN_MASK_B),
+                        req0->i_port_bitmap, req0->i_port_direction);
+
+               snprintf(printf_buf + strlen(printf_buf),
+                        HCLGE_DBG_BUF_LEN - strlen(printf_buf),
+                        "%d     |%d    |%02d   |%04d|%x\n",
+                        !!(req0->egress_port & HCLGE_DBG_MNG_E_TYPE_B),
+                        req0->egress_port & HCLGE_DBG_MNG_PF_ID,
+                        (req0->egress_port >> 3) & HCLGE_DBG_MNG_VF_ID,
+                        req0->egress_queue,
+                        !!(req0->egress_port & HCLGE_DBG_MNG_DROP_B));
+
+               dev_info(&hdev->pdev->dev, "%s", printf_buf);
+       }
+}
+
+static void hclge_dbg_fd_tcam_read(struct hclge_dev *hdev, u8 stage,
+                                  bool sel_x, u32 loc)
+{
+       struct hclge_fd_tcam_config_1_cmd *req1;
+       struct hclge_fd_tcam_config_2_cmd *req2;
+       struct hclge_fd_tcam_config_3_cmd *req3;
+       struct hclge_desc desc[3];
+       int ret, i;
+       u32 *req;
+
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_FD_TCAM_OP, true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_FD_TCAM_OP, true);
+       desc[1].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_FD_TCAM_OP, true);
+
+       req1 = (struct hclge_fd_tcam_config_1_cmd *)desc[0].data;
+       req2 = (struct hclge_fd_tcam_config_2_cmd *)desc[1].data;
+       req3 = (struct hclge_fd_tcam_config_3_cmd *)desc[2].data;
+
+       req1->stage  = stage;
+       req1->xy_sel = sel_x ? 1 : 0;
+       req1->index  = cpu_to_le32(loc);
+
+       ret = hclge_cmd_send(&hdev->hw, desc, 3);
+       if (ret)
+               return;
+
+       dev_info(&hdev->pdev->dev, " read result tcam key %s(%u):\n",
+                sel_x ? "x" : "y", loc);
+
+       req = (u32 *)req1->tcam_data;
+       for (i = 0; i < 2; i++)
+               dev_info(&hdev->pdev->dev, "%08x\n", *req++);
+
+       req = (u32 *)req2->tcam_data;
+       for (i = 0; i < 6; i++)
+               dev_info(&hdev->pdev->dev, "%08x\n", *req++);
+
+       req = (u32 *)req3->tcam_data;
+       for (i = 0; i < 5; i++)
+               dev_info(&hdev->pdev->dev, "%08x\n", *req++);
+}
+
+static void hclge_dbg_fd_tcam(struct hclge_dev *hdev)
+{
+       u32 i;
+
+       for (i = 0; i < hdev->fd_cfg.rule_num[0]; i++) {
+               hclge_dbg_fd_tcam_read(hdev, 0, true, i);
+               hclge_dbg_fd_tcam_read(hdev, 0, false, i);
+       }
+}
+
+int hclge_dbg_run_cmd(struct hnae3_handle *handle, char *cmd_buf)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+
+       if (strncmp(cmd_buf, "dump fd tcam", 12) == 0) {
+               hclge_dbg_fd_tcam(hdev);
+       } else if (strncmp(cmd_buf, "dump tc", 7) == 0) {
+               hclge_dbg_dump_tc(hdev);
+       } else if (strncmp(cmd_buf, "dump tm map", 11) == 0) {
+               hclge_dbg_dump_tm_map(hdev, cmd_buf);
+       } else if (strncmp(cmd_buf, "dump tm", 7) == 0) {
+               hclge_dbg_dump_tm(hdev);
+       } else if (strncmp(cmd_buf, "dump qos pause cfg", 18) == 0) {
+               hclge_dbg_dump_qos_pause_cfg(hdev);
+       } else if (strncmp(cmd_buf, "dump qos pri map", 16) == 0) {
+               hclge_dbg_dump_qos_pri_map(hdev);
+       } else if (strncmp(cmd_buf, "dump qos buf cfg", 16) == 0) {
+               hclge_dbg_dump_qos_buf_cfg(hdev);
+       } else if (strncmp(cmd_buf, "dump mng tbl", 12) == 0) {
+               hclge_dbg_dump_mng_table(hdev);
+       } else if (strncmp(cmd_buf, "dump reg", 8) == 0) {
+               hclge_dbg_dump_reg_cmd(hdev, cmd_buf);
+       } else {
+               dev_info(&hdev->pdev->dev, "unknown command\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.h
new file mode 100644 (file)
index 0000000..d055fda
--- /dev/null
@@ -0,0 +1,713 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2018-2019 Hisilicon Limited. */
+
+#ifndef __HCLGE_DEBUGFS_H
+#define __HCLGE_DEBUGFS_H
+
+#define HCLGE_DBG_BUF_LEN         256
+#define HCLGE_DBG_MNG_TBL_MAX     64
+
+#define HCLGE_DBG_MNG_VLAN_MASK_B  BIT(0)
+#define HCLGE_DBG_MNG_MAC_MASK_B   BIT(1)
+#define HCLGE_DBG_MNG_ETHER_MASK_B BIT(2)
+#define HCLGE_DBG_MNG_E_TYPE_B    BIT(11)
+#define HCLGE_DBG_MNG_DROP_B      BIT(13)
+#define HCLGE_DBG_MNG_VLAN_TAG    0x0FFF
+#define HCLGE_DBG_MNG_PF_ID       0x0007
+#define HCLGE_DBG_MNG_VF_ID       0x00FF
+
+/* Get DFX BD number offset */
+#define HCLGE_DBG_DFX_BIOS_OFFSET  1
+#define HCLGE_DBG_DFX_SSU_0_OFFSET 2
+#define HCLGE_DBG_DFX_SSU_1_OFFSET 3
+#define HCLGE_DBG_DFX_IGU_OFFSET   4
+#define HCLGE_DBG_DFX_RPU_0_OFFSET 5
+
+#define HCLGE_DBG_DFX_RPU_1_OFFSET 6
+#define HCLGE_DBG_DFX_NCSI_OFFSET  7
+#define HCLGE_DBG_DFX_RTC_OFFSET   8
+#define HCLGE_DBG_DFX_PPP_OFFSET   9
+#define HCLGE_DBG_DFX_RCB_OFFSET   10
+#define HCLGE_DBG_DFX_TQP_OFFSET   11
+
+#define HCLGE_DBG_DFX_SSU_2_OFFSET 12
+
+#pragma pack(1)
+
+struct hclge_qos_pri_map_cmd {
+       u8 pri0_tc  : 4,
+          pri1_tc  : 4;
+       u8 pri2_tc  : 4,
+          pri3_tc  : 4;
+       u8 pri4_tc  : 4,
+          pri5_tc  : 4;
+       u8 pri6_tc  : 4,
+          pri7_tc  : 4;
+       u8 vlan_pri : 4,
+          rev      : 4;
+};
+
+struct hclge_dbg_bitmap_cmd {
+       union {
+               u8 bitmap;
+               struct {
+                       u8 bit0 : 1,
+                          bit1 : 1,
+                          bit2 : 1,
+                          bit3 : 1,
+                          bit4 : 1,
+                          bit5 : 1,
+                          bit6 : 1,
+                          bit7 : 1;
+               };
+       };
+};
+
+struct hclge_dbg_dfx_message {
+       int flag;
+       char message[60];
+};
+
+#pragma pack()
+
+static struct hclge_dbg_dfx_message hclge_dbg_bios_common_reg[] = {
+       {false, "Reserved"},
+       {true,  "BP_CPU_STATE"},
+       {true,  "DFX_MSIX_INFO_NIC_0"},
+       {true,  "DFX_MSIX_INFO_NIC_1"},
+       {true,  "DFX_MSIX_INFO_NIC_2"},
+       {true,  "DFX_MSIX_INFO_NIC_3"},
+
+       {true,  "DFX_MSIX_INFO_ROC_0"},
+       {true,  "DFX_MSIX_INFO_ROC_1"},
+       {true,  "DFX_MSIX_INFO_ROC_2"},
+       {true,  "DFX_MSIX_INFO_ROC_3"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_ssu_reg_0[] = {
+       {false, "Reserved"},
+       {true,  "SSU_ETS_PORT_STATUS"},
+       {true,  "SSU_ETS_TCG_STATUS"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {true,  "SSU_BP_STATUS_0"},
+
+       {true,  "SSU_BP_STATUS_1"},
+       {true,  "SSU_BP_STATUS_2"},
+       {true,  "SSU_BP_STATUS_3"},
+       {true,  "SSU_BP_STATUS_4"},
+       {true,  "SSU_BP_STATUS_5"},
+       {true,  "SSU_MAC_TX_PFC_IND"},
+
+       {true,  "MAC_SSU_RX_PFC_IND"},
+       {true,  "BTMP_AGEING_ST_B0"},
+       {true,  "BTMP_AGEING_ST_B1"},
+       {true,  "BTMP_AGEING_ST_B2"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+
+       {true,  "FULL_DROP_NUM"},
+       {true,  "PART_DROP_NUM"},
+       {true,  "PPP_KEY_DROP_NUM"},
+       {true,  "PPP_RLT_DROP_NUM"},
+       {true,  "LO_PRI_UNICAST_RLT_DROP_NUM"},
+       {true,  "HI_PRI_MULTICAST_RLT_DROP_NUM"},
+
+       {true,  "LO_PRI_MULTICAST_RLT_DROP_NUM"},
+       {true,  "NCSI_PACKET_CURR_BUFFER_CNT"},
+       {true,  "BTMP_AGEING_RLS_CNT_BANK0"},
+       {true,  "BTMP_AGEING_RLS_CNT_BANK1"},
+       {true,  "BTMP_AGEING_RLS_CNT_BANK2"},
+       {true,  "SSU_MB_RD_RLT_DROP_CNT"},
+
+       {true,  "SSU_PPP_MAC_KEY_NUM_L"},
+       {true,  "SSU_PPP_MAC_KEY_NUM_H"},
+       {true,  "SSU_PPP_HOST_KEY_NUM_L"},
+       {true,  "SSU_PPP_HOST_KEY_NUM_H"},
+       {true,  "PPP_SSU_MAC_RLT_NUM_L"},
+       {true,  "PPP_SSU_MAC_RLT_NUM_H"},
+
+       {true,  "PPP_SSU_HOST_RLT_NUM_L"},
+       {true,  "PPP_SSU_HOST_RLT_NUM_H"},
+       {true,  "NCSI_RX_PACKET_IN_CNT_L"},
+       {true,  "NCSI_RX_PACKET_IN_CNT_H"},
+       {true,  "NCSI_TX_PACKET_OUT_CNT_L"},
+       {true,  "NCSI_TX_PACKET_OUT_CNT_H"},
+
+       {true,  "SSU_KEY_DROP_NUM"},
+       {true,  "MB_UNCOPY_NUM"},
+       {true,  "RX_OQ_DROP_PKT_CNT"},
+       {true,  "TX_OQ_DROP_PKT_CNT"},
+       {true,  "BANK_UNBALANCE_DROP_CNT"},
+       {true,  "BANK_UNBALANCE_RX_DROP_CNT"},
+
+       {true,  "NIC_L2_ERR_DROP_PKT_CNT"},
+       {true,  "ROC_L2_ERR_DROP_PKT_CNT"},
+       {true,  "NIC_L2_ERR_DROP_PKT_CNT_RX"},
+       {true,  "ROC_L2_ERR_DROP_PKT_CNT_RX"},
+       {true,  "RX_OQ_GLB_DROP_PKT_CNT"},
+       {false, "Reserved"},
+
+       {true,  "LO_PRI_UNICAST_CUR_CNT"},
+       {true,  "HI_PRI_MULTICAST_CUR_CNT"},
+       {true,  "LO_PRI_MULTICAST_CUR_CNT"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_ssu_reg_1[] = {
+       {true,  "prt_id"},
+       {true,  "PACKET_TC_CURR_BUFFER_CNT_0"},
+       {true,  "PACKET_TC_CURR_BUFFER_CNT_1"},
+       {true,  "PACKET_TC_CURR_BUFFER_CNT_2"},
+       {true,  "PACKET_TC_CURR_BUFFER_CNT_3"},
+       {true,  "PACKET_TC_CURR_BUFFER_CNT_4"},
+
+       {true,  "PACKET_TC_CURR_BUFFER_CNT_5"},
+       {true,  "PACKET_TC_CURR_BUFFER_CNT_6"},
+       {true,  "PACKET_TC_CURR_BUFFER_CNT_7"},
+       {true,  "PACKET_CURR_BUFFER_CNT"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+
+       {true,  "RX_PACKET_IN_CNT_L"},
+       {true,  "RX_PACKET_IN_CNT_H"},
+       {true,  "RX_PACKET_OUT_CNT_L"},
+       {true,  "RX_PACKET_OUT_CNT_H"},
+       {true,  "TX_PACKET_IN_CNT_L"},
+       {true,  "TX_PACKET_IN_CNT_H"},
+
+       {true,  "TX_PACKET_OUT_CNT_L"},
+       {true,  "TX_PACKET_OUT_CNT_H"},
+       {true,  "ROC_RX_PACKET_IN_CNT_L"},
+       {true,  "ROC_RX_PACKET_IN_CNT_H"},
+       {true,  "ROC_TX_PACKET_OUT_CNT_L"},
+       {true,  "ROC_TX_PACKET_OUT_CNT_H"},
+
+       {true,  "RX_PACKET_TC_IN_CNT_0_L"},
+       {true,  "RX_PACKET_TC_IN_CNT_0_H"},
+       {true,  "RX_PACKET_TC_IN_CNT_1_L"},
+       {true,  "RX_PACKET_TC_IN_CNT_1_H"},
+       {true,  "RX_PACKET_TC_IN_CNT_2_L"},
+       {true,  "RX_PACKET_TC_IN_CNT_2_H"},
+
+       {true,  "RX_PACKET_TC_IN_CNT_3_L"},
+       {true,  "RX_PACKET_TC_IN_CNT_3_H"},
+       {true,  "RX_PACKET_TC_IN_CNT_4_L"},
+       {true,  "RX_PACKET_TC_IN_CNT_4_H"},
+       {true,  "RX_PACKET_TC_IN_CNT_5_L"},
+       {true,  "RX_PACKET_TC_IN_CNT_5_H"},
+
+       {true,  "RX_PACKET_TC_IN_CNT_6_L"},
+       {true,  "RX_PACKET_TC_IN_CNT_6_H"},
+       {true,  "RX_PACKET_TC_IN_CNT_7_L"},
+       {true,  "RX_PACKET_TC_IN_CNT_7_H"},
+       {true,  "RX_PACKET_TC_OUT_CNT_0_L"},
+       {true,  "RX_PACKET_TC_OUT_CNT_0_H"},
+
+       {true,  "RX_PACKET_TC_OUT_CNT_1_L"},
+       {true,  "RX_PACKET_TC_OUT_CNT_1_H"},
+       {true,  "RX_PACKET_TC_OUT_CNT_2_L"},
+       {true,  "RX_PACKET_TC_OUT_CNT_2_H"},
+       {true,  "RX_PACKET_TC_OUT_CNT_3_L"},
+       {true,  "RX_PACKET_TC_OUT_CNT_3_H"},
+
+       {true,  "RX_PACKET_TC_OUT_CNT_4_L"},
+       {true,  "RX_PACKET_TC_OUT_CNT_4_H"},
+       {true,  "RX_PACKET_TC_OUT_CNT_5_L"},
+       {true,  "RX_PACKET_TC_OUT_CNT_5_H"},
+       {true,  "RX_PACKET_TC_OUT_CNT_6_L"},
+       {true,  "RX_PACKET_TC_OUT_CNT_6_H"},
+
+       {true,  "RX_PACKET_TC_OUT_CNT_7_L"},
+       {true,  "RX_PACKET_TC_OUT_CNT_7_H"},
+       {true,  "TX_PACKET_TC_IN_CNT_0_L"},
+       {true,  "TX_PACKET_TC_IN_CNT_0_H"},
+       {true,  "TX_PACKET_TC_IN_CNT_1_L"},
+       {true,  "TX_PACKET_TC_IN_CNT_1_H"},
+
+       {true,  "TX_PACKET_TC_IN_CNT_2_L"},
+       {true,  "TX_PACKET_TC_IN_CNT_2_H"},
+       {true,  "TX_PACKET_TC_IN_CNT_3_L"},
+       {true,  "TX_PACKET_TC_IN_CNT_3_H"},
+       {true,  "TX_PACKET_TC_IN_CNT_4_L"},
+       {true,  "TX_PACKET_TC_IN_CNT_4_H"},
+
+       {true,  "TX_PACKET_TC_IN_CNT_5_L"},
+       {true,  "TX_PACKET_TC_IN_CNT_5_H"},
+       {true,  "TX_PACKET_TC_IN_CNT_6_L"},
+       {true,  "TX_PACKET_TC_IN_CNT_6_H"},
+       {true,  "TX_PACKET_TC_IN_CNT_7_L"},
+       {true,  "TX_PACKET_TC_IN_CNT_7_H"},
+
+       {true,  "TX_PACKET_TC_OUT_CNT_0_L"},
+       {true,  "TX_PACKET_TC_OUT_CNT_0_H"},
+       {true,  "TX_PACKET_TC_OUT_CNT_1_L"},
+       {true,  "TX_PACKET_TC_OUT_CNT_1_H"},
+       {true,  "TX_PACKET_TC_OUT_CNT_2_L"},
+       {true,  "TX_PACKET_TC_OUT_CNT_2_H"},
+
+       {true,  "TX_PACKET_TC_OUT_CNT_3_L"},
+       {true,  "TX_PACKET_TC_OUT_CNT_3_H"},
+       {true,  "TX_PACKET_TC_OUT_CNT_4_L"},
+       {true,  "TX_PACKET_TC_OUT_CNT_4_H"},
+       {true,  "TX_PACKET_TC_OUT_CNT_5_L"},
+       {true,  "TX_PACKET_TC_OUT_CNT_5_H"},
+
+       {true,  "TX_PACKET_TC_OUT_CNT_6_L"},
+       {true,  "TX_PACKET_TC_OUT_CNT_6_H"},
+       {true,  "TX_PACKET_TC_OUT_CNT_7_L"},
+       {true,  "TX_PACKET_TC_OUT_CNT_7_H"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_ssu_reg_2[] = {
+       {true,  "OQ_INDEX"},
+       {true,  "QUEUE_CNT"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_igu_egu_reg[] = {
+       {true,  "prt_id"},
+       {true,  "IGU_RX_ERR_PKT"},
+       {true,  "IGU_RX_NO_SOF_PKT"},
+       {true,  "EGU_TX_1588_SHORT_PKT"},
+       {true,  "EGU_TX_1588_PKT"},
+       {true,  "EGU_TX_ERR_PKT"},
+
+       {true,  "IGU_RX_OUT_L2_PKT"},
+       {true,  "IGU_RX_OUT_L3_PKT"},
+       {true,  "IGU_RX_OUT_L4_PKT"},
+       {true,  "IGU_RX_IN_L2_PKT"},
+       {true,  "IGU_RX_IN_L3_PKT"},
+       {true,  "IGU_RX_IN_L4_PKT"},
+
+       {true,  "IGU_RX_EL3E_PKT"},
+       {true,  "IGU_RX_EL4E_PKT"},
+       {true,  "IGU_RX_L3E_PKT"},
+       {true,  "IGU_RX_L4E_PKT"},
+       {true,  "IGU_RX_ROCEE_PKT"},
+       {true,  "IGU_RX_OUT_UDP0_PKT"},
+
+       {true,  "IGU_RX_IN_UDP0_PKT"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+
+       {true,  "IGU_RX_OVERSIZE_PKT_L"},
+       {true,  "IGU_RX_OVERSIZE_PKT_H"},
+       {true,  "IGU_RX_UNDERSIZE_PKT_L"},
+       {true,  "IGU_RX_UNDERSIZE_PKT_H"},
+       {true,  "IGU_RX_OUT_ALL_PKT_L"},
+       {true,  "IGU_RX_OUT_ALL_PKT_H"},
+
+       {true,  "IGU_TX_OUT_ALL_PKT_L"},
+       {true,  "IGU_TX_OUT_ALL_PKT_H"},
+       {true,  "IGU_RX_UNI_PKT_L"},
+       {true,  "IGU_RX_UNI_PKT_H"},
+       {true,  "IGU_RX_MULTI_PKT_L"},
+       {true,  "IGU_RX_MULTI_PKT_H"},
+
+       {true,  "IGU_RX_BROAD_PKT_L"},
+       {true,  "IGU_RX_BROAD_PKT_H"},
+       {true,  "EGU_TX_OUT_ALL_PKT_L"},
+       {true,  "EGU_TX_OUT_ALL_PKT_H"},
+       {true,  "EGU_TX_UNI_PKT_L"},
+       {true,  "EGU_TX_UNI_PKT_H"},
+
+       {true,  "EGU_TX_MULTI_PKT_L"},
+       {true,  "EGU_TX_MULTI_PKT_H"},
+       {true,  "EGU_TX_BROAD_PKT_L"},
+       {true,  "EGU_TX_BROAD_PKT_H"},
+       {true,  "IGU_TX_KEY_NUM_L"},
+       {true,  "IGU_TX_KEY_NUM_H"},
+
+       {true,  "IGU_RX_NON_TUN_PKT_L"},
+       {true,  "IGU_RX_NON_TUN_PKT_H"},
+       {true,  "IGU_RX_TUN_PKT_L"},
+       {true,  "IGU_RX_TUN_PKT_H"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_rpu_reg_0[] = {
+       {true, "tc_queue_num"},
+       {true, "FSM_DFX_ST0"},
+       {true, "FSM_DFX_ST1"},
+       {true, "RPU_RX_PKT_DROP_CNT"},
+       {true, "BUF_WAIT_TIMEOUT"},
+       {true, "BUF_WAIT_TIMEOUT_QID"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_rpu_reg_1[] = {
+       {false, "Reserved"},
+       {true,  "FIFO_DFX_ST0"},
+       {true,  "FIFO_DFX_ST1"},
+       {true,  "FIFO_DFX_ST2"},
+       {true,  "FIFO_DFX_ST3"},
+       {true,  "FIFO_DFX_ST4"},
+
+       {true,  "FIFO_DFX_ST5"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_ncsi_reg[] = {
+       {false, "Reserved"},
+       {true,  "NCSI_EGU_TX_FIFO_STS"},
+       {true,  "NCSI_PAUSE_STATUS"},
+       {true,  "NCSI_RX_CTRL_DMAC_ERR_CNT"},
+       {true,  "NCSI_RX_CTRL_SMAC_ERR_CNT"},
+       {true,  "NCSI_RX_CTRL_CKS_ERR_CNT"},
+
+       {true,  "NCSI_RX_CTRL_PKT_CNT"},
+       {true,  "NCSI_RX_PT_DMAC_ERR_CNT"},
+       {true,  "NCSI_RX_PT_SMAC_ERR_CNT"},
+       {true,  "NCSI_RX_PT_PKT_CNT"},
+       {true,  "NCSI_RX_FCS_ERR_CNT"},
+       {true,  "NCSI_TX_CTRL_DMAC_ERR_CNT"},
+
+       {true,  "NCSI_TX_CTRL_SMAC_ERR_CNT"},
+       {true,  "NCSI_TX_CTRL_PKT_CNT"},
+       {true,  "NCSI_TX_PT_DMAC_ERR_CNT"},
+       {true,  "NCSI_TX_PT_SMAC_ERR_CNT"},
+       {true,  "NCSI_TX_PT_PKT_CNT"},
+       {true,  "NCSI_TX_PT_PKT_TRUNC_CNT"},
+
+       {true,  "NCSI_TX_PT_PKT_ERR_CNT"},
+       {true,  "NCSI_TX_CTRL_PKT_ERR_CNT"},
+       {true,  "NCSI_RX_CTRL_PKT_TRUNC_CNT"},
+       {true,  "NCSI_RX_CTRL_PKT_CFLIT_CNT"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+
+       {true,  "NCSI_MAC_RX_OCTETS_OK"},
+       {true,  "NCSI_MAC_RX_OCTETS_BAD"},
+       {true,  "NCSI_MAC_RX_UC_PKTS"},
+       {true,  "NCSI_MAC_RX_MC_PKTS"},
+       {true,  "NCSI_MAC_RX_BC_PKTS"},
+       {true,  "NCSI_MAC_RX_PKTS_64OCTETS"},
+
+       {true,  "NCSI_MAC_RX_PKTS_65TO127OCTETS"},
+       {true,  "NCSI_MAC_RX_PKTS_128TO255OCTETS"},
+       {true,  "NCSI_MAC_RX_PKTS_255TO511OCTETS"},
+       {true,  "NCSI_MAC_RX_PKTS_512TO1023OCTETS"},
+       {true,  "NCSI_MAC_RX_PKTS_1024TO1518OCTETS"},
+       {true,  "NCSI_MAC_RX_PKTS_1519TOMAXOCTETS"},
+
+       {true,  "NCSI_MAC_RX_FCS_ERRORS"},
+       {true,  "NCSI_MAC_RX_LONG_ERRORS"},
+       {true,  "NCSI_MAC_RX_JABBER_ERRORS"},
+       {true,  "NCSI_MAC_RX_RUNT_ERR_CNT"},
+       {true,  "NCSI_MAC_RX_SHORT_ERR_CNT"},
+       {true,  "NCSI_MAC_RX_FILT_PKT_CNT"},
+
+       {true,  "NCSI_MAC_RX_OCTETS_TOTAL_FILT"},
+       {true,  "NCSI_MAC_TX_OCTETS_OK"},
+       {true,  "NCSI_MAC_TX_OCTETS_BAD"},
+       {true,  "NCSI_MAC_TX_UC_PKTS"},
+       {true,  "NCSI_MAC_TX_MC_PKTS"},
+       {true,  "NCSI_MAC_TX_BC_PKTS"},
+
+       {true,  "NCSI_MAC_TX_PKTS_64OCTETS"},
+       {true,  "NCSI_MAC_TX_PKTS_65TO127OCTETS"},
+       {true,  "NCSI_MAC_TX_PKTS_128TO255OCTETS"},
+       {true,  "NCSI_MAC_TX_PKTS_256TO511OCTETS"},
+       {true,  "NCSI_MAC_TX_PKTS_512TO1023OCTETS"},
+       {true,  "NCSI_MAC_TX_PKTS_1024TO1518OCTETS"},
+
+       {true,  "NCSI_MAC_TX_PKTS_1519TOMAXOCTETS"},
+       {true,  "NCSI_MAC_TX_UNDERRUN"},
+       {true,  "NCSI_MAC_TX_CRC_ERROR"},
+       {true,  "NCSI_MAC_TX_PAUSE_FRAMES"},
+       {true,  "NCSI_MAC_RX_PAD_PKTS"},
+       {true,  "NCSI_MAC_RX_PAUSE_FRAMES"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_rtc_reg[] = {
+       {false, "Reserved"},
+       {true,  "LGE_IGU_AFIFO_DFX_0"},
+       {true,  "LGE_IGU_AFIFO_DFX_1"},
+       {true,  "LGE_IGU_AFIFO_DFX_2"},
+       {true,  "LGE_IGU_AFIFO_DFX_3"},
+       {true,  "LGE_IGU_AFIFO_DFX_4"},
+
+       {true,  "LGE_IGU_AFIFO_DFX_5"},
+       {true,  "LGE_IGU_AFIFO_DFX_6"},
+       {true,  "LGE_IGU_AFIFO_DFX_7"},
+       {true,  "LGE_EGU_AFIFO_DFX_0"},
+       {true,  "LGE_EGU_AFIFO_DFX_1"},
+       {true,  "LGE_EGU_AFIFO_DFX_2"},
+
+       {true,  "LGE_EGU_AFIFO_DFX_3"},
+       {true,  "LGE_EGU_AFIFO_DFX_4"},
+       {true,  "LGE_EGU_AFIFO_DFX_5"},
+       {true,  "LGE_EGU_AFIFO_DFX_6"},
+       {true,  "LGE_EGU_AFIFO_DFX_7"},
+       {true,  "CGE_IGU_AFIFO_DFX_0"},
+
+       {true,  "CGE_IGU_AFIFO_DFX_1"},
+       {true,  "CGE_EGU_AFIFO_DFX_0"},
+       {true,  "CGE_EGU_AFIFO_DFX_1"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_ppp_reg[] = {
+       {false, "Reserved"},
+       {true,  "DROP_FROM_PRT_PKT_CNT"},
+       {true,  "DROP_FROM_HOST_PKT_CNT"},
+       {true,  "DROP_TX_VLAN_PROC_CNT"},
+       {true,  "DROP_MNG_CNT"},
+       {true,  "DROP_FD_CNT"},
+
+       {true,  "DROP_NO_DST_CNT"},
+       {true,  "DROP_MC_MBID_FULL_CNT"},
+       {true,  "DROP_SC_FILTERED"},
+       {true,  "PPP_MC_DROP_PKT_CNT"},
+       {true,  "DROP_PT_CNT"},
+       {true,  "DROP_MAC_ANTI_SPOOF_CNT"},
+
+       {true,  "DROP_IG_VFV_CNT"},
+       {true,  "DROP_IG_PRTV_CNT"},
+       {true,  "DROP_CNM_PFC_PAUSE_CNT"},
+       {true,  "DROP_TORUS_TC_CNT"},
+       {true,  "DROP_TORUS_LPBK_CNT"},
+       {true,  "PPP_HFS_STS"},
+
+       {true,  "PPP_MC_RSLT_STS"},
+       {true,  "PPP_P3U_STS"},
+       {true,  "PPP_RSLT_DESCR_STS"},
+       {true,  "PPP_UMV_STS_0"},
+       {true,  "PPP_UMV_STS_1"},
+       {true,  "PPP_VFV_STS"},
+
+       {true,  "PPP_GRO_KEY_CNT"},
+       {true,  "PPP_GRO_INFO_CNT"},
+       {true,  "PPP_GRO_DROP_CNT"},
+       {true,  "PPP_GRO_OUT_CNT"},
+       {true,  "PPP_GRO_KEY_MATCH_DATA_CNT"},
+       {true,  "PPP_GRO_KEY_MATCH_TCAM_CNT"},
+
+       {true,  "PPP_GRO_INFO_MATCH_CNT"},
+       {true,  "PPP_GRO_FREE_ENTRY_CNT"},
+       {true,  "PPP_GRO_INNER_DFX_SIGNAL"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+
+       {true,  "GET_RX_PKT_CNT_L"},
+       {true,  "GET_RX_PKT_CNT_H"},
+       {true,  "GET_TX_PKT_CNT_L"},
+       {true,  "GET_TX_PKT_CNT_H"},
+       {true,  "SEND_UC_PRT2HOST_PKT_CNT_L"},
+       {true,  "SEND_UC_PRT2HOST_PKT_CNT_H"},
+
+       {true,  "SEND_UC_PRT2PRT_PKT_CNT_L"},
+       {true,  "SEND_UC_PRT2PRT_PKT_CNT_H"},
+       {true,  "SEND_UC_HOST2HOST_PKT_CNT_L"},
+       {true,  "SEND_UC_HOST2HOST_PKT_CNT_H"},
+       {true,  "SEND_UC_HOST2PRT_PKT_CNT_L"},
+       {true,  "SEND_UC_HOST2PRT_PKT_CNT_H"},
+
+       {true,  "SEND_MC_FROM_PRT_CNT_L"},
+       {true,  "SEND_MC_FROM_PRT_CNT_H"},
+       {true,  "SEND_MC_FROM_HOST_CNT_L"},
+       {true,  "SEND_MC_FROM_HOST_CNT_H"},
+       {true,  "SSU_MC_RD_CNT_L"},
+       {true,  "SSU_MC_RD_CNT_H"},
+
+       {true,  "SSU_MC_DROP_CNT_L"},
+       {true,  "SSU_MC_DROP_CNT_H"},
+       {true,  "SSU_MC_RD_PKT_CNT_L"},
+       {true,  "SSU_MC_RD_PKT_CNT_H"},
+       {true,  "PPP_MC_2HOST_PKT_CNT_L"},
+       {true,  "PPP_MC_2HOST_PKT_CNT_H"},
+
+       {true,  "PPP_MC_2PRT_PKT_CNT_L"},
+       {true,  "PPP_MC_2PRT_PKT_CNT_H"},
+       {true,  "NTSNOS_PKT_CNT_L"},
+       {true,  "NTSNOS_PKT_CNT_H"},
+       {true,  "NTUP_PKT_CNT_L"},
+       {true,  "NTUP_PKT_CNT_H"},
+
+       {true,  "NTLCL_PKT_CNT_L"},
+       {true,  "NTLCL_PKT_CNT_H"},
+       {true,  "NTTGT_PKT_CNT_L"},
+       {true,  "NTTGT_PKT_CNT_H"},
+       {true,  "RTNS_PKT_CNT_L"},
+       {true,  "RTNS_PKT_CNT_H"},
+
+       {true,  "RTLPBK_PKT_CNT_L"},
+       {true,  "RTLPBK_PKT_CNT_H"},
+       {true,  "NR_PKT_CNT_L"},
+       {true,  "NR_PKT_CNT_H"},
+       {true,  "RR_PKT_CNT_L"},
+       {true,  "RR_PKT_CNT_H"},
+
+       {true,  "MNG_TBL_HIT_CNT_L"},
+       {true,  "MNG_TBL_HIT_CNT_H"},
+       {true,  "FD_TBL_HIT_CNT_L"},
+       {true,  "FD_TBL_HIT_CNT_H"},
+       {true,  "FD_LKUP_CNT_L"},
+       {true,  "FD_LKUP_CNT_H"},
+
+       {true,  "BC_HIT_CNT_L"},
+       {true,  "BC_HIT_CNT_H"},
+       {true,  "UM_TBL_UC_HIT_CNT_L"},
+       {true,  "UM_TBL_UC_HIT_CNT_H"},
+       {true,  "UM_TBL_MC_HIT_CNT_L"},
+       {true,  "UM_TBL_MC_HIT_CNT_H"},
+
+       {true,  "UM_TBL_VMDQ1_HIT_CNT_L"},
+       {true,  "UM_TBL_VMDQ1_HIT_CNT_H"},
+       {true,  "MTA_TBL_HIT_CNT_L"},
+       {true,  "MTA_TBL_HIT_CNT_H"},
+       {true,  "FWD_BONDING_HIT_CNT_L"},
+       {true,  "FWD_BONDING_HIT_CNT_H"},
+
+       {true,  "PROMIS_TBL_HIT_CNT_L"},
+       {true,  "PROMIS_TBL_HIT_CNT_H"},
+       {true,  "GET_TUNL_PKT_CNT_L"},
+       {true,  "GET_TUNL_PKT_CNT_H"},
+       {true,  "GET_BMC_PKT_CNT_L"},
+       {true,  "GET_BMC_PKT_CNT_H"},
+
+       {true,  "SEND_UC_PRT2BMC_PKT_CNT_L"},
+       {true,  "SEND_UC_PRT2BMC_PKT_CNT_H"},
+       {true,  "SEND_UC_HOST2BMC_PKT_CNT_L"},
+       {true,  "SEND_UC_HOST2BMC_PKT_CNT_H"},
+       {true,  "SEND_UC_BMC2HOST_PKT_CNT_L"},
+       {true,  "SEND_UC_BMC2HOST_PKT_CNT_H"},
+
+       {true,  "SEND_UC_BMC2PRT_PKT_CNT_L"},
+       {true,  "SEND_UC_BMC2PRT_PKT_CNT_H"},
+       {true,  "PPP_MC_2BMC_PKT_CNT_L"},
+       {true,  "PPP_MC_2BMC_PKT_CNT_H"},
+       {true,  "VLAN_MIRR_CNT_L"},
+       {true,  "VLAN_MIRR_CNT_H"},
+
+       {true,  "IG_MIRR_CNT_L"},
+       {true,  "IG_MIRR_CNT_H"},
+       {true,  "EG_MIRR_CNT_L"},
+       {true,  "EG_MIRR_CNT_H"},
+       {true,  "RX_DEFAULT_HOST_HIT_CNT_L"},
+       {true,  "RX_DEFAULT_HOST_HIT_CNT_H"},
+
+       {true,  "LAN_PAIR_CNT_L"},
+       {true,  "LAN_PAIR_CNT_H"},
+       {true,  "UM_TBL_MC_HIT_PKT_CNT_L"},
+       {true,  "UM_TBL_MC_HIT_PKT_CNT_H"},
+       {true,  "MTA_TBL_HIT_PKT_CNT_L"},
+       {true,  "MTA_TBL_HIT_PKT_CNT_H"},
+
+       {true,  "PROMIS_TBL_HIT_PKT_CNT_L"},
+       {true,  "PROMIS_TBL_HIT_PKT_CNT_H"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_rcb_reg[] = {
+       {false, "Reserved"},
+       {true,  "FSM_DFX_ST0"},
+       {true,  "FSM_DFX_ST1"},
+       {true,  "FSM_DFX_ST2"},
+       {true,  "FIFO_DFX_ST0"},
+       {true,  "FIFO_DFX_ST1"},
+
+       {true,  "FIFO_DFX_ST2"},
+       {true,  "FIFO_DFX_ST3"},
+       {true,  "FIFO_DFX_ST4"},
+       {true,  "FIFO_DFX_ST5"},
+       {true,  "FIFO_DFX_ST6"},
+       {true,  "FIFO_DFX_ST7"},
+
+       {true,  "FIFO_DFX_ST8"},
+       {true,  "FIFO_DFX_ST9"},
+       {true,  "FIFO_DFX_ST10"},
+       {true,  "FIFO_DFX_ST11"},
+       {true,  "Q_CREDIT_VLD_0"},
+       {true,  "Q_CREDIT_VLD_1"},
+
+       {true,  "Q_CREDIT_VLD_2"},
+       {true,  "Q_CREDIT_VLD_3"},
+       {true,  "Q_CREDIT_VLD_4"},
+       {true,  "Q_CREDIT_VLD_5"},
+       {true,  "Q_CREDIT_VLD_6"},
+       {true,  "Q_CREDIT_VLD_7"},
+
+       {true,  "Q_CREDIT_VLD_8"},
+       {true,  "Q_CREDIT_VLD_9"},
+       {true,  "Q_CREDIT_VLD_10"},
+       {true,  "Q_CREDIT_VLD_11"},
+       {true,  "Q_CREDIT_VLD_12"},
+       {true,  "Q_CREDIT_VLD_13"},
+
+       {true,  "Q_CREDIT_VLD_14"},
+       {true,  "Q_CREDIT_VLD_15"},
+       {true,  "Q_CREDIT_VLD_16"},
+       {true,  "Q_CREDIT_VLD_17"},
+       {true,  "Q_CREDIT_VLD_18"},
+       {true,  "Q_CREDIT_VLD_19"},
+
+       {true,  "Q_CREDIT_VLD_20"},
+       {true,  "Q_CREDIT_VLD_21"},
+       {true,  "Q_CREDIT_VLD_22"},
+       {true,  "Q_CREDIT_VLD_23"},
+       {true,  "Q_CREDIT_VLD_24"},
+       {true,  "Q_CREDIT_VLD_25"},
+
+       {true,  "Q_CREDIT_VLD_26"},
+       {true,  "Q_CREDIT_VLD_27"},
+       {true,  "Q_CREDIT_VLD_28"},
+       {true,  "Q_CREDIT_VLD_29"},
+       {true,  "Q_CREDIT_VLD_30"},
+       {true,  "Q_CREDIT_VLD_31"},
+
+       {true,  "GRO_BD_SERR_CNT"},
+       {true,  "GRO_CONTEXT_SERR_CNT"},
+       {true,  "RX_STASH_CFG_SERR_CNT"},
+       {true,  "AXI_RD_FBD_SERR_CNT"},
+       {true,  "GRO_BD_MERR_CNT"},
+       {true,  "GRO_CONTEXT_MERR_CNT"},
+
+       {true,  "RX_STASH_CFG_MERR_CNT"},
+       {true,  "AXI_RD_FBD_MERR_CNT"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+       {false, "Reserved"},
+};
+
+static struct hclge_dbg_dfx_message hclge_dbg_tqp_reg[] = {
+       {true, "q_num"},
+       {true, "RCB_CFG_RX_RING_TAIL"},
+       {true, "RCB_CFG_RX_RING_HEAD"},
+       {true, "RCB_CFG_RX_RING_FBDNUM"},
+       {true, "RCB_CFG_RX_RING_OFFSET"},
+       {true, "RCB_CFG_RX_RING_FBDOFFSET"},
+
+       {true, "RCB_CFG_RX_RING_PKTNUM_RECORD"},
+       {true, "RCB_CFG_TX_RING_TAIL"},
+       {true, "RCB_CFG_TX_RING_HEAD"},
+       {true, "RCB_CFG_TX_RING_FBDNUM"},
+       {true, "RCB_CFG_TX_RING_OFFSET"},
+       {true, "RCB_CFG_TX_RING_EBDNUM"},
+};
+
+#endif
index 6da9e22..d0f6541 100644 (file)
@@ -4,78 +4,39 @@
 #include "hclge_err.h"
 
 static const struct hclge_hw_error hclge_imp_tcm_ecc_int[] = {
-       { .int_msk = BIT(0), .msg = "imp_itcm0_ecc_1bit_err" },
        { .int_msk = BIT(1), .msg = "imp_itcm0_ecc_mbit_err" },
-       { .int_msk = BIT(2), .msg = "imp_itcm1_ecc_1bit_err" },
        { .int_msk = BIT(3), .msg = "imp_itcm1_ecc_mbit_err" },
-       { .int_msk = BIT(4), .msg = "imp_itcm2_ecc_1bit_err" },
        { .int_msk = BIT(5), .msg = "imp_itcm2_ecc_mbit_err" },
-       { .int_msk = BIT(6), .msg = "imp_itcm3_ecc_1bit_err" },
        { .int_msk = BIT(7), .msg = "imp_itcm3_ecc_mbit_err" },
-       { .int_msk = BIT(8), .msg = "imp_dtcm0_mem0_ecc_1bit_err" },
        { .int_msk = BIT(9), .msg = "imp_dtcm0_mem0_ecc_mbit_err" },
-       { .int_msk = BIT(10), .msg = "imp_dtcm0_mem1_ecc_1bit_err" },
        { .int_msk = BIT(11), .msg = "imp_dtcm0_mem1_ecc_mbit_err" },
-       { .int_msk = BIT(12), .msg = "imp_dtcm1_mem0_ecc_1bit_err" },
        { .int_msk = BIT(13), .msg = "imp_dtcm1_mem0_ecc_mbit_err" },
-       { .int_msk = BIT(14), .msg = "imp_dtcm1_mem1_ecc_1bit_err" },
        { .int_msk = BIT(15), .msg = "imp_dtcm1_mem1_ecc_mbit_err" },
-       { /* sentinel */ }
-};
-
-static const struct hclge_hw_error hclge_imp_itcm4_ecc_int[] = {
-       { .int_msk = BIT(0), .msg = "imp_itcm4_ecc_1bit_err" },
-       { .int_msk = BIT(1), .msg = "imp_itcm4_ecc_mbit_err" },
+       { .int_msk = BIT(17), .msg = "imp_itcm4_ecc_mbit_err" },
        { /* sentinel */ }
 };
 
 static const struct hclge_hw_error hclge_cmdq_nic_mem_ecc_int[] = {
-       { .int_msk = BIT(0), .msg = "cmdq_nic_rx_depth_ecc_1bit_err" },
        { .int_msk = BIT(1), .msg = "cmdq_nic_rx_depth_ecc_mbit_err" },
-       { .int_msk = BIT(2), .msg = "cmdq_nic_tx_depth_ecc_1bit_err" },
        { .int_msk = BIT(3), .msg = "cmdq_nic_tx_depth_ecc_mbit_err" },
-       { .int_msk = BIT(4), .msg = "cmdq_nic_rx_tail_ecc_1bit_err" },
        { .int_msk = BIT(5), .msg = "cmdq_nic_rx_tail_ecc_mbit_err" },
-       { .int_msk = BIT(6), .msg = "cmdq_nic_tx_tail_ecc_1bit_err" },
        { .int_msk = BIT(7), .msg = "cmdq_nic_tx_tail_ecc_mbit_err" },
-       { .int_msk = BIT(8), .msg = "cmdq_nic_rx_head_ecc_1bit_err" },
        { .int_msk = BIT(9), .msg = "cmdq_nic_rx_head_ecc_mbit_err" },
-       { .int_msk = BIT(10), .msg = "cmdq_nic_tx_head_ecc_1bit_err" },
        { .int_msk = BIT(11), .msg = "cmdq_nic_tx_head_ecc_mbit_err" },
-       { .int_msk = BIT(12), .msg = "cmdq_nic_rx_addr_ecc_1bit_err" },
        { .int_msk = BIT(13), .msg = "cmdq_nic_rx_addr_ecc_mbit_err" },
-       { .int_msk = BIT(14), .msg = "cmdq_nic_tx_addr_ecc_1bit_err" },
        { .int_msk = BIT(15), .msg = "cmdq_nic_tx_addr_ecc_mbit_err" },
-       { /* sentinel */ }
-};
-
-static const struct hclge_hw_error hclge_cmdq_rocee_mem_ecc_int[] = {
-       { .int_msk = BIT(0), .msg = "cmdq_rocee_rx_depth_ecc_1bit_err" },
-       { .int_msk = BIT(1), .msg = "cmdq_rocee_rx_depth_ecc_mbit_err" },
-       { .int_msk = BIT(2), .msg = "cmdq_rocee_tx_depth_ecc_1bit_err" },
-       { .int_msk = BIT(3), .msg = "cmdq_rocee_tx_depth_ecc_mbit_err" },
-       { .int_msk = BIT(4), .msg = "cmdq_rocee_rx_tail_ecc_1bit_err" },
-       { .int_msk = BIT(5), .msg = "cmdq_rocee_rx_tail_ecc_mbit_err" },
-       { .int_msk = BIT(6), .msg = "cmdq_rocee_tx_tail_ecc_1bit_err" },
-       { .int_msk = BIT(7), .msg = "cmdq_rocee_tx_tail_ecc_mbit_err" },
-       { .int_msk = BIT(8), .msg = "cmdq_rocee_rx_head_ecc_1bit_err" },
-       { .int_msk = BIT(9), .msg = "cmdq_rocee_rx_head_ecc_mbit_err" },
-       { .int_msk = BIT(10), .msg = "cmdq_rocee_tx_head_ecc_1bit_err" },
-       { .int_msk = BIT(11), .msg = "cmdq_rocee_tx_head_ecc_mbit_err" },
-       { .int_msk = BIT(12), .msg = "cmdq_rocee_rx_addr_ecc_1bit_err" },
-       { .int_msk = BIT(13), .msg = "cmdq_rocee_rx_addr_ecc_mbit_err" },
-       { .int_msk = BIT(14), .msg = "cmdq_rocee_tx_addr_ecc_1bit_err" },
-       { .int_msk = BIT(15), .msg = "cmdq_rocee_tx_addr_ecc_mbit_err" },
+       { .int_msk = BIT(17), .msg = "cmdq_rocee_rx_depth_ecc_mbit_err" },
+       { .int_msk = BIT(19), .msg = "cmdq_rocee_tx_depth_ecc_mbit_err" },
+       { .int_msk = BIT(21), .msg = "cmdq_rocee_rx_tail_ecc_mbit_err" },
+       { .int_msk = BIT(23), .msg = "cmdq_rocee_tx_tail_ecc_mbit_err" },
+       { .int_msk = BIT(25), .msg = "cmdq_rocee_rx_head_ecc_mbit_err" },
+       { .int_msk = BIT(27), .msg = "cmdq_rocee_tx_head_ecc_mbit_err" },
+       { .int_msk = BIT(29), .msg = "cmdq_rocee_rx_addr_ecc_mbit_err" },
+       { .int_msk = BIT(31), .msg = "cmdq_rocee_tx_addr_ecc_mbit_err" },
        { /* sentinel */ }
 };
 
 static const struct hclge_hw_error hclge_tqp_int_ecc_int[] = {
-       { .int_msk = BIT(0), .msg = "tqp_int_cfg_even_ecc_1bit_err" },
-       { .int_msk = BIT(1), .msg = "tqp_int_cfg_odd_ecc_1bit_err" },
-       { .int_msk = BIT(2), .msg = "tqp_int_ctrl_even_ecc_1bit_err" },
-       { .int_msk = BIT(3), .msg = "tqp_int_ctrl_odd_ecc_1bit_err" },
-       { .int_msk = BIT(4), .msg = "tx_que_scan_int_ecc_1bit_err" },
-       { .int_msk = BIT(5), .msg = "rx_que_scan_int_ecc_1bit_err" },
        { .int_msk = BIT(6), .msg = "tqp_int_cfg_even_ecc_mbit_err" },
        { .int_msk = BIT(7), .msg = "tqp_int_cfg_odd_ecc_mbit_err" },
        { .int_msk = BIT(8), .msg = "tqp_int_ctrl_even_ecc_mbit_err" },
@@ -85,15 +46,19 @@ static const struct hclge_hw_error hclge_tqp_int_ecc_int[] = {
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_igu_com_err_int[] = {
+static const struct hclge_hw_error hclge_msix_sram_ecc_int[] = {
+       { .int_msk = BIT(1), .msg = "msix_nic_ecc_mbit_err" },
+       { .int_msk = BIT(3), .msg = "msix_rocee_ecc_mbit_err" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_igu_int[] = {
        { .int_msk = BIT(0), .msg = "igu_rx_buf0_ecc_mbit_err" },
-       { .int_msk = BIT(1), .msg = "igu_rx_buf0_ecc_1bit_err" },
        { .int_msk = BIT(2), .msg = "igu_rx_buf1_ecc_mbit_err" },
-       { .int_msk = BIT(3), .msg = "igu_rx_buf1_ecc_1bit_err" },
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_igu_egu_tnl_err_int[] = {
+static const struct hclge_hw_error hclge_igu_egu_tnl_int[] = {
        { .int_msk = BIT(0), .msg = "rx_buf_overflow" },
        { .int_msk = BIT(1), .msg = "rx_stp_fifo_overflow" },
        { .int_msk = BIT(2), .msg = "rx_stp_fifo_undeflow" },
@@ -104,51 +69,11 @@ static const struct hclge_hw_error hclge_igu_egu_tnl_err_int[] = {
 };
 
 static const struct hclge_hw_error hclge_ncsi_err_int[] = {
-       { .int_msk = BIT(0), .msg = "ncsi_tx_ecc_1bit_err" },
        { .int_msk = BIT(1), .msg = "ncsi_tx_ecc_mbit_err" },
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_ppp_mpf_int0[] = {
-       { .int_msk = BIT(0), .msg = "vf_vlan_ad_mem_ecc_1bit_err" },
-       { .int_msk = BIT(1), .msg = "umv_mcast_group_mem_ecc_1bit_err" },
-       { .int_msk = BIT(2), .msg = "umv_key_mem0_ecc_1bit_err" },
-       { .int_msk = BIT(3), .msg = "umv_key_mem1_ecc_1bit_err" },
-       { .int_msk = BIT(4), .msg = "umv_key_mem2_ecc_1bit_err" },
-       { .int_msk = BIT(5), .msg = "umv_key_mem3_ecc_1bit_err" },
-       { .int_msk = BIT(6), .msg = "umv_ad_mem_ecc_1bit_err" },
-       { .int_msk = BIT(7), .msg = "rss_tc_mode_mem_ecc_1bit_err" },
-       { .int_msk = BIT(8), .msg = "rss_idt_mem0_ecc_1bit_err" },
-       { .int_msk = BIT(9), .msg = "rss_idt_mem1_ecc_1bit_err" },
-       { .int_msk = BIT(10), .msg = "rss_idt_mem2_ecc_1bit_err" },
-       { .int_msk = BIT(11), .msg = "rss_idt_mem3_ecc_1bit_err" },
-       { .int_msk = BIT(12), .msg = "rss_idt_mem4_ecc_1bit_err" },
-       { .int_msk = BIT(13), .msg = "rss_idt_mem5_ecc_1bit_err" },
-       { .int_msk = BIT(14), .msg = "rss_idt_mem6_ecc_1bit_err" },
-       { .int_msk = BIT(15), .msg = "rss_idt_mem7_ecc_1bit_err" },
-       { .int_msk = BIT(16), .msg = "rss_idt_mem8_ecc_1bit_err" },
-       { .int_msk = BIT(17), .msg = "rss_idt_mem9_ecc_1bit_err" },
-       { .int_msk = BIT(18), .msg = "rss_idt_mem10_ecc_1bit_err" },
-       { .int_msk = BIT(19), .msg = "rss_idt_mem11_ecc_1bit_err" },
-       { .int_msk = BIT(20), .msg = "rss_idt_mem12_ecc_1bit_err" },
-       { .int_msk = BIT(21), .msg = "rss_idt_mem13_ecc_1bit_err" },
-       { .int_msk = BIT(22), .msg = "rss_idt_mem14_ecc_1bit_err" },
-       { .int_msk = BIT(23), .msg = "rss_idt_mem15_ecc_1bit_err" },
-       { .int_msk = BIT(24), .msg = "port_vlan_mem_ecc_1bit_err" },
-       { .int_msk = BIT(25), .msg = "mcast_linear_table_mem_ecc_1bit_err" },
-       { .int_msk = BIT(26), .msg = "mcast_result_mem_ecc_1bit_err" },
-       { .int_msk = BIT(27),
-               .msg = "flow_director_ad_mem0_ecc_1bit_err" },
-       { .int_msk = BIT(28),
-               .msg = "flow_director_ad_mem1_ecc_1bit_err" },
-       { .int_msk = BIT(29),
-               .msg = "rx_vlan_tag_memory_ecc_1bit_err" },
-       { .int_msk = BIT(30),
-               .msg = "Tx_UP_mapping_config_mem_ecc_1bit_err" },
-       { /* sentinel */ }
-};
-
-static const struct hclge_hw_error hclge_ppp_mpf_int1[] = {
+static const struct hclge_hw_error hclge_ppp_mpf_abnormal_int_st1[] = {
        { .int_msk = BIT(0), .msg = "vf_vlan_ad_mem_ecc_mbit_err" },
        { .int_msk = BIT(1), .msg = "umv_mcast_group_mem_ecc_mbit_err" },
        { .int_msk = BIT(2), .msg = "umv_key_mem0_ecc_mbit_err" },
@@ -187,23 +112,13 @@ static const struct hclge_hw_error hclge_ppp_mpf_int1[] = {
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_ppp_pf_int[] = {
-       { .int_msk = BIT(0), .msg = "Tx_vlan_tag_err" },
+static const struct hclge_hw_error hclge_ppp_pf_abnormal_int[] = {
+       { .int_msk = BIT(0), .msg = "tx_vlan_tag_err" },
        { .int_msk = BIT(1), .msg = "rss_list_tc_unassigned_queue_err" },
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_ppp_mpf_int2[] = {
-       { .int_msk = BIT(0), .msg = "hfs_fifo_mem_ecc_1bit_err" },
-       { .int_msk = BIT(1), .msg = "rslt_descr_fifo_mem_ecc_1bit_err" },
-       { .int_msk = BIT(2), .msg = "tx_vlan_tag_mem_ecc_1bit_err" },
-       { .int_msk = BIT(3), .msg = "FD_CN0_memory_ecc_1bit_err" },
-       { .int_msk = BIT(4), .msg = "FD_CN1_memory_ecc_1bit_err" },
-       { .int_msk = BIT(5), .msg = "GRO_AD_memory_ecc_1bit_err" },
-       { /* sentinel */ }
-};
-
-static const struct hclge_hw_error hclge_ppp_mpf_int3[] = {
+static const struct hclge_hw_error hclge_ppp_mpf_abnormal_int_st3[] = {
        { .int_msk = BIT(0), .msg = "hfs_fifo_mem_ecc_mbit_err" },
        { .int_msk = BIT(1), .msg = "rslt_descr_fifo_mem_ecc_mbit_err" },
        { .int_msk = BIT(2), .msg = "tx_vlan_tag_mem_ecc_mbit_err" },
@@ -213,145 +128,248 @@ static const struct hclge_hw_error hclge_ppp_mpf_int3[] = {
        { /* sentinel */ }
 };
 
-struct hclge_tm_sch_ecc_info {
-       const char *name;
-};
-
-static const struct hclge_tm_sch_ecc_info hclge_tm_sch_ecc_err[7][15] = {
-       {
-               { .name = "QSET_QUEUE_CTRL:PRI_LEN TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPA_LEN TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPB_LEN TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRA_LEN TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRB_LEN TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPA_HPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPB_HPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRA_HPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRB_HPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:QS_LINKLIST TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPA_TPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPB_TPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRA_TPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRB_TPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:QS_DEFICITCNT TAB" },
-       },
-       {
-               { .name = "ROCE_QUEUE_CTRL:QS_LEN TAB" },
-               { .name = "ROCE_QUEUE_CTRL:QS_TPTR TAB" },
-               { .name = "ROCE_QUEUE_CTRL:QS_HPTR TAB" },
-               { .name = "ROCE_QUEUE_CTRL:QLINKLIST TAB" },
-               { .name = "ROCE_QUEUE_CTRL:QCLEN TAB" },
-       },
-       {
-               { .name = "NIC_QUEUE_CTRL:QS_LEN TAB" },
-               { .name = "NIC_QUEUE_CTRL:QS_TPTR TAB" },
-               { .name = "NIC_QUEUE_CTRL:QS_HPTR TAB" },
-               { .name = "NIC_QUEUE_CTRL:QLINKLIST TAB" },
-               { .name = "NIC_QUEUE_CTRL:QCLEN TAB" },
-       },
-       {
-               { .name = "RAM_CFG_CTRL:CSHAP TAB" },
-               { .name = "RAM_CFG_CTRL:PSHAP TAB" },
-       },
-       {
-               { .name = "SHAPER_CTRL:PSHAP TAB" },
-       },
-       {
-               { .name = "MSCH_CTRL" },
-       },
-       {
-               { .name = "TOP_CTRL" },
-       },
-};
-
-static const struct hclge_hw_error hclge_tm_sch_err_int[] = {
-       { .int_msk = BIT(0), .msg = "tm_sch_ecc_1bit_err" },
+static const struct hclge_hw_error hclge_tm_sch_rint[] = {
        { .int_msk = BIT(1), .msg = "tm_sch_ecc_mbit_err" },
-       { .int_msk = BIT(2), .msg = "tm_sch_port_shap_sub_fifo_wr_full_err" },
-       { .int_msk = BIT(3), .msg = "tm_sch_port_shap_sub_fifo_rd_empty_err" },
-       { .int_msk = BIT(4), .msg = "tm_sch_pg_pshap_sub_fifo_wr_full_err" },
-       { .int_msk = BIT(5), .msg = "tm_sch_pg_pshap_sub_fifo_rd_empty_err" },
-       { .int_msk = BIT(6), .msg = "tm_sch_pg_cshap_sub_fifo_wr_full_err" },
-       { .int_msk = BIT(7), .msg = "tm_sch_pg_cshap_sub_fifo_rd_empty_err" },
-       { .int_msk = BIT(8), .msg = "tm_sch_pri_pshap_sub_fifo_wr_full_err" },
-       { .int_msk = BIT(9), .msg = "tm_sch_pri_pshap_sub_fifo_rd_empty_err" },
-       { .int_msk = BIT(10), .msg = "tm_sch_pri_cshap_sub_fifo_wr_full_err" },
-       { .int_msk = BIT(11), .msg = "tm_sch_pri_cshap_sub_fifo_rd_empty_err" },
+       { .int_msk = BIT(2), .msg = "tm_sch_port_shap_sub_fifo_wr_err" },
+       { .int_msk = BIT(3), .msg = "tm_sch_port_shap_sub_fifo_rd_err" },
+       { .int_msk = BIT(4), .msg = "tm_sch_pg_pshap_sub_fifo_wr_err" },
+       { .int_msk = BIT(5), .msg = "tm_sch_pg_pshap_sub_fifo_rd_err" },
+       { .int_msk = BIT(6), .msg = "tm_sch_pg_cshap_sub_fifo_wr_err" },
+       { .int_msk = BIT(7), .msg = "tm_sch_pg_cshap_sub_fifo_rd_err" },
+       { .int_msk = BIT(8), .msg = "tm_sch_pri_pshap_sub_fifo_wr_err" },
+       { .int_msk = BIT(9), .msg = "tm_sch_pri_pshap_sub_fifo_rd_err" },
+       { .int_msk = BIT(10), .msg = "tm_sch_pri_cshap_sub_fifo_wr_err" },
+       { .int_msk = BIT(11), .msg = "tm_sch_pri_cshap_sub_fifo_rd_err" },
        { .int_msk = BIT(12),
-         .msg = "tm_sch_port_shap_offset_fifo_wr_full_err" },
+         .msg = "tm_sch_port_shap_offset_fifo_wr_err" },
        { .int_msk = BIT(13),
-         .msg = "tm_sch_port_shap_offset_fifo_rd_empty_err" },
+         .msg = "tm_sch_port_shap_offset_fifo_rd_err" },
        { .int_msk = BIT(14),
-         .msg = "tm_sch_pg_pshap_offset_fifo_wr_full_err" },
+         .msg = "tm_sch_pg_pshap_offset_fifo_wr_err" },
        { .int_msk = BIT(15),
-         .msg = "tm_sch_pg_pshap_offset_fifo_rd_empty_err" },
+         .msg = "tm_sch_pg_pshap_offset_fifo_rd_err" },
        { .int_msk = BIT(16),
-         .msg = "tm_sch_pg_cshap_offset_fifo_wr_full_err" },
+         .msg = "tm_sch_pg_cshap_offset_fifo_wr_err" },
        { .int_msk = BIT(17),
-         .msg = "tm_sch_pg_cshap_offset_fifo_rd_empty_err" },
+         .msg = "tm_sch_pg_cshap_offset_fifo_rd_err" },
        { .int_msk = BIT(18),
-         .msg = "tm_sch_pri_pshap_offset_fifo_wr_full_err" },
+         .msg = "tm_sch_pri_pshap_offset_fifo_wr_err" },
        { .int_msk = BIT(19),
-         .msg = "tm_sch_pri_pshap_offset_fifo_rd_empty_err" },
+         .msg = "tm_sch_pri_pshap_offset_fifo_rd_err" },
        { .int_msk = BIT(20),
-         .msg = "tm_sch_pri_cshap_offset_fifo_wr_full_err" },
+         .msg = "tm_sch_pri_cshap_offset_fifo_wr_err" },
        { .int_msk = BIT(21),
-         .msg = "tm_sch_pri_cshap_offset_fifo_rd_empty_err" },
-       { .int_msk = BIT(22), .msg = "tm_sch_rq_fifo_wr_full_err" },
-       { .int_msk = BIT(23), .msg = "tm_sch_rq_fifo_rd_empty_err" },
-       { .int_msk = BIT(24), .msg = "tm_sch_nq_fifo_wr_full_err" },
-       { .int_msk = BIT(25), .msg = "tm_sch_nq_fifo_rd_empty_err" },
-       { .int_msk = BIT(26), .msg = "tm_sch_roce_up_fifo_wr_full_err" },
-       { .int_msk = BIT(27), .msg = "tm_sch_roce_up_fifo_rd_empty_err" },
-       { .int_msk = BIT(28), .msg = "tm_sch_rcb_byte_fifo_wr_full_err" },
-       { .int_msk = BIT(29), .msg = "tm_sch_rcb_byte_fifo_rd_empty_err" },
-       { .int_msk = BIT(30), .msg = "tm_sch_ssu_byte_fifo_wr_full_err" },
-       { .int_msk = BIT(31), .msg = "tm_sch_ssu_byte_fifo_rd_empty_err" },
+         .msg = "tm_sch_pri_cshap_offset_fifo_rd_err" },
+       { .int_msk = BIT(22), .msg = "tm_sch_rq_fifo_wr_err" },
+       { .int_msk = BIT(23), .msg = "tm_sch_rq_fifo_rd_err" },
+       { .int_msk = BIT(24), .msg = "tm_sch_nq_fifo_wr_err" },
+       { .int_msk = BIT(25), .msg = "tm_sch_nq_fifo_rd_err" },
+       { .int_msk = BIT(26), .msg = "tm_sch_roce_up_fifo_wr_err" },
+       { .int_msk = BIT(27), .msg = "tm_sch_roce_up_fifo_rd_err" },
+       { .int_msk = BIT(28), .msg = "tm_sch_rcb_byte_fifo_wr_err" },
+       { .int_msk = BIT(29), .msg = "tm_sch_rcb_byte_fifo_rd_err" },
+       { .int_msk = BIT(30), .msg = "tm_sch_ssu_byte_fifo_wr_err" },
+       { .int_msk = BIT(31), .msg = "tm_sch_ssu_byte_fifo_rd_err" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_qcn_fifo_rint[] = {
+       { .int_msk = BIT(0), .msg = "qcn_shap_gp0_sch_fifo_rd_err" },
+       { .int_msk = BIT(1), .msg = "qcn_shap_gp0_sch_fifo_wr_err" },
+       { .int_msk = BIT(2), .msg = "qcn_shap_gp1_sch_fifo_rd_err" },
+       { .int_msk = BIT(3), .msg = "qcn_shap_gp1_sch_fifo_wr_err" },
+       { .int_msk = BIT(4), .msg = "qcn_shap_gp2_sch_fifo_rd_err" },
+       { .int_msk = BIT(5), .msg = "qcn_shap_gp2_sch_fifo_wr_err" },
+       { .int_msk = BIT(6), .msg = "qcn_shap_gp3_sch_fifo_rd_err" },
+       { .int_msk = BIT(7), .msg = "qcn_shap_gp3_sch_fifo_wr_err" },
+       { .int_msk = BIT(8), .msg = "qcn_shap_gp0_offset_fifo_rd_err" },
+       { .int_msk = BIT(9), .msg = "qcn_shap_gp0_offset_fifo_wr_err" },
+       { .int_msk = BIT(10), .msg = "qcn_shap_gp1_offset_fifo_rd_err" },
+       { .int_msk = BIT(11), .msg = "qcn_shap_gp1_offset_fifo_wr_err" },
+       { .int_msk = BIT(12), .msg = "qcn_shap_gp2_offset_fifo_rd_err" },
+       { .int_msk = BIT(13), .msg = "qcn_shap_gp2_offset_fifo_wr_err" },
+       { .int_msk = BIT(14), .msg = "qcn_shap_gp3_offset_fifo_rd_err" },
+       { .int_msk = BIT(15), .msg = "qcn_shap_gp3_offset_fifo_wr_err" },
+       { .int_msk = BIT(16), .msg = "qcn_byte_info_fifo_rd_err" },
+       { .int_msk = BIT(17), .msg = "qcn_byte_info_fifo_wr_err" },
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_qcn_ecc_err_int[] = {
-       { .int_msk = BIT(0), .msg = "qcn_byte_mem_ecc_1bit_err" },
+static const struct hclge_hw_error hclge_qcn_ecc_rint[] = {
        { .int_msk = BIT(1), .msg = "qcn_byte_mem_ecc_mbit_err" },
-       { .int_msk = BIT(2), .msg = "qcn_time_mem_ecc_1bit_err" },
        { .int_msk = BIT(3), .msg = "qcn_time_mem_ecc_mbit_err" },
-       { .int_msk = BIT(4), .msg = "qcn_fb_mem_ecc_1bit_err" },
        { .int_msk = BIT(5), .msg = "qcn_fb_mem_ecc_mbit_err" },
-       { .int_msk = BIT(6), .msg = "qcn_link_mem_ecc_1bit_err" },
        { .int_msk = BIT(7), .msg = "qcn_link_mem_ecc_mbit_err" },
-       { .int_msk = BIT(8), .msg = "qcn_rate_mem_ecc_1bit_err" },
        { .int_msk = BIT(9), .msg = "qcn_rate_mem_ecc_mbit_err" },
-       { .int_msk = BIT(10), .msg = "qcn_tmplt_mem_ecc_1bit_err" },
        { .int_msk = BIT(11), .msg = "qcn_tmplt_mem_ecc_mbit_err" },
-       { .int_msk = BIT(12), .msg = "qcn_shap_cfg_mem_ecc_1bit_err" },
        { .int_msk = BIT(13), .msg = "qcn_shap_cfg_mem_ecc_mbit_err" },
-       { .int_msk = BIT(14), .msg = "qcn_gp0_barrel_mem_ecc_1bit_err" },
        { .int_msk = BIT(15), .msg = "qcn_gp0_barrel_mem_ecc_mbit_err" },
-       { .int_msk = BIT(16), .msg = "qcn_gp1_barrel_mem_ecc_1bit_err" },
        { .int_msk = BIT(17), .msg = "qcn_gp1_barrel_mem_ecc_mbit_err" },
-       { .int_msk = BIT(18), .msg = "qcn_gp2_barrel_mem_ecc_1bit_err" },
        { .int_msk = BIT(19), .msg = "qcn_gp2_barrel_mem_ecc_mbit_err" },
-       { .int_msk = BIT(20), .msg = "qcn_gp3_barral_mem_ecc_1bit_err" },
        { .int_msk = BIT(21), .msg = "qcn_gp3_barral_mem_ecc_mbit_err" },
        { /* sentinel */ }
 };
 
-static void hclge_log_error(struct device *dev,
-                           const struct hclge_hw_error *err_list,
+static const struct hclge_hw_error hclge_mac_afifo_tnl_int[] = {
+       { .int_msk = BIT(0), .msg = "egu_cge_afifo_ecc_1bit_err" },
+       { .int_msk = BIT(1), .msg = "egu_cge_afifo_ecc_mbit_err" },
+       { .int_msk = BIT(2), .msg = "egu_lge_afifo_ecc_1bit_err" },
+       { .int_msk = BIT(3), .msg = "egu_lge_afifo_ecc_mbit_err" },
+       { .int_msk = BIT(4), .msg = "cge_igu_afifo_ecc_1bit_err" },
+       { .int_msk = BIT(5), .msg = "cge_igu_afifo_ecc_mbit_err" },
+       { .int_msk = BIT(6), .msg = "lge_igu_afifo_ecc_1bit_err" },
+       { .int_msk = BIT(7), .msg = "lge_igu_afifo_ecc_mbit_err" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ppu_mpf_abnormal_int_st2[] = {
+       { .int_msk = BIT(13), .msg = "rpu_rx_pkt_bit32_ecc_mbit_err" },
+       { .int_msk = BIT(14), .msg = "rpu_rx_pkt_bit33_ecc_mbit_err" },
+       { .int_msk = BIT(15), .msg = "rpu_rx_pkt_bit34_ecc_mbit_err" },
+       { .int_msk = BIT(16), .msg = "rpu_rx_pkt_bit35_ecc_mbit_err" },
+       { .int_msk = BIT(17), .msg = "rcb_tx_ring_ecc_mbit_err" },
+       { .int_msk = BIT(18), .msg = "rcb_rx_ring_ecc_mbit_err" },
+       { .int_msk = BIT(19), .msg = "rcb_tx_fbd_ecc_mbit_err" },
+       { .int_msk = BIT(20), .msg = "rcb_rx_ebd_ecc_mbit_err" },
+       { .int_msk = BIT(21), .msg = "rcb_tso_info_ecc_mbit_err" },
+       { .int_msk = BIT(22), .msg = "rcb_tx_int_info_ecc_mbit_err" },
+       { .int_msk = BIT(23), .msg = "rcb_rx_int_info_ecc_mbit_err" },
+       { .int_msk = BIT(24), .msg = "tpu_tx_pkt_0_ecc_mbit_err" },
+       { .int_msk = BIT(25), .msg = "tpu_tx_pkt_1_ecc_mbit_err" },
+       { .int_msk = BIT(26), .msg = "rd_bus_err" },
+       { .int_msk = BIT(27), .msg = "wr_bus_err" },
+       { .int_msk = BIT(28), .msg = "reg_search_miss" },
+       { .int_msk = BIT(29), .msg = "rx_q_search_miss" },
+       { .int_msk = BIT(30), .msg = "ooo_ecc_err_detect" },
+       { .int_msk = BIT(31), .msg = "ooo_ecc_err_multpl" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ppu_mpf_abnormal_int_st3[] = {
+       { .int_msk = BIT(4), .msg = "gro_bd_ecc_mbit_err" },
+       { .int_msk = BIT(5), .msg = "gro_context_ecc_mbit_err" },
+       { .int_msk = BIT(6), .msg = "rx_stash_cfg_ecc_mbit_err" },
+       { .int_msk = BIT(7), .msg = "axi_rd_fbd_ecc_mbit_err" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ppu_pf_abnormal_int[] = {
+       { .int_msk = BIT(0), .msg = "over_8bd_no_fe" },
+       { .int_msk = BIT(1), .msg = "tso_mss_cmp_min_err" },
+       { .int_msk = BIT(2), .msg = "tso_mss_cmp_max_err" },
+       { .int_msk = BIT(3), .msg = "tx_rd_fbd_poison" },
+       { .int_msk = BIT(4), .msg = "rx_rd_ebd_poison" },
+       { .int_msk = BIT(5), .msg = "buf_wait_timeout" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ssu_com_err_int[] = {
+       { .int_msk = BIT(0), .msg = "buf_sum_err" },
+       { .int_msk = BIT(1), .msg = "ppp_mb_num_err" },
+       { .int_msk = BIT(2), .msg = "ppp_mbid_err" },
+       { .int_msk = BIT(3), .msg = "ppp_rlt_mac_err" },
+       { .int_msk = BIT(4), .msg = "ppp_rlt_host_err" },
+       { .int_msk = BIT(5), .msg = "cks_edit_position_err" },
+       { .int_msk = BIT(6), .msg = "cks_edit_condition_err" },
+       { .int_msk = BIT(7), .msg = "vlan_edit_condition_err" },
+       { .int_msk = BIT(8), .msg = "vlan_num_ot_err" },
+       { .int_msk = BIT(9), .msg = "vlan_num_in_err" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ssu_port_based_err_int[] = {
+       { .int_msk = BIT(0), .msg = "roc_pkt_without_key_port" },
+       { .int_msk = BIT(1), .msg = "tpu_pkt_without_key_port" },
+       { .int_msk = BIT(2), .msg = "igu_pkt_without_key_port" },
+       { .int_msk = BIT(3), .msg = "roc_eof_mis_match_port" },
+       { .int_msk = BIT(4), .msg = "tpu_eof_mis_match_port" },
+       { .int_msk = BIT(5), .msg = "igu_eof_mis_match_port" },
+       { .int_msk = BIT(6), .msg = "roc_sof_mis_match_port" },
+       { .int_msk = BIT(7), .msg = "tpu_sof_mis_match_port" },
+       { .int_msk = BIT(8), .msg = "igu_sof_mis_match_port" },
+       { .int_msk = BIT(11), .msg = "ets_rd_int_rx_port" },
+       { .int_msk = BIT(12), .msg = "ets_wr_int_rx_port" },
+       { .int_msk = BIT(13), .msg = "ets_rd_int_tx_port" },
+       { .int_msk = BIT(14), .msg = "ets_wr_int_tx_port" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ssu_fifo_overflow_int[] = {
+       { .int_msk = BIT(0), .msg = "ig_mac_inf_int" },
+       { .int_msk = BIT(1), .msg = "ig_host_inf_int" },
+       { .int_msk = BIT(2), .msg = "ig_roc_buf_int" },
+       { .int_msk = BIT(3), .msg = "ig_host_data_fifo_int" },
+       { .int_msk = BIT(4), .msg = "ig_host_key_fifo_int" },
+       { .int_msk = BIT(5), .msg = "tx_qcn_fifo_int" },
+       { .int_msk = BIT(6), .msg = "rx_qcn_fifo_int" },
+       { .int_msk = BIT(7), .msg = "tx_pf_rd_fifo_int" },
+       { .int_msk = BIT(8), .msg = "rx_pf_rd_fifo_int" },
+       { .int_msk = BIT(9), .msg = "qm_eof_fifo_int" },
+       { .int_msk = BIT(10), .msg = "mb_rlt_fifo_int" },
+       { .int_msk = BIT(11), .msg = "dup_uncopy_fifo_int" },
+       { .int_msk = BIT(12), .msg = "dup_cnt_rd_fifo_int" },
+       { .int_msk = BIT(13), .msg = "dup_cnt_drop_fifo_int" },
+       { .int_msk = BIT(14), .msg = "dup_cnt_wrb_fifo_int" },
+       { .int_msk = BIT(15), .msg = "host_cmd_fifo_int" },
+       { .int_msk = BIT(16), .msg = "mac_cmd_fifo_int" },
+       { .int_msk = BIT(17), .msg = "host_cmd_bitmap_empty_int" },
+       { .int_msk = BIT(18), .msg = "mac_cmd_bitmap_empty_int" },
+       { .int_msk = BIT(19), .msg = "dup_bitmap_empty_int" },
+       { .int_msk = BIT(20), .msg = "out_queue_bitmap_empty_int" },
+       { .int_msk = BIT(21), .msg = "bank2_bitmap_empty_int" },
+       { .int_msk = BIT(22), .msg = "bank1_bitmap_empty_int" },
+       { .int_msk = BIT(23), .msg = "bank0_bitmap_empty_int" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ssu_ets_tcg_int[] = {
+       { .int_msk = BIT(0), .msg = "ets_rd_int_rx_tcg" },
+       { .int_msk = BIT(1), .msg = "ets_wr_int_rx_tcg" },
+       { .int_msk = BIT(2), .msg = "ets_rd_int_tx_tcg" },
+       { .int_msk = BIT(3), .msg = "ets_wr_int_tx_tcg" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ssu_port_based_pf_int[] = {
+       { .int_msk = BIT(0), .msg = "roc_pkt_without_key_port" },
+       { .int_msk = BIT(9), .msg = "low_water_line_err_port" },
+       { .int_msk = BIT(10), .msg = "hi_water_line_err_port" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_rocee_qmm_ovf_err_int[] = {
+       { .int_msk = 0, .msg = "rocee qmm ovf: sgid invalid err" },
+       { .int_msk = 0x4, .msg = "rocee qmm ovf: sgid ovf err" },
+       { .int_msk = 0x8, .msg = "rocee qmm ovf: smac invalid err" },
+       { .int_msk = 0xC, .msg = "rocee qmm ovf: smac ovf err" },
+       { .int_msk = 0x10, .msg = "rocee qmm ovf: cqc invalid err" },
+       { .int_msk = 0x11, .msg = "rocee qmm ovf: cqc ovf err" },
+       { .int_msk = 0x12, .msg = "rocee qmm ovf: cqc hopnum err" },
+       { .int_msk = 0x13, .msg = "rocee qmm ovf: cqc ba0 err" },
+       { .int_msk = 0x14, .msg = "rocee qmm ovf: srqc invalid err" },
+       { .int_msk = 0x15, .msg = "rocee qmm ovf: srqc ovf err" },
+       { .int_msk = 0x16, .msg = "rocee qmm ovf: srqc hopnum err" },
+       { .int_msk = 0x17, .msg = "rocee qmm ovf: srqc ba0 err" },
+       { .int_msk = 0x18, .msg = "rocee qmm ovf: mpt invalid err" },
+       { .int_msk = 0x19, .msg = "rocee qmm ovf: mpt ovf err" },
+       { .int_msk = 0x1A, .msg = "rocee qmm ovf: mpt hopnum err" },
+       { .int_msk = 0x1B, .msg = "rocee qmm ovf: mpt ba0 err" },
+       { .int_msk = 0x1C, .msg = "rocee qmm ovf: qpc invalid err" },
+       { .int_msk = 0x1D, .msg = "rocee qmm ovf: qpc ovf err" },
+       { .int_msk = 0x1E, .msg = "rocee qmm ovf: qpc hopnum err" },
+       { .int_msk = 0x1F, .msg = "rocee qmm ovf: qpc ba0 err" },
+       { /* sentinel */ }
+};
+
+static void hclge_log_error(struct device *dev, char *reg,
+                           const struct hclge_hw_error *err,
                            u32 err_sts)
 {
-       const struct hclge_hw_error *err;
-       int i = 0;
-
-       while (err_list[i].msg) {
-               err = &err_list[i];
-               if (!(err->int_msk & err_sts)) {
-                       i++;
-                       continue;
-               }
-               dev_warn(dev, "%s [error status=0x%x] found\n",
-                        err->msg, err_sts);
-               i++;
+       while (err->msg) {
+               if (err->int_msk & err_sts)
+                       dev_warn(dev, "%s %s found [error status=0x%x]\n",
+                                reg, err->msg, err_sts);
+               err++;
        }
 }
 
@@ -391,96 +409,44 @@ static int hclge_cmd_query_error(struct hclge_dev *hdev,
        return ret;
 }
 
-/* hclge_cmd_clear_error: clear the error status
- * @hdev: pointer to struct hclge_dev
- * @desc: descriptor for describing the command
- * @desc_src: prefilled descriptor from the previous command for reusing
- * @cmd:  command opcode
- * @flag: flag for extended command structure
- *
- * This function clear the error status in the hw register/s using command
- */
-static int hclge_cmd_clear_error(struct hclge_dev *hdev,
-                                struct hclge_desc *desc,
-                                struct hclge_desc *desc_src,
-                                u32 cmd, u16 flag)
-{
-       struct device *dev = &hdev->pdev->dev;
-       int num = 1;
-       int ret, i;
-
-       if (cmd) {
-               hclge_cmd_setup_basic_desc(&desc[0], cmd, false);
-               if (flag) {
-                       desc[0].flag |= cpu_to_le16(flag);
-                       hclge_cmd_setup_basic_desc(&desc[1], cmd, false);
-                       num = 2;
-               }
-               if (desc_src) {
-                       for (i = 0; i < 6; i++) {
-                               desc[0].data[i] = desc_src[0].data[i];
-                               if (flag)
-                                       desc[1].data[i] = desc_src[1].data[i];
-                       }
-               }
-       } else {
-               hclge_cmd_reuse_desc(&desc[0], false);
-               if (flag) {
-                       desc[0].flag |= cpu_to_le16(flag);
-                       hclge_cmd_reuse_desc(&desc[1], false);
-                       num = 2;
-               }
-       }
-       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
-       if (ret)
-               dev_err(dev, "clear error cmd failed (%d)\n", ret);
-
-       return ret;
-}
-
-static int hclge_enable_common_error(struct hclge_dev *hdev, bool en)
+static int hclge_config_common_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc[2];
        int ret;
 
+       /* configure common error interrupts */
        hclge_cmd_setup_basic_desc(&desc[0], HCLGE_COMMON_ECC_INT_CFG, false);
        desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
        hclge_cmd_setup_basic_desc(&desc[1], HCLGE_COMMON_ECC_INT_CFG, false);
 
        if (en) {
-               /* enable COMMON error interrupts */
                desc[0].data[0] = cpu_to_le32(HCLGE_IMP_TCM_ECC_ERR_INT_EN);
                desc[0].data[2] = cpu_to_le32(HCLGE_CMDQ_NIC_ECC_ERR_INT_EN |
                                        HCLGE_CMDQ_ROCEE_ECC_ERR_INT_EN);
                desc[0].data[3] = cpu_to_le32(HCLGE_IMP_RD_POISON_ERR_INT_EN);
-               desc[0].data[4] = cpu_to_le32(HCLGE_TQP_ECC_ERR_INT_EN);
+               desc[0].data[4] = cpu_to_le32(HCLGE_TQP_ECC_ERR_INT_EN |
+                                             HCLGE_MSIX_SRAM_ECC_ERR_INT_EN);
                desc[0].data[5] = cpu_to_le32(HCLGE_IMP_ITCM4_ECC_ERR_INT_EN);
-       } else {
-               /* disable COMMON error interrupts */
-               desc[0].data[0] = 0;
-               desc[0].data[2] = 0;
-               desc[0].data[3] = 0;
-               desc[0].data[4] = 0;
-               desc[0].data[5] = 0;
        }
+
        desc[1].data[0] = cpu_to_le32(HCLGE_IMP_TCM_ECC_ERR_INT_EN_MASK);
        desc[1].data[2] = cpu_to_le32(HCLGE_CMDQ_NIC_ECC_ERR_INT_EN_MASK |
                                HCLGE_CMDQ_ROCEE_ECC_ERR_INT_EN_MASK);
        desc[1].data[3] = cpu_to_le32(HCLGE_IMP_RD_POISON_ERR_INT_EN_MASK);
-       desc[1].data[4] = cpu_to_le32(HCLGE_TQP_ECC_ERR_INT_EN_MASK);
+       desc[1].data[4] = cpu_to_le32(HCLGE_TQP_ECC_ERR_INT_EN_MASK |
+                                     HCLGE_MSIX_SRAM_ECC_ERR_INT_EN_MASK);
        desc[1].data[5] = cpu_to_le32(HCLGE_IMP_ITCM4_ECC_ERR_INT_EN_MASK);
 
        ret = hclge_cmd_send(&hdev->hw, &desc[0], 2);
        if (ret)
                dev_err(dev,
-                       "failed(%d) to enable/disable COMMON err interrupts\n",
-                       ret);
+                       "fail(%d) to configure common err interrupts\n", ret);
 
        return ret;
 }
 
-static int hclge_enable_ncsi_error(struct hclge_dev *hdev, bool en)
+static int hclge_config_ncsi_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc;
@@ -489,74 +455,65 @@ static int hclge_enable_ncsi_error(struct hclge_dev *hdev, bool en)
        if (hdev->pdev->revision < 0x21)
                return 0;
 
-       /* enable/disable NCSI  error interrupts */
+       /* configure NCSI error interrupts */
        hclge_cmd_setup_basic_desc(&desc, HCLGE_NCSI_INT_EN, false);
        if (en)
                desc.data[0] = cpu_to_le32(HCLGE_NCSI_ERR_INT_EN);
-       else
-               desc.data[0] = 0;
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret)
                dev_err(dev,
-                       "failed(%d) to enable/disable NCSI error interrupts\n",
-                       ret);
+                       "fail(%d) to configure  NCSI error interrupts\n", ret);
 
        return ret;
 }
 
-static int hclge_enable_igu_egu_error(struct hclge_dev *hdev, bool en)
+static int hclge_config_igu_egu_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc;
        int ret;
 
-       /* enable/disable error interrupts */
+       /* configure IGU,EGU error interrupts */
        hclge_cmd_setup_basic_desc(&desc, HCLGE_IGU_COMMON_INT_EN, false);
        if (en)
                desc.data[0] = cpu_to_le32(HCLGE_IGU_ERR_INT_EN);
-       else
-               desc.data[0] = 0;
+
        desc.data[1] = cpu_to_le32(HCLGE_IGU_ERR_INT_EN_MASK);
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret) {
                dev_err(dev,
-                       "failed(%d) to enable/disable IGU common interrupts\n",
-                       ret);
+                       "fail(%d) to configure IGU common interrupts\n", ret);
                return ret;
        }
 
        hclge_cmd_setup_basic_desc(&desc, HCLGE_IGU_EGU_TNL_INT_EN, false);
        if (en)
                desc.data[0] = cpu_to_le32(HCLGE_IGU_TNL_ERR_INT_EN);
-       else
-               desc.data[0] = 0;
+
        desc.data[1] = cpu_to_le32(HCLGE_IGU_TNL_ERR_INT_EN_MASK);
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret) {
                dev_err(dev,
-                       "failed(%d) to enable/disable IGU-EGU TNL interrupts\n",
-                       ret);
+                       "fail(%d) to configure IGU-EGU TNL interrupts\n", ret);
                return ret;
        }
 
-       ret = hclge_enable_ncsi_error(hdev, en);
-       if (ret)
-               dev_err(dev, "fail(%d) to en/disable err int\n", ret);
+       ret = hclge_config_ncsi_hw_err_int(hdev, en);
 
        return ret;
 }
 
-static int hclge_enable_ppp_error_interrupt(struct hclge_dev *hdev, u32 cmd,
+static int hclge_config_ppp_error_interrupt(struct hclge_dev *hdev, u32 cmd,
                                            bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc[2];
        int ret;
 
-       /* enable/disable PPP error interrupts */
+       /* configure PPP error interrupts */
        hclge_cmd_setup_basic_desc(&desc[0], cmd, false);
        desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
        hclge_cmd_setup_basic_desc(&desc[1], cmd, false);
@@ -567,24 +524,24 @@ static int hclge_enable_ppp_error_interrupt(struct hclge_dev *hdev, u32 cmd,
                                cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT0_EN);
                        desc[0].data[1] =
                                cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT1_EN);
-               } else {
-                       desc[0].data[0] = 0;
-                       desc[0].data[1] = 0;
+                       desc[0].data[4] = cpu_to_le32(HCLGE_PPP_PF_ERR_INT_EN);
                }
+
                desc[1].data[0] =
                        cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT0_EN_MASK);
                desc[1].data[1] =
                        cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT1_EN_MASK);
+               if (hdev->pdev->revision >= 0x21)
+                       desc[1].data[2] =
+                               cpu_to_le32(HCLGE_PPP_PF_ERR_INT_EN_MASK);
        } else if (cmd == HCLGE_PPP_CMD1_INT_CMD) {
                if (en) {
                        desc[0].data[0] =
                                cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT2_EN);
                        desc[0].data[1] =
                                cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT3_EN);
-               } else {
-                       desc[0].data[0] = 0;
-                       desc[0].data[1] = 0;
                }
+
                desc[1].data[0] =
                                cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT2_EN_MASK);
                desc[1].data[1] =
@@ -593,491 +550,863 @@ static int hclge_enable_ppp_error_interrupt(struct hclge_dev *hdev, u32 cmd,
 
        ret = hclge_cmd_send(&hdev->hw, &desc[0], 2);
        if (ret)
-               dev_err(dev,
-                       "failed(%d) to enable/disable PPP error interrupts\n",
-                       ret);
+               dev_err(dev, "fail(%d) to configure PPP error intr\n", ret);
 
        return ret;
 }
 
-static int hclge_enable_ppp_error(struct hclge_dev *hdev, bool en)
+static int hclge_config_ppp_hw_err_int(struct hclge_dev *hdev, bool en)
 {
-       struct device *dev = &hdev->pdev->dev;
        int ret;
 
-       ret = hclge_enable_ppp_error_interrupt(hdev, HCLGE_PPP_CMD0_INT_CMD,
+       ret = hclge_config_ppp_error_interrupt(hdev, HCLGE_PPP_CMD0_INT_CMD,
                                               en);
-       if (ret) {
-               dev_err(dev,
-                       "failed(%d) to enable/disable PPP error intr 0,1\n",
-                       ret);
+       if (ret)
                return ret;
-       }
 
-       ret = hclge_enable_ppp_error_interrupt(hdev, HCLGE_PPP_CMD1_INT_CMD,
+       ret = hclge_config_ppp_error_interrupt(hdev, HCLGE_PPP_CMD1_INT_CMD,
                                               en);
-       if (ret)
-               dev_err(dev,
-                       "failed(%d) to enable/disable PPP error intr 2,3\n",
-                       ret);
 
        return ret;
 }
 
-int hclge_enable_tm_hw_error(struct hclge_dev *hdev, bool en)
+static int hclge_config_tm_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc;
        int ret;
 
-       /* enable TM SCH hw errors */
+       /* configure TM SCH hw errors */
        hclge_cmd_setup_basic_desc(&desc, HCLGE_TM_SCH_ECC_INT_EN, false);
        if (en)
                desc.data[0] = cpu_to_le32(HCLGE_TM_SCH_ECC_ERR_INT_EN);
-       else
-               desc.data[0] = 0;
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret) {
-               dev_err(dev, "failed(%d) to configure TM SCH errors\n", ret);
+               dev_err(dev, "fail(%d) to configure TM SCH errors\n", ret);
                return ret;
        }
 
-       /* enable TM QCN hw errors */
+       /* configure TM QCN hw errors */
        ret = hclge_cmd_query_error(hdev, &desc, HCLGE_TM_QCN_MEM_INT_CFG,
                                    0, 0, 0);
        if (ret) {
-               dev_err(dev, "failed(%d) to read TM QCN CFG status\n", ret);
+               dev_err(dev, "fail(%d) to read TM QCN CFG status\n", ret);
                return ret;
        }
 
        hclge_cmd_reuse_desc(&desc, false);
        if (en)
                desc.data[1] = cpu_to_le32(HCLGE_TM_QCN_MEM_ERR_INT_EN);
-       else
-               desc.data[1] = 0;
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret)
                dev_err(dev,
-                       "failed(%d) to configure TM QCN mem errors\n", ret);
+                       "fail(%d) to configure TM QCN mem errors\n", ret);
 
        return ret;
 }
 
-static void hclge_process_common_error(struct hclge_dev *hdev,
-                                      enum hclge_err_int_type type)
+static int hclge_config_mac_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
-       struct hclge_desc desc[2];
-       u32 err_sts;
+       struct hclge_desc desc;
        int ret;
 
-       /* read err sts */
-       ret = hclge_cmd_query_error(hdev, &desc[0],
-                                   HCLGE_COMMON_ECC_INT_CFG,
-                                   HCLGE_CMD_FLAG_NEXT, 0, 0);
-       if (ret) {
+       /* configure MAC common error interrupts */
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_MAC_COMMON_INT_EN, false);
+       if (en)
+               desc.data[0] = cpu_to_le32(HCLGE_MAC_COMMON_ERR_INT_EN);
+
+       desc.data[1] = cpu_to_le32(HCLGE_MAC_COMMON_ERR_INT_EN_MASK);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
                dev_err(dev,
-                       "failed(=%d) to query COMMON error interrupt status\n",
-                       ret);
-               return;
-       }
+                       "fail(%d) to configure MAC COMMON error intr\n", ret);
 
-       /* log err */
-       err_sts = (le32_to_cpu(desc[0].data[0])) & HCLGE_IMP_TCM_ECC_INT_MASK;
-       hclge_log_error(dev, &hclge_imp_tcm_ecc_int[0], err_sts);
+       return ret;
+}
 
-       err_sts = (le32_to_cpu(desc[0].data[1])) & HCLGE_CMDQ_ECC_INT_MASK;
-       hclge_log_error(dev, &hclge_cmdq_nic_mem_ecc_int[0], err_sts);
+static int hclge_config_ppu_error_interrupts(struct hclge_dev *hdev, u32 cmd,
+                                            bool en)
+{
+       struct device *dev = &hdev->pdev->dev;
+       struct hclge_desc desc[2];
+       int num = 1;
+       int ret;
 
-       err_sts = (le32_to_cpu(desc[0].data[1]) >> HCLGE_CMDQ_ROC_ECC_INT_SHIFT)
-                  & HCLGE_CMDQ_ECC_INT_MASK;
-       hclge_log_error(dev, &hclge_cmdq_rocee_mem_ecc_int[0], err_sts);
+       /* configure PPU error interrupts */
+       if (cmd == HCLGE_PPU_MPF_ECC_INT_CMD) {
+               hclge_cmd_setup_basic_desc(&desc[0], cmd, false);
+               desc[0].flag |= HCLGE_CMD_FLAG_NEXT;
+               hclge_cmd_setup_basic_desc(&desc[1], cmd, false);
+               if (en) {
+                       desc[0].data[0] = HCLGE_PPU_MPF_ABNORMAL_INT0_EN;
+                       desc[0].data[1] = HCLGE_PPU_MPF_ABNORMAL_INT1_EN;
+                       desc[1].data[3] = HCLGE_PPU_MPF_ABNORMAL_INT3_EN;
+                       desc[1].data[4] = HCLGE_PPU_MPF_ABNORMAL_INT2_EN;
+               }
 
-       if ((le32_to_cpu(desc[0].data[3])) & BIT(0))
-               dev_warn(dev, "imp_rd_data_poison_err found\n");
+               desc[1].data[0] = HCLGE_PPU_MPF_ABNORMAL_INT0_EN_MASK;
+               desc[1].data[1] = HCLGE_PPU_MPF_ABNORMAL_INT1_EN_MASK;
+               desc[1].data[2] = HCLGE_PPU_MPF_ABNORMAL_INT2_EN_MASK;
+               desc[1].data[3] |= HCLGE_PPU_MPF_ABNORMAL_INT3_EN_MASK;
+               num = 2;
+       } else if (cmd == HCLGE_PPU_MPF_OTHER_INT_CMD) {
+               hclge_cmd_setup_basic_desc(&desc[0], cmd, false);
+               if (en)
+                       desc[0].data[0] = HCLGE_PPU_MPF_ABNORMAL_INT2_EN2;
 
-       err_sts = (le32_to_cpu(desc[0].data[3]) >> HCLGE_TQP_ECC_INT_SHIFT) &
-                  HCLGE_TQP_ECC_INT_MASK;
-       hclge_log_error(dev, &hclge_tqp_int_ecc_int[0], err_sts);
+               desc[0].data[2] = HCLGE_PPU_MPF_ABNORMAL_INT2_EN2_MASK;
+       } else if (cmd == HCLGE_PPU_PF_OTHER_INT_CMD) {
+               hclge_cmd_setup_basic_desc(&desc[0], cmd, false);
+               if (en)
+                       desc[0].data[0] = HCLGE_PPU_PF_ABNORMAL_INT_EN;
 
-       err_sts = (le32_to_cpu(desc[0].data[5])) &
-                  HCLGE_IMP_ITCM4_ECC_INT_MASK;
-       hclge_log_error(dev, &hclge_imp_itcm4_ecc_int[0], err_sts);
+               desc[0].data[2] = HCLGE_PPU_PF_ABNORMAL_INT_EN_MASK;
+       } else {
+               dev_err(dev, "Invalid cmd to configure PPU error interrupts\n");
+               return -EINVAL;
+       }
 
-       /* clear error interrupts */
-       desc[1].data[0] = cpu_to_le32(HCLGE_IMP_TCM_ECC_CLR_MASK);
-       desc[1].data[1] = cpu_to_le32(HCLGE_CMDQ_NIC_ECC_CLR_MASK |
-                               HCLGE_CMDQ_ROCEE_ECC_CLR_MASK);
-       desc[1].data[3] = cpu_to_le32(HCLGE_TQP_IMP_ERR_CLR_MASK);
-       desc[1].data[5] = cpu_to_le32(HCLGE_IMP_ITCM4_ECC_CLR_MASK);
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
 
-       ret = hclge_cmd_clear_error(hdev, &desc[0], NULL, 0,
-                                   HCLGE_CMD_FLAG_NEXT);
-       if (ret)
-               dev_err(dev,
-                       "failed(%d) to clear COMMON error interrupt status\n",
-                       ret);
+       return ret;
 }
 
-static void hclge_process_ncsi_error(struct hclge_dev *hdev,
-                                    enum hclge_err_int_type type)
+static int hclge_config_ppu_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
-       struct hclge_desc desc_rd;
-       struct hclge_desc desc_wr;
-       u32 err_sts;
        int ret;
 
-       if (hdev->pdev->revision < 0x21)
-               return;
-
-       /* read NCSI error status */
-       ret = hclge_cmd_query_error(hdev, &desc_rd, HCLGE_NCSI_INT_QUERY,
-                                   0, 1, HCLGE_NCSI_ERR_INT_TYPE);
+       ret = hclge_config_ppu_error_interrupts(hdev, HCLGE_PPU_MPF_ECC_INT_CMD,
+                                               en);
        if (ret) {
-               dev_err(dev,
-                       "failed(=%d) to query NCSI error interrupt status\n",
+               dev_err(dev, "fail(%d) to configure PPU MPF ECC error intr\n",
                        ret);
-               return;
+               return ret;
        }
 
-       /* log err */
-       err_sts = le32_to_cpu(desc_rd.data[0]);
-       hclge_log_error(dev, &hclge_ncsi_err_int[0], err_sts);
+       ret = hclge_config_ppu_error_interrupts(hdev,
+                                               HCLGE_PPU_MPF_OTHER_INT_CMD,
+                                               en);
+       if (ret) {
+               dev_err(dev, "fail(%d) to configure PPU MPF other intr\n", ret);
+               return ret;
+       }
 
-       /* clear err int */
-       ret = hclge_cmd_clear_error(hdev, &desc_wr, &desc_rd,
-                                   HCLGE_NCSI_INT_CLR, 0);
+       ret = hclge_config_ppu_error_interrupts(hdev,
+                                               HCLGE_PPU_PF_OTHER_INT_CMD, en);
        if (ret)
-               dev_err(dev, "failed(=%d) to clear NCSI interrupt status\n",
+               dev_err(dev, "fail(%d) to configure PPU PF error interrupts\n",
                        ret);
+       return ret;
 }
 
-static void hclge_process_igu_egu_error(struct hclge_dev *hdev,
-                                       enum hclge_err_int_type int_type)
+static int hclge_config_ssu_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
-       struct hclge_desc desc_rd;
-       struct hclge_desc desc_wr;
-       u32 err_sts;
+       struct hclge_desc desc[2];
        int ret;
 
-       /* read IGU common err sts */
-       ret = hclge_cmd_query_error(hdev, &desc_rd,
-                                   HCLGE_IGU_COMMON_INT_QUERY,
-                                   0, 1, int_type);
-       if (ret) {
-               dev_err(dev, "failed(=%d) to query IGU common int status\n",
-                       ret);
-               return;
+       /* configure SSU ecc error interrupts */
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_SSU_ECC_INT_CMD, false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], HCLGE_SSU_ECC_INT_CMD, false);
+       if (en) {
+               desc[0].data[0] = cpu_to_le32(HCLGE_SSU_1BIT_ECC_ERR_INT_EN);
+               desc[0].data[1] =
+                       cpu_to_le32(HCLGE_SSU_MULTI_BIT_ECC_ERR_INT_EN);
+               desc[0].data[4] = cpu_to_le32(HCLGE_SSU_BIT32_ECC_ERR_INT_EN);
        }
 
-       /* log err */
-       err_sts = le32_to_cpu(desc_rd.data[0]) &
-                                  HCLGE_IGU_COM_INT_MASK;
-       hclge_log_error(dev, &hclge_igu_com_err_int[0], err_sts);
+       desc[1].data[0] = cpu_to_le32(HCLGE_SSU_1BIT_ECC_ERR_INT_EN_MASK);
+       desc[1].data[1] = cpu_to_le32(HCLGE_SSU_MULTI_BIT_ECC_ERR_INT_EN_MASK);
+       desc[1].data[2] = cpu_to_le32(HCLGE_SSU_BIT32_ECC_ERR_INT_EN_MASK);
 
-       /* clear err int */
-       ret = hclge_cmd_clear_error(hdev, &desc_wr, &desc_rd,
-                                   HCLGE_IGU_COMMON_INT_CLR, 0);
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], 2);
        if (ret) {
-               dev_err(dev, "failed(=%d) to clear IGU common int status\n",
-                       ret);
-               return;
+               dev_err(dev,
+                       "fail(%d) to configure SSU ECC error interrupt\n", ret);
+               return ret;
        }
 
-       /* read IGU-EGU TNL err sts */
-       ret = hclge_cmd_query_error(hdev, &desc_rd,
-                                   HCLGE_IGU_EGU_TNL_INT_QUERY,
-                                   0, 1, int_type);
-       if (ret) {
-               dev_err(dev, "failed(=%d) to query IGU-EGU TNL int status\n",
-                       ret);
-               return;
+       /* configure SSU common error interrupts */
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_SSU_COMMON_INT_CMD, false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], HCLGE_SSU_COMMON_INT_CMD, false);
+
+       if (en) {
+               if (hdev->pdev->revision >= 0x21)
+                       desc[0].data[0] =
+                               cpu_to_le32(HCLGE_SSU_COMMON_INT_EN);
+               else
+                       desc[0].data[0] =
+                               cpu_to_le32(HCLGE_SSU_COMMON_INT_EN & ~BIT(5));
+               desc[0].data[1] = cpu_to_le32(HCLGE_SSU_PORT_BASED_ERR_INT_EN);
+               desc[0].data[2] =
+                       cpu_to_le32(HCLGE_SSU_FIFO_OVERFLOW_ERR_INT_EN);
        }
 
-       /* log err */
-       err_sts = le32_to_cpu(desc_rd.data[0]) &
-                                  HCLGE_IGU_EGU_TNL_INT_MASK;
-       hclge_log_error(dev, &hclge_igu_egu_tnl_err_int[0], err_sts);
+       desc[1].data[0] = cpu_to_le32(HCLGE_SSU_COMMON_INT_EN_MASK |
+                               HCLGE_SSU_PORT_BASED_ERR_INT_EN_MASK);
+       desc[1].data[1] = cpu_to_le32(HCLGE_SSU_FIFO_OVERFLOW_ERR_INT_EN_MASK);
 
-       /* clear err int */
-       ret = hclge_cmd_clear_error(hdev, &desc_wr, &desc_rd,
-                                   HCLGE_IGU_EGU_TNL_INT_CLR, 0);
-       if (ret) {
-               dev_err(dev, "failed(=%d) to clear IGU-EGU TNL int status\n",
-                       ret);
-               return;
-       }
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], 2);
+       if (ret)
+               dev_err(dev,
+                       "fail(%d) to configure SSU COMMON error intr\n", ret);
 
-       hclge_process_ncsi_error(hdev, HCLGE_ERR_INT_RAS_NFE);
+       return ret;
 }
 
-static int hclge_log_and_clear_ppp_error(struct hclge_dev *hdev, u32 cmd,
-                                        enum hclge_err_int_type int_type)
+#define HCLGE_SET_DEFAULT_RESET_REQUEST(reset_type) \
+       do { \
+               if (ae_dev->ops->set_default_reset_request) \
+                       ae_dev->ops->set_default_reset_request(ae_dev, \
+                                                              reset_type); \
+       } while (0)
+
+/* hclge_handle_mpf_ras_error: handle all main PF RAS errors
+ * @hdev: pointer to struct hclge_dev
+ * @desc: descriptor for describing the command
+ * @num:  number of extended command structures
+ *
+ * This function handles all the main PF RAS errors in the
+ * hw register/s using command.
+ */
+static int hclge_handle_mpf_ras_error(struct hclge_dev *hdev,
+                                     struct hclge_desc *desc,
+                                     int num)
 {
+       struct hnae3_ae_dev *ae_dev = hdev->ae_dev;
        struct device *dev = &hdev->pdev->dev;
-       const struct hclge_hw_error *hw_err_lst1, *hw_err_lst2, *hw_err_lst3;
-       struct hclge_desc desc[2];
-       u32 err_sts;
+       __le32 *desc_data;
+       u32 status;
        int ret;
 
-       /* read PPP INT sts */
-       ret = hclge_cmd_query_error(hdev, &desc[0], cmd,
-                                   HCLGE_CMD_FLAG_NEXT, 5, int_type);
+       /* query all main PF RAS errors */
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_QUERY_CLEAR_MPF_RAS_INT,
+                                  true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
        if (ret) {
-               dev_err(dev, "failed(=%d) to query PPP interrupt status\n",
-                       ret);
-               return -EIO;
+               dev_err(dev, "query all mpf ras int cmd failed (%d)\n", ret);
+               return ret;
        }
 
-       /* log error */
-       if (cmd == HCLGE_PPP_CMD0_INT_CMD) {
-               hw_err_lst1 = &hclge_ppp_mpf_int0[0];
-               hw_err_lst2 = &hclge_ppp_mpf_int1[0];
-               hw_err_lst3 = &hclge_ppp_pf_int[0];
-       } else if (cmd == HCLGE_PPP_CMD1_INT_CMD) {
-               hw_err_lst1 = &hclge_ppp_mpf_int2[0];
-               hw_err_lst2 = &hclge_ppp_mpf_int3[0];
-       } else {
-               dev_err(dev, "invalid command(=%d)\n", cmd);
-               return -EINVAL;
+       /* log HNS common errors */
+       status = le32_to_cpu(desc[0].data[0]);
+       if (status) {
+               hclge_log_error(dev, "IMP_TCM_ECC_INT_STS",
+                               &hclge_imp_tcm_ecc_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
        }
 
-       err_sts = le32_to_cpu(desc[0].data[2]);
-       if (err_sts)
-               hclge_log_error(dev, hw_err_lst1, err_sts);
+       status = le32_to_cpu(desc[0].data[1]);
+       if (status) {
+               hclge_log_error(dev, "CMDQ_MEM_ECC_INT_STS",
+                               &hclge_cmdq_nic_mem_ecc_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+       }
 
-       err_sts = le32_to_cpu(desc[0].data[3]);
-       if (err_sts)
-               hclge_log_error(dev, hw_err_lst2, err_sts);
+       if ((le32_to_cpu(desc[0].data[2])) & BIT(0)) {
+               dev_warn(dev, "imp_rd_data_poison_err found\n");
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+       }
 
-       if (cmd == HCLGE_PPP_CMD0_INT_CMD) {
-               err_sts = (le32_to_cpu(desc[0].data[4]) >> 8) & 0x3;
-               if (err_sts)
-                       hclge_log_error(dev, hw_err_lst3, err_sts);
+       status = le32_to_cpu(desc[0].data[3]);
+       if (status) {
+               hclge_log_error(dev, "TQP_INT_ECC_INT_STS",
+                               &hclge_tqp_int_ecc_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
        }
 
-       /* clear PPP INT */
-       ret = hclge_cmd_clear_error(hdev, &desc[0], NULL, 0,
-                                   HCLGE_CMD_FLAG_NEXT);
-       if (ret) {
-               dev_err(dev, "failed(=%d) to clear PPP interrupt status\n",
-                       ret);
-               return -EIO;
+       status = le32_to_cpu(desc[0].data[4]);
+       if (status) {
+               hclge_log_error(dev, "MSIX_ECC_INT_STS",
+                               &hclge_msix_sram_ecc_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
        }
 
-       return 0;
+       /* log SSU(Storage Switch Unit) errors */
+       desc_data = (__le32 *)&desc[2];
+       status = le32_to_cpu(*(desc_data + 2));
+       if (status) {
+               dev_warn(dev, "SSU_ECC_MULTI_BIT_INT_0 ssu_ecc_mbit_int[31:0]\n");
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       status = le32_to_cpu(*(desc_data + 3)) & BIT(0);
+       if (status) {
+               dev_warn(dev, "SSU_ECC_MULTI_BIT_INT_1 ssu_ecc_mbit_int[32]\n");
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       status = le32_to_cpu(*(desc_data + 4)) & HCLGE_SSU_COMMON_ERR_INT_MASK;
+       if (status) {
+               hclge_log_error(dev, "SSU_COMMON_ERR_INT",
+                               &hclge_ssu_com_err_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+       }
+
+       /* log IGU(Ingress Unit) errors */
+       desc_data = (__le32 *)&desc[3];
+       status = le32_to_cpu(*desc_data) & HCLGE_IGU_INT_MASK;
+       if (status)
+               hclge_log_error(dev, "IGU_INT_STS",
+                               &hclge_igu_int[0], status);
+
+       /* log PPP(Programmable Packet Process) errors */
+       desc_data = (__le32 *)&desc[4];
+       status = le32_to_cpu(*(desc_data + 1));
+       if (status)
+               hclge_log_error(dev, "PPP_MPF_ABNORMAL_INT_ST1",
+                               &hclge_ppp_mpf_abnormal_int_st1[0], status);
+
+       status = le32_to_cpu(*(desc_data + 3)) & HCLGE_PPP_MPF_INT_ST3_MASK;
+       if (status)
+               hclge_log_error(dev, "PPP_MPF_ABNORMAL_INT_ST3",
+                               &hclge_ppp_mpf_abnormal_int_st3[0], status);
+
+       /* log PPU(RCB) errors */
+       desc_data = (__le32 *)&desc[5];
+       status = le32_to_cpu(*(desc_data + 1));
+       if (status) {
+               dev_warn(dev, "PPU_MPF_ABNORMAL_INT_ST1 %s found\n",
+                        "rpu_rx_pkt_ecc_mbit_err");
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       status = le32_to_cpu(*(desc_data + 2));
+       if (status) {
+               hclge_log_error(dev, "PPU_MPF_ABNORMAL_INT_ST2",
+                               &hclge_ppu_mpf_abnormal_int_st2[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       status = le32_to_cpu(*(desc_data + 3)) & HCLGE_PPU_MPF_INT_ST3_MASK;
+       if (status) {
+               hclge_log_error(dev, "PPU_MPF_ABNORMAL_INT_ST3",
+                               &hclge_ppu_mpf_abnormal_int_st3[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       /* log TM(Traffic Manager) errors */
+       desc_data = (__le32 *)&desc[6];
+       status = le32_to_cpu(*desc_data);
+       if (status) {
+               hclge_log_error(dev, "TM_SCH_RINT",
+                               &hclge_tm_sch_rint[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       /* log QCN(Quantized Congestion Control) errors */
+       desc_data = (__le32 *)&desc[7];
+       status = le32_to_cpu(*desc_data) & HCLGE_QCN_FIFO_INT_MASK;
+       if (status) {
+               hclge_log_error(dev, "QCN_FIFO_RINT",
+                               &hclge_qcn_fifo_rint[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       status = le32_to_cpu(*(desc_data + 1)) & HCLGE_QCN_ECC_INT_MASK;
+       if (status) {
+               hclge_log_error(dev, "QCN_ECC_RINT",
+                               &hclge_qcn_ecc_rint[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       /* log NCSI errors */
+       desc_data = (__le32 *)&desc[9];
+       status = le32_to_cpu(*desc_data) & HCLGE_NCSI_ECC_INT_MASK;
+       if (status) {
+               hclge_log_error(dev, "NCSI_ECC_INT_RPT",
+                               &hclge_ncsi_err_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       /* clear all main PF RAS errors */
+       hclge_cmd_reuse_desc(&desc[0], false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
+       if (ret)
+               dev_err(dev, "clear all mpf ras int cmd failed (%d)\n", ret);
+
+       return ret;
 }
 
-static void hclge_process_ppp_error(struct hclge_dev *hdev,
-                                   enum hclge_err_int_type int_type)
+/* hclge_handle_pf_ras_error: handle all PF RAS errors
+ * @hdev: pointer to struct hclge_dev
+ * @desc: descriptor for describing the command
+ * @num:  number of extended command structures
+ *
+ * This function handles all the PF RAS errors in the
+ * hw register/s using command.
+ */
+static int hclge_handle_pf_ras_error(struct hclge_dev *hdev,
+                                    struct hclge_desc *desc,
+                                    int num)
 {
+       struct hnae3_ae_dev *ae_dev = hdev->ae_dev;
        struct device *dev = &hdev->pdev->dev;
+       __le32 *desc_data;
+       u32 status;
        int ret;
 
-       /* read PPP INT0,1 sts */
-       ret = hclge_log_and_clear_ppp_error(hdev, HCLGE_PPP_CMD0_INT_CMD,
-                                           int_type);
-       if (ret < 0) {
-               dev_err(dev, "failed(=%d) to clear PPP interrupt 0,1 status\n",
-                       ret);
-               return;
+       /* query all PF RAS errors */
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_QUERY_CLEAR_PF_RAS_INT,
+                                  true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
+       if (ret) {
+               dev_err(dev, "query all pf ras int cmd failed (%d)\n", ret);
+               return ret;
        }
 
-       /* read err PPP INT2,3 sts */
-       ret = hclge_log_and_clear_ppp_error(hdev, HCLGE_PPP_CMD1_INT_CMD,
-                                           int_type);
-       if (ret < 0)
-               dev_err(dev, "failed(=%d) to clear PPP interrupt 2,3 status\n",
-                       ret);
+       /* log SSU(Storage Switch Unit) errors */
+       status = le32_to_cpu(desc[0].data[0]);
+       if (status) {
+               hclge_log_error(dev, "SSU_PORT_BASED_ERR_INT",
+                               &hclge_ssu_port_based_err_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+       }
+
+       status = le32_to_cpu(desc[0].data[1]);
+       if (status) {
+               hclge_log_error(dev, "SSU_FIFO_OVERFLOW_INT",
+                               &hclge_ssu_fifo_overflow_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+       }
+
+       status = le32_to_cpu(desc[0].data[2]);
+       if (status) {
+               hclge_log_error(dev, "SSU_ETS_TCG_INT",
+                               &hclge_ssu_ets_tcg_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+       }
+
+       /* log IGU(Ingress Unit) EGU(Egress Unit) TNL errors */
+       desc_data = (__le32 *)&desc[1];
+       status = le32_to_cpu(*desc_data) & HCLGE_IGU_EGU_TNL_INT_MASK;
+       if (status)
+               hclge_log_error(dev, "IGU_EGU_TNL_INT_STS",
+                               &hclge_igu_egu_tnl_int[0], status);
+
+       /* clear all PF RAS errors */
+       hclge_cmd_reuse_desc(&desc[0], false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
+       if (ret)
+               dev_err(dev, "clear all pf ras int cmd failed (%d)\n", ret);
+
+       return ret;
 }
 
-static void hclge_process_tm_sch_error(struct hclge_dev *hdev)
+static int hclge_handle_all_ras_errors(struct hclge_dev *hdev)
 {
        struct device *dev = &hdev->pdev->dev;
-       const struct hclge_tm_sch_ecc_info *tm_sch_ecc_info;
-       struct hclge_desc desc;
-       u32 ecc_info;
-       u8 module_no;
-       u8 ram_no;
+       u32 mpf_bd_num, pf_bd_num, bd_num;
+       struct hclge_desc desc_bd;
+       struct hclge_desc *desc;
        int ret;
 
-       /* read TM scheduler errors */
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_SCH_MBIT_ECC_INFO_CMD, 0, 0, 0);
+       /* query the number of registers in the RAS int status */
+       hclge_cmd_setup_basic_desc(&desc_bd, HCLGE_QUERY_RAS_INT_STS_BD_NUM,
+                                  true);
+       ret = hclge_cmd_send(&hdev->hw, &desc_bd, 1);
        if (ret) {
-               dev_err(dev, "failed(%d) to read SCH mbit ECC err info\n", ret);
-               return;
+               dev_err(dev, "fail(%d) to query ras int status bd num\n", ret);
+               return ret;
        }
-       ecc_info = le32_to_cpu(desc.data[0]);
+       mpf_bd_num = le32_to_cpu(desc_bd.data[0]);
+       pf_bd_num = le32_to_cpu(desc_bd.data[1]);
+       bd_num = max_t(u32, mpf_bd_num, pf_bd_num);
+
+       desc = kcalloc(bd_num, sizeof(struct hclge_desc), GFP_KERNEL);
+       if (!desc)
+               return -ENOMEM;
 
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_SCH_ECC_ERR_RINT_CMD, 0, 0, 0);
+       /* handle all main PF RAS errors */
+       ret = hclge_handle_mpf_ras_error(hdev, desc, mpf_bd_num);
        if (ret) {
-               dev_err(dev, "failed(%d) to read SCH ECC err status\n", ret);
-               return;
+               kfree(desc);
+               return ret;
        }
+       memset(desc, 0, bd_num * sizeof(struct hclge_desc));
 
-       /* log TM scheduler errors */
-       if (le32_to_cpu(desc.data[0])) {
-               hclge_log_error(dev, &hclge_tm_sch_err_int[0],
-                               le32_to_cpu(desc.data[0]));
-               if (le32_to_cpu(desc.data[0]) & 0x2) {
-                       module_no = (ecc_info >> 20) & 0xF;
-                       ram_no = (ecc_info >> 16) & 0xF;
-                       tm_sch_ecc_info =
-                               &hclge_tm_sch_ecc_err[module_no][ram_no];
-                       dev_warn(dev, "ecc err module:ram=%s\n",
-                                tm_sch_ecc_info->name);
-                       dev_warn(dev, "ecc memory address = 0x%x\n",
-                                ecc_info & 0xFFFF);
+       /* handle all PF RAS errors */
+       ret = hclge_handle_pf_ras_error(hdev, desc, pf_bd_num);
+       kfree(desc);
+
+       return ret;
+}
+
+static int hclge_log_rocee_ovf_error(struct hclge_dev *hdev)
+{
+       struct device *dev = &hdev->pdev->dev;
+       struct hclge_desc desc[2];
+       int ret;
+
+       /* read overflow error status */
+       ret = hclge_cmd_query_error(hdev, &desc[0],
+                                   HCLGE_ROCEE_PF_RAS_INT_CMD,
+                                   0, 0, 0);
+       if (ret) {
+               dev_err(dev, "failed(%d) to query ROCEE OVF error sts\n", ret);
+               return ret;
+       }
+
+       /* log overflow error */
+       if (le32_to_cpu(desc[0].data[0]) & HCLGE_ROCEE_OVF_ERR_INT_MASK) {
+               const struct hclge_hw_error *err;
+               u32 err_sts;
+
+               err = &hclge_rocee_qmm_ovf_err_int[0];
+               err_sts = HCLGE_ROCEE_OVF_ERR_TYPE_MASK &
+                         le32_to_cpu(desc[0].data[0]);
+               while (err->msg) {
+                       if (err->int_msk == err_sts) {
+                               dev_warn(dev, "%s [error status=0x%x] found\n",
+                                        err->msg,
+                                        le32_to_cpu(desc[0].data[0]));
+                               break;
+                       }
+                       err++;
                }
        }
 
-       /* clear TM scheduler errors */
-       ret = hclge_cmd_clear_error(hdev, &desc, NULL, 0, 0);
-       if (ret) {
-               dev_err(dev, "failed(%d) to clear TM SCH error status\n", ret);
-               return;
+       if (le32_to_cpu(desc[0].data[1]) & HCLGE_ROCEE_OVF_ERR_INT_MASK) {
+               dev_warn(dev, "ROCEE TSP OVF [error status=0x%x] found\n",
+                        le32_to_cpu(desc[0].data[1]));
        }
 
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_SCH_ECC_ERR_RINT_CE, 0, 0, 0);
-       if (ret) {
-               dev_err(dev, "failed(%d) to read SCH CE status\n", ret);
-               return;
+       if (le32_to_cpu(desc[0].data[2]) & HCLGE_ROCEE_OVF_ERR_INT_MASK) {
+               dev_warn(dev, "ROCEE SCC OVF [error status=0x%x] found\n",
+                        le32_to_cpu(desc[0].data[2]));
        }
 
-       ret = hclge_cmd_clear_error(hdev, &desc, NULL, 0, 0);
+       return 0;
+}
+
+static int hclge_log_and_clear_rocee_ras_error(struct hclge_dev *hdev)
+{
+       enum hnae3_reset_type reset_type = HNAE3_FUNC_RESET;
+       struct hnae3_ae_dev *ae_dev = hdev->ae_dev;
+       struct device *dev = &hdev->pdev->dev;
+       struct hclge_desc desc[2];
+       unsigned int status;
+       int ret;
+
+       /* read RAS error interrupt status */
+       ret = hclge_cmd_query_error(hdev, &desc[0],
+                                   HCLGE_QUERY_CLEAR_ROCEE_RAS_INT,
+                                   0, 0, 0);
        if (ret) {
-               dev_err(dev, "failed(%d) to clear TM SCH CE status\n", ret);
-               return;
+               dev_err(dev, "failed(%d) to query ROCEE RAS INT SRC\n", ret);
+               /* reset everything for now */
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+               return ret;
        }
 
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_SCH_ECC_ERR_RINT_NFE, 0, 0, 0);
-       if (ret) {
-               dev_err(dev, "failed(%d) to read SCH NFE status\n", ret);
-               return;
+       status = le32_to_cpu(desc[0].data[0]);
+
+       if (status & HCLGE_ROCEE_RERR_INT_MASK)
+               dev_warn(dev, "ROCEE RAS AXI rresp error\n");
+
+       if (status & HCLGE_ROCEE_BERR_INT_MASK)
+               dev_warn(dev, "ROCEE RAS AXI bresp error\n");
+
+       if (status & HCLGE_ROCEE_ECC_INT_MASK) {
+               dev_warn(dev, "ROCEE RAS 2bit ECC error\n");
+               reset_type = HNAE3_GLOBAL_RESET;
        }
 
-       ret = hclge_cmd_clear_error(hdev, &desc, NULL, 0, 0);
-       if (ret) {
-               dev_err(dev, "failed(%d) to clear TM SCH NFE status\n", ret);
-               return;
+       if (status & HCLGE_ROCEE_OVF_INT_MASK) {
+               ret = hclge_log_rocee_ovf_error(hdev);
+               if (ret) {
+                       dev_err(dev, "failed(%d) to process ovf error\n", ret);
+                       /* reset everything for now */
+                       HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+                       return ret;
+               }
        }
 
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_SCH_ECC_ERR_RINT_FE, 0, 0, 0);
+       /* clear error status */
+       hclge_cmd_reuse_desc(&desc[0], false);
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], 1);
        if (ret) {
-               dev_err(dev, "failed(%d) to read SCH FE status\n", ret);
-               return;
+               dev_err(dev, "failed(%d) to clear ROCEE RAS error\n", ret);
+               /* reset everything for now */
+               reset_type = HNAE3_GLOBAL_RESET;
        }
 
-       ret = hclge_cmd_clear_error(hdev, &desc, NULL, 0, 0);
-       if (ret)
-               dev_err(dev, "failed(%d) to clear TM SCH FE status\n", ret);
+       HCLGE_SET_DEFAULT_RESET_REQUEST(reset_type);
+
+       return ret;
 }
 
-static void hclge_process_tm_qcn_error(struct hclge_dev *hdev)
+static int hclge_config_rocee_ras_interrupt(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc;
        int ret;
 
-       /* read QCN errors */
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_QCN_MEM_INT_INFO_CMD, 0, 0, 0);
-       if (ret) {
-               dev_err(dev, "failed(%d) to read QCN ECC err status\n", ret);
-               return;
-       }
+       if (hdev->pdev->revision < 0x21 || !hnae3_dev_roce_supported(hdev))
+               return 0;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_CONFIG_ROCEE_RAS_INT_EN, false);
+       if (en) {
+               /* enable ROCEE hw error interrupts */
+               desc.data[0] = cpu_to_le32(HCLGE_ROCEE_RAS_NFE_INT_EN);
+               desc.data[1] = cpu_to_le32(HCLGE_ROCEE_RAS_CE_INT_EN);
 
-       /* log QCN errors */
-       if (le32_to_cpu(desc.data[0]))
-               hclge_log_error(dev, &hclge_qcn_ecc_err_int[0],
-                               le32_to_cpu(desc.data[0]));
+               hclge_log_and_clear_rocee_ras_error(hdev);
+       }
+       desc.data[2] = cpu_to_le32(HCLGE_ROCEE_RAS_NFE_INT_EN_MASK);
+       desc.data[3] = cpu_to_le32(HCLGE_ROCEE_RAS_CE_INT_EN_MASK);
 
-       /* clear QCN errors */
-       ret = hclge_cmd_clear_error(hdev, &desc, NULL, 0, 0);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret)
-               dev_err(dev, "failed(%d) to clear QCN error status\n", ret);
+               dev_err(dev, "failed(%d) to config ROCEE RAS interrupt\n", ret);
+
+       return ret;
 }
 
-static void hclge_process_tm_error(struct hclge_dev *hdev,
-                                  enum hclge_err_int_type type)
+static int hclge_handle_rocee_ras_error(struct hnae3_ae_dev *ae_dev)
 {
-       hclge_process_tm_sch_error(hdev);
-       hclge_process_tm_qcn_error(hdev);
+       struct hclge_dev *hdev = ae_dev->priv;
+
+       if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) ||
+           hdev->pdev->revision < 0x21)
+               return HNAE3_NONE_RESET;
+
+       return hclge_log_and_clear_rocee_ras_error(hdev);
 }
 
 static const struct hclge_hw_blk hw_blk[] = {
-       { .msk = BIT(0), .name = "IGU_EGU",
-         .enable_error = hclge_enable_igu_egu_error,
-         .process_error = hclge_process_igu_egu_error, },
-       { .msk = BIT(5), .name = "COMMON",
-         .enable_error = hclge_enable_common_error,
-         .process_error = hclge_process_common_error, },
-       { .msk = BIT(4), .name = "TM",
-         .enable_error = hclge_enable_tm_hw_error,
-         .process_error = hclge_process_tm_error, },
-       { .msk = BIT(1), .name = "PPP",
-         .enable_error = hclge_enable_ppp_error,
-         .process_error = hclge_process_ppp_error, },
+       {
+         .msk = BIT(0), .name = "IGU_EGU",
+         .config_err_int = hclge_config_igu_egu_hw_err_int,
+       },
+       {
+         .msk = BIT(1), .name = "PPP",
+         .config_err_int = hclge_config_ppp_hw_err_int,
+       },
+       {
+         .msk = BIT(2), .name = "SSU",
+         .config_err_int = hclge_config_ssu_hw_err_int,
+       },
+       {
+         .msk = BIT(3), .name = "PPU",
+         .config_err_int = hclge_config_ppu_hw_err_int,
+       },
+       {
+         .msk = BIT(4), .name = "TM",
+         .config_err_int = hclge_config_tm_hw_err_int,
+       },
+       {
+         .msk = BIT(5), .name = "COMMON",
+         .config_err_int = hclge_config_common_hw_err_int,
+       },
+       {
+         .msk = BIT(8), .name = "MAC",
+         .config_err_int = hclge_config_mac_err_int,
+       },
        { /* sentinel */ }
 };
 
 int hclge_hw_error_set_state(struct hclge_dev *hdev, bool state)
 {
+       const struct hclge_hw_blk *module = hw_blk;
        struct device *dev = &hdev->pdev->dev;
        int ret = 0;
-       int i = 0;
 
-       while (hw_blk[i].name) {
-               if (!hw_blk[i].enable_error) {
-                       i++;
-                       continue;
-               }
-               ret = hw_blk[i].enable_error(hdev, state);
-               if (ret) {
-                       dev_err(dev, "fail(%d) to en/disable err int\n", ret);
-                       return ret;
+       while (module->name) {
+               if (module->config_err_int) {
+                       ret = module->config_err_int(hdev, state);
+                       if (ret)
+                               return ret;
                }
-               i++;
+               module++;
        }
 
+       ret = hclge_config_rocee_ras_interrupt(hdev, state);
+       if (ret)
+               dev_err(dev, "fail(%d) to configure ROCEE err int\n", ret);
+
        return ret;
 }
 
-pci_ers_result_t hclge_process_ras_hw_error(struct hnae3_ae_dev *ae_dev)
+pci_ers_result_t hclge_handle_hw_ras_error(struct hnae3_ae_dev *ae_dev)
 {
        struct hclge_dev *hdev = ae_dev->priv;
        struct device *dev = &hdev->pdev->dev;
-       u32 sts, val;
-       int i = 0;
-
-       sts = hclge_read_dev(&hdev->hw, HCLGE_RAS_PF_OTHER_INT_STS_REG);
-
-       /* Processing Non-fatal errors */
-       if (sts & HCLGE_RAS_REG_NFE_MASK) {
-               val = (sts >> HCLGE_RAS_REG_NFE_SHIFT) & 0xFF;
-               i = 0;
-               while (hw_blk[i].name) {
-                       if (!(hw_blk[i].msk & val)) {
-                               i++;
-                               continue;
-                       }
-                       dev_warn(dev, "%s ras non-fatal error identified\n",
-                                hw_blk[i].name);
-                       if (hw_blk[i].process_error)
-                               hw_blk[i].process_error(hdev,
-                                                        HCLGE_ERR_INT_RAS_NFE);
-                       i++;
-               }
+       u32 status;
+
+       status = hclge_read_dev(&hdev->hw, HCLGE_RAS_PF_OTHER_INT_STS_REG);
+
+       /* Handling Non-fatal HNS RAS errors */
+       if (status & HCLGE_RAS_REG_NFE_MASK) {
+               dev_warn(dev,
+                        "HNS Non-Fatal RAS error(status=0x%x) identified\n",
+                        status);
+               hclge_handle_all_ras_errors(hdev);
+       } else {
+               if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) ||
+                   hdev->pdev->revision < 0x21)
+                       return PCI_ERS_RESULT_RECOVERED;
        }
 
-       return PCI_ERS_RESULT_NEED_RESET;
+       if (status & HCLGE_RAS_REG_ROCEE_ERR_MASK) {
+               dev_warn(dev, "ROCEE uncorrected RAS error identified\n");
+               hclge_handle_rocee_ras_error(ae_dev);
+       }
+
+       if (status & HCLGE_RAS_REG_NFE_MASK ||
+           status & HCLGE_RAS_REG_ROCEE_ERR_MASK)
+               return PCI_ERS_RESULT_NEED_RESET;
+
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
+int hclge_handle_hw_msix_error(struct hclge_dev *hdev,
+                              unsigned long *reset_requests)
+{
+       struct device *dev = &hdev->pdev->dev;
+       u32 mpf_bd_num, pf_bd_num, bd_num;
+       struct hclge_desc desc_bd;
+       struct hclge_desc *desc;
+       __le32 *desc_data;
+       int ret = 0;
+       u32 status;
+
+       /* set default handling */
+       set_bit(HNAE3_FUNC_RESET, reset_requests);
+
+       /* query the number of bds for the MSIx int status */
+       hclge_cmd_setup_basic_desc(&desc_bd, HCLGE_QUERY_MSIX_INT_STS_BD_NUM,
+                                  true);
+       ret = hclge_cmd_send(&hdev->hw, &desc_bd, 1);
+       if (ret) {
+               dev_err(dev, "fail(%d) to query msix int status bd num\n",
+                       ret);
+               /* reset everything for now */
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+               return ret;
+       }
+
+       mpf_bd_num = le32_to_cpu(desc_bd.data[0]);
+       pf_bd_num = le32_to_cpu(desc_bd.data[1]);
+       bd_num = max_t(u32, mpf_bd_num, pf_bd_num);
+
+       desc = kcalloc(bd_num, sizeof(struct hclge_desc), GFP_KERNEL);
+       if (!desc)
+               goto out;
+
+       /* query all main PF MSIx errors */
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_QUERY_CLEAR_ALL_MPF_MSIX_INT,
+                                  true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], mpf_bd_num);
+       if (ret) {
+               dev_err(dev, "query all mpf msix int cmd failed (%d)\n",
+                       ret);
+               /* reset everything for now */
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+               goto msi_error;
+       }
+
+       /* log MAC errors */
+       desc_data = (__le32 *)&desc[1];
+       status = le32_to_cpu(*desc_data);
+       if (status) {
+               hclge_log_error(dev, "MAC_AFIFO_TNL_INT_R",
+                               &hclge_mac_afifo_tnl_int[0], status);
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+       }
+
+       /* log PPU(RCB) errors */
+       desc_data = (__le32 *)&desc[5];
+       status = le32_to_cpu(*(desc_data + 2)) &
+                       HCLGE_PPU_MPF_INT_ST2_MSIX_MASK;
+       if (status) {
+               dev_warn(dev,
+                        "PPU_MPF_ABNORMAL_INT_ST2[28:29], err_status(0x%x)\n",
+                        status);
+               set_bit(HNAE3_CORE_RESET, reset_requests);
+       }
+
+       /* clear all main PF MSIx errors */
+       hclge_cmd_reuse_desc(&desc[0], false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], mpf_bd_num);
+       if (ret) {
+               dev_err(dev, "clear all mpf msix int cmd failed (%d)\n",
+                       ret);
+               /* reset everything for now */
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+               goto msi_error;
+       }
+
+       /* query all PF MSIx errors */
+       memset(desc, 0, bd_num * sizeof(struct hclge_desc));
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_QUERY_CLEAR_ALL_PF_MSIX_INT,
+                                  true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], pf_bd_num);
+       if (ret) {
+               dev_err(dev, "query all pf msix int cmd failed (%d)\n",
+                       ret);
+               /* reset everything for now */
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+               goto msi_error;
+       }
+
+       /* log SSU PF errors */
+       status = le32_to_cpu(desc[0].data[0]) & HCLGE_SSU_PORT_INT_MSIX_MASK;
+       if (status) {
+               hclge_log_error(dev, "SSU_PORT_BASED_ERR_INT",
+                               &hclge_ssu_port_based_pf_int[0], status);
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+       }
+
+       /* read and log PPP PF errors */
+       desc_data = (__le32 *)&desc[2];
+       status = le32_to_cpu(*desc_data);
+       if (status)
+               hclge_log_error(dev, "PPP_PF_ABNORMAL_INT_ST0",
+                               &hclge_ppp_pf_abnormal_int[0], status);
+
+       /* PPU(RCB) PF errors */
+       desc_data = (__le32 *)&desc[3];
+       status = le32_to_cpu(*desc_data) & HCLGE_PPU_PF_INT_MSIX_MASK;
+       if (status)
+               hclge_log_error(dev, "PPU_PF_ABNORMAL_INT_ST",
+                               &hclge_ppu_pf_abnormal_int[0], status);
+
+       /* clear all PF MSIx errors */
+       hclge_cmd_reuse_desc(&desc[0], false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], pf_bd_num);
+       if (ret) {
+               dev_err(dev, "clear all pf msix int cmd failed (%d)\n",
+                       ret);
+               /* reset everything for now */
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+       }
+
+msi_error:
+       kfree(desc);
+out:
+       return ret;
 }
index e0e3b58..51a7d4e 100644 (file)
@@ -7,9 +7,11 @@
 #include "hclge_main.h"
 
 #define HCLGE_RAS_PF_OTHER_INT_STS_REG   0x20B00
-#define HCLGE_RAS_REG_FE_MASK    0xFF
 #define HCLGE_RAS_REG_NFE_MASK   0xFF00
-#define HCLGE_RAS_REG_NFE_SHIFT        8
+#define HCLGE_RAS_REG_ROCEE_ERR_MASK   0x3000000
+
+#define HCLGE_VECTOR0_PF_OTHER_INT_STS_REG   0x20800
+#define HCLGE_VECTOR0_REG_MSIX_MASK   0x1FF00
 
 #define HCLGE_IMP_TCM_ECC_ERR_INT_EN   0xFFFF0000
 #define HCLGE_IMP_TCM_ECC_ERR_INT_EN_MASK      0xFFFF0000
@@ -23,6 +25,8 @@
 #define HCLGE_IMP_RD_POISON_ERR_INT_EN_MASK    0x0100
 #define HCLGE_TQP_ECC_ERR_INT_EN       0x0FFF
 #define HCLGE_TQP_ECC_ERR_INT_EN_MASK  0x0FFF
+#define HCLGE_MSIX_SRAM_ECC_ERR_INT_EN_MASK    0x0F000000
+#define HCLGE_MSIX_SRAM_ECC_ERR_INT_EN 0x0F000000
 #define HCLGE_IGU_ERR_INT_EN   0x0000066F
 #define HCLGE_IGU_ERR_INT_EN_MASK      0x000F
 #define HCLGE_IGU_TNL_ERR_INT_EN    0x0002AABF
 #define HCLGE_TM_QCN_MEM_ERR_INT_EN    0xFFFFFF
 #define HCLGE_NCSI_ERR_INT_EN  0x3
 #define HCLGE_NCSI_ERR_INT_TYPE        0x9
+#define HCLGE_MAC_COMMON_ERR_INT_EN            GENMASK(7, 0)
+#define HCLGE_MAC_COMMON_ERR_INT_EN_MASK       GENMASK(7, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT0_EN         GENMASK(31, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT0_EN_MASK    GENMASK(31, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT1_EN         GENMASK(31, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT1_EN_MASK    GENMASK(31, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT2_EN         0x3FFF3FFF
+#define HCLGE_PPU_MPF_ABNORMAL_INT2_EN_MASK    0x3FFF3FFF
+#define HCLGE_PPU_MPF_ABNORMAL_INT2_EN2                0xB
+#define HCLGE_PPU_MPF_ABNORMAL_INT2_EN2_MASK   0xB
+#define HCLGE_PPU_MPF_ABNORMAL_INT3_EN         GENMASK(7, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT3_EN_MASK    GENMASK(23, 16)
+#define HCLGE_PPU_PF_ABNORMAL_INT_EN           GENMASK(5, 0)
+#define HCLGE_PPU_PF_ABNORMAL_INT_EN_MASK      GENMASK(5, 0)
+#define HCLGE_SSU_1BIT_ECC_ERR_INT_EN          GENMASK(31, 0)
+#define HCLGE_SSU_1BIT_ECC_ERR_INT_EN_MASK     GENMASK(31, 0)
+#define HCLGE_SSU_MULTI_BIT_ECC_ERR_INT_EN     GENMASK(31, 0)
+#define HCLGE_SSU_MULTI_BIT_ECC_ERR_INT_EN_MASK        GENMASK(31, 0)
+#define HCLGE_SSU_BIT32_ECC_ERR_INT_EN         0x0101
+#define HCLGE_SSU_BIT32_ECC_ERR_INT_EN_MASK    0x0101
+#define HCLGE_SSU_COMMON_INT_EN                        GENMASK(9, 0)
+#define HCLGE_SSU_COMMON_INT_EN_MASK           GENMASK(9, 0)
+#define HCLGE_SSU_PORT_BASED_ERR_INT_EN                0x0BFF
+#define HCLGE_SSU_PORT_BASED_ERR_INT_EN_MASK   0x0BFF0000
+#define HCLGE_SSU_FIFO_OVERFLOW_ERR_INT_EN     GENMASK(23, 0)
+#define HCLGE_SSU_FIFO_OVERFLOW_ERR_INT_EN_MASK        GENMASK(23, 0)
+
+#define HCLGE_SSU_COMMON_ERR_INT_MASK  GENMASK(9, 0)
+#define HCLGE_SSU_PORT_INT_MSIX_MASK   0x7BFF
+#define HCLGE_IGU_INT_MASK             GENMASK(3, 0)
+#define HCLGE_IGU_EGU_TNL_INT_MASK     GENMASK(5, 0)
+#define HCLGE_PPP_MPF_INT_ST3_MASK     GENMASK(5, 0)
+#define HCLGE_PPU_MPF_INT_ST3_MASK     GENMASK(7, 0)
+#define HCLGE_PPU_MPF_INT_ST2_MSIX_MASK        GENMASK(29, 28)
+#define HCLGE_PPU_PF_INT_MSIX_MASK     0x27
+#define HCLGE_QCN_FIFO_INT_MASK                GENMASK(17, 0)
+#define HCLGE_QCN_ECC_INT_MASK         GENMASK(21, 0)
+#define HCLGE_NCSI_ECC_INT_MASK                GENMASK(1, 0)
 
-#define HCLGE_IMP_TCM_ECC_INT_MASK     0xFFFF
-#define HCLGE_IMP_ITCM4_ECC_INT_MASK   0x3
-#define HCLGE_CMDQ_ECC_INT_MASK                0xFFFF
-#define HCLGE_CMDQ_ROC_ECC_INT_SHIFT   16
-#define HCLGE_TQP_ECC_INT_MASK         0xFFF
-#define HCLGE_TQP_ECC_INT_SHIFT                16
-#define HCLGE_IMP_TCM_ECC_CLR_MASK     0xFFFF
-#define HCLGE_IMP_ITCM4_ECC_CLR_MASK   0x3
-#define HCLGE_CMDQ_NIC_ECC_CLR_MASK    0xFFFF
-#define HCLGE_CMDQ_ROCEE_ECC_CLR_MASK  0xFFFF0000
-#define HCLGE_TQP_IMP_ERR_CLR_MASK     0x0FFF0001
-#define HCLGE_IGU_COM_INT_MASK         0xF
-#define HCLGE_IGU_EGU_TNL_INT_MASK     0x3F
-#define HCLGE_PPP_PF_INT_MASK          0x100
+#define HCLGE_ROCEE_RAS_NFE_INT_EN             0xF
+#define HCLGE_ROCEE_RAS_CE_INT_EN              0x1
+#define HCLGE_ROCEE_RAS_NFE_INT_EN_MASK                0xF
+#define HCLGE_ROCEE_RAS_CE_INT_EN_MASK         0x1
+#define HCLGE_ROCEE_RERR_INT_MASK              BIT(0)
+#define HCLGE_ROCEE_BERR_INT_MASK              BIT(1)
+#define HCLGE_ROCEE_ECC_INT_MASK               BIT(2)
+#define HCLGE_ROCEE_OVF_INT_MASK               BIT(3)
+#define HCLGE_ROCEE_OVF_ERR_INT_MASK           0x10000
+#define HCLGE_ROCEE_OVF_ERR_TYPE_MASK          0x3F
 
 enum hclge_err_int_type {
        HCLGE_ERR_INT_MSIX = 0,
@@ -67,9 +105,7 @@ enum hclge_err_int_type {
 struct hclge_hw_blk {
        u32 msk;
        const char *name;
-       int (*enable_error)(struct hclge_dev *hdev, bool en);
-       void (*process_error)(struct hclge_dev *hdev,
-                             enum hclge_err_int_type type);
+       int (*config_err_int)(struct hclge_dev *hdev, bool en);
 };
 
 struct hclge_hw_error {
@@ -78,6 +114,7 @@ struct hclge_hw_error {
 };
 
 int hclge_hw_error_set_state(struct hclge_dev *hdev, bool state);
-int hclge_enable_tm_hw_error(struct hclge_dev *hdev, bool en);
-pci_ers_result_t hclge_process_ras_hw_error(struct hnae3_ae_dev *ae_dev);
+pci_ers_result_t hclge_handle_hw_ras_error(struct hnae3_ae_dev *ae_dev);
+int hclge_handle_hw_msix_error(struct hclge_dev *hdev,
+                              unsigned long *reset_requests);
 #endif
index 43bfc73..f7637c0 100644 (file)
@@ -26,7 +26,9 @@
 #define HCLGE_STATS_READ(p, offset) (*((u64 *)((u8 *)(p) + (offset))))
 #define HCLGE_MAC_STATS_FIELD_OFF(f) (offsetof(struct hclge_mac_stats, f))
 
-static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu);
+#define HCLGE_BUF_SIZE_UNIT    256
+
+static int hclge_set_mac_mtu(struct hclge_dev *hdev, int new_mps);
 static int hclge_init_vlan_config(struct hclge_dev *hdev);
 static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev);
 static int hclge_set_umv_space(struct hclge_dev *hdev, u16 space_size,
@@ -48,6 +50,62 @@ static const struct pci_device_id ae_algo_pci_tbl[] = {
 
 MODULE_DEVICE_TABLE(pci, ae_algo_pci_tbl);
 
+static const u32 cmdq_reg_addr_list[] = {HCLGE_CMDQ_TX_ADDR_L_REG,
+                                        HCLGE_CMDQ_TX_ADDR_H_REG,
+                                        HCLGE_CMDQ_TX_DEPTH_REG,
+                                        HCLGE_CMDQ_TX_TAIL_REG,
+                                        HCLGE_CMDQ_TX_HEAD_REG,
+                                        HCLGE_CMDQ_RX_ADDR_L_REG,
+                                        HCLGE_CMDQ_RX_ADDR_H_REG,
+                                        HCLGE_CMDQ_RX_DEPTH_REG,
+                                        HCLGE_CMDQ_RX_TAIL_REG,
+                                        HCLGE_CMDQ_RX_HEAD_REG,
+                                        HCLGE_VECTOR0_CMDQ_SRC_REG,
+                                        HCLGE_CMDQ_INTR_STS_REG,
+                                        HCLGE_CMDQ_INTR_EN_REG,
+                                        HCLGE_CMDQ_INTR_GEN_REG};
+
+static const u32 common_reg_addr_list[] = {HCLGE_MISC_VECTOR_REG_BASE,
+                                          HCLGE_VECTOR0_OTER_EN_REG,
+                                          HCLGE_MISC_RESET_STS_REG,
+                                          HCLGE_MISC_VECTOR_INT_STS,
+                                          HCLGE_GLOBAL_RESET_REG,
+                                          HCLGE_FUN_RST_ING,
+                                          HCLGE_GRO_EN_REG};
+
+static const u32 ring_reg_addr_list[] = {HCLGE_RING_RX_ADDR_L_REG,
+                                        HCLGE_RING_RX_ADDR_H_REG,
+                                        HCLGE_RING_RX_BD_NUM_REG,
+                                        HCLGE_RING_RX_BD_LENGTH_REG,
+                                        HCLGE_RING_RX_MERGE_EN_REG,
+                                        HCLGE_RING_RX_TAIL_REG,
+                                        HCLGE_RING_RX_HEAD_REG,
+                                        HCLGE_RING_RX_FBD_NUM_REG,
+                                        HCLGE_RING_RX_OFFSET_REG,
+                                        HCLGE_RING_RX_FBD_OFFSET_REG,
+                                        HCLGE_RING_RX_STASH_REG,
+                                        HCLGE_RING_RX_BD_ERR_REG,
+                                        HCLGE_RING_TX_ADDR_L_REG,
+                                        HCLGE_RING_TX_ADDR_H_REG,
+                                        HCLGE_RING_TX_BD_NUM_REG,
+                                        HCLGE_RING_TX_PRIORITY_REG,
+                                        HCLGE_RING_TX_TC_REG,
+                                        HCLGE_RING_TX_MERGE_EN_REG,
+                                        HCLGE_RING_TX_TAIL_REG,
+                                        HCLGE_RING_TX_HEAD_REG,
+                                        HCLGE_RING_TX_FBD_NUM_REG,
+                                        HCLGE_RING_TX_OFFSET_REG,
+                                        HCLGE_RING_TX_EBD_NUM_REG,
+                                        HCLGE_RING_TX_EBD_OFFSET_REG,
+                                        HCLGE_RING_TX_BD_ERR_REG,
+                                        HCLGE_RING_EN_REG};
+
+static const u32 tqp_intr_reg_addr_list[] = {HCLGE_TQP_INTR_CTRL_REG,
+                                            HCLGE_TQP_INTR_GL0_REG,
+                                            HCLGE_TQP_INTR_GL1_REG,
+                                            HCLGE_TQP_INTR_GL2_REG,
+                                            HCLGE_TQP_INTR_RL_REG};
+
 static const char hns3_nic_test_strs[][ETH_GSTRING_LEN] = {
        "App    Loopback test",
        "Serdes serial Loopback test",
@@ -631,6 +689,22 @@ static int hclge_query_pf_resource(struct hclge_dev *hdev)
        hdev->num_tqps = __le16_to_cpu(req->tqp_num);
        hdev->pkt_buf_size = __le16_to_cpu(req->buf_size) << HCLGE_BUF_UNIT_S;
 
+       if (req->tx_buf_size)
+               hdev->tx_buf_size =
+                       __le16_to_cpu(req->tx_buf_size) << HCLGE_BUF_UNIT_S;
+       else
+               hdev->tx_buf_size = HCLGE_DEFAULT_TX_BUF;
+
+       hdev->tx_buf_size = roundup(hdev->tx_buf_size, HCLGE_BUF_SIZE_UNIT);
+
+       if (req->dv_buf_size)
+               hdev->dv_buf_size =
+                       __le16_to_cpu(req->dv_buf_size) << HCLGE_BUF_UNIT_S;
+       else
+               hdev->dv_buf_size = HCLGE_DEFAULT_DV;
+
+       hdev->dv_buf_size = roundup(hdev->dv_buf_size, HCLGE_BUF_SIZE_UNIT);
+
        if (hnae3_dev_roce_supported(hdev)) {
                hdev->roce_base_msix_offset =
                hnae3_get_field(__le16_to_cpu(req->msixcap_localid_ba_rocee),
@@ -886,7 +960,7 @@ static int hclge_configure(struct hclge_dev *hdev)
                hdev->pfc_max = hdev->tc_max;
        }
 
-       hdev->tm_info.num_tc = hdev->tc_max;
+       hdev->tm_info.num_tc = 1;
 
        /* Currently not support uncontiuous tc */
        for (i = 0; i < hdev->tm_info.num_tc; i++)
@@ -921,6 +995,28 @@ static int hclge_config_tso(struct hclge_dev *hdev, int tso_mss_min,
        return hclge_cmd_send(&hdev->hw, &desc, 1);
 }
 
+static int hclge_config_gro(struct hclge_dev *hdev, bool en)
+{
+       struct hclge_cfg_gro_status_cmd *req;
+       struct hclge_desc desc;
+       int ret;
+
+       if (!hnae3_dev_gro_supported(hdev))
+               return 0;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GRO_GENERIC_CONFIG, false);
+       req = (struct hclge_cfg_gro_status_cmd *)desc.data;
+
+       req->gro_en = cpu_to_le16(en ? 1 : 0);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               dev_err(&hdev->pdev->dev,
+                       "GRO hardware config cmd failed, ret = %d\n", ret);
+
+       return ret;
+}
+
 static int hclge_alloc_tqps(struct hclge_dev *hdev)
 {
        struct hclge_tqp *tqp;
@@ -1144,6 +1240,7 @@ static int hclge_alloc_vport(struct hclge_dev *hdev)
        for (i = 0; i < num_vport; i++) {
                vport->back = hdev;
                vport->vport_id = i;
+               vport->mps = HCLGE_MAC_DEFAULT_FRAME;
 
                if (i == 0)
                        ret = hclge_vport_setup(vport, tqp_main_vport);
@@ -1289,40 +1386,51 @@ static bool  hclge_is_rx_buf_ok(struct hclge_dev *hdev,
 {
        u32 shared_buf_min, shared_buf_tc, shared_std;
        int tc_num, pfc_enable_num;
-       u32 shared_buf;
+       u32 shared_buf, aligned_mps;
        u32 rx_priv;
        int i;
 
        tc_num = hclge_get_tc_num(hdev);
        pfc_enable_num = hclge_get_pfc_enalbe_num(hdev);
+       aligned_mps = roundup(hdev->mps, HCLGE_BUF_SIZE_UNIT);
 
        if (hnae3_dev_dcb_supported(hdev))
-               shared_buf_min = 2 * hdev->mps + HCLGE_DEFAULT_DV;
+               shared_buf_min = 2 * aligned_mps + hdev->dv_buf_size;
        else
-               shared_buf_min = 2 * hdev->mps + HCLGE_DEFAULT_NON_DCB_DV;
+               shared_buf_min = aligned_mps + HCLGE_NON_DCB_ADDITIONAL_BUF
+                                       + hdev->dv_buf_size;
 
-       shared_buf_tc = pfc_enable_num * hdev->mps +
-                       (tc_num - pfc_enable_num) * hdev->mps / 2 +
-                       hdev->mps;
-       shared_std = max_t(u32, shared_buf_min, shared_buf_tc);
+       shared_buf_tc = pfc_enable_num * aligned_mps +
+                       (tc_num - pfc_enable_num) * aligned_mps / 2 +
+                       aligned_mps;
+       shared_std = roundup(max_t(u32, shared_buf_min, shared_buf_tc),
+                            HCLGE_BUF_SIZE_UNIT);
 
        rx_priv = hclge_get_rx_priv_buff_alloced(buf_alloc);
-       if (rx_all <= rx_priv + shared_std)
+       if (rx_all < rx_priv + shared_std)
                return false;
 
-       shared_buf = rx_all - rx_priv;
+       shared_buf = rounddown(rx_all - rx_priv, HCLGE_BUF_SIZE_UNIT);
        buf_alloc->s_buf.buf_size = shared_buf;
-       buf_alloc->s_buf.self.high = shared_buf;
-       buf_alloc->s_buf.self.low =  2 * hdev->mps;
+       if (hnae3_dev_dcb_supported(hdev)) {
+               buf_alloc->s_buf.self.high = shared_buf - hdev->dv_buf_size;
+               buf_alloc->s_buf.self.low = buf_alloc->s_buf.self.high
+                       - roundup(aligned_mps / 2, HCLGE_BUF_SIZE_UNIT);
+       } else {
+               buf_alloc->s_buf.self.high = aligned_mps +
+                                               HCLGE_NON_DCB_ADDITIONAL_BUF;
+               buf_alloc->s_buf.self.low =
+                       roundup(aligned_mps / 2, HCLGE_BUF_SIZE_UNIT);
+       }
 
        for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
                if ((hdev->hw_tc_map & BIT(i)) &&
                    (hdev->tm_info.hw_pfc_map & BIT(i))) {
-                       buf_alloc->s_buf.tc_thrd[i].low = hdev->mps;
-                       buf_alloc->s_buf.tc_thrd[i].high = 2 * hdev->mps;
+                       buf_alloc->s_buf.tc_thrd[i].low = aligned_mps;
+                       buf_alloc->s_buf.tc_thrd[i].high = 2 * aligned_mps;
                } else {
                        buf_alloc->s_buf.tc_thrd[i].low = 0;
-                       buf_alloc->s_buf.tc_thrd[i].high = hdev->mps;
+                       buf_alloc->s_buf.tc_thrd[i].high = aligned_mps;
                }
        }
 
@@ -1340,11 +1448,11 @@ 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 < HCLGE_DEFAULT_TX_BUF)
+               if (total_size < hdev->tx_buf_size)
                        return -ENOMEM;
 
                if (hdev->hw_tc_map & BIT(i))
-                       priv->tx_buf_size = HCLGE_DEFAULT_TX_BUF;
+                       priv->tx_buf_size = hdev->tx_buf_size;
                else
                        priv->tx_buf_size = 0;
 
@@ -1362,7 +1470,6 @@ static int hclge_tx_buffer_calc(struct hclge_dev *hdev,
 static int hclge_rx_buffer_calc(struct hclge_dev *hdev,
                                struct hclge_pkt_buf_alloc *buf_alloc)
 {
-#define HCLGE_BUF_SIZE_UNIT    128
        u32 rx_all = hdev->pkt_buf_size, aligned_mps;
        int no_pfc_priv_num, pfc_priv_num;
        struct hclge_priv_buf *priv;
@@ -1388,13 +1495,16 @@ 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 = aligned_mps;
-                               priv->wl.high = priv->wl.low + aligned_mps;
+                               priv->wl.high =
+                                       roundup(priv->wl.low + aligned_mps,
+                                               HCLGE_BUF_SIZE_UNIT);
                                priv->buf_size = priv->wl.high +
-                                               HCLGE_DEFAULT_DV;
+                                       hdev->dv_buf_size;
                        } else {
                                priv->wl.low = 0;
                                priv->wl.high = 2 * aligned_mps;
-                               priv->buf_size = priv->wl.high;
+                               priv->buf_size = priv->wl.high +
+                                               hdev->dv_buf_size;
                        }
                } else {
                        priv->enable = 0;
@@ -1424,13 +1534,13 @@ 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 = 128;
+                       priv->wl.low = 256;
                        priv->wl.high = priv->wl.low + aligned_mps;
-                       priv->buf_size = priv->wl.high + HCLGE_DEFAULT_DV;
+                       priv->buf_size = priv->wl.high + hdev->dv_buf_size;
                } else {
                        priv->wl.low = 0;
                        priv->wl.high = aligned_mps;
-                       priv->buf_size = priv->wl.high;
+                       priv->buf_size = priv->wl.high + hdev->dv_buf_size;
                }
        }
 
@@ -1873,37 +1983,6 @@ static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed,
        return hclge_cfg_mac_speed_dup(hdev, speed, duplex);
 }
 
-static int hclge_query_mac_an_speed_dup(struct hclge_dev *hdev, int *speed,
-                                       u8 *duplex)
-{
-       struct hclge_query_an_speed_dup_cmd *req;
-       struct hclge_desc desc;
-       int speed_tmp;
-       int ret;
-
-       req = (struct hclge_query_an_speed_dup_cmd *)desc.data;
-
-       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_AN_RESULT, true);
-       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
-       if (ret) {
-               dev_err(&hdev->pdev->dev,
-                       "mac speed/autoneg/duplex query cmd failed %d\n",
-                       ret);
-               return ret;
-       }
-
-       *duplex = hnae3_get_bit(req->an_syn_dup_speed, HCLGE_QUERY_DUPLEX_B);
-       speed_tmp = hnae3_get_field(req->an_syn_dup_speed, HCLGE_QUERY_SPEED_M,
-                                   HCLGE_QUERY_SPEED_S);
-
-       ret = hclge_parse_speed(speed_tmp, speed);
-       if (ret)
-               dev_err(&hdev->pdev->dev,
-                       "could not parse speed(=%d), %d\n", speed_tmp, ret);
-
-       return ret;
-}
-
 static int hclge_set_autoneg_en(struct hclge_dev *hdev, bool enable)
 {
        struct hclge_config_auto_neg_cmd *req;
@@ -1947,12 +2026,10 @@ static int hclge_get_autoneg(struct hnae3_handle *handle)
 
 static int hclge_mac_init(struct hclge_dev *hdev)
 {
-       struct hnae3_handle *handle = &hdev->vport[0].nic;
-       struct net_device *netdev = handle->kinfo.netdev;
        struct hclge_mac *mac = &hdev->hw.mac;
-       int mtu;
        int ret;
 
+       hdev->support_sfp_query = true;
        hdev->hw.mac.duplex = HCLGE_MAC_FULL;
        ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.speed,
                                         hdev->hw.mac.duplex);
@@ -1964,15 +2041,16 @@ static int hclge_mac_init(struct hclge_dev *hdev)
 
        mac->link = 0;
 
-       if (netdev)
-               mtu = netdev->mtu;
-       else
-               mtu = ETH_DATA_LEN;
+       ret = hclge_set_mac_mtu(hdev, hdev->mps);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "set mtu failed ret=%d\n", ret);
+               return ret;
+       }
 
-       ret = hclge_set_mtu(handle, mtu);
+       ret = hclge_buffer_alloc(hdev);
        if (ret)
                dev_err(&hdev->pdev->dev,
-                       "set mtu failed ret=%d\n", ret);
+                       "allocate buffer fail, ret=%d\n", ret);
 
        return ret;
 }
@@ -2061,34 +2139,58 @@ static void hclge_update_link_status(struct hclge_dev *hdev)
        }
 }
 
+static int hclge_get_sfp_speed(struct hclge_dev *hdev, u32 *speed)
+{
+       struct hclge_sfp_speed_cmd *resp = NULL;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_SFP_GET_SPEED, true);
+       resp = (struct hclge_sfp_speed_cmd *)desc.data;
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret == -EOPNOTSUPP) {
+               dev_warn(&hdev->pdev->dev,
+                        "IMP do not support get SFP speed %d\n", ret);
+               return ret;
+       } else if (ret) {
+               dev_err(&hdev->pdev->dev, "get sfp speed failed %d\n", ret);
+               return ret;
+       }
+
+       *speed = resp->sfp_speed;
+
+       return 0;
+}
+
 static int hclge_update_speed_duplex(struct hclge_dev *hdev)
 {
        struct hclge_mac mac = hdev->hw.mac;
-       u8 duplex;
        int speed;
        int ret;
 
-       /* get the speed and duplex as autoneg'result from mac cmd when phy
+       /* get the speed from SFP cmd when phy
         * doesn't exit.
         */
-       if (mac.phydev || !mac.autoneg)
+       if (mac.phydev)
                return 0;
 
-       ret = hclge_query_mac_an_speed_dup(hdev, &speed, &duplex);
-       if (ret) {
-               dev_err(&hdev->pdev->dev,
-                       "mac autoneg/speed/duplex query failed %d\n", ret);
-               return ret;
-       }
+       /* if IMP does not support get SFP/qSFP speed, return directly */
+       if (!hdev->support_sfp_query)
+               return 0;
 
-       ret = hclge_cfg_mac_speed_dup(hdev, speed, duplex);
-       if (ret) {
-               dev_err(&hdev->pdev->dev,
-                       "mac speed/duplex config failed %d\n", ret);
+       ret = hclge_get_sfp_speed(hdev, &speed);
+       if (ret == -EOPNOTSUPP) {
+               hdev->support_sfp_query = false;
+               return ret;
+       } else if (ret) {
                return ret;
        }
 
-       return 0;
+       if (speed == HCLGE_MAC_SPEED_UNKNOWN)
+               return 0; /* do nothing if no SFP */
+
+       /* must config full duplex for SFP */
+       return hclge_cfg_mac_speed_dup(hdev, speed, HCLGE_MAC_FULL);
 }
 
 static int hclge_update_speed_duplex_h(struct hnae3_handle *handle)
@@ -2129,12 +2231,13 @@ static void hclge_service_complete(struct hclge_dev *hdev)
 
 static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval)
 {
-       u32 rst_src_reg;
-       u32 cmdq_src_reg;
+       u32 rst_src_reg, cmdq_src_reg, msix_src_reg;
 
        /* fetch the events from their corresponding regs */
        rst_src_reg = hclge_read_dev(&hdev->hw, HCLGE_MISC_VECTOR_INT_STS);
        cmdq_src_reg = hclge_read_dev(&hdev->hw, HCLGE_VECTOR0_CMDQ_SRC_REG);
+       msix_src_reg = hclge_read_dev(&hdev->hw,
+                                     HCLGE_VECTOR0_PF_OTHER_INT_STS_REG);
 
        /* Assumption: If by any chance reset and mailbox events are reported
         * together then we will only process reset event in this go and will
@@ -2168,6 +2271,10 @@ static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval)
                return HCLGE_VECTOR0_EVENT_RST;
        }
 
+       /* check for vector0 msix event source */
+       if (msix_src_reg & HCLGE_VECTOR0_REG_MSIX_MASK)
+               return HCLGE_VECTOR0_EVENT_ERR;
+
        /* check for vector0 mailbox(=CMDQ RX) event source */
        if (BIT(HCLGE_VECTOR0_RX_CMDQ_INT_B) & cmdq_src_reg) {
                cmdq_src_reg &= ~BIT(HCLGE_VECTOR0_RX_CMDQ_INT_B);
@@ -2218,6 +2325,19 @@ static irqreturn_t hclge_misc_irq_handle(int irq, void *data)
 
        /* vector 0 interrupt is shared with reset and mailbox source events.*/
        switch (event_cause) {
+       case HCLGE_VECTOR0_EVENT_ERR:
+               /* we do not know what type of reset is required now. This could
+                * only be decided after we fetch the type of errors which
+                * caused this event. Therefore, we will do below for now:
+                * 1. Assert HNAE3_UNKNOWN_RESET type of reset. This means we
+                *    have defered type of reset to be used.
+                * 2. Schedule the reset serivce task.
+                * 3. When service task receives  HNAE3_UNKNOWN_RESET type it
+                *    will fetch the correct type of reset.  This would be done
+                *    by first decoding the types of errors.
+                */
+               set_bit(HNAE3_UNKNOWN_RESET, &hdev->reset_request);
+               /* fall through */
        case HCLGE_VECTOR0_EVENT_RST:
                hclge_reset_task_schedule(hdev);
                break;
@@ -2440,7 +2560,7 @@ int hclge_set_all_vf_rst(struct hclge_dev *hdev, bool reset)
                ret = hclge_set_vf_rst(hdev, vport->vport_id, reset);
                if (ret) {
                        dev_err(&hdev->pdev->dev,
-                               "set vf(%d) rst failded %d!\n",
+                               "set vf(%d) rst failed %d!\n",
                                vport->vport_id, ret);
                        return ret;
                }
@@ -2455,7 +2575,7 @@ int hclge_set_all_vf_rst(struct hclge_dev *hdev, bool reset)
                ret = hclge_inform_reset_assert_to_vf(vport);
                if (ret)
                        dev_warn(&hdev->pdev->dev,
-                                "inform reset to vf(%d) failded %d!\n",
+                                "inform reset to vf(%d) failed %d!\n",
                                 vport->vport_id, ret);
        }
 
@@ -2522,6 +2642,23 @@ static enum hnae3_reset_type hclge_get_reset_level(struct hclge_dev *hdev,
 {
        enum hnae3_reset_type rst_level = HNAE3_NONE_RESET;
 
+       /* first, resolve any unknown reset type to the known type(s) */
+       if (test_bit(HNAE3_UNKNOWN_RESET, addr)) {
+               /* we will intentionally ignore any errors from this function
+                *  as we will end up in *some* reset request in any case
+                */
+               hclge_handle_hw_msix_error(hdev, addr);
+               clear_bit(HNAE3_UNKNOWN_RESET, addr);
+               /* We defered the clearing of the error event which caused
+                * interrupt since it was not posssible to do that in
+                * interrupt context (and this is the reason we introduced
+                * new UNKNOWN reset type). Now, the errors have been
+                * handled and cleared in hardware we can safely enable
+                * interrupts. This is an exception to the norm.
+                */
+               hclge_enable_vector(&hdev->misc_vector, true);
+       }
+
        /* return the highest priority reset level amongst all */
        if (test_bit(HNAE3_IMP_RESET, addr)) {
                rst_level = HNAE3_IMP_RESET;
@@ -2704,7 +2841,6 @@ static void hclge_reset(struct hclge_dev *hdev)
         */
        ae_dev->reset_type = hdev->reset_type;
        hdev->reset_count++;
-       hdev->last_reset_time = jiffies;
        /* perform reset of the stack & ae device for a client */
        ret = hclge_notify_roce_client(hdev, HNAE3_DOWN_CLIENT);
        if (ret)
@@ -2767,6 +2903,10 @@ static void hclge_reset(struct hclge_dev *hdev)
        if (ret)
                goto err_reset;
 
+       hdev->last_reset_time = jiffies;
+       hdev->reset_fail_cnt = 0;
+       ae_dev->reset_type = HNAE3_NONE_RESET;
+
        return;
 
 err_reset_lock:
@@ -2891,6 +3031,23 @@ static void hclge_mailbox_service_task(struct work_struct *work)
        clear_bit(HCLGE_STATE_MBX_HANDLING, &hdev->state);
 }
 
+static void hclge_update_vport_alive(struct hclge_dev *hdev)
+{
+       int i;
+
+       /* start from vport 1 for PF is always alive */
+       for (i = 1; i < hdev->num_alloc_vport; i++) {
+               struct hclge_vport *vport = &hdev->vport[i];
+
+               if (time_after(jiffies, vport->last_active_jiffies + 8 * HZ))
+                       clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
+
+               /* If vf is not alive, set to default value */
+               if (!test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state))
+                       vport->mps = HCLGE_MAC_DEFAULT_FRAME;
+       }
+}
+
 static void hclge_service_task(struct work_struct *work)
 {
        struct hclge_dev *hdev =
@@ -2903,6 +3060,7 @@ static void hclge_service_task(struct work_struct *work)
 
        hclge_update_speed_duplex(hdev);
        hclge_update_link_status(hdev);
+       hclge_update_vport_alive(hdev);
        hclge_service_complete(hdev);
 }
 
@@ -4519,6 +4677,13 @@ static int hclge_add_fd_entry(struct hnae3_handle *handle,
                u8 vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie);
                u16 tqps;
 
+               if (vf > hdev->num_req_vfs) {
+                       dev_err(&hdev->pdev->dev,
+                               "Error: vf id (%d) > max vf num (%d)\n",
+                               vf, hdev->num_req_vfs);
+                       return -EINVAL;
+               }
+
                dst_vport_id = vf ? hdev->vport[vf].vport_id : vport->vport_id;
                tqps = vf ? hdev->vport[vf].alloc_tqps : vport->alloc_tqps;
 
@@ -4529,13 +4694,6 @@ static int hclge_add_fd_entry(struct hnae3_handle *handle,
                        return -EINVAL;
                }
 
-               if (vf > hdev->num_req_vfs) {
-                       dev_err(&hdev->pdev->dev,
-                               "Error: vf id (%d) > max vf num (%d)\n",
-                               vf, hdev->num_req_vfs);
-                       return -EINVAL;
-               }
-
                action = HCLGE_FD_ACTION_ACCEPT_PACKET;
                q_index = ring;
        }
@@ -4650,6 +4808,10 @@ static int hclge_restore_fd_entries(struct hnae3_handle *handle)
        if (!hnae3_dev_fd_supported(hdev))
                return 0;
 
+       /* if fd is disabled, should not restore it when reset */
+       if (!hdev->fd_cfg.fd_en)
+               return 0;
+
        hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
                ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
                if (!ret)
@@ -5137,6 +5299,20 @@ static void hclge_reset_tqp_stats(struct hnae3_handle *handle)
        }
 }
 
+static void hclge_set_timer_task(struct hnae3_handle *handle, bool enable)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+
+       if (enable) {
+               mod_timer(&hdev->service_timer, jiffies + HZ);
+       } else {
+               del_timer_sync(&hdev->service_timer);
+               cancel_work_sync(&hdev->service_task);
+               clear_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state);
+       }
+}
+
 static int hclge_ae_start(struct hnae3_handle *handle)
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
@@ -5145,7 +5321,6 @@ static int hclge_ae_start(struct hnae3_handle *handle)
        /* mac enable */
        hclge_cfg_mac_mode(hdev, true);
        clear_bit(HCLGE_STATE_DOWN, &hdev->state);
-       mod_timer(&hdev->service_timer, jiffies + HZ);
        hdev->hw.mac.link = 0;
 
        /* reset tqp stats */
@@ -5160,13 +5335,10 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_dev *hdev = vport->back;
+       int i;
 
        set_bit(HCLGE_STATE_DOWN, &hdev->state);
 
-       del_timer_sync(&hdev->service_timer);
-       cancel_work_sync(&hdev->service_task);
-       clear_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state);
-
        /* If it is not PF reset, the firmware will disable the MAC,
         * so it only need to stop phy here.
         */
@@ -5176,6 +5348,9 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
                return;
        }
 
+       for (i = 0; i < handle->kinfo.num_tqps; i++)
+               hclge_reset_tqp(handle, i);
+
        /* Mac disable */
        hclge_cfg_mac_mode(hdev, false);
 
@@ -5183,11 +5358,35 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
 
        /* reset tqp stats */
        hclge_reset_tqp_stats(handle);
-       del_timer_sync(&hdev->service_timer);
-       cancel_work_sync(&hdev->service_task);
        hclge_update_link_status(hdev);
 }
 
+int hclge_vport_start(struct hclge_vport *vport)
+{
+       set_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
+       vport->last_active_jiffies = jiffies;
+       return 0;
+}
+
+void hclge_vport_stop(struct hclge_vport *vport)
+{
+       clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
+}
+
+static int hclge_client_start(struct hnae3_handle *handle)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+
+       return hclge_vport_start(vport);
+}
+
+static void hclge_client_stop(struct hnae3_handle *handle)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+
+       hclge_vport_stop(vport);
+}
+
 static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport,
                                         u16 cmdq_resp, u8  resp_code,
                                         enum hclge_mac_vlan_tbl_opcode op)
@@ -6335,54 +6534,76 @@ int hclge_en_hw_strip_rxvtag(struct hnae3_handle *handle, bool enable)
        return hclge_set_vlan_rx_offload_cfg(vport);
 }
 
-static int hclge_set_mac_mtu(struct hclge_dev *hdev, int new_mtu)
+static int hclge_set_mac_mtu(struct hclge_dev *hdev, int new_mps)
 {
        struct hclge_config_max_frm_size_cmd *req;
        struct hclge_desc desc;
-       int max_frm_size;
-       int ret;
-
-       max_frm_size = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
-
-       if (max_frm_size < HCLGE_MAC_MIN_FRAME ||
-           max_frm_size > HCLGE_MAC_MAX_FRAME)
-               return -EINVAL;
-
-       max_frm_size = max(max_frm_size, HCLGE_MAC_DEFAULT_FRAME);
 
        hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAX_FRM_SIZE, false);
 
        req = (struct hclge_config_max_frm_size_cmd *)desc.data;
-       req->max_frm_size = cpu_to_le16(max_frm_size);
+       req->max_frm_size = cpu_to_le16(new_mps);
        req->min_frm_size = HCLGE_MAC_MIN_FRAME;
 
-       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
-       if (ret)
-               dev_err(&hdev->pdev->dev, "set mtu fail, ret =%d.\n", ret);
-       else
-               hdev->mps = max_frm_size;
-
-       return ret;
+       return hclge_cmd_send(&hdev->hw, &desc, 1);
 }
 
 static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu)
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
+
+       return hclge_set_vport_mtu(vport, new_mtu);
+}
+
+int hclge_set_vport_mtu(struct hclge_vport *vport, int new_mtu)
+{
        struct hclge_dev *hdev = vport->back;
-       int ret;
+       int i, max_frm_size, ret = 0;
+
+       max_frm_size = new_mtu + ETH_HLEN + ETH_FCS_LEN + 2 * VLAN_HLEN;
+       if (max_frm_size < HCLGE_MAC_MIN_FRAME ||
+           max_frm_size > HCLGE_MAC_MAX_FRAME)
+               return -EINVAL;
+
+       max_frm_size = max(max_frm_size, HCLGE_MAC_DEFAULT_FRAME);
+       mutex_lock(&hdev->vport_lock);
+       /* VF's mps must fit within hdev->mps */
+       if (vport->vport_id && max_frm_size > hdev->mps) {
+               mutex_unlock(&hdev->vport_lock);
+               return -EINVAL;
+       } else if (vport->vport_id) {
+               vport->mps = max_frm_size;
+               mutex_unlock(&hdev->vport_lock);
+               return 0;
+       }
+
+       /* PF's mps must be greater then VF's mps */
+       for (i = 1; i < hdev->num_alloc_vport; i++)
+               if (max_frm_size < hdev->vport[i].mps) {
+                       mutex_unlock(&hdev->vport_lock);
+                       return -EINVAL;
+               }
+
+       hclge_notify_client(hdev, HNAE3_DOWN_CLIENT);
 
-       ret = hclge_set_mac_mtu(hdev, new_mtu);
+       ret = hclge_set_mac_mtu(hdev, max_frm_size);
        if (ret) {
                dev_err(&hdev->pdev->dev,
                        "Change mtu fail, ret =%d\n", ret);
-               return ret;
+               goto out;
        }
 
+       hdev->mps = max_frm_size;
+       vport->mps = max_frm_size;
+
        ret = hclge_buffer_alloc(hdev);
        if (ret)
                dev_err(&hdev->pdev->dev,
                        "Allocate buffer fail, ret =%d\n", ret);
 
+out:
+       hclge_notify_client(hdev, HNAE3_UP_CLIENT);
+       mutex_unlock(&hdev->vport_lock);
        return ret;
 }
 
@@ -6430,8 +6651,7 @@ static int hclge_get_reset_status(struct hclge_dev *hdev, u16 queue_id)
        return hnae3_get_bit(req->ready_to_reset, HCLGE_TQP_RESET_B);
 }
 
-static u16 hclge_covert_handle_qid_global(struct hnae3_handle *handle,
-                                         u16 queue_id)
+u16 hclge_covert_handle_qid_global(struct hnae3_handle *handle, u16 queue_id)
 {
        struct hnae3_queue *queue;
        struct hclge_tqp *tqp;
@@ -6999,6 +7219,9 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
        hdev->reset_type = HNAE3_NONE_RESET;
        hdev->reset_level = HNAE3_FUNC_RESET;
        ae_dev->priv = hdev;
+       hdev->mps = ETH_FRAME_LEN + ETH_FCS_LEN + 2 * VLAN_HLEN;
+
+       mutex_init(&hdev->vport_lock);
 
        ret = hclge_pci_init(hdev);
        if (ret) {
@@ -7090,6 +7313,10 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
                goto err_mdiobus_unreg;
        }
 
+       ret = hclge_config_gro(hdev, true);
+       if (ret)
+               goto err_mdiobus_unreg;
+
        ret = hclge_init_vlan_config(hdev);
        if (ret) {
                dev_err(&pdev->dev, "VLAN init fail, ret =%d\n", ret);
@@ -7125,7 +7352,7 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
        ret = hclge_hw_error_set_state(hdev, true);
        if (ret) {
                dev_err(&pdev->dev,
-                       "hw error interrupts enable failed, ret =%d\n", ret);
+                       "fail(%d) to enable hw error interrupts\n", ret);
                goto err_mdiobus_unreg;
        }
 
@@ -7171,6 +7398,17 @@ static void hclge_stats_clear(struct hclge_dev *hdev)
        memset(&hdev->hw_stats, 0, sizeof(hdev->hw_stats));
 }
 
+static void hclge_reset_vport_state(struct hclge_dev *hdev)
+{
+       struct hclge_vport *vport = hdev->vport;
+       int i;
+
+       for (i = 0; i < hdev->num_alloc_vport; i++) {
+               hclge_vport_start(vport);
+               vport++;
+       }
+}
+
 static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
 {
        struct hclge_dev *hdev = ae_dev->priv;
@@ -7188,19 +7426,6 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
                return ret;
        }
 
-       ret = hclge_get_cap(hdev);
-       if (ret) {
-               dev_err(&pdev->dev, "get hw capability error, ret = %d.\n",
-                       ret);
-               return ret;
-       }
-
-       ret = hclge_configure(hdev);
-       if (ret) {
-               dev_err(&pdev->dev, "Configure dev error, ret = %d.\n", ret);
-               return ret;
-       }
-
        ret = hclge_map_tqp(hdev);
        if (ret) {
                dev_err(&pdev->dev, "Map tqp error, ret = %d.\n", ret);
@@ -7221,6 +7446,10 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
                return ret;
        }
 
+       ret = hclge_config_gro(hdev, true);
+       if (ret)
+               return ret;
+
        ret = hclge_init_vlan_config(hdev);
        if (ret) {
                dev_err(&pdev->dev, "VLAN init fail, ret =%d\n", ret);
@@ -7246,11 +7475,17 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
                return ret;
        }
 
-       /* Re-enable the TM hw error interrupts because
-        * they get disabled on core/global reset.
+       /* Re-enable the hw error interrupts because
+        * the interrupts get disabled on core/global reset.
         */
-       if (hclge_enable_tm_hw_error(hdev, true))
-               dev_err(&pdev->dev, "failed to enable TM hw error interrupts\n");
+       ret = hclge_hw_error_set_state(hdev, true);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "fail(%d) to re-enable HNS hw error interrupts\n", ret);
+               return ret;
+       }
+
+       hclge_reset_vport_state(hdev);
 
        dev_info(&pdev->dev, "Reset done, %s driver initialization finished.\n",
                 HCLGE_DRIVER_NAME);
@@ -7278,6 +7513,7 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
        hclge_destroy_cmd_queue(&hdev->hw);
        hclge_misc_irq_uninit(hdev);
        hclge_pci_uninit(hdev);
+       mutex_destroy(&hdev->vport_lock);
        ae_dev->priv = NULL;
 }
 
@@ -7531,8 +7767,15 @@ static int hclge_get_64_bit_regs(struct hclge_dev *hdev, u32 regs_num,
        return 0;
 }
 
+#define MAX_SEPARATE_NUM       4
+#define SEPARATOR_VALUE                0xFFFFFFFF
+#define REG_NUM_PER_LINE       4
+#define REG_LEN_PER_LINE       (REG_NUM_PER_LINE * sizeof(u32))
+
 static int hclge_get_regs_len(struct hnae3_handle *handle)
 {
+       int cmdq_lines, common_lines, ring_lines, tqp_intr_lines;
+       struct hnae3_knic_private_info *kinfo = &handle->kinfo;
        struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_dev *hdev = vport->back;
        u32 regs_num_32_bit, regs_num_64_bit;
@@ -7545,15 +7788,25 @@ static int hclge_get_regs_len(struct hnae3_handle *handle)
                return -EOPNOTSUPP;
        }
 
-       return regs_num_32_bit * sizeof(u32) + regs_num_64_bit * sizeof(u64);
+       cmdq_lines = sizeof(cmdq_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       common_lines = sizeof(common_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       ring_lines = sizeof(ring_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       tqp_intr_lines = sizeof(tqp_intr_reg_addr_list) / REG_LEN_PER_LINE + 1;
+
+       return (cmdq_lines + common_lines + ring_lines * kinfo->num_tqps +
+               tqp_intr_lines * (hdev->num_msi_used - 1)) * REG_LEN_PER_LINE +
+               regs_num_32_bit * sizeof(u32) + regs_num_64_bit * sizeof(u64);
 }
 
 static void hclge_get_regs(struct hnae3_handle *handle, u32 *version,
                           void *data)
 {
+       struct hnae3_knic_private_info *kinfo = &handle->kinfo;
        struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_dev *hdev = vport->back;
        u32 regs_num_32_bit, regs_num_64_bit;
+       int i, j, reg_um, separator_num;
+       u32 *reg = data;
        int ret;
 
        *version = hdev->fw_version;
@@ -7565,16 +7818,53 @@ static void hclge_get_regs(struct hnae3_handle *handle, u32 *version,
                return;
        }
 
-       ret = hclge_get_32_bit_regs(hdev, regs_num_32_bit, data);
+       /* fetching per-PF registers valus from PF PCIe register space */
+       reg_um = sizeof(cmdq_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (i = 0; i < reg_um; i++)
+               *reg++ = hclge_read_dev(&hdev->hw, cmdq_reg_addr_list[i]);
+       for (i = 0; i < separator_num; i++)
+               *reg++ = SEPARATOR_VALUE;
+
+       reg_um = sizeof(common_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (i = 0; i < reg_um; i++)
+               *reg++ = hclge_read_dev(&hdev->hw, common_reg_addr_list[i]);
+       for (i = 0; i < separator_num; i++)
+               *reg++ = SEPARATOR_VALUE;
+
+       reg_um = sizeof(ring_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (j = 0; j < kinfo->num_tqps; j++) {
+               for (i = 0; i < reg_um; i++)
+                       *reg++ = hclge_read_dev(&hdev->hw,
+                                               ring_reg_addr_list[i] +
+                                               0x200 * j);
+               for (i = 0; i < separator_num; i++)
+                       *reg++ = SEPARATOR_VALUE;
+       }
+
+       reg_um = sizeof(tqp_intr_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (j = 0; j < hdev->num_msi_used - 1; j++) {
+               for (i = 0; i < reg_um; i++)
+                       *reg++ = hclge_read_dev(&hdev->hw,
+                                               tqp_intr_reg_addr_list[i] +
+                                               4 * j);
+               for (i = 0; i < separator_num; i++)
+                       *reg++ = SEPARATOR_VALUE;
+       }
+
+       /* fetching PF common registers values from firmware */
+       ret = hclge_get_32_bit_regs(hdev, regs_num_32_bit, reg);
        if (ret) {
                dev_err(&hdev->pdev->dev,
                        "Get 32 bit register failed, ret = %d.\n", ret);
                return;
        }
 
-       data = (u32 *)data + regs_num_32_bit;
-       ret = hclge_get_64_bit_regs(hdev, regs_num_64_bit,
-                                   data);
+       reg += regs_num_32_bit;
+       ret = hclge_get_64_bit_regs(hdev, regs_num_64_bit, reg);
        if (ret)
                dev_err(&hdev->pdev->dev,
                        "Get 64 bit register failed, ret = %d.\n", ret);
@@ -7637,6 +7927,14 @@ static void hclge_get_link_mode(struct hnae3_handle *handle,
        }
 }
 
+static int hclge_gro_en(struct hnae3_handle *handle, int enable)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+
+       return hclge_config_gro(hdev, enable);
+}
+
 static const struct hnae3_ae_ops hclge_ops = {
        .init_ae_dev = hclge_init_ae_dev,
        .uninit_ae_dev = hclge_uninit_ae_dev,
@@ -7652,6 +7950,8 @@ static const struct hnae3_ae_ops hclge_ops = {
        .set_loopback = hclge_set_loopback,
        .start = hclge_ae_start,
        .stop = hclge_ae_stop,
+       .client_start = hclge_client_start,
+       .client_stop = hclge_client_stop,
        .get_status = hclge_get_status,
        .get_ksettings_an_result = hclge_get_ksettings_an_result,
        .update_speed_duplex_h = hclge_update_speed_duplex_h,
@@ -7704,10 +8004,14 @@ static const struct hnae3_ae_ops hclge_ops = {
        .get_fd_all_rules = hclge_get_all_rules,
        .restore_fd_rules = hclge_restore_fd_entries,
        .enable_fd = hclge_enable_fd,
-       .process_hw_error = hclge_process_ras_hw_error,
+       .dbg_run_cmd = hclge_dbg_run_cmd,
+       .handle_hw_ras_error = hclge_handle_hw_ras_error,
        .get_hw_reset_stat = hclge_get_hw_reset_stat,
        .ae_dev_resetting = hclge_ae_dev_resetting,
        .ae_dev_reset_cnt = hclge_ae_dev_reset_cnt,
+       .set_gro_en = hclge_gro_en,
+       .get_global_queue_id = hclge_covert_handle_qid_global,
+       .set_timer_task = hclge_set_timer_task,
 };
 
 static struct hnae3_ae_algo ae_algo = {
index ca90b66..6615b85 100644 (file)
 #define HCLGE_VECTOR_REG_OFFSET                0x4
 #define HCLGE_VECTOR_VF_OFFSET         0x100000
 
+#define HCLGE_CMDQ_TX_ADDR_L_REG       0x27000
+#define HCLGE_CMDQ_TX_ADDR_H_REG       0x27004
+#define HCLGE_CMDQ_TX_DEPTH_REG                0x27008
+#define HCLGE_CMDQ_TX_TAIL_REG         0x27010
+#define HCLGE_CMDQ_TX_HEAD_REG         0x27014
+#define HCLGE_CMDQ_RX_ADDR_L_REG       0x27018
+#define HCLGE_CMDQ_RX_ADDR_H_REG       0x2701C
+#define HCLGE_CMDQ_RX_DEPTH_REG                0x27020
+#define HCLGE_CMDQ_RX_TAIL_REG         0x27024
+#define HCLGE_CMDQ_RX_HEAD_REG         0x27028
+#define HCLGE_CMDQ_INTR_SRC_REG                0x27100
+#define HCLGE_CMDQ_INTR_STS_REG                0x27104
+#define HCLGE_CMDQ_INTR_EN_REG         0x27108
+#define HCLGE_CMDQ_INTR_GEN_REG                0x2710C
+
+/* bar registers for common func */
+#define HCLGE_VECTOR0_OTER_EN_REG      0x20600
+#define HCLGE_RAS_OTHER_STS_REG                0x20B00
+#define HCLGE_FUNC_RESET_STS_REG       0x20C00
+#define HCLGE_GRO_EN_REG               0x28000
+
+/* bar registers for rcb */
+#define HCLGE_RING_RX_ADDR_L_REG       0x80000
+#define HCLGE_RING_RX_ADDR_H_REG       0x80004
+#define HCLGE_RING_RX_BD_NUM_REG       0x80008
+#define HCLGE_RING_RX_BD_LENGTH_REG    0x8000C
+#define HCLGE_RING_RX_MERGE_EN_REG     0x80014
+#define HCLGE_RING_RX_TAIL_REG         0x80018
+#define HCLGE_RING_RX_HEAD_REG         0x8001C
+#define HCLGE_RING_RX_FBD_NUM_REG      0x80020
+#define HCLGE_RING_RX_OFFSET_REG       0x80024
+#define HCLGE_RING_RX_FBD_OFFSET_REG   0x80028
+#define HCLGE_RING_RX_STASH_REG                0x80030
+#define HCLGE_RING_RX_BD_ERR_REG       0x80034
+#define HCLGE_RING_TX_ADDR_L_REG       0x80040
+#define HCLGE_RING_TX_ADDR_H_REG       0x80044
+#define HCLGE_RING_TX_BD_NUM_REG       0x80048
+#define HCLGE_RING_TX_PRIORITY_REG     0x8004C
+#define HCLGE_RING_TX_TC_REG           0x80050
+#define HCLGE_RING_TX_MERGE_EN_REG     0x80054
+#define HCLGE_RING_TX_TAIL_REG         0x80058
+#define HCLGE_RING_TX_HEAD_REG         0x8005C
+#define HCLGE_RING_TX_FBD_NUM_REG      0x80060
+#define HCLGE_RING_TX_OFFSET_REG       0x80064
+#define HCLGE_RING_TX_EBD_NUM_REG      0x80068
+#define HCLGE_RING_TX_EBD_OFFSET_REG   0x80070
+#define HCLGE_RING_TX_BD_ERR_REG       0x80074
+#define HCLGE_RING_EN_REG              0x80090
+
+/* bar registers for tqp interrupt */
+#define HCLGE_TQP_INTR_CTRL_REG                0x20000
+#define HCLGE_TQP_INTR_GL0_REG         0x20100
+#define HCLGE_TQP_INTR_GL1_REG         0x20200
+#define HCLGE_TQP_INTR_GL2_REG         0x20300
+#define HCLGE_TQP_INTR_RL_REG          0x20900
+
 #define HCLGE_RSS_IND_TBL_SIZE         512
 #define HCLGE_RSS_SET_BITMAP_MSK       GENMASK(15, 0)
 #define HCLGE_RSS_KEY_SIZE             40
@@ -120,7 +176,7 @@ enum HLCGE_PORT_TYPE {
 #define HCLGE_VECTOR0_IMP_RESET_INT_B  1
 
 #define HCLGE_MAC_DEFAULT_FRAME \
-       (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN + ETH_DATA_LEN)
+       (ETH_HLEN + ETH_FCS_LEN + 2 * VLAN_HLEN + ETH_DATA_LEN)
 #define HCLGE_MAC_MIN_FRAME            64
 #define HCLGE_MAC_MAX_FRAME            9728
 
@@ -149,12 +205,14 @@ enum HCLGE_DEV_STATE {
 enum hclge_evt_cause {
        HCLGE_VECTOR0_EVENT_RST,
        HCLGE_VECTOR0_EVENT_MBX,
+       HCLGE_VECTOR0_EVENT_ERR,
        HCLGE_VECTOR0_EVENT_OTHER,
 };
 
 #define HCLGE_MPF_ENBALE 1
 
 enum HCLGE_MAC_SPEED {
+       HCLGE_MAC_SPEED_UNKNOWN = 0,            /* unknown */
        HCLGE_MAC_SPEED_10M     = 10,           /* 10 Mbps */
        HCLGE_MAC_SPEED_100M    = 100,          /* 100 Mbps */
        HCLGE_MAC_SPEED_1G      = 1000,         /* 1000 Mbps   = 1 Gbps */
@@ -624,6 +682,7 @@ struct hclge_dev {
        u8 hw_tc_map;
        u8 tc_num_last_time;
        enum hclge_fc_mode fc_mode_last_time;
+       u8 support_sfp_query;
 
 #define HCLGE_FLAG_TC_BASE_SCH_MODE            1
 #define HCLGE_FLAG_VNET_BASE_SCH_MODE          2
@@ -677,7 +736,12 @@ struct hclge_dev {
        u32 flag;
 
        u32 pkt_buf_size; /* Total pf buf size for tx/rx */
+       u32 tx_buf_size; /* Tx buffer size for each TC */
+       u32 dv_buf_size; /* Dv buffer size for each TC */
+
        u32 mps; /* Max packet size */
+       /* vport_lock protect resource shared by vports */
+       struct mutex vport_lock;
 
        struct hclge_vlan_type_cfg vlan_type_cfg;
 
@@ -728,6 +792,11 @@ struct hclge_rss_tuple_cfg {
        u8 ipv6_fragment_en;
 };
 
+enum HCLGE_VPORT_STATE {
+       HCLGE_VPORT_STATE_ALIVE,
+       HCLGE_VPORT_STATE_MAX
+};
+
 struct hclge_vport {
        u16 alloc_tqps; /* Allocated Tx/Rx queues */
 
@@ -753,6 +822,10 @@ struct hclge_vport {
        struct hclge_dev *back;  /* Back reference to associated dev */
        struct hnae3_handle nic;
        struct hnae3_handle roce;
+
+       unsigned long state;
+       unsigned long last_active_jiffies;
+       u32 mps; /* Max packet size */
 };
 
 void hclge_promisc_param_init(struct hclge_promisc_param *param, bool en_uc,
@@ -800,4 +873,9 @@ int hclge_reset_tqp(struct hnae3_handle *handle, u16 queue_id);
 void hclge_reset_vf_queue(struct hclge_vport *vport, u16 queue_id);
 int hclge_cfg_flowctrl(struct hclge_dev *hdev);
 int hclge_func_reset_cmd(struct hclge_dev *hdev, int func_id);
+int hclge_vport_start(struct hclge_vport *vport);
+void hclge_vport_stop(struct hclge_vport *vport);
+int hclge_set_vport_mtu(struct hclge_vport *vport, int new_mtu);
+int hclge_dbg_run_cmd(struct hnae3_handle *handle, char *cmd_buf);
+u16 hclge_covert_handle_qid_global(struct hnae3_handle *handle, u16 queue_id);
 #endif
index 4c7e7bd..a1de451 100644 (file)
@@ -301,6 +301,21 @@ static int hclge_set_vf_vlan_cfg(struct hclge_vport *vport,
        return status;
 }
 
+static int hclge_set_vf_alive(struct hclge_vport *vport,
+                             struct hclge_mbx_vf_to_pf_cmd *mbx_req,
+                             bool gen_resp)
+{
+       bool alive = !!mbx_req->msg[2];
+       int ret = 0;
+
+       if (alive)
+               ret = hclge_vport_start(vport);
+       else
+               hclge_vport_stop(vport);
+
+       return ret;
+}
+
 static int hclge_get_vf_tcinfo(struct hclge_vport *vport,
                               struct hclge_mbx_vf_to_pf_cmd *mbx_req,
                               bool gen_resp)
@@ -380,6 +395,37 @@ static void hclge_reset_vf(struct hclge_vport *vport,
        hclge_gen_resp_to_vf(vport, mbx_req, ret, NULL, 0);
 }
 
+static void hclge_vf_keep_alive(struct hclge_vport *vport,
+                               struct hclge_mbx_vf_to_pf_cmd *mbx_req)
+{
+       vport->last_active_jiffies = jiffies;
+}
+
+static int hclge_set_vf_mtu(struct hclge_vport *vport,
+                           struct hclge_mbx_vf_to_pf_cmd *mbx_req)
+{
+       int ret;
+       u32 mtu;
+
+       memcpy(&mtu, &mbx_req->msg[2], sizeof(mtu));
+       ret = hclge_set_vport_mtu(vport, mtu);
+
+       return hclge_gen_resp_to_vf(vport, mbx_req, ret, NULL, 0);
+}
+
+static int hclge_get_queue_id_in_pf(struct hclge_vport *vport,
+                                   struct hclge_mbx_vf_to_pf_cmd *mbx_req)
+{
+       u16 queue_id, qid_in_pf;
+       u8 resp_data[2];
+
+       memcpy(&queue_id, &mbx_req->msg[2], sizeof(queue_id));
+       qid_in_pf = hclge_covert_handle_qid_global(&vport->nic, queue_id);
+       memcpy(resp_data, &qid_in_pf, sizeof(qid_in_pf));
+
+       return hclge_gen_resp_to_vf(vport, mbx_req, 0, resp_data, 2);
+}
+
 static bool hclge_cmd_crq_empty(struct hclge_hw *hw)
 {
        u32 tail = hclge_read_dev(hw, HCLGE_NIC_CRQ_TAIL_REG);
@@ -457,6 +503,13 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
                                        "PF failed(%d) to config VF's VLAN\n",
                                        ret);
                        break;
+               case HCLGE_MBX_SET_ALIVE:
+                       ret = hclge_set_vf_alive(vport, req, false);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "PF failed(%d) to set VF's ALIVE\n",
+                                       ret);
+                       break;
                case HCLGE_MBX_GET_QINFO:
                        ret = hclge_get_vf_queue_info(vport, req, true);
                        if (ret)
@@ -484,6 +537,22 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
                case HCLGE_MBX_RESET:
                        hclge_reset_vf(vport, req);
                        break;
+               case HCLGE_MBX_KEEP_ALIVE:
+                       hclge_vf_keep_alive(vport, req);
+                       break;
+               case HCLGE_MBX_SET_MTU:
+                       ret = hclge_set_vf_mtu(vport, req);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "VF fail(%d) to set mtu\n", ret);
+                       break;
+               case HCLGE_MBX_GET_QID_IN_PF:
+                       ret = hclge_get_queue_id_in_pf(vport, req);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "PF failed(%d) to get qid for VF\n",
+                                       ret);
+                       break;
                default:
                        dev_err(&hdev->pdev->dev,
                                "un-supported mailbox message, code = %d\n",
index 741cb3b..dabb843 100644 (file)
@@ -12,7 +12,7 @@
                                         SUPPORTED_TP | \
                                         PHY_10BT_FEATURES | \
                                         PHY_100BT_FEATURES | \
-                                        PHY_1000BT_FEATURES)
+                                        SUPPORTED_1000baseT_Full)
 
 enum hclge_mdio_c22_op_seq {
        HCLGE_MDIO_C22_WRITE = 1,
@@ -179,6 +179,10 @@ static void hclge_mac_adjust_link(struct net_device *netdev)
        int duplex, speed;
        int ret;
 
+       /* When phy link down, do nothing */
+       if (netdev->phydev->link == 0)
+               return;
+
        speed = netdev->phydev->speed;
        duplex = netdev->phydev->duplex;
 
index 494e562..00458da 100644 (file)
@@ -1259,15 +1259,13 @@ int hclge_pause_setup_hw(struct hclge_dev *hdev)
        return 0;
 }
 
-int hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc)
+void hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc)
 {
        struct hclge_vport *vport = hdev->vport;
        struct hnae3_knic_private_info *kinfo;
        u32 i, k;
 
        for (i = 0; i < HNAE3_MAX_USER_PRIO; i++) {
-               if (prio_tc[i] >= hdev->tm_info.num_tc)
-                       return -EINVAL;
                hdev->tm_info.prio_tc[i] = prio_tc[i];
 
                for (k = 0;  k < hdev->num_alloc_vport; k++) {
@@ -1275,18 +1273,12 @@ int hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc)
                        kinfo->prio_tc[i] = prio_tc[i];
                }
        }
-       return 0;
 }
 
-int hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc)
+void hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc)
 {
        u8 i, bit_map = 0;
 
-       for (i = 0; i < hdev->num_alloc_vport; i++) {
-               if (num_tc > hdev->vport[i].alloc_tqps)
-                       return -EINVAL;
-       }
-
        hdev->tm_info.num_tc = num_tc;
 
        for (i = 0; i < hdev->tm_info.num_tc; i++)
@@ -1300,8 +1292,6 @@ int hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc)
        hdev->hw_tc_map = bit_map;
 
        hclge_tm_schd_info_init(hdev);
-
-       return 0;
 }
 
 int hclge_tm_init_hw(struct hclge_dev *hdev)
index 25eef13..b6496a4 100644 (file)
@@ -40,6 +40,13 @@ struct hclge_nq_to_qs_link_cmd {
        __le16 qset_id;
 };
 
+struct hclge_tqp_tx_queue_tc_cmd {
+       __le16 queue_id;
+       __le16 rsvd;
+       u8 tc_id;
+       u8 rev[3];
+};
+
 struct hclge_pg_weight_cmd {
        u8 pg_id;
        u8 dwrr;
@@ -55,6 +62,12 @@ struct hclge_qs_weight_cmd {
        u8 dwrr;
 };
 
+struct hclge_ets_tc_weight_cmd {
+       u8 tc_weight[HNAE3_MAX_TC];
+       u8 weight_offset;
+       u8 rsvd[15];
+};
+
 #define HCLGE_TM_SHAP_IR_B_MSK  GENMASK(7, 0)
 #define HCLGE_TM_SHAP_IR_B_LSH 0
 #define HCLGE_TM_SHAP_IR_U_MSK  GENMASK(11, 8)
@@ -131,8 +144,8 @@ struct hclge_port_shapping_cmd {
 int hclge_tm_schd_init(struct hclge_dev *hdev);
 int hclge_pause_setup_hw(struct hclge_dev *hdev);
 int hclge_tm_schd_mode_hw(struct hclge_dev *hdev);
-int hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc);
-int hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc);
+void hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc);
+void hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc);
 int hclge_tm_dwrr_cfg(struct hclge_dev *hdev);
 int hclge_tm_map_cfg(struct hclge_dev *hdev);
 int hclge_tm_init_hw(struct hclge_dev *hdev);
index 090541d..47030b4 100644 (file)
@@ -87,6 +87,8 @@ enum hclgevf_opcode_type {
        HCLGEVF_OPC_QUERY_TX_STATUS     = 0x0B03,
        HCLGEVF_OPC_QUERY_RX_STATUS     = 0x0B13,
        HCLGEVF_OPC_CFG_COM_TQP_QUEUE   = 0x0B20,
+       /* GRO command */
+       HCLGEVF_OPC_GRO_GENERIC_CONFIG  = 0x0C10,
        /* RSS cmd */
        HCLGEVF_OPC_RSS_GENERIC_CONFIG  = 0x0D01,
        HCLGEVF_OPC_RSS_INPUT_TUPLE     = 0x0D02,
@@ -149,6 +151,12 @@ struct hclgevf_query_res_cmd {
        __le16 rsv[7];
 };
 
+#define HCLGEVF_GRO_EN_B               0
+struct hclgevf_cfg_gro_status_cmd {
+       __le16 gro_en;
+       u8 rsv[22];
+};
+
 #define HCLGEVF_RSS_DEFAULT_OUTPORT_B  4
 #define HCLGEVF_RSS_HASH_KEY_OFFSET_B  4
 #define HCLGEVF_RSS_HASH_KEY_NUM       16
index 6b4d147..82103d5 100644 (file)
@@ -23,6 +23,58 @@ static const struct pci_device_id ae_algovf_pci_tbl[] = {
 
 MODULE_DEVICE_TABLE(pci, ae_algovf_pci_tbl);
 
+static const u32 cmdq_reg_addr_list[] = {HCLGEVF_CMDQ_TX_ADDR_L_REG,
+                                        HCLGEVF_CMDQ_TX_ADDR_H_REG,
+                                        HCLGEVF_CMDQ_TX_DEPTH_REG,
+                                        HCLGEVF_CMDQ_TX_TAIL_REG,
+                                        HCLGEVF_CMDQ_TX_HEAD_REG,
+                                        HCLGEVF_CMDQ_RX_ADDR_L_REG,
+                                        HCLGEVF_CMDQ_RX_ADDR_H_REG,
+                                        HCLGEVF_CMDQ_RX_DEPTH_REG,
+                                        HCLGEVF_CMDQ_RX_TAIL_REG,
+                                        HCLGEVF_CMDQ_RX_HEAD_REG,
+                                        HCLGEVF_VECTOR0_CMDQ_SRC_REG,
+                                        HCLGEVF_CMDQ_INTR_STS_REG,
+                                        HCLGEVF_CMDQ_INTR_EN_REG,
+                                        HCLGEVF_CMDQ_INTR_GEN_REG};
+
+static const u32 common_reg_addr_list[] = {HCLGEVF_MISC_VECTOR_REG_BASE,
+                                          HCLGEVF_RST_ING,
+                                          HCLGEVF_GRO_EN_REG};
+
+static const u32 ring_reg_addr_list[] = {HCLGEVF_RING_RX_ADDR_L_REG,
+                                        HCLGEVF_RING_RX_ADDR_H_REG,
+                                        HCLGEVF_RING_RX_BD_NUM_REG,
+                                        HCLGEVF_RING_RX_BD_LENGTH_REG,
+                                        HCLGEVF_RING_RX_MERGE_EN_REG,
+                                        HCLGEVF_RING_RX_TAIL_REG,
+                                        HCLGEVF_RING_RX_HEAD_REG,
+                                        HCLGEVF_RING_RX_FBD_NUM_REG,
+                                        HCLGEVF_RING_RX_OFFSET_REG,
+                                        HCLGEVF_RING_RX_FBD_OFFSET_REG,
+                                        HCLGEVF_RING_RX_STASH_REG,
+                                        HCLGEVF_RING_RX_BD_ERR_REG,
+                                        HCLGEVF_RING_TX_ADDR_L_REG,
+                                        HCLGEVF_RING_TX_ADDR_H_REG,
+                                        HCLGEVF_RING_TX_BD_NUM_REG,
+                                        HCLGEVF_RING_TX_PRIORITY_REG,
+                                        HCLGEVF_RING_TX_TC_REG,
+                                        HCLGEVF_RING_TX_MERGE_EN_REG,
+                                        HCLGEVF_RING_TX_TAIL_REG,
+                                        HCLGEVF_RING_TX_HEAD_REG,
+                                        HCLGEVF_RING_TX_FBD_NUM_REG,
+                                        HCLGEVF_RING_TX_OFFSET_REG,
+                                        HCLGEVF_RING_TX_EBD_NUM_REG,
+                                        HCLGEVF_RING_TX_EBD_OFFSET_REG,
+                                        HCLGEVF_RING_TX_BD_ERR_REG,
+                                        HCLGEVF_RING_EN_REG};
+
+static const u32 tqp_intr_reg_addr_list[] = {HCLGEVF_TQP_INTR_CTRL_REG,
+                                            HCLGEVF_TQP_INTR_GL0_REG,
+                                            HCLGEVF_TQP_INTR_GL1_REG,
+                                            HCLGEVF_TQP_INTR_GL2_REG,
+                                            HCLGEVF_TQP_INTR_RL_REG};
+
 static inline struct hclgevf_dev *hclgevf_ae_get_hdev(
        struct hnae3_handle *handle)
 {
@@ -204,6 +256,23 @@ static int hclgevf_get_queue_info(struct hclgevf_dev *hdev)
        return 0;
 }
 
+static u16 hclgevf_get_qid_global(struct hnae3_handle *handle, u16 queue_id)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+       u8 msg_data[2], resp_data[2];
+       u16 qid_in_pf = 0;
+       int ret;
+
+       memcpy(&msg_data[0], &queue_id, sizeof(queue_id));
+
+       ret = hclgevf_send_mbx_msg(hdev, HCLGE_MBX_GET_QID_IN_PF, 0, msg_data,
+                                  2, true, resp_data, 2);
+       if (!ret)
+               qid_in_pf = *(u16 *)resp_data;
+
+       return qid_in_pf;
+}
+
 static int hclgevf_alloc_tqps(struct hclgevf_dev *hdev)
 {
        struct hclgevf_tqp *tqp;
@@ -1081,6 +1150,14 @@ static int hclgevf_reset_tqp(struct hnae3_handle *handle, u16 queue_id)
                                    2, true, NULL, 0);
 }
 
+static int hclgevf_set_mtu(struct hnae3_handle *handle, int new_mtu)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       return hclgevf_send_mbx_msg(hdev, HCLGE_MBX_SET_MTU, 0, (u8 *)&new_mtu,
+                                   sizeof(new_mtu), true, NULL, 0);
+}
+
 static int hclgevf_notify_client(struct hclgevf_dev *hdev,
                                 enum hnae3_reset_notify_type type)
 {
@@ -1265,6 +1342,9 @@ static int hclgevf_reset(struct hclgevf_dev *hdev)
 
        rtnl_unlock();
 
+       hdev->last_reset_time = jiffies;
+       ae_dev->reset_type = HNAE3_NONE_RESET;
+
        return ret;
 err_reset_lock:
        rtnl_unlock();
@@ -1515,6 +1595,28 @@ static void hclgevf_mailbox_service_task(struct work_struct *work)
        clear_bit(HCLGEVF_STATE_MBX_HANDLING, &hdev->state);
 }
 
+static void hclgevf_keep_alive_timer(struct timer_list *t)
+{
+       struct hclgevf_dev *hdev = from_timer(hdev, t, keep_alive_timer);
+
+       schedule_work(&hdev->keep_alive_task);
+       mod_timer(&hdev->keep_alive_timer, jiffies + 2 * HZ);
+}
+
+static void hclgevf_keep_alive_task(struct work_struct *work)
+{
+       struct hclgevf_dev *hdev;
+       u8 respmsg;
+       int ret;
+
+       hdev = container_of(work, struct hclgevf_dev, keep_alive_task);
+       ret = hclgevf_send_mbx_msg(hdev, HCLGE_MBX_KEEP_ALIVE, 0, NULL,
+                                  0, false, &respmsg, sizeof(u8));
+       if (ret)
+               dev_err(&hdev->pdev->dev,
+                       "VF sends keep alive cmd failed(=%d)\n", ret);
+}
+
 static void hclgevf_service_task(struct work_struct *work)
 {
        struct hclgevf_dev *hdev;
@@ -1619,7 +1721,7 @@ static int hclgevf_configure(struct hclgevf_dev *hdev)
 static int hclgevf_alloc_hdev(struct hnae3_ae_dev *ae_dev)
 {
        struct pci_dev *pdev = ae_dev->pdev;
-       struct hclgevf_dev *hdev = ae_dev->priv;
+       struct hclgevf_dev *hdev;
 
        hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
        if (!hdev)
@@ -1655,6 +1757,29 @@ static int hclgevf_init_roce_base_info(struct hclgevf_dev *hdev)
        return 0;
 }
 
+static int hclgevf_config_gro(struct hclgevf_dev *hdev, bool en)
+{
+       struct hclgevf_cfg_gro_status_cmd *req;
+       struct hclgevf_desc desc;
+       int ret;
+
+       if (!hnae3_dev_gro_supported(hdev))
+               return 0;
+
+       hclgevf_cmd_setup_basic_desc(&desc, HCLGEVF_OPC_GRO_GENERIC_CONFIG,
+                                    false);
+       req = (struct hclgevf_cfg_gro_status_cmd *)desc.data;
+
+       req->gro_en = cpu_to_le16(en ? 1 : 0);
+
+       ret = hclgevf_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               dev_err(&hdev->pdev->dev,
+                       "VF GRO hardware config cmd failed, ret = %d.\n", ret);
+
+       return ret;
+}
+
 static int hclgevf_rss_init_hw(struct hclgevf_dev *hdev)
 {
        struct hclgevf_rss_cfg *rss_cfg = &hdev->rss_cfg;
@@ -1715,6 +1840,19 @@ static int hclgevf_init_vlan_config(struct hclgevf_dev *hdev)
                                       false);
 }
 
+static void hclgevf_set_timer_task(struct hnae3_handle *handle, bool enable)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       if (enable) {
+               mod_timer(&hdev->service_timer, jiffies + HZ);
+       } else {
+               del_timer_sync(&hdev->service_timer);
+               cancel_work_sync(&hdev->service_task);
+               clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state);
+       }
+}
+
 static int hclgevf_ae_start(struct hnae3_handle *handle)
 {
        struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
@@ -1725,7 +1863,6 @@ static int hclgevf_ae_start(struct hnae3_handle *handle)
        hclgevf_request_link_info(hdev);
 
        clear_bit(HCLGEVF_STATE_DOWN, &hdev->state);
-       mod_timer(&hdev->service_timer, jiffies + HZ);
 
        return 0;
 }
@@ -1733,17 +1870,50 @@ static int hclgevf_ae_start(struct hnae3_handle *handle)
 static void hclgevf_ae_stop(struct hnae3_handle *handle)
 {
        struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+       int i;
 
        set_bit(HCLGEVF_STATE_DOWN, &hdev->state);
 
+       for (i = 0; i < handle->kinfo.num_tqps; i++)
+               hclgevf_reset_tqp(handle, i);
+
        /* reset tqp stats */
        hclgevf_reset_tqp_stats(handle);
-       del_timer_sync(&hdev->service_timer);
-       cancel_work_sync(&hdev->service_task);
-       clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state);
        hclgevf_update_link_status(hdev, 0);
 }
 
+static int hclgevf_set_alive(struct hnae3_handle *handle, bool alive)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+       u8 msg_data;
+
+       msg_data = alive ? 1 : 0;
+       return hclgevf_send_mbx_msg(hdev, HCLGE_MBX_SET_ALIVE,
+                                   0, &msg_data, 1, false, NULL, 0);
+}
+
+static int hclgevf_client_start(struct hnae3_handle *handle)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       mod_timer(&hdev->keep_alive_timer, jiffies + 2 * HZ);
+       return hclgevf_set_alive(handle, true);
+}
+
+static void hclgevf_client_stop(struct hnae3_handle *handle)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+       int ret;
+
+       ret = hclgevf_set_alive(handle, false);
+       if (ret)
+               dev_warn(&hdev->pdev->dev,
+                        "%s failed %d\n", __func__, ret);
+
+       del_timer_sync(&hdev->keep_alive_timer);
+       cancel_work_sync(&hdev->keep_alive_task);
+}
+
 static void hclgevf_state_init(struct hclgevf_dev *hdev)
 {
        /* setup tasks for the MBX */
@@ -2122,6 +2292,10 @@ static int hclgevf_reset_hdev(struct hclgevf_dev *hdev)
                return ret;
        }
 
+       ret = hclgevf_config_gro(hdev, true);
+       if (ret)
+               return ret;
+
        ret = hclgevf_init_vlan_config(hdev);
        if (ret) {
                dev_err(&hdev->pdev->dev,
@@ -2199,6 +2373,10 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
                goto err_config;
        }
 
+       ret = hclgevf_config_gro(hdev, true);
+       if (ret)
+               goto err_config;
+
        /* Initialize RSS for this VF */
        ret = hclgevf_rss_init_hw(hdev);
        if (ret) {
@@ -2239,15 +2417,16 @@ static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev)
        if (test_bit(HCLGEVF_STATE_IRQ_INITED, &hdev->state)) {
                hclgevf_misc_irq_uninit(hdev);
                hclgevf_uninit_msi(hdev);
-               hclgevf_pci_uninit(hdev);
        }
 
+       hclgevf_pci_uninit(hdev);
        hclgevf_cmd_uninit(hdev);
 }
 
 static int hclgevf_init_ae_dev(struct hnae3_ae_dev *ae_dev)
 {
        struct pci_dev *pdev = ae_dev->pdev;
+       struct hclgevf_dev *hdev;
        int ret;
 
        ret = hclgevf_alloc_hdev(ae_dev);
@@ -2257,10 +2436,16 @@ static int hclgevf_init_ae_dev(struct hnae3_ae_dev *ae_dev)
        }
 
        ret = hclgevf_init_hdev(ae_dev->priv);
-       if (ret)
+       if (ret) {
                dev_err(&pdev->dev, "hclge device initialization failed\n");
+               return ret;
+       }
 
-       return ret;
+       hdev = ae_dev->priv;
+       timer_setup(&hdev->keep_alive_timer, hclgevf_keep_alive_timer, 0);
+       INIT_WORK(&hdev->keep_alive_task, hclgevf_keep_alive_task);
+
+       return 0;
 }
 
 static void hclgevf_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
@@ -2337,6 +2522,13 @@ 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)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       return hclgevf_config_gro(hdev, enable);
+}
+
 static void hclgevf_get_media_type(struct hnae3_handle *handle,
                                  u8 *media_type)
 {
@@ -2366,6 +2558,72 @@ static unsigned long hclgevf_ae_dev_reset_cnt(struct hnae3_handle *handle)
        return hdev->reset_count;
 }
 
+#define MAX_SEPARATE_NUM       4
+#define SEPARATOR_VALUE                0xFFFFFFFF
+#define REG_NUM_PER_LINE       4
+#define REG_LEN_PER_LINE       (REG_NUM_PER_LINE * sizeof(u32))
+
+static int hclgevf_get_regs_len(struct hnae3_handle *handle)
+{
+       int cmdq_lines, common_lines, ring_lines, tqp_intr_lines;
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       cmdq_lines = sizeof(cmdq_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       common_lines = sizeof(common_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       ring_lines = sizeof(ring_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       tqp_intr_lines = sizeof(tqp_intr_reg_addr_list) / REG_LEN_PER_LINE + 1;
+
+       return (cmdq_lines + common_lines + ring_lines * hdev->num_tqps +
+               tqp_intr_lines * (hdev->num_msi_used - 1)) * REG_LEN_PER_LINE;
+}
+
+static void hclgevf_get_regs(struct hnae3_handle *handle, u32 *version,
+                            void *data)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+       int i, j, reg_um, separator_num;
+       u32 *reg = data;
+
+       *version = hdev->fw_version;
+
+       /* fetching per-VF registers values from VF PCIe register space */
+       reg_um = sizeof(cmdq_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (i = 0; i < reg_um; i++)
+               *reg++ = hclgevf_read_dev(&hdev->hw, cmdq_reg_addr_list[i]);
+       for (i = 0; i < separator_num; i++)
+               *reg++ = SEPARATOR_VALUE;
+
+       reg_um = sizeof(common_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (i = 0; i < reg_um; i++)
+               *reg++ = hclgevf_read_dev(&hdev->hw, common_reg_addr_list[i]);
+       for (i = 0; i < separator_num; i++)
+               *reg++ = SEPARATOR_VALUE;
+
+       reg_um = sizeof(ring_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (j = 0; j < hdev->num_tqps; j++) {
+               for (i = 0; i < reg_um; i++)
+                       *reg++ = hclgevf_read_dev(&hdev->hw,
+                                                 ring_reg_addr_list[i] +
+                                                 0x200 * j);
+               for (i = 0; i < separator_num; i++)
+                       *reg++ = SEPARATOR_VALUE;
+       }
+
+       reg_um = sizeof(tqp_intr_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (j = 0; j < hdev->num_msi_used - 1; j++) {
+               for (i = 0; i < reg_um; i++)
+                       *reg++ = hclgevf_read_dev(&hdev->hw,
+                                                 tqp_intr_reg_addr_list[i] +
+                                                 4 * j);
+               for (i = 0; i < separator_num; i++)
+                       *reg++ = SEPARATOR_VALUE;
+       }
+}
+
 static const struct hnae3_ae_ops hclgevf_ops = {
        .init_ae_dev = hclgevf_init_ae_dev,
        .uninit_ae_dev = hclgevf_uninit_ae_dev,
@@ -2375,6 +2633,8 @@ static const struct hnae3_ae_ops hclgevf_ops = {
        .uninit_client_instance = hclgevf_uninit_client_instance,
        .start = hclgevf_ae_start,
        .stop = hclgevf_ae_stop,
+       .client_start = hclgevf_client_start,
+       .client_stop = hclgevf_client_stop,
        .map_ring_to_vector = hclgevf_map_ring_to_vector,
        .unmap_ring_from_vector = hclgevf_unmap_ring_from_vector,
        .get_vector = hclgevf_get_vector,
@@ -2405,12 +2665,18 @@ static const struct hnae3_ae_ops hclgevf_ops = {
        .set_default_reset_request = hclgevf_set_def_reset_request,
        .get_channels = hclgevf_get_channels,
        .get_tqps_and_rss_info = hclgevf_get_tqps_and_rss_info,
+       .get_regs_len = hclgevf_get_regs_len,
+       .get_regs = hclgevf_get_regs,
        .get_status = hclgevf_get_status,
        .get_ksettings_an_result = hclgevf_get_ksettings_an_result,
        .get_media_type = hclgevf_get_media_type,
        .get_hw_reset_stat = hclgevf_get_hw_reset_stat,
        .ae_dev_resetting = hclgevf_ae_dev_resetting,
        .ae_dev_reset_cnt = hclgevf_ae_dev_reset_cnt,
+       .set_gro_en = hclgevf_gro_en,
+       .set_mtu = hclgevf_set_mtu,
+       .get_global_queue_id = hclgevf_get_qid_global,
+       .set_timer_task = hclgevf_set_timer_task,
 };
 
 static struct hnae3_ae_algo ae_algovf = {
index 4c5ea7e..787bc06 100644 (file)
 #define HCLGEVF_VECTOR_REG_OFFSET      0x4
 #define HCLGEVF_VECTOR_VF_OFFSET               0x100000
 
+/* bar registers for cmdq */
+#define HCLGEVF_CMDQ_TX_ADDR_L_REG             0x27000
+#define HCLGEVF_CMDQ_TX_ADDR_H_REG             0x27004
+#define HCLGEVF_CMDQ_TX_DEPTH_REG              0x27008
+#define HCLGEVF_CMDQ_TX_TAIL_REG               0x27010
+#define HCLGEVF_CMDQ_TX_HEAD_REG               0x27014
+#define HCLGEVF_CMDQ_RX_ADDR_L_REG             0x27018
+#define HCLGEVF_CMDQ_RX_ADDR_H_REG             0x2701C
+#define HCLGEVF_CMDQ_RX_DEPTH_REG              0x27020
+#define HCLGEVF_CMDQ_RX_TAIL_REG               0x27024
+#define HCLGEVF_CMDQ_RX_HEAD_REG               0x27028
+#define HCLGEVF_CMDQ_INTR_SRC_REG              0x27100
+#define HCLGEVF_CMDQ_INTR_STS_REG              0x27104
+#define HCLGEVF_CMDQ_INTR_EN_REG               0x27108
+#define HCLGEVF_CMDQ_INTR_GEN_REG              0x2710C
+
+/* bar registers for common func */
+#define HCLGEVF_GRO_EN_REG                     0x28000
+
+/* bar registers for rcb */
+#define HCLGEVF_RING_RX_ADDR_L_REG             0x80000
+#define HCLGEVF_RING_RX_ADDR_H_REG             0x80004
+#define HCLGEVF_RING_RX_BD_NUM_REG             0x80008
+#define HCLGEVF_RING_RX_BD_LENGTH_REG          0x8000C
+#define HCLGEVF_RING_RX_MERGE_EN_REG           0x80014
+#define HCLGEVF_RING_RX_TAIL_REG               0x80018
+#define HCLGEVF_RING_RX_HEAD_REG               0x8001C
+#define HCLGEVF_RING_RX_FBD_NUM_REG            0x80020
+#define HCLGEVF_RING_RX_OFFSET_REG             0x80024
+#define HCLGEVF_RING_RX_FBD_OFFSET_REG         0x80028
+#define HCLGEVF_RING_RX_STASH_REG              0x80030
+#define HCLGEVF_RING_RX_BD_ERR_REG             0x80034
+#define HCLGEVF_RING_TX_ADDR_L_REG             0x80040
+#define HCLGEVF_RING_TX_ADDR_H_REG             0x80044
+#define HCLGEVF_RING_TX_BD_NUM_REG             0x80048
+#define HCLGEVF_RING_TX_PRIORITY_REG           0x8004C
+#define HCLGEVF_RING_TX_TC_REG                 0x80050
+#define HCLGEVF_RING_TX_MERGE_EN_REG           0x80054
+#define HCLGEVF_RING_TX_TAIL_REG               0x80058
+#define HCLGEVF_RING_TX_HEAD_REG               0x8005C
+#define HCLGEVF_RING_TX_FBD_NUM_REG            0x80060
+#define HCLGEVF_RING_TX_OFFSET_REG             0x80064
+#define HCLGEVF_RING_TX_EBD_NUM_REG            0x80068
+#define HCLGEVF_RING_TX_EBD_OFFSET_REG         0x80070
+#define HCLGEVF_RING_TX_BD_ERR_REG             0x80074
+#define HCLGEVF_RING_EN_REG                    0x80090
+
+/* bar registers for tqp interrupt */
+#define HCLGEVF_TQP_INTR_CTRL_REG              0x20000
+#define HCLGEVF_TQP_INTR_GL0_REG               0x20100
+#define HCLGEVF_TQP_INTR_GL1_REG               0x20200
+#define HCLGEVF_TQP_INTR_GL2_REG               0x20300
+#define HCLGEVF_TQP_INTR_RL_REG                        0x20900
+
 /* Vector0 interrupt CMDQ event source register(RW) */
 #define HCLGEVF_VECTOR0_CMDQ_SRC_REG   0x27100
 /* CMDQ register bits for RX event(=MBX event) */
@@ -201,7 +255,9 @@ struct hclgevf_dev {
        struct hclgevf_mbx_arq_ring arq; /* mailbox async rx queue */
 
        struct timer_list service_timer;
+       struct timer_list keep_alive_timer;
        struct work_struct service_task;
+       struct work_struct keep_alive_task;
        struct work_struct rst_service_task;
        struct work_struct mbx_service_task;
 
index ef9c8e6..84653f5 100644 (file)
@@ -26,7 +26,7 @@ static int hclgevf_get_mbx_resp(struct hclgevf_dev *hdev, u16 code0, u16 code1,
                                u8 *resp_data, u16 resp_len)
 {
 #define HCLGEVF_MAX_TRY_TIMES  500
-#define HCLGEVF_SLEEP_USCOEND  1000
+#define HCLGEVF_SLEEP_USECOND  1000
        struct hclgevf_mbx_resp_status *mbx_resp;
        u16 r_code0, r_code1;
        int i = 0;
@@ -43,7 +43,7 @@ static int hclgevf_get_mbx_resp(struct hclgevf_dev *hdev, u16 code0, u16 code1,
                if (test_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state))
                        return -EIO;
 
-               udelay(HCLGEVF_SLEEP_USCOEND);
+               usleep_range(HCLGEVF_SLEEP_USECOND, HCLGEVF_SLEEP_USECOND * 2);
                i++;
        }
 
index 097b550..d1a7d25 100644 (file)
@@ -50,6 +50,8 @@ enum hinic_port_cmd {
 
        HINIC_PORT_CMD_GET_LINK_STATE   = 24,
 
+       HINIC_PORT_CMD_SET_RX_CSUM      = 26,
+
        HINIC_PORT_CMD_SET_PORT_STATE   = 41,
 
        HINIC_PORT_CMD_FWCTXT_INIT      = 69,
index f92f1bf..1dfa7eb 100644 (file)
                        ((void *)((cmdq_pages)->shadow_page_vaddr) \
                                + (wq)->block_idx * CMDQ_BLOCK_SIZE)
 
-#define WQE_PAGE_OFF(wq, idx)   (((idx) & ((wq)->num_wqebbs_per_page - 1)) * \
-                                       (wq)->wqebb_size)
-
-#define WQE_PAGE_NUM(wq, idx)   (((idx) / ((wq)->num_wqebbs_per_page)) \
-                                       & ((wq)->num_q_pages - 1))
-
 #define WQ_PAGE_ADDR(wq, idx)           \
                        ((wq)->shadow_block_vaddr[WQE_PAGE_NUM(wq, idx)])
 
                (((unsigned long)(wqe) - (unsigned long)(wq)->shadow_wqe) \
                        / (wq)->max_wqe_size)
 
+static inline int WQE_PAGE_OFF(struct hinic_wq *wq, u16 idx)
+{
+       return (((idx) & ((wq)->num_wqebbs_per_page - 1))
+               << (wq)->wqebb_size_shift);
+}
+
+static inline int WQE_PAGE_NUM(struct hinic_wq *wq, u16 idx)
+{
+       return (((idx) >> ((wq)->wqebbs_per_page_shift))
+               & ((wq)->num_q_pages - 1));
+}
 /**
  * queue_alloc_page - allocate page for Queue
  * @hwif: HW interface for allocating DMA
@@ -513,10 +518,11 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
        struct hinic_hwif *hwif = wqs->hwif;
        struct pci_dev *pdev = hwif->pdev;
        u16 num_wqebbs_per_page;
+       u16 wqebb_size_shift;
        int err;
 
-       if (wqebb_size == 0) {
-               dev_err(&pdev->dev, "wqebb_size must be > 0\n");
+       if (!is_power_of_2(wqebb_size)) {
+               dev_err(&pdev->dev, "wqebb_size must be power of 2\n");
                return -EINVAL;
        }
 
@@ -530,9 +536,11 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
                return -EINVAL;
        }
 
-       num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size;
+       wqebb_size_shift = ilog2(wqebb_size);
+       num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size)
+                               >> wqebb_size_shift;
 
-       if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) {
+       if (!is_power_of_2(num_wqebbs_per_page)) {
                dev_err(&pdev->dev, "num wqebbs per page must be power of 2\n");
                return -EINVAL;
        }
@@ -550,7 +558,8 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
        wq->q_depth = q_depth;
        wq->max_wqe_size = max_wqe_size;
        wq->num_wqebbs_per_page = num_wqebbs_per_page;
-
+       wq->wqebbs_per_page_shift = ilog2(num_wqebbs_per_page);
+       wq->wqebb_size_shift = wqebb_size_shift;
        wq->block_vaddr = WQ_BASE_VADDR(wqs, wq);
        wq->shadow_block_vaddr = WQ_BASE_ADDR(wqs, wq);
        wq->block_paddr = WQ_BASE_PADDR(wqs, wq);
@@ -604,11 +613,13 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
                         u16 q_depth, u16 max_wqe_size)
 {
        struct pci_dev *pdev = hwif->pdev;
+       u16 num_wqebbs_per_page_shift;
        u16 num_wqebbs_per_page;
+       u16 wqebb_size_shift;
        int i, j, err = -ENOMEM;
 
-       if (wqebb_size == 0) {
-               dev_err(&pdev->dev, "wqebb_size must be > 0\n");
+       if (!is_power_of_2(wqebb_size)) {
+               dev_err(&pdev->dev, "wqebb_size must be power of 2\n");
                return -EINVAL;
        }
 
@@ -622,9 +633,11 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
                return -EINVAL;
        }
 
-       num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size;
+       wqebb_size_shift = ilog2(wqebb_size);
+       num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size)
+                               >> wqebb_size_shift;
 
-       if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) {
+       if (!is_power_of_2(num_wqebbs_per_page)) {
                dev_err(&pdev->dev, "num wqebbs per page must be power of 2\n");
                return -EINVAL;
        }
@@ -636,6 +649,7 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
                dev_err(&pdev->dev, "Failed to allocate CMDQ page\n");
                return err;
        }
+       num_wqebbs_per_page_shift = ilog2(num_wqebbs_per_page);
 
        for (i = 0; i < cmdq_blocks; i++) {
                wq[i].hwif = hwif;
@@ -647,7 +661,8 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
                wq[i].q_depth = q_depth;
                wq[i].max_wqe_size = max_wqe_size;
                wq[i].num_wqebbs_per_page = num_wqebbs_per_page;
-
+               wq[i].wqebbs_per_page_shift = num_wqebbs_per_page_shift;
+               wq[i].wqebb_size_shift = wqebb_size_shift;
                wq[i].block_vaddr = CMDQ_BASE_VADDR(cmdq_pages, &wq[i]);
                wq[i].shadow_block_vaddr = CMDQ_BASE_ADDR(cmdq_pages, &wq[i]);
                wq[i].block_paddr = CMDQ_BASE_PADDR(cmdq_pages, &wq[i]);
@@ -741,7 +756,7 @@ struct hinic_hw_wqe *hinic_get_wqe(struct hinic_wq *wq, unsigned int wqe_size,
 
        *prod_idx = MASKED_WQE_IDX(wq, atomic_read(&wq->prod_idx));
 
-       num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+       num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) >> wq->wqebb_size_shift;
 
        if (atomic_sub_return(num_wqebbs, &wq->delta) <= 0) {
                atomic_add(num_wqebbs, &wq->delta);
@@ -795,7 +810,8 @@ void hinic_return_wqe(struct hinic_wq *wq, unsigned int wqe_size)
  **/
 void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size)
 {
-       int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+       int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size)
+                       >> wq->wqebb_size_shift;
 
        atomic_add(num_wqebbs, &wq->cons_idx);
 
@@ -813,7 +829,8 @@ void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size)
 struct hinic_hw_wqe *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size,
                                    u16 *cons_idx)
 {
-       int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+       int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size)
+                       >> wq->wqebb_size_shift;
        u16 curr_cons_idx, end_cons_idx;
        int curr_pg, end_pg;
 
index 9b66545..0a936cd 100644 (file)
@@ -39,7 +39,8 @@ struct hinic_wq {
        u16             q_depth;
        u16             max_wqe_size;
        u16             num_wqebbs_per_page;
-
+       u16             wqebbs_per_page_shift;
+       u16             wqebb_size_shift;
        /* The addresses are 64 bit in the HW */
        u64             block_paddr;
        void            **shadow_block_vaddr;
index 9754d6e..1389415 100644 (file)
 
 #define HINIC_RQ_CQE_STATUS_RXDONE_MASK         0x1
 
+#define HINIC_RQ_CQE_STATUS_CSUM_ERR_SHIFT     0
+
+#define HINIC_RQ_CQE_STATUS_CSUM_ERR_MASK      0xFFFFU
+
 #define HINIC_RQ_CQE_STATUS_GET(val, member)    \
                (((val) >> HINIC_RQ_CQE_STATUS_##member##_SHIFT) & \
                 HINIC_RQ_CQE_STATUS_##member##_MASK)
index fdf2bdb..6d48dc6 100644 (file)
@@ -600,9 +600,6 @@ static int add_mac_addr(struct net_device *netdev, const u8 *addr)
        u16 vid = 0;
        int err;
 
-       if (!is_valid_ether_addr(addr))
-               return -EADDRNOTAVAIL;
-
        netif_info(nic_dev, drv, netdev, "set mac addr = %02x %02x %02x %02x %02x %02x\n",
                   addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
 
@@ -726,6 +723,7 @@ static void set_rx_mode(struct work_struct *work)
 {
        struct hinic_rx_mode_work *rx_mode_work = work_to_rx_mode_work(work);
        struct hinic_dev *nic_dev = rx_mode_work_to_nic_dev(rx_mode_work);
+       struct netdev_hw_addr *ha;
 
        netif_info(nic_dev, drv, nic_dev->netdev, "set rx mode work\n");
 
@@ -733,6 +731,9 @@ static void set_rx_mode(struct work_struct *work)
 
        __dev_uc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
        __dev_mc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
+
+       netdev_for_each_mc_addr(ha, nic_dev->netdev)
+               add_mac_addr(nic_dev->netdev, ha->addr);
 }
 
 static void hinic_set_rx_mode(struct net_device *netdev)
@@ -806,7 +807,8 @@ static const struct net_device_ops hinic_netdev_ops = {
 static void netdev_features_init(struct net_device *netdev)
 {
        netdev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
-                             NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6;
+                             NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |
+                             NETIF_F_RXCSUM;
 
        netdev->vlan_features = netdev->hw_features;
 
@@ -869,12 +871,16 @@ static int set_features(struct hinic_dev *nic_dev,
                        netdev_features_t features, bool force_change)
 {
        netdev_features_t changed = force_change ? ~0 : pre_features ^ features;
+       u32 csum_en = HINIC_RX_CSUM_OFFLOAD_EN;
        int err = 0;
 
        if (changed & NETIF_F_TSO)
                err = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ?
                                         HINIC_TSO_ENABLE : HINIC_TSO_DISABLE);
 
+       if (changed & NETIF_F_RXCSUM)
+               err = hinic_set_rx_csum_offload(nic_dev, csum_en);
+
        return err;
 }
 
index 7575a7d..122c935 100644 (file)
@@ -409,3 +409,33 @@ int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state)
 
        return 0;
 }
+
+int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en)
+{
+       struct hinic_checksum_offload rx_csum_cfg = {0};
+       struct hinic_hwdev *hwdev = nic_dev->hwdev;
+       struct hinic_hwif *hwif;
+       struct pci_dev *pdev;
+       u16 out_size;
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       hwif = hwdev->hwif;
+       pdev = hwif->pdev;
+       rx_csum_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
+       rx_csum_cfg.rx_csum_offload = en;
+
+       err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_CSUM,
+                                &rx_csum_cfg, sizeof(rx_csum_cfg),
+                                &rx_csum_cfg, &out_size);
+       if (err || !out_size || rx_csum_cfg.status) {
+               dev_err(&pdev->dev,
+                       "Failed to set rx csum offload, ret = %d\n",
+                       rx_csum_cfg.status);
+               return -EINVAL;
+       }
+
+       return 0;
+}
index f6e3220..02d896e 100644 (file)
@@ -183,6 +183,15 @@ struct hinic_tso_config {
        u8      resv2[3];
 };
 
+struct hinic_checksum_offload {
+       u8      status;
+       u8      version;
+       u8      rsvd0[6];
+
+       u16     func_id;
+       u16     rsvd1;
+       u32     rx_csum_offload;
+};
 int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr,
                       u16 vlan_id);
 
@@ -213,4 +222,5 @@ int hinic_port_get_cap(struct hinic_dev *nic_dev,
 
 int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state);
 
+int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en);
 #endif
index 4c0f7ed..0098b20 100644 (file)
@@ -43,6 +43,7 @@
 #define RX_IRQ_NO_LLI_TIMER             0
 #define RX_IRQ_NO_CREDIT                0
 #define RX_IRQ_NO_RESEND_TIMER          0
+#define HINIC_RX_BUFFER_WRITE           16
 
 /**
  * hinic_rxq_clean_stats - Clean the statistics of specific queue
@@ -89,6 +90,28 @@ static void rxq_stats_init(struct hinic_rxq *rxq)
        hinic_rxq_clean_stats(rxq);
 }
 
+static void rx_csum(struct hinic_rxq *rxq, u16 cons_idx,
+                   struct sk_buff *skb)
+{
+       struct net_device *netdev = rxq->netdev;
+       struct hinic_rq_cqe *cqe;
+       struct hinic_rq *rq;
+       u32 csum_err;
+       u32 status;
+
+       rq = rxq->rq;
+       cqe = rq->cqe[cons_idx];
+       status = be32_to_cpu(cqe->status);
+       csum_err = HINIC_RQ_CQE_STATUS_GET(status, CSUM_ERR);
+
+       if (!(netdev->features & NETIF_F_RXCSUM))
+               return;
+
+       if (!csum_err)
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       else
+               skb->ip_summed = CHECKSUM_NONE;
+}
 /**
  * rx_alloc_skb - allocate skb and map it to dma address
  * @rxq: rx queue
@@ -209,7 +232,6 @@ skb_out:
                hinic_rq_update(rxq->rq, prod_idx);
        }
 
-       tasklet_schedule(&rxq->rx_task);
        return i;
 }
 
@@ -236,17 +258,6 @@ static void free_all_rx_skbs(struct hinic_rxq *rxq)
        }
 }
 
-/**
- * rx_alloc_task - tasklet for queue allocation
- * @data: rx queue
- **/
-static void rx_alloc_task(unsigned long data)
-{
-       struct hinic_rxq *rxq = (struct hinic_rxq *)data;
-
-       (void)rx_alloc_pkts(rxq);
-}
-
 /**
  * rx_recv_jumbo_pkt - Rx handler for jumbo pkt
  * @rxq: rx queue
@@ -311,6 +322,7 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
        struct hinic_qp *qp = container_of(rxq->rq, struct hinic_qp, rq);
        u64 pkt_len = 0, rx_bytes = 0;
        struct hinic_rq_wqe *rq_wqe;
+       unsigned int free_wqebbs;
        int num_wqes, pkts = 0;
        struct hinic_sge sge;
        struct sk_buff *skb;
@@ -328,6 +340,8 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
 
                rx_unmap_skb(rxq, hinic_sge_to_dma(&sge));
 
+               rx_csum(rxq, ci, skb);
+
                prefetch(skb->data);
 
                pkt_len = sge.len;
@@ -352,8 +366,9 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
                rx_bytes += pkt_len;
        }
 
-       if (pkts)
-               tasklet_schedule(&rxq->rx_task); /* rx_alloc_pkts */
+       free_wqebbs = hinic_get_rq_free_wqebbs(rxq->rq);
+       if (free_wqebbs > HINIC_RX_BUFFER_WRITE)
+               rx_alloc_pkts(rxq);
 
        u64_stats_update_begin(&rxq->rxq_stats.syncp);
        rxq->rxq_stats.pkts += pkts;
@@ -470,8 +485,6 @@ int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq,
 
        sprintf(rxq->irq_name, "hinic_rxq%d", qp->q_id);
 
-       tasklet_init(&rxq->rx_task, rx_alloc_task, (unsigned long)rxq);
-
        pkts = rx_alloc_pkts(rxq);
        if (!pkts) {
                err = -ENOMEM;
@@ -488,7 +501,6 @@ int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq,
 
 err_req_rx_irq:
 err_rx_pkts:
-       tasklet_kill(&rxq->rx_task);
        free_all_rx_skbs(rxq);
        devm_kfree(&netdev->dev, rxq->irq_name);
        return err;
@@ -504,7 +516,6 @@ void hinic_clean_rxq(struct hinic_rxq *rxq)
 
        rx_free_irq(rxq);
 
-       tasklet_kill(&rxq->rx_task);
        free_all_rx_skbs(rxq);
        devm_kfree(&netdev->dev, rxq->irq_name);
 }
index 27c9af4..f8ed3fa 100644 (file)
 
 #include "hinic_hw_qp.h"
 
+#define HINIC_RX_CSUM_OFFLOAD_EN       0xFFF
+#define HINIC_RX_CSUM_HW_CHECK_NONE    BIT(7)
+#define HINIC_RX_CSUM_IPSU_OTHER_ERR   BIT(8)
+
 struct hinic_rxq_stats {
        u64                     pkts;
        u64                     bytes;
@@ -38,8 +42,6 @@ struct hinic_rxq {
 
        char                    *irq_name;
 
-       struct tasklet_struct   rx_task;
-
        struct napi_struct      napi;
 };
 
index e2f80cc..0d2de6f 100644 (file)
@@ -231,7 +231,7 @@ struct emac_regs {
 #define EMAC_STACR_PHYE                        0x00004000
 #define EMAC_STACR_STAC_MASK           0x00003000
 #define EMAC_STACR_STAC_READ           0x00001000
-#define EMAC_STACR_STAC_WRITE          0x00000800
+#define EMAC_STACR_STAC_WRITE          0x00002000
 #define EMAC_STACR_OPBC_MASK           0x00000C00
 #define EMAC_STACR_OPBC_50             0x00000000
 #define EMAC_STACR_OPBC_66             0x00000400
index c9d5d0a..5ecbb1a 100644 (file)
@@ -485,8 +485,8 @@ static void release_rx_pools(struct ibmvnic_adapter *adapter)
 
                for (j = 0; j < rx_pool->size; j++) {
                        if (rx_pool->rx_buff[j].skb) {
-                               dev_kfree_skb_any(rx_pool->rx_buff[i].skb);
-                               rx_pool->rx_buff[i].skb = NULL;
+                               dev_kfree_skb_any(rx_pool->rx_buff[j].skb);
+                               rx_pool->rx_buff[j].skb = NULL;
                        }
                }
 
@@ -773,11 +773,8 @@ static void release_napi(struct ibmvnic_adapter *adapter)
                return;
 
        for (i = 0; i < adapter->num_active_rx_napi; i++) {
-               if (&adapter->napi[i]) {
-                       netdev_dbg(adapter->netdev,
-                                  "Releasing napi[%d]\n", i);
-                       netif_napi_del(&adapter->napi[i]);
-               }
+               netdev_dbg(adapter->netdev, "Releasing napi[%d]\n", i);
+               netif_napi_del(&adapter->napi[i]);
        }
 
        kfree(adapter->napi);
@@ -1103,20 +1100,15 @@ static int ibmvnic_open(struct net_device *netdev)
                return 0;
        }
 
-       mutex_lock(&adapter->reset_lock);
-
        if (adapter->state != VNIC_CLOSED) {
                rc = ibmvnic_login(netdev);
-               if (rc) {
-                       mutex_unlock(&adapter->reset_lock);
+               if (rc)
                        return rc;
-               }
 
                rc = init_resources(adapter);
                if (rc) {
                        netdev_err(netdev, "failed to initialize resources\n");
                        release_resources(adapter);
-                       mutex_unlock(&adapter->reset_lock);
                        return rc;
                }
        }
@@ -1124,8 +1116,6 @@ static int ibmvnic_open(struct net_device *netdev)
        rc = __ibmvnic_open(netdev);
        netif_carrier_on(netdev);
 
-       mutex_unlock(&adapter->reset_lock);
-
        return rc;
 }
 
@@ -1269,10 +1259,8 @@ static int ibmvnic_close(struct net_device *netdev)
                return 0;
        }
 
-       mutex_lock(&adapter->reset_lock);
        rc = __ibmvnic_close(netdev);
        ibmvnic_cleanup(netdev);
-       mutex_unlock(&adapter->reset_lock);
 
        return rc;
 }
@@ -1746,6 +1734,7 @@ static int do_reset(struct ibmvnic_adapter *adapter,
                    struct ibmvnic_rwi *rwi, u32 reset_state)
 {
        u64 old_num_rx_queues, old_num_tx_queues;
+       u64 old_num_rx_slots, old_num_tx_slots;
        struct net_device *netdev = adapter->netdev;
        int i, rc;
 
@@ -1757,6 +1746,8 @@ static int do_reset(struct ibmvnic_adapter *adapter,
 
        old_num_rx_queues = adapter->req_rx_queues;
        old_num_tx_queues = adapter->req_tx_queues;
+       old_num_rx_slots = adapter->req_rx_add_entries_per_subcrq;
+       old_num_tx_slots = adapter->req_tx_entries_per_subcrq;
 
        ibmvnic_cleanup(netdev);
 
@@ -1819,21 +1810,20 @@ static int do_reset(struct ibmvnic_adapter *adapter,
                        if (rc)
                                return rc;
                } else if (adapter->req_rx_queues != old_num_rx_queues ||
-                          adapter->req_tx_queues != old_num_tx_queues) {
-                       adapter->map_id = 1;
+                          adapter->req_tx_queues != old_num_tx_queues ||
+                          adapter->req_rx_add_entries_per_subcrq !=
+                                                       old_num_rx_slots ||
+                          adapter->req_tx_entries_per_subcrq !=
+                                                       old_num_tx_slots) {
                        release_rx_pools(adapter);
                        release_tx_pools(adapter);
-                       rc = init_rx_pools(netdev);
-                       if (rc)
-                               return rc;
-                       rc = init_tx_pools(netdev);
-                       if (rc)
-                               return rc;
-
                        release_napi(adapter);
-                       rc = init_napi(adapter);
+                       release_vpd_data(adapter);
+
+                       rc = init_resources(adapter);
                        if (rc)
                                return rc;
+
                } else {
                        rc = reset_tx_pools(adapter);
                        if (rc)
@@ -1866,7 +1856,7 @@ static int do_reset(struct ibmvnic_adapter *adapter,
 
        if (adapter->reset_reason != VNIC_RESET_FAILOVER &&
            adapter->reset_reason != VNIC_RESET_CHANGE_PARAM)
-               netdev_notify_peers(netdev);
+               call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, netdev);
 
        netif_carrier_on(netdev);
 
@@ -1917,17 +1907,8 @@ static int do_hard_reset(struct ibmvnic_adapter *adapter,
                adapter->state = VNIC_PROBED;
                return 0;
        }
-       /* netif_set_real_num_xx_queues needs to take rtnl lock here
-        * unless wait_for_reset is set, in which case the rtnl lock
-        * has already been taken before initializing the reset
-        */
-       if (!adapter->wait_for_reset) {
-               rtnl_lock();
-               rc = init_resources(adapter);
-               rtnl_unlock();
-       } else {
-               rc = init_resources(adapter);
-       }
+
+       rc = init_resources(adapter);
        if (rc)
                return rc;
 
@@ -1955,8 +1936,9 @@ static int do_hard_reset(struct ibmvnic_adapter *adapter,
 static struct ibmvnic_rwi *get_next_rwi(struct ibmvnic_adapter *adapter)
 {
        struct ibmvnic_rwi *rwi;
+       unsigned long flags;
 
-       mutex_lock(&adapter->rwi_lock);
+       spin_lock_irqsave(&adapter->rwi_lock, flags);
 
        if (!list_empty(&adapter->rwi_list)) {
                rwi = list_first_entry(&adapter->rwi_list, struct ibmvnic_rwi,
@@ -1966,7 +1948,7 @@ static struct ibmvnic_rwi *get_next_rwi(struct ibmvnic_adapter *adapter)
                rwi = NULL;
        }
 
-       mutex_unlock(&adapter->rwi_lock);
+       spin_unlock_irqrestore(&adapter->rwi_lock, flags);
        return rwi;
 }
 
@@ -1986,13 +1968,21 @@ static void __ibmvnic_reset(struct work_struct *work)
        struct ibmvnic_rwi *rwi;
        struct ibmvnic_adapter *adapter;
        struct net_device *netdev;
+       bool we_lock_rtnl = false;
        u32 reset_state;
        int rc = 0;
 
        adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset);
        netdev = adapter->netdev;
 
-       mutex_lock(&adapter->reset_lock);
+       /* netif_set_real_num_xx_queues needs to take rtnl lock here
+        * unless wait_for_reset is set, in which case the rtnl lock
+        * has already been taken before initializing the reset
+        */
+       if (!adapter->wait_for_reset) {
+               rtnl_lock();
+               we_lock_rtnl = true;
+       }
        reset_state = adapter->state;
 
        rwi = get_next_rwi(adapter);
@@ -2020,12 +2010,11 @@ static void __ibmvnic_reset(struct work_struct *work)
        if (rc) {
                netdev_dbg(adapter->netdev, "Reset failed\n");
                free_all_rwi(adapter);
-               mutex_unlock(&adapter->reset_lock);
-               return;
        }
 
        adapter->resetting = false;
-       mutex_unlock(&adapter->reset_lock);
+       if (we_lock_rtnl)
+               rtnl_unlock();
 }
 
 static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
@@ -2034,6 +2023,7 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
        struct list_head *entry, *tmp_entry;
        struct ibmvnic_rwi *rwi, *tmp;
        struct net_device *netdev = adapter->netdev;
+       unsigned long flags;
        int ret;
 
        if (adapter->state == VNIC_REMOVING ||
@@ -2050,21 +2040,21 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
                goto err;
        }
 
-       mutex_lock(&adapter->rwi_lock);
+       spin_lock_irqsave(&adapter->rwi_lock, flags);
 
        list_for_each(entry, &adapter->rwi_list) {
                tmp = list_entry(entry, struct ibmvnic_rwi, list);
                if (tmp->reset_reason == reason) {
                        netdev_dbg(netdev, "Skipping matching reset\n");
-                       mutex_unlock(&adapter->rwi_lock);
+                       spin_unlock_irqrestore(&adapter->rwi_lock, flags);
                        ret = EBUSY;
                        goto err;
                }
        }
 
-       rwi = kzalloc(sizeof(*rwi), GFP_KERNEL);
+       rwi = kzalloc(sizeof(*rwi), GFP_ATOMIC);
        if (!rwi) {
-               mutex_unlock(&adapter->rwi_lock);
+               spin_unlock_irqrestore(&adapter->rwi_lock, flags);
                ibmvnic_close(netdev);
                ret = ENOMEM;
                goto err;
@@ -2078,7 +2068,7 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
        }
        rwi->reset_reason = reason;
        list_add_tail(&rwi->list, &adapter->rwi_list);
-       mutex_unlock(&adapter->rwi_lock);
+       spin_unlock_irqrestore(&adapter->rwi_lock, flags);
        adapter->resetting = true;
        netdev_dbg(adapter->netdev, "Scheduling reset (reason %d)\n", reason);
        schedule_work(&adapter->ibmvnic_reset);
@@ -4768,8 +4758,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
 
        INIT_WORK(&adapter->ibmvnic_reset, __ibmvnic_reset);
        INIT_LIST_HEAD(&adapter->rwi_list);
-       mutex_init(&adapter->reset_lock);
-       mutex_init(&adapter->rwi_lock);
+       spin_lock_init(&adapter->rwi_lock);
        adapter->resetting = false;
 
        adapter->mac_change_pending = false;
@@ -4840,8 +4829,8 @@ static int ibmvnic_remove(struct vio_dev *dev)
        struct ibmvnic_adapter *adapter = netdev_priv(netdev);
 
        adapter->state = VNIC_REMOVING;
-       unregister_netdev(netdev);
-       mutex_lock(&adapter->reset_lock);
+       rtnl_lock();
+       unregister_netdevice(netdev);
 
        release_resources(adapter);
        release_sub_crqs(adapter, 1);
@@ -4852,7 +4841,7 @@ static int ibmvnic_remove(struct vio_dev *dev)
 
        adapter->state = VNIC_REMOVED;
 
-       mutex_unlock(&adapter->reset_lock);
+       rtnl_unlock();
        device_remove_file(&dev->dev, &dev_attr_failover);
        free_netdev(netdev);
        dev_set_drvdata(&dev->dev, NULL);
index 18103b8..f2018db 100644 (file)
@@ -1075,7 +1075,7 @@ struct ibmvnic_adapter {
        struct tasklet_struct tasklet;
        enum vnic_state state;
        enum ibmvnic_reset_reason reset_reason;
-       struct mutex reset_lock, rwi_lock;
+       spinlock_t rwi_lock;
        struct list_head rwi_list;
        struct work_struct ibmvnic_reset;
        bool resetting;
index 59e1bc0..31fb76e 100644 (file)
@@ -33,7 +33,7 @@ config E100
          to identify the adapter.
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/e100.rst>.
+         <file:Documentation/networking/device_drivers/intel/e100.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called e100.
@@ -49,7 +49,7 @@ config E1000
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/e1000.rst>.
+         <file:Documentation/networking/device_drivers/intel/e1000.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called e1000.
@@ -69,7 +69,7 @@ config E1000E
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/e1000e.rst>.
+         <file:Documentation/networking/device_drivers/intel/e1000e.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called e1000e.
@@ -97,7 +97,7 @@ config IGB
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/igb.rst>.
+         <file:Documentation/networking/device_drivers/intel/igb.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called igb.
@@ -133,7 +133,7 @@ config IGBVF
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/igbvf.rst>.
+         <file:Documentation/networking/device_drivers/intel/igbvf.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called igbvf.
@@ -150,7 +150,7 @@ config IXGB
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/ixgb.rst>.
+         <file:Documentation/networking/device_drivers/intel/ixgb.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called ixgb.
@@ -159,6 +159,7 @@ config IXGBE
        tristate "Intel(R) 10GbE PCI Express adapters support"
        depends on PCI
        select MDIO
+       select MDIO_DEVICE
        imply PTP_1588_CLOCK
        ---help---
          This driver supports Intel(R) 10GbE PCI Express family of
@@ -168,7 +169,7 @@ config IXGBE
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/ixgbe.rst>.
+         <file:Documentation/networking/device_drivers/intel/ixgbe.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called ixgbe.
@@ -220,7 +221,7 @@ config IXGBEVF
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/ixgbevf.rst>.
+         <file:Documentation/networking/device_drivers/intel/ixgbevf.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called ixgbevf.  MSI-X interrupt support is required
@@ -247,7 +248,7 @@ config I40E
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/i40e.rst>.
+         <file:Documentation/networking/device_drivers/intel/i40e.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called i40e.
@@ -282,7 +283,7 @@ config I40EVF
          This driver was formerly named i40evf.
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/iavf.rst>.
+         <file:Documentation/networking/device_drivers/intel/iavf.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called iavf.  MSI-X interrupt support is required
@@ -300,7 +301,7 @@ config ICE
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/ice.rst>.
+         <file:Documentation/networking/device_drivers/intel/ice.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called ice.
@@ -318,7 +319,7 @@ config FM10K
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/fm10k.rst>.
+         <file:Documentation/networking/device_drivers/intel/fm10k.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called fm10k.  MSI-X interrupt support is required
index 7c4b554..0fd2680 100644 (file)
@@ -1345,8 +1345,8 @@ static inline int e100_load_ucode_wait(struct nic *nic)
 
        fw = e100_request_firmware(nic);
        /* If it's NULL, then no ucode is required */
-       if (!fw || IS_ERR(fw))
-               return PTR_ERR(fw);
+       if (IS_ERR_OR_NULL(fw))
+               return PTR_ERR_OR_ZERO(fw);
 
        if ((err = e100_exec_cb(nic, (void *)fw, e100_setup_ucode)))
                netif_err(nic, probe, nic->netdev,
@@ -2225,11 +2225,13 @@ static int e100_poll(struct napi_struct *napi, int budget)
        e100_rx_clean(nic, &work_done, budget);
        e100_tx_clean(nic);
 
-       /* If budget not fully consumed, exit the polling mode */
-       if (work_done < budget) {
-               napi_complete_done(napi, work_done);
+       /* If budget fully consumed, continue polling */
+       if (work_done == budget)
+               return budget;
+
+       /* only re-enable interrupt if stack agrees polling is really done */
+       if (likely(napi_complete_done(napi, work_done)))
                e100_enable_irq(nic);
-       }
 
        return work_done;
 }
index 43b6d3c..8fe9af0 100644 (file)
@@ -3803,14 +3803,15 @@ static int e1000_clean(struct napi_struct *napi, int budget)
 
        adapter->clean_rx(adapter, &adapter->rx_ring[0], &work_done, budget);
 
-       if (!tx_clean_complete)
-               work_done = budget;
+       if (!tx_clean_complete || work_done == budget)
+               return budget;
 
-       /* If budget not fully consumed, exit the polling mode */
-       if (work_done < budget) {
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done))) {
                if (likely(adapter->itr_setting & 3))
                        e1000_set_itr(adapter);
-               napi_complete_done(napi, work_done);
                if (!test_bit(__E1000_DOWN, &adapter->flags))
                        e1000_irq_enable(adapter);
        }
index 59bd587..308c006 100644 (file)
@@ -2651,9 +2651,9 @@ err:
 /**
  * e1000e_poll - NAPI Rx polling callback
  * @napi: struct associated with this polling callback
- * @weight: number of packets driver is allowed to process this poll
+ * @budget: number of packets driver is allowed to process this poll
  **/
-static int e1000e_poll(struct napi_struct *napi, int weight)
+static int e1000e_poll(struct napi_struct *napi, int budget)
 {
        struct e1000_adapter *adapter = container_of(napi, struct e1000_adapter,
                                                     napi);
@@ -2667,16 +2667,17 @@ static int e1000e_poll(struct napi_struct *napi, int weight)
            (adapter->rx_ring->ims_val & adapter->tx_ring->ims_val))
                tx_cleaned = e1000_clean_tx_irq(adapter->tx_ring);
 
-       adapter->clean_rx(adapter->rx_ring, &work_done, weight);
+       adapter->clean_rx(adapter->rx_ring, &work_done, budget);
 
-       if (!tx_cleaned)
-               work_done = weight;
+       if (!tx_cleaned || work_done == budget)
+               return budget;
 
-       /* If weight not fully consumed, exit the polling mode */
-       if (work_done < weight) {
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done))) {
                if (adapter->itr_setting & 3)
                        e1000_set_itr(adapter);
-               napi_complete_done(napi, work_done);
                if (!test_bit(__E1000_DOWN, &adapter->state)) {
                        if (adapter->msix_entries)
                                ew32(IMS, adapter->rx_ring->ims_val);
index 5b2a50e..6fd15a7 100644 (file)
@@ -1465,11 +1465,11 @@ static int fm10k_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
 
-       /* all work done, exit the polling mode */
-       napi_complete_done(napi, work_done);
-
-       /* re-enable the q_vector */
-       fm10k_qv_enable(q_vector);
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               fm10k_qv_enable(q_vector);
 
        return min(work_done, budget - 1);
 }
index 876cac3..8de9085 100644 (file)
@@ -122,6 +122,7 @@ enum i40e_state_t {
        __I40E_MDD_EVENT_PENDING,
        __I40E_VFLR_EVENT_PENDING,
        __I40E_RESET_RECOVERY_PENDING,
+       __I40E_TIMEOUT_RECOVERY_PENDING,
        __I40E_MISC_IRQ_REQUESTED,
        __I40E_RESET_INTR_RECEIVED,
        __I40E_REINIT_REQUESTED,
@@ -146,6 +147,7 @@ enum i40e_state_t {
        __I40E_CLIENT_SERVICE_REQUESTED,
        __I40E_CLIENT_L2_CHANGE,
        __I40E_CLIENT_RESET,
+       __I40E_VIRTCHNL_OP_PENDING,
        /* This must be last as it determines the size of the BITMAP */
        __I40E_STATE_SIZE__,
 };
@@ -494,7 +496,6 @@ struct i40e_pf {
 #define I40E_HW_STOP_FW_LLDP                   BIT(16)
 #define I40E_HW_PORT_ID_VALID                  BIT(17)
 #define I40E_HW_RESTART_AUTONEG                        BIT(18)
-#define I40E_HW_STOPPABLE_FW_LLDP              BIT(19)
 
        u32 flags;
 #define I40E_FLAG_RX_CSUM_ENABLED              BIT(0)
index 501ee71..7ab61f6 100644 (file)
@@ -588,6 +588,12 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw)
            hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
            hw->aq.api_min_ver >= I40E_MINOR_VER_GET_LINK_INFO_XL710) {
                hw->flags |= I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE;
+               hw->flags |= I40E_HW_FLAG_FW_LLDP_STOPPABLE;
+       }
+       if (hw->mac.type == I40E_MAC_X722 &&
+           hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
+           hw->aq.api_min_ver >= I40E_MINOR_VER_FW_LLDP_STOPPABLE_X722) {
+               hw->flags |= I40E_HW_FLAG_FW_LLDP_STOPPABLE;
        }
 
        /* Newer versions of firmware require lock when reading the NVM */
index 80e3eec..1150610 100644 (file)
@@ -11,7 +11,7 @@
  */
 
 #define I40E_FW_API_VERSION_MAJOR      0x0001
-#define I40E_FW_API_VERSION_MINOR_X722 0x0005
+#define I40E_FW_API_VERSION_MINOR_X722 0x0006
 #define I40E_FW_API_VERSION_MINOR_X710 0x0007
 
 #define I40E_FW_MINOR_VERSION(_h) ((_h)->mac.type == I40E_MAC_XL710 ? \
@@ -20,6 +20,8 @@
 
 /* API version 1.7 implements additional link and PHY-specific APIs  */
 #define I40E_MINOR_VER_GET_LINK_INFO_XL710 0x0007
+/* API version 1.6 for X722 devices adds ability to stop FW LLDP agent */
+#define I40E_MINOR_VER_FW_LLDP_STOPPABLE_X722 0x0006
 
 struct i40e_aq_desc {
        __le16 flags;
index 85f75b5..97a9b1f 100644 (file)
@@ -3723,6 +3723,9 @@ i40e_aq_set_dcb_parameters(struct i40e_hw *hw, bool dcb_enable,
                (struct i40e_aqc_set_dcb_parameters *)&desc.params.raw;
        i40e_status status;
 
+       if (!(hw->flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE))
+               return I40E_ERR_DEVICE_NOT_SUPPORTED;
+
        i40e_fill_default_direct_cmd_desc(&desc,
                                          i40e_aqc_opc_set_dcb_parameters);
 
index 9c1211a..a6bc784 100644 (file)
@@ -906,6 +906,7 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw,
                ks->base.speed = SPEED_100;
                break;
        default:
+               ks->base.speed = SPEED_UNKNOWN;
                break;
        }
        ks->base.duplex = DUPLEX_FULL;
@@ -1335,6 +1336,7 @@ static int i40e_set_pauseparam(struct net_device *netdev,
        i40e_status status;
        u8 aq_failures;
        int err = 0;
+       u32 is_an;
 
        /* Changing the port's flow control is not supported if this isn't the
         * port's controlling PF
@@ -1347,15 +1349,14 @@ static int i40e_set_pauseparam(struct net_device *netdev,
        if (vsi != pf->vsi[pf->lan_vsi])
                return -EOPNOTSUPP;
 
-       if (pause->autoneg != ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ?
-           AUTONEG_ENABLE : AUTONEG_DISABLE)) {
+       is_an = hw_link_info->an_info & I40E_AQ_AN_COMPLETED;
+       if (pause->autoneg != is_an) {
                netdev_info(netdev, "To change autoneg please use: ethtool -s <dev> autoneg <on|off>\n");
                return -EOPNOTSUPP;
        }
 
        /* If we have link and don't have autoneg */
-       if (!test_bit(__I40E_DOWN, pf->state) &&
-           !(hw_link_info->an_info & I40E_AQ_AN_COMPLETED)) {
+       if (!test_bit(__I40E_DOWN, pf->state) && !is_an) {
                /* Send message that it might not necessarily work*/
                netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n");
        }
@@ -1406,7 +1407,7 @@ static int i40e_set_pauseparam(struct net_device *netdev,
                err = -EAGAIN;
        }
 
-       if (!test_bit(__I40E_DOWN, pf->state)) {
+       if (!test_bit(__I40E_DOWN, pf->state) && is_an) {
                /* Give it a little more time to try to come back */
                msleep(75);
                if (!test_bit(__I40E_DOWN, pf->state))
@@ -4660,14 +4661,15 @@ flags_complete:
                return -EOPNOTSUPP;
 
        /* If the driver detected FW LLDP was disabled on init, this flag could
-        * be set, however we do not support _changing_ the flag if NPAR is
-        * enabled or FW API version < 1.7.  There are situations where older
-        * FW versions/NPAR enabled PFs could disable LLDP, however we _must_
-        * not allow the user to enable/disable LLDP with this flag on
-        * unsupported FW versions.
+        * be set, however we do not support _changing_ the flag:
+        * - on XL710 if NPAR is enabled or FW API version < 1.7
+        * - on X722 with FW API version < 1.6
+        * There are situations where older FW versions/NPAR enabled PFs could
+        * disable LLDP, however we _must_ not allow the user to enable/disable
+        * LLDP with this flag on unsupported FW versions.
         */
        if (changed_flags & I40E_FLAG_DISABLE_FW_LLDP) {
-               if (!(pf->hw_features & I40E_HW_STOPPABLE_FW_LLDP)) {
+               if (!(pf->hw.flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE)) {
                        dev_warn(&pf->pdev->dev,
                                 "Device does not support changing FW LLDP\n");
                        return -EOPNOTSUPP;
index 21c2688..4d40878 100644 (file)
@@ -26,8 +26,8 @@ static const char i40e_driver_string[] =
 #define DRV_KERN "-k"
 
 #define DRV_VERSION_MAJOR 2
-#define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 2
+#define DRV_VERSION_MINOR 7
+#define DRV_VERSION_BUILD 6
 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
             __stringify(DRV_VERSION_MINOR) "." \
             __stringify(DRV_VERSION_BUILD)    DRV_KERN
@@ -338,6 +338,10 @@ static void i40e_tx_timeout(struct net_device *netdev)
                      (pf->tx_timeout_last_recovery + netdev->watchdog_timeo)))
                return;   /* don't do any new action before the next timeout */
 
+       /* don't kick off another recovery if one is already pending */
+       if (test_and_set_bit(__I40E_TIMEOUT_RECOVERY_PENDING, pf->state))
+               return;
+
        if (tx_ring) {
                head = i40e_get_head(tx_ring);
                /* Read interrupt register */
@@ -1413,7 +1417,7 @@ void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f)
        }
 
        vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
-       set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->state);
+       set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->back->state);
 }
 
 /**
@@ -1493,8 +1497,7 @@ int i40e_del_mac_filter(struct i40e_vsi *vsi, const u8 *macaddr)
        bool found = false;
        int bkt;
 
-       WARN(!spin_is_locked(&vsi->mac_filter_hash_lock),
-            "Missing mac_filter_hash_lock\n");
+       lockdep_assert_held(&vsi->mac_filter_hash_lock);
        hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
                if (ether_addr_equal(macaddr, f->macaddr)) {
                        __i40e_del_filter(vsi, f);
@@ -1543,17 +1546,17 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
                netdev_info(netdev, "set new mac address %pM\n", addr->sa_data);
 
        /* Copy the address first, so that we avoid a possible race with
-        * .set_rx_mode(). If we copy after changing the address in the filter
-        * list, we might open ourselves to a narrow race window where
-        * .set_rx_mode could delete our dev_addr filter and prevent traffic
-        * from passing.
+        * .set_rx_mode().
+        * - Remove old address from MAC filter
+        * - Copy new address
+        * - Add new address to MAC filter
         */
-       ether_addr_copy(netdev->dev_addr, addr->sa_data);
-
        spin_lock_bh(&vsi->mac_filter_hash_lock);
        i40e_del_mac_filter(vsi, netdev->dev_addr);
-       i40e_add_mac_filter(vsi, addr->sa_data);
+       ether_addr_copy(netdev->dev_addr, addr->sa_data);
+       i40e_add_mac_filter(vsi, netdev->dev_addr);
        spin_unlock_bh(&vsi->mac_filter_hash_lock);
+
        if (vsi->type == I40E_VSI_MAIN) {
                i40e_status ret;
 
@@ -9632,6 +9635,7 @@ end_core_reset:
        clear_bit(__I40E_RESET_FAILED, pf->state);
 clear_recovery:
        clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state);
+       clear_bit(__I40E_TIMEOUT_RECOVERY_PENDING, pf->state);
 }
 
 /**
@@ -11332,16 +11336,15 @@ static int i40e_sw_init(struct i40e_pf *pf)
                /* IWARP needs one extra vector for CQP just like MISC.*/
                pf->num_iwarp_msix = (int)num_online_cpus() + 1;
        }
-       /* Stopping the FW LLDP engine is only supported on the
-        * XL710 with a FW ver >= 1.7.  Also, stopping FW LLDP
-        * engine is not supported if NPAR is functioning on this
-        * part
+       /* Stopping FW LLDP engine is supported on XL710 and X722
+        * starting from FW versions determined in i40e_init_adminq.
+        * Stopping the FW LLDP engine is not supported on XL710
+        * if NPAR is functioning so unset this hw flag in this case.
         */
        if (pf->hw.mac.type == I40E_MAC_XL710 &&
-           !pf->hw.func_caps.npar_enable &&
-           (pf->hw.aq.api_maj_ver > 1 ||
-            (pf->hw.aq.api_maj_ver == 1 && pf->hw.aq.api_min_ver > 6)))
-               pf->hw_features |= I40E_HW_STOPPABLE_FW_LLDP;
+           pf->hw.func_caps.npar_enable &&
+           (pf->hw.flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE))
+               pf->hw.flags &= ~I40E_HW_FLAG_FW_LLDP_STOPPABLE;
 
 #ifdef CONFIG_PCI_IOV
        if (pf->hw.func_caps.num_vfs && pf->hw.partition_id == 1) {
@@ -11682,6 +11685,7 @@ static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
  * @dev: the netdev being configured
  * @nlh: RTNL message
  * @flags: bridge flags
+ * @extack: netlink extended ack
  *
  * Inserts a new hardware bridge if not already created and
  * enables the bridging mode requested (VEB or VEPA). If the
@@ -11694,7 +11698,8 @@ static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
  **/
 static int i40e_ndo_bridge_setlink(struct net_device *dev,
                                   struct nlmsghdr *nlh,
-                                  u16 flags)
+                                  u16 flags,
+                                  struct netlink_ext_ack *extack)
 {
        struct i40e_netdev_priv *np = netdev_priv(dev);
        struct i40e_vsi *vsi = np->vsi;
@@ -12334,6 +12339,9 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
        ether_addr_copy(netdev->dev_addr, mac_addr);
        ether_addr_copy(netdev->perm_addr, mac_addr);
 
+       /* i40iw_net_event() reads 16 bytes from neigh->primary_key */
+       netdev->neigh_priv_len = sizeof(u32) * 4;
+
        netdev->priv_flags |= IFF_UNICAST_FLT;
        netdev->priv_flags |= IFF_SUPP_NOFCS;
        /* Setup netdev TC information */
@@ -14302,23 +14310,23 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
                switch (hw->bus.speed) {
                case i40e_bus_speed_8000:
-                       strncpy(speed, "8.0", PCI_SPEED_SIZE); break;
+                       strlcpy(speed, "8.0", PCI_SPEED_SIZE); break;
                case i40e_bus_speed_5000:
-                       strncpy(speed, "5.0", PCI_SPEED_SIZE); break;
+                       strlcpy(speed, "5.0", PCI_SPEED_SIZE); break;
                case i40e_bus_speed_2500:
-                       strncpy(speed, "2.5", PCI_SPEED_SIZE); break;
+                       strlcpy(speed, "2.5", PCI_SPEED_SIZE); break;
                default:
                        break;
                }
                switch (hw->bus.width) {
                case i40e_bus_width_pcie_x8:
-                       strncpy(width, "8", PCI_WIDTH_SIZE); break;
+                       strlcpy(width, "8", PCI_WIDTH_SIZE); break;
                case i40e_bus_width_pcie_x4:
-                       strncpy(width, "4", PCI_WIDTH_SIZE); break;
+                       strlcpy(width, "4", PCI_WIDTH_SIZE); break;
                case i40e_bus_width_pcie_x2:
-                       strncpy(width, "2", PCI_WIDTH_SIZE); break;
+                       strlcpy(width, "2", PCI_WIDTH_SIZE); break;
                case i40e_bus_width_pcie_x1:
-                       strncpy(width, "1", PCI_WIDTH_SIZE); break;
+                       strlcpy(width, "1", PCI_WIDTH_SIZE); break;
                default:
                        break;
                }
index 1199f05..5fb4353 100644 (file)
  * i40e_ptp_read - Read the PHC time from the device
  * @pf: Board private structure
  * @ts: timespec structure to hold the current time value
+ * @sts: structure to hold the system time before and after reading the PHC
  *
  * This function reads the PRTTSYN_TIME registers and stores them in a
  * timespec. However, since the registers are 64 bits of nanoseconds, we must
  * convert the result to a timespec before we can return.
  **/
-static void i40e_ptp_read(struct i40e_pf *pf, struct timespec64 *ts)
+static void i40e_ptp_read(struct i40e_pf *pf, struct timespec64 *ts,
+                         struct ptp_system_timestamp *sts)
 {
        struct i40e_hw *hw = &pf->hw;
        u32 hi, lo;
        u64 ns;
 
        /* The timer latches on the lowest register read. */
+       ptp_read_system_prets(sts);
        lo = rd32(hw, I40E_PRTTSYN_TIME_L);
+       ptp_read_system_postts(sts);
        hi = rd32(hw, I40E_PRTTSYN_TIME_H);
 
        ns = (((u64)hi) << 32) | lo;
@@ -146,7 +150,7 @@ static int i40e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
 
        mutex_lock(&pf->tmreg_lock);
 
-       i40e_ptp_read(pf, &now);
+       i40e_ptp_read(pf, &now, NULL);
        timespec64_add_ns(&now, delta);
        i40e_ptp_write(pf, (const struct timespec64 *)&now);
 
@@ -156,19 +160,21 @@ static int i40e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
 }
 
 /**
- * i40e_ptp_gettime - Get the time of the PHC
+ * i40e_ptp_gettimex - Get the time of the PHC
  * @ptp: The PTP clock structure
  * @ts: timespec structure to hold the current time value
+ * @sts: structure to hold the system time before and after reading the PHC
  *
  * Read the device clock and return the correct value on ns, after converting it
  * into a timespec struct.
  **/
-static int i40e_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+static int i40e_ptp_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts,
+                            struct ptp_system_timestamp *sts)
 {
        struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps);
 
        mutex_lock(&pf->tmreg_lock);
-       i40e_ptp_read(pf, ts);
+       i40e_ptp_read(pf, ts, sts);
        mutex_unlock(&pf->tmreg_lock);
 
        return 0;
@@ -694,7 +700,7 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf)
        if (!IS_ERR_OR_NULL(pf->ptp_clock))
                return 0;
 
-       strncpy(pf->ptp_caps.name, i40e_driver_name,
+       strlcpy(pf->ptp_caps.name, i40e_driver_name,
                sizeof(pf->ptp_caps.name) - 1);
        pf->ptp_caps.owner = THIS_MODULE;
        pf->ptp_caps.max_adj = 999999999;
@@ -702,7 +708,7 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf)
        pf->ptp_caps.pps = 0;
        pf->ptp_caps.adjfreq = i40e_ptp_adjfreq;
        pf->ptp_caps.adjtime = i40e_ptp_adjtime;
-       pf->ptp_caps.gettime64 = i40e_ptp_gettime;
+       pf->ptp_caps.gettimex64 = i40e_ptp_gettimex;
        pf->ptp_caps.settime64 = i40e_ptp_settime;
        pf->ptp_caps.enable = i40e_ptp_feature_enable;
 
index 1384a5a..a7e14e9 100644 (file)
@@ -1558,24 +1558,6 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring,
        return true;
 }
 
-/**
- * i40e_receive_skb - Send a completed packet up the stack
- * @rx_ring:  rx ring in play
- * @skb: packet to send up
- * @vlan_tag: vlan tag for packet
- **/
-void i40e_receive_skb(struct i40e_ring *rx_ring,
-                     struct sk_buff *skb, u16 vlan_tag)
-{
-       struct i40e_q_vector *q_vector = rx_ring->q_vector;
-
-       if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
-           (vlan_tag & VLAN_VID_MASK))
-               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
-
-       napi_gro_receive(&q_vector->napi, skb);
-}
-
 /**
  * i40e_alloc_rx_buffers - Replace used receive buffers
  * @rx_ring: ring to place buffers on
@@ -1793,8 +1775,7 @@ static inline void i40e_rx_hash(struct i40e_ring *ring,
  * other fields within the skb.
  **/
 void i40e_process_skb_fields(struct i40e_ring *rx_ring,
-                            union i40e_rx_desc *rx_desc, struct sk_buff *skb,
-                            u8 rx_ptype)
+                            union i40e_rx_desc *rx_desc, struct sk_buff *skb)
 {
        u64 qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len);
        u32 rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >>
@@ -1802,6 +1783,8 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring,
        u32 tsynvalid = rx_status & I40E_RXD_QW1_STATUS_TSYNVALID_MASK;
        u32 tsyn = (rx_status & I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >>
                   I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT;
+       u8 rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >>
+                     I40E_RXD_QW1_PTYPE_SHIFT;
 
        if (unlikely(tsynvalid))
                i40e_ptp_rx_hwtstamp(rx_ring->vsi->back, skb, tsyn);
@@ -1812,6 +1795,13 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring,
 
        skb_record_rx_queue(skb, rx_ring->queue_index);
 
+       if (qword & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT)) {
+               u16 vlan_tag = rx_desc->wb.qword0.lo_dword.l2tag1;
+
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+                                      le16_to_cpu(vlan_tag));
+       }
+
        /* modifies the skb - consumes the enet header */
        skb->protocol = eth_type_trans(skb, rx_ring->netdev);
 }
@@ -2350,8 +2340,6 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
                struct i40e_rx_buffer *rx_buffer;
                union i40e_rx_desc *rx_desc;
                unsigned int size;
-               u16 vlan_tag;
-               u8 rx_ptype;
                u64 qword;
 
                /* return some buffers to hardware, one at a time is too slow */
@@ -2444,18 +2432,11 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
                /* probably a little skewed due to removing CRC */
                total_rx_bytes += skb->len;
 
-               qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len);
-               rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >>
-                          I40E_RXD_QW1_PTYPE_SHIFT;
-
                /* populate checksum, VLAN, and protocol */
-               i40e_process_skb_fields(rx_ring, rx_desc, skb, rx_ptype);
-
-               vlan_tag = (qword & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT)) ?
-                          le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) : 0;
+               i40e_process_skb_fields(rx_ring, rx_desc, skb);
 
                i40e_trace(clean_rx_irq_rx, rx_ring, rx_desc, skb);
-               i40e_receive_skb(rx_ring, skb, vlan_tag);
+               napi_gro_receive(&rx_ring->q_vector->napi, skb);
                skb = NULL;
 
                /* update budget accounting */
@@ -2667,10 +2648,11 @@ tx_only:
        if (vsi->back->flags & I40E_TXR_FLAGS_WB_ON_ITR)
                q_vector->arm_wb_state = false;
 
-       /* Work is done so exit the polling mode and re-enable the interrupt */
-       napi_complete_done(napi, work_done);
-
-       i40e_update_enable_itr(vsi, q_vector);
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               i40e_update_enable_itr(vsi, q_vector);
 
        return min(work_done, budget - 1);
 }
@@ -3528,6 +3510,7 @@ static int i40e_xmit_xdp_ring(struct xdp_frame *xdpf,
        u16 i = xdp_ring->next_to_use;
        struct i40e_tx_buffer *tx_bi;
        struct i40e_tx_desc *tx_desc;
+       void *data = xdpf->data;
        u32 size = xdpf->len;
        dma_addr_t dma;
 
@@ -3535,8 +3518,7 @@ static int i40e_xmit_xdp_ring(struct xdp_frame *xdpf,
                xdp_ring->tx_stats.tx_busy++;
                return I40E_XDP_CONSUMED;
        }
-
-       dma = dma_map_single(xdp_ring->dev, xdpf->data, size, DMA_TO_DEVICE);
+       dma = dma_map_single(xdp_ring->dev, data, size, DMA_TO_DEVICE);
        if (dma_mapping_error(xdp_ring->dev, dma))
                return I40E_XDP_CONSUMED;
 
index 09809df..8af0e99 100644 (file)
@@ -12,10 +12,7 @@ struct i40e_rx_buffer *i40e_clean_programming_status(
        union i40e_rx_desc *rx_desc,
        u64 qw);
 void i40e_process_skb_fields(struct i40e_ring *rx_ring,
-                            union i40e_rx_desc *rx_desc, struct sk_buff *skb,
-                            u8 rx_ptype);
-void i40e_receive_skb(struct i40e_ring *rx_ring,
-                     struct sk_buff *skb, u16 vlan_tag);
+                            union i40e_rx_desc *rx_desc, struct sk_buff *skb);
 void i40e_xdp_ring_update_tail(struct i40e_ring *xdp_ring);
 void i40e_update_rx_stats(struct i40e_ring *rx_ring,
                          unsigned int total_rx_bytes,
index 7df969c..2781ab9 100644 (file)
@@ -615,6 +615,7 @@ struct i40e_hw {
 #define I40E_HW_FLAG_802_1AD_CAPABLE        BIT_ULL(1)
 #define I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE  BIT_ULL(2)
 #define I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK BIT_ULL(3)
+#define I40E_HW_FLAG_FW_LLDP_STOPPABLE      BIT_ULL(4)
        u64 flags;
 
        /* Used in set switch config AQ command */
index ac5698e..2ac23eb 100644 (file)
@@ -1112,7 +1112,8 @@ static i40e_status i40e_config_vf_promiscuous_mode(struct i40e_vf *vf,
        if (!i40e_vc_isvalid_vsi_id(vf, vsi_id) || !vsi)
                return I40E_ERR_PARAM;
 
-       if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) {
+       if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) &&
+           (allmulti || alluni)) {
                dev_err(&pf->pdev->dev,
                        "Unprivileged VF %d is attempting to configure promiscuous mode\n",
                        vf->vf_id);
@@ -1675,13 +1676,20 @@ err_out:
 int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
 {
        struct i40e_pf *pf = pci_get_drvdata(pdev);
+       int ret = 0;
+
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
 
        if (num_vfs) {
                if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
                        pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
                        i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG);
                }
-               return i40e_pci_sriov_enable(pdev, num_vfs);
+               ret = i40e_pci_sriov_enable(pdev, num_vfs);
+               goto sriov_configure_out;
        }
 
        if (!pci_vfs_assigned(pf->pdev)) {
@@ -1690,9 +1698,12 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
                i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG);
        } else {
                dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto sriov_configure_out;
        }
-       return 0;
+sriov_configure_out:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
+       return ret;
 }
 
 /***********************virtual channel routines******************/
@@ -3893,6 +3904,11 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
                goto error_param;
        }
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        if (is_multicast_ether_addr(mac)) {
                dev_err(&pf->pdev->dev,
                        "Invalid Ethernet address %pM for VF %d\n", mac, vf_id);
@@ -3941,6 +3957,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
        dev_info(&pf->pdev->dev, "Bring down and up the VF interface to make this change effective.\n");
 
 error_param:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -3992,6 +4009,11 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id,
        struct i40e_vf *vf;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        ret = i40e_validate_vf(pf, vf_id);
        if (ret)
@@ -4107,6 +4129,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id,
        ret = 0;
 
 error_pvid:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -4128,6 +4151,11 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
        struct i40e_vf *vf;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        ret = i40e_validate_vf(pf, vf_id);
        if (ret)
@@ -4154,6 +4182,7 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
 
        vf->tx_rate = max_tx_rate;
 error:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -4174,6 +4203,11 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
        struct i40e_vf *vf;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        ret = i40e_validate_vf(pf, vf_id);
        if (ret)
@@ -4209,6 +4243,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
        ret = 0;
 
 error_param:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -4230,6 +4265,11 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
        int abs_vf_id;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        if (vf_id >= pf->num_alloc_vfs) {
                dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
@@ -4273,6 +4313,7 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
                               0, (u8 *)&pfe, sizeof(pfe), NULL);
 
 error_out:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -4294,6 +4335,11 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
        struct i40e_vf *vf;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        if (vf_id >= pf->num_alloc_vfs) {
                dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
@@ -4327,6 +4373,7 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
                ret = -EIO;
        }
 out:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -4345,15 +4392,22 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
        struct i40e_vf *vf;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        if (vf_id >= pf->num_alloc_vfs) {
                dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        if (pf->flags & I40E_FLAG_MFP_ENABLED) {
                dev_err(&pf->pdev->dev, "Trusted VF not supported in MFP mode.\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        vf = &pf->vf[vf_id];
@@ -4376,5 +4430,6 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
        }
 
 out:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
index bf67d62..f962102 100644 (file)
@@ -13,9 +13,9 @@
 #define I40E_DEFAULT_NUM_MDD_EVENTS_ALLOWED    3
 #define I40E_DEFAULT_NUM_INVALID_MSGS_ALLOWED  10
 
-#define I40E_VLAN_PRIORITY_SHIFT       12
+#define I40E_VLAN_PRIORITY_SHIFT       13
 #define I40E_VLAN_MASK                 0xFFF
-#define I40E_PRIORITY_MASK             0x7000
+#define I40E_PRIORITY_MASK             0xE000
 
 /* Various queue ctrls */
 enum i40e_queue_ctrl {
index add1e45..870cf65 100644 (file)
@@ -33,7 +33,7 @@ static int i40e_alloc_xsk_umems(struct i40e_vsi *vsi)
 }
 
 /**
- * i40e_add_xsk_umem - Store an UMEM for a certain ring/qid
+ * i40e_add_xsk_umem - Store a UMEM for a certain ring/qid
  * @vsi: Current VSI
  * @umem: UMEM to store
  * @qid: Ring/qid to associate with the UMEM
@@ -56,7 +56,7 @@ static int i40e_add_xsk_umem(struct i40e_vsi *vsi, struct xdp_umem *umem,
 }
 
 /**
- * i40e_remove_xsk_umem - Remove an UMEM for a certain ring/qid
+ * i40e_remove_xsk_umem - Remove a UMEM for a certain ring/qid
  * @vsi: Current VSI
  * @qid: Ring/qid associated with the UMEM
  **/
@@ -130,7 +130,7 @@ static void i40e_xsk_umem_dma_unmap(struct i40e_vsi *vsi, struct xdp_umem *umem)
 }
 
 /**
- * i40e_xsk_umem_enable - Enable/associate an UMEM to a certain ring/qid
+ * i40e_xsk_umem_enable - Enable/associate a UMEM to a certain ring/qid
  * @vsi: Current VSI
  * @umem: UMEM
  * @qid: Rx ring to associate UMEM to
@@ -189,7 +189,7 @@ static int i40e_xsk_umem_enable(struct i40e_vsi *vsi, struct xdp_umem *umem,
 }
 
 /**
- * i40e_xsk_umem_disable - Diassociate an UMEM from a certain ring/qid
+ * i40e_xsk_umem_disable - Disassociate a UMEM from a certain ring/qid
  * @vsi: Current VSI
  * @qid: Rx ring to associate UMEM to
  *
@@ -255,12 +255,12 @@ int i40e_xsk_umem_query(struct i40e_vsi *vsi, struct xdp_umem **umem,
 }
 
 /**
- * i40e_xsk_umem_query - Queries a certain ring/qid for its UMEM
+ * 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
  * @qid: Rx ring to (dis)associate UMEM (from)to
  *
- * This function enables or disables an UMEM to a certain ring.
+ * This function enables or disables a UMEM to a certain ring.
  *
  * Returns 0 on success, <0 on failure
  **/
@@ -276,7 +276,7 @@ int i40e_xsk_umem_setup(struct i40e_vsi *vsi, struct xdp_umem *umem,
  * @rx_ring: Rx ring
  * @xdp: xdp_buff used as input to the XDP program
  *
- * This function enables or disables an UMEM to a certain ring.
+ * This function enables or disables a UMEM to a certain ring.
  *
  * Returns any of I40E_XDP_{PASS, CONSUMED, TX, REDIR}
  **/
@@ -634,8 +634,6 @@ int i40e_clean_rx_irq_zc(struct i40e_ring *rx_ring, int budget)
                struct i40e_rx_buffer *bi;
                union i40e_rx_desc *rx_desc;
                unsigned int size;
-               u16 vlan_tag;
-               u8 rx_ptype;
                u64 qword;
 
                if (cleaned_count >= I40E_RX_BUFFER_WRITE) {
@@ -713,14 +711,8 @@ int i40e_clean_rx_irq_zc(struct i40e_ring *rx_ring, int budget)
                total_rx_bytes += skb->len;
                total_rx_packets++;
 
-               qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len);
-               rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >>
-                          I40E_RXD_QW1_PTYPE_SHIFT;
-               i40e_process_skb_fields(rx_ring, rx_desc, skb, rx_ptype);
-
-               vlan_tag = (qword & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT)) ?
-                          le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) : 0;
-               i40e_receive_skb(rx_ring, skb, vlan_tag);
+               i40e_process_skb_fields(rx_ring, rx_desc, skb);
+               napi_gro_receive(&rx_ring->q_vector->napi, skb);
        }
 
        i40e_finalize_xdp_rx(rx_ring, xdp_xmit);
index 3b1dc77..9b4d7ce 100644 (file)
@@ -1761,10 +1761,11 @@ tx_only:
        if (vsi->back->flags & IAVF_TXR_FLAGS_WB_ON_ITR)
                q_vector->arm_wb_state = false;
 
-       /* Work is done so exit the polling mode and re-enable the interrupt */
-       napi_complete_done(napi, work_done);
-
-       iavf_update_enable_itr(vsi, q_vector);
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               iavf_update_enable_itr(vsi, q_vector);
 
        return min(work_done, budget - 1);
 }
index b854837..a385575 100644 (file)
@@ -52,7 +52,6 @@ extern const char ice_drv_ver[];
 #define ICE_MBXQ_LEN           64
 #define ICE_MIN_MSIX           2
 #define ICE_NO_VSI             0xffff
-#define ICE_MAX_VSI_ALLOC      130
 #define ICE_MAX_TXQS           2048
 #define ICE_MAX_RXQS           2048
 #define ICE_VSI_MAP_CONTIG     0
@@ -97,14 +96,14 @@ extern const char ice_drv_ver[];
 #define ice_for_each_vsi(pf, i) \
        for ((i) = 0; (i) < (pf)->num_alloc_vsi; (i)++)
 
-/* Macros for each tx/rx ring in a VSI */
+/* Macros for each Tx/Rx ring in a VSI */
 #define ice_for_each_txq(vsi, i) \
        for ((i) = 0; (i) < (vsi)->num_txq; (i)++)
 
 #define ice_for_each_rxq(vsi, i) \
        for ((i) = 0; (i) < (vsi)->num_rxq; (i)++)
 
-/* Macros for each allocated tx/rx ring whether used or not in a VSI */
+/* Macros for each allocated Tx/Rx ring whether used or not in a VSI */
 #define ice_for_each_alloc_txq(vsi, i) \
        for ((i) = 0; (i) < (vsi)->alloc_txq; (i)++)
 
@@ -113,7 +112,9 @@ extern const char ice_drv_ver[];
 
 struct ice_tc_info {
        u16 qoffset;
-       u16 qcount;
+       u16 qcount_tx;
+       u16 qcount_rx;
+       u8 netdev_tc;
 };
 
 struct ice_tc_cfg {
@@ -149,10 +150,10 @@ enum ice_state {
        __ICE_RESET_FAILED,             /* set by reset/rebuild */
        /* When checking for the PF to be in a nominal operating state, the
         * bits that are grouped at the beginning of the list need to be
-        * checked.  Bits occurring before __ICE_STATE_NOMINAL_CHECK_BITS will
-        * be checked.  If you need to add a bit into consideration for nominal
+        * checked. Bits occurring before __ICE_STATE_NOMINAL_CHECK_BITS will
+        * be checked. If you need to add a bit into consideration for nominal
         * operating state, it must be added before
-        * __ICE_STATE_NOMINAL_CHECK_BITS.  Do not move this entry's position
+        * __ICE_STATE_NOMINAL_CHECK_BITS. Do not move this entry's position
         * without appropriate consideration.
         */
        __ICE_STATE_NOMINAL_CHECK_BITS,
@@ -182,8 +183,8 @@ struct ice_vsi {
        struct ice_sw *vsw;              /* switch this VSI is on */
        struct ice_pf *back;             /* back pointer to PF */
        struct ice_port_info *port_info; /* back pointer to port_info */
-       struct ice_ring **rx_rings;      /* rx ring array */
-       struct ice_ring **tx_rings;      /* tx ring array */
+       struct ice_ring **rx_rings;      /* Rx ring array */
+       struct ice_ring **tx_rings;      /* Tx ring array */
        struct ice_q_vector **q_vectors; /* q_vector array */
 
        irqreturn_t (*irq_handler)(int irq, void *data);
@@ -200,8 +201,8 @@ struct ice_vsi {
        int sw_base_vector;             /* Irq base for OS reserved vectors */
        int hw_base_vector;             /* HW (absolute) index of a vector */
        enum ice_vsi_type type;
-       u16 vsi_num;                     /* HW (absolute) index of this VSI */
-       u16 idx;                         /* software index in pf->vsi[] */
+       u16 vsi_num;                    /* HW (absolute) index of this VSI */
+       u16 idx;                        /* software index in pf->vsi[] */
 
        /* Interrupt thresholds */
        u16 work_lmt;
@@ -254,8 +255,8 @@ struct ice_q_vector {
        struct ice_ring_container tx;
        struct irq_affinity_notify affinity_notify;
        u16 v_idx;                      /* index in the vsi->q_vector array. */
-       u8 num_ring_tx;                 /* total number of tx rings in vector */
-       u8 num_ring_rx;                 /* total number of rx rings in vector */
+       u8 num_ring_tx;                 /* total number of Tx rings in vector */
+       u8 num_ring_rx;                 /* total number of Rx rings in vector */
        char name[ICE_INT_NAME_STR_LEN];
        /* in usecs, need to use ice_intrl_to_usecs_reg() before writing this
         * value to the device
@@ -307,10 +308,10 @@ struct ice_pf {
        u32 hw_oicr_idx;        /* Other interrupt cause vector HW index */
        u32 num_avail_hw_msix;  /* remaining HW MSIX vectors left unclaimed */
        u32 num_lan_msix;       /* Total MSIX vectors for base driver */
-       u16 num_lan_tx;         /* num lan tx queues setup */
-       u16 num_lan_rx;         /* num lan rx queues setup */
-       u16 q_left_tx;          /* remaining num tx queues left unclaimed */
-       u16 q_left_rx;          /* remaining num rx queues left unclaimed */
+       u16 num_lan_tx;         /* num lan Tx queues setup */
+       u16 num_lan_rx;         /* num lan Rx queues setup */
+       u16 q_left_tx;          /* remaining num Tx queues left unclaimed */
+       u16 q_left_rx;          /* remaining num Rx queues left unclaimed */
        u16 next_vsi;           /* Next free slot in pf->vsi[] - 0-based! */
        u16 num_alloc_vsi;
        u16 corer_count;        /* Core reset count */
index 6653555..fcdcd80 100644 (file)
@@ -5,7 +5,7 @@
 #define _ICE_ADMINQ_CMD_H_
 
 /* This header file defines the Admin Queue commands, error codes and
- * descriptor format.  It is shared between Firmware and Software.
+ * descriptor format. It is shared between Firmware and Software.
  */
 
 #define ICE_MAX_VSI                    768
@@ -87,6 +87,7 @@ struct ice_aqc_list_caps {
 /* Device/Function buffer entry, repeated per reported capability */
 struct ice_aqc_list_caps_elem {
        __le16 cap;
+#define ICE_AQC_CAPS_VALID_FUNCTIONS                   0x0005
 #define ICE_AQC_CAPS_SRIOV                             0x0012
 #define ICE_AQC_CAPS_VF                                        0x0013
 #define ICE_AQC_CAPS_VSI                               0x0017
@@ -462,7 +463,7 @@ struct ice_aqc_sw_rules {
 };
 
 /* Add/Update/Get/Remove lookup Rx/Tx command/response entry
- * This structures describes the lookup rules and associated actions.  "index"
+ * This structures describes the lookup rules and associated actions. "index"
  * is returned as part of a response to a successful Add command, and can be
  * used to identify the rule for Update/Get/Remove commands.
  */
@@ -1065,10 +1066,10 @@ struct ice_aqc_nvm {
 #define ICE_AQC_NVM_LAST_CMD           BIT(0)
 #define ICE_AQC_NVM_PCIR_REQ           BIT(0)  /* Used by NVM Update reply */
 #define ICE_AQC_NVM_PRESERVATION_S     1
-#define ICE_AQC_NVM_PRESERVATION_M     (3 << CSR_AQ_NVM_PRESERVATION_S)
-#define ICE_AQC_NVM_NO_PRESERVATION    (0 << CSR_AQ_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_PRESERVATION_M     (3 << ICE_AQC_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_NO_PRESERVATION    (0 << ICE_AQC_NVM_PRESERVATION_S)
 #define ICE_AQC_NVM_PRESERVE_ALL       BIT(1)
-#define ICE_AQC_NVM_PRESERVE_SELECTED  (3 << CSR_AQ_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_PRESERVE_SELECTED  (3 << ICE_AQC_NVM_PRESERVATION_S)
 #define ICE_AQC_NVM_FLASH_ONLY         BIT(7)
        __le16 module_typeid;
        __le16 length;
@@ -1110,7 +1111,7 @@ struct ice_aqc_get_set_rss_keys {
 };
 
 /* Get/Set RSS LUT (indirect 0x0B05/0x0B03) */
-struct  ice_aqc_get_set_rss_lut {
+struct ice_aqc_get_set_rss_lut {
 #define ICE_AQC_GSET_RSS_LUT_VSI_VALID BIT(15)
 #define ICE_AQC_GSET_RSS_LUT_VSI_ID_S  0
 #define ICE_AQC_GSET_RSS_LUT_VSI_ID_M  (0x1FF << ICE_AQC_GSET_RSS_LUT_VSI_ID_S)
@@ -1314,10 +1315,10 @@ struct ice_aqc_get_clear_fw_log {
  * @params: command-specific parameters
  *
  * Descriptor format for commands the driver posts on the Admin Transmit Queue
- * (ATQ).  The firmware writes back onto the command descriptor and returns
- * the result of the command.  Asynchronous events that are not an immediate
+ * (ATQ). The firmware writes back onto the command descriptor and returns
+ * the result of the command. Asynchronous events that are not an immediate
  * result of the command are written to the Admin Receive Queue (ARQ) using
- * the same descriptor format.  Descriptors are in little-endian notation with
+ * the same descriptor format. Descriptors are in little-endian notation with
  * 32-bit words.
  */
 struct ice_aq_desc {
@@ -1379,10 +1380,10 @@ struct ice_aq_desc {
 
 /* error codes */
 enum ice_aq_err {
-       ICE_AQ_RC_OK            = 0,  /* success */
+       ICE_AQ_RC_OK            = 0,  /* Success */
        ICE_AQ_RC_ENOMEM        = 9,  /* Out of memory */
        ICE_AQ_RC_EBUSY         = 12, /* Device or resource busy */
-       ICE_AQ_RC_EEXIST        = 13, /* object already exists */
+       ICE_AQ_RC_EEXIST        = 13, /* Object already exists */
        ICE_AQ_RC_ENOSPC        = 16, /* No space left or allocation failure */
 };
 
index 554fd70..4c1d35d 100644 (file)
@@ -405,9 +405,7 @@ static enum ice_status ice_init_fltr_mgmt_struct(struct ice_hw *hw)
 
        INIT_LIST_HEAD(&sw->vsi_list_map_head);
 
-       ice_init_def_sw_recp(hw);
-
-       return 0;
+       return ice_init_def_sw_recp(hw);
 }
 
 /**
@@ -715,7 +713,7 @@ enum ice_status ice_init_hw(struct ice_hw *hw)
 
        hw->evb_veb = true;
 
-       /* Query the allocated resources for tx scheduler */
+       /* Query the allocated resources for Tx scheduler */
        status = ice_sched_query_res_alloc(hw);
        if (status) {
                ice_debug(hw, ICE_DBG_SCHED,
@@ -958,7 +956,7 @@ enum ice_status ice_reset(struct ice_hw *hw, enum ice_reset_req req)
  * ice_copy_rxq_ctx_to_hw
  * @hw: pointer to the hardware structure
  * @ice_rxq_ctx: pointer to the rxq context
- * @rxq_index: the index of the rx queue
+ * @rxq_index: the index of the Rx queue
  *
  * Copies rxq context from dense structure to hw register space
  */
@@ -1014,7 +1012,7 @@ static const struct ice_ctx_ele ice_rlan_ctx_info[] = {
  * ice_write_rxq_ctx
  * @hw: pointer to the hardware structure
  * @rlan_ctx: pointer to the rxq context
- * @rxq_index: the index of the rx queue
+ * @rxq_index: the index of the Rx queue
  *
  * Converts rxq context from sparse to dense structure and then writes
  * it to hw register space
@@ -1386,6 +1384,27 @@ void ice_release_res(struct ice_hw *hw, enum ice_aq_res_ids res)
        }
 }
 
+/**
+ * ice_get_guar_num_vsi - determine number of guar VSI for a PF
+ * @hw: pointer to the hw structure
+ *
+ * Determine the number of valid functions by going through the bitmap returned
+ * from parsing capabilities and use this to calculate the number of VSI per PF.
+ */
+static u32 ice_get_guar_num_vsi(struct ice_hw *hw)
+{
+       u8 funcs;
+
+#define ICE_CAPS_VALID_FUNCS_M 0xFF
+       funcs = hweight8(hw->dev_caps.common_cap.valid_functions &
+                        ICE_CAPS_VALID_FUNCS_M);
+
+       if (!funcs)
+               return 0;
+
+       return ICE_MAX_VSI / funcs;
+}
+
 /**
  * ice_parse_caps - parse function/device capabilities
  * @hw: pointer to the hw struct
@@ -1428,6 +1447,12 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
                u16 cap = le16_to_cpu(cap_resp->cap);
 
                switch (cap) {
+               case ICE_AQC_CAPS_VALID_FUNCTIONS:
+                       caps->valid_functions = number;
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: Valid Functions = %d\n",
+                                 caps->valid_functions);
+                       break;
                case ICE_AQC_CAPS_SRIOV:
                        caps->sr_iov_1_1 = (number == 1);
                        ice_debug(hw, ICE_DBG_INIT,
@@ -1457,10 +1482,10 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
                                          "HW caps: Dev.VSI cnt = %d\n",
                                          dev_p->num_vsi_allocd_to_host);
                        } else if (func_p) {
-                               func_p->guaranteed_num_vsi = number;
+                               func_p->guar_num_vsi = ice_get_guar_num_vsi(hw);
                                ice_debug(hw, ICE_DBG_INIT,
                                          "HW caps: Func.VSI cnt = %d\n",
-                                         func_p->guaranteed_num_vsi);
+                                         number);
                        }
                        break;
                case ICE_AQC_CAPS_RSS:
@@ -1688,8 +1713,7 @@ void ice_clear_pxe_mode(struct ice_hw *hw)
  * If no bit gets set, ICE_LINK_SPEED_UNKNOWN will be returned
  * If more than one bit gets set, ICE_LINK_SPEED_UNKNOWN will be returned
  */
-static u16
-ice_get_link_speed_based_on_phy_type(u64 phy_type_low)
+static u16 ice_get_link_speed_based_on_phy_type(u64 phy_type_low)
 {
        u16 speed_phy_type_low = ICE_AQ_LINK_SPEED_UNKNOWN;
 
index 84c9672..2bf5e11 100644 (file)
@@ -3,6 +3,26 @@
 
 #include "ice_common.h"
 
+#define ICE_CQ_INIT_REGS(qinfo, prefix)                                \
+do {                                                           \
+       (qinfo)->sq.head = prefix##_ATQH;                       \
+       (qinfo)->sq.tail = prefix##_ATQT;                       \
+       (qinfo)->sq.len = prefix##_ATQLEN;                      \
+       (qinfo)->sq.bah = prefix##_ATQBAH;                      \
+       (qinfo)->sq.bal = prefix##_ATQBAL;                      \
+       (qinfo)->sq.len_mask = prefix##_ATQLEN_ATQLEN_M;        \
+       (qinfo)->sq.len_ena_mask = prefix##_ATQLEN_ATQENABLE_M; \
+       (qinfo)->sq.head_mask = prefix##_ATQH_ATQH_M;           \
+       (qinfo)->rq.head = prefix##_ARQH;                       \
+       (qinfo)->rq.tail = prefix##_ARQT;                       \
+       (qinfo)->rq.len = prefix##_ARQLEN;                      \
+       (qinfo)->rq.bah = prefix##_ARQBAH;                      \
+       (qinfo)->rq.bal = prefix##_ARQBAL;                      \
+       (qinfo)->rq.len_mask = prefix##_ARQLEN_ARQLEN_M;        \
+       (qinfo)->rq.len_ena_mask = prefix##_ARQLEN_ARQENABLE_M; \
+       (qinfo)->rq.head_mask = prefix##_ARQH_ARQH_M;           \
+} while (0)
+
 /**
  * ice_adminq_init_regs - Initialize AdminQ registers
  * @hw: pointer to the hardware structure
@@ -13,23 +33,7 @@ static void ice_adminq_init_regs(struct ice_hw *hw)
 {
        struct ice_ctl_q_info *cq = &hw->adminq;
 
-       cq->sq.head = PF_FW_ATQH;
-       cq->sq.tail = PF_FW_ATQT;
-       cq->sq.len = PF_FW_ATQLEN;
-       cq->sq.bah = PF_FW_ATQBAH;
-       cq->sq.bal = PF_FW_ATQBAL;
-       cq->sq.len_mask = PF_FW_ATQLEN_ATQLEN_M;
-       cq->sq.len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M;
-       cq->sq.head_mask = PF_FW_ATQH_ATQH_M;
-
-       cq->rq.head = PF_FW_ARQH;
-       cq->rq.tail = PF_FW_ARQT;
-       cq->rq.len = PF_FW_ARQLEN;
-       cq->rq.bah = PF_FW_ARQBAH;
-       cq->rq.bal = PF_FW_ARQBAL;
-       cq->rq.len_mask = PF_FW_ARQLEN_ARQLEN_M;
-       cq->rq.len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M;
-       cq->rq.head_mask = PF_FW_ARQH_ARQH_M;
+       ICE_CQ_INIT_REGS(cq, PF_FW);
 }
 
 /**
@@ -42,24 +46,7 @@ static void ice_mailbox_init_regs(struct ice_hw *hw)
 {
        struct ice_ctl_q_info *cq = &hw->mailboxq;
 
-       /* set head and tail registers in our local struct */
-       cq->sq.head = PF_MBX_ATQH;
-       cq->sq.tail = PF_MBX_ATQT;
-       cq->sq.len = PF_MBX_ATQLEN;
-       cq->sq.bah = PF_MBX_ATQBAH;
-       cq->sq.bal = PF_MBX_ATQBAL;
-       cq->sq.len_mask = PF_MBX_ATQLEN_ATQLEN_M;
-       cq->sq.len_ena_mask = PF_MBX_ATQLEN_ATQENABLE_M;
-       cq->sq.head_mask = PF_MBX_ATQH_ATQH_M;
-
-       cq->rq.head = PF_MBX_ARQH;
-       cq->rq.tail = PF_MBX_ARQT;
-       cq->rq.len = PF_MBX_ARQLEN;
-       cq->rq.bah = PF_MBX_ARQBAH;
-       cq->rq.bal = PF_MBX_ARQBAL;
-       cq->rq.len_mask = PF_MBX_ARQLEN_ARQLEN_M;
-       cq->rq.len_ena_mask = PF_MBX_ARQLEN_ARQENABLE_M;
-       cq->rq.head_mask = PF_MBX_ARQH_ARQH_M;
+       ICE_CQ_INIT_REGS(cq, PF_MBX);
 }
 
 /**
@@ -131,37 +118,20 @@ ice_alloc_ctrlq_rq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
 }
 
 /**
- * ice_free_ctrlq_sq_ring - Free Control Transmit Queue (ATQ) rings
+ * ice_free_cq_ring - Free control queue ring
  * @hw: pointer to the hardware structure
- * @cq: pointer to the specific Control queue
+ * @ring: pointer to the specific control queue ring
  *
- * This assumes the posted send buffers have already been cleaned
+ * This assumes the posted buffers have already been cleaned
  * and de-allocated
  */
-static void ice_free_ctrlq_sq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+static void ice_free_cq_ring(struct ice_hw *hw, struct ice_ctl_q_ring *ring)
 {
-       dmam_free_coherent(ice_hw_to_dev(hw), cq->sq.desc_buf.size,
-                          cq->sq.desc_buf.va, cq->sq.desc_buf.pa);
-       cq->sq.desc_buf.va = NULL;
-       cq->sq.desc_buf.pa = 0;
-       cq->sq.desc_buf.size = 0;
-}
-
-/**
- * ice_free_ctrlq_rq_ring - Free Control Receive Queue (ARQ) rings
- * @hw: pointer to the hardware structure
- * @cq: pointer to the specific Control queue
- *
- * This assumes the posted receive buffers have already been cleaned
- * and de-allocated
- */
-static void ice_free_ctrlq_rq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
-{
-       dmam_free_coherent(ice_hw_to_dev(hw), cq->rq.desc_buf.size,
-                          cq->rq.desc_buf.va, cq->rq.desc_buf.pa);
-       cq->rq.desc_buf.va = NULL;
-       cq->rq.desc_buf.pa = 0;
-       cq->rq.desc_buf.size = 0;
+       dmam_free_coherent(ice_hw_to_dev(hw), ring->desc_buf.size,
+                          ring->desc_buf.va, ring->desc_buf.pa);
+       ring->desc_buf.va = NULL;
+       ring->desc_buf.pa = 0;
+       ring->desc_buf.size = 0;
 }
 
 /**
@@ -280,54 +250,23 @@ unwind_alloc_sq_bufs:
        return ICE_ERR_NO_MEMORY;
 }
 
-/**
- * ice_free_rq_bufs - Free ARQ buffer info elements
- * @hw: pointer to the hardware structure
- * @cq: pointer to the specific Control queue
- */
-static void ice_free_rq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
-{
-       int i;
-
-       /* free descriptors */
-       for (i = 0; i < cq->num_rq_entries; i++) {
-               dmam_free_coherent(ice_hw_to_dev(hw), cq->rq.r.rq_bi[i].size,
-                                  cq->rq.r.rq_bi[i].va, cq->rq.r.rq_bi[i].pa);
-               cq->rq.r.rq_bi[i].va = NULL;
-               cq->rq.r.rq_bi[i].pa = 0;
-               cq->rq.r.rq_bi[i].size = 0;
-       }
-
-       /* free the dma header */
-       devm_kfree(ice_hw_to_dev(hw), cq->rq.dma_head);
-}
-
-/**
- * ice_free_sq_bufs - Free ATQ buffer info elements
- * @hw: pointer to the hardware structure
- * @cq: pointer to the specific Control queue
- */
-static void ice_free_sq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+static enum ice_status
+ice_cfg_cq_regs(struct ice_hw *hw, struct ice_ctl_q_ring *ring, u16 num_entries)
 {
-       int i;
+       /* Clear Head and Tail */
+       wr32(hw, ring->head, 0);
+       wr32(hw, ring->tail, 0);
 
-       /* only unmap if the address is non-NULL */
-       for (i = 0; i < cq->num_sq_entries; i++)
-               if (cq->sq.r.sq_bi[i].pa) {
-                       dmam_free_coherent(ice_hw_to_dev(hw),
-                                          cq->sq.r.sq_bi[i].size,
-                                          cq->sq.r.sq_bi[i].va,
-                                          cq->sq.r.sq_bi[i].pa);
-                       cq->sq.r.sq_bi[i].va = NULL;
-                       cq->sq.r.sq_bi[i].pa = 0;
-                       cq->sq.r.sq_bi[i].size = 0;
-               }
+       /* set starting point */
+       wr32(hw, ring->len, (num_entries | ring->len_ena_mask));
+       wr32(hw, ring->bal, lower_32_bits(ring->desc_buf.pa));
+       wr32(hw, ring->bah, upper_32_bits(ring->desc_buf.pa));
 
-       /* free the buffer info list */
-       devm_kfree(ice_hw_to_dev(hw), cq->sq.cmd_buf);
+       /* Check one register to verify that config was applied */
+       if (rd32(hw, ring->bal) != lower_32_bits(ring->desc_buf.pa))
+               return ICE_ERR_AQ_ERROR;
 
-       /* free the dma header */
-       devm_kfree(ice_hw_to_dev(hw), cq->sq.dma_head);
+       return 0;
 }
 
 /**
@@ -340,23 +279,7 @@ static void ice_free_sq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
 static enum ice_status
 ice_cfg_sq_regs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
 {
-       u32 reg = 0;
-
-       /* Clear Head and Tail */
-       wr32(hw, cq->sq.head, 0);
-       wr32(hw, cq->sq.tail, 0);
-
-       /* set starting point */
-       wr32(hw, cq->sq.len, (cq->num_sq_entries | cq->sq.len_ena_mask));
-       wr32(hw, cq->sq.bal, lower_32_bits(cq->sq.desc_buf.pa));
-       wr32(hw, cq->sq.bah, upper_32_bits(cq->sq.desc_buf.pa));
-
-       /* Check one register to verify that config was applied */
-       reg = rd32(hw, cq->sq.bal);
-       if (reg != lower_32_bits(cq->sq.desc_buf.pa))
-               return ICE_ERR_AQ_ERROR;
-
-       return 0;
+       return ice_cfg_cq_regs(hw, &cq->sq, cq->num_sq_entries);
 }
 
 /**
@@ -369,25 +292,15 @@ ice_cfg_sq_regs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
 static enum ice_status
 ice_cfg_rq_regs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
 {
-       u32 reg = 0;
-
-       /* Clear Head and Tail */
-       wr32(hw, cq->rq.head, 0);
-       wr32(hw, cq->rq.tail, 0);
+       enum ice_status status;
 
-       /* set starting point */
-       wr32(hw, cq->rq.len, (cq->num_rq_entries | cq->rq.len_ena_mask));
-       wr32(hw, cq->rq.bal, lower_32_bits(cq->rq.desc_buf.pa));
-       wr32(hw, cq->rq.bah, upper_32_bits(cq->rq.desc_buf.pa));
+       status = ice_cfg_cq_regs(hw, &cq->rq, cq->num_rq_entries);
+       if (status)
+               return status;
 
        /* Update tail in the HW to post pre-allocated buffers */
        wr32(hw, cq->rq.tail, (u32)(cq->num_rq_entries - 1));
 
-       /* Check one register to verify that config was applied */
-       reg = rd32(hw, cq->rq.bal);
-       if (reg != lower_32_bits(cq->rq.desc_buf.pa))
-               return ICE_ERR_AQ_ERROR;
-
        return 0;
 }
 
@@ -444,7 +357,7 @@ static enum ice_status ice_init_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
        goto init_ctrlq_exit;
 
 init_ctrlq_free_rings:
-       ice_free_ctrlq_sq_ring(hw, cq);
+       ice_free_cq_ring(hw, &cq->sq);
 
 init_ctrlq_exit:
        return ret_code;
@@ -503,12 +416,33 @@ static enum ice_status ice_init_rq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
        goto init_ctrlq_exit;
 
 init_ctrlq_free_rings:
-       ice_free_ctrlq_rq_ring(hw, cq);
+       ice_free_cq_ring(hw, &cq->rq);
 
 init_ctrlq_exit:
        return ret_code;
 }
 
+#define ICE_FREE_CQ_BUFS(hw, qi, ring)                                 \
+do {                                                                   \
+       int i;                                                          \
+       /* free descriptors */                                          \
+       for (i = 0; i < (qi)->num_##ring##_entries; i++)                \
+               if ((qi)->ring.r.ring##_bi[i].pa) {                     \
+                       dmam_free_coherent(ice_hw_to_dev(hw),           \
+                                          (qi)->ring.r.ring##_bi[i].size,\
+                                          (qi)->ring.r.ring##_bi[i].va,\
+                                          (qi)->ring.r.ring##_bi[i].pa);\
+                       (qi)->ring.r.ring##_bi[i].va = NULL;            \
+                       (qi)->ring.r.ring##_bi[i].pa = 0;               \
+                       (qi)->ring.r.ring##_bi[i].size = 0;             \
+               }                                                       \
+       /* free the buffer info list */                                 \
+       if ((qi)->ring.cmd_buf)                                         \
+               devm_kfree(ice_hw_to_dev(hw), (qi)->ring.cmd_buf);      \
+       /* free dma head */                                             \
+       devm_kfree(ice_hw_to_dev(hw), (qi)->ring.dma_head);             \
+} while (0)
+
 /**
  * ice_shutdown_sq - shutdown the Control ATQ
  * @hw: pointer to the hardware structure
@@ -538,8 +472,8 @@ ice_shutdown_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
        cq->sq.count = 0;       /* to indicate uninitialized queue */
 
        /* free ring buffers and the ring itself */
-       ice_free_sq_bufs(hw, cq);
-       ice_free_ctrlq_sq_ring(hw, cq);
+       ICE_FREE_CQ_BUFS(hw, cq, sq);
+       ice_free_cq_ring(hw, &cq->sq);
 
 shutdown_sq_out:
        mutex_unlock(&cq->sq_lock);
@@ -606,8 +540,8 @@ ice_shutdown_rq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
        cq->rq.count = 0;
 
        /* free ring buffers and the ring itself */
-       ice_free_rq_bufs(hw, cq);
-       ice_free_ctrlq_rq_ring(hw, cq);
+       ICE_FREE_CQ_BUFS(hw, cq, rq);
+       ice_free_cq_ring(hw, &cq->rq);
 
 shutdown_rq_out:
        mutex_unlock(&cq->rq_lock);
@@ -657,7 +591,6 @@ init_ctrlq_free_rq:
  *     - cq->num_rq_entries
  *     - cq->rq_buf_size
  *     - cq->sq_buf_size
- *
  */
 static enum ice_status ice_init_ctrlq(struct ice_hw *hw, enum ice_ctl_q q_type)
 {
@@ -841,7 +774,7 @@ static bool ice_sq_done(struct ice_hw *hw, struct ice_ctl_q_info *cq)
  * @buf_size: size of buffer for indirect commands (or 0 for direct commands)
  * @cd: pointer to command details structure
  *
- * This is the main send command routine for the ATQ.  It runs the q,
+ * This is the main send command routine for the ATQ. It runs the queue,
  * cleans the queue, etc.
  */
 enum ice_status
@@ -1035,7 +968,7 @@ void ice_fill_dflt_direct_cmd_desc(struct ice_aq_desc *desc, u16 opcode)
  * @pending: number of events that could be left to process
  *
  * This function cleans one Admin Receive Queue element and returns
- * the contents through e.  It can also return how many events are
+ * the contents through e. It can also return how many events are
  * left to process through 'pending'.
  */
 enum ice_status
index 648acdb..3b6e387 100644 (file)
@@ -62,7 +62,7 @@ static const struct ice_stats ice_gstrings_vsi_stats[] = {
  * The PF_STATs are appended to the netdev stats only when ethtool -S
  * is queried on the base PF netdev.
  */
-static struct ice_stats ice_gstrings_pf_stats[] = {
+static const struct ice_stats ice_gstrings_pf_stats[] = {
        ICE_PF_STAT("tx_bytes", stats.eth.tx_bytes),
        ICE_PF_STAT("rx_bytes", stats.eth.rx_bytes),
        ICE_PF_STAT("tx_unicast", stats.eth.tx_unicast),
@@ -104,7 +104,7 @@ static struct ice_stats ice_gstrings_pf_stats[] = {
        ICE_PF_STAT("mac_remote_faults", stats.mac_remote_faults),
 };
 
-static u32 ice_regs_dump_list[] = {
+static const u32 ice_regs_dump_list[] = {
        PFGEN_STATE,
        PRTGEN_STATUS,
        QRX_CTRL(0),
@@ -260,10 +260,10 @@ static int ice_get_sset_count(struct net_device *netdev, int sset)
                 * a private ethtool flag). This is due to the nature of the
                 * ethtool stats API.
                 *
-                * User space programs such as ethtool must make 3 separate
+                * Userspace programs such as ethtool must make 3 separate
                 * ioctl requests, one for size, one for the strings, and
                 * finally one for the stats. Since these cross into
-                * user space, changes to the number or size could result in
+                * userspace, changes to the number or size could result in
                 * undefined memory access or incorrect string<->value
                 * correlations for statistics.
                 *
@@ -1392,17 +1392,17 @@ static int ice_nway_reset(struct net_device *netdev)
 {
        /* restart autonegotiation */
        struct ice_netdev_priv *np = netdev_priv(netdev);
-       struct ice_link_status *hw_link_info;
        struct ice_vsi *vsi = np->vsi;
        struct ice_port_info *pi;
        enum ice_status status;
-       bool link_up;
 
        pi = vsi->port_info;
-       hw_link_info = &pi->phy.link_info;
-       link_up = hw_link_info->link_info & ICE_AQ_LINK_UP;
+       /* If VSI state is up, then restart autoneg with link up */
+       if (!test_bit(__ICE_DOWN, vsi->back->state))
+               status = ice_aq_set_link_restart_an(pi, true, NULL);
+       else
+               status = ice_aq_set_link_restart_an(pi, false, NULL);
 
-       status = ice_aq_set_link_restart_an(pi, link_up, NULL);
        if (status) {
                netdev_info(netdev, "link restart failed, err %d aq_err %d\n",
                            status, pi->hw->adminq.sq_last_status);
@@ -1441,7 +1441,7 @@ ice_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
 /**
  * ice_set_pauseparam - Set Flow Control parameter
  * @netdev: network interface device structure
- * @pause: return tx/rx flow control status
+ * @pause: return Tx/Rx flow control status
  */
 static int
 ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
@@ -1543,7 +1543,7 @@ static u32 ice_get_rxfh_key_size(struct net_device __always_unused *netdev)
 }
 
 /**
- * ice_get_rxfh_indir_size - get the rx flow hash indirection table size
+ * ice_get_rxfh_indir_size - get the Rx flow hash indirection table size
  * @netdev: network interface device structure
  *
  * Returns the table size.
@@ -1556,7 +1556,7 @@ static u32 ice_get_rxfh_indir_size(struct net_device *netdev)
 }
 
 /**
- * ice_get_rxfh - get the rx flow hash indirection table
+ * ice_get_rxfh - get the Rx flow hash indirection table
  * @netdev: network interface device structure
  * @indir: indirection table
  * @key: hash key
@@ -1603,7 +1603,7 @@ out:
 }
 
 /**
- * ice_set_rxfh - set the rx flow hash indirection table
+ * ice_set_rxfh - set the Rx flow hash indirection table
  * @netdev: network interface device structure
  * @indir: indirection table
  * @key: hash key
index 596b9fb..5507928 100644 (file)
@@ -7,6 +7,9 @@
 #define _ICE_HW_AUTOGEN_H_
 
 #define QTX_COMM_DBELL(_DBQM)                  (0x002C0000 + ((_DBQM) * 4))
+#define QTX_COMM_HEAD(_DBQM)                   (0x000E0000 + ((_DBQM) * 4))
+#define QTX_COMM_HEAD_HEAD_S                   0
+#define QTX_COMM_HEAD_HEAD_M                   ICE_M(0x1FFF, 0)
 #define PF_FW_ARQBAH                           0x00080180
 #define PF_FW_ARQBAL                           0x00080080
 #define PF_FW_ARQH                             0x00080380
index 7d2a667..bb51dd7 100644 (file)
@@ -6,11 +6,11 @@
 
 union ice_32byte_rx_desc {
        struct {
-               __le64  pkt_addr; /* Packet buffer address */
-               __le64  hdr_addr; /* Header buffer address */
+               __le64 pkt_addr; /* Packet buffer address */
+               __le64 hdr_addr; /* Header buffer address */
                        /* bit 0 of hdr_addr is DD bit */
-               __le64  rsvd1;
-               __le64  rsvd2;
+               __le64 rsvd1;
+               __le64 rsvd2;
        } read;
        struct {
                struct {
@@ -105,11 +105,11 @@ enum ice_rx_ptype_payload_layer {
  */
 union ice_32b_rx_flex_desc {
        struct {
-               __le64  pkt_addr; /* Packet buffer address */
-               __le64  hdr_addr; /* Header buffer address */
-                                 /* bit 0 of hdr_addr is DD bit */
-               __le64  rsvd1;
-               __le64  rsvd2;
+               __le64 pkt_addr; /* Packet buffer address */
+               __le64 hdr_addr; /* Header buffer address */
+                                /* bit 0 of hdr_addr is DD bit */
+               __le64 rsvd1;
+               __le64 rsvd2;
        } read;
        struct {
                /* Qword 0 */
@@ -256,6 +256,9 @@ enum ice_rx_flex_desc_status_error_0_bits {
 
 #define ICE_RXQ_CTX_SIZE_DWORDS                8
 #define ICE_RXQ_CTX_SZ                 (ICE_RXQ_CTX_SIZE_DWORDS * sizeof(u32))
+#define ICE_TX_CMPLTNQ_CTX_SIZE_DWORDS 22
+#define ICE_TX_DRBELL_Q_CTX_SIZE_DWORDS        5
+#define GLTCLAN_CQ_CNTX(i, CQ)         (GLTCLAN_CQ_CNTX0(CQ) + ((i) * 0x0800))
 
 /* RLAN Rx queue context data
  *
@@ -274,18 +277,18 @@ struct ice_rlan_ctx {
        u16 dbuf; /* bigger than needed, see above for reason */
 #define ICE_RLAN_CTX_HBUF_S 6
        u16 hbuf; /* bigger than needed, see above for reason */
-       u8  dtype;
-       u8  dsize;
-       u8  crcstrip;
-       u8  l2tsel;
-       u8  hsplit_0;
-       u8  hsplit_1;
-       u8  showiv;
+       u8 dtype;
+       u8 dsize;
+       u8 crcstrip;
+       u8 l2tsel;
+       u8 hsplit_0;
+       u8 hsplit_1;
+       u8 showiv;
        u32 rxmax; /* bigger than needed, see above for reason */
-       u8  tphrdesc_ena;
-       u8  tphwdesc_ena;
-       u8  tphdata_ena;
-       u8  tphhead_ena;
+       u8 tphrdesc_ena;
+       u8 tphwdesc_ena;
+       u8 tphdata_ena;
+       u8 tphhead_ena;
        u16 lrxqthresh; /* bigger than needed, see above for reason */
 };
 
@@ -413,35 +416,35 @@ enum ice_tx_ctx_desc_cmd_bits {
 struct ice_tlan_ctx {
 #define ICE_TLAN_CTX_BASE_S    7
        u64 base;               /* base is defined in 128-byte units */
-       u8  port_num;
+       u8 port_num;
        u16 cgd_num;            /* bigger than needed, see above for reason */
-       u8  pf_num;
+       u8 pf_num;
        u16 vmvf_num;
-       u8  vmvf_type;
+       u8 vmvf_type;
 #define ICE_TLAN_CTX_VMVF_TYPE_VF      0
 #define ICE_TLAN_CTX_VMVF_TYPE_VMQ     1
 #define ICE_TLAN_CTX_VMVF_TYPE_PF      2
        u16 src_vsi;
-       u8  tsyn_ena;
-       u8  alt_vlan;
+       u8 tsyn_ena;
+       u8 alt_vlan;
        u16 cpuid;              /* bigger than needed, see above for reason */
-       u8  wb_mode;
-       u8  tphrd_desc;
-       u8  tphrd;
-       u8  tphwr_desc;
+       u8 wb_mode;
+       u8 tphrd_desc;
+       u8 tphrd;
+       u8 tphwr_desc;
        u16 cmpq_id;
        u16 qnum_in_func;
-       u8  itr_notification_mode;
-       u8  adjust_prof_id;
+       u8 itr_notification_mode;
+       u8 adjust_prof_id;
        u32 qlen;               /* bigger than needed, see above for reason */
-       u8  quanta_prof_idx;
-       u8  tso_ena;
+       u8 quanta_prof_idx;
+       u8 tso_ena;
        u16 tso_qnum;
-       u8  legacy_int;
-       u8  drop_ena;
-       u8  cache_prof_idx;
-       u8  pkt_shaper_prof_idx;
-       u8  int_q_state;        /* width not needed - internal do not write */
+       u8 legacy_int;
+       u8 drop_ena;
+       u8 cache_prof_idx;
+       u8 pkt_shaper_prof_idx;
+       u8 int_q_state; /* width not needed - internal do not write */
 };
 
 /* macro to make the table lines short */
index 1041fa2..29b1dcf 100644 (file)
@@ -20,7 +20,7 @@ static int ice_setup_rx_ctx(struct ice_ring *ring)
        u16 pf_q;
        int err;
 
-       /* what is RX queue number in global space of 2K Rx queues */
+       /* what is Rx queue number in global space of 2K Rx queues */
        pf_q = vsi->rxq_map[ring->q_index];
 
        /* clear the context structure first */
@@ -174,15 +174,15 @@ static int ice_pf_rxq_wait(struct ice_pf *pf, int pf_q, bool ena)
 {
        int i;
 
-       for (i = 0; i < ICE_Q_WAIT_RETRY_LIMIT; i++) {
+       for (i = 0; i < ICE_Q_WAIT_MAX_RETRY; i++) {
                u32 rx_reg = rd32(&pf->hw, QRX_CTRL(pf_q));
 
                if (ena == !!(rx_reg & QRX_CTRL_QENA_STAT_M))
                        break;
 
-               usleep_range(10, 20);
+               usleep_range(20, 40);
        }
-       if (i >= ICE_Q_WAIT_RETRY_LIMIT)
+       if (i >= ICE_Q_WAIT_MAX_RETRY)
                return -ETIMEDOUT;
 
        return 0;
@@ -774,11 +774,13 @@ static void ice_set_dflt_vsi_ctx(struct ice_vsi_ctx *ctxt)
  */
 static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
 {
-       u16 offset = 0, qmap = 0, numq_tc;
-       u16 pow = 0, max_rss = 0, qcount;
+       u16 offset = 0, qmap = 0, tx_count = 0;
        u16 qcount_tx = vsi->alloc_txq;
        u16 qcount_rx = vsi->alloc_rxq;
+       u16 tx_numq_tc, rx_numq_tc;
+       u16 pow = 0, max_rss = 0;
        bool ena_tc0 = false;
+       u8 netdev_tc = 0;
        int i;
 
        /* at least TC0 should be enabled by default */
@@ -794,7 +796,12 @@ static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
                vsi->tc_cfg.ena_tc |= 1;
        }
 
-       numq_tc = qcount_rx / vsi->tc_cfg.numtc;
+       rx_numq_tc = qcount_rx / vsi->tc_cfg.numtc;
+       if (!rx_numq_tc)
+               rx_numq_tc = 1;
+       tx_numq_tc = qcount_tx / vsi->tc_cfg.numtc;
+       if (!tx_numq_tc)
+               tx_numq_tc = 1;
 
        /* TC mapping is a function of the number of Rx queues assigned to the
         * VSI for each traffic class and the offset of these queues.
@@ -808,7 +815,8 @@ static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
         * Setup number and offset of Rx queues for all TCs for the VSI
         */
 
-       qcount = numq_tc;
+       qcount_rx = rx_numq_tc;
+
        /* qcount will change if RSS is enabled */
        if (test_bit(ICE_FLAG_RSS_ENA, vsi->back->flags)) {
                if (vsi->type == ICE_VSI_PF || vsi->type == ICE_VSI_VF) {
@@ -816,37 +824,41 @@ static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
                                max_rss = ICE_MAX_LG_RSS_QS;
                        else
                                max_rss = ICE_MAX_SMALL_RSS_QS;
-                       qcount = min_t(int, numq_tc, max_rss);
-                       qcount = min_t(int, qcount, vsi->rss_size);
+                       qcount_rx = min_t(int, rx_numq_tc, max_rss);
+                       qcount_rx = min_t(int, qcount_rx, vsi->rss_size);
                }
        }
 
        /* find the (rounded up) power-of-2 of qcount */
-       pow = order_base_2(qcount);
+       pow = order_base_2(qcount_rx);
 
        for (i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) {
                if (!(vsi->tc_cfg.ena_tc & BIT(i))) {
                        /* TC is not enabled */
                        vsi->tc_cfg.tc_info[i].qoffset = 0;
-                       vsi->tc_cfg.tc_info[i].qcount = 1;
+                       vsi->tc_cfg.tc_info[i].qcount_rx = 1;
+                       vsi->tc_cfg.tc_info[i].qcount_tx = 1;
+                       vsi->tc_cfg.tc_info[i].netdev_tc = 0;
                        ctxt->info.tc_mapping[i] = 0;
                        continue;
                }
 
                /* TC is enabled */
                vsi->tc_cfg.tc_info[i].qoffset = offset;
-               vsi->tc_cfg.tc_info[i].qcount = qcount;
+               vsi->tc_cfg.tc_info[i].qcount_rx = qcount_rx;
+               vsi->tc_cfg.tc_info[i].qcount_tx = tx_numq_tc;
+               vsi->tc_cfg.tc_info[i].netdev_tc = netdev_tc++;
 
                qmap = ((offset << ICE_AQ_VSI_TC_Q_OFFSET_S) &
                        ICE_AQ_VSI_TC_Q_OFFSET_M) |
                        ((pow << ICE_AQ_VSI_TC_Q_NUM_S) &
                         ICE_AQ_VSI_TC_Q_NUM_M);
-               offset += qcount;
+               offset += qcount_rx;
+               tx_count += tx_numq_tc;
                ctxt->info.tc_mapping[i] = cpu_to_le16(qmap);
        }
-
-       vsi->num_txq = qcount_tx;
        vsi->num_rxq = offset;
+       vsi->num_txq = tx_count;
 
        if (vsi->type == ICE_VSI_VF && vsi->num_txq != vsi->num_rxq) {
                dev_dbg(&vsi->back->pdev->dev, "VF VSI should have same number of Tx and Rx queues. Hence making them equal\n");
@@ -1000,7 +1012,7 @@ void ice_vsi_free_q_vectors(struct ice_vsi *vsi)
  * @vsi: the VSI being configured
  * @v_idx: index of the vector in the VSI struct
  *
- * We allocate one q_vector.  If allocation fails we return -ENOMEM.
+ * We allocate one q_vector. If allocation fails we return -ENOMEM.
  */
 static int ice_vsi_alloc_q_vector(struct ice_vsi *vsi, int v_idx)
 {
@@ -1039,7 +1051,7 @@ out:
  * ice_vsi_alloc_q_vectors - Allocate memory for interrupt vectors
  * @vsi: the VSI being configured
  *
- * We allocate one q_vector per queue interrupt.  If allocation fails we
+ * We allocate one q_vector per queue interrupt. If allocation fails we
  * return -ENOMEM.
  */
 static int ice_vsi_alloc_q_vectors(struct ice_vsi *vsi)
@@ -1188,7 +1200,7 @@ static int ice_vsi_alloc_rings(struct ice_vsi *vsi)
        struct ice_pf *pf = vsi->back;
        int i;
 
-       /* Allocate tx_rings */
+       /* Allocate Tx rings */
        for (i = 0; i < vsi->alloc_txq; i++) {
                struct ice_ring *ring;
 
@@ -1207,7 +1219,7 @@ static int ice_vsi_alloc_rings(struct ice_vsi *vsi)
                vsi->tx_rings[i] = ring;
        }
 
-       /* Allocate rx_rings */
+       /* Allocate Rx rings */
        for (i = 0; i < vsi->alloc_rxq; i++) {
                struct ice_ring *ring;
 
@@ -1611,55 +1623,62 @@ int ice_vsi_cfg_txqs(struct ice_vsi *vsi)
        struct ice_aqc_add_tx_qgrp *qg_buf;
        struct ice_aqc_add_txqs_perq *txq;
        struct ice_pf *pf = vsi->back;
+       u8 num_q_grps, q_idx = 0;
        enum ice_status status;
        u16 buf_len, i, pf_q;
        int err = 0, tc = 0;
-       u8 num_q_grps;
 
        buf_len = sizeof(struct ice_aqc_add_tx_qgrp);
        qg_buf = devm_kzalloc(&pf->pdev->dev, buf_len, GFP_KERNEL);
        if (!qg_buf)
                return -ENOMEM;
 
-       if (vsi->num_txq > ICE_MAX_TXQ_PER_TXQG) {
-               err = -EINVAL;
-               goto err_cfg_txqs;
-       }
        qg_buf->num_txqs = 1;
        num_q_grps = 1;
 
-       /* set up and configure the Tx queues */
-       ice_for_each_txq(vsi, i) {
-               struct ice_tlan_ctx tlan_ctx = { 0 };
+       /* set up and configure the Tx queues for each enabled TC */
+       for (tc = 0; tc < ICE_MAX_TRAFFIC_CLASS; tc++) {
+               if (!(vsi->tc_cfg.ena_tc & BIT(tc)))
+                       break;
 
-               pf_q = vsi->txq_map[i];
-               ice_setup_tx_ctx(vsi->tx_rings[i], &tlan_ctx, pf_q);
-               /* copy context contents into the qg_buf */
-               qg_buf->txqs[0].txq_id = cpu_to_le16(pf_q);
-               ice_set_ctx((u8 *)&tlan_ctx, qg_buf->txqs[0].txq_ctx,
-                           ice_tlan_ctx_info);
+               for (i = 0; i < vsi->tc_cfg.tc_info[tc].qcount_tx; i++) {
+                       struct ice_tlan_ctx tlan_ctx = { 0 };
+
+                       pf_q = vsi->txq_map[q_idx];
+                       ice_setup_tx_ctx(vsi->tx_rings[q_idx], &tlan_ctx,
+                                        pf_q);
+                       /* copy context contents into the qg_buf */
+                       qg_buf->txqs[0].txq_id = cpu_to_le16(pf_q);
+                       ice_set_ctx((u8 *)&tlan_ctx, qg_buf->txqs[0].txq_ctx,
+                                   ice_tlan_ctx_info);
+
+                       /* init queue specific tail reg. It is referred as
+                        * transmit comm scheduler queue doorbell.
+                        */
+                       vsi->tx_rings[q_idx]->tail =
+                               pf->hw.hw_addr + QTX_COMM_DBELL(pf_q);
+                       status = ice_ena_vsi_txq(vsi->port_info, vsi->idx, tc,
+                                                num_q_grps, qg_buf, buf_len,
+                                                NULL);
+                       if (status) {
+                               dev_err(&vsi->back->pdev->dev,
+                                       "Failed to set LAN Tx queue context, error: %d\n",
+                                       status);
+                               err = -ENODEV;
+                               goto err_cfg_txqs;
+                       }
 
-               /* init queue specific tail reg. It is referred as transmit
-                * comm scheduler queue doorbell.
-                */
-               vsi->tx_rings[i]->tail = pf->hw.hw_addr + QTX_COMM_DBELL(pf_q);
-               status = ice_ena_vsi_txq(vsi->port_info, vsi->idx, tc,
-                                        num_q_grps, qg_buf, buf_len, NULL);
-               if (status) {
-                       dev_err(&vsi->back->pdev->dev,
-                               "Failed to set LAN Tx queue context, error: %d\n",
-                               status);
-                       err = -ENODEV;
-                       goto err_cfg_txqs;
-               }
+                       /* Add Tx Queue TEID into the VSI Tx ring from the
+                        * response. This will complete configuring and
+                        * enabling the queue.
+                        */
+                       txq = &qg_buf->txqs[0];
+                       if (pf_q == le16_to_cpu(txq->txq_id))
+                               vsi->tx_rings[q_idx]->txq_teid =
+                                       le32_to_cpu(txq->q_teid);
 
-               /* Add Tx Queue TEID into the VSI Tx ring from the response
-                * This will complete configuring and enabling the queue.
-                */
-               txq = &qg_buf->txqs[0];
-               if (pf_q == le16_to_cpu(txq->txq_id))
-                       vsi->tx_rings[i]->txq_teid =
-                               le32_to_cpu(txq->q_teid);
+                       q_idx++;
+               }
        }
 err_cfg_txqs:
        devm_kfree(&pf->pdev->dev, qg_buf);
@@ -1908,7 +1927,8 @@ int ice_vsi_stop_tx_rings(struct ice_vsi *vsi, enum ice_disq_rst_src rst_src,
        ice_for_each_txq(vsi, i) {
                u16 v_idx;
 
-               if (!vsi->tx_rings || !vsi->tx_rings[i]) {
+               if (!vsi->tx_rings || !vsi->tx_rings[i] ||
+                   !vsi->tx_rings[i]->q_vector) {
                        err = -EINVAL;
                        goto err_out;
                }
@@ -2056,6 +2076,9 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi,
        /* set RSS capabilities */
        ice_vsi_set_rss_params(vsi);
 
+       /* set tc configuration */
+       ice_vsi_set_tc_cfg(vsi);
+
        /* create the VSI */
        ret = ice_vsi_init(vsi);
        if (ret)
@@ -2113,17 +2136,13 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi,
                pf->q_left_rx -= vsi->alloc_rxq;
                break;
        default:
-               /* if VSI type is not recognized, clean up the resources and
-                * exit
-                */
+               /* clean up the resources and exit */
                goto unroll_vsi_init;
        }
 
-       ice_vsi_set_tc_cfg(vsi);
-
        /* configure VSI nodes based on number of queues and TC's */
        for (i = 0; i < vsi->tc_cfg.numtc; i++)
-               max_txqs[i] = vsi->num_txq;
+               max_txqs[i] = pf->num_lan_tx;
 
        ret = ice_cfg_vsi_lan(vsi->port_info, vsi->idx, vsi->tc_cfg.ena_tc,
                              max_txqs);
@@ -2314,7 +2333,7 @@ static int ice_search_res(struct ice_res_tracker *res, u16 needed, u16 id)
        int start = res->search_hint;
        int end = start;
 
-       if ((start + needed) >  res->num_entries)
+       if ((start + needed) > res->num_entries)
                return -ENOMEM;
 
        id |= ICE_RES_VALID_BIT;
@@ -2491,6 +2510,7 @@ int ice_vsi_release(struct ice_vsi *vsi)
        }
 
        ice_remove_vsi_fltr(&pf->hw, vsi->idx);
+       ice_rm_vsi_lan_cfg(vsi->port_info, vsi->idx);
        ice_vsi_delete(vsi);
        ice_vsi_free_q_vectors(vsi);
        ice_vsi_clear_rings(vsi);
@@ -2518,11 +2538,14 @@ int ice_vsi_release(struct ice_vsi *vsi)
 int ice_vsi_rebuild(struct ice_vsi *vsi)
 {
        u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 };
+       struct ice_pf *pf;
        int ret, i;
 
        if (!vsi)
                return -EINVAL;
 
+       pf = vsi->back;
+       ice_rm_vsi_lan_cfg(vsi->port_info, vsi->idx);
        ice_vsi_free_q_vectors(vsi);
        ice_free_res(vsi->back->sw_irq_tracker, vsi->sw_base_vector, vsi->idx);
        ice_free_res(vsi->back->hw_irq_tracker, vsi->hw_base_vector, vsi->idx);
@@ -2532,6 +2555,7 @@ int ice_vsi_rebuild(struct ice_vsi *vsi)
        ice_vsi_free_arrays(vsi, false);
        ice_dev_onetime_setup(&vsi->back->hw);
        ice_vsi_set_num_qs(vsi);
+       ice_vsi_set_tc_cfg(vsi);
 
        /* Initialize VSI struct elements and create VSI in FW */
        ret = ice_vsi_init(vsi);
@@ -2578,11 +2602,9 @@ int ice_vsi_rebuild(struct ice_vsi *vsi)
                break;
        }
 
-       ice_vsi_set_tc_cfg(vsi);
-
        /* configure VSI nodes based on number of queues and TC's */
        for (i = 0; i < vsi->tc_cfg.numtc; i++)
-               max_txqs[i] = vsi->num_txq;
+               max_txqs[i] = pf->num_lan_tx;
 
        ret = ice_cfg_vsi_lan(vsi->port_info, vsi->idx, vsi->tc_cfg.ena_tc,
                              max_txqs);
index 333312a..8725569 100644 (file)
@@ -349,6 +349,9 @@ ice_prepare_for_reset(struct ice_pf *pf)
        /* disable the VSIs and their queues that are not already DOWN */
        ice_pf_dis_all_vsi(pf);
 
+       if (hw->port_info)
+               ice_sched_clear_port(hw->port_info);
+
        ice_shutdown_all_ctrlq(hw);
 
        set_bit(__ICE_PREPARED_FOR_RESET, pf->state);
@@ -405,7 +408,7 @@ static void ice_reset_subtask(struct ice_pf *pf)
        /* When a CORER/GLOBR/EMPR is about to happen, the hardware triggers an
         * OICR interrupt. The OICR handler (ice_misc_intr) determines what type
         * of reset is pending and sets bits in pf->state indicating the reset
-        * type and __ICE_RESET_OICR_RECV.  So, if the latter bit is set
+        * type and __ICE_RESET_OICR_RECV. So, if the latter bit is set
         * prepare for pending reset if not already (for PF software-initiated
         * global resets the software should already be prepared for it as
         * indicated by __ICE_PREPARED_FOR_RESET; for global resets initiated
@@ -1379,7 +1382,7 @@ static void ice_free_irq_msix_misc(struct ice_pf *pf)
  * @pf: board private structure
  *
  * This sets up the handler for MSIX 0, which is used to manage the
- * non-queue interrupts, e.g. AdminQ and errors.  This is not used
+ * non-queue interrupts, e.g. AdminQ and errors. This is not used
  * when in MSI or Legacy interrupt mode.
  */
 static int ice_req_irq_msix_misc(struct ice_pf *pf)
@@ -1783,7 +1786,7 @@ static void ice_determine_q_usage(struct ice_pf *pf)
 
        pf->num_lan_tx = min_t(int, q_left_tx, num_online_cpus());
 
-       /* only 1 rx queue unless RSS is enabled */
+       /* only 1 Rx queue unless RSS is enabled */
        if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags))
                pf->num_lan_rx = 1;
        else
@@ -2091,8 +2094,7 @@ static int ice_probe(struct pci_dev *pdev,
 
        ice_determine_q_usage(pf);
 
-       pf->num_alloc_vsi = min_t(u16, ICE_MAX_VSI_ALLOC,
-                                 hw->func_caps.guaranteed_num_vsi);
+       pf->num_alloc_vsi = hw->func_caps.guar_num_vsi;
        if (!pf->num_alloc_vsi) {
                err = -EIO;
                goto err_init_pf_unroll;
@@ -2544,7 +2546,6 @@ static int ice_vsi_cfg(struct ice_vsi *vsi)
                if (err)
                        return err;
        }
-
        err = ice_vsi_cfg_txqs(vsi);
        if (!err)
                err = ice_vsi_cfg_rxqs(vsi);
@@ -2563,8 +2564,12 @@ static void ice_napi_enable_all(struct ice_vsi *vsi)
        if (!vsi->netdev)
                return;
 
-       for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++)
-               napi_enable(&vsi->q_vectors[q_idx]->napi);
+       for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++) {
+               struct ice_q_vector *q_vector = vsi->q_vectors[q_idx];
+
+               if (q_vector->rx.ring || q_vector->tx.ring)
+                       napi_enable(&q_vector->napi);
+       }
 }
 
 /**
@@ -2931,8 +2936,12 @@ static void ice_napi_disable_all(struct ice_vsi *vsi)
        if (!vsi->netdev)
                return;
 
-       for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++)
-               napi_disable(&vsi->q_vectors[q_idx]->napi);
+       for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++) {
+               struct ice_q_vector *q_vector = vsi->q_vectors[q_idx];
+
+               if (q_vector->rx.ring || q_vector->tx.ring)
+                       napi_disable(&q_vector->napi);
+       }
 }
 
 /**
@@ -3138,8 +3147,9 @@ static void ice_vsi_release_all(struct ice_pf *pf)
 /**
  * ice_dis_vsi - pause a VSI
  * @vsi: the VSI being paused
+ * @locked: is the rtnl_lock already held
  */
-static void ice_dis_vsi(struct ice_vsi *vsi)
+static void ice_dis_vsi(struct ice_vsi *vsi, bool locked)
 {
        if (test_bit(__ICE_DOWN, vsi->state))
                return;
@@ -3148,9 +3158,13 @@ static void ice_dis_vsi(struct ice_vsi *vsi)
 
        if (vsi->type == ICE_VSI_PF && vsi->netdev) {
                if (netif_running(vsi->netdev)) {
-                       rtnl_lock();
-                       vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
-                       rtnl_unlock();
+                       if (!locked) {
+                               rtnl_lock();
+                               vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
+                               rtnl_unlock();
+                       } else {
+                               vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
+                       }
                } else {
                        ice_vsi_close(vsi);
                }
@@ -3189,7 +3203,7 @@ static void ice_pf_dis_all_vsi(struct ice_pf *pf)
 
        ice_for_each_vsi(pf, v)
                if (pf->vsi[v])
-                       ice_dis_vsi(pf->vsi[v]);
+                       ice_dis_vsi(pf->vsi[v], false);
 }
 
 /**
@@ -3618,6 +3632,7 @@ static int ice_vsi_update_bridge_mode(struct ice_vsi *vsi, u16 bmode)
  * @dev: the netdev being configured
  * @nlh: RTNL message
  * @flags: bridge setlink flags
+ * @extack: netlink extended ack
  *
  * Sets the bridge mode (VEB/VEPA) of the switch to which the netdev (VSI) is
  * hooked up to. Iterates through the PF VSI list and sets the loopback mode (if
@@ -3626,7 +3641,7 @@ static int ice_vsi_update_bridge_mode(struct ice_vsi *vsi, u16 bmode)
  */
 static int
 ice_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
-                  u16 __always_unused flags)
+                  u16 __always_unused flags, struct netlink_ext_ack *extack)
 {
        struct ice_netdev_priv *np = netdev_priv(dev);
        struct ice_pf *pf = np->vsi->back;
@@ -3668,7 +3683,7 @@ ice_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
                 */
                status = ice_update_sw_rule_bridge_mode(hw);
                if (status) {
-                       netdev_err(dev, "update SW_RULE for bridge mode failed,  = %d err %d aq_err %d\n",
+                       netdev_err(dev, "switch rule update failed, mode = %d err %d aq_err %d\n",
                                   mode, status, hw->adminq.sq_last_status);
                        /* revert hw->evb_veb */
                        hw->evb_veb = (pf_sw->bridge_mode == BRIDGE_MODE_VEB);
@@ -3691,40 +3706,36 @@ static void ice_tx_timeout(struct net_device *netdev)
        struct ice_ring *tx_ring = NULL;
        struct ice_vsi *vsi = np->vsi;
        struct ice_pf *pf = vsi->back;
-       u32 head, val = 0, i;
        int hung_queue = -1;
+       u32 i;
 
        pf->tx_timeout_count++;
 
-       /* find the stopped queue the same way the stack does */
+       /* find the stopped queue the same way dev_watchdog() does */
        for (i = 0; i < netdev->num_tx_queues; i++) {
-               struct netdev_queue *q;
                unsigned long trans_start;
+               struct netdev_queue *q;
 
                q = netdev_get_tx_queue(netdev, i);
                trans_start = q->trans_start;
                if (netif_xmit_stopped(q) &&
                    time_after(jiffies,
-                              (trans_start + netdev->watchdog_timeo))) {
+                              trans_start + netdev->watchdog_timeo)) {
                        hung_queue = i;
                        break;
                }
        }
 
-       if (i == netdev->num_tx_queues) {
+       if (i == netdev->num_tx_queues)
                netdev_info(netdev, "tx_timeout: no netdev hung queue found\n");
-       } else {
+       else
                /* now that we have an index, find the tx_ring struct */
-               for (i = 0; i < vsi->num_txq; i++) {
-                       if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) {
-                               if (hung_queue ==
-                                   vsi->tx_rings[i]->q_index) {
+               for (i = 0; i < vsi->num_txq; i++)
+                       if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc)
+                               if (hung_queue == vsi->tx_rings[i]->q_index) {
                                        tx_ring = vsi->tx_rings[i];
                                        break;
                                }
-                       }
-               }
-       }
 
        /* Reset recovery level if enough time has elapsed after last timeout.
         * Also ensure no new reset action happens before next timeout period.
@@ -3736,17 +3747,20 @@ static void ice_tx_timeout(struct net_device *netdev)
                return;
 
        if (tx_ring) {
-               head = tx_ring->next_to_clean;
+               struct ice_hw *hw = &pf->hw;
+               u32 head, val = 0;
+
+               head = (rd32(hw, QTX_COMM_HEAD(vsi->txq_map[hung_queue])) &
+                       QTX_COMM_HEAD_HEAD_M) >> QTX_COMM_HEAD_HEAD_S;
                /* Read interrupt register */
                if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
-                       val = rd32(&pf->hw,
+                       val = rd32(hw,
                                   GLINT_DYN_CTL(tx_ring->q_vector->v_idx +
-                                       tx_ring->vsi->hw_base_vector));
+                                                tx_ring->vsi->hw_base_vector));
 
-               netdev_info(netdev, "tx_timeout: VSI_num: %d, Q %d, NTC: 0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x, INT: 0x%x\n",
+               netdev_info(netdev, "tx_timeout: VSI_num: %d, Q %d, NTC: 0x%x, HW_HEAD: 0x%x, NTU: 0x%x, INT: 0x%x\n",
                            vsi->vsi_num, hung_queue, tx_ring->next_to_clean,
-                           head, tx_ring->next_to_use,
-                           readl(tx_ring->tail), val);
+                           head, tx_ring->next_to_use, val);
        }
 
        pf->tx_timeout_last_recovery = jiffies;
@@ -3780,7 +3794,7 @@ static void ice_tx_timeout(struct net_device *netdev)
  * @netdev: network interface device structure
  *
  * The open entry point is called when a network interface is made
- * active by the system (IFF_UP).  At this point all resources needed
+ * active by the system (IFF_UP). At this point all resources needed
  * for transmit and receive operations are allocated, the interrupt
  * handler is registered with the OS, the netdev watchdog is enabled,
  * and the stack is notified that the interface is ready.
@@ -3813,7 +3827,7 @@ static int ice_open(struct net_device *netdev)
  * @netdev: network interface device structure
  *
  * The stop entry point is called when an interface is de-activated by the OS,
- * and the netdevice enters the DOWN state.  The hardware is still under the
+ * and the netdevice enters the DOWN state. The hardware is still under the
  * driver's control, but the netdev interface is disabled.
  *
  * Returns success only - not allowed to fail
@@ -3842,14 +3856,14 @@ ice_features_check(struct sk_buff *skb,
        size_t len;
 
        /* No point in doing any of this if neither checksum nor GSO are
-        * being requested for this frame.  We can rule out both by just
+        * being requested for this frame. We can rule out both by just
         * checking for CHECKSUM_PARTIAL
         */
        if (skb->ip_summed != CHECKSUM_PARTIAL)
                return features;
 
        /* We cannot support GSO if the MSS is going to be less than
-        * 64 bytes.  If it is then we need to drop support for GSO.
+        * 64 bytes. If it is then we need to drop support for GSO.
         */
        if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_size < 64))
                features &= ~NETIF_F_GSO_MASK;
index 7cc8aa1..a168185 100644 (file)
@@ -630,7 +630,7 @@ static void ice_sched_clear_tx_topo(struct ice_port_info *pi)
  *
  * Cleanup scheduling elements from SW DB
  */
-static void ice_sched_clear_port(struct ice_port_info *pi)
+void ice_sched_clear_port(struct ice_port_info *pi)
 {
        if (!pi || pi->port_state != ICE_SCHED_PORT_STATE_READY)
                return;
@@ -894,8 +894,7 @@ static u8 ice_sched_get_vsi_layer(struct ice_hw *hw)
  * This function removes the leaf node that was created by the FW
  * during initialization
  */
-static void
-ice_rm_dflt_leaf_node(struct ice_port_info *pi)
+static void ice_rm_dflt_leaf_node(struct ice_port_info *pi)
 {
        struct ice_sched_node *node;
 
@@ -923,8 +922,7 @@ ice_rm_dflt_leaf_node(struct ice_port_info *pi)
  * This function frees all the nodes except root and TC that were created by
  * the FW during initialization
  */
-static void
-ice_sched_rm_dflt_nodes(struct ice_port_info *pi)
+static void ice_sched_rm_dflt_nodes(struct ice_port_info *pi)
 {
        struct ice_sched_node *node;
 
@@ -1339,7 +1337,7 @@ ice_sched_rm_vsi_child_nodes(struct ice_port_info *pi,
  * @num_nodes: pointer to num nodes array
  *
  * This function calculates the number of supported nodes needed to add this
- * VSI into tx tree including the VSI, parent and intermediate nodes in below
+ * VSI into Tx tree including the VSI, parent and intermediate nodes in below
  * layers
  */
 static void
@@ -1376,13 +1374,13 @@ ice_sched_calc_vsi_support_nodes(struct ice_hw *hw,
 }
 
 /**
- * ice_sched_add_vsi_support_nodes - add VSI supported nodes into tx tree
+ * ice_sched_add_vsi_support_nodes - add VSI supported nodes into Tx tree
  * @pi: port information structure
  * @vsi_handle: software VSI handle
  * @tc_node: pointer to TC node
  * @num_nodes: pointer to num nodes array
  *
- * This function adds the VSI supported nodes into tx tree including the
+ * This function adds the VSI supported nodes into Tx tree including the
  * VSI, its parent and intermediate nodes in below layers
  */
 static enum ice_status
@@ -1527,7 +1525,7 @@ ice_sched_update_vsi_child_nodes(struct ice_port_info *pi, u16 vsi_handle,
 }
 
 /**
- * ice_sched_cfg_vsi - configure the new/exisiting VSI
+ * ice_sched_cfg_vsi - configure the new/existing VSI
  * @pi: port information structure
  * @vsi_handle: software VSI handle
  * @tc: TC number
@@ -1605,3 +1603,109 @@ ice_sched_cfg_vsi(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u16 maxqs,
 
        return status;
 }
+
+/**
+ * ice_sched_rm_agg_vsi_entry - remove agg related VSI info entry
+ * @pi: port information structure
+ * @vsi_handle: software VSI handle
+ *
+ * This function removes single aggregator VSI info entry from
+ * aggregator list.
+ */
+static void
+ice_sched_rm_agg_vsi_info(struct ice_port_info *pi, u16 vsi_handle)
+{
+       struct ice_sched_agg_info *agg_info;
+       struct ice_sched_agg_info *atmp;
+
+       list_for_each_entry_safe(agg_info, atmp, &pi->agg_list, list_entry) {
+               struct ice_sched_agg_vsi_info *agg_vsi_info;
+               struct ice_sched_agg_vsi_info *vtmp;
+
+               list_for_each_entry_safe(agg_vsi_info, vtmp,
+                                        &agg_info->agg_vsi_list, list_entry)
+                       if (agg_vsi_info->vsi_handle == vsi_handle) {
+                               list_del(&agg_vsi_info->list_entry);
+                               devm_kfree(ice_hw_to_dev(pi->hw),
+                                          agg_vsi_info);
+                               return;
+                       }
+       }
+}
+
+/**
+ * ice_sched_rm_vsi_cfg - remove the VSI and its children nodes
+ * @pi: port information structure
+ * @vsi_handle: software VSI handle
+ * @owner: LAN or RDMA
+ *
+ * This function removes the VSI and its LAN or RDMA children nodes from the
+ * scheduler tree.
+ */
+static enum ice_status
+ice_sched_rm_vsi_cfg(struct ice_port_info *pi, u16 vsi_handle, u8 owner)
+{
+       enum ice_status status = ICE_ERR_PARAM;
+       struct ice_vsi_ctx *vsi_ctx;
+       u8 i, j = 0;
+
+       if (!ice_is_vsi_valid(pi->hw, vsi_handle))
+               return status;
+       mutex_lock(&pi->sched_lock);
+       vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
+       if (!vsi_ctx)
+               goto exit_sched_rm_vsi_cfg;
+
+       for (i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) {
+               struct ice_sched_node *vsi_node, *tc_node;
+
+               tc_node = ice_sched_get_tc_node(pi, i);
+               if (!tc_node)
+                       continue;
+
+               vsi_node = ice_sched_get_vsi_node(pi->hw, tc_node, vsi_handle);
+               if (!vsi_node)
+                       continue;
+
+               while (j < vsi_node->num_children) {
+                       if (vsi_node->children[j]->owner == owner) {
+                               ice_free_sched_node(pi, vsi_node->children[j]);
+
+                               /* reset the counter again since the num
+                                * children will be updated after node removal
+                                */
+                               j = 0;
+                       } else {
+                               j++;
+                       }
+               }
+               /* remove the VSI if it has no children */
+               if (!vsi_node->num_children) {
+                       ice_free_sched_node(pi, vsi_node);
+                       vsi_ctx->sched.vsi_node[i] = NULL;
+
+                       /* clean up agg related vsi info if any */
+                       ice_sched_rm_agg_vsi_info(pi, vsi_handle);
+               }
+               if (owner == ICE_SCHED_NODE_OWNER_LAN)
+                       vsi_ctx->sched.max_lanq[i] = 0;
+       }
+       status = 0;
+
+exit_sched_rm_vsi_cfg:
+       mutex_unlock(&pi->sched_lock);
+       return status;
+}
+
+/**
+ * ice_rm_vsi_lan_cfg - remove VSI and its LAN children nodes
+ * @pi: port information structure
+ * @vsi_handle: software VSI handle
+ *
+ * This function clears the VSI and its LAN children nodes from scheduler tree
+ * for all TCs.
+ */
+enum ice_status ice_rm_vsi_lan_cfg(struct ice_port_info *pi, u16 vsi_handle)
+{
+       return ice_sched_rm_vsi_cfg(pi, vsi_handle, ICE_SCHED_NODE_OWNER_LAN);
+}
index 5dc9cfa..da5b4c1 100644 (file)
@@ -12,6 +12,7 @@
 struct ice_sched_agg_vsi_info {
        struct list_head list_entry;
        DECLARE_BITMAP(tc_bitmap, ICE_MAX_TRAFFIC_CLASS);
+       u16 vsi_handle;
 };
 
 struct ice_sched_agg_info {
@@ -25,6 +26,7 @@ struct ice_sched_agg_info {
 /* FW AQ command calls */
 enum ice_status ice_sched_init_port(struct ice_port_info *pi);
 enum ice_status ice_sched_query_res_alloc(struct ice_hw *hw);
+void ice_sched_clear_port(struct ice_port_info *pi);
 void ice_sched_cleanup_all(struct ice_hw *hw);
 struct ice_sched_node *
 ice_sched_find_node_by_teid(struct ice_sched_node *start_node, u32 teid);
@@ -39,4 +41,5 @@ ice_sched_get_free_qparent(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
 enum ice_status
 ice_sched_cfg_vsi(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u16 maxqs,
                  u8 owner, bool enable);
+enum ice_status ice_rm_vsi_lan_cfg(struct ice_port_info *pi, u16 vsi_handle);
 #endif /* _ICE_SCHED_H_ */
index 027eba4..533b989 100644 (file)
@@ -46,7 +46,7 @@ ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval,
  * @link_speed: variable containing the link_speed to be converted
  *
  * Convert link speed supported by HW to link speed supported by virtchnl.
- * If adv_link_support is true, then return link speed in Mbps.  Else return
+ * If adv_link_support is true, then return link speed in Mbps. Else return
  * link speed as a VIRTCHNL_LINK_SPEED_* casted to a u32. Note that the caller
  * needs to cast back to an enum virtchnl_link_speed in the case where
  * adv_link_support is false, but when adv_link_support is true the caller can
index 40c9c65..2e56931 100644 (file)
@@ -92,8 +92,7 @@ ice_aq_alloc_free_res(struct ice_hw *hw, u16 num_entries,
  * Allocate memory for the entire recipe table and initialize the structures/
  * entries corresponding to basic recipes.
  */
-enum ice_status
-ice_init_def_sw_recp(struct ice_hw *hw)
+enum ice_status ice_init_def_sw_recp(struct ice_hw *hw)
 {
        struct ice_sw_recipe *recps;
        u8 i;
@@ -130,7 +129,7 @@ ice_init_def_sw_recp(struct ice_hw *hw)
  *
  * NOTE: *req_desc is both an input/output parameter.
  * The caller of this function first calls this function with *request_desc set
- * to 0.  If the response from f/w has *req_desc set to 0, all the switch
+ * to 0. If the response from f/w has *req_desc set to 0, all the switch
  * configuration information has been returned; if non-zero (meaning not all
  * the information was returned), the caller should call this function again
  * with *req_desc set to the previous value returned by f/w to get the
@@ -629,25 +628,36 @@ enum ice_status ice_get_initial_sw_cfg(struct ice_hw *hw)
 /**
  * ice_fill_sw_info - Helper function to populate lb_en and lan_en
  * @hw: pointer to the hardware structure
- * @f_info: filter info structure to fill/update
+ * @fi: filter info structure to fill/update
  *
  * This helper function populates the lb_en and lan_en elements of the provided
  * ice_fltr_info struct using the switch's type and characteristics of the
  * switch rule being configured.
  */
-static void ice_fill_sw_info(struct ice_hw *hw, struct ice_fltr_info *f_info)
+static void ice_fill_sw_info(struct ice_hw *hw, struct ice_fltr_info *fi)
 {
-       f_info->lb_en = false;
-       f_info->lan_en = false;
-       if ((f_info->flag & ICE_FLTR_TX) &&
-           (f_info->fltr_act == ICE_FWD_TO_VSI ||
-            f_info->fltr_act == ICE_FWD_TO_VSI_LIST ||
-            f_info->fltr_act == ICE_FWD_TO_Q ||
-            f_info->fltr_act == ICE_FWD_TO_QGRP)) {
-               f_info->lb_en = true;
-               if (!(hw->evb_veb && f_info->lkup_type == ICE_SW_LKUP_MAC &&
-                     is_unicast_ether_addr(f_info->l_data.mac.mac_addr)))
-                       f_info->lan_en = true;
+       fi->lb_en = false;
+       fi->lan_en = false;
+       if ((fi->flag & ICE_FLTR_TX) &&
+           (fi->fltr_act == ICE_FWD_TO_VSI ||
+            fi->fltr_act == ICE_FWD_TO_VSI_LIST ||
+            fi->fltr_act == ICE_FWD_TO_Q ||
+            fi->fltr_act == ICE_FWD_TO_QGRP)) {
+               fi->lb_en = true;
+               /* Do not set lan_en to TRUE if
+                * 1. The switch is a VEB AND
+                * 2
+                * 2.1 The lookup is MAC with unicast addr for MAC, OR
+                * 2.2 The lookup is MAC_VLAN with unicast addr for MAC
+                *
+                * In all other cases, the LAN enable has to be set to true.
+                */
+               if (!(hw->evb_veb &&
+                     ((fi->lkup_type == ICE_SW_LKUP_MAC &&
+                       is_unicast_ether_addr(fi->l_data.mac.mac_addr)) ||
+                      (fi->lkup_type == ICE_SW_LKUP_MAC_VLAN &&
+                       is_unicast_ether_addr(fi->l_data.mac_vlan.mac_addr)))))
+                       fi->lan_en = true;
        }
 }
 
@@ -817,7 +827,7 @@ ice_add_marker_act(struct ice_hw *hw, struct ice_fltr_mgmt_list_entry *m_ent,
        /* Create two back-to-back switch rules and submit them to the HW using
         * one memory buffer:
         *    1. Large Action
-        *    2. Look up tx rx
+        *    2. Look up Tx Rx
         */
        lg_act_size = (u16)ICE_SW_RULE_LG_ACT_SIZE(num_lg_acts);
        rules_size = lg_act_size + ICE_SW_RULE_RX_TX_ETH_HDR_SIZE;
@@ -861,7 +871,7 @@ ice_add_marker_act(struct ice_hw *hw, struct ice_fltr_mgmt_list_entry *m_ent,
 
        lg_act->pdata.lg_act.act[2] = cpu_to_le32(act);
 
-       /* call the fill switch rule to fill the lookup tx rx structure */
+       /* call the fill switch rule to fill the lookup Tx Rx structure */
        ice_fill_sw_rule(hw, &m_ent->fltr_info, rx_tx,
                         ice_aqc_opc_update_sw_rules);
 
@@ -1158,8 +1168,8 @@ enum ice_status ice_update_sw_rule_bridge_mode(struct ice_hw *hw)
  * Call AQ command to add or update previously created VSI list with new VSI.
  *
  * Helper function to do book keeping associated with adding filter information
- * The algorithm to do the booking keeping is described below :
- * When a VSI needs to subscribe to a given filterMAC/VLAN/Ethtype etc.)
+ * The algorithm to do the book keeping is described below :
+ * When a VSI needs to subscribe to a given filter (MAC/VLAN/Ethtype etc.)
  *     if only one VSI has been added till now
  *             Allocate a new VSI list and add two VSIs
  *             to this list using switch rule command
@@ -1237,6 +1247,9 @@ ice_add_update_vsi_list(struct ice_hw *hw,
                u16 vsi_handle = new_fltr->vsi_handle;
                enum ice_adminq_opc opcode;
 
+               if (!m_entry->vsi_list_info)
+                       return ICE_ERR_CFG;
+
                /* A rule already exists with the new VSI being added */
                if (test_bit(vsi_handle, m_entry->vsi_list_info->vsi_map))
                        return 0;
@@ -1853,7 +1866,7 @@ ice_add_vlan_internal(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry)
                tmp_fltr.fwd_id.vsi_list_id = vsi_list_id;
                tmp_fltr.fltr_act = ICE_FWD_TO_VSI_LIST;
                /* Update the previous switch rule to a new VSI list which
-                * includes current VSI thats requested
+                * includes current VSI that is requested
                 */
                status = ice_update_pkt_fwd_rule(hw, &tmp_fltr);
                if (status)
index fe5bbab..49fc380 100644 (file)
@@ -219,7 +219,7 @@ static bool ice_clean_tx_irq(struct ice_vsi *vsi, struct ice_ring *tx_ring,
 
 /**
  * ice_setup_tx_ring - Allocate the Tx descriptors
- * @tx_ring: the tx ring to set up
+ * @tx_ring: the Tx ring to set up
  *
  * Return 0 on success, negative on error
  */
@@ -324,7 +324,7 @@ void ice_free_rx_ring(struct ice_ring *rx_ring)
 
 /**
  * ice_setup_rx_ring - Allocate the Rx descriptors
- * @rx_ring: the rx ring to set up
+ * @rx_ring: the Rx ring to set up
  *
  * Return 0 on success, negative on error
  */
@@ -377,7 +377,7 @@ static void ice_release_rx_desc(struct ice_ring *rx_ring, u32 val)
        rx_ring->next_to_alloc = val;
 
        /* Force memory writes to complete before letting h/w
-        * know there are new descriptors to fetch.  (Only
+        * know there are new descriptors to fetch. (Only
         * applicable for weak-ordered memory model archs,
         * such as IA-64).
         */
@@ -586,7 +586,7 @@ static bool ice_add_rx_frag(struct ice_rx_buf *rx_buf,
 
 /**
  * ice_reuse_rx_page - page flip buffer and store it back on the ring
- * @rx_ring: rx descriptor ring to store buffers on
+ * @rx_ring: Rx descriptor ring to store buffers on
  * @old_buf: donor buffer to have page reused
  *
  * Synchronizes page for reuse by the adapter
@@ -609,7 +609,7 @@ static void ice_reuse_rx_page(struct ice_ring *rx_ring,
 
 /**
  * ice_fetch_rx_buf - Allocate skb and populate it
- * @rx_ring: rx descriptor ring to transact packets on
+ * @rx_ring: Rx descriptor ring to transact packets on
  * @rx_desc: descriptor containing info written by hardware
  *
  * This function allocates an skb on the fly, and populates it with the page
@@ -686,7 +686,7 @@ static struct sk_buff *ice_fetch_rx_buf(struct ice_ring *rx_ring,
  * ice_pull_tail - ice specific version of skb_pull_tail
  * @skb: pointer to current skb being adjusted
  *
- * This function is an ice specific version of __pskb_pull_tail.  The
+ * This function is an ice specific version of __pskb_pull_tail. The
  * main difference between this version and the original function is that
  * this function can make several assumptions about the state of things
  * that allow for significant optimizations versus the standard function.
@@ -768,7 +768,7 @@ static bool ice_test_staterr(union ice_32b_rx_flex_desc *rx_desc,
  * @rx_desc: Rx descriptor for current buffer
  * @skb: Current socket buffer containing buffer in progress
  *
- * This function updates next to clean.  If the buffer is an EOP buffer
+ * This function updates next to clean. If the buffer is an EOP buffer
  * this function exits returning false, otherwise it will place the
  * sk_buff in the next buffer to be chained and return true indicating
  * that this is in fact a non-EOP buffer.
@@ -904,7 +904,7 @@ checksum_fail:
 
 /**
  * ice_process_skb_fields - Populate skb header fields from Rx descriptor
- * @rx_ring: rx descriptor ring packet is being transacted on
+ * @rx_ring: Rx descriptor ring packet is being transacted on
  * @rx_desc: pointer to the EOP Rx descriptor
  * @skb: pointer to current skb being populated
  * @ptype: the packet type decoded by hardware
@@ -927,7 +927,7 @@ static void ice_process_skb_fields(struct ice_ring *rx_ring,
 
 /**
  * ice_receive_skb - Send a completed packet up the stack
- * @rx_ring: rx ring in play
+ * @rx_ring: Rx ring in play
  * @skb: packet to send up
  * @vlan_tag: vlan tag for packet
  *
@@ -946,11 +946,11 @@ static void ice_receive_skb(struct ice_ring *rx_ring, struct sk_buff *skb,
 
 /**
  * ice_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf
- * @rx_ring: rx descriptor ring to transact packets on
+ * @rx_ring: Rx descriptor ring to transact packets on
  * @budget: Total limit on number of packets to process
  *
  * This function provides a "bounce buffer" approach to Rx interrupt
- * processing.  The advantage to this is that on systems that have
+ * processing. The advantage to this is that on systems that have
  * expensive overhead for IOMMU access this provides a means of avoiding
  * it by maintaining the mapping of the page to the system.
  *
@@ -1103,11 +1103,14 @@ int ice_napi_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
 
-       /* Work is done so exit the polling mode and re-enable the interrupt */
-       napi_complete_done(napi, work_done);
-       if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
-               ice_irq_dynamic_ena(&vsi->back->hw, vsi, q_vector);
-       return 0;
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
+                       ice_irq_dynamic_ena(&vsi->back->hw, vsi, q_vector);
+
+       return min(work_done, budget - 1);
 }
 
 /* helper function for building cmd/type/offset */
@@ -1122,7 +1125,7 @@ build_ctob(u64 td_cmd, u64 td_offset, unsigned int size, u64 td_tag)
 }
 
 /**
- * __ice_maybe_stop_tx - 2nd level check for tx stop conditions
+ * __ice_maybe_stop_tx - 2nd level check for Tx stop conditions
  * @tx_ring: the ring to be checked
  * @size: the size buffer we want to assure is available
  *
@@ -1145,7 +1148,7 @@ static int __ice_maybe_stop_tx(struct ice_ring *tx_ring, unsigned int size)
 }
 
 /**
- * ice_maybe_stop_tx - 1st level check for tx stop conditions
+ * ice_maybe_stop_tx - 1st level check for Tx stop conditions
  * @tx_ring: the ring to be checked
  * @size:    the size buffer we want to assure is available
  *
@@ -1155,6 +1158,7 @@ static int ice_maybe_stop_tx(struct ice_ring *tx_ring, unsigned int size)
 {
        if (likely(ICE_DESC_UNUSED(tx_ring) >= size))
                return 0;
+
        return __ice_maybe_stop_tx(tx_ring, size);
 }
 
@@ -1552,7 +1556,7 @@ int ice_tso(struct ice_tx_buf *first, struct ice_tx_offload_params *off)
  * Finally, we add one to round up. Because 256 isn't an exact multiple of
  * 3, we'll underestimate near each multiple of 12K. This is actually more
  * accurate as we have 4K - 1 of wiggle room that we can fit into the last
- * segment.  For our purposes this is accurate out to 1M which is orders of
+ * segment. For our purposes this is accurate out to 1M which is orders of
  * magnitude greater than our largest possible GSO size.
  *
  * This would then be implemented as:
@@ -1568,7 +1572,7 @@ static unsigned int ice_txd_use_count(unsigned int size)
 }
 
 /**
- * ice_xmit_desc_count - calculate number of tx descriptors needed
+ * ice_xmit_desc_count - calculate number of Tx descriptors needed
  * @skb: send buffer
  *
  * Returns number of data descriptors needed for this skb.
@@ -1620,7 +1624,7 @@ static bool __ice_chk_linearize(struct sk_buff *skb)
        nr_frags -= ICE_MAX_BUF_TXD - 2;
        frag = &skb_shinfo(skb)->frags[0];
 
-       /* Initialize size to the negative value of gso_size minus 1.  We
+       /* Initialize size to the negative value of gso_size minus 1. We
         * use this as the worst case scenerio in which the frag ahead
         * of us only provides one byte which is why we are limited to 6
         * descriptors for a single transmit as the header and previous
index f4dbc81..0ea4281 100644 (file)
@@ -124,6 +124,8 @@ struct ice_phy_info {
 
 /* Common HW capabilities for SW use */
 struct ice_hw_common_caps {
+       u32 valid_functions;
+
        /* TX/RX queues */
        u16 num_rxq;            /* Number/Total RX queues */
        u16 rxq_first_id;       /* First queue ID for RX queues */
@@ -150,7 +152,7 @@ struct ice_hw_func_caps {
        struct ice_hw_common_caps common_cap;
        u32 num_allocd_vfs;             /* Number of allocated VFs */
        u32 vf_base_id;                 /* Logical ID of the first VF */
-       u32 guaranteed_num_vsi;
+       u32 guar_num_vsi;
 };
 
 /* Device wide capabilities */
index e71065f..05ff4f9 100644 (file)
@@ -156,8 +156,6 @@ static void ice_free_vf_res(struct ice_vf *vf)
        clear_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states);
 }
 
-/***********************enable_vf routines*****************************/
-
 /**
  * ice_dis_vf_mappings
  * @vf: pointer to the VF structure
@@ -215,6 +213,15 @@ void ice_free_vfs(struct ice_pf *pf)
        while (test_and_set_bit(__ICE_VF_DIS, pf->state))
                usleep_range(1000, 2000);
 
+       /* Disable IOV before freeing resources. This lets any VF drivers
+        * running in the host get themselves cleaned up before we yank
+        * the carpet out from underneath their feet.
+        */
+       if (!pci_vfs_assigned(pf->pdev))
+               pci_disable_sriov(pf->pdev);
+       else
+               dev_warn(&pf->pdev->dev, "VFs are assigned - not disabling SR-IOV\n");
+
        /* Avoid wait time by stopping all VFs at the same time */
        for (i = 0; i < pf->num_alloc_vfs; i++) {
                if (!test_bit(ICE_VF_STATE_ENA, pf->vf[i].vf_states))
@@ -228,15 +235,6 @@ void ice_free_vfs(struct ice_pf *pf)
                clear_bit(ICE_VF_STATE_ENA, pf->vf[i].vf_states);
        }
 
-       /* Disable IOV before freeing resources. This lets any VF drivers
-        * running in the host get themselves cleaned up before we yank
-        * the carpet out from underneath their feet.
-        */
-       if (!pci_vfs_assigned(pf->pdev))
-               pci_disable_sriov(pf->pdev);
-       else
-               dev_warn(&pf->pdev->dev, "VFs are assigned - not disabling SR-IOV\n");
-
        tmp = pf->num_alloc_vfs;
        pf->num_vf_qps = 0;
        pf->num_alloc_vfs = 0;
@@ -454,7 +452,7 @@ static int ice_alloc_vsi_res(struct ice_vf *vf)
 
        /* Clear this bit after VF initialization since we shouldn't reclaim
         * and reassign interrupts for synchronous or asynchronous VFR events.
-        * We don't want to reconfigure interrupts since AVF driver doesn't
+        * We dont want to reconfigure interrupts since AVF driver doesn't
         * expect vector assignment to be changed unless there is a request for
         * more vectors.
         */
@@ -1105,7 +1103,7 @@ int ice_sriov_configure(struct pci_dev *pdev, int num_vfs)
  * ice_process_vflr_event - Free VF resources via IRQ calls
  * @pf: pointer to the PF structure
  *
- * called from the VLFR IRQ handler to
+ * called from the VFLR IRQ handler to
  * free up VF resources and state variables
  */
 void ice_process_vflr_event(struct ice_pf *pf)
@@ -1764,7 +1762,7 @@ static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
                /* copy Tx queue info from VF into VSI */
                vsi->tx_rings[i]->dma = qpi->txq.dma_ring_addr;
                vsi->tx_rings[i]->count = qpi->txq.ring_len;
-               /* copy Rx queue info from VF into vsi */
+               /* copy Rx queue info from VF into VSI */
                vsi->rx_rings[i]->dma = qpi->rxq.dma_ring_addr;
                vsi->rx_rings[i]->count = qpi->rxq.ring_len;
                if (qpi->rxq.databuffer_size > ((16 * 1024) - 128)) {
@@ -1830,7 +1828,7 @@ static bool ice_can_vf_change_mac(struct ice_vf *vf)
  * @msg: pointer to the msg buffer
  * @set: true if mac filters are being set, false otherwise
  *
- * add guest mac address filter
+ * add guest MAC address filter
  */
 static int
 ice_vc_handle_mac_addr_msg(struct ice_vf *vf, u8 *msg, bool set)
@@ -1968,9 +1966,9 @@ static int ice_vc_del_mac_addr_msg(struct ice_vf *vf, u8 *msg)
  * @msg: pointer to the msg buffer
  *
  * VFs get a default number of queues but can use this message to request a
- * different number.  If the request is successful, PF will reset the VF and
+ * different number. If the request is successful, PF will reset the VF and
  * return 0. If unsuccessful, PF will send message informing VF of number of
- * available queue pairs via virtchnl message response to VF.
+ * available queue pairs via virtchnl message response to vf.
  */
 static int ice_vc_request_qs_msg(struct ice_vf *vf, u8 *msg)
 {
@@ -1991,7 +1989,7 @@ static int ice_vc_request_qs_msg(struct ice_vf *vf, u8 *msg)
        tx_rx_queue_left = min_t(int, pf->q_left_tx, pf->q_left_rx);
        if (req_queues <= 0) {
                dev_err(&pf->pdev->dev,
-                       "VF %d tried to request %d queues.  Ignoring.\n",
+                       "VF %d tried to request %d queues. Ignoring.\n",
                        vf->vf_id, req_queues);
        } else if (req_queues > ICE_MAX_QS_PER_VF) {
                dev_err(&pf->pdev->dev,
index 10131e0..01470a8 100644 (file)
@@ -70,7 +70,7 @@ struct ice_vf {
        u8 spoofchk;
        u16 num_mac;
        u16 num_vlan;
-       u8 num_req_qs;          /* num of queue pairs requested by VF */
+       u8 num_req_qs;                  /* num of queue pairs requested by VF */
 };
 
 #ifdef CONFIG_PCI_IOV
index 8a28f33..01fcfc6 100644 (file)
 
 #define I210_RXPBSIZE_DEFAULT          0x000000A2 /* RXPBSIZE default */
 #define I210_RXPBSIZE_MASK             0x0000003F
+#define I210_RXPBSIZE_PB_30KB          0x0000001E
 #define I210_RXPBSIZE_PB_32KB          0x00000020
 #define I210_TXPBSIZE_DEFAULT          0x04000014 /* TXPBSIZE default */
 #define I210_TXPBSIZE_MASK             0xC0FFFFFF
index c54ebed..c393cb2 100644 (file)
@@ -842,6 +842,7 @@ s32 igb_pll_workaround_i210(struct e1000_hw *hw)
                nvm_word = E1000_INVM_DEFAULT_AL;
        tmp_nvm = nvm_word | E1000_INVM_PLL_WO_VAL;
        igb_write_phy_reg_82580(hw, I347AT4_PAGE_SELECT, E1000_PHY_PLL_FREQ_PAGE);
+       phy_word = E1000_PHY_PLL_UNCONF;
        for (i = 0; i < E1000_MAX_PLL_TRIES; i++) {
                /* check current state directly from internal PHY */
                igb_read_phy_reg_82580(hw, E1000_PHY_PLL_FREQ_REG, &phy_word);
index ca54e26..fe1592a 100644 (file)
@@ -515,7 +515,7 @@ struct igb_adapter {
        /* OS defined structs */
        struct pci_dev *pdev;
 
-       spinlock_t stats64_lock;
+       struct mutex stats64_lock;
        struct rtnl_link_stats64 stats64;
 
        /* structs defined in e1000_hw.h */
index c576710..7426060 100644 (file)
@@ -2295,7 +2295,7 @@ static void igb_get_ethtool_stats(struct net_device *netdev,
        int i, j;
        char *p;
 
-       spin_lock(&adapter->stats64_lock);
+       mutex_lock(&adapter->stats64_lock);
        igb_update_stats(adapter);
 
        for (i = 0; i < IGB_GLOBAL_STATS_LEN; i++) {
@@ -2338,7 +2338,7 @@ static void igb_get_ethtool_stats(struct net_device *netdev,
                } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start));
                i += IGB_RX_QUEUE_STATS_LEN;
        }
-       spin_unlock(&adapter->stats64_lock);
+       mutex_unlock(&adapter->stats64_lock);
 }
 
 static void igb_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
index 4584ebc..87bdf16 100644 (file)
@@ -1850,13 +1850,12 @@ static void igb_config_tx_modes(struct igb_adapter *adapter, int queue)
         * configuration' in respect to these parameters.
         */
 
-       netdev_dbg(netdev, "Qav Tx mode: cbs %s, launchtime %s, queue %d \
-                           idleslope %d sendslope %d hiCredit %d \
-                           locredit %d\n",
-                  (ring->cbs_enable) ? "enabled" : "disabled",
-                  (ring->launchtime_enable) ? "enabled" : "disabled", queue,
-                  ring->idleslope, ring->sendslope, ring->hicredit,
-                  ring->locredit);
+       netdev_dbg(netdev, "Qav Tx mode: cbs %s, launchtime %s, queue %d idleslope %d sendslope %d hiCredit %d locredit %d\n",
+                  ring->cbs_enable ? "enabled" : "disabled",
+                  ring->launchtime_enable ? "enabled" : "disabled",
+                  queue,
+                  ring->idleslope, ring->sendslope,
+                  ring->hicredit, ring->locredit);
 }
 
 static int igb_save_txtime_params(struct igb_adapter *adapter, int queue,
@@ -1935,7 +1934,7 @@ static void igb_setup_tx_mode(struct igb_adapter *adapter)
 
                val = rd32(E1000_RXPBS);
                val &= ~I210_RXPBSIZE_MASK;
-               val |= I210_RXPBSIZE_PB_32KB;
+               val |= I210_RXPBSIZE_PB_30KB;
                wr32(E1000_RXPBS, val);
 
                /* Section 8.12.9 states that MAX_TPKT_SIZE from DTXMXPKTSZ
@@ -2204,9 +2203,9 @@ void igb_down(struct igb_adapter *adapter)
        del_timer_sync(&adapter->phy_info_timer);
 
        /* record the stats before reset*/
-       spin_lock(&adapter->stats64_lock);
+       mutex_lock(&adapter->stats64_lock);
        igb_update_stats(adapter);
-       spin_unlock(&adapter->stats64_lock);
+       mutex_unlock(&adapter->stats64_lock);
 
        adapter->link_speed = 0;
        adapter->link_duplex = 0;
@@ -3841,7 +3840,7 @@ static int igb_sw_init(struct igb_adapter *adapter)
        adapter->min_frame_size = ETH_ZLEN + ETH_FCS_LEN;
 
        spin_lock_init(&adapter->nfc_lock);
-       spin_lock_init(&adapter->stats64_lock);
+       mutex_init(&adapter->stats64_lock);
 #ifdef CONFIG_PCI_IOV
        switch (hw->mac.type) {
        case e1000_82576:
@@ -5407,9 +5406,9 @@ no_wait:
                }
        }
 
-       spin_lock(&adapter->stats64_lock);
+       mutex_lock(&adapter->stats64_lock);
        igb_update_stats(adapter);
-       spin_unlock(&adapter->stats64_lock);
+       mutex_unlock(&adapter->stats64_lock);
 
        for (i = 0; i < adapter->num_tx_queues; i++) {
                struct igb_ring *tx_ring = adapter->tx_ring[i];
@@ -6236,10 +6235,10 @@ static void igb_get_stats64(struct net_device *netdev,
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
 
-       spin_lock(&adapter->stats64_lock);
+       mutex_lock(&adapter->stats64_lock);
        igb_update_stats(adapter);
        memcpy(stats, &adapter->stats64, sizeof(*stats));
-       spin_unlock(&adapter->stats64_lock);
+       mutex_unlock(&adapter->stats64_lock);
 }
 
 /**
@@ -7753,11 +7752,13 @@ static int igb_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
 
-       /* If not enough Rx work done, exit the polling mode */
-       napi_complete_done(napi, work_done);
-       igb_ring_irq_enable(q_vector);
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               igb_ring_irq_enable(q_vector);
 
-       return 0;
+       return min(work_done, budget - 1);
 }
 
 /**
@@ -8770,9 +8771,11 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake,
        rtnl_unlock();
 
 #ifdef CONFIG_PM
-       retval = pci_save_state(pdev);
-       if (retval)
-               return retval;
+       if (!runtime) {
+               retval = pci_save_state(pdev);
+               if (retval)
+                       return retval;
+       }
 #endif
 
        status = rd32(E1000_STATUS);
index 820d49e..4eab83f 100644 (file)
@@ -1186,10 +1186,13 @@ static int igbvf_poll(struct napi_struct *napi, int budget)
 
        igbvf_clean_rx_irq(adapter, &work_done, budget);
 
-       /* If not enough Rx work done, exit the polling mode */
-       if (work_done < budget) {
-               napi_complete_done(napi, work_done);
+       if (work_done == budget)
+               return budget;
 
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done))) {
                if (adapter->requested_itr & 3)
                        igbvf_set_itr(adapter);
 
index 3b00b10..b1039dd 100644 (file)
@@ -11,8 +11,6 @@
 #include <linux/ethtool.h>
 #include <linux/sctp.h>
 
-#define IGC_ERR(args...) pr_err("igc: " args)
-
 #include "igc_hw.h"
 
 /* main */
index d002055..f201830 100644 (file)
@@ -2852,11 +2852,13 @@ static int igc_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
 
-       /* If not enough Rx work done, exit the polling mode */
-       napi_complete_done(napi, work_done);
-       igc_ring_irq_enable(q_vector);
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               igc_ring_irq_enable(q_vector);
 
-       return 0;
+       return min(work_done, budget - 1);
 }
 
 /**
@@ -3533,7 +3535,7 @@ static int igc_probe(struct pci_dev *pdev,
                        err = dma_set_coherent_mask(&pdev->dev,
                                                    DMA_BIT_MASK(32));
                        if (err) {
-                               IGC_ERR("Wrong DMA configuration, aborting\n");
+                               dev_err(&pdev->dev, "igc: Wrong DMA config\n");
                                goto err_dma;
                        }
                }
index 143bdd5..08d85e3 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/aer.h>
 #include <linux/if_vlan.h>
 #include <linux/jiffies.h>
+#include <linux/phy.h>
 
 #include <linux/timecounter.h>
 #include <linux/net_tstamp.h>
@@ -561,6 +562,7 @@ struct ixgbe_adapter {
        struct net_device *netdev;
        struct bpf_prog *xdp_prog;
        struct pci_dev *pdev;
+       struct mii_bus *mii_bus;
 
        unsigned long state;
 
index 4d77f42..ff85ce5 100644 (file)
@@ -1065,11 +1065,13 @@ int ixgbe_ipsec_tx(struct ixgbe_ring *tx_ring,
        struct ixgbe_adapter *adapter = netdev_priv(tx_ring->netdev);
        struct ixgbe_ipsec *ipsec = adapter->ipsec;
        struct xfrm_state *xs;
+       struct sec_path *sp;
        struct tx_sa *tsa;
 
-       if (unlikely(!first->skb->sp->len)) {
+       sp = skb_sec_path(first->skb);
+       if (unlikely(!sp->len)) {
                netdev_err(tx_ring->netdev, "%s: no xfrm state len = %d\n",
-                          __func__, first->skb->sp->len);
+                          __func__, sp->len);
                return 0;
        }
 
@@ -1159,6 +1161,7 @@ void ixgbe_ipsec_rx(struct ixgbe_ring *rx_ring,
        struct xfrm_state *xs = NULL;
        struct ipv6hdr *ip6 = NULL;
        struct iphdr *ip4 = NULL;
+       struct sec_path *sp;
        void *daddr;
        __be32 spi;
        u8 *c_hdr;
@@ -1198,12 +1201,12 @@ void ixgbe_ipsec_rx(struct ixgbe_ring *rx_ring,
        if (unlikely(!xs))
                return;
 
-       skb->sp = secpath_dup(skb->sp);
-       if (unlikely(!skb->sp))
+       sp = secpath_set(skb);
+       if (unlikely(!sp))
                return;
 
-       skb->sp->xvec[skb->sp->len++] = xs;
-       skb->sp->olen++;
+       sp->xvec[sp->len++] = xs;
+       sp->olen++;
        xo = xfrm_offload(skb);
        xo->flags = CRYPTO_DONE;
        xo->status = CRYPTO_SUCCESS;
index cfb8368..daff818 100644 (file)
@@ -39,6 +39,7 @@
 #include "ixgbe.h"
 #include "ixgbe_common.h"
 #include "ixgbe_dcb_82599.h"
+#include "ixgbe_phy.h"
 #include "ixgbe_sriov.h"
 #include "ixgbe_model.h"
 #include "ixgbe_txrx_common.h"
@@ -6077,9 +6078,9 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
        /* Disable Rx */
        ixgbe_disable_rx(adapter);
 
-       /* synchronize_sched() needed for pending XDP buffers to drain */
+       /* synchronize_rcu() needed for pending XDP buffers to drain */
        if (adapter->xdp_ring[0])
-               synchronize_sched();
+               synchronize_rcu();
 
        ixgbe_irq_disable(adapter);
 
@@ -8695,7 +8696,8 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
 #endif /* IXGBE_FCOE */
 
 #ifdef CONFIG_IXGBE_IPSEC
-       if (skb->sp && !ixgbe_ipsec_tx(tx_ring, first, &ipsec_tx))
+       if (secpath_exists(skb) &&
+           !ixgbe_ipsec_tx(tx_ring, first, &ipsec_tx))
                goto out_drop;
 #endif
        tso = ixgbe_tso(tx_ring, first, &hdr_len, &ipsec_tx);
@@ -8789,6 +8791,15 @@ ixgbe_mdio_read(struct net_device *netdev, int prtad, int devad, u16 addr)
        u16 value;
        int rc;
 
+       if (adapter->mii_bus) {
+               int regnum = addr;
+
+               if (devad != MDIO_DEVAD_NONE)
+                       regnum |= (devad << 16) | MII_ADDR_C45;
+
+               return mdiobus_read(adapter->mii_bus, prtad, regnum);
+       }
+
        if (prtad != hw->phy.mdio.prtad)
                return -EINVAL;
        rc = hw->phy.ops.read_reg(hw, addr, devad, &value);
@@ -8803,6 +8814,15 @@ static int ixgbe_mdio_write(struct net_device *netdev, int prtad, int devad,
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
 
+       if (adapter->mii_bus) {
+               int regnum = addr;
+
+               if (devad != MDIO_DEVAD_NONE)
+                       regnum |= (devad << 16) | MII_ADDR_C45;
+
+               return mdiobus_write(adapter->mii_bus, prtad, regnum, value);
+       }
+
        if (prtad != hw->phy.mdio.prtad)
                return -EINVAL;
        return hw->phy.ops.write_reg(hw, addr, devad, value);
@@ -9979,7 +9999,8 @@ static int ixgbe_configure_bridge_mode(struct ixgbe_adapter *adapter,
 }
 
 static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
-                                   struct nlmsghdr *nlh, u16 flags)
+                                   struct nlmsghdr *nlh, u16 flags,
+                                   struct netlink_ext_ack *extack)
 {
        struct ixgbe_adapter *adapter = netdev_priv(dev);
        struct nlattr *attr, *br_spec;
@@ -10191,7 +10212,7 @@ ixgbe_features_check(struct sk_buff *skb, struct net_device *dev,
         */
        if (skb->encapsulation && !(features & NETIF_F_TSO_MANGLEID)) {
 #ifdef CONFIG_IXGBE_IPSEC
-               if (!skb->sp)
+               if (!secpath_exists(skb))
 #endif
                        features &= ~NETIF_F_TSO;
        }
@@ -10476,7 +10497,7 @@ void ixgbe_txrx_ring_disable(struct ixgbe_adapter *adapter, int ring)
        ixgbe_disable_rxr_hw(adapter, rx_ring);
 
        if (xdp_ring)
-               synchronize_sched();
+               synchronize_rcu();
 
        /* Rx/Tx/XDP Tx share the same napi context. */
        napi_disable(&rx_ring->q_vector->napi);
@@ -11120,6 +11141,8 @@ skip_sriov:
                        IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL,
                        true);
 
+       ixgbe_mii_bus_init(hw);
+
        return 0;
 
 err_register:
@@ -11170,6 +11193,8 @@ static void ixgbe_remove(struct pci_dev *pdev)
        set_bit(__IXGBE_REMOVING, &adapter->state);
        cancel_work_sync(&adapter->service_task);
 
+       if (adapter->mii_bus)
+               mdiobus_unregister(adapter->mii_bus);
 
 #ifdef CONFIG_IXGBE_DCA
        if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) {
index 919a7af..cc4907f 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/pci.h>
 #include <linux/delay.h>
+#include <linux/iopoll.h>
 #include <linux/sched.h>
 
 #include "ixgbe.h"
@@ -658,6 +659,304 @@ s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
        return status;
 }
 
+#define IXGBE_HW_READ_REG(addr) IXGBE_READ_REG(hw, addr)
+
+/**
+ *  ixgbe_msca_cmd - Write the command register and poll for completion/timeout
+ *  @hw: pointer to hardware structure
+ *  @cmd: command register value to write
+ **/
+static s32 ixgbe_msca_cmd(struct ixgbe_hw *hw, u32 cmd)
+{
+       IXGBE_WRITE_REG(hw, IXGBE_MSCA, cmd);
+
+       return readx_poll_timeout(IXGBE_HW_READ_REG, IXGBE_MSCA, cmd,
+                                 !(cmd & IXGBE_MSCA_MDI_COMMAND), 10,
+                                 10 * IXGBE_MDIO_COMMAND_TIMEOUT);
+}
+
+/**
+ *  ixgbe_mii_bus_read_generic - Read a clause 22/45 register with gssr flags
+ *  @hw: pointer to hardware structure
+ *  @addr: address
+ *  @regnum: register number
+ *  @gssr: semaphore flags to acquire
+ **/
+static s32 ixgbe_mii_bus_read_generic(struct ixgbe_hw *hw, int addr,
+                                     int regnum, u32 gssr)
+{
+       u32 hwaddr, cmd;
+       s32 data;
+
+       if (hw->mac.ops.acquire_swfw_sync(hw, gssr))
+               return -EBUSY;
+
+       hwaddr = addr << IXGBE_MSCA_PHY_ADDR_SHIFT;
+       if (regnum & MII_ADDR_C45) {
+               hwaddr |= regnum & GENMASK(21, 0);
+               cmd = hwaddr | IXGBE_MSCA_ADDR_CYCLE | IXGBE_MSCA_MDI_COMMAND;
+       } else {
+               hwaddr |= (regnum & GENMASK(5, 0)) << IXGBE_MSCA_DEV_TYPE_SHIFT;
+               cmd = hwaddr | IXGBE_MSCA_OLD_PROTOCOL |
+                       IXGBE_MSCA_READ_AUTOINC | IXGBE_MSCA_MDI_COMMAND;
+       }
+
+       data = ixgbe_msca_cmd(hw, cmd);
+       if (data < 0)
+               goto mii_bus_read_done;
+
+       /* For a clause 45 access the address cycle just completed, we still
+        * need to do the read command, otherwise just get the data
+        */
+       if (!(regnum & MII_ADDR_C45))
+               goto do_mii_bus_read;
+
+       cmd = hwaddr | IXGBE_MSCA_READ | IXGBE_MSCA_MDI_COMMAND;
+       data = ixgbe_msca_cmd(hw, cmd);
+       if (data < 0)
+               goto mii_bus_read_done;
+
+do_mii_bus_read:
+       data = IXGBE_READ_REG(hw, IXGBE_MSRWD);
+       data = (data >> IXGBE_MSRWD_READ_DATA_SHIFT) & GENMASK(16, 0);
+
+mii_bus_read_done:
+       hw->mac.ops.release_swfw_sync(hw, gssr);
+       return data;
+}
+
+/**
+ *  ixgbe_mii_bus_write_generic - Write a clause 22/45 register with gssr flags
+ *  @hw: pointer to hardware structure
+ *  @addr: address
+ *  @regnum: register number
+ *  @val: value to write
+ *  @gssr: semaphore flags to acquire
+ **/
+static s32 ixgbe_mii_bus_write_generic(struct ixgbe_hw *hw, int addr,
+                                      int regnum, u16 val, u32 gssr)
+{
+       u32 hwaddr, cmd;
+       s32 err;
+
+       if (hw->mac.ops.acquire_swfw_sync(hw, gssr))
+               return -EBUSY;
+
+       IXGBE_WRITE_REG(hw, IXGBE_MSRWD, (u32)val);
+
+       hwaddr = addr << IXGBE_MSCA_PHY_ADDR_SHIFT;
+       if (regnum & MII_ADDR_C45) {
+               hwaddr |= regnum & GENMASK(21, 0);
+               cmd = hwaddr | IXGBE_MSCA_ADDR_CYCLE | IXGBE_MSCA_MDI_COMMAND;
+       } else {
+               hwaddr |= (regnum & GENMASK(5, 0)) << IXGBE_MSCA_DEV_TYPE_SHIFT;
+               cmd = hwaddr | IXGBE_MSCA_OLD_PROTOCOL | IXGBE_MSCA_WRITE |
+                       IXGBE_MSCA_MDI_COMMAND;
+       }
+
+       /* For clause 45 this is an address cycle, for clause 22 this is the
+        * entire transaction
+        */
+       err = ixgbe_msca_cmd(hw, cmd);
+       if (err < 0 || !(regnum & MII_ADDR_C45))
+               goto mii_bus_write_done;
+
+       cmd = hwaddr | IXGBE_MSCA_WRITE | IXGBE_MSCA_MDI_COMMAND;
+       err = ixgbe_msca_cmd(hw, cmd);
+
+mii_bus_write_done:
+       hw->mac.ops.release_swfw_sync(hw, gssr);
+       return err;
+}
+
+/**
+ *  ixgbe_mii_bus_read - Read a clause 22/45 register
+ *  @hw: pointer to hardware structure
+ *  @addr: address
+ *  @regnum: register number
+ **/
+static s32 ixgbe_mii_bus_read(struct mii_bus *bus, int addr, int regnum)
+{
+       struct ixgbe_adapter *adapter = bus->priv;
+       struct ixgbe_hw *hw = &adapter->hw;
+       u32 gssr = hw->phy.phy_semaphore_mask;
+
+       return ixgbe_mii_bus_read_generic(hw, addr, regnum, gssr);
+}
+
+/**
+ *  ixgbe_mii_bus_write - Write a clause 22/45 register
+ *  @hw: pointer to hardware structure
+ *  @addr: address
+ *  @regnum: register number
+ *  @val: value to write
+ **/
+static s32 ixgbe_mii_bus_write(struct mii_bus *bus, int addr, int regnum,
+                              u16 val)
+{
+       struct ixgbe_adapter *adapter = bus->priv;
+       struct ixgbe_hw *hw = &adapter->hw;
+       u32 gssr = hw->phy.phy_semaphore_mask;
+
+       return ixgbe_mii_bus_write_generic(hw, addr, regnum, val, gssr);
+}
+
+/**
+ *  ixgbe_x550em_a_mii_bus_read - Read a clause 22/45 register on x550em_a
+ *  @hw: pointer to hardware structure
+ *  @addr: address
+ *  @regnum: register number
+ **/
+static s32 ixgbe_x550em_a_mii_bus_read(struct mii_bus *bus, int addr,
+                                      int regnum)
+{
+       struct ixgbe_adapter *adapter = bus->priv;
+       struct ixgbe_hw *hw = &adapter->hw;
+       u32 gssr = hw->phy.phy_semaphore_mask;
+
+       gssr |= IXGBE_GSSR_TOKEN_SM | IXGBE_GSSR_PHY0_SM;
+       return ixgbe_mii_bus_read_generic(hw, addr, regnum, gssr);
+}
+
+/**
+ *  ixgbe_x550em_a_mii_bus_write - Write a clause 22/45 register on x550em_a
+ *  @hw: pointer to hardware structure
+ *  @addr: address
+ *  @regnum: register number
+ *  @val: value to write
+ **/
+static s32 ixgbe_x550em_a_mii_bus_write(struct mii_bus *bus, int addr,
+                                       int regnum, u16 val)
+{
+       struct ixgbe_adapter *adapter = bus->priv;
+       struct ixgbe_hw *hw = &adapter->hw;
+       u32 gssr = hw->phy.phy_semaphore_mask;
+
+       gssr |= IXGBE_GSSR_TOKEN_SM | IXGBE_GSSR_PHY0_SM;
+       return ixgbe_mii_bus_write_generic(hw, addr, regnum, val, gssr);
+}
+
+/**
+ * ixgbe_get_first_secondary_devfn - get first device downstream of root port
+ * @devfn: PCI_DEVFN of root port on domain 0, bus 0
+ *
+ * Returns pci_dev pointer to PCI_DEVFN(0, 0) on subordinate side of root
+ * on domain 0, bus 0, devfn = 'devfn'
+ **/
+static struct pci_dev *ixgbe_get_first_secondary_devfn(unsigned int devfn)
+{
+       struct pci_dev *rp_pdev;
+       int bus;
+
+       rp_pdev = pci_get_domain_bus_and_slot(0, 0, devfn);
+       if (rp_pdev && rp_pdev->subordinate) {
+               bus = rp_pdev->subordinate->number;
+               return pci_get_domain_bus_and_slot(0, bus, 0);
+       }
+
+       return NULL;
+}
+
+/**
+ * ixgbe_x550em_a_has_mii - is this the first ixgbe x550em_a PCI function?
+ * @hw: pointer to hardware structure
+ *
+ * Returns true if hw points to lowest numbered PCI B:D.F x550_em_a device in
+ * the SoC.  There are up to 4 MACs sharing a single MDIO bus on the x550em_a,
+ * but we only want to register one MDIO bus.
+ **/
+static bool ixgbe_x550em_a_has_mii(struct ixgbe_hw *hw)
+{
+       struct ixgbe_adapter *adapter = hw->back;
+       struct pci_dev *pdev = adapter->pdev;
+       struct pci_dev *func0_pdev;
+
+       /* For the C3000 family of SoCs (x550em_a) the internal ixgbe devices
+        * are always downstream of root ports @ 0000:00:16.0 & 0000:00:17.0
+        * It's not valid for function 0 to be disabled and function 1 is up,
+        * so the lowest numbered ixgbe dev will be device 0 function 0 on one
+        * of those two root ports
+        */
+       func0_pdev = ixgbe_get_first_secondary_devfn(PCI_DEVFN(0x16, 0));
+       if (func0_pdev) {
+               if (func0_pdev == pdev)
+                       return true;
+               else
+                       return false;
+       }
+       func0_pdev = ixgbe_get_first_secondary_devfn(PCI_DEVFN(0x17, 0));
+       if (func0_pdev == pdev)
+               return true;
+
+       return false;
+}
+
+/**
+ * ixgbe_mii_bus_init - mii_bus structure setup
+ * @hw: pointer to hardware structure
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * ixgbe_mii_bus_init initializes a mii_bus structure in adapter
+ **/
+s32 ixgbe_mii_bus_init(struct ixgbe_hw *hw)
+{
+       struct ixgbe_adapter *adapter = hw->back;
+       struct pci_dev *pdev = adapter->pdev;
+       struct device *dev = &adapter->netdev->dev;
+       struct mii_bus *bus;
+
+       adapter->mii_bus = devm_mdiobus_alloc(dev);
+       if (!adapter->mii_bus)
+               return -ENOMEM;
+
+       bus = adapter->mii_bus;
+
+       switch (hw->device_id) {
+       /* C3000 SoCs */
+       case IXGBE_DEV_ID_X550EM_A_KR:
+       case IXGBE_DEV_ID_X550EM_A_KR_L:
+       case IXGBE_DEV_ID_X550EM_A_SFP_N:
+       case IXGBE_DEV_ID_X550EM_A_SGMII:
+       case IXGBE_DEV_ID_X550EM_A_SGMII_L:
+       case IXGBE_DEV_ID_X550EM_A_10G_T:
+       case IXGBE_DEV_ID_X550EM_A_SFP:
+       case IXGBE_DEV_ID_X550EM_A_1G_T:
+       case IXGBE_DEV_ID_X550EM_A_1G_T_L:
+               if (!ixgbe_x550em_a_has_mii(hw))
+                       goto ixgbe_no_mii_bus;
+               bus->read = &ixgbe_x550em_a_mii_bus_read;
+               bus->write = &ixgbe_x550em_a_mii_bus_write;
+               break;
+       default:
+               bus->read = &ixgbe_mii_bus_read;
+               bus->write = &ixgbe_mii_bus_write;
+               break;
+       }
+
+       /* Use the position of the device in the PCI hierarchy as the id */
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mdio-%s", ixgbe_driver_name,
+                pci_name(pdev));
+
+       bus->name = "ixgbe-mdio";
+       bus->priv = adapter;
+       bus->parent = dev;
+       bus->phy_mask = GENMASK(31, 0);
+
+       /* Support clause 22/45 natively.  ixgbe_probe() sets MDIO_EMULATE_C22
+        * unfortunately that causes some clause 22 frames to be sent with
+        * clause 45 addressing.  We don't want that.
+        */
+       hw->phy.mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22;
+
+       return mdiobus_register(bus);
+
+ixgbe_no_mii_bus:
+       devm_mdiobus_free(dev, bus);
+       adapter->mii_bus = NULL;
+       return -ENODEV;
+}
+
 /**
  *  ixgbe_setup_phy_link_generic - Set and restart autoneg
  *  @hw: pointer to hardware structure
index 64e44e0..214b010 100644 (file)
 /* SFP+ SFF-8472 Compliance code */
 #define IXGBE_SFF_SFF_8472_UNSUP      0x00
 
+s32 ixgbe_mii_bus_init(struct ixgbe_hw *hw);
+
 s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw);
 s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw);
 s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
index 5dacfc8..345701a 100644 (file)
@@ -700,7 +700,6 @@ static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
        u8 num_tcs = adapter->hw_tcs;
        u32 reg_val;
        u32 queue;
-       u32 word;
 
        /* remove VLAN filters beloning to this VF */
        ixgbe_clear_vf_vlans(adapter, vf);
@@ -758,6 +757,14 @@ static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
                }
        }
 
+       IXGBE_WRITE_FLUSH(hw);
+}
+
+static void ixgbe_vf_clear_mbx(struct ixgbe_adapter *adapter, u32 vf)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       u32 word;
+
        /* Clear VF's mailbox memory */
        for (word = 0; word < IXGBE_VFMAILBOX_SIZE; word++)
                IXGBE_WRITE_REG_ARRAY(hw, IXGBE_PFMBMEM(vf), word, 0);
@@ -831,6 +838,8 @@ static int ixgbe_vf_reset_msg(struct ixgbe_adapter *adapter, u32 vf)
        /* reset the filters for the device */
        ixgbe_vf_reset_event(adapter, vf);
 
+       ixgbe_vf_clear_mbx(adapter, vf);
+
        /* set vf mac address */
        if (!is_zero_ether_addr(vf_mac))
                ixgbe_set_vf_mac(adapter, vf, vf_mac);
index 10dbaf4..9c42f74 100644 (file)
@@ -2262,7 +2262,9 @@ static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw,
                *autoneg = false;
 
                if (hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 ||
-                   hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1) {
+                   hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1 ||
+                   hw->phy.sfp_type == ixgbe_sfp_type_1g_lx_core0 ||
+                   hw->phy.sfp_type == ixgbe_sfp_type_1g_lx_core1) {
                        *speed = IXGBE_LINK_SPEED_1GB_FULL;
                        return 0;
                }
index e8a3231..5170dd9 100644 (file)
@@ -450,12 +450,14 @@ int ixgbevf_ipsec_tx(struct ixgbevf_ring *tx_ring,
        struct ixgbevf_adapter *adapter = netdev_priv(tx_ring->netdev);
        struct ixgbevf_ipsec *ipsec = adapter->ipsec;
        struct xfrm_state *xs;
+       struct sec_path *sp;
        struct tx_sa *tsa;
        u16 sa_idx;
 
-       if (unlikely(!first->skb->sp->len)) {
+       sp = skb_sec_path(first->skb);
+       if (unlikely(!sp->len)) {
                netdev_err(tx_ring->netdev, "%s: no xfrm state len = %d\n",
-                          __func__, first->skb->sp->len);
+                          __func__, sp->len);
                return 0;
        }
 
@@ -546,6 +548,7 @@ void ixgbevf_ipsec_rx(struct ixgbevf_ring *rx_ring,
        struct xfrm_state *xs = NULL;
        struct ipv6hdr *ip6 = NULL;
        struct iphdr *ip4 = NULL;
+       struct sec_path *sp;
        void *daddr;
        __be32 spi;
        u8 *c_hdr;
@@ -585,12 +588,12 @@ void ixgbevf_ipsec_rx(struct ixgbevf_ring *rx_ring,
        if (unlikely(!xs))
                return;
 
-       skb->sp = secpath_dup(skb->sp);
-       if (unlikely(!skb->sp))
+       sp = secpath_set(skb);
+       if (unlikely(!sp))
                return;
 
-       skb->sp->xvec[skb->sp->len++] = xs;
-       skb->sp->olen++;
+       sp->xvec[sp->len++] = xs;
+       sp->olen++;
        xo = xfrm_offload(skb);
        xo->flags = CRYPTO_DONE;
        xo->status = CRYPTO_SUCCESS;
index 196b890..49e23af 100644 (file)
@@ -1293,16 +1293,20 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
        /* If all work not completed, return budget and keep polling */
        if (!clean_complete)
                return budget;
-       /* all work done, exit the polling mode */
-       napi_complete_done(napi, work_done);
-       if (adapter->rx_itr_setting == 1)
-               ixgbevf_set_itr(q_vector);
-       if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
-           !test_bit(__IXGBEVF_REMOVING, &adapter->state))
-               ixgbevf_irq_enable_queues(adapter,
-                                         BIT(q_vector->v_idx));
 
-       return 0;
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done))) {
+               if (adapter->rx_itr_setting == 1)
+                       ixgbevf_set_itr(q_vector);
+               if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
+                   !test_bit(__IXGBEVF_REMOVING, &adapter->state))
+                       ixgbevf_irq_enable_queues(adapter,
+                                                 BIT(q_vector->v_idx));
+       }
+
+       return min(work_done, budget - 1);
 }
 
 /**
@@ -4153,7 +4157,7 @@ static int ixgbevf_xmit_frame_ring(struct sk_buff *skb,
        first->protocol = vlan_get_protocol(skb);
 
 #ifdef CONFIG_IXGBEVF_IPSEC
-       if (skb->sp && !ixgbevf_ipsec_tx(tx_ring, first, &ipsec_tx))
+       if (secpath_exists(skb) && !ixgbevf_ipsec_tx(tx_ring, first, &ipsec_tx))
                goto out_drop;
 #endif
        tso = ixgbevf_tso(tx_ring, first, &hdr_len, &ipsec_tx);
index 8c5ba4b..2d4d10a 100644 (file)
@@ -512,7 +512,8 @@ static int xrx200_probe(struct platform_device *pdev)
        err = register_netdev(net_dev);
        if (err)
                goto err_unprepare_clk;
-       return err;
+
+       return 0;
 
 err_unprepare_clk:
        clk_disable_unprepare(priv->clk);
@@ -520,7 +521,7 @@ err_unprepare_clk:
 err_uninit_dma:
        xrx200_hw_cleanup(priv);
 
-       return 0;
+       return err;
 }
 
 static int xrx200_remove(struct platform_device *pdev)
index 3ba672e..9d4568e 100644 (file)
@@ -408,7 +408,6 @@ struct mvneta_port {
        struct mvneta_pcpu_stats __percpu       *stats;
 
        int pkt_size;
-       unsigned int frag_size;
        void __iomem *base;
        struct mvneta_rx_queue *rxqs;
        struct mvneta_tx_queue *txqs;
@@ -2905,7 +2904,9 @@ static void mvneta_rxq_hw_init(struct mvneta_port *pp,
        if (!pp->bm_priv) {
                /* Set Offset */
                mvneta_rxq_offset_set(pp, rxq, 0);
-               mvneta_rxq_buf_size_set(pp, rxq, pp->frag_size);
+               mvneta_rxq_buf_size_set(pp, rxq, PAGE_SIZE < SZ_64K ?
+                                       PAGE_SIZE :
+                                       MVNETA_RX_BUF_SIZE(pp->pkt_size));
                mvneta_rxq_bm_disable(pp, rxq);
                mvneta_rxq_fill(pp, rxq, rxq->size);
        } else {
@@ -3343,7 +3344,6 @@ static void mvneta_validate(struct net_device *ndev, unsigned long *supported,
        if (state->interface != PHY_INTERFACE_MODE_NA &&
            state->interface != PHY_INTERFACE_MODE_QSGMII &&
            state->interface != PHY_INTERFACE_MODE_SGMII &&
-           state->interface != PHY_INTERFACE_MODE_2500BASEX &&
            !phy_interface_mode_is_8023z(state->interface) &&
            !phy_interface_mode_is_rgmii(state->interface)) {
                bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
@@ -3357,14 +3357,9 @@ static void mvneta_validate(struct net_device *ndev, unsigned long *supported,
        /* Asymmetric pause is unsupported */
        phylink_set(mask, Pause);
 
-       /* We cannot use 1Gbps when using the 2.5G interface. */
-       if (state->interface == PHY_INTERFACE_MODE_2500BASEX) {
-               phylink_set(mask, 2500baseT_Full);
-               phylink_set(mask, 2500baseX_Full);
-       } else {
-               phylink_set(mask, 1000baseT_Full);
-               phylink_set(mask, 1000baseX_Full);
-       }
+       /* Half-duplex at speeds higher than 100Mbit is unsupported */
+       phylink_set(mask, 1000baseT_Full);
+       phylink_set(mask, 1000baseX_Full);
 
        if (!phy_interface_mode_is_8023z(state->interface)) {
                /* 10M and 100M are only supported in non-802.3z mode */
@@ -3766,7 +3761,6 @@ static int mvneta_open(struct net_device *dev)
        int ret;
 
        pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
-       pp->frag_size = PAGE_SIZE;
 
        ret = mvneta_setup_rxqs(pp);
        if (ret)
@@ -4254,8 +4248,7 @@ static int mvneta_ethtool_set_eee(struct net_device *dev,
 
        /* The Armada 37x documents do not give limits for this other than
         * it being an 8-bit register. */
-       if (eee->tx_lpi_enabled &&
-           (eee->tx_lpi_timer < 0 || eee->tx_lpi_timer > 255))
+       if (eee->tx_lpi_enabled && eee->tx_lpi_timer > 255)
                return -EINVAL;
 
        lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
index 7a37a37..f1dab0b 100644 (file)
@@ -4375,8 +4375,27 @@ static void mvpp2_phylink_validate(struct net_device *dev,
                                   unsigned long *supported,
                                   struct phylink_link_state *state)
 {
+       struct mvpp2_port *port = netdev_priv(dev);
        __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 
+       /* Invalid combinations */
+       switch (state->interface) {
+       case PHY_INTERFACE_MODE_10GKR:
+       case PHY_INTERFACE_MODE_XAUI:
+               if (port->gop_id != 0)
+                       goto empty_set;
+               break;
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+               if (port->gop_id == 0)
+                       goto empty_set;
+               break;
+       default:
+               break;
+       }
+
        phylink_set(mask, Autoneg);
        phylink_set_port_modes(mask);
        phylink_set(mask, Pause);
@@ -4384,30 +4403,45 @@ static void mvpp2_phylink_validate(struct net_device *dev,
 
        switch (state->interface) {
        case PHY_INTERFACE_MODE_10GKR:
-               phylink_set(mask, 10000baseCR_Full);
-               phylink_set(mask, 10000baseSR_Full);
-               phylink_set(mask, 10000baseLR_Full);
-               phylink_set(mask, 10000baseLRM_Full);
-               phylink_set(mask, 10000baseER_Full);
-               phylink_set(mask, 10000baseKR_Full);
+       case PHY_INTERFACE_MODE_XAUI:
+       case PHY_INTERFACE_MODE_NA:
+               if (port->gop_id == 0) {
+                       phylink_set(mask, 10000baseT_Full);
+                       phylink_set(mask, 10000baseCR_Full);
+                       phylink_set(mask, 10000baseSR_Full);
+                       phylink_set(mask, 10000baseLR_Full);
+                       phylink_set(mask, 10000baseLRM_Full);
+                       phylink_set(mask, 10000baseER_Full);
+                       phylink_set(mask, 10000baseKR_Full);
+               }
                /* Fall-through */
-       default:
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+       case PHY_INTERFACE_MODE_SGMII:
                phylink_set(mask, 10baseT_Half);
                phylink_set(mask, 10baseT_Full);
                phylink_set(mask, 100baseT_Half);
                phylink_set(mask, 100baseT_Full);
-               phylink_set(mask, 10000baseT_Full);
                /* Fall-through */
        case PHY_INTERFACE_MODE_1000BASEX:
        case PHY_INTERFACE_MODE_2500BASEX:
                phylink_set(mask, 1000baseT_Full);
                phylink_set(mask, 1000baseX_Full);
                phylink_set(mask, 2500baseX_Full);
+               break;
+       default:
+               goto empty_set;
        }
 
        bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
        bitmap_and(state->advertising, state->advertising, mask,
                   __ETHTOOL_LINK_MODE_MASK_NBITS);
+       return;
+
+empty_set:
+       bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
 }
 
 static void mvpp22_xlg_link_state(struct mvpp2_port *port,
index 12db256..742f0c1 100644 (file)
@@ -31,6 +31,7 @@
  * @resp:              command response
  * @link_info:         link related information
  * @event_cb:          callback for linkchange events
+ * @event_cb_lock:     lock for serializing callback with unregister
  * @cmd_pend:          flag set before new command is started
  *                     flag cleared after command response is received
  * @cgx:               parent cgx port
@@ -43,6 +44,7 @@ struct lmac {
        u64 resp;
        struct cgx_link_user_info link_info;
        struct cgx_event_cb event_cb;
+       spinlock_t event_cb_lock;
        bool cmd_pend;
        struct cgx *cgx;
        u8 lmac_id;
@@ -55,6 +57,8 @@ struct cgx {
        u8                      cgx_id;
        u8                      lmac_count;
        struct lmac             *lmac_idmap[MAX_LMAC_PER_CGX];
+       struct                  work_struct cgx_cmd_work;
+       struct                  workqueue_struct *cgx_cmd_workq;
        struct list_head        cgx_list;
 };
 
@@ -66,6 +70,9 @@ static u32 cgx_speed_mbps[CGX_LINK_SPEED_MAX];
 /* Convert firmware lmac type encoding to string */
 static char *cgx_lmactype_string[LMAC_MODE_MAX];
 
+/* CGX PHY management internal APIs */
+static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool en);
+
 /* Supported devices */
 static const struct pci_device_id cgx_id_table[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) },
@@ -92,17 +99,21 @@ static inline struct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx)
        return cgx->lmac_idmap[lmac_id];
 }
 
-int cgx_get_cgx_cnt(void)
+int cgx_get_cgxcnt_max(void)
 {
        struct cgx *cgx_dev;
-       int count = 0;
+       int idmax = -ENODEV;
 
        list_for_each_entry(cgx_dev, &cgx_list, cgx_list)
-               count++;
+               if (cgx_dev->cgx_id > idmax)
+                       idmax = cgx_dev->cgx_id;
+
+       if (idmax < 0)
+               return 0;
 
-       return count;
+       return idmax + 1;
 }
-EXPORT_SYMBOL(cgx_get_cgx_cnt);
+EXPORT_SYMBOL(cgx_get_cgxcnt_max);
 
 int cgx_get_lmac_cnt(void *cgxd)
 {
@@ -445,6 +456,9 @@ static inline void cgx_link_change_handler(u64 lstat,
        lmac->link_info = event.link_uinfo;
        linfo = &lmac->link_info;
 
+       /* Ensure callback doesn't get unregistered until we finish it */
+       spin_lock(&lmac->event_cb_lock);
+
        if (!lmac->event_cb.notify_link_chg) {
                dev_dbg(dev, "cgx port %d:%d Link change handler null",
                        cgx->cgx_id, lmac->lmac_id);
@@ -455,11 +469,13 @@ static inline void cgx_link_change_handler(u64 lstat,
                dev_info(dev, "cgx port %d:%d Link is %s %d Mbps\n",
                         cgx->cgx_id, lmac->lmac_id,
                         linfo->link_up ? "UP" : "DOWN", linfo->speed);
-               return;
+               goto err;
        }
 
        if (lmac->event_cb.notify_link_chg(&event, lmac->event_cb.data))
                dev_err(dev, "event notification failure\n");
+err:
+       spin_unlock(&lmac->event_cb_lock);
 }
 
 static inline bool cgx_cmdresp_is_linkevent(u64 event)
@@ -482,6 +498,60 @@ static inline bool cgx_event_is_linkevent(u64 event)
                return false;
 }
 
+static inline int cgx_fwi_get_mkex_prfl_sz(u64 *prfl_sz,
+                                          struct cgx *cgx)
+{
+       u64 req = 0;
+       u64 resp;
+       int err;
+
+       req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_MKEX_PRFL_SIZE, req);
+       err = cgx_fwi_cmd_generic(req, &resp, cgx, 0);
+       if (!err)
+               *prfl_sz = FIELD_GET(RESP_MKEX_PRFL_SIZE, resp);
+
+       return err;
+}
+
+static inline int cgx_fwi_get_mkex_prfl_addr(u64 *prfl_addr,
+                                            struct cgx *cgx)
+{
+       u64 req = 0;
+       u64 resp;
+       int err;
+
+       req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_MKEX_PRFL_ADDR, req);
+       err = cgx_fwi_cmd_generic(req, &resp, cgx, 0);
+       if (!err)
+               *prfl_addr = FIELD_GET(RESP_MKEX_PRFL_ADDR, resp);
+
+       return err;
+}
+
+int cgx_get_mkex_prfl_info(u64 *addr, u64 *size)
+{
+       struct cgx *cgx_dev;
+       int err;
+
+       if (!addr || !size)
+               return -EINVAL;
+
+       cgx_dev = list_first_entry(&cgx_list, struct cgx, cgx_list);
+       if (!cgx_dev)
+               return -ENXIO;
+
+       err = cgx_fwi_get_mkex_prfl_sz(size, cgx_dev);
+       if (err)
+               return -EIO;
+
+       err = cgx_fwi_get_mkex_prfl_addr(addr, cgx_dev);
+       if (err)
+               return -EIO;
+
+       return 0;
+}
+EXPORT_SYMBOL(cgx_get_mkex_prfl_info);
+
 static irqreturn_t cgx_fwi_event_handler(int irq, void *data)
 {
        struct lmac *lmac = data;
@@ -548,6 +618,38 @@ int cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id)
 }
 EXPORT_SYMBOL(cgx_lmac_evh_register);
 
+int cgx_lmac_evh_unregister(void *cgxd, int lmac_id)
+{
+       struct lmac *lmac;
+       unsigned long flags;
+       struct cgx *cgx = cgxd;
+
+       lmac = lmac_pdata(lmac_id, cgx);
+       if (!lmac)
+               return -ENODEV;
+
+       spin_lock_irqsave(&lmac->event_cb_lock, flags);
+       lmac->event_cb.notify_link_chg = NULL;
+       lmac->event_cb.data = NULL;
+       spin_unlock_irqrestore(&lmac->event_cb_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(cgx_lmac_evh_unregister);
+
+static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable)
+{
+       u64 req = 0;
+       u64 resp;
+
+       if (enable)
+               req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_UP, req);
+       else
+               req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_DOWN, req);
+
+       return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id);
+}
+
 static inline int cgx_fwi_read_version(u64 *resp, struct cgx *cgx)
 {
        u64 req = 0;
@@ -581,6 +683,34 @@ static int cgx_lmac_verify_fwi_version(struct cgx *cgx)
                return 0;
 }
 
+static void cgx_lmac_linkup_work(struct work_struct *work)
+{
+       struct cgx *cgx = container_of(work, struct cgx, cgx_cmd_work);
+       struct device *dev = &cgx->pdev->dev;
+       int i, err;
+
+       /* Do Link up for all the lmacs */
+       for (i = 0; i < cgx->lmac_count; i++) {
+               err = cgx_fwi_link_change(cgx, i, true);
+               if (err)
+                       dev_info(dev, "cgx port %d:%d Link up command failed\n",
+                                cgx->cgx_id, i);
+       }
+}
+
+int cgx_lmac_linkup_start(void *cgxd)
+{
+       struct cgx *cgx = cgxd;
+
+       if (!cgx)
+               return -ENODEV;
+
+       queue_work(cgx->cgx_cmd_workq, &cgx->cgx_cmd_work);
+
+       return 0;
+}
+EXPORT_SYMBOL(cgx_lmac_linkup_start);
+
 static int cgx_lmac_init(struct cgx *cgx)
 {
        struct lmac *lmac;
@@ -602,6 +732,7 @@ static int cgx_lmac_init(struct cgx *cgx)
                lmac->cgx = cgx;
                init_waitqueue_head(&lmac->wq_cmd_cmplt);
                mutex_init(&lmac->cmd_lock);
+               spin_lock_init(&lmac->event_cb_lock);
                err = request_irq(pci_irq_vector(cgx->pdev,
                                                 CGX_LMAC_FWI + i * 9),
                                   cgx_fwi_event_handler, 0, lmac->name, lmac);
@@ -624,6 +755,12 @@ static int cgx_lmac_exit(struct cgx *cgx)
        struct lmac *lmac;
        int i;
 
+       if (cgx->cgx_cmd_workq) {
+               flush_workqueue(cgx->cgx_cmd_workq);
+               destroy_workqueue(cgx->cgx_cmd_workq);
+               cgx->cgx_cmd_workq = NULL;
+       }
+
        /* Free all lmac related resources */
        for (i = 0; i < cgx->lmac_count; i++) {
                lmac = cgx->lmac_idmap[i];
@@ -679,8 +816,19 @@ static int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_release_regions;
        }
 
+       cgx->cgx_id = (pci_resource_start(pdev, PCI_CFG_REG_BAR_NUM) >> 24)
+               & CGX_ID_MASK;
+
+       /* init wq for processing linkup requests */
+       INIT_WORK(&cgx->cgx_cmd_work, cgx_lmac_linkup_work);
+       cgx->cgx_cmd_workq = alloc_workqueue("cgx_cmd_workq", 0, 0);
+       if (!cgx->cgx_cmd_workq) {
+               dev_err(dev, "alloc workqueue failed for cgx cmd");
+               err = -ENOMEM;
+               goto err_release_regions;
+       }
+
        list_add(&cgx->cgx_list, &cgx_list);
-       cgx->cgx_id = cgx_get_cgx_cnt() - 1;
 
        cgx_link_usertable_init();
 
index 0a66d27..206dc5d 100644 (file)
 /* PCI BAR nos */
 #define PCI_CFG_REG_BAR_NUM            0
 
-#define MAX_CGX                                3
+#define CGX_ID_MASK                    0x7
 #define MAX_LMAC_PER_CGX               4
+#define CGX_FIFO_LEN                   65536 /* 64K for both Rx & Tx */
 #define CGX_OFFSET(x)                  ((x) * MAX_LMAC_PER_CGX)
 
 /* Registers */
 #define CGXX_CMRX_CFG                  0x00
-#define  CMR_EN                                        BIT_ULL(55)
-#define  DATA_PKT_TX_EN                                BIT_ULL(53)
-#define  DATA_PKT_RX_EN                                BIT_ULL(54)
-#define  CGX_LMAC_TYPE_SHIFT                   40
-#define  CGX_LMAC_TYPE_MASK                    0xF
+#define CMR_EN                         BIT_ULL(55)
+#define DATA_PKT_TX_EN                 BIT_ULL(53)
+#define DATA_PKT_RX_EN                 BIT_ULL(54)
+#define CGX_LMAC_TYPE_SHIFT            40
+#define CGX_LMAC_TYPE_MASK             0xF
 #define CGXX_CMRX_INT                  0x040
-#define  FW_CGX_INT                            BIT_ULL(1)
+#define FW_CGX_INT                     BIT_ULL(1)
 #define CGXX_CMRX_INT_ENA_W1S          0x058
 #define CGXX_CMRX_RX_ID_MAP            0x060
 #define CGXX_CMRX_RX_STAT0             0x070
 #define CGXX_CMRX_RX_LMACS             0x128
 #define CGXX_CMRX_RX_DMAC_CTL0         0x1F8
-#define  CGX_DMAC_CTL0_CAM_ENABLE              BIT_ULL(3)
-#define  CGX_DMAC_CAM_ACCEPT                   BIT_ULL(3)
-#define  CGX_DMAC_MCAST_MODE                   BIT_ULL(1)
-#define  CGX_DMAC_BCAST_MODE                   BIT_ULL(0)
+#define CGX_DMAC_CTL0_CAM_ENABLE       BIT_ULL(3)
+#define CGX_DMAC_CAM_ACCEPT            BIT_ULL(3)
+#define CGX_DMAC_MCAST_MODE            BIT_ULL(1)
+#define CGX_DMAC_BCAST_MODE            BIT_ULL(0)
 #define CGXX_CMRX_RX_DMAC_CAM0         0x200
-#define  CGX_DMAC_CAM_ADDR_ENABLE              BIT_ULL(48)
+#define CGX_DMAC_CAM_ADDR_ENABLE       BIT_ULL(48)
 #define CGXX_CMRX_RX_DMAC_CAM1         0x400
-#define CGX_RX_DMAC_ADR_MASK                   GENMASK_ULL(47, 0)
+#define CGX_RX_DMAC_ADR_MASK           GENMASK_ULL(47, 0)
 #define CGXX_CMRX_TX_STAT0             0x700
 #define CGXX_SCRATCH0_REG              0x1050
 #define CGXX_SCRATCH1_REG              0x1058
 #define CGX_CONST                      0x2000
 #define CGXX_SPUX_CONTROL1             0x10000
-#define  CGXX_SPUX_CONTROL1_LBK                        BIT_ULL(14)
+#define CGXX_SPUX_CONTROL1_LBK         BIT_ULL(14)
 #define CGXX_GMP_PCS_MRX_CTL           0x30000
-#define  CGXX_GMP_PCS_MRX_CTL_LBK              BIT_ULL(14)
+#define CGXX_GMP_PCS_MRX_CTL_LBK       BIT_ULL(14)
 
 #define CGX_COMMAND_REG                        CGXX_SCRATCH1_REG
 #define CGX_EVENT_REG                  CGXX_SCRATCH0_REG
@@ -94,11 +95,12 @@ struct cgx_event_cb {
 
 extern struct pci_driver cgx_driver;
 
-int cgx_get_cgx_cnt(void);
+int cgx_get_cgxcnt_max(void);
 int cgx_get_lmac_cnt(void *cgxd);
 void *cgx_get_pdata(int cgx_id);
 int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind);
 int cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id);
+int cgx_lmac_evh_unregister(void *cgxd, int lmac_id);
 int cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat);
 int cgx_get_rx_stats(void *cgxd, int lmac_id, int idx, u64 *rx_stat);
 int cgx_lmac_rx_tx_enable(void *cgxd, int lmac_id, bool enable);
@@ -108,4 +110,6 @@ void cgx_lmac_promisc_config(int cgx_id, int lmac_id, bool enable);
 int cgx_lmac_internal_loopback(void *cgxd, int lmac_id, bool enable);
 int cgx_get_link_info(void *cgxd, int lmac_id,
                      struct cgx_link_user_info *linfo);
+int cgx_lmac_linkup_start(void *cgxd);
+int cgx_get_mkex_prfl_info(u64 *addr, u64 *size);
 #endif /* CGX_H */
index fa17af3..fb3ba49 100644 (file)
@@ -78,8 +78,8 @@ enum cgx_cmd_id {
        CGX_CMD_LINK_STATE_CHANGE,
        CGX_CMD_MODE_CHANGE,            /* hot plug support */
        CGX_CMD_INTF_SHUTDOWN,
-       CGX_CMD_IRQ_ENABLE,
-       CGX_CMD_IRQ_DISABLE,
+       CGX_CMD_GET_MKEX_PRFL_SIZE,
+       CGX_CMD_GET_MKEX_PRFL_ADDR
 };
 
 /* async event ids */
@@ -139,6 +139,16 @@ enum cgx_cmd_own {
  */
 #define RESP_MAC_ADDR          GENMASK_ULL(56, 9)
 
+/* Response to cmd ID as CGX_CMD_GET_MKEX_PRFL_SIZE with cmd status as
+ * CGX_STAT_SUCCESS
+ */
+#define RESP_MKEX_PRFL_SIZE            GENMASK_ULL(63, 9)
+
+/* Response to cmd ID as CGX_CMD_GET_MKEX_PRFL_ADDR with cmd status as
+ * CGX_STAT_SUCCESS
+ */
+#define RESP_MKEX_PRFL_ADDR            GENMASK_ULL(63, 9)
+
 /* Response to cmd ID - CGX_CMD_LINK_BRING_UP/DOWN, event ID CGX_EVT_LINK_CHANGE
  * status can be either CGX_STAT_FAIL or CGX_STAT_SUCCESS
  *
index d39ada4..ec50a21 100644 (file)
@@ -143,6 +143,14 @@ enum nix_scheduler {
        NIX_TXSCH_LVL_CNT = 0x5,
 };
 
+#define TXSCH_TL1_DFLT_RR_QTM      ((1 << 24) - 1)
+#define TXSCH_TL1_DFLT_RR_PRIO     (0x1ull)
+
+/* Min/Max packet sizes, excluding FCS */
+#define        NIC_HW_MIN_FRS                  40
+#define        NIC_HW_MAX_FRS                  9212
+#define        SDP_HW_MAX_FRS                  65535
+
 /* NIX RX action operation*/
 #define NIX_RX_ACTIONOP_DROP           (0x0ull)
 #define NIX_RX_ACTIONOP_UCAST          (0x1ull)
@@ -169,7 +177,9 @@ enum nix_scheduler {
 
 #define MAX_LMAC_PKIND                 12
 #define NIX_LINK_CGX_LMAC(a, b)                (0 + 4 * (a) + (b))
+#define NIX_LINK_LBK(a)                        (12 + (a))
 #define NIX_CHAN_CGX_LMAC_CHX(a, b, c) (0x800 + 0x100 * (a) + 0x10 * (b) + (c))
+#define NIX_CHAN_LBK_CHX(a, b)         (0 + 0x100 * (a) + (b))
 
 /* NIX LSO format indices.
  * As of now TSO is the only one using, so statically assigning indices.
@@ -186,26 +196,4 @@ enum nix_scheduler {
 #define DEFAULT_RSS_CONTEXT_GROUP      0
 #define MAX_RSS_INDIR_TBL_SIZE         256 /* 1 << Max adder bits */
 
-/* NIX flow tag, key type flags */
-#define FLOW_KEY_TYPE_PORT     BIT(0)
-#define FLOW_KEY_TYPE_IPV4     BIT(1)
-#define FLOW_KEY_TYPE_IPV6     BIT(2)
-#define FLOW_KEY_TYPE_TCP      BIT(3)
-#define FLOW_KEY_TYPE_UDP      BIT(4)
-#define FLOW_KEY_TYPE_SCTP     BIT(5)
-
-/* NIX flow tag algorithm indices, max is 31 */
-enum {
-       FLOW_KEY_ALG_PORT,
-       FLOW_KEY_ALG_IP,
-       FLOW_KEY_ALG_TCP,
-       FLOW_KEY_ALG_UDP,
-       FLOW_KEY_ALG_SCTP,
-       FLOW_KEY_ALG_TCP_UDP,
-       FLOW_KEY_ALG_TCP_SCTP,
-       FLOW_KEY_ALG_UDP_SCTP,
-       FLOW_KEY_ALG_TCP_UDP_SCTP,
-       FLOW_KEY_ALG_MAX,
-};
-
 #endif /* COMMON_H */
index 85ba24a..d6f9ed8 100644 (file)
@@ -290,7 +290,7 @@ EXPORT_SYMBOL(otx2_mbox_nonempty);
 const char *otx2_mbox_id2name(u16 id)
 {
        switch (id) {
-#define M(_name, _id, _1, _2) case _id: return # _name;
+#define M(_name, _id, _1, _2, _3) case _id: return # _name;
        MBOX_MESSAGES
 #undef M
        default:
index a15a59c..76a4575 100644 (file)
@@ -120,54 +120,101 @@ static inline struct mbox_msghdr *otx2_mbox_alloc_msg(struct otx2_mbox *mbox,
 
 #define MBOX_MESSAGES                                                  \
 /* Generic mbox IDs (range 0x000 - 0x1FF) */                           \
-M(READY,               0x001, msg_req, ready_msg_rsp)                  \
-M(ATTACH_RESOURCES,    0x002, rsrc_attach, msg_rsp)                    \
-M(DETACH_RESOURCES,    0x003, rsrc_detach, msg_rsp)                    \
-M(MSIX_OFFSET,         0x004, msg_req, msix_offset_rsp)                \
+M(READY,               0x001, ready, msg_req, ready_msg_rsp)           \
+M(ATTACH_RESOURCES,    0x002, attach_resources, rsrc_attach, msg_rsp)  \
+M(DETACH_RESOURCES,    0x003, detach_resources, rsrc_detach, msg_rsp)  \
+M(MSIX_OFFSET,         0x004, msix_offset, msg_req, msix_offset_rsp)   \
+M(VF_FLR,              0x006, vf_flr, msg_req, msg_rsp)                \
 /* CGX mbox IDs (range 0x200 - 0x3FF) */                               \
-M(CGX_START_RXTX,      0x200, msg_req, msg_rsp)                        \
-M(CGX_STOP_RXTX,       0x201, msg_req, msg_rsp)                        \
-M(CGX_STATS,           0x202, msg_req, cgx_stats_rsp)                  \
-M(CGX_MAC_ADDR_SET,    0x203, cgx_mac_addr_set_or_get,                 \
+M(CGX_START_RXTX,      0x200, cgx_start_rxtx, msg_req, msg_rsp)        \
+M(CGX_STOP_RXTX,       0x201, cgx_stop_rxtx, msg_req, msg_rsp)         \
+M(CGX_STATS,           0x202, cgx_stats, msg_req, cgx_stats_rsp)       \
+M(CGX_MAC_ADDR_SET,    0x203, cgx_mac_addr_set, cgx_mac_addr_set_or_get,    \
                                cgx_mac_addr_set_or_get)                \
-M(CGX_MAC_ADDR_GET,    0x204, cgx_mac_addr_set_or_get,                 \
+M(CGX_MAC_ADDR_GET,    0x204, cgx_mac_addr_get, cgx_mac_addr_set_or_get,    \
                                cgx_mac_addr_set_or_get)                \
-M(CGX_PROMISC_ENABLE,  0x205, msg_req, msg_rsp)                        \
-M(CGX_PROMISC_DISABLE, 0x206, msg_req, msg_rsp)                        \
-M(CGX_START_LINKEVENTS, 0x207, msg_req, msg_rsp)                       \
-M(CGX_STOP_LINKEVENTS, 0x208, msg_req, msg_rsp)                        \
-M(CGX_GET_LINKINFO,    0x209, msg_req, cgx_link_info_msg)              \
-M(CGX_INTLBK_ENABLE,   0x20A, msg_req, msg_rsp)                        \
-M(CGX_INTLBK_DISABLE,  0x20B, msg_req, msg_rsp)                        \
+M(CGX_PROMISC_ENABLE,  0x205, cgx_promisc_enable, msg_req, msg_rsp)    \
+M(CGX_PROMISC_DISABLE, 0x206, cgx_promisc_disable, msg_req, msg_rsp)   \
+M(CGX_START_LINKEVENTS, 0x207, cgx_start_linkevents, msg_req, msg_rsp) \
+M(CGX_STOP_LINKEVENTS, 0x208, cgx_stop_linkevents, msg_req, msg_rsp)   \
+M(CGX_GET_LINKINFO,    0x209, cgx_get_linkinfo, msg_req, cgx_link_info_msg) \
+M(CGX_INTLBK_ENABLE,   0x20A, cgx_intlbk_enable, msg_req, msg_rsp)     \
+M(CGX_INTLBK_DISABLE,  0x20B, cgx_intlbk_disable, msg_req, msg_rsp)    \
 /* NPA mbox IDs (range 0x400 - 0x5FF) */                               \
-M(NPA_LF_ALLOC,                0x400, npa_lf_alloc_req, npa_lf_alloc_rsp)      \
-M(NPA_LF_FREE,         0x401, msg_req, msg_rsp)                        \
-M(NPA_AQ_ENQ,          0x402, npa_aq_enq_req, npa_aq_enq_rsp)          \
-M(NPA_HWCTX_DISABLE,   0x403, hwctx_disable_req, msg_rsp)              \
+M(NPA_LF_ALLOC,                0x400, npa_lf_alloc,                            \
+                               npa_lf_alloc_req, npa_lf_alloc_rsp)     \
+M(NPA_LF_FREE,         0x401, npa_lf_free, msg_req, msg_rsp)           \
+M(NPA_AQ_ENQ,          0x402, npa_aq_enq, npa_aq_enq_req, npa_aq_enq_rsp)   \
+M(NPA_HWCTX_DISABLE,   0x403, npa_hwctx_disable, hwctx_disable_req, msg_rsp)\
 /* SSO/SSOW mbox IDs (range 0x600 - 0x7FF) */                          \
 /* TIM mbox IDs (range 0x800 - 0x9FF) */                               \
 /* CPT mbox IDs (range 0xA00 - 0xBFF) */                               \
 /* NPC mbox IDs (range 0x6000 - 0x7FFF) */                             \
+M(NPC_MCAM_ALLOC_ENTRY,        0x6000, npc_mcam_alloc_entry, npc_mcam_alloc_entry_req,\
+                               npc_mcam_alloc_entry_rsp)               \
+M(NPC_MCAM_FREE_ENTRY, 0x6001, npc_mcam_free_entry,                    \
+                                npc_mcam_free_entry_req, msg_rsp)      \
+M(NPC_MCAM_WRITE_ENTRY,        0x6002, npc_mcam_write_entry,                   \
+                                npc_mcam_write_entry_req, msg_rsp)     \
+M(NPC_MCAM_ENA_ENTRY,   0x6003, npc_mcam_ena_entry,                    \
+                                npc_mcam_ena_dis_entry_req, msg_rsp)   \
+M(NPC_MCAM_DIS_ENTRY,   0x6004, npc_mcam_dis_entry,                    \
+                                npc_mcam_ena_dis_entry_req, msg_rsp)   \
+M(NPC_MCAM_SHIFT_ENTRY, 0x6005, npc_mcam_shift_entry, npc_mcam_shift_entry_req,\
+                               npc_mcam_shift_entry_rsp)               \
+M(NPC_MCAM_ALLOC_COUNTER, 0x6006, npc_mcam_alloc_counter,              \
+                                       npc_mcam_alloc_counter_req,     \
+                                       npc_mcam_alloc_counter_rsp)     \
+M(NPC_MCAM_FREE_COUNTER,  0x6007, npc_mcam_free_counter,               \
+                                   npc_mcam_oper_counter_req, msg_rsp) \
+M(NPC_MCAM_UNMAP_COUNTER, 0x6008, npc_mcam_unmap_counter,              \
+                                  npc_mcam_unmap_counter_req, msg_rsp) \
+M(NPC_MCAM_CLEAR_COUNTER, 0x6009, npc_mcam_clear_counter,              \
+                                  npc_mcam_oper_counter_req, msg_rsp)  \
+M(NPC_MCAM_COUNTER_STATS, 0x600a, npc_mcam_counter_stats,              \
+                                  npc_mcam_oper_counter_req,           \
+                                  npc_mcam_oper_counter_rsp)           \
+M(NPC_MCAM_ALLOC_AND_WRITE_ENTRY, 0x600b, npc_mcam_alloc_and_write_entry,      \
+                                         npc_mcam_alloc_and_write_entry_req,  \
+                                         npc_mcam_alloc_and_write_entry_rsp)  \
+M(NPC_GET_KEX_CFG,       0x600c, npc_get_kex_cfg,                      \
+                                  msg_req, npc_get_kex_cfg_rsp)        \
 /* NIX mbox IDs (range 0x8000 - 0xFFFF) */                             \
-M(NIX_LF_ALLOC,                0x8000, nix_lf_alloc_req, nix_lf_alloc_rsp)     \
-M(NIX_LF_FREE,         0x8001, msg_req, msg_rsp)                       \
-M(NIX_AQ_ENQ,          0x8002, nix_aq_enq_req, nix_aq_enq_rsp)         \
-M(NIX_HWCTX_DISABLE,   0x8003, hwctx_disable_req, msg_rsp)             \
-M(NIX_TXSCH_ALLOC,     0x8004, nix_txsch_alloc_req, nix_txsch_alloc_rsp) \
-M(NIX_TXSCH_FREE,      0x8005, nix_txsch_free_req, msg_rsp)            \
-M(NIX_TXSCHQ_CFG,      0x8006, nix_txschq_config, msg_rsp)             \
-M(NIX_STATS_RST,       0x8007, msg_req, msg_rsp)                       \
-M(NIX_VTAG_CFG,        0x8008, nix_vtag_config, msg_rsp)               \
-M(NIX_RSS_FLOWKEY_CFG,  0x8009, nix_rss_flowkey_cfg, msg_rsp)          \
-M(NIX_SET_MAC_ADDR,    0x800a, nix_set_mac_addr, msg_rsp)              \
-M(NIX_SET_RX_MODE,     0x800b, nix_rx_mode, msg_rsp)
+M(NIX_LF_ALLOC,                0x8000, nix_lf_alloc,                           \
+                                nix_lf_alloc_req, nix_lf_alloc_rsp)    \
+M(NIX_LF_FREE,         0x8001, nix_lf_free, msg_req, msg_rsp)          \
+M(NIX_AQ_ENQ,          0x8002, nix_aq_enq, nix_aq_enq_req, nix_aq_enq_rsp)  \
+M(NIX_HWCTX_DISABLE,   0x8003, nix_hwctx_disable,                      \
+                                hwctx_disable_req, msg_rsp)            \
+M(NIX_TXSCH_ALLOC,     0x8004, nix_txsch_alloc,                        \
+                                nix_txsch_alloc_req, nix_txsch_alloc_rsp)   \
+M(NIX_TXSCH_FREE,      0x8005, nix_txsch_free, nix_txsch_free_req, msg_rsp) \
+M(NIX_TXSCHQ_CFG,      0x8006, nix_txschq_cfg, nix_txschq_config, msg_rsp)  \
+M(NIX_STATS_RST,       0x8007, nix_stats_rst, msg_req, msg_rsp)        \
+M(NIX_VTAG_CFG,                0x8008, nix_vtag_cfg, nix_vtag_config, msg_rsp) \
+M(NIX_RSS_FLOWKEY_CFG,  0x8009, nix_rss_flowkey_cfg,                   \
+                                nix_rss_flowkey_cfg,                   \
+                                nix_rss_flowkey_cfg_rsp)               \
+M(NIX_SET_MAC_ADDR,    0x800a, nix_set_mac_addr, nix_set_mac_addr, msg_rsp) \
+M(NIX_SET_RX_MODE,     0x800b, nix_set_rx_mode, nix_rx_mode, msg_rsp)  \
+M(NIX_SET_HW_FRS,      0x800c, nix_set_hw_frs, nix_frs_cfg, msg_rsp)   \
+M(NIX_LF_START_RX,     0x800d, nix_lf_start_rx, msg_req, msg_rsp)      \
+M(NIX_LF_STOP_RX,      0x800e, nix_lf_stop_rx, msg_req, msg_rsp)       \
+M(NIX_MARK_FORMAT_CFG, 0x800f, nix_mark_format_cfg,                    \
+                                nix_mark_format_cfg,                   \
+                                nix_mark_format_cfg_rsp)               \
+M(NIX_SET_RX_CFG,      0x8010, nix_set_rx_cfg, nix_rx_cfg, msg_rsp)    \
+M(NIX_LSO_FORMAT_CFG,  0x8011, nix_lso_format_cfg,                     \
+                                nix_lso_format_cfg,                    \
+                                nix_lso_format_cfg_rsp)                \
+M(NIX_RXVLAN_ALLOC,    0x8012, nix_rxvlan_alloc, msg_req, msg_rsp)
 
 /* Messages initiated by AF (range 0xC00 - 0xDFF) */
 #define MBOX_UP_CGX_MESSAGES                                           \
-M(CGX_LINK_EVENT,              0xC00, cgx_link_info_msg, msg_rsp)
+M(CGX_LINK_EVENT,      0xC00, cgx_link_event, cgx_link_info_msg, msg_rsp)
 
 enum {
-#define M(_name, _id, _1, _2) MBOX_MSG_ ## _name = _id,
+#define M(_name, _id, _1, _2, _3) MBOX_MSG_ ## _name = _id,
 MBOX_MESSAGES
 MBOX_UP_CGX_MESSAGES
 #undef M
@@ -191,6 +238,13 @@ struct msg_rsp {
        struct mbox_msghdr hdr;
 };
 
+/* RVU mailbox error codes
+ * Range 256 - 300.
+ */
+enum rvu_af_status {
+       RVU_INVALID_VF_ID           = -256,
+};
+
 struct ready_msg_rsp {
        struct mbox_msghdr hdr;
        u16    sclk_feq;        /* SCLK frequency */
@@ -347,6 +401,8 @@ struct hwctx_disable_req {
        u8 ctype;
 };
 
+/* NIX mbox message formats */
+
 /* NIX mailbox error codes
  * Range 401 - 500.
  */
@@ -365,6 +421,12 @@ enum nix_af_status {
        NIX_AF_INVAL_TXSCHQ_CFG     = -412,
        NIX_AF_SMQ_FLUSH_FAILED     = -413,
        NIX_AF_ERR_LF_RESET         = -414,
+       NIX_AF_ERR_RSS_NOSPC_FIELD  = -415,
+       NIX_AF_ERR_RSS_NOSPC_ALGO   = -416,
+       NIX_AF_ERR_MARK_CFG_FAIL    = -417,
+       NIX_AF_ERR_LSO_CFG_FAIL     = -418,
+       NIX_AF_INVAL_NPA_PF_FUNC    = -419,
+       NIX_AF_INVAL_SSO_PF_FUNC    = -420,
 };
 
 /* For NIX LF context alloc and init */
@@ -392,6 +454,10 @@ struct nix_lf_alloc_rsp {
        u8      lso_tsov4_idx;
        u8      lso_tsov6_idx;
        u8      mac_addr[ETH_ALEN];
+       u8      lf_rx_stats; /* NIX_AF_CONST1::LF_RX_STATS */
+       u8      lf_tx_stats; /* NIX_AF_CONST1::LF_TX_STATS */
+       u16     cints; /* NIX_AF_CONST2::CINTS */
+       u16     qints; /* NIX_AF_CONST2::QINTS */
 };
 
 /* NIX AQ enqueue msg */
@@ -472,6 +538,7 @@ struct nix_txschq_config {
 
 struct nix_vtag_config {
        struct mbox_msghdr hdr;
+       /* '0' for 4 octet VTAG, '1' for 8 octet VTAG */
        u8 vtag_size;
        /* cfg_type is '0' for tx vlan cfg
         * cfg_type is '1' for rx vlan cfg
@@ -492,7 +559,7 @@ struct nix_vtag_config {
 
                /* valid when cfg_type is '1' */
                struct {
-                       /* rx vtag type index */
+                       /* rx vtag type index, valid values are in 0..7 range */
                        u8 vtag_type;
                        /* rx vtag strip */
                        u8 strip_vtag :1;
@@ -505,15 +572,40 @@ struct nix_vtag_config {
 struct nix_rss_flowkey_cfg {
        struct mbox_msghdr hdr;
        int     mcam_index;  /* MCAM entry index to modify */
+#define NIX_FLOW_KEY_TYPE_PORT BIT(0)
+#define NIX_FLOW_KEY_TYPE_IPV4 BIT(1)
+#define NIX_FLOW_KEY_TYPE_IPV6 BIT(2)
+#define NIX_FLOW_KEY_TYPE_TCP  BIT(3)
+#define NIX_FLOW_KEY_TYPE_UDP  BIT(4)
+#define NIX_FLOW_KEY_TYPE_SCTP BIT(5)
        u32     flowkey_cfg; /* Flowkey types selected */
        u8      group;       /* RSS context or group */
 };
 
+struct nix_rss_flowkey_cfg_rsp {
+       struct mbox_msghdr hdr;
+       u8      alg_idx; /* Selected algo index */
+};
+
 struct nix_set_mac_addr {
        struct mbox_msghdr hdr;
        u8 mac_addr[ETH_ALEN]; /* MAC address to be set for this pcifunc */
 };
 
+struct nix_mark_format_cfg {
+       struct mbox_msghdr hdr;
+       u8 offset;
+       u8 y_mask;
+       u8 y_val;
+       u8 r_mask;
+       u8 r_val;
+};
+
+struct nix_mark_format_cfg_rsp {
+       struct mbox_msghdr hdr;
+       u8 mark_format_idx;
+};
+
 struct nix_rx_mode {
        struct mbox_msghdr hdr;
 #define NIX_RX_MODE_UCAST      BIT(0)
@@ -522,4 +614,182 @@ struct nix_rx_mode {
        u16     mode;
 };
 
+struct nix_rx_cfg {
+       struct mbox_msghdr hdr;
+#define NIX_RX_OL3_VERIFY   BIT(0)
+#define NIX_RX_OL4_VERIFY   BIT(1)
+       u8 len_verify; /* Outer L3/L4 len check */
+#define NIX_RX_CSUM_OL4_VERIFY  BIT(0)
+       u8 csum_verify; /* Outer L4 checksum verification */
+};
+
+struct nix_frs_cfg {
+       struct mbox_msghdr hdr;
+       u8      update_smq;    /* Update SMQ's min/max lens */
+       u8      update_minlen; /* Set minlen also */
+       u8      sdp_link;      /* Set SDP RX link */
+       u16     maxlen;
+       u16     minlen;
+};
+
+struct nix_lso_format_cfg {
+       struct mbox_msghdr hdr;
+       u64 field_mask;
+#define NIX_LSO_FIELD_MAX      8
+       u64 fields[NIX_LSO_FIELD_MAX];
+};
+
+struct nix_lso_format_cfg_rsp {
+       struct mbox_msghdr hdr;
+       u8 lso_format_idx;
+};
+
+/* NPC mbox message structs */
+
+#define NPC_MCAM_ENTRY_INVALID 0xFFFF
+#define NPC_MCAM_INVALID_MAP   0xFFFF
+
+/* NPC mailbox error codes
+ * Range 701 - 800.
+ */
+enum npc_af_status {
+       NPC_MCAM_INVALID_REQ    = -701,
+       NPC_MCAM_ALLOC_DENIED   = -702,
+       NPC_MCAM_ALLOC_FAILED   = -703,
+       NPC_MCAM_PERM_DENIED    = -704,
+};
+
+struct npc_mcam_alloc_entry_req {
+       struct mbox_msghdr hdr;
+#define NPC_MAX_NONCONTIG_ENTRIES      256
+       u8  contig;   /* Contiguous entries ? */
+#define NPC_MCAM_ANY_PRIO              0
+#define NPC_MCAM_LOWER_PRIO            1
+#define NPC_MCAM_HIGHER_PRIO           2
+       u8  priority; /* Lower or higher w.r.t ref_entry */
+       u16 ref_entry;
+       u16 count;    /* Number of entries requested */
+};
+
+struct npc_mcam_alloc_entry_rsp {
+       struct mbox_msghdr hdr;
+       u16 entry; /* Entry allocated or start index if contiguous.
+                   * Invalid incase of non-contiguous.
+                   */
+       u16 count; /* Number of entries allocated */
+       u16 free_count; /* Number of entries available */
+       u16 entry_list[NPC_MAX_NONCONTIG_ENTRIES];
+};
+
+struct npc_mcam_free_entry_req {
+       struct mbox_msghdr hdr;
+       u16 entry; /* Entry index to be freed */
+       u8  all;   /* If all entries allocated to this PFVF to be freed */
+};
+
+struct mcam_entry {
+#define NPC_MAX_KWS_IN_KEY     7 /* Number of keywords in max keywidth */
+       u64     kw[NPC_MAX_KWS_IN_KEY];
+       u64     kw_mask[NPC_MAX_KWS_IN_KEY];
+       u64     action;
+       u64     vtag_action;
+};
+
+struct npc_mcam_write_entry_req {
+       struct mbox_msghdr hdr;
+       struct mcam_entry entry_data;
+       u16 entry;       /* MCAM entry to write this match key */
+       u16 cntr;        /* Counter for this MCAM entry */
+       u8  intf;        /* Rx or Tx interface */
+       u8  enable_entry;/* Enable this MCAM entry ? */
+       u8  set_cntr;    /* Set counter for this entry ? */
+};
+
+/* Enable/Disable a given entry */
+struct npc_mcam_ena_dis_entry_req {
+       struct mbox_msghdr hdr;
+       u16 entry;
+};
+
+struct npc_mcam_shift_entry_req {
+       struct mbox_msghdr hdr;
+#define NPC_MCAM_MAX_SHIFTS    64
+       u16 curr_entry[NPC_MCAM_MAX_SHIFTS];
+       u16 new_entry[NPC_MCAM_MAX_SHIFTS];
+       u16 shift_count; /* Number of entries to shift */
+};
+
+struct npc_mcam_shift_entry_rsp {
+       struct mbox_msghdr hdr;
+       u16 failed_entry_idx; /* Index in 'curr_entry', not entry itself */
+};
+
+struct npc_mcam_alloc_counter_req {
+       struct mbox_msghdr hdr;
+       u8  contig;     /* Contiguous counters ? */
+#define NPC_MAX_NONCONTIG_COUNTERS       64
+       u16 count;      /* Number of counters requested */
+};
+
+struct npc_mcam_alloc_counter_rsp {
+       struct mbox_msghdr hdr;
+       u16 cntr;   /* Counter allocated or start index if contiguous.
+                    * Invalid incase of non-contiguous.
+                    */
+       u16 count;  /* Number of counters allocated */
+       u16 cntr_list[NPC_MAX_NONCONTIG_COUNTERS];
+};
+
+struct npc_mcam_oper_counter_req {
+       struct mbox_msghdr hdr;
+       u16 cntr;   /* Free a counter or clear/fetch it's stats */
+};
+
+struct npc_mcam_oper_counter_rsp {
+       struct mbox_msghdr hdr;
+       u64 stat;  /* valid only while fetching counter's stats */
+};
+
+struct npc_mcam_unmap_counter_req {
+       struct mbox_msghdr hdr;
+       u16 cntr;
+       u16 entry; /* Entry and counter to be unmapped */
+       u8  all;   /* Unmap all entries using this counter ? */
+};
+
+struct npc_mcam_alloc_and_write_entry_req {
+       struct mbox_msghdr hdr;
+       struct mcam_entry entry_data;
+       u16 ref_entry;
+       u8  priority;    /* Lower or higher w.r.t ref_entry */
+       u8  intf;        /* Rx or Tx interface */
+       u8  enable_entry;/* Enable this MCAM entry ? */
+       u8  alloc_cntr;  /* Allocate counter and map ? */
+};
+
+struct npc_mcam_alloc_and_write_entry_rsp {
+       struct mbox_msghdr hdr;
+       u16 entry;
+       u16 cntr;
+};
+
+struct npc_get_kex_cfg_rsp {
+       struct mbox_msghdr hdr;
+       u64 rx_keyx_cfg;   /* NPC_AF_INTF(0)_KEX_CFG */
+       u64 tx_keyx_cfg;   /* NPC_AF_INTF(1)_KEX_CFG */
+#define NPC_MAX_INTF   2
+#define NPC_MAX_LID    8
+#define NPC_MAX_LT     16
+#define NPC_MAX_LD     2
+#define NPC_MAX_LFL    16
+       /* NPC_AF_KEX_LDATA(0..1)_FLAGS_CFG */
+       u64 kex_ld_flags[NPC_MAX_LD];
+       /* NPC_AF_INTF(0..1)_LID(0..7)_LT(0..15)_LD(0..1)_CFG */
+       u64 intf_lid_lt_ld[NPC_MAX_INTF][NPC_MAX_LID][NPC_MAX_LT][NPC_MAX_LD];
+       /* NPC_AF_INTF(0..1)_LDATA(0..1)_FLAGS(0..15)_CFG */
+       u64 intf_ld_flags[NPC_MAX_INTF][NPC_MAX_LD][NPC_MAX_LFL];
+#define MKEX_NAME_LEN 128
+       u8 mkex_pfl_name[MKEX_NAME_LEN];
+};
+
 #endif /* MBOX_H */
index f98b011..8d6d90f 100644 (file)
@@ -259,4 +259,28 @@ struct nix_rx_action {
 #endif
 };
 
+/* NIX Receive Vtag Action Structure */
+#define VTAG0_VALID_BIT                BIT_ULL(15)
+#define VTAG0_TYPE_MASK                GENMASK_ULL(14, 12)
+#define VTAG0_LID_MASK         GENMASK_ULL(10, 8)
+#define VTAG0_RELPTR_MASK      GENMASK_ULL(7, 0)
+
+struct npc_mcam_kex {
+       /* MKEX Profle Header */
+       u64 mkex_sign; /* "mcam-kex-profile" (8 bytes/ASCII characters) */
+       u8 name[MKEX_NAME_LEN];   /* MKEX Profile name */
+       u64 cpu_model;   /* Format as profiled by CPU hardware */
+       u64 kpu_version; /* KPU firmware/profile version */
+       u64 reserved; /* Reserved for extension */
+
+       /* MKEX Profle Data */
+       u64 keyx_cfg[NPC_MAX_INTF]; /* NPC_AF_INTF(0..1)_KEX_CFG */
+       /* NPC_AF_KEX_LDATA(0..1)_FLAGS_CFG */
+       u64 kex_ld_flags[NPC_MAX_LD];
+       /* NPC_AF_INTF(0..1)_LID(0..7)_LT(0..15)_LD(0..1)_CFG */
+       u64 intf_lid_lt_ld[NPC_MAX_INTF][NPC_MAX_LID][NPC_MAX_LT][NPC_MAX_LD];
+       /* NPC_AF_INTF(0..1)_LDATA(0..1)_FLAGS(0..15)_CFG */
+       u64 intf_ld_flags[NPC_MAX_INTF][NPC_MAX_LD][NPC_MAX_LFL];
+} __packed;
+
 #endif /* NPC_H */
index dc28fa2..e581091 100644 (file)
@@ -29,6 +29,16 @@ static void rvu_set_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
                                struct rvu_block *block, int lf);
 static void rvu_clear_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
                                  struct rvu_block *block, int lf);
+static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc);
+
+static int rvu_mbox_init(struct rvu *rvu, struct mbox_wq_info *mw,
+                        int type, int num,
+                        void (mbox_handler)(struct work_struct *),
+                        void (mbox_up_handler)(struct work_struct *));
+enum {
+       TYPE_AFVF,
+       TYPE_AFPF,
+};
 
 /* Supported devices */
 static const struct pci_device_id rvu_id_table[] = {
@@ -42,6 +52,10 @@ MODULE_LICENSE("GPL v2");
 MODULE_VERSION(DRV_VERSION);
 MODULE_DEVICE_TABLE(pci, rvu_id_table);
 
+static char *mkex_profile; /* MKEX profile name */
+module_param(mkex_profile, charp, 0000);
+MODULE_PARM_DESC(mkex_profile, "MKEX profile name string");
+
 /* Poll a RVU block's register 'offset', for a 'zero'
  * or 'nonzero' at bits specified by 'mask'
  */
@@ -153,17 +167,17 @@ int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot)
        u16 match = 0;
        int lf;
 
-       spin_lock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
        for (lf = 0; lf < block->lf.max; lf++) {
                if (block->fn_map[lf] == pcifunc) {
                        if (slot == match) {
-                               spin_unlock(&rvu->rsrc_lock);
+                               mutex_unlock(&rvu->rsrc_lock);
                                return lf;
                        }
                        match++;
                }
        }
-       spin_unlock(&rvu->rsrc_lock);
+       mutex_unlock(&rvu->rsrc_lock);
        return -ENODEV;
 }
 
@@ -337,6 +351,28 @@ struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc)
                return &rvu->pf[rvu_get_pf(pcifunc)];
 }
 
+static bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc)
+{
+       int pf, vf, nvfs;
+       u64 cfg;
+
+       pf = rvu_get_pf(pcifunc);
+       if (pf >= rvu->hw->total_pfs)
+               return false;
+
+       if (!(pcifunc & RVU_PFVF_FUNC_MASK))
+               return true;
+
+       /* Check if VF is within number of VFs attached to this PF */
+       vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1;
+       cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
+       nvfs = (cfg >> 12) & 0xFF;
+       if (vf >= nvfs)
+               return false;
+
+       return true;
+}
+
 bool is_block_implemented(struct rvu_hwinfo *hw, int blkaddr)
 {
        struct rvu_block *block;
@@ -597,6 +633,8 @@ static void rvu_free_hw_resources(struct rvu *rvu)
        dma_unmap_resource(rvu->dev, rvu->msix_base_iova,
                           max_msix * PCI_MSIX_ENTRY_SIZE,
                           DMA_BIDIRECTIONAL, 0);
+
+       mutex_destroy(&rvu->rsrc_lock);
 }
 
 static int rvu_setup_hw_resources(struct rvu *rvu)
@@ -752,7 +790,7 @@ init:
        if (!rvu->hwvf)
                return -ENOMEM;
 
-       spin_lock_init(&rvu->rsrc_lock);
+       mutex_init(&rvu->rsrc_lock);
 
        err = rvu_setup_msix_resources(rvu);
        if (err)
@@ -777,17 +815,26 @@ init:
 
        err = rvu_npc_init(rvu);
        if (err)
-               return err;
+               goto exit;
+
+       err = rvu_cgx_init(rvu);
+       if (err)
+               goto exit;
 
        err = rvu_npa_init(rvu);
        if (err)
-               return err;
+               goto cgx_err;
 
        err = rvu_nix_init(rvu);
        if (err)
-               return err;
+               goto cgx_err;
 
        return 0;
+
+cgx_err:
+       rvu_cgx_exit(rvu);
+exit:
+       return err;
 }
 
 /* NPA and NIX admin queue APIs */
@@ -830,7 +877,7 @@ int rvu_aq_alloc(struct rvu *rvu, struct admin_queue **ad_queue,
        return 0;
 }
 
-static int rvu_mbox_handler_READY(struct rvu *rvu, struct msg_req *req,
+static int rvu_mbox_handler_ready(struct rvu *rvu, struct msg_req *req,
                                  struct ready_msg_rsp *rsp)
 {
        return 0;
@@ -858,6 +905,22 @@ static u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blktype)
        return 0;
 }
 
+bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype)
+{
+       struct rvu_pfvf *pfvf;
+
+       if (!is_pf_func_valid(rvu, pcifunc))
+               return false;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+
+       /* Check if this PFFUNC has a LF of type blktype attached */
+       if (!rvu_get_rsrc_mapcount(pfvf, blktype))
+               return false;
+
+       return true;
+}
+
 static int rvu_lookup_rsrc(struct rvu *rvu, struct rvu_block *block,
                           int pcifunc, int slot)
 {
@@ -926,7 +989,7 @@ static int rvu_detach_rsrcs(struct rvu *rvu, struct rsrc_detach *detach,
        struct rvu_block *block;
        int blkid;
 
-       spin_lock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
 
        /* Check for partial resource detach */
        if (detach && detach->partial)
@@ -956,11 +1019,11 @@ static int rvu_detach_rsrcs(struct rvu *rvu, struct rsrc_detach *detach,
                rvu_detach_block(rvu, pcifunc, block->type);
        }
 
-       spin_unlock(&rvu->rsrc_lock);
+       mutex_unlock(&rvu->rsrc_lock);
        return 0;
 }
 
-static int rvu_mbox_handler_DETACH_RESOURCES(struct rvu *rvu,
+static int rvu_mbox_handler_detach_resources(struct rvu *rvu,
                                             struct rsrc_detach *detach,
                                             struct msg_rsp *rsp)
 {
@@ -1108,7 +1171,7 @@ fail:
        return -ENOSPC;
 }
 
-static int rvu_mbox_handler_ATTACH_RESOURCES(struct rvu *rvu,
+static int rvu_mbox_handler_attach_resources(struct rvu *rvu,
                                             struct rsrc_attach *attach,
                                             struct msg_rsp *rsp)
 {
@@ -1119,7 +1182,7 @@ static int rvu_mbox_handler_ATTACH_RESOURCES(struct rvu *rvu,
        if (!attach->modify)
                rvu_detach_rsrcs(rvu, NULL, pcifunc);
 
-       spin_lock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
 
        /* Check if the request can be accommodated */
        err = rvu_check_rsrc_availability(rvu, attach, pcifunc);
@@ -1163,7 +1226,7 @@ static int rvu_mbox_handler_ATTACH_RESOURCES(struct rvu *rvu,
        }
 
 exit:
-       spin_unlock(&rvu->rsrc_lock);
+       mutex_unlock(&rvu->rsrc_lock);
        return err;
 }
 
@@ -1231,7 +1294,7 @@ static void rvu_clear_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
        rvu_free_rsrc_contig(&pfvf->msix, nvecs, offset);
 }
 
-static int rvu_mbox_handler_MSIX_OFFSET(struct rvu *rvu, struct msg_req *req,
+static int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req,
                                        struct msix_offset_rsp *rsp)
 {
        struct rvu_hwinfo *hw = rvu->hw;
@@ -1280,22 +1343,51 @@ static int rvu_mbox_handler_MSIX_OFFSET(struct rvu *rvu, struct msg_req *req,
        return 0;
 }
 
-static int rvu_process_mbox_msg(struct rvu *rvu, int devid,
+static int rvu_mbox_handler_vf_flr(struct rvu *rvu, struct msg_req *req,
+                                  struct msg_rsp *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       u16 vf, numvfs;
+       u64 cfg;
+
+       vf = pcifunc & RVU_PFVF_FUNC_MASK;
+       cfg = rvu_read64(rvu, BLKADDR_RVUM,
+                        RVU_PRIV_PFX_CFG(rvu_get_pf(pcifunc)));
+       numvfs = (cfg >> 12) & 0xFF;
+
+       if (vf && vf <= numvfs)
+               __rvu_flr_handler(rvu, pcifunc);
+       else
+               return RVU_INVALID_VF_ID;
+
+       return 0;
+}
+
+static int rvu_process_mbox_msg(struct otx2_mbox *mbox, int devid,
                                struct mbox_msghdr *req)
 {
+       struct rvu *rvu = pci_get_drvdata(mbox->pdev);
+
        /* Check if valid, if not reply with a invalid msg */
        if (req->sig != OTX2_MBOX_REQ_SIG)
                goto bad_message;
 
        switch (req->id) {
-#define M(_name, _id, _req_type, _rsp_type)                            \
+#define M(_name, _id, _fn_name, _req_type, _rsp_type)                  \
        case _id: {                                                     \
                struct _rsp_type *rsp;                                  \
                int err;                                                \
                                                                        \
                rsp = (struct _rsp_type *)otx2_mbox_alloc_msg(          \
-                       &rvu->mbox, devid,                              \
+                       mbox, devid,                                    \
                        sizeof(struct _rsp_type));                      \
+               /* some handlers should complete even if reply */       \
+               /* could not be allocated */                            \
+               if (!rsp &&                                             \
+                   _id != MBOX_MSG_DETACH_RESOURCES &&                 \
+                   _id != MBOX_MSG_NIX_TXSCH_FREE &&                   \
+                   _id != MBOX_MSG_VF_FLR)                             \
+                       return -ENOMEM;                                 \
                if (rsp) {                                              \
                        rsp->hdr.id = _id;                              \
                        rsp->hdr.sig = OTX2_MBOX_RSP_SIG;               \
@@ -1303,9 +1395,9 @@ static int rvu_process_mbox_msg(struct rvu *rvu, int devid,
                        rsp->hdr.rc = 0;                                \
                }                                                       \
                                                                        \
-               err = rvu_mbox_handler_ ## _name(rvu,                   \
-                                                (struct _req_type *)req, \
-                                                rsp);                  \
+               err = rvu_mbox_handler_ ## _fn_name(rvu,                \
+                                                   (struct _req_type *)req, \
+                                                   rsp);               \
                if (rsp && err)                                         \
                        rsp->hdr.rc = err;                              \
                                                                        \
@@ -1313,29 +1405,38 @@ static int rvu_process_mbox_msg(struct rvu *rvu, int devid,
        }
 MBOX_MESSAGES
 #undef M
-               break;
+
 bad_message:
        default:
-               otx2_reply_invalid_msg(&rvu->mbox, devid, req->pcifunc,
-                                      req->id);
+               otx2_reply_invalid_msg(mbox, devid, req->pcifunc, req->id);
                return -ENODEV;
        }
 }
 
-static void rvu_mbox_handler(struct work_struct *work)
+static void __rvu_mbox_handler(struct rvu_work *mwork, int type)
 {
-       struct rvu_work *mwork = container_of(work, struct rvu_work, work);
        struct rvu *rvu = mwork->rvu;
+       int offset, err, id, devid;
        struct otx2_mbox_dev *mdev;
        struct mbox_hdr *req_hdr;
        struct mbox_msghdr *msg;
+       struct mbox_wq_info *mw;
        struct otx2_mbox *mbox;
-       int offset, id, err;
-       u16 pf;
 
-       mbox = &rvu->mbox;
-       pf = mwork - rvu->mbox_wrk;
-       mdev = &mbox->dev[pf];
+       switch (type) {
+       case TYPE_AFPF:
+               mw = &rvu->afpf_wq_info;
+               break;
+       case TYPE_AFVF:
+               mw = &rvu->afvf_wq_info;
+               break;
+       default:
+               return;
+       }
+
+       devid = mwork - mw->mbox_wrk;
+       mbox = &mw->mbox;
+       mdev = &mbox->dev[devid];
 
        /* Process received mbox messages */
        req_hdr = mdev->mbase + mbox->rx_start;
@@ -1347,10 +1448,21 @@ static void rvu_mbox_handler(struct work_struct *work)
        for (id = 0; id < req_hdr->num_msgs; id++) {
                msg = mdev->mbase + offset;
 
-               /* Set which PF sent this message based on mbox IRQ */
-               msg->pcifunc &= ~(RVU_PFVF_PF_MASK << RVU_PFVF_PF_SHIFT);
-               msg->pcifunc |= (pf << RVU_PFVF_PF_SHIFT);
-               err = rvu_process_mbox_msg(rvu, pf, msg);
+               /* Set which PF/VF sent this message based on mbox IRQ */
+               switch (type) {
+               case TYPE_AFPF:
+                       msg->pcifunc &=
+                               ~(RVU_PFVF_PF_MASK << RVU_PFVF_PF_SHIFT);
+                       msg->pcifunc |= (devid << RVU_PFVF_PF_SHIFT);
+                       break;
+               case TYPE_AFVF:
+                       msg->pcifunc &=
+                               ~(RVU_PFVF_FUNC_MASK << RVU_PFVF_FUNC_SHIFT);
+                       msg->pcifunc |= (devid << RVU_PFVF_FUNC_SHIFT) + 1;
+                       break;
+               }
+
+               err = rvu_process_mbox_msg(mbox, devid, msg);
                if (!err) {
                        offset = mbox->rx_start + msg->next_msgoff;
                        continue;
@@ -1358,31 +1470,57 @@ static void rvu_mbox_handler(struct work_struct *work)
 
                if (msg->pcifunc & RVU_PFVF_FUNC_MASK)
                        dev_warn(rvu->dev, "Error %d when processing message %s (0x%x) from PF%d:VF%d\n",
-                                err, otx2_mbox_id2name(msg->id), msg->id, pf,
+                                err, otx2_mbox_id2name(msg->id),
+                                msg->id, devid,
                                 (msg->pcifunc & RVU_PFVF_FUNC_MASK) - 1);
                else
                        dev_warn(rvu->dev, "Error %d when processing message %s (0x%x) from PF%d\n",
-                                err, otx2_mbox_id2name(msg->id), msg->id, pf);
+                                err, otx2_mbox_id2name(msg->id),
+                                msg->id, devid);
        }
 
-       /* Send mbox responses to PF */
-       otx2_mbox_msg_send(mbox, pf);
+       /* Send mbox responses to VF/PF */
+       otx2_mbox_msg_send(mbox, devid);
+}
+
+static inline void rvu_afpf_mbox_handler(struct work_struct *work)
+{
+       struct rvu_work *mwork = container_of(work, struct rvu_work, work);
+
+       __rvu_mbox_handler(mwork, TYPE_AFPF);
 }
 
-static void rvu_mbox_up_handler(struct work_struct *work)
+static inline void rvu_afvf_mbox_handler(struct work_struct *work)
 {
        struct rvu_work *mwork = container_of(work, struct rvu_work, work);
+
+       __rvu_mbox_handler(mwork, TYPE_AFVF);
+}
+
+static void __rvu_mbox_up_handler(struct rvu_work *mwork, int type)
+{
        struct rvu *rvu = mwork->rvu;
        struct otx2_mbox_dev *mdev;
        struct mbox_hdr *rsp_hdr;
        struct mbox_msghdr *msg;
+       struct mbox_wq_info *mw;
        struct otx2_mbox *mbox;
-       int offset, id;
-       u16 pf;
+       int offset, id, devid;
+
+       switch (type) {
+       case TYPE_AFPF:
+               mw = &rvu->afpf_wq_info;
+               break;
+       case TYPE_AFVF:
+               mw = &rvu->afvf_wq_info;
+               break;
+       default:
+               return;
+       }
 
-       mbox = &rvu->mbox_up;
-       pf = mwork - rvu->mbox_wrk_up;
-       mdev = &mbox->dev[pf];
+       devid = mwork - mw->mbox_wrk_up;
+       mbox = &mw->mbox_up;
+       mdev = &mbox->dev[devid];
 
        rsp_hdr = mdev->mbase + mbox->rx_start;
        if (rsp_hdr->num_msgs == 0) {
@@ -1423,128 +1561,182 @@ end:
                mdev->msgs_acked++;
        }
 
-       otx2_mbox_reset(mbox, 0);
+       otx2_mbox_reset(mbox, devid);
 }
 
-static int rvu_mbox_init(struct rvu *rvu)
+static inline void rvu_afpf_mbox_up_handler(struct work_struct *work)
 {
-       struct rvu_hwinfo *hw = rvu->hw;
-       void __iomem *hwbase = NULL;
+       struct rvu_work *mwork = container_of(work, struct rvu_work, work);
+
+       __rvu_mbox_up_handler(mwork, TYPE_AFPF);
+}
+
+static inline void rvu_afvf_mbox_up_handler(struct work_struct *work)
+{
+       struct rvu_work *mwork = container_of(work, struct rvu_work, work);
+
+       __rvu_mbox_up_handler(mwork, TYPE_AFVF);
+}
+
+static int rvu_mbox_init(struct rvu *rvu, struct mbox_wq_info *mw,
+                        int type, int num,
+                        void (mbox_handler)(struct work_struct *),
+                        void (mbox_up_handler)(struct work_struct *))
+{
+       void __iomem *hwbase = NULL, *reg_base;
+       int err, i, dir, dir_up;
        struct rvu_work *mwork;
+       const char *name;
        u64 bar4_addr;
-       int err, pf;
 
-       rvu->mbox_wq = alloc_workqueue("rvu_afpf_mailbox",
-                                      WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM,
-                                      hw->total_pfs);
-       if (!rvu->mbox_wq)
+       switch (type) {
+       case TYPE_AFPF:
+               name = "rvu_afpf_mailbox";
+               bar4_addr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PF_BAR4_ADDR);
+               dir = MBOX_DIR_AFPF;
+               dir_up = MBOX_DIR_AFPF_UP;
+               reg_base = rvu->afreg_base;
+               break;
+       case TYPE_AFVF:
+               name = "rvu_afvf_mailbox";
+               bar4_addr = rvupf_read64(rvu, RVU_PF_VF_BAR4_ADDR);
+               dir = MBOX_DIR_PFVF;
+               dir_up = MBOX_DIR_PFVF_UP;
+               reg_base = rvu->pfreg_base;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       mw->mbox_wq = alloc_workqueue(name,
+                                     WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM,
+                                     num);
+       if (!mw->mbox_wq)
                return -ENOMEM;
 
-       rvu->mbox_wrk = devm_kcalloc(rvu->dev, hw->total_pfs,
-                                    sizeof(struct rvu_work), GFP_KERNEL);
-       if (!rvu->mbox_wrk) {
+       mw->mbox_wrk = devm_kcalloc(rvu->dev, num,
+                                   sizeof(struct rvu_work), GFP_KERNEL);
+       if (!mw->mbox_wrk) {
                err = -ENOMEM;
                goto exit;
        }
 
-       rvu->mbox_wrk_up = devm_kcalloc(rvu->dev, hw->total_pfs,
-                                       sizeof(struct rvu_work), GFP_KERNEL);
-       if (!rvu->mbox_wrk_up) {
+       mw->mbox_wrk_up = devm_kcalloc(rvu->dev, num,
+                                      sizeof(struct rvu_work), GFP_KERNEL);
+       if (!mw->mbox_wrk_up) {
                err = -ENOMEM;
                goto exit;
        }
 
-       /* Map mbox region shared with PFs */
-       bar4_addr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PF_BAR4_ADDR);
        /* Mailbox is a reserved memory (in RAM) region shared between
         * RVU devices, shouldn't be mapped as device memory to allow
         * unaligned accesses.
         */
-       hwbase = ioremap_wc(bar4_addr, MBOX_SIZE * hw->total_pfs);
+       hwbase = ioremap_wc(bar4_addr, MBOX_SIZE * num);
        if (!hwbase) {
                dev_err(rvu->dev, "Unable to map mailbox region\n");
                err = -ENOMEM;
                goto exit;
        }
 
-       err = otx2_mbox_init(&rvu->mbox, hwbase, rvu->pdev, rvu->afreg_base,
-                            MBOX_DIR_AFPF, hw->total_pfs);
+       err = otx2_mbox_init(&mw->mbox, hwbase, rvu->pdev, reg_base, dir, num);
        if (err)
                goto exit;
 
-       err = otx2_mbox_init(&rvu->mbox_up, hwbase, rvu->pdev, rvu->afreg_base,
-                            MBOX_DIR_AFPF_UP, hw->total_pfs);
+       err = otx2_mbox_init(&mw->mbox_up, hwbase, rvu->pdev,
+                            reg_base, dir_up, num);
        if (err)
                goto exit;
 
-       for (pf = 0; pf < hw->total_pfs; pf++) {
-               mwork = &rvu->mbox_wrk[pf];
+       for (i = 0; i < num; i++) {
+               mwork = &mw->mbox_wrk[i];
                mwork->rvu = rvu;
-               INIT_WORK(&mwork->work, rvu_mbox_handler);
-       }
+               INIT_WORK(&mwork->work, mbox_handler);
 
-       for (pf = 0; pf < hw->total_pfs; pf++) {
-               mwork = &rvu->mbox_wrk_up[pf];
+               mwork = &mw->mbox_wrk_up[i];
                mwork->rvu = rvu;
-               INIT_WORK(&mwork->work, rvu_mbox_up_handler);
+               INIT_WORK(&mwork->work, mbox_up_handler);
        }
 
        return 0;
 exit:
        if (hwbase)
                iounmap((void __iomem *)hwbase);
-       destroy_workqueue(rvu->mbox_wq);
+       destroy_workqueue(mw->mbox_wq);
        return err;
 }
 
-static void rvu_mbox_destroy(struct rvu *rvu)
+static void rvu_mbox_destroy(struct mbox_wq_info *mw)
 {
-       if (rvu->mbox_wq) {
-               flush_workqueue(rvu->mbox_wq);
-               destroy_workqueue(rvu->mbox_wq);
-               rvu->mbox_wq = NULL;
+       if (mw->mbox_wq) {
+               flush_workqueue(mw->mbox_wq);
+               destroy_workqueue(mw->mbox_wq);
+               mw->mbox_wq = NULL;
        }
 
-       if (rvu->mbox.hwbase)
-               iounmap((void __iomem *)rvu->mbox.hwbase);
+       if (mw->mbox.hwbase)
+               iounmap((void __iomem *)mw->mbox.hwbase);
 
-       otx2_mbox_destroy(&rvu->mbox);
-       otx2_mbox_destroy(&rvu->mbox_up);
+       otx2_mbox_destroy(&mw->mbox);
+       otx2_mbox_destroy(&mw->mbox_up);
 }
 
-static irqreturn_t rvu_mbox_intr_handler(int irq, void *rvu_irq)
+static void rvu_queue_work(struct mbox_wq_info *mw, int first,
+                          int mdevs, u64 intr)
 {
-       struct rvu *rvu = (struct rvu *)rvu_irq;
        struct otx2_mbox_dev *mdev;
        struct otx2_mbox *mbox;
        struct mbox_hdr *hdr;
+       int i;
+
+       for (i = first; i < mdevs; i++) {
+               /* start from 0 */
+               if (!(intr & BIT_ULL(i - first)))
+                       continue;
+
+               mbox = &mw->mbox;
+               mdev = &mbox->dev[i];
+               hdr = mdev->mbase + mbox->rx_start;
+               if (hdr->num_msgs)
+                       queue_work(mw->mbox_wq, &mw->mbox_wrk[i].work);
+
+               mbox = &mw->mbox_up;
+               mdev = &mbox->dev[i];
+               hdr = mdev->mbase + mbox->rx_start;
+               if (hdr->num_msgs)
+                       queue_work(mw->mbox_wq, &mw->mbox_wrk_up[i].work);
+       }
+}
+
+static irqreturn_t rvu_mbox_intr_handler(int irq, void *rvu_irq)
+{
+       struct rvu *rvu = (struct rvu *)rvu_irq;
+       int vfs = rvu->vfs;
        u64 intr;
-       u8  pf;
 
        intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT);
        /* Clear interrupts */
        rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT, intr);
 
        /* Sync with mbox memory region */
-       smp_wmb();
+       rmb();
 
-       for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
-               if (intr & (1ULL << pf)) {
-                       mbox = &rvu->mbox;
-                       mdev = &mbox->dev[pf];
-                       hdr = mdev->mbase + mbox->rx_start;
-                       if (hdr->num_msgs)
-                               queue_work(rvu->mbox_wq,
-                                          &rvu->mbox_wrk[pf].work);
-                       mbox = &rvu->mbox_up;
-                       mdev = &mbox->dev[pf];
-                       hdr = mdev->mbase + mbox->rx_start;
-                       if (hdr->num_msgs)
-                               queue_work(rvu->mbox_wq,
-                                          &rvu->mbox_wrk_up[pf].work);
-               }
+       rvu_queue_work(&rvu->afpf_wq_info, 0, rvu->hw->total_pfs, intr);
+
+       /* Handle VF interrupts */
+       if (vfs > 64) {
+               intr = rvupf_read64(rvu, RVU_PF_VFPF_MBOX_INTX(1));
+               rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(1), intr);
+
+               rvu_queue_work(&rvu->afvf_wq_info, 64, vfs, intr);
+               vfs -= 64;
        }
 
+       intr = rvupf_read64(rvu, RVU_PF_VFPF_MBOX_INTX(0));
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(0), intr);
+
+       rvu_queue_work(&rvu->afvf_wq_info, 0, vfs, intr);
+
        return IRQ_HANDLED;
 }
 
@@ -1561,6 +1753,216 @@ static void rvu_enable_mbox_intr(struct rvu *rvu)
                    INTR_MASK(hw->total_pfs) & ~1ULL);
 }
 
+static void rvu_blklf_teardown(struct rvu *rvu, u16 pcifunc, u8 blkaddr)
+{
+       struct rvu_block *block;
+       int slot, lf, num_lfs;
+       int err;
+
+       block = &rvu->hw->block[blkaddr];
+       num_lfs = rvu_get_rsrc_mapcount(rvu_get_pfvf(rvu, pcifunc),
+                                       block->type);
+       if (!num_lfs)
+               return;
+       for (slot = 0; slot < num_lfs; slot++) {
+               lf = rvu_get_lf(rvu, block, pcifunc, slot);
+               if (lf < 0)
+                       continue;
+
+               /* Cleanup LF and reset it */
+               if (block->addr == BLKADDR_NIX0)
+                       rvu_nix_lf_teardown(rvu, pcifunc, block->addr, lf);
+               else if (block->addr == BLKADDR_NPA)
+                       rvu_npa_lf_teardown(rvu, pcifunc, lf);
+
+               err = rvu_lf_reset(rvu, block, lf);
+               if (err) {
+                       dev_err(rvu->dev, "Failed to reset blkaddr %d LF%d\n",
+                               block->addr, lf);
+               }
+       }
+}
+
+static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc)
+{
+       mutex_lock(&rvu->flr_lock);
+       /* Reset order should reflect inter-block dependencies:
+        * 1. Reset any packet/work sources (NIX, CPT, TIM)
+        * 2. Flush and reset SSO/SSOW
+        * 3. Cleanup pools (NPA)
+        */
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NIX0);
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_CPT0);
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_TIM);
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_SSOW);
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_SSO);
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NPA);
+       rvu_detach_rsrcs(rvu, NULL, pcifunc);
+       mutex_unlock(&rvu->flr_lock);
+}
+
+static void rvu_afvf_flr_handler(struct rvu *rvu, int vf)
+{
+       int reg = 0;
+
+       /* pcifunc = 0(PF0) | (vf + 1) */
+       __rvu_flr_handler(rvu, vf + 1);
+
+       if (vf >= 64) {
+               reg = 1;
+               vf = vf - 64;
+       }
+
+       /* Signal FLR finish and enable IRQ */
+       rvupf_write64(rvu, RVU_PF_VFTRPENDX(reg), BIT_ULL(vf));
+       rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1SX(reg), BIT_ULL(vf));
+}
+
+static void rvu_flr_handler(struct work_struct *work)
+{
+       struct rvu_work *flrwork = container_of(work, struct rvu_work, work);
+       struct rvu *rvu = flrwork->rvu;
+       u16 pcifunc, numvfs, vf;
+       u64 cfg;
+       int pf;
+
+       pf = flrwork - rvu->flr_wrk;
+       if (pf >= rvu->hw->total_pfs) {
+               rvu_afvf_flr_handler(rvu, pf - rvu->hw->total_pfs);
+               return;
+       }
+
+       cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
+       numvfs = (cfg >> 12) & 0xFF;
+       pcifunc  = pf << RVU_PFVF_PF_SHIFT;
+
+       for (vf = 0; vf < numvfs; vf++)
+               __rvu_flr_handler(rvu, (pcifunc | (vf + 1)));
+
+       __rvu_flr_handler(rvu, pcifunc);
+
+       /* Signal FLR finish */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFTRPEND, BIT_ULL(pf));
+
+       /* Enable interrupt */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1S,  BIT_ULL(pf));
+}
+
+static void rvu_afvf_queue_flr_work(struct rvu *rvu, int start_vf, int numvfs)
+{
+       int dev, vf, reg = 0;
+       u64 intr;
+
+       if (start_vf >= 64)
+               reg = 1;
+
+       intr = rvupf_read64(rvu, RVU_PF_VFFLR_INTX(reg));
+       if (!intr)
+               return;
+
+       for (vf = 0; vf < numvfs; vf++) {
+               if (!(intr & BIT_ULL(vf)))
+                       continue;
+               dev = vf + start_vf + rvu->hw->total_pfs;
+               queue_work(rvu->flr_wq, &rvu->flr_wrk[dev].work);
+               /* Clear and disable the interrupt */
+               rvupf_write64(rvu, RVU_PF_VFFLR_INTX(reg), BIT_ULL(vf));
+               rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1CX(reg), BIT_ULL(vf));
+       }
+}
+
+static irqreturn_t rvu_flr_intr_handler(int irq, void *rvu_irq)
+{
+       struct rvu *rvu = (struct rvu *)rvu_irq;
+       u64 intr;
+       u8  pf;
+
+       intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT);
+       if (!intr)
+               goto afvf_flr;
+
+       for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+               if (intr & (1ULL << pf)) {
+                       /* PF is already dead do only AF related operations */
+                       queue_work(rvu->flr_wq, &rvu->flr_wrk[pf].work);
+                       /* clear interrupt */
+                       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT,
+                                   BIT_ULL(pf));
+                       /* Disable the interrupt */
+                       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1C,
+                                   BIT_ULL(pf));
+               }
+       }
+
+afvf_flr:
+       rvu_afvf_queue_flr_work(rvu, 0, 64);
+       if (rvu->vfs > 64)
+               rvu_afvf_queue_flr_work(rvu, 64, rvu->vfs - 64);
+
+       return IRQ_HANDLED;
+}
+
+static void rvu_me_handle_vfset(struct rvu *rvu, int idx, u64 intr)
+{
+       int vf;
+
+       /* Nothing to be done here other than clearing the
+        * TRPEND bit.
+        */
+       for (vf = 0; vf < 64; vf++) {
+               if (intr & (1ULL << vf)) {
+                       /* clear the trpend due to ME(master enable) */
+                       rvupf_write64(rvu, RVU_PF_VFTRPENDX(idx), BIT_ULL(vf));
+                       /* clear interrupt */
+                       rvupf_write64(rvu, RVU_PF_VFME_INTX(idx), BIT_ULL(vf));
+               }
+       }
+}
+
+/* Handles ME interrupts from VFs of AF */
+static irqreturn_t rvu_me_vf_intr_handler(int irq, void *rvu_irq)
+{
+       struct rvu *rvu = (struct rvu *)rvu_irq;
+       int vfset;
+       u64 intr;
+
+       intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT);
+
+       for (vfset = 0; vfset <= 1; vfset++) {
+               intr = rvupf_read64(rvu, RVU_PF_VFME_INTX(vfset));
+               if (intr)
+                       rvu_me_handle_vfset(rvu, vfset, intr);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/* Handles ME interrupts from PFs */
+static irqreturn_t rvu_me_pf_intr_handler(int irq, void *rvu_irq)
+{
+       struct rvu *rvu = (struct rvu *)rvu_irq;
+       u64 intr;
+       u8  pf;
+
+       intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT);
+
+       /* Nothing to be done here other than clearing the
+        * TRPEND bit.
+        */
+       for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+               if (intr & (1ULL << pf)) {
+                       /* clear the trpend due to ME(master enable) */
+                       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFTRPEND,
+                                   BIT_ULL(pf));
+                       /* clear interrupt */
+                       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT,
+                                   BIT_ULL(pf));
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
 static void rvu_unregister_interrupts(struct rvu *rvu)
 {
        int irq;
@@ -1569,6 +1971,14 @@ static void rvu_unregister_interrupts(struct rvu *rvu)
        rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT_ENA_W1C,
                    INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
 
+       /* Disable the PF FLR interrupt */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1C,
+                   INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
+
+       /* Disable the PF ME interrupt */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT_ENA_W1C,
+                   INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
+
        for (irq = 0; irq < rvu->num_vec; irq++) {
                if (rvu->irq_allocated[irq])
                        free_irq(pci_irq_vector(rvu->pdev, irq), rvu);
@@ -1578,9 +1988,25 @@ static void rvu_unregister_interrupts(struct rvu *rvu)
        rvu->num_vec = 0;
 }
 
+static int rvu_afvf_msix_vectors_num_ok(struct rvu *rvu)
+{
+       struct rvu_pfvf *pfvf = &rvu->pf[0];
+       int offset;
+
+       pfvf = &rvu->pf[0];
+       offset = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_INT_CFG(0)) & 0x3ff;
+
+       /* Make sure there are enough MSIX vectors configured so that
+        * VF interrupts can be handled. Offset equal to zero means
+        * that PF vectors are not configured and overlapping AF vectors.
+        */
+       return (pfvf->msix.max >= RVU_AF_INT_VEC_CNT + RVU_PF_INT_VEC_CNT) &&
+              offset;
+}
+
 static int rvu_register_interrupts(struct rvu *rvu)
 {
-       int ret;
+       int ret, offset, pf_vec_start;
 
        rvu->num_vec = pci_msix_vec_count(rvu->pdev);
 
@@ -1620,13 +2046,331 @@ static int rvu_register_interrupts(struct rvu *rvu)
        /* Enable mailbox interrupts from all PFs */
        rvu_enable_mbox_intr(rvu);
 
+       /* Register FLR interrupt handler */
+       sprintf(&rvu->irq_name[RVU_AF_INT_VEC_PFFLR * NAME_SIZE],
+               "RVUAF FLR");
+       ret = request_irq(pci_irq_vector(rvu->pdev, RVU_AF_INT_VEC_PFFLR),
+                         rvu_flr_intr_handler, 0,
+                         &rvu->irq_name[RVU_AF_INT_VEC_PFFLR * NAME_SIZE],
+                         rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for FLR\n");
+               goto fail;
+       }
+       rvu->irq_allocated[RVU_AF_INT_VEC_PFFLR] = true;
+
+       /* Enable FLR interrupt for all PFs*/
+       rvu_write64(rvu, BLKADDR_RVUM,
+                   RVU_AF_PFFLR_INT, INTR_MASK(rvu->hw->total_pfs));
+
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1S,
+                   INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
+
+       /* Register ME interrupt handler */
+       sprintf(&rvu->irq_name[RVU_AF_INT_VEC_PFME * NAME_SIZE],
+               "RVUAF ME");
+       ret = request_irq(pci_irq_vector(rvu->pdev, RVU_AF_INT_VEC_PFME),
+                         rvu_me_pf_intr_handler, 0,
+                         &rvu->irq_name[RVU_AF_INT_VEC_PFME * NAME_SIZE],
+                         rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for ME\n");
+       }
+       rvu->irq_allocated[RVU_AF_INT_VEC_PFME] = true;
+
+       /* Enable ME interrupt for all PFs*/
+       rvu_write64(rvu, BLKADDR_RVUM,
+                   RVU_AF_PFME_INT, INTR_MASK(rvu->hw->total_pfs));
+
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT_ENA_W1S,
+                   INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
+
+       if (!rvu_afvf_msix_vectors_num_ok(rvu))
+               return 0;
+
+       /* Get PF MSIX vectors offset. */
+       pf_vec_start = rvu_read64(rvu, BLKADDR_RVUM,
+                                 RVU_PRIV_PFX_INT_CFG(0)) & 0x3ff;
+
+       /* Register MBOX0 interrupt. */
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFPF_MBOX0;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF Mbox0");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_mbox_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE],
+                         rvu);
+       if (ret)
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for Mbox0\n");
+
+       rvu->irq_allocated[offset] = true;
+
+       /* Register MBOX1 interrupt. MBOX1 IRQ number follows MBOX0 so
+        * simply increment current offset by 1.
+        */
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFPF_MBOX1;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF Mbox1");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_mbox_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE],
+                         rvu);
+       if (ret)
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for Mbox1\n");
+
+       rvu->irq_allocated[offset] = true;
+
+       /* Register FLR interrupt handler for AF's VFs */
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFFLR0;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF FLR0");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_flr_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE], rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for RVUAFVF FLR0\n");
+               goto fail;
+       }
+       rvu->irq_allocated[offset] = true;
+
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFFLR1;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF FLR1");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_flr_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE], rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for RVUAFVF FLR1\n");
+               goto fail;
+       }
+       rvu->irq_allocated[offset] = true;
+
+       /* Register ME interrupt handler for AF's VFs */
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFME0;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF ME0");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_me_vf_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE], rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for RVUAFVF ME0\n");
+               goto fail;
+       }
+       rvu->irq_allocated[offset] = true;
+
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFME1;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF ME1");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_me_vf_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE], rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for RVUAFVF ME1\n");
+               goto fail;
+       }
+       rvu->irq_allocated[offset] = true;
        return 0;
 
 fail:
-       pci_free_irq_vectors(rvu->pdev);
+       rvu_unregister_interrupts(rvu);
+       return ret;
+}
+
+static void rvu_flr_wq_destroy(struct rvu *rvu)
+{
+       if (rvu->flr_wq) {
+               flush_workqueue(rvu->flr_wq);
+               destroy_workqueue(rvu->flr_wq);
+               rvu->flr_wq = NULL;
+       }
+}
+
+static int rvu_flr_init(struct rvu *rvu)
+{
+       int dev, num_devs;
+       u64 cfg;
+       int pf;
+
+       /* Enable FLR for all PFs*/
+       for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+               cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
+               rvu_write64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf),
+                           cfg | BIT_ULL(22));
+       }
+
+       rvu->flr_wq = alloc_workqueue("rvu_afpf_flr",
+                                     WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM,
+                                      1);
+       if (!rvu->flr_wq)
+               return -ENOMEM;
+
+       num_devs = rvu->hw->total_pfs + pci_sriov_get_totalvfs(rvu->pdev);
+       rvu->flr_wrk = devm_kcalloc(rvu->dev, num_devs,
+                                   sizeof(struct rvu_work), GFP_KERNEL);
+       if (!rvu->flr_wrk) {
+               destroy_workqueue(rvu->flr_wq);
+               return -ENOMEM;
+       }
+
+       for (dev = 0; dev < num_devs; dev++) {
+               rvu->flr_wrk[dev].rvu = rvu;
+               INIT_WORK(&rvu->flr_wrk[dev].work, rvu_flr_handler);
+       }
+
+       mutex_init(&rvu->flr_lock);
+
+       return 0;
+}
+
+static void rvu_disable_afvf_intr(struct rvu *rvu)
+{
+       int vfs = rvu->vfs;
+
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1CX(0), INTR_MASK(vfs));
+       rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1CX(0), INTR_MASK(vfs));
+       rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1CX(0), INTR_MASK(vfs));
+       if (vfs <= 64)
+               return;
+
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1CX(1),
+                     INTR_MASK(vfs - 64));
+       rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1CX(1), INTR_MASK(vfs - 64));
+       rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1CX(1), INTR_MASK(vfs - 64));
+}
+
+static void rvu_enable_afvf_intr(struct rvu *rvu)
+{
+       int vfs = rvu->vfs;
+
+       /* Clear any pending interrupts and enable AF VF interrupts for
+        * the first 64 VFs.
+        */
+       /* Mbox */
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(0), INTR_MASK(vfs));
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1SX(0), INTR_MASK(vfs));
+
+       /* FLR */
+       rvupf_write64(rvu, RVU_PF_VFFLR_INTX(0), INTR_MASK(vfs));
+       rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1SX(0), INTR_MASK(vfs));
+       rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1SX(0), INTR_MASK(vfs));
+
+       /* Same for remaining VFs, if any. */
+       if (vfs <= 64)
+               return;
+
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(1), INTR_MASK(vfs - 64));
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1SX(1),
+                     INTR_MASK(vfs - 64));
+
+       rvupf_write64(rvu, RVU_PF_VFFLR_INTX(1), INTR_MASK(vfs - 64));
+       rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1SX(1), INTR_MASK(vfs - 64));
+       rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1SX(1), INTR_MASK(vfs - 64));
+}
+
+#define PCI_DEVID_OCTEONTX2_LBK 0xA061
+
+static int lbk_get_num_chans(void)
+{
+       struct pci_dev *pdev;
+       void __iomem *base;
+       int ret = -EIO;
+
+       pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_LBK,
+                             NULL);
+       if (!pdev)
+               goto err;
+
+       base = pci_ioremap_bar(pdev, 0);
+       if (!base)
+               goto err_put;
+
+       /* Read number of available LBK channels from LBK(0)_CONST register. */
+       ret = (readq(base + 0x10) >> 32) & 0xffff;
+       iounmap(base);
+err_put:
+       pci_dev_put(pdev);
+err:
        return ret;
 }
 
+static int rvu_enable_sriov(struct rvu *rvu)
+{
+       struct pci_dev *pdev = rvu->pdev;
+       int err, chans, vfs;
+
+       if (!rvu_afvf_msix_vectors_num_ok(rvu)) {
+               dev_warn(&pdev->dev,
+                        "Skipping SRIOV enablement since not enough IRQs are available\n");
+               return 0;
+       }
+
+       chans = lbk_get_num_chans();
+       if (chans < 0)
+               return chans;
+
+       vfs = pci_sriov_get_totalvfs(pdev);
+
+       /* Limit VFs in case we have more VFs than LBK channels available. */
+       if (vfs > chans)
+               vfs = chans;
+
+       /* AF's VFs work in pairs and talk over consecutive loopback channels.
+        * Thus we want to enable maximum even number of VFs. In case
+        * odd number of VFs are available then the last VF on the list
+        * remains disabled.
+        */
+       if (vfs & 0x1) {
+               dev_warn(&pdev->dev,
+                        "Number of VFs should be even. Enabling %d out of %d.\n",
+                        vfs - 1, vfs);
+               vfs--;
+       }
+
+       if (!vfs)
+               return 0;
+
+       /* Save VFs number for reference in VF interrupts handlers.
+        * Since interrupts might start arriving during SRIOV enablement
+        * ordinary API cannot be used to get number of enabled VFs.
+        */
+       rvu->vfs = vfs;
+
+       err = rvu_mbox_init(rvu, &rvu->afvf_wq_info, TYPE_AFVF, vfs,
+                           rvu_afvf_mbox_handler, rvu_afvf_mbox_up_handler);
+       if (err)
+               return err;
+
+       rvu_enable_afvf_intr(rvu);
+       /* Make sure IRQs are enabled before SRIOV. */
+       mb();
+
+       err = pci_enable_sriov(pdev, vfs);
+       if (err) {
+               rvu_disable_afvf_intr(rvu);
+               rvu_mbox_destroy(&rvu->afvf_wq_info);
+               return err;
+       }
+
+       return 0;
+}
+
+static void rvu_disable_sriov(struct rvu *rvu)
+{
+       rvu_disable_afvf_intr(rvu);
+       rvu_mbox_destroy(&rvu->afvf_wq_info);
+       pci_disable_sriov(rvu->pdev);
+}
+
+static void rvu_update_module_params(struct rvu *rvu)
+{
+       const char *default_pfl_name = "default";
+
+       strscpy(rvu->mkex_pfl_name,
+               mkex_profile ? mkex_profile : default_pfl_name, MKEX_NAME_LEN);
+}
+
 static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct device *dev = &pdev->dev;
@@ -1680,6 +2424,9 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_release_regions;
        }
 
+       /* Store module params in rvu structure */
+       rvu_update_module_params(rvu);
+
        /* Check which blocks the HW supports */
        rvu_check_block_implemented(rvu);
 
@@ -1689,24 +2436,35 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (err)
                goto err_release_regions;
 
-       err = rvu_mbox_init(rvu);
+       /* Init mailbox btw AF and PFs */
+       err = rvu_mbox_init(rvu, &rvu->afpf_wq_info, TYPE_AFPF,
+                           rvu->hw->total_pfs, rvu_afpf_mbox_handler,
+                           rvu_afpf_mbox_up_handler);
        if (err)
                goto err_hwsetup;
 
-       err = rvu_cgx_probe(rvu);
+       err = rvu_flr_init(rvu);
        if (err)
                goto err_mbox;
 
        err = rvu_register_interrupts(rvu);
        if (err)
-               goto err_cgx;
+               goto err_flr;
+
+       /* Enable AF's VFs (if any) */
+       err = rvu_enable_sriov(rvu);
+       if (err)
+               goto err_irq;
 
        return 0;
-err_cgx:
-       rvu_cgx_wq_destroy(rvu);
+err_irq:
+       rvu_unregister_interrupts(rvu);
+err_flr:
+       rvu_flr_wq_destroy(rvu);
 err_mbox:
-       rvu_mbox_destroy(rvu);
+       rvu_mbox_destroy(&rvu->afpf_wq_info);
 err_hwsetup:
+       rvu_cgx_exit(rvu);
        rvu_reset_all_blocks(rvu);
        rvu_free_hw_resources(rvu);
 err_release_regions:
@@ -1725,8 +2483,10 @@ static void rvu_remove(struct pci_dev *pdev)
        struct rvu *rvu = pci_get_drvdata(pdev);
 
        rvu_unregister_interrupts(rvu);
-       rvu_cgx_wq_destroy(rvu);
-       rvu_mbox_destroy(rvu);
+       rvu_flr_wq_destroy(rvu);
+       rvu_cgx_exit(rvu);
+       rvu_mbox_destroy(&rvu->afpf_wq_info);
+       rvu_disable_sriov(rvu);
        rvu_reset_all_blocks(rvu);
        rvu_free_hw_resources(rvu);
 
index 2c0580c..c9d60b0 100644 (file)
@@ -11,6 +11,7 @@
 #ifndef RVU_H
 #define RVU_H
 
+#include <linux/pci.h>
 #include "rvu_struct.h"
 #include "common.h"
 #include "mbox.h"
@@ -18,6 +19,9 @@
 /* PCI device IDs */
 #define        PCI_DEVID_OCTEONTX2_RVU_AF              0xA065
 
+/* Subsystem Device ID */
+#define PCI_SUBSYS_DEVID_96XX                  0xB200
+
 /* PCI BAR nos */
 #define        PCI_AF_REG_BAR_NUM                      0
 #define        PCI_PF_REG_BAR_NUM                      2
@@ -64,7 +68,7 @@ struct nix_mcast {
        struct qmem     *mcast_buf;
        int             replay_pkind;
        int             next_free_mce;
-       spinlock_t      mce_lock; /* Serialize MCE updates */
+       struct mutex    mce_lock; /* Serialize MCE updates */
 };
 
 struct nix_mce_list {
@@ -74,15 +78,27 @@ struct nix_mce_list {
 };
 
 struct npc_mcam {
-       spinlock_t      lock;   /* MCAM entries and counters update lock */
+       struct rsrc_bmap counters;
+       struct mutex    lock;   /* MCAM entries and counters update lock */
+       unsigned long   *bmap;          /* bitmap, 0 => bmap_entries */
+       unsigned long   *bmap_reverse;  /* Reverse bitmap, bmap_entries => 0 */
+       u16     bmap_entries;   /* Number of unreserved MCAM entries */
+       u16     bmap_fcnt;      /* MCAM entries free count */
+       u16     *entry2pfvf_map;
+       u16     *entry2cntr_map;
+       u16     *cntr2pfvf_map;
+       u16     *cntr_refcnt;
        u8      keysize;        /* MCAM keysize 112/224/448 bits */
        u8      banks;          /* Number of MCAM banks */
        u8      banks_per_entry;/* Number of keywords in key */
        u16     banksize;       /* Number of MCAM entries in each bank */
        u16     total_entries;  /* Total number of MCAM entries */
-       u16     entries;        /* Total minus reserved for NIX LFs */
        u16     nixlf_offset;   /* Offset of nixlf rsvd uncast entries */
        u16     pf_offset;      /* Offset of PF's rsvd bcast, promisc entries */
+       u16     lprio_count;
+       u16     lprio_start;
+       u16     hprio_count;
+       u16     hprio_end;
 };
 
 /* Structure for per RVU func info ie PF/VF */
@@ -122,18 +138,35 @@ struct rvu_pfvf {
        u16             tx_chan_base;
        u8              rx_chan_cnt; /* total number of RX channels */
        u8              tx_chan_cnt; /* total number of TX channels */
+       u16             maxlen;
+       u16             minlen;
 
        u8              mac_addr[ETH_ALEN]; /* MAC address of this PF/VF */
 
        /* Broadcast pkt replication info */
        u16                     bcast_mce_idx;
        struct nix_mce_list     bcast_mce_list;
+
+       /* VLAN offload */
+       struct mcam_entry entry;
+       int rxvlan_index;
+       bool rxvlan;
 };
 
 struct nix_txsch {
        struct rsrc_bmap schq;
        u8   lvl;
-       u16  *pfvf_map;
+#define NIX_TXSCHQ_TL1_CFG_DONE       BIT_ULL(0)
+#define TXSCH_MAP_FUNC(__pfvf_map)    ((__pfvf_map) & 0xFFFF)
+#define TXSCH_MAP_FLAGS(__pfvf_map)   ((__pfvf_map) >> 16)
+#define TXSCH_MAP(__func, __flags)    (((__func) & 0xFFFF) | ((__flags) << 16))
+       u32  *pfvf_map;
+};
+
+struct nix_mark_format {
+       u8 total;
+       u8 in_use;
+       u32 *cfg;
 };
 
 struct npc_pkind {
@@ -141,9 +174,23 @@ struct npc_pkind {
        u32     *pfchan_map;
 };
 
+struct nix_flowkey {
+#define NIX_FLOW_KEY_ALG_MAX 32
+       u32 flowkey[NIX_FLOW_KEY_ALG_MAX];
+       int in_use;
+};
+
+struct nix_lso {
+       u8 total;
+       u8 in_use;
+};
+
 struct nix_hw {
        struct nix_txsch txsch[NIX_TXSCH_LVL_CNT]; /* Tx schedulers */
        struct nix_mcast mcast;
+       struct nix_flowkey flowkey;
+       struct nix_mark_format mark_format;
+       struct nix_lso lso;
 };
 
 struct rvu_hwinfo {
@@ -164,6 +211,16 @@ struct rvu_hwinfo {
        struct npc_mcam  mcam;
 };
 
+struct mbox_wq_info {
+       struct otx2_mbox mbox;
+       struct rvu_work *mbox_wrk;
+
+       struct otx2_mbox mbox_up;
+       struct rvu_work *mbox_wrk_up;
+
+       struct workqueue_struct *mbox_wq;
+};
+
 struct rvu {
        void __iomem            *afreg_base;
        void __iomem            *pfreg_base;
@@ -172,14 +229,17 @@ struct rvu {
        struct rvu_hwinfo       *hw;
        struct rvu_pfvf         *pf;
        struct rvu_pfvf         *hwvf;
-       spinlock_t              rsrc_lock; /* Serialize resource alloc/free */
+       struct mutex            rsrc_lock; /* Serialize resource alloc/free */
+       int                     vfs; /* Number of VFs attached to RVU */
 
        /* Mbox */
-       struct otx2_mbox        mbox;
-       struct rvu_work         *mbox_wrk;
-       struct otx2_mbox        mbox_up;
-       struct rvu_work         *mbox_wrk_up;
-       struct workqueue_struct *mbox_wq;
+       struct mbox_wq_info     afpf_wq_info;
+       struct mbox_wq_info     afvf_wq_info;
+
+       /* PF FLR */
+       struct rvu_work         *flr_wrk;
+       struct workqueue_struct *flr_wq;
+       struct mutex            flr_lock; /* Serialize FLRs */
 
        /* MSI-X */
        u16                     num_vec;
@@ -190,7 +250,7 @@ struct rvu {
        /* CGX */
 #define PF_CGXMAP_BASE         1 /* PF 0 is reserved for RVU PF */
        u8                      cgx_mapped_pfs;
-       u8                      cgx_cnt; /* available cgx ports */
+       u8                      cgx_cnt_max;     /* CGX port count max */
        u8                      *pf2cgxlmac_map; /* pf to cgx_lmac map */
        u16                     *cgxlmac2pf_map; /* bitmap of mapped pfs for
                                                  * every cgx lmac port
@@ -201,6 +261,8 @@ struct rvu {
        struct                  workqueue_struct *cgx_evh_wq;
        spinlock_t              cgx_evq_lock; /* cgx event queue lock */
        struct list_head        cgx_evq_head; /* cgx event queue head */
+
+       char mkex_pfl_name[MKEX_NAME_LEN]; /* Configured MKEX profile name */
 };
 
 static inline void rvu_write64(struct rvu *rvu, u64 block, u64 offset, u64 val)
@@ -223,9 +285,22 @@ static inline u64 rvupf_read64(struct rvu *rvu, u64 offset)
        return readq(rvu->pfreg_base + offset);
 }
 
+static inline bool is_rvu_9xxx_A0(struct rvu *rvu)
+{
+       struct pci_dev *pdev = rvu->pdev;
+
+       return (pdev->revision == 0x00) &&
+               (pdev->subsystem_device == PCI_SUBSYS_DEVID_96XX);
+}
+
 /* Function Prototypes
  * RVU
  */
+static inline int is_afvf(u16 pcifunc)
+{
+       return !(pcifunc & ~RVU_PFVF_FUNC_MASK);
+}
+
 int rvu_alloc_bitmap(struct rsrc_bmap *rsrc);
 int rvu_alloc_rsrc(struct rsrc_bmap *rsrc);
 void rvu_free_rsrc(struct rsrc_bmap *rsrc, int id);
@@ -236,6 +311,7 @@ int rvu_get_pf(u16 pcifunc);
 struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc);
 void rvu_get_pf_numvfs(struct rvu *rvu, int pf, int *numvfs, int *hwvf);
 bool is_block_implemented(struct rvu_hwinfo *hw, int blkaddr);
+bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype);
 int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot);
 int rvu_lf_reset(struct rvu *rvu, struct rvu_block *block, int lf);
 int rvu_get_blkaddr(struct rvu *rvu, int blktype, u16 pcifunc);
@@ -266,89 +342,110 @@ static inline void rvu_get_cgx_lmac_id(u8 map, u8 *cgx_id, u8 *lmac_id)
        *lmac_id = (map & 0xF);
 }
 
-int rvu_cgx_probe(struct rvu *rvu);
-void rvu_cgx_wq_destroy(struct rvu *rvu);
+int rvu_cgx_init(struct rvu *rvu);
+int rvu_cgx_exit(struct rvu *rvu);
 void *rvu_cgx_pdata(u8 cgx_id, struct rvu *rvu);
 int rvu_cgx_config_rxtx(struct rvu *rvu, u16 pcifunc, bool start);
-int rvu_mbox_handler_CGX_START_RXTX(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_start_rxtx(struct rvu *rvu, struct msg_req *req,
                                    struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_STOP_RXTX(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stop_rxtx(struct rvu *rvu, struct msg_req *req,
                                   struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_STATS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stats(struct rvu *rvu, struct msg_req *req,
                               struct cgx_stats_rsp *rsp);
-int rvu_mbox_handler_CGX_MAC_ADDR_SET(struct rvu *rvu,
+int rvu_mbox_handler_cgx_mac_addr_set(struct rvu *rvu,
                                      struct cgx_mac_addr_set_or_get *req,
                                      struct cgx_mac_addr_set_or_get *rsp);
-int rvu_mbox_handler_CGX_MAC_ADDR_GET(struct rvu *rvu,
+int rvu_mbox_handler_cgx_mac_addr_get(struct rvu *rvu,
                                      struct cgx_mac_addr_set_or_get *req,
                                      struct cgx_mac_addr_set_or_get *rsp);
-int rvu_mbox_handler_CGX_PROMISC_ENABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_promisc_enable(struct rvu *rvu, struct msg_req *req,
                                        struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_PROMISC_DISABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_promisc_disable(struct rvu *rvu, struct msg_req *req,
                                         struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_START_LINKEVENTS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_start_linkevents(struct rvu *rvu, struct msg_req *req,
                                          struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_STOP_LINKEVENTS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stop_linkevents(struct rvu *rvu, struct msg_req *req,
                                         struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_GET_LINKINFO(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_get_linkinfo(struct rvu *rvu, struct msg_req *req,
                                      struct cgx_link_info_msg *rsp);
-int rvu_mbox_handler_CGX_INTLBK_ENABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_intlbk_enable(struct rvu *rvu, struct msg_req *req,
                                       struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_INTLBK_DISABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_intlbk_disable(struct rvu *rvu, struct msg_req *req,
                                        struct msg_rsp *rsp);
 
 /* NPA APIs */
 int rvu_npa_init(struct rvu *rvu);
 void rvu_npa_freemem(struct rvu *rvu);
-int rvu_mbox_handler_NPA_AQ_ENQ(struct rvu *rvu,
+void rvu_npa_lf_teardown(struct rvu *rvu, u16 pcifunc, int npalf);
+int rvu_mbox_handler_npa_aq_enq(struct rvu *rvu,
                                struct npa_aq_enq_req *req,
                                struct npa_aq_enq_rsp *rsp);
-int rvu_mbox_handler_NPA_HWCTX_DISABLE(struct rvu *rvu,
+int rvu_mbox_handler_npa_hwctx_disable(struct rvu *rvu,
                                       struct hwctx_disable_req *req,
                                       struct msg_rsp *rsp);
-int rvu_mbox_handler_NPA_LF_ALLOC(struct rvu *rvu,
+int rvu_mbox_handler_npa_lf_alloc(struct rvu *rvu,
                                  struct npa_lf_alloc_req *req,
                                  struct npa_lf_alloc_rsp *rsp);
-int rvu_mbox_handler_NPA_LF_FREE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_npa_lf_free(struct rvu *rvu, struct msg_req *req,
                                 struct msg_rsp *rsp);
 
 /* NIX APIs */
+bool is_nixlf_attached(struct rvu *rvu, u16 pcifunc);
 int rvu_nix_init(struct rvu *rvu);
+int rvu_nix_reserve_mark_format(struct rvu *rvu, struct nix_hw *nix_hw,
+                               int blkaddr, u32 cfg);
 void rvu_nix_freemem(struct rvu *rvu);
 int rvu_get_nixlf_count(struct rvu *rvu);
-int rvu_mbox_handler_NIX_LF_ALLOC(struct rvu *rvu,
+void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int npalf);
+int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
                                  struct nix_lf_alloc_req *req,
                                  struct nix_lf_alloc_rsp *rsp);
-int rvu_mbox_handler_NIX_LF_FREE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct msg_req *req,
                                 struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_AQ_ENQ(struct rvu *rvu,
+int rvu_mbox_handler_nix_aq_enq(struct rvu *rvu,
                                struct nix_aq_enq_req *req,
                                struct nix_aq_enq_rsp *rsp);
-int rvu_mbox_handler_NIX_HWCTX_DISABLE(struct rvu *rvu,
+int rvu_mbox_handler_nix_hwctx_disable(struct rvu *rvu,
                                       struct hwctx_disable_req *req,
                                       struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
+int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
                                     struct nix_txsch_alloc_req *req,
                                     struct nix_txsch_alloc_rsp *rsp);
-int rvu_mbox_handler_NIX_TXSCH_FREE(struct rvu *rvu,
+int rvu_mbox_handler_nix_txsch_free(struct rvu *rvu,
                                    struct nix_txsch_free_req *req,
                                    struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_TXSCHQ_CFG(struct rvu *rvu,
+int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu,
                                    struct nix_txschq_config *req,
                                    struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_STATS_RST(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_nix_stats_rst(struct rvu *rvu, struct msg_req *req,
                                   struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_VTAG_CFG(struct rvu *rvu,
+int rvu_mbox_handler_nix_vtag_cfg(struct rvu *rvu,
                                  struct nix_vtag_config *req,
                                  struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_RSS_FLOWKEY_CFG(struct rvu *rvu,
+int rvu_mbox_handler_nix_rxvlan_alloc(struct rvu *rvu, struct msg_req *req,
+                                     struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_rss_flowkey_cfg(struct rvu *rvu,
                                         struct nix_rss_flowkey_cfg *req,
-                                        struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_SET_MAC_ADDR(struct rvu *rvu,
+                                        struct nix_rss_flowkey_cfg_rsp *rsp);
+int rvu_mbox_handler_nix_set_mac_addr(struct rvu *rvu,
                                      struct nix_set_mac_addr *req,
                                      struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_SET_RX_MODE(struct rvu *rvu, struct nix_rx_mode *req,
+int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,
                                     struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req,
+                                   struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_lf_start_rx(struct rvu *rvu, struct msg_req *req,
+                                    struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req,
+                                   struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_mark_format_cfg(struct rvu *rvu,
+                                        struct nix_mark_format_cfg  *req,
+                                        struct nix_mark_format_cfg_rsp *rsp);
+int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req,
+                                   struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_lso_format_cfg(struct rvu *rvu,
+                                       struct nix_lso_format_cfg *req,
+                                       struct nix_lso_format_cfg_rsp *rsp);
 
 /* NPC APIs */
 int rvu_npc_init(struct rvu *rvu);
@@ -360,9 +457,48 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc,
 void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc,
                                   int nixlf, u64 chan, bool allmulti);
 void rvu_npc_disable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf);
+void rvu_npc_enable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf);
 void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc,
                                       int nixlf, u64 chan);
+int rvu_npc_update_rxvlan(struct rvu *rvu, u16 pcifunc, int nixlf);
 void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf);
+void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf);
+void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf);
 void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf,
                                    int group, int alg_idx, int mcam_index);
+int rvu_mbox_handler_npc_mcam_alloc_entry(struct rvu *rvu,
+                                         struct npc_mcam_alloc_entry_req *req,
+                                         struct npc_mcam_alloc_entry_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu,
+                                        struct npc_mcam_free_entry_req *req,
+                                        struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_write_entry(struct rvu *rvu,
+                                         struct npc_mcam_write_entry_req *req,
+                                         struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_ena_entry(struct rvu *rvu,
+                                       struct npc_mcam_ena_dis_entry_req *req,
+                                       struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_dis_entry(struct rvu *rvu,
+                                       struct npc_mcam_ena_dis_entry_req *req,
+                                       struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu,
+                                         struct npc_mcam_shift_entry_req *req,
+                                         struct npc_mcam_shift_entry_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_alloc_counter(struct rvu *rvu,
+                               struct npc_mcam_alloc_counter_req *req,
+                               struct npc_mcam_alloc_counter_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_free_counter(struct rvu *rvu,
+                  struct npc_mcam_oper_counter_req *req, struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_clear_counter(struct rvu *rvu,
+               struct npc_mcam_oper_counter_req *req, struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu,
+               struct npc_mcam_unmap_counter_req *req, struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_counter_stats(struct rvu *rvu,
+                       struct npc_mcam_oper_counter_req *req,
+                       struct npc_mcam_oper_counter_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_alloc_and_write_entry(struct rvu *rvu,
+                         struct npc_mcam_alloc_and_write_entry_req *req,
+                         struct npc_mcam_alloc_and_write_entry_rsp *rsp);
+int rvu_mbox_handler_npc_get_kex_cfg(struct rvu *rvu, struct msg_req *req,
+                                    struct npc_get_kex_cfg_rsp *rsp);
 #endif /* RVU_H */
index 188185c..7d7133c 100644 (file)
@@ -20,14 +20,14 @@ struct cgx_evq_entry {
        struct cgx_link_event link_event;
 };
 
-#define M(_name, _id, _req_type, _rsp_type)                            \
+#define M(_name, _id, _fn_name, _req_type, _rsp_type)                  \
 static struct _req_type __maybe_unused                                 \
-*otx2_mbox_alloc_msg_ ## _name(struct rvu *rvu, int devid)             \
+*otx2_mbox_alloc_msg_ ## _fn_name(struct rvu *rvu, int devid)          \
 {                                                                      \
        struct _req_type *req;                                          \
                                                                        \
        req = (struct _req_type *)otx2_mbox_alloc_msg_rsp(              \
-               &rvu->mbox_up, devid, sizeof(struct _req_type),         \
+               &rvu->afpf_wq_info.mbox_up, devid, sizeof(struct _req_type), \
                sizeof(struct _rsp_type));                              \
        if (!req)                                                       \
                return NULL;                                            \
@@ -52,7 +52,7 @@ static inline u8 cgxlmac_id_to_bmap(u8 cgx_id, u8 lmac_id)
 
 void *rvu_cgx_pdata(u8 cgx_id, struct rvu *rvu)
 {
-       if (cgx_id >= rvu->cgx_cnt)
+       if (cgx_id >= rvu->cgx_cnt_max)
                return NULL;
 
        return rvu->cgx_idmap[cgx_id];
@@ -61,38 +61,40 @@ void *rvu_cgx_pdata(u8 cgx_id, struct rvu *rvu)
 static int rvu_map_cgx_lmac_pf(struct rvu *rvu)
 {
        struct npc_pkind *pkind = &rvu->hw->pkind;
-       int cgx_cnt = rvu->cgx_cnt;
+       int cgx_cnt_max = rvu->cgx_cnt_max;
        int cgx, lmac_cnt, lmac;
        int pf = PF_CGXMAP_BASE;
        int size, free_pkind;
 
-       if (!cgx_cnt)
+       if (!cgx_cnt_max)
                return 0;
 
-       if (cgx_cnt > 0xF || MAX_LMAC_PER_CGX > 0xF)
+       if (cgx_cnt_max > 0xF || MAX_LMAC_PER_CGX > 0xF)
                return -EINVAL;
 
        /* Alloc map table
         * An additional entry is required since PF id starts from 1 and
         * hence entry at offset 0 is invalid.
         */
-       size = (cgx_cnt * MAX_LMAC_PER_CGX + 1) * sizeof(u8);
-       rvu->pf2cgxlmac_map = devm_kzalloc(rvu->dev, size, GFP_KERNEL);
+       size = (cgx_cnt_max * MAX_LMAC_PER_CGX + 1) * sizeof(u8);
+       rvu->pf2cgxlmac_map = devm_kmalloc(rvu->dev, size, GFP_KERNEL);
        if (!rvu->pf2cgxlmac_map)
                return -ENOMEM;
 
-       /* Initialize offset 0 with an invalid cgx and lmac id */
-       rvu->pf2cgxlmac_map[0] = 0xFF;
+       /* Initialize all entries with an invalid cgx and lmac id */
+       memset(rvu->pf2cgxlmac_map, 0xFF, size);
 
        /* Reverse map table */
        rvu->cgxlmac2pf_map = devm_kzalloc(rvu->dev,
-                                 cgx_cnt * MAX_LMAC_PER_CGX * sizeof(u16),
+                                 cgx_cnt_max * MAX_LMAC_PER_CGX * sizeof(u16),
                                  GFP_KERNEL);
        if (!rvu->cgxlmac2pf_map)
                return -ENOMEM;
 
        rvu->cgx_mapped_pfs = 0;
-       for (cgx = 0; cgx < cgx_cnt; cgx++) {
+       for (cgx = 0; cgx < cgx_cnt_max; cgx++) {
+               if (!rvu_cgx_pdata(cgx, rvu))
+                       continue;
                lmac_cnt = cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu));
                for (lmac = 0; lmac < lmac_cnt; lmac++, pf++) {
                        rvu->pf2cgxlmac_map[pf] = cgxlmac_id_to_bmap(cgx, lmac);
@@ -177,12 +179,12 @@ static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu)
                }
 
                /* Send mbox message to PF */
-               msg = otx2_mbox_alloc_msg_CGX_LINK_EVENT(rvu, pfid);
+               msg = otx2_mbox_alloc_msg_cgx_link_event(rvu, pfid);
                if (!msg)
                        continue;
                msg->link_info = *linfo;
-               otx2_mbox_msg_send(&rvu->mbox_up, pfid);
-               err = otx2_mbox_wait_for_rsp(&rvu->mbox_up, pfid);
+               otx2_mbox_msg_send(&rvu->afpf_wq_info.mbox_up, pfid);
+               err = otx2_mbox_wait_for_rsp(&rvu->afpf_wq_info.mbox_up, pfid);
                if (err)
                        dev_warn(rvu->dev, "notification to pf %d failed\n",
                                 pfid);
@@ -216,7 +218,7 @@ static void cgx_evhandler_task(struct work_struct *work)
        } while (1);
 }
 
-static void cgx_lmac_event_handler_init(struct rvu *rvu)
+static int cgx_lmac_event_handler_init(struct rvu *rvu)
 {
        struct cgx_event_cb cb;
        int cgx, lmac, err;
@@ -228,14 +230,16 @@ static void cgx_lmac_event_handler_init(struct rvu *rvu)
        rvu->cgx_evh_wq = alloc_workqueue("rvu_evh_wq", 0, 0);
        if (!rvu->cgx_evh_wq) {
                dev_err(rvu->dev, "alloc workqueue failed");
-               return;
+               return -ENOMEM;
        }
 
        cb.notify_link_chg = cgx_lmac_postevent; /* link change call back */
        cb.data = rvu;
 
-       for (cgx = 0; cgx < rvu->cgx_cnt; cgx++) {
+       for (cgx = 0; cgx <= rvu->cgx_cnt_max; cgx++) {
                cgxd = rvu_cgx_pdata(cgx, rvu);
+               if (!cgxd)
+                       continue;
                for (lmac = 0; lmac < cgx_get_lmac_cnt(cgxd); lmac++) {
                        err = cgx_lmac_evh_register(&cb, cgxd, lmac);
                        if (err)
@@ -244,9 +248,11 @@ static void cgx_lmac_event_handler_init(struct rvu *rvu)
                                        cgx, lmac);
                }
        }
+
+       return 0;
 }
 
-void rvu_cgx_wq_destroy(struct rvu *rvu)
+static void rvu_cgx_wq_destroy(struct rvu *rvu)
 {
        if (rvu->cgx_evh_wq) {
                flush_workqueue(rvu->cgx_evh_wq);
@@ -255,25 +261,28 @@ void rvu_cgx_wq_destroy(struct rvu *rvu)
        }
 }
 
-int rvu_cgx_probe(struct rvu *rvu)
+int rvu_cgx_init(struct rvu *rvu)
 {
-       int i, err;
+       int cgx, err;
+       void *cgxd;
 
-       /* find available cgx ports */
-       rvu->cgx_cnt = cgx_get_cgx_cnt();
-       if (!rvu->cgx_cnt) {
+       /* CGX port id starts from 0 and are not necessarily contiguous
+        * Hence we allocate resources based on the maximum port id value.
+        */
+       rvu->cgx_cnt_max = cgx_get_cgxcnt_max();
+       if (!rvu->cgx_cnt_max) {
                dev_info(rvu->dev, "No CGX devices found!\n");
                return -ENODEV;
        }
 
-       rvu->cgx_idmap = devm_kzalloc(rvu->dev, rvu->cgx_cnt * sizeof(void *),
-                                     GFP_KERNEL);
+       rvu->cgx_idmap = devm_kzalloc(rvu->dev, rvu->cgx_cnt_max *
+                                     sizeof(void *), GFP_KERNEL);
        if (!rvu->cgx_idmap)
                return -ENOMEM;
 
        /* Initialize the cgxdata table */
-       for (i = 0; i < rvu->cgx_cnt; i++)
-               rvu->cgx_idmap[i] = cgx_get_pdata(i);
+       for (cgx = 0; cgx < rvu->cgx_cnt_max; cgx++)
+               rvu->cgx_idmap[cgx] = cgx_get_pdata(cgx);
 
        /* Map CGX LMAC interfaces to RVU PFs */
        err = rvu_map_cgx_lmac_pf(rvu);
@@ -281,7 +290,47 @@ int rvu_cgx_probe(struct rvu *rvu)
                return err;
 
        /* Register for CGX events */
-       cgx_lmac_event_handler_init(rvu);
+       err = cgx_lmac_event_handler_init(rvu);
+       if (err)
+               return err;
+
+       /* Ensure event handler registration is completed, before
+        * we turn on the links
+        */
+       mb();
+
+       /* Do link up for all CGX ports */
+       for (cgx = 0; cgx <= rvu->cgx_cnt_max; cgx++) {
+               cgxd = rvu_cgx_pdata(cgx, rvu);
+               if (!cgxd)
+                       continue;
+               err = cgx_lmac_linkup_start(cgxd);
+               if (err)
+                       dev_err(rvu->dev,
+                               "Link up process failed to start on cgx %d\n",
+                               cgx);
+       }
+
+       return 0;
+}
+
+int rvu_cgx_exit(struct rvu *rvu)
+{
+       int cgx, lmac;
+       void *cgxd;
+
+       for (cgx = 0; cgx <= rvu->cgx_cnt_max; cgx++) {
+               cgxd = rvu_cgx_pdata(cgx, rvu);
+               if (!cgxd)
+                       continue;
+               for (lmac = 0; lmac < cgx_get_lmac_cnt(cgxd); lmac++)
+                       cgx_lmac_evh_unregister(cgxd, lmac);
+       }
+
+       /* Ensure event handler unregister is completed */
+       mb();
+
+       rvu_cgx_wq_destroy(rvu);
        return 0;
 }
 
@@ -303,21 +352,21 @@ int rvu_cgx_config_rxtx(struct rvu *rvu, u16 pcifunc, bool start)
        return 0;
 }
 
-int rvu_mbox_handler_CGX_START_RXTX(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_start_rxtx(struct rvu *rvu, struct msg_req *req,
                                    struct msg_rsp *rsp)
 {
        rvu_cgx_config_rxtx(rvu, req->hdr.pcifunc, true);
        return 0;
 }
 
-int rvu_mbox_handler_CGX_STOP_RXTX(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stop_rxtx(struct rvu *rvu, struct msg_req *req,
                                   struct msg_rsp *rsp)
 {
        rvu_cgx_config_rxtx(rvu, req->hdr.pcifunc, false);
        return 0;
 }
 
-int rvu_mbox_handler_CGX_STATS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stats(struct rvu *rvu, struct msg_req *req,
                               struct cgx_stats_rsp *rsp)
 {
        int pf = rvu_get_pf(req->hdr.pcifunc);
@@ -354,7 +403,7 @@ int rvu_mbox_handler_CGX_STATS(struct rvu *rvu, struct msg_req *req,
        return 0;
 }
 
-int rvu_mbox_handler_CGX_MAC_ADDR_SET(struct rvu *rvu,
+int rvu_mbox_handler_cgx_mac_addr_set(struct rvu *rvu,
                                      struct cgx_mac_addr_set_or_get *req,
                                      struct cgx_mac_addr_set_or_get *rsp)
 {
@@ -368,7 +417,7 @@ int rvu_mbox_handler_CGX_MAC_ADDR_SET(struct rvu *rvu,
        return 0;
 }
 
-int rvu_mbox_handler_CGX_MAC_ADDR_GET(struct rvu *rvu,
+int rvu_mbox_handler_cgx_mac_addr_get(struct rvu *rvu,
                                      struct cgx_mac_addr_set_or_get *req,
                                      struct cgx_mac_addr_set_or_get *rsp)
 {
@@ -387,7 +436,7 @@ int rvu_mbox_handler_CGX_MAC_ADDR_GET(struct rvu *rvu,
        return 0;
 }
 
-int rvu_mbox_handler_CGX_PROMISC_ENABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_promisc_enable(struct rvu *rvu, struct msg_req *req,
                                        struct msg_rsp *rsp)
 {
        u16 pcifunc = req->hdr.pcifunc;
@@ -407,7 +456,7 @@ int rvu_mbox_handler_CGX_PROMISC_ENABLE(struct rvu *rvu, struct msg_req *req,
        return 0;
 }
 
-int rvu_mbox_handler_CGX_PROMISC_DISABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_promisc_disable(struct rvu *rvu, struct msg_req *req,
                                         struct msg_rsp *rsp)
 {
        u16 pcifunc = req->hdr.pcifunc;
@@ -451,21 +500,21 @@ static int rvu_cgx_config_linkevents(struct rvu *rvu, u16 pcifunc, bool en)
        return 0;
 }
 
-int rvu_mbox_handler_CGX_START_LINKEVENTS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_start_linkevents(struct rvu *rvu, struct msg_req *req,
                                          struct msg_rsp *rsp)
 {
        rvu_cgx_config_linkevents(rvu, req->hdr.pcifunc, true);
        return 0;
 }
 
-int rvu_mbox_handler_CGX_STOP_LINKEVENTS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stop_linkevents(struct rvu *rvu, struct msg_req *req,
                                         struct msg_rsp *rsp)
 {
        rvu_cgx_config_linkevents(rvu, req->hdr.pcifunc, false);
        return 0;
 }
 
-int rvu_mbox_handler_CGX_GET_LINKINFO(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_get_linkinfo(struct rvu *rvu, struct msg_req *req,
                                      struct cgx_link_info_msg *rsp)
 {
        u8 cgx_id, lmac_id;
@@ -500,14 +549,14 @@ static int rvu_cgx_config_intlbk(struct rvu *rvu, u16 pcifunc, bool en)
                                          lmac_id, en);
 }
 
-int rvu_mbox_handler_CGX_INTLBK_ENABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_intlbk_enable(struct rvu *rvu, struct msg_req *req,
                                       struct msg_rsp *rsp)
 {
        rvu_cgx_config_intlbk(rvu, req->hdr.pcifunc, true);
        return 0;
 }
 
-int rvu_mbox_handler_CGX_INTLBK_DISABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_intlbk_disable(struct rvu *rvu, struct msg_req *req,
                                        struct msg_rsp *rsp)
 {
        rvu_cgx_config_intlbk(rvu, req->hdr.pcifunc, false);
index a5ab7ef..4a7609f 100644 (file)
@@ -43,6 +43,19 @@ enum mc_buf_cnt {
        MC_BUF_CNT_2048,
 };
 
+enum nix_makr_fmt_indexes {
+       NIX_MARK_CFG_IP_DSCP_RED,
+       NIX_MARK_CFG_IP_DSCP_YELLOW,
+       NIX_MARK_CFG_IP_DSCP_YELLOW_RED,
+       NIX_MARK_CFG_IP_ECN_RED,
+       NIX_MARK_CFG_IP_ECN_YELLOW,
+       NIX_MARK_CFG_IP_ECN_YELLOW_RED,
+       NIX_MARK_CFG_VLAN_DEI_RED,
+       NIX_MARK_CFG_VLAN_DEI_YELLOW,
+       NIX_MARK_CFG_VLAN_DEI_YELLOW_RED,
+       NIX_MARK_CFG_MAX,
+};
+
 /* For now considering MC resources needed for broadcast
  * pkt replication only. i.e 256 HWVFs + 12 PFs.
  */
@@ -55,6 +68,17 @@ struct mce {
        u16                     pcifunc;
 };
 
+bool is_nixlf_attached(struct rvu *rvu, u16 pcifunc)
+{
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (!pfvf->nixlf || blkaddr < 0)
+               return false;
+       return true;
+}
+
 int rvu_get_nixlf_count(struct rvu *rvu)
 {
        struct rvu_block *block;
@@ -94,11 +118,29 @@ static inline struct nix_hw *get_nix_hw(struct rvu_hwinfo *hw, int blkaddr)
        return NULL;
 }
 
+static void nix_rx_sync(struct rvu *rvu, int blkaddr)
+{
+       int err;
+
+       /*Sync all in flight RX packets to LLC/DRAM */
+       rvu_write64(rvu, blkaddr, NIX_AF_RX_SW_SYNC, BIT_ULL(0));
+       err = rvu_poll_reg(rvu, blkaddr, NIX_AF_RX_SW_SYNC, BIT_ULL(0), true);
+       if (err)
+               dev_err(rvu->dev, "NIX RX software sync failed\n");
+
+       /* As per a HW errata in 9xxx A0 silicon, HW may clear SW_SYNC[ENA]
+        * bit too early. Hence wait for 50us more.
+        */
+       if (is_rvu_9xxx_A0(rvu))
+               usleep_range(50, 60);
+}
+
 static bool is_valid_txschq(struct rvu *rvu, int blkaddr,
                            int lvl, u16 pcifunc, u16 schq)
 {
        struct nix_txsch *txsch;
        struct nix_hw *nix_hw;
+       u16 map_func;
 
        nix_hw = get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
@@ -109,12 +151,19 @@ static bool is_valid_txschq(struct rvu *rvu, int blkaddr,
        if (schq >= txsch->schq.max)
                return false;
 
-       spin_lock(&rvu->rsrc_lock);
-       if (txsch->pfvf_map[schq] != pcifunc) {
-               spin_unlock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
+       map_func = TXSCH_MAP_FUNC(txsch->pfvf_map[schq]);
+       mutex_unlock(&rvu->rsrc_lock);
+
+       /* For TL1 schq, sharing across VF's of same PF is ok */
+       if (lvl == NIX_TXSCH_LVL_TL1 &&
+           rvu_get_pf(map_func) != rvu_get_pf(pcifunc))
                return false;
-       }
-       spin_unlock(&rvu->rsrc_lock);
+
+       if (lvl != NIX_TXSCH_LVL_TL1 &&
+           map_func != pcifunc)
+               return false;
+
        return true;
 }
 
@@ -122,7 +171,7 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf)
 {
        struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
        u8 cgx_id, lmac_id;
-       int pkind, pf;
+       int pkind, pf, vf;
        int err;
 
        pf = rvu_get_pf(pcifunc);
@@ -148,6 +197,14 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf)
                rvu_npc_set_pkind(rvu, pkind, pfvf);
                break;
        case NIX_INTF_TYPE_LBK:
+               vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1;
+               pfvf->rx_chan_base = NIX_CHAN_LBK_CHX(0, vf);
+               pfvf->tx_chan_base = vf & 0x1 ? NIX_CHAN_LBK_CHX(0, vf - 1) :
+                                               NIX_CHAN_LBK_CHX(0, vf + 1);
+               pfvf->rx_chan_cnt = 1;
+               pfvf->tx_chan_cnt = 1;
+               rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf,
+                                             pfvf->rx_chan_base, false);
                break;
        }
 
@@ -168,14 +225,21 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf)
 
        rvu_npc_install_bcast_match_entry(rvu, pcifunc,
                                          nixlf, pfvf->rx_chan_base);
+       pfvf->maxlen = NIC_HW_MIN_FRS;
+       pfvf->minlen = NIC_HW_MIN_FRS;
 
        return 0;
 }
 
 static void nix_interface_deinit(struct rvu *rvu, u16 pcifunc, u8 nixlf)
 {
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
        int err;
 
+       pfvf->maxlen = 0;
+       pfvf->minlen = 0;
+       pfvf->rxvlan = false;
+
        /* Remove this PF_FUNC from bcast pkt replication list */
        err = nix_update_bcast_mce_list(rvu, pcifunc, false);
        if (err) {
@@ -234,17 +298,21 @@ static void nix_setup_lso_tso_l4(struct rvu *rvu, int blkaddr,
        /* TCP's flags field */
        field.layer = NIX_TXLAYER_OL4;
        field.offset = 12;
-       field.sizem1 = 0; /* not needed */
+       field.sizem1 = 1; /* 2 bytes */
        field.alg = NIX_LSOALG_TCP_FLAGS;
        rvu_write64(rvu, blkaddr,
                    NIX_AF_LSO_FORMATX_FIELDX(format, (*fidx)++),
                    *(u64 *)&field);
 }
 
-static void nix_setup_lso(struct rvu *rvu, int blkaddr)
+static void nix_setup_lso(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
 {
        u64 cfg, idx, fidx = 0;
 
+       /* Get max HW supported format indices */
+       cfg = (rvu_read64(rvu, blkaddr, NIX_AF_CONST1) >> 48) & 0xFF;
+       nix_hw->lso.total = cfg;
+
        /* Enable LSO */
        cfg = rvu_read64(rvu, blkaddr, NIX_AF_LSO_CFG);
        /* For TSO, set first and middle segment flags to
@@ -254,7 +322,10 @@ static void nix_setup_lso(struct rvu *rvu, int blkaddr)
        cfg |= (0xFFF2ULL << 32) | (0xFFF2ULL << 16);
        rvu_write64(rvu, blkaddr, NIX_AF_LSO_CFG, cfg | BIT_ULL(63));
 
-       /* Configure format fields for TCPv4 segmentation offload */
+       /* Setup default static LSO formats
+        *
+        * Configure format fields for TCPv4 segmentation offload
+        */
        idx = NIX_LSO_FORMAT_IDX_TSOV4;
        nix_setup_lso_tso_l3(rvu, blkaddr, idx, true, &fidx);
        nix_setup_lso_tso_l4(rvu, blkaddr, idx, &fidx);
@@ -264,6 +335,7 @@ static void nix_setup_lso(struct rvu *rvu, int blkaddr)
                rvu_write64(rvu, blkaddr,
                            NIX_AF_LSO_FORMATX_FIELDX(idx, fidx), 0x0ULL);
        }
+       nix_hw->lso.in_use++;
 
        /* Configure format fields for TCPv6 segmentation offload */
        idx = NIX_LSO_FORMAT_IDX_TSOV6;
@@ -276,6 +348,7 @@ static void nix_setup_lso(struct rvu *rvu, int blkaddr)
                rvu_write64(rvu, blkaddr,
                            NIX_AF_LSO_FORMATX_FIELDX(idx, fidx), 0x0ULL);
        }
+       nix_hw->lso.in_use++;
 }
 
 static void nix_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf)
@@ -388,9 +461,8 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req,
        bool ena;
        u64 cfg;
 
-       pfvf = rvu_get_pfvf(rvu, pcifunc);
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
-       if (!pfvf->nixlf || blkaddr < 0)
+       if (blkaddr < 0)
                return NIX_AF_ERR_AF_LF_INVALID;
 
        block = &hw->block[blkaddr];
@@ -400,9 +472,14 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req,
                return NIX_AF_ERR_AQ_ENQUEUE;
        }
 
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
        nixlf = rvu_get_lf(rvu, block, pcifunc, 0);
-       if (nixlf < 0)
-               return NIX_AF_ERR_AF_LF_INVALID;
+
+       /* Skip NIXLF check for broadcast MCE entry init */
+       if (!(!rsp && req->ctype == NIX_AQ_CTYPE_MCE)) {
+               if (!pfvf->nixlf || nixlf < 0)
+                       return NIX_AF_ERR_AF_LF_INVALID;
+       }
 
        switch (req->ctype) {
        case NIX_AQ_CTYPE_RQ:
@@ -447,7 +524,9 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req,
 
        /* Check if SQ pointed SMQ belongs to this PF/VF or not */
        if (req->ctype == NIX_AQ_CTYPE_SQ &&
-           req->op != NIX_AQ_INSTOP_WRITE) {
+           ((req->op == NIX_AQ_INSTOP_INIT && req->sq.ena) ||
+            (req->op == NIX_AQ_INSTOP_WRITE &&
+             req->sq_mask.ena && req->sq_mask.smq && req->sq.ena))) {
                if (!is_valid_txschq(rvu, blkaddr, NIX_TXSCH_LVL_SMQ,
                                     pcifunc, req->sq.smq))
                        return NIX_AF_ERR_AQ_ENQUEUE;
@@ -637,25 +716,25 @@ static int nix_lf_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req)
        return err;
 }
 
-int rvu_mbox_handler_NIX_AQ_ENQ(struct rvu *rvu,
+int rvu_mbox_handler_nix_aq_enq(struct rvu *rvu,
                                struct nix_aq_enq_req *req,
                                struct nix_aq_enq_rsp *rsp)
 {
        return rvu_nix_aq_enq_inst(rvu, req, rsp);
 }
 
-int rvu_mbox_handler_NIX_HWCTX_DISABLE(struct rvu *rvu,
+int rvu_mbox_handler_nix_hwctx_disable(struct rvu *rvu,
                                       struct hwctx_disable_req *req,
                                       struct msg_rsp *rsp)
 {
        return nix_lf_hwctx_disable(rvu, req);
 }
 
-int rvu_mbox_handler_NIX_LF_ALLOC(struct rvu *rvu,
+int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
                                  struct nix_lf_alloc_req *req,
                                  struct nix_lf_alloc_rsp *rsp)
 {
-       int nixlf, qints, hwctx_size, err, rc = 0;
+       int nixlf, qints, hwctx_size, intf, err, rc = 0;
        struct rvu_hwinfo *hw = rvu->hw;
        u16 pcifunc = req->hdr.pcifunc;
        struct rvu_block *block;
@@ -676,6 +755,24 @@ int rvu_mbox_handler_NIX_LF_ALLOC(struct rvu *rvu,
        if (nixlf < 0)
                return NIX_AF_ERR_AF_LF_INVALID;
 
+       /* Check if requested 'NIXLF <=> NPALF' mapping is valid */
+       if (req->npa_func) {
+               /* If default, use 'this' NIXLF's PFFUNC */
+               if (req->npa_func == RVU_DEFAULT_PF_FUNC)
+                       req->npa_func = pcifunc;
+               if (!is_pffunc_map_valid(rvu, req->npa_func, BLKTYPE_NPA))
+                       return NIX_AF_INVAL_NPA_PF_FUNC;
+       }
+
+       /* Check if requested 'NIXLF <=> SSOLF' mapping is valid */
+       if (req->sso_func) {
+               /* If default, use 'this' NIXLF's PFFUNC */
+               if (req->sso_func == RVU_DEFAULT_PF_FUNC)
+                       req->sso_func = pcifunc;
+               if (!is_pffunc_map_valid(rvu, req->sso_func, BLKTYPE_SSO))
+                       return NIX_AF_INVAL_SSO_PF_FUNC;
+       }
+
        /* If RSS is being enabled, check if requested config is valid.
         * RSS table size should be power of two, otherwise
         * RSS_GRP::OFFSET + adder might go beyond that group or
@@ -777,21 +874,20 @@ int rvu_mbox_handler_NIX_LF_ALLOC(struct rvu *rvu,
                    (u64)pfvf->nix_qints_ctx->iova);
        rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_CFG(nixlf), BIT_ULL(36));
 
+       /* Setup VLANX TPID's.
+        * Use VLAN1 for 802.1Q
+        * and VLAN0 for 802.1AD.
+        */
+       cfg = (0x8100ULL << 16) | 0x88A8ULL;
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_CFG(nixlf), cfg);
+
        /* Enable LMTST for this NIX LF */
        rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_CFG2(nixlf), BIT_ULL(0));
 
-       /* Set CQE/WQE size, NPA_PF_FUNC for SQBs and also SSO_PF_FUNC
-        * If requester has sent a 'RVU_DEFAULT_PF_FUNC' use this NIX LF's
-        * PCIFUNC itself.
-        */
-       if (req->npa_func == RVU_DEFAULT_PF_FUNC)
-               cfg = pcifunc;
-       else
+       /* Set CQE/WQE size, NPA_PF_FUNC for SQBs and also SSO_PF_FUNC */
+       if (req->npa_func)
                cfg = req->npa_func;
-
-       if (req->sso_func == RVU_DEFAULT_PF_FUNC)
-               cfg |= (u64)pcifunc << 16;
-       else
+       if (req->sso_func)
                cfg |= (u64)req->sso_func << 16;
 
        cfg |= (u64)req->xqe_sz << 33;
@@ -800,10 +896,14 @@ int rvu_mbox_handler_NIX_LF_ALLOC(struct rvu *rvu,
        /* Config Rx pkt length, csum checks and apad  enable / disable */
        rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf), req->rx_cfg);
 
-       err = nix_interface_init(rvu, pcifunc, NIX_INTF_TYPE_CGX, nixlf);
+       intf = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX;
+       err = nix_interface_init(rvu, pcifunc, intf, nixlf);
        if (err)
                goto free_mem;
 
+       /* Disable NPC entries as NIXLF's contexts are not initialized yet */
+       rvu_npc_disable_default_entries(rvu, pcifunc, nixlf);
+
        goto exit;
 
 free_mem:
@@ -823,10 +923,18 @@ exit:
        rsp->tx_chan_cnt = pfvf->tx_chan_cnt;
        rsp->lso_tsov4_idx = NIX_LSO_FORMAT_IDX_TSOV4;
        rsp->lso_tsov6_idx = NIX_LSO_FORMAT_IDX_TSOV6;
+       /* Get HW supported stat count */
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST1);
+       rsp->lf_rx_stats = ((cfg >> 32) & 0xFF);
+       rsp->lf_tx_stats = ((cfg >> 24) & 0xFF);
+       /* Get count of CQ IRQs and error IRQs supported per LF */
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
+       rsp->qints = ((cfg >> 12) & 0xFFF);
+       rsp->cints = ((cfg >> 24) & 0xFFF);
        return rc;
 }
 
-int rvu_mbox_handler_NIX_LF_FREE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct msg_req *req,
                                 struct msg_rsp *rsp)
 {
        struct rvu_hwinfo *hw = rvu->hw;
@@ -860,6 +968,41 @@ int rvu_mbox_handler_NIX_LF_FREE(struct rvu *rvu, struct msg_req *req,
        return 0;
 }
 
+int rvu_mbox_handler_nix_mark_format_cfg(struct rvu *rvu,
+                                        struct nix_mark_format_cfg  *req,
+                                        struct nix_mark_format_cfg_rsp *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       struct nix_hw *nix_hw;
+       struct rvu_pfvf *pfvf;
+       int blkaddr, rc;
+       u32 cfg;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (!pfvf->nixlf || blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       cfg = (((u32)req->offset & 0x7) << 16) |
+             (((u32)req->y_mask & 0xF) << 12) |
+             (((u32)req->y_val & 0xF) << 8) |
+             (((u32)req->r_mask & 0xF) << 4) | ((u32)req->r_val & 0xF);
+
+       rc = rvu_nix_reserve_mark_format(rvu, nix_hw, blkaddr, cfg);
+       if (rc < 0) {
+               dev_err(rvu->dev, "No mark_format_ctl for (pf:%d, vf:%d)",
+                       rvu_get_pf(pcifunc), pcifunc & RVU_PFVF_FUNC_MASK);
+               return NIX_AF_ERR_MARK_CFG_FAIL;
+       }
+
+       rsp->mark_format_idx = rc;
+       return 0;
+}
+
 /* Disable shaping of pkts by a scheduler queue
  * at a given scheduler level.
  */
@@ -918,7 +1061,74 @@ static void nix_reset_tx_linkcfg(struct rvu *rvu, int blkaddr,
                            NIX_AF_TL3_TL2X_LINKX_CFG(schq, link), 0x00);
 }
 
-int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
+static int
+rvu_get_tl1_schqs(struct rvu *rvu, int blkaddr, u16 pcifunc,
+                 u16 *schq_list, u16 *schq_cnt)
+{
+       struct nix_txsch *txsch;
+       struct nix_hw *nix_hw;
+       struct rvu_pfvf *pfvf;
+       u8 cgx_id, lmac_id;
+       u16 schq_base;
+       u32 *pfvf_map;
+       int pf, intf;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -ENODEV;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL1];
+       pfvf_map = txsch->pfvf_map;
+       pf = rvu_get_pf(pcifunc);
+
+       /* static allocation as two TL1's per link */
+       intf = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX;
+
+       switch (intf) {
+       case NIX_INTF_TYPE_CGX:
+               rvu_get_cgx_lmac_id(pfvf->cgx_lmac, &cgx_id, &lmac_id);
+               schq_base = (cgx_id * MAX_LMAC_PER_CGX + lmac_id) * 2;
+               break;
+       case NIX_INTF_TYPE_LBK:
+               schq_base = rvu->cgx_cnt_max * MAX_LMAC_PER_CGX * 2;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       if (schq_base + 1 > txsch->schq.max)
+               return -ENODEV;
+
+       /* init pfvf_map as we store flags */
+       if (pfvf_map[schq_base] == U32_MAX) {
+               pfvf_map[schq_base] =
+                       TXSCH_MAP((pf << RVU_PFVF_PF_SHIFT), 0);
+               pfvf_map[schq_base + 1] =
+                       TXSCH_MAP((pf << RVU_PFVF_PF_SHIFT), 0);
+
+               /* Onetime reset for TL1 */
+               nix_reset_tx_linkcfg(rvu, blkaddr,
+                                    NIX_TXSCH_LVL_TL1, schq_base);
+               nix_reset_tx_shaping(rvu, blkaddr,
+                                    NIX_TXSCH_LVL_TL1, schq_base);
+
+               nix_reset_tx_linkcfg(rvu, blkaddr,
+                                    NIX_TXSCH_LVL_TL1, schq_base + 1);
+               nix_reset_tx_shaping(rvu, blkaddr,
+                                    NIX_TXSCH_LVL_TL1, schq_base + 1);
+       }
+
+       if (schq_list && schq_cnt) {
+               schq_list[0] = schq_base;
+               schq_list[1] = schq_base + 1;
+               *schq_cnt = 2;
+       }
+
+       return 0;
+}
+
+int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
                                     struct nix_txsch_alloc_req *req,
                                     struct nix_txsch_alloc_rsp *rsp)
 {
@@ -928,6 +1138,7 @@ int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
        struct rvu_pfvf *pfvf;
        struct nix_hw *nix_hw;
        int blkaddr, rc = 0;
+       u32 *pfvf_map;
        u16 schq;
 
        pfvf = rvu_get_pfvf(rvu, pcifunc);
@@ -939,17 +1150,27 @@ int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
        if (!nix_hw)
                return -EINVAL;
 
-       spin_lock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
        for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
                txsch = &nix_hw->txsch[lvl];
                req_schq = req->schq_contig[lvl] + req->schq[lvl];
+               pfvf_map = txsch->pfvf_map;
+
+               if (!req_schq)
+                       continue;
 
                /* There are only 28 TL1s */
-               if (lvl == NIX_TXSCH_LVL_TL1 && req_schq > txsch->schq.max)
-                       goto err;
+               if (lvl == NIX_TXSCH_LVL_TL1) {
+                       if (req->schq_contig[lvl] ||
+                           req->schq[lvl] > 2 ||
+                           rvu_get_tl1_schqs(rvu, blkaddr,
+                                             pcifunc, NULL, NULL))
+                               goto err;
+                       continue;
+               }
 
                /* Check if request is valid */
-               if (!req_schq || req_schq > MAX_TXSCHQ_PER_FUNC)
+               if (req_schq > MAX_TXSCHQ_PER_FUNC)
                        goto err;
 
                /* If contiguous queues are needed, check for availability */
@@ -965,16 +1186,32 @@ int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
        for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
                txsch = &nix_hw->txsch[lvl];
                rsp->schq_contig[lvl] = req->schq_contig[lvl];
+               pfvf_map = txsch->pfvf_map;
                rsp->schq[lvl] = req->schq[lvl];
 
-               schq = 0;
+               if (!req->schq[lvl] && !req->schq_contig[lvl])
+                       continue;
+
+               /* Handle TL1 specially as it is
+                * allocation is restricted to 2 TL1's
+                * per link
+                */
+
+               if (lvl == NIX_TXSCH_LVL_TL1) {
+                       rsp->schq_contig[lvl] = 0;
+                       rvu_get_tl1_schqs(rvu, blkaddr, pcifunc,
+                                         &rsp->schq_list[lvl][0],
+                                         &rsp->schq[lvl]);
+                       continue;
+               }
+
                /* Alloc contiguous queues first */
                if (req->schq_contig[lvl]) {
                        schq = rvu_alloc_rsrc_contig(&txsch->schq,
                                                     req->schq_contig[lvl]);
 
                        for (idx = 0; idx < req->schq_contig[lvl]; idx++) {
-                               txsch->pfvf_map[schq] = pcifunc;
+                               pfvf_map[schq] = TXSCH_MAP(pcifunc, 0);
                                nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
                                nix_reset_tx_shaping(rvu, blkaddr, lvl, schq);
                                rsp->schq_contig_list[lvl][idx] = schq;
@@ -985,7 +1222,7 @@ int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
                /* Alloc non-contiguous queues */
                for (idx = 0; idx < req->schq[lvl]; idx++) {
                        schq = rvu_alloc_rsrc(&txsch->schq);
-                       txsch->pfvf_map[schq] = pcifunc;
+                       pfvf_map[schq] = TXSCH_MAP(pcifunc, 0);
                        nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
                        nix_reset_tx_shaping(rvu, blkaddr, lvl, schq);
                        rsp->schq_list[lvl][idx] = schq;
@@ -995,7 +1232,7 @@ int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
 err:
        rc = NIX_AF_ERR_TLX_ALLOC_FAIL;
 exit:
-       spin_unlock(&rvu->rsrc_lock);
+       mutex_unlock(&rvu->rsrc_lock);
        return rc;
 }
 
@@ -1020,14 +1257,14 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
                return NIX_AF_ERR_AF_LF_INVALID;
 
        /* Disable TL2/3 queue links before SMQ flush*/
-       spin_lock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
        for (lvl = NIX_TXSCH_LVL_TL4; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
                if (lvl != NIX_TXSCH_LVL_TL2 && lvl != NIX_TXSCH_LVL_TL4)
                        continue;
 
                txsch = &nix_hw->txsch[lvl];
                for (schq = 0; schq < txsch->schq.max; schq++) {
-                       if (txsch->pfvf_map[schq] != pcifunc)
+                       if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
                                continue;
                        nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
                }
@@ -1036,7 +1273,7 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
        /* Flush SMQs */
        txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ];
        for (schq = 0; schq < txsch->schq.max; schq++) {
-               if (txsch->pfvf_map[schq] != pcifunc)
+               if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
                        continue;
                cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq));
                /* Do SMQ flush and set enqueue xoff */
@@ -1054,15 +1291,21 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
 
        /* Now free scheduler queues to free pool */
        for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
+               /* Free all SCHQ's except TL1 as
+                * TL1 is shared across all VF's for a RVU PF
+                */
+               if (lvl == NIX_TXSCH_LVL_TL1)
+                       continue;
+
                txsch = &nix_hw->txsch[lvl];
                for (schq = 0; schq < txsch->schq.max; schq++) {
-                       if (txsch->pfvf_map[schq] != pcifunc)
+                       if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
                                continue;
                        rvu_free_rsrc(&txsch->schq, schq);
                        txsch->pfvf_map[schq] = 0;
                }
        }
-       spin_unlock(&rvu->rsrc_lock);
+       mutex_unlock(&rvu->rsrc_lock);
 
        /* Sync cached info for this LF in NDC-TX to LLC/DRAM */
        rvu_write64(rvu, blkaddr, NIX_AF_NDC_TX_SYNC, BIT_ULL(12) | nixlf);
@@ -1073,11 +1316,81 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
        return 0;
 }
 
-int rvu_mbox_handler_NIX_TXSCH_FREE(struct rvu *rvu,
+static int nix_txschq_free_one(struct rvu *rvu,
+                              struct nix_txsch_free_req *req)
+{
+       int lvl, schq, nixlf, blkaddr, rc;
+       struct rvu_hwinfo *hw = rvu->hw;
+       u16 pcifunc = req->hdr.pcifunc;
+       struct nix_txsch *txsch;
+       struct nix_hw *nix_hw;
+       u32 *pfvf_map;
+       u64 cfg;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
+       if (nixlf < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       lvl = req->schq_lvl;
+       schq = req->schq;
+       txsch = &nix_hw->txsch[lvl];
+
+       /* Don't allow freeing TL1 */
+       if (lvl > NIX_TXSCH_LVL_TL2 ||
+           schq >= txsch->schq.max)
+               goto err;
+
+       pfvf_map = txsch->pfvf_map;
+       mutex_lock(&rvu->rsrc_lock);
+
+       if (TXSCH_MAP_FUNC(pfvf_map[schq]) != pcifunc) {
+               mutex_unlock(&rvu->rsrc_lock);
+               goto err;
+       }
+
+       /* Flush if it is a SMQ. Onus of disabling
+        * TL2/3 queue links before SMQ flush is on user
+        */
+       if (lvl == NIX_TXSCH_LVL_SMQ) {
+               cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq));
+               /* Do SMQ flush and set enqueue xoff */
+               cfg |= BIT_ULL(50) | BIT_ULL(49);
+               rvu_write64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq), cfg);
+
+               /* Wait for flush to complete */
+               rc = rvu_poll_reg(rvu, blkaddr,
+                                 NIX_AF_SMQX_CFG(schq), BIT_ULL(49), true);
+               if (rc) {
+                       dev_err(rvu->dev,
+                               "NIXLF%d: SMQ%d flush failed\n", nixlf, schq);
+               }
+       }
+
+       /* Free the resource */
+       rvu_free_rsrc(&txsch->schq, schq);
+       txsch->pfvf_map[schq] = 0;
+       mutex_unlock(&rvu->rsrc_lock);
+       return 0;
+err:
+       return NIX_AF_ERR_TLX_INVALID;
+}
+
+int rvu_mbox_handler_nix_txsch_free(struct rvu *rvu,
                                    struct nix_txsch_free_req *req,
                                    struct msg_rsp *rsp)
 {
-       return nix_txschq_free(rvu, req->hdr.pcifunc);
+       if (req->flags & TXSCHQ_FREE_ALL)
+               return nix_txschq_free(rvu, req->hdr.pcifunc);
+       else
+               return nix_txschq_free_one(rvu, req);
 }
 
 static bool is_txschq_config_valid(struct rvu *rvu, u16 pcifunc, int blkaddr,
@@ -1118,16 +1431,73 @@ static bool is_txschq_config_valid(struct rvu *rvu, u16 pcifunc, int blkaddr,
        return true;
 }
 
-int rvu_mbox_handler_NIX_TXSCHQ_CFG(struct rvu *rvu,
+static int
+nix_tl1_default_cfg(struct rvu *rvu, u16 pcifunc)
+{
+       u16 schq_list[2], schq_cnt, schq;
+       int blkaddr, idx, err = 0;
+       u16 map_func, map_flags;
+       struct nix_hw *nix_hw;
+       u64 reg, regval;
+       u32 *pfvf_map;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       pfvf_map = nix_hw->txsch[NIX_TXSCH_LVL_TL1].pfvf_map;
+
+       mutex_lock(&rvu->rsrc_lock);
+
+       err = rvu_get_tl1_schqs(rvu, blkaddr,
+                               pcifunc, schq_list, &schq_cnt);
+       if (err)
+               goto unlock;
+
+       for (idx = 0; idx < schq_cnt; idx++) {
+               schq = schq_list[idx];
+               map_func = TXSCH_MAP_FUNC(pfvf_map[schq]);
+               map_flags = TXSCH_MAP_FLAGS(pfvf_map[schq]);
+
+               /* check if config is already done or this is pf */
+               if (map_flags & NIX_TXSCHQ_TL1_CFG_DONE)
+                       continue;
+
+               /* default configuration */
+               reg = NIX_AF_TL1X_TOPOLOGY(schq);
+               regval = (TXSCH_TL1_DFLT_RR_PRIO << 1);
+               rvu_write64(rvu, blkaddr, reg, regval);
+               reg = NIX_AF_TL1X_SCHEDULE(schq);
+               regval = TXSCH_TL1_DFLT_RR_QTM;
+               rvu_write64(rvu, blkaddr, reg, regval);
+               reg = NIX_AF_TL1X_CIR(schq);
+               regval = 0;
+               rvu_write64(rvu, blkaddr, reg, regval);
+
+               map_flags |= NIX_TXSCHQ_TL1_CFG_DONE;
+               pfvf_map[schq] = TXSCH_MAP(map_func, map_flags);
+       }
+unlock:
+       mutex_unlock(&rvu->rsrc_lock);
+       return err;
+}
+
+int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu,
                                    struct nix_txschq_config *req,
                                    struct msg_rsp *rsp)
 {
+       u16 schq, pcifunc = req->hdr.pcifunc;
        struct rvu_hwinfo *hw = rvu->hw;
-       u16 pcifunc = req->hdr.pcifunc;
        u64 reg, regval, schq_regbase;
        struct nix_txsch *txsch;
+       u16 map_func, map_flags;
        struct nix_hw *nix_hw;
        int blkaddr, idx, err;
+       u32 *pfvf_map;
        int nixlf;
 
        if (req->lvl >= NIX_TXSCH_LVL_CNT ||
@@ -1147,6 +1517,16 @@ int rvu_mbox_handler_NIX_TXSCHQ_CFG(struct rvu *rvu,
                return NIX_AF_ERR_AF_LF_INVALID;
 
        txsch = &nix_hw->txsch[req->lvl];
+       pfvf_map = txsch->pfvf_map;
+
+       /* VF is only allowed to trigger
+        * setting default cfg on TL1
+        */
+       if (pcifunc & RVU_PFVF_FUNC_MASK &&
+           req->lvl == NIX_TXSCH_LVL_TL1) {
+               return nix_tl1_default_cfg(rvu, pcifunc);
+       }
+
        for (idx = 0; idx < req->num_regs; idx++) {
                reg = req->reg[idx];
                regval = req->regval[idx];
@@ -1164,6 +1544,21 @@ int rvu_mbox_handler_NIX_TXSCHQ_CFG(struct rvu *rvu,
                        regval |= ((u64)nixlf << 24);
                }
 
+               /* Mark config as done for TL1 by PF */
+               if (schq_regbase >= NIX_AF_TL1X_SCHEDULE(0) &&
+                   schq_regbase <= NIX_AF_TL1X_GREEN_BYTES(0)) {
+                       schq = TXSCHQ_IDX(reg, TXSCHQ_IDX_SHIFT);
+
+                       mutex_lock(&rvu->rsrc_lock);
+
+                       map_func = TXSCH_MAP_FUNC(pfvf_map[schq]);
+                       map_flags = TXSCH_MAP_FLAGS(pfvf_map[schq]);
+
+                       map_flags |= NIX_TXSCHQ_TL1_CFG_DONE;
+                       pfvf_map[schq] = TXSCH_MAP(map_func, map_flags);
+                       mutex_unlock(&rvu->rsrc_lock);
+               }
+
                rvu_write64(rvu, blkaddr, reg, regval);
 
                /* Check for SMQ flush, if so, poll for its completion */
@@ -1181,35 +1576,22 @@ int rvu_mbox_handler_NIX_TXSCHQ_CFG(struct rvu *rvu,
 static int nix_rx_vtag_cfg(struct rvu *rvu, int nixlf, int blkaddr,
                           struct nix_vtag_config *req)
 {
-       u64 regval = 0;
+       u64 regval = req->vtag_size;
 
-#define NIX_VTAGTYPE_MAX 0x8ull
-#define NIX_VTAGSIZE_MASK 0x7ull
-#define NIX_VTAGSTRIP_CAP_MASK 0x30ull
-
-       if (req->rx.vtag_type >= NIX_VTAGTYPE_MAX ||
-           req->vtag_size > VTAGSIZE_T8)
+       if (req->rx.vtag_type > 7 || req->vtag_size > VTAGSIZE_T8)
                return -EINVAL;
 
-       regval = rvu_read64(rvu, blkaddr,
-                           NIX_AF_LFX_RX_VTAG_TYPEX(nixlf, req->rx.vtag_type));
-
-       if (req->rx.strip_vtag && req->rx.capture_vtag)
-               regval |= BIT_ULL(4) | BIT_ULL(5);
-       else if (req->rx.strip_vtag)
+       if (req->rx.capture_vtag)
+               regval |= BIT_ULL(5);
+       if (req->rx.strip_vtag)
                regval |= BIT_ULL(4);
-       else
-               regval &= ~(BIT_ULL(4) | BIT_ULL(5));
-
-       regval &= ~NIX_VTAGSIZE_MASK;
-       regval |= req->vtag_size & NIX_VTAGSIZE_MASK;
 
        rvu_write64(rvu, blkaddr,
                    NIX_AF_LFX_RX_VTAG_TYPEX(nixlf, req->rx.vtag_type), regval);
        return 0;
 }
 
-int rvu_mbox_handler_NIX_VTAG_CFG(struct rvu *rvu,
+int rvu_mbox_handler_nix_vtag_cfg(struct rvu *rvu,
                                  struct nix_vtag_config *req,
                                  struct msg_rsp *rsp)
 {
@@ -1243,7 +1625,7 @@ static int nix_setup_mce(struct rvu *rvu, int mce, u8 op,
        struct nix_aq_enq_req aq_req;
        int err;
 
-       aq_req.hdr.pcifunc = pcifunc;
+       aq_req.hdr.pcifunc = 0;
        aq_req.ctype = NIX_AQ_CTYPE_MCE;
        aq_req.op = op;
        aq_req.qidx = mce;
@@ -1294,7 +1676,7 @@ static int nix_update_mce_list(struct nix_mce_list *mce_list,
                return 0;
 
        /* Add a new one to the list, at the tail */
-       mce = kzalloc(sizeof(*mce), GFP_ATOMIC);
+       mce = kzalloc(sizeof(*mce), GFP_KERNEL);
        if (!mce)
                return -ENOMEM;
        mce->idx = idx;
@@ -1317,6 +1699,10 @@ static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add)
        struct rvu_pfvf *pfvf;
        int blkaddr;
 
+       /* Broadcast pkt replication is not needed for AF's VFs, hence skip */
+       if (is_afvf(pcifunc))
+               return 0;
+
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
        if (blkaddr < 0)
                return 0;
@@ -1340,7 +1726,7 @@ static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add)
                return -EINVAL;
        }
 
-       spin_lock(&mcast->mce_lock);
+       mutex_lock(&mcast->mce_lock);
 
        err = nix_update_mce_list(mce_list, pcifunc, idx, add);
        if (err)
@@ -1370,7 +1756,7 @@ static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add)
        }
 
 end:
-       spin_unlock(&mcast->mce_lock);
+       mutex_unlock(&mcast->mce_lock);
        return err;
 }
 
@@ -1455,7 +1841,7 @@ static int nix_setup_mcast(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
                    BIT_ULL(63) | (mcast->replay_pkind << 24) |
                    BIT_ULL(20) | MC_BUF_CNT);
 
-       spin_lock_init(&mcast->mce_lock);
+       mutex_init(&mcast->mce_lock);
 
        return nix_setup_bcast_tables(rvu, nix_hw);
 }
@@ -1499,14 +1885,66 @@ static int nix_setup_txschq(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
                 * PF/VF pcifunc mapping info.
                 */
                txsch->pfvf_map = devm_kcalloc(rvu->dev, txsch->schq.max,
-                                              sizeof(u16), GFP_KERNEL);
+                                              sizeof(u32), GFP_KERNEL);
                if (!txsch->pfvf_map)
                        return -ENOMEM;
+               memset(txsch->pfvf_map, U8_MAX, txsch->schq.max * sizeof(u32));
+       }
+       return 0;
+}
+
+int rvu_nix_reserve_mark_format(struct rvu *rvu, struct nix_hw *nix_hw,
+                               int blkaddr, u32 cfg)
+{
+       int fmt_idx;
+
+       for (fmt_idx = 0; fmt_idx < nix_hw->mark_format.in_use; fmt_idx++) {
+               if (nix_hw->mark_format.cfg[fmt_idx] == cfg)
+                       return fmt_idx;
        }
+       if (fmt_idx >= nix_hw->mark_format.total)
+               return -ERANGE;
+
+       rvu_write64(rvu, blkaddr, NIX_AF_MARK_FORMATX_CTL(fmt_idx), cfg);
+       nix_hw->mark_format.cfg[fmt_idx] = cfg;
+       nix_hw->mark_format.in_use++;
+       return fmt_idx;
+}
+
+static int nix_af_mark_format_setup(struct rvu *rvu, struct nix_hw *nix_hw,
+                                   int blkaddr)
+{
+       u64 cfgs[] = {
+               [NIX_MARK_CFG_IP_DSCP_RED]         = 0x10003,
+               [NIX_MARK_CFG_IP_DSCP_YELLOW]      = 0x11200,
+               [NIX_MARK_CFG_IP_DSCP_YELLOW_RED]  = 0x11203,
+               [NIX_MARK_CFG_IP_ECN_RED]          = 0x6000c,
+               [NIX_MARK_CFG_IP_ECN_YELLOW]       = 0x60c00,
+               [NIX_MARK_CFG_IP_ECN_YELLOW_RED]   = 0x60c0c,
+               [NIX_MARK_CFG_VLAN_DEI_RED]        = 0x30008,
+               [NIX_MARK_CFG_VLAN_DEI_YELLOW]     = 0x30800,
+               [NIX_MARK_CFG_VLAN_DEI_YELLOW_RED] = 0x30808,
+       };
+       int i, rc;
+       u64 total;
+
+       total = (rvu_read64(rvu, blkaddr, NIX_AF_PSE_CONST) & 0xFF00) >> 8;
+       nix_hw->mark_format.total = (u8)total;
+       nix_hw->mark_format.cfg = devm_kcalloc(rvu->dev, total, sizeof(u32),
+                                              GFP_KERNEL);
+       if (!nix_hw->mark_format.cfg)
+               return -ENOMEM;
+       for (i = 0; i < NIX_MARK_CFG_MAX; i++) {
+               rc = rvu_nix_reserve_mark_format(rvu, nix_hw, blkaddr, cfgs[i]);
+               if (rc < 0)
+                       dev_err(rvu->dev, "Err %d in setup mark format %d\n",
+                               i, rc);
+       }
+
        return 0;
 }
 
-int rvu_mbox_handler_NIX_STATS_RST(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_nix_stats_rst(struct rvu *rvu, struct msg_req *req,
                                   struct msg_rsp *rsp)
 {
        struct rvu_hwinfo *hw = rvu->hw;
@@ -1537,190 +1975,287 @@ int rvu_mbox_handler_NIX_STATS_RST(struct rvu *rvu, struct msg_req *req,
 }
 
 /* Returns the ALG index to be set into NPC_RX_ACTION */
-static int get_flowkey_alg_idx(u32 flow_cfg)
-{
-       u32 ip_cfg;
-
-       flow_cfg &= ~FLOW_KEY_TYPE_PORT;
-       ip_cfg = FLOW_KEY_TYPE_IPV4 | FLOW_KEY_TYPE_IPV6;
-       if (flow_cfg == ip_cfg)
-               return FLOW_KEY_ALG_IP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_TCP))
-               return FLOW_KEY_ALG_TCP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_UDP))
-               return FLOW_KEY_ALG_UDP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_SCTP))
-               return FLOW_KEY_ALG_SCTP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_TCP | FLOW_KEY_TYPE_UDP))
-               return FLOW_KEY_ALG_TCP_UDP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_TCP | FLOW_KEY_TYPE_SCTP))
-               return FLOW_KEY_ALG_TCP_SCTP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_UDP | FLOW_KEY_TYPE_SCTP))
-               return FLOW_KEY_ALG_UDP_SCTP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_TCP |
-                             FLOW_KEY_TYPE_UDP | FLOW_KEY_TYPE_SCTP))
-               return FLOW_KEY_ALG_TCP_UDP_SCTP;
-
-       return FLOW_KEY_ALG_PORT;
-}
-
-int rvu_mbox_handler_NIX_RSS_FLOWKEY_CFG(struct rvu *rvu,
-                                        struct nix_rss_flowkey_cfg *req,
-                                        struct msg_rsp *rsp)
+static int get_flowkey_alg_idx(struct nix_hw *nix_hw, u32 flow_cfg)
 {
-       struct rvu_hwinfo *hw = rvu->hw;
-       u16 pcifunc = req->hdr.pcifunc;
-       int alg_idx, nixlf, blkaddr;
-
-       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
-       if (blkaddr < 0)
-               return NIX_AF_ERR_AF_LF_INVALID;
-
-       nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
-       if (nixlf < 0)
-               return NIX_AF_ERR_AF_LF_INVALID;
+       int i;
 
-       alg_idx = get_flowkey_alg_idx(req->flowkey_cfg);
+       /* Scan over exiting algo entries to find a match */
+       for (i = 0; i < nix_hw->flowkey.in_use; i++)
+               if (nix_hw->flowkey.flowkey[i] == flow_cfg)
+                       return i;
 
-       rvu_npc_update_flowkey_alg_idx(rvu, pcifunc, nixlf, req->group,
-                                      alg_idx, req->mcam_index);
-       return 0;
+       return -ERANGE;
 }
 
-static void set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg)
+static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg)
 {
-       struct nix_rx_flowkey_alg *field = NULL;
-       int idx, key_type;
+       int idx, nr_field, key_off, field_marker, keyoff_marker;
+       int max_key_off, max_bit_pos, group_member;
+       struct nix_rx_flowkey_alg *field;
+       struct nix_rx_flowkey_alg tmp;
+       u32 key_type, valid_key;
 
        if (!alg)
-               return;
+               return -EINVAL;
 
-       /* FIELD0: IPv4
-        * FIELD1: IPv6
-        * FIELD2: TCP/UDP/SCTP/ALL
-        * FIELD3: Unused
-        * FIELD4: Unused
-        *
-        * Each of the 32 possible flow key algorithm definitions should
+#define FIELDS_PER_ALG  5
+#define MAX_KEY_OFF    40
+       /* Clear all fields */
+       memset(alg, 0, sizeof(uint64_t) * FIELDS_PER_ALG);
+
+       /* Each of the 32 possible flow key algorithm definitions should
         * fall into above incremental config (except ALG0). Otherwise a
         * single NPC MCAM entry is not sufficient for supporting RSS.
         *
         * If a different definition or combination needed then NPC MCAM
         * has to be programmed to filter such pkts and it's action should
         * point to this definition to calculate flowtag or hash.
+        *
+        * The `for loop` goes over _all_ protocol field and the following
+        * variables depicts the state machine forward progress logic.
+        *
+        * keyoff_marker - Enabled when hash byte length needs to be accounted
+        * in field->key_offset update.
+        * field_marker - Enabled when a new field needs to be selected.
+        * group_member - Enabled when protocol is part of a group.
         */
-       for (idx = 0; idx < 32; idx++) {
-               key_type = flow_cfg & BIT_ULL(idx);
-               if (!key_type)
-                       continue;
+
+       keyoff_marker = 0; max_key_off = 0; group_member = 0;
+       nr_field = 0; key_off = 0; field_marker = 1;
+       field = &tmp; max_bit_pos = fls(flow_cfg);
+       for (idx = 0;
+            idx < max_bit_pos && nr_field < FIELDS_PER_ALG &&
+            key_off < MAX_KEY_OFF; idx++) {
+               key_type = BIT(idx);
+               valid_key = flow_cfg & key_type;
+               /* Found a field marker, reset the field values */
+               if (field_marker)
+                       memset(&tmp, 0, sizeof(tmp));
+
                switch (key_type) {
-               case FLOW_KEY_TYPE_PORT:
-                       field = &alg[0];
+               case NIX_FLOW_KEY_TYPE_PORT:
                        field->sel_chan = true;
                        /* This should be set to 1, when SEL_CHAN is set */
                        field->bytesm1 = 1;
+                       field_marker = true;
+                       keyoff_marker = true;
                        break;
-               case FLOW_KEY_TYPE_IPV4:
-                       field = &alg[0];
+               case NIX_FLOW_KEY_TYPE_IPV4:
                        field->lid = NPC_LID_LC;
                        field->ltype_match = NPC_LT_LC_IP;
                        field->hdr_offset = 12; /* SIP offset */
                        field->bytesm1 = 7; /* SIP + DIP, 8 bytes */
                        field->ltype_mask = 0xF; /* Match only IPv4 */
+                       field_marker = true;
+                       keyoff_marker = false;
                        break;
-               case FLOW_KEY_TYPE_IPV6:
-                       field = &alg[1];
+               case NIX_FLOW_KEY_TYPE_IPV6:
                        field->lid = NPC_LID_LC;
                        field->ltype_match = NPC_LT_LC_IP6;
                        field->hdr_offset = 8; /* SIP offset */
                        field->bytesm1 = 31; /* SIP + DIP, 32 bytes */
                        field->ltype_mask = 0xF; /* Match only IPv6 */
+                       field_marker = true;
+                       keyoff_marker = true;
                        break;
-               case FLOW_KEY_TYPE_TCP:
-               case FLOW_KEY_TYPE_UDP:
-               case FLOW_KEY_TYPE_SCTP:
-                       field = &alg[2];
+               case NIX_FLOW_KEY_TYPE_TCP:
+               case NIX_FLOW_KEY_TYPE_UDP:
+               case NIX_FLOW_KEY_TYPE_SCTP:
                        field->lid = NPC_LID_LD;
                        field->bytesm1 = 3; /* Sport + Dport, 4 bytes */
-                       if (key_type == FLOW_KEY_TYPE_TCP)
+                       if (key_type == NIX_FLOW_KEY_TYPE_TCP && valid_key) {
                                field->ltype_match |= NPC_LT_LD_TCP;
-                       else if (key_type == FLOW_KEY_TYPE_UDP)
+                               group_member = true;
+                       } else if (key_type == NIX_FLOW_KEY_TYPE_UDP &&
+                                  valid_key) {
                                field->ltype_match |= NPC_LT_LD_UDP;
-                       else if (key_type == FLOW_KEY_TYPE_SCTP)
+                               group_member = true;
+                       } else if (key_type == NIX_FLOW_KEY_TYPE_SCTP &&
+                                  valid_key) {
                                field->ltype_match |= NPC_LT_LD_SCTP;
-                       field->key_offset = 32; /* After IPv4/v6 SIP, DIP */
+                               group_member = true;
+                       }
                        field->ltype_mask = ~field->ltype_match;
+                       if (key_type == NIX_FLOW_KEY_TYPE_SCTP) {
+                               /* Handle the case where any of the group item
+                                * is enabled in the group but not the final one
+                                */
+                               if (group_member) {
+                                       valid_key = true;
+                                       group_member = false;
+                               }
+                               field_marker = true;
+                               keyoff_marker = true;
+                       } else {
+                               field_marker = false;
+                               keyoff_marker = false;
+                       }
                        break;
                }
-               if (field)
-                       field->ena = 1;
-               field = NULL;
+               field->ena = 1;
+
+               /* Found a valid flow key type */
+               if (valid_key) {
+                       field->key_offset = key_off;
+                       memcpy(&alg[nr_field], field, sizeof(*field));
+                       max_key_off = max(max_key_off, field->bytesm1 + 1);
+
+                       /* Found a field marker, get the next field */
+                       if (field_marker)
+                               nr_field++;
+               }
+
+               /* Found a keyoff marker, update the new key_off */
+               if (keyoff_marker) {
+                       key_off += max_key_off;
+                       max_key_off = 0;
+               }
        }
+       /* Processed all the flow key types */
+       if (idx == max_bit_pos && key_off <= MAX_KEY_OFF)
+               return 0;
+       else
+               return NIX_AF_ERR_RSS_NOSPC_FIELD;
 }
 
-static void nix_rx_flowkey_alg_cfg(struct rvu *rvu, int blkaddr)
+static int reserve_flowkey_alg_idx(struct rvu *rvu, int blkaddr, u32 flow_cfg)
 {
-#define FIELDS_PER_ALG 5
-       u64 field[FLOW_KEY_ALG_MAX][FIELDS_PER_ALG];
-       u32 flowkey_cfg, minkey_cfg;
-       int alg, fid;
+       u64 field[FIELDS_PER_ALG];
+       struct nix_hw *hw;
+       int fid, rc;
 
-       memset(&field, 0, sizeof(u64) * FLOW_KEY_ALG_MAX * FIELDS_PER_ALG);
+       hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!hw)
+               return -EINVAL;
 
-       /* Only incoming channel number */
-       flowkey_cfg = FLOW_KEY_TYPE_PORT;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_PORT], flowkey_cfg);
+       /* No room to add new flow hash algoritham */
+       if (hw->flowkey.in_use >= NIX_FLOW_KEY_ALG_MAX)
+               return NIX_AF_ERR_RSS_NOSPC_ALGO;
 
-       /* For a incoming pkt if none of the fields match then flowkey
-        * will be zero, hence tag generated will also be zero.
-        * RSS entry at rsse_index = NIX_AF_LF()_RSS_GRP()[OFFSET] will
-        * be used to queue the packet.
-        */
+       /* Generate algo fields for the given flow_cfg */
+       rc = set_flowkey_fields((struct nix_rx_flowkey_alg *)field, flow_cfg);
+       if (rc)
+               return rc;
 
-       /* IPv4/IPv6 SIP/DIPs */
-       flowkey_cfg = FLOW_KEY_TYPE_IPV4 | FLOW_KEY_TYPE_IPV6;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_IP], flowkey_cfg);
+       /* Update ALGX_FIELDX register with generated fields */
+       for (fid = 0; fid < FIELDS_PER_ALG; fid++)
+               rvu_write64(rvu, blkaddr,
+                           NIX_AF_RX_FLOW_KEY_ALGX_FIELDX(hw->flowkey.in_use,
+                                                          fid), field[fid]);
 
-       /* TCPv4/v6 4-tuple, SIP, DIP, Sport, Dport */
+       /* Store the flow_cfg for futher lookup */
+       rc = hw->flowkey.in_use;
+       hw->flowkey.flowkey[rc] = flow_cfg;
+       hw->flowkey.in_use++;
+
+       return rc;
+}
+
+int rvu_mbox_handler_nix_rss_flowkey_cfg(struct rvu *rvu,
+                                        struct nix_rss_flowkey_cfg *req,
+                                        struct nix_rss_flowkey_cfg_rsp *rsp)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       u16 pcifunc = req->hdr.pcifunc;
+       int alg_idx, nixlf, blkaddr;
+       struct nix_hw *nix_hw;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
+       if (nixlf < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       alg_idx = get_flowkey_alg_idx(nix_hw, req->flowkey_cfg);
+       /* Failed to get algo index from the exiting list, reserve new  */
+       if (alg_idx < 0) {
+               alg_idx = reserve_flowkey_alg_idx(rvu, blkaddr,
+                                                 req->flowkey_cfg);
+               if (alg_idx < 0)
+                       return alg_idx;
+       }
+       rsp->alg_idx = alg_idx;
+       rvu_npc_update_flowkey_alg_idx(rvu, pcifunc, nixlf, req->group,
+                                      alg_idx, req->mcam_index);
+       return 0;
+}
+
+static int nix_rx_flowkey_alg_cfg(struct rvu *rvu, int blkaddr)
+{
+       u32 flowkey_cfg, minkey_cfg;
+       int alg, fid, rc;
+
+       /* Disable all flow key algx fieldx */
+       for (alg = 0; alg < NIX_FLOW_KEY_ALG_MAX; alg++) {
+               for (fid = 0; fid < FIELDS_PER_ALG; fid++)
+                       rvu_write64(rvu, blkaddr,
+                                   NIX_AF_RX_FLOW_KEY_ALGX_FIELDX(alg, fid),
+                                   0);
+       }
+
+       /* IPv4/IPv6 SIP/DIPs */
+       flowkey_cfg = NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
+
+       /* TCPv4/v6 4-tuple, SIP, DIP, Sport, Dport */
        minkey_cfg = flowkey_cfg;
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_TCP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_TCP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_TCP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* UDPv4/v6 4-tuple, SIP, DIP, Sport, Dport */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_UDP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_UDP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_UDP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* SCTPv4/v6 4-tuple, SIP, DIP, Sport, Dport */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_SCTP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_SCTP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_SCTP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* TCP/UDP v4/v6 4-tuple, rest IP pkts 2-tuple */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_TCP | FLOW_KEY_TYPE_UDP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_TCP_UDP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_TCP |
+                       NIX_FLOW_KEY_TYPE_UDP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* TCP/SCTP v4/v6 4-tuple, rest IP pkts 2-tuple */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_TCP | FLOW_KEY_TYPE_SCTP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_TCP_SCTP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_TCP |
+                       NIX_FLOW_KEY_TYPE_SCTP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* UDP/SCTP v4/v6 4-tuple, rest IP pkts 2-tuple */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_UDP | FLOW_KEY_TYPE_SCTP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_UDP_SCTP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_UDP |
+                       NIX_FLOW_KEY_TYPE_SCTP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* TCP/UDP/SCTP v4/v6 4-tuple, rest IP pkts 2-tuple */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_TCP |
-                     FLOW_KEY_TYPE_UDP | FLOW_KEY_TYPE_SCTP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_TCP_UDP_SCTP],
-                          flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_TCP |
+                     NIX_FLOW_KEY_TYPE_UDP | NIX_FLOW_KEY_TYPE_SCTP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
-       for (alg = 0; alg < FLOW_KEY_ALG_MAX; alg++) {
-               for (fid = 0; fid < FIELDS_PER_ALG; fid++)
-                       rvu_write64(rvu, blkaddr,
-                                   NIX_AF_RX_FLOW_KEY_ALGX_FIELDX(alg, fid),
-                                   field[alg][fid]);
-       }
+       return 0;
 }
 
-int rvu_mbox_handler_NIX_SET_MAC_ADDR(struct rvu *rvu,
+int rvu_mbox_handler_nix_set_mac_addr(struct rvu *rvu,
                                      struct nix_set_mac_addr *req,
                                      struct msg_rsp *rsp)
 {
@@ -1742,10 +2277,13 @@ int rvu_mbox_handler_NIX_SET_MAC_ADDR(struct rvu *rvu,
 
        rvu_npc_install_ucast_entry(rvu, pcifunc, nixlf,
                                    pfvf->rx_chan_base, req->mac_addr);
+
+       rvu_npc_update_rxvlan(rvu, pcifunc, nixlf);
+
        return 0;
 }
 
-int rvu_mbox_handler_NIX_SET_RX_MODE(struct rvu *rvu, struct nix_rx_mode *req,
+int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,
                                     struct msg_rsp *rsp)
 {
        bool allmulti = false, disable_promisc = false;
@@ -1775,9 +2313,303 @@ int rvu_mbox_handler_NIX_SET_RX_MODE(struct rvu *rvu, struct nix_rx_mode *req,
        else
                rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf,
                                              pfvf->rx_chan_base, allmulti);
+
+       rvu_npc_update_rxvlan(rvu, pcifunc, nixlf);
+
        return 0;
 }
 
+static void nix_find_link_frs(struct rvu *rvu,
+                             struct nix_frs_cfg *req, u16 pcifunc)
+{
+       int pf = rvu_get_pf(pcifunc);
+       struct rvu_pfvf *pfvf;
+       int maxlen, minlen;
+       int numvfs, hwvf;
+       int vf;
+
+       /* Update with requester's min/max lengths */
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       pfvf->maxlen = req->maxlen;
+       if (req->update_minlen)
+               pfvf->minlen = req->minlen;
+
+       maxlen = req->maxlen;
+       minlen = req->update_minlen ? req->minlen : 0;
+
+       /* Get this PF's numVFs and starting hwvf */
+       rvu_get_pf_numvfs(rvu, pf, &numvfs, &hwvf);
+
+       /* For each VF, compare requested max/minlen */
+       for (vf = 0; vf < numvfs; vf++) {
+               pfvf =  &rvu->hwvf[hwvf + vf];
+               if (pfvf->maxlen > maxlen)
+                       maxlen = pfvf->maxlen;
+               if (req->update_minlen &&
+                   pfvf->minlen && pfvf->minlen < minlen)
+                       minlen = pfvf->minlen;
+       }
+
+       /* Compare requested max/minlen with PF's max/minlen */
+       pfvf = &rvu->pf[pf];
+       if (pfvf->maxlen > maxlen)
+               maxlen = pfvf->maxlen;
+       if (req->update_minlen &&
+           pfvf->minlen && pfvf->minlen < minlen)
+               minlen = pfvf->minlen;
+
+       /* Update the request with max/min PF's and it's VF's max/min */
+       req->maxlen = maxlen;
+       if (req->update_minlen)
+               req->minlen = minlen;
+}
+
+int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req,
+                                   struct msg_rsp *rsp)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       u16 pcifunc = req->hdr.pcifunc;
+       int pf = rvu_get_pf(pcifunc);
+       int blkaddr, schq, link = -1;
+       struct nix_txsch *txsch;
+       u64 cfg, lmac_fifo_len;
+       struct nix_hw *nix_hw;
+       u8 cgx = 0, lmac = 0;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       if (!req->sdp_link && req->maxlen > NIC_HW_MAX_FRS)
+               return NIX_AF_ERR_FRS_INVALID;
+
+       if (req->update_minlen && req->minlen < NIC_HW_MIN_FRS)
+               return NIX_AF_ERR_FRS_INVALID;
+
+       /* Check if requester wants to update SMQ's */
+       if (!req->update_smq)
+               goto rx_frscfg;
+
+       /* Update min/maxlen in each of the SMQ attached to this PF/VF */
+       txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ];
+       mutex_lock(&rvu->rsrc_lock);
+       for (schq = 0; schq < txsch->schq.max; schq++) {
+               if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
+                       continue;
+               cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq));
+               cfg = (cfg & ~(0xFFFFULL << 8)) | ((u64)req->maxlen << 8);
+               if (req->update_minlen)
+                       cfg = (cfg & ~0x7FULL) | ((u64)req->minlen & 0x7F);
+               rvu_write64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq), cfg);
+       }
+       mutex_unlock(&rvu->rsrc_lock);
+
+rx_frscfg:
+       /* Check if config is for SDP link */
+       if (req->sdp_link) {
+               if (!hw->sdp_links)
+                       return NIX_AF_ERR_RX_LINK_INVALID;
+               link = hw->cgx_links + hw->lbk_links;
+               goto linkcfg;
+       }
+
+       /* Check if the request is from CGX mapped RVU PF */
+       if (is_pf_cgxmapped(rvu, pf)) {
+               /* Get CGX and LMAC to which this PF is mapped and find link */
+               rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx, &lmac);
+               link = (cgx * hw->lmac_per_cgx) + lmac;
+       } else if (pf == 0) {
+               /* For VFs of PF0 ingress is LBK port, so config LBK link */
+               link = hw->cgx_links;
+       }
+
+       if (link < 0)
+               return NIX_AF_ERR_RX_LINK_INVALID;
+
+       nix_find_link_frs(rvu, req, pcifunc);
+
+linkcfg:
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link));
+       cfg = (cfg & ~(0xFFFFULL << 16)) | ((u64)req->maxlen << 16);
+       if (req->update_minlen)
+               cfg = (cfg & ~0xFFFFULL) | req->minlen;
+       rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link), cfg);
+
+       if (req->sdp_link || pf == 0)
+               return 0;
+
+       /* Update transmit credits for CGX links */
+       lmac_fifo_len =
+               CGX_FIFO_LEN / cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu));
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link));
+       cfg &= ~(0xFFFFFULL << 12);
+       cfg |=  ((lmac_fifo_len - req->maxlen) / 16) << 12;
+       rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), cfg);
+       rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_EXPR_CREDIT(link), cfg);
+
+       return 0;
+}
+
+int rvu_mbox_handler_nix_rxvlan_alloc(struct rvu *rvu, struct msg_req *req,
+                                     struct msg_rsp *rsp)
+{
+       struct npc_mcam_alloc_entry_req alloc_req = { };
+       struct npc_mcam_alloc_entry_rsp alloc_rsp = { };
+       struct npc_mcam_free_entry_req free_req = { };
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr, nixlf, err;
+       struct rvu_pfvf *pfvf;
+
+       /* LBK VFs do not have separate MCAM UCAST entry hence
+        * skip allocating rxvlan for them
+        */
+       if (is_afvf(pcifunc))
+               return 0;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       if (pfvf->rxvlan)
+               return 0;
+
+       /* alloc new mcam entry */
+       alloc_req.hdr.pcifunc = pcifunc;
+       alloc_req.count = 1;
+
+       err = rvu_mbox_handler_npc_mcam_alloc_entry(rvu, &alloc_req,
+                                                   &alloc_rsp);
+       if (err)
+               return err;
+
+       /* update entry to enable rxvlan offload */
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0) {
+               err = NIX_AF_ERR_AF_LF_INVALID;
+               goto free_entry;
+       }
+
+       nixlf = rvu_get_lf(rvu, &rvu->hw->block[blkaddr], pcifunc, 0);
+       if (nixlf < 0) {
+               err = NIX_AF_ERR_AF_LF_INVALID;
+               goto free_entry;
+       }
+
+       pfvf->rxvlan_index = alloc_rsp.entry_list[0];
+       /* all it means is that rxvlan_index is valid */
+       pfvf->rxvlan = true;
+
+       err = rvu_npc_update_rxvlan(rvu, pcifunc, nixlf);
+       if (err)
+               goto free_entry;
+
+       return 0;
+free_entry:
+       free_req.hdr.pcifunc = pcifunc;
+       free_req.entry = alloc_rsp.entry_list[0];
+       rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, rsp);
+       pfvf->rxvlan = false;
+       return err;
+}
+
+int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req,
+                                   struct msg_rsp *rsp)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       u16 pcifunc = req->hdr.pcifunc;
+       struct rvu_block *block;
+       struct rvu_pfvf *pfvf;
+       int nixlf, blkaddr;
+       u64 cfg;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (!pfvf->nixlf || blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       block = &hw->block[blkaddr];
+       nixlf = rvu_get_lf(rvu, block, pcifunc, 0);
+       if (nixlf < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf));
+       /* Set the interface configuration */
+       if (req->len_verify & BIT(0))
+               cfg |= BIT_ULL(41);
+       else
+               cfg &= ~BIT_ULL(41);
+
+       if (req->len_verify & BIT(1))
+               cfg |= BIT_ULL(40);
+       else
+               cfg &= ~BIT_ULL(40);
+
+       if (req->csum_verify & BIT(0))
+               cfg |= BIT_ULL(37);
+       else
+               cfg &= ~BIT_ULL(37);
+
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf), cfg);
+
+       return 0;
+}
+
+static void nix_link_config(struct rvu *rvu, int blkaddr)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       int cgx, lmac_cnt, slink, link;
+       u64 tx_credits;
+
+       /* Set default min/max packet lengths allowed on NIX Rx links.
+        *
+        * With HW reset minlen value of 60byte, HW will treat ARP pkts
+        * as undersize and report them to SW as error pkts, hence
+        * setting it to 40 bytes.
+        */
+       for (link = 0; link < (hw->cgx_links + hw->lbk_links); link++) {
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link),
+                           NIC_HW_MAX_FRS << 16 | NIC_HW_MIN_FRS);
+       }
+
+       if (hw->sdp_links) {
+               link = hw->cgx_links + hw->lbk_links;
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link),
+                           SDP_HW_MAX_FRS << 16 | NIC_HW_MIN_FRS);
+       }
+
+       /* Set credits for Tx links assuming max packet length allowed.
+        * This will be reconfigured based on MTU set for PF/VF.
+        */
+       for (cgx = 0; cgx < hw->cgx; cgx++) {
+               lmac_cnt = cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu));
+               tx_credits = ((CGX_FIFO_LEN / lmac_cnt) - NIC_HW_MAX_FRS) / 16;
+               /* Enable credits and set credit pkt count to max allowed */
+               tx_credits =  (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1);
+               slink = cgx * hw->lmac_per_cgx;
+               for (link = slink; link < (slink + lmac_cnt); link++) {
+                       rvu_write64(rvu, blkaddr,
+                                   NIX_AF_TX_LINKX_NORM_CREDIT(link),
+                                   tx_credits);
+                       rvu_write64(rvu, blkaddr,
+                                   NIX_AF_TX_LINKX_EXPR_CREDIT(link),
+                                   tx_credits);
+               }
+       }
+
+       /* Set Tx credits for LBK link */
+       slink = hw->cgx_links;
+       for (link = slink; link < (slink + hw->lbk_links); link++) {
+               tx_credits = 1000; /* 10 * max LBK datarate = 10 * 100Gbps */
+               /* Enable credits and set credit pkt count to max allowed */
+               tx_credits =  (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1);
+               rvu_write64(rvu, blkaddr,
+                           NIX_AF_TX_LINKX_NORM_CREDIT(link), tx_credits);
+               rvu_write64(rvu, blkaddr,
+                           NIX_AF_TX_LINKX_EXPR_CREDIT(link), tx_credits);
+       }
+}
+
 static int nix_calibrate_x2p(struct rvu *rvu, int blkaddr)
 {
        int idx, err;
@@ -1796,8 +2628,10 @@ static int nix_calibrate_x2p(struct rvu *rvu, int blkaddr)
 
        status = rvu_read64(rvu, blkaddr, NIX_AF_STATUS);
        /* Check if CGX devices are ready */
-       for (idx = 0; idx < cgx_get_cgx_cnt(); idx++) {
-               if (status & (BIT_ULL(16 + idx)))
+       for (idx = 0; idx < rvu->cgx_cnt_max; idx++) {
+               /* Skip when cgx port is not available */
+               if (!rvu_cgx_pdata(idx, rvu) ||
+                   (status & (BIT_ULL(16 + idx))))
                        continue;
                dev_err(rvu->dev,
                        "CGX%d didn't respond to NIX X2P calibration\n", idx);
@@ -1830,10 +2664,10 @@ static int nix_aq_init(struct rvu *rvu, struct rvu_block *block)
        /* Set admin queue endianness */
        cfg = rvu_read64(rvu, block->addr, NIX_AF_CFG);
 #ifdef __BIG_ENDIAN
-       cfg |= BIT_ULL(1);
+       cfg |= BIT_ULL(8);
        rvu_write64(rvu, block->addr, NIX_AF_CFG, cfg);
 #else
-       cfg &= ~BIT_ULL(1);
+       cfg &= ~BIT_ULL(8);
        rvu_write64(rvu, block->addr, NIX_AF_CFG, cfg);
 #endif
 
@@ -1870,6 +2704,14 @@ int rvu_nix_init(struct rvu *rvu)
                return 0;
        block = &hw->block[blkaddr];
 
+       /* As per a HW errata in 9xxx A0 silicon, NIX may corrupt
+        * internal state when conditional clocks are turned off.
+        * Hence enable them.
+        */
+       if (is_rvu_9xxx_A0(rvu))
+               rvu_write64(rvu, blkaddr, NIX_AF_CFG,
+                           rvu_read64(rvu, blkaddr, NIX_AF_CFG) | 0x5EULL);
+
        /* Calibrate X2P bus to check if CGX/LBK links are fine */
        err = nix_calibrate_x2p(rvu, blkaddr);
        if (err)
@@ -1891,9 +2733,6 @@ int rvu_nix_init(struct rvu *rvu)
        /* Restore CINT timer delay to HW reset values */
        rvu_write64(rvu, blkaddr, NIX_AF_CINT_DELAY, 0x0ULL);
 
-       /* Configure segmentation offload formats */
-       nix_setup_lso(rvu, blkaddr);
-
        if (blkaddr == BLKADDR_NIX0) {
                hw->nix0 = devm_kzalloc(rvu->dev,
                                        sizeof(struct nix_hw), GFP_KERNEL);
@@ -1904,24 +2743,51 @@ int rvu_nix_init(struct rvu *rvu)
                if (err)
                        return err;
 
+               err = nix_af_mark_format_setup(rvu, hw->nix0, blkaddr);
+               if (err)
+                       return err;
+
                err = nix_setup_mcast(rvu, hw->nix0, blkaddr);
                if (err)
                        return err;
 
-               /* Config Outer L2, IP, TCP and UDP's NPC layer info.
+               /* Configure segmentation offload formats */
+               nix_setup_lso(rvu, hw->nix0, blkaddr);
+
+               /* Config Outer/Inner L2, IP, TCP, UDP and SCTP NPC layer info.
                 * This helps HW protocol checker to identify headers
                 * and validate length and checksums.
                 */
                rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OL2,
                            (NPC_LID_LA << 8) | (NPC_LT_LA_ETHER << 4) | 0x0F);
-               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OUDP,
-                           (NPC_LID_LD << 8) | (NPC_LT_LD_UDP << 4) | 0x0F);
-               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OTCP,
-                           (NPC_LID_LD << 8) | (NPC_LT_LD_TCP << 4) | 0x0F);
                rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OIP4,
                            (NPC_LID_LC << 8) | (NPC_LT_LC_IP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IIP4,
+                           (NPC_LID_LF << 8) | (NPC_LT_LF_TU_IP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OIP6,
+                           (NPC_LID_LC << 8) | (NPC_LT_LC_IP6 << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IIP6,
+                           (NPC_LID_LF << 8) | (NPC_LT_LF_TU_IP6 << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OTCP,
+                           (NPC_LID_LD << 8) | (NPC_LT_LD_TCP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_ITCP,
+                           (NPC_LID_LG << 8) | (NPC_LT_LG_TU_TCP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OUDP,
+                           (NPC_LID_LD << 8) | (NPC_LT_LD_UDP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IUDP,
+                           (NPC_LID_LG << 8) | (NPC_LT_LG_TU_UDP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OSCTP,
+                           (NPC_LID_LD << 8) | (NPC_LT_LD_SCTP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_ISCTP,
+                           (NPC_LID_LG << 8) | (NPC_LT_LG_TU_SCTP << 4) |
+                           0x0F);
+
+               err = nix_rx_flowkey_alg_cfg(rvu, blkaddr);
+               if (err)
+                       return err;
 
-               nix_rx_flowkey_alg_cfg(rvu, blkaddr);
+               /* Initialize CGX/LBK/SDP link credits, min/max pkt lengths */
+               nix_link_config(rvu, blkaddr);
        }
        return 0;
 }
@@ -1955,5 +2821,139 @@ void rvu_nix_freemem(struct rvu *rvu)
                mcast = &nix_hw->mcast;
                qmem_free(rvu->dev, mcast->mce_ctx);
                qmem_free(rvu->dev, mcast->mcast_buf);
+               mutex_destroy(&mcast->mce_lock);
+       }
+}
+
+static int nix_get_nixlf(struct rvu *rvu, u16 pcifunc, int *nixlf)
+{
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       struct rvu_hwinfo *hw = rvu->hw;
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (!pfvf->nixlf || blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       *nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
+       if (*nixlf < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       return 0;
+}
+
+int rvu_mbox_handler_nix_lf_start_rx(struct rvu *rvu, struct msg_req *req,
+                                    struct msg_rsp *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       int nixlf, err;
+
+       err = nix_get_nixlf(rvu, pcifunc, &nixlf);
+       if (err)
+               return err;
+
+       rvu_npc_enable_default_entries(rvu, pcifunc, nixlf);
+       return 0;
+}
+
+int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req,
+                                   struct msg_rsp *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       int nixlf, err;
+
+       err = nix_get_nixlf(rvu, pcifunc, &nixlf);
+       if (err)
+               return err;
+
+       rvu_npc_disable_default_entries(rvu, pcifunc, nixlf);
+       return 0;
+}
+
+void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int nixlf)
+{
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       struct hwctx_disable_req ctx_req;
+       int err;
+
+       ctx_req.hdr.pcifunc = pcifunc;
+
+       /* Cleanup NPC MCAM entries, free Tx scheduler queues being used */
+       nix_interface_deinit(rvu, pcifunc, nixlf);
+       nix_rx_sync(rvu, blkaddr);
+       nix_txschq_free(rvu, pcifunc);
+
+       if (pfvf->sq_ctx) {
+               ctx_req.ctype = NIX_AQ_CTYPE_SQ;
+               err = nix_lf_hwctx_disable(rvu, &ctx_req);
+               if (err)
+                       dev_err(rvu->dev, "SQ ctx disable failed\n");
+       }
+
+       if (pfvf->rq_ctx) {
+               ctx_req.ctype = NIX_AQ_CTYPE_RQ;
+               err = nix_lf_hwctx_disable(rvu, &ctx_req);
+               if (err)
+                       dev_err(rvu->dev, "RQ ctx disable failed\n");
        }
+
+       if (pfvf->cq_ctx) {
+               ctx_req.ctype = NIX_AQ_CTYPE_CQ;
+               err = nix_lf_hwctx_disable(rvu, &ctx_req);
+               if (err)
+                       dev_err(rvu->dev, "CQ ctx disable failed\n");
+       }
+
+       nix_ctx_free(rvu, pfvf);
+}
+
+int rvu_mbox_handler_nix_lso_format_cfg(struct rvu *rvu,
+                                       struct nix_lso_format_cfg *req,
+                                       struct nix_lso_format_cfg_rsp *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       struct nix_hw *nix_hw;
+       struct rvu_pfvf *pfvf;
+       int blkaddr, idx, f;
+       u64 reg;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (!pfvf->nixlf || blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       /* Find existing matching LSO format, if any */
+       for (idx = 0; idx < nix_hw->lso.in_use; idx++) {
+               for (f = 0; f < NIX_LSO_FIELD_MAX; f++) {
+                       reg = rvu_read64(rvu, blkaddr,
+                                        NIX_AF_LSO_FORMATX_FIELDX(idx, f));
+                       if (req->fields[f] != (reg & req->field_mask))
+                               break;
+               }
+
+               if (f == NIX_LSO_FIELD_MAX)
+                       break;
+       }
+
+       if (idx < nix_hw->lso.in_use) {
+               /* Match found */
+               rsp->lso_format_idx = idx;
+               return 0;
+       }
+
+       if (nix_hw->lso.in_use == nix_hw->lso.total)
+               return NIX_AF_ERR_LSO_CFG_FAIL;
+
+       rsp->lso_format_idx = nix_hw->lso.in_use++;
+
+       for (f = 0; f < NIX_LSO_FIELD_MAX; f++)
+               rvu_write64(rvu, blkaddr,
+                           NIX_AF_LSO_FORMATX_FIELDX(rsp->lso_format_idx, f),
+                           req->fields[f]);
+
+       return 0;
 }
index 7531fdc..c0e165d 100644 (file)
@@ -241,14 +241,14 @@ static int npa_lf_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req)
        return err;
 }
 
-int rvu_mbox_handler_NPA_AQ_ENQ(struct rvu *rvu,
+int rvu_mbox_handler_npa_aq_enq(struct rvu *rvu,
                                struct npa_aq_enq_req *req,
                                struct npa_aq_enq_rsp *rsp)
 {
        return rvu_npa_aq_enq_inst(rvu, req, rsp);
 }
 
-int rvu_mbox_handler_NPA_HWCTX_DISABLE(struct rvu *rvu,
+int rvu_mbox_handler_npa_hwctx_disable(struct rvu *rvu,
                                       struct hwctx_disable_req *req,
                                       struct msg_rsp *rsp)
 {
@@ -273,7 +273,7 @@ static void npa_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf)
        pfvf->npa_qints_ctx = NULL;
 }
 
-int rvu_mbox_handler_NPA_LF_ALLOC(struct rvu *rvu,
+int rvu_mbox_handler_npa_lf_alloc(struct rvu *rvu,
                                  struct npa_lf_alloc_req *req,
                                  struct npa_lf_alloc_rsp *rsp)
 {
@@ -372,7 +372,7 @@ exit:
        return rc;
 }
 
-int rvu_mbox_handler_NPA_LF_FREE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_npa_lf_free(struct rvu *rvu, struct msg_req *req,
                                 struct msg_rsp *rsp)
 {
        struct rvu_hwinfo *hw = rvu->hw;
@@ -470,3 +470,20 @@ void rvu_npa_freemem(struct rvu *rvu)
        block = &hw->block[blkaddr];
        rvu_aq_free(rvu, block->aq);
 }
+
+void rvu_npa_lf_teardown(struct rvu *rvu, u16 pcifunc, int npalf)
+{
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       struct hwctx_disable_req ctx_req;
+
+       /* Disable all pools */
+       ctx_req.hdr.pcifunc = pcifunc;
+       ctx_req.ctype = NPA_AQ_CTYPE_POOL;
+       npa_lf_hwctx_disable(rvu, &ctx_req);
+
+       /* Disable all auras */
+       ctx_req.ctype = NPA_AQ_CTYPE_AURA;
+       npa_lf_hwctx_disable(rvu, &ctx_req);
+
+       npa_ctx_free(rvu, pfvf);
+}
index 23ff47f..15f7027 100644 (file)
@@ -8,6 +8,7 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/bitfield.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 
@@ -15,6 +16,7 @@
 #include "rvu_reg.h"
 #include "rvu.h"
 #include "npc.h"
+#include "cgx.h"
 #include "npc_profile.h"
 
 #define RSVD_MCAM_ENTRIES_PER_PF       2 /* Bcast & Promisc */
 
 #define NPC_PARSE_RESULT_DMAC_OFFSET   8
 
-struct mcam_entry {
-#define NPC_MAX_KWS_IN_KEY     7 /* Number of keywords in max keywidth */
-       u64     kw[NPC_MAX_KWS_IN_KEY];
-       u64     kw_mask[NPC_MAX_KWS_IN_KEY];
-       u64     action;
-       u64     vtag_action;
-};
+static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam,
+                                     int blkaddr, u16 pcifunc);
+static void npc_mcam_free_all_counters(struct rvu *rvu, struct npc_mcam *mcam,
+                                      u16 pcifunc);
 
 void rvu_npc_set_pkind(struct rvu *rvu, int pkind, struct rvu_pfvf *pfvf)
 {
@@ -256,6 +255,46 @@ static void npc_config_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
                npc_enable_mcam_entry(rvu, mcam, blkaddr, actindex, false);
 }
 
+static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
+                               int blkaddr, u16 src, u16 dest)
+{
+       int dbank = npc_get_bank(mcam, dest);
+       int sbank = npc_get_bank(mcam, src);
+       u64 cfg, sreg, dreg;
+       int bank, i;
+
+       src &= (mcam->banksize - 1);
+       dest &= (mcam->banksize - 1);
+
+       /* Copy INTF's, W0's, W1's CAM0 and CAM1 configuration */
+       for (bank = 0; bank < mcam->banks_per_entry; bank++) {
+               sreg = NPC_AF_MCAMEX_BANKX_CAMX_INTF(src, sbank + bank, 0);
+               dreg = NPC_AF_MCAMEX_BANKX_CAMX_INTF(dest, dbank + bank, 0);
+               for (i = 0; i < 6; i++) {
+                       cfg = rvu_read64(rvu, blkaddr, sreg + (i * 8));
+                       rvu_write64(rvu, blkaddr, dreg + (i * 8), cfg);
+               }
+       }
+
+       /* Copy action */
+       cfg = rvu_read64(rvu, blkaddr,
+                        NPC_AF_MCAMEX_BANKX_ACTION(src, sbank));
+       rvu_write64(rvu, blkaddr,
+                   NPC_AF_MCAMEX_BANKX_ACTION(dest, dbank), cfg);
+
+       /* Copy TAG action */
+       cfg = rvu_read64(rvu, blkaddr,
+                        NPC_AF_MCAMEX_BANKX_TAG_ACT(src, sbank));
+       rvu_write64(rvu, blkaddr,
+                   NPC_AF_MCAMEX_BANKX_TAG_ACT(dest, dbank), cfg);
+
+       /* Enable or disable */
+       cfg = rvu_read64(rvu, blkaddr,
+                        NPC_AF_MCAMEX_BANKX_CFG(src, sbank));
+       rvu_write64(rvu, blkaddr,
+                   NPC_AF_MCAMEX_BANKX_CFG(dest, dbank), cfg);
+}
+
 static u64 npc_get_mcam_action(struct rvu *rvu, struct npc_mcam *mcam,
                               int blkaddr, int index)
 {
@@ -269,12 +308,17 @@ static u64 npc_get_mcam_action(struct rvu *rvu, struct npc_mcam *mcam,
 void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc,
                                 int nixlf, u64 chan, u8 *mac_addr)
 {
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
        struct npc_mcam *mcam = &rvu->hw->mcam;
        struct mcam_entry entry = { {0} };
        struct nix_rx_action action;
        int blkaddr, index, kwi;
        u64 mac = 0;
 
+       /* AF's VFs work in promiscuous mode */
+       if (is_afvf(pcifunc))
+               return;
+
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0)
                return;
@@ -308,22 +352,33 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc,
        entry.action = *(u64 *)&action;
        npc_config_mcam_entry(rvu, mcam, blkaddr, index,
                              NIX_INTF_RX, &entry, true);
+
+       /* add VLAN matching, setup action and save entry back for later */
+       entry.kw[0] |= (NPC_LT_LB_STAG | NPC_LT_LB_CTAG) << 20;
+       entry.kw_mask[0] |= (NPC_LT_LB_STAG & NPC_LT_LB_CTAG) << 20;
+
+       entry.vtag_action = VTAG0_VALID_BIT |
+                           FIELD_PREP(VTAG0_TYPE_MASK, 0) |
+                           FIELD_PREP(VTAG0_LID_MASK, NPC_LID_LA) |
+                           FIELD_PREP(VTAG0_RELPTR_MASK, 12);
+
+       memcpy(&pfvf->entry, &entry, sizeof(entry));
 }
 
 void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc,
                                   int nixlf, u64 chan, bool allmulti)
 {
        struct npc_mcam *mcam = &rvu->hw->mcam;
+       int blkaddr, ucast_idx, index, kwi;
        struct mcam_entry entry = { {0} };
-       struct nix_rx_action action;
-       int blkaddr, index, kwi;
+       struct nix_rx_action action = { };
 
-       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
-       if (blkaddr < 0)
+       /* Only PF or AF VF can add a promiscuous entry */
+       if ((pcifunc & RVU_PFVF_FUNC_MASK) && !is_afvf(pcifunc))
                return;
 
-       /* Only PF or AF VF can add a promiscuous entry */
-       if (pcifunc & RVU_PFVF_FUNC_MASK)
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
                return;
 
        index = npc_get_nixlf_mcam_index(mcam, pcifunc,
@@ -338,16 +393,29 @@ void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc,
                entry.kw_mask[kwi] = BIT_ULL(40);
        }
 
-       *(u64 *)&action = 0x00;
-       action.op = NIX_RX_ACTIONOP_UCAST;
-       action.pf_func = pcifunc;
+       ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc,
+                                            nixlf, NIXLF_UCAST_ENTRY);
+
+       /* If the corresponding PF's ucast action is RSS,
+        * use the same action for promisc also
+        */
+       if (is_mcam_entry_enabled(rvu, mcam, blkaddr, ucast_idx))
+               *(u64 *)&action = npc_get_mcam_action(rvu, mcam,
+                                                       blkaddr, ucast_idx);
+
+       if (action.op != NIX_RX_ACTIONOP_RSS) {
+               *(u64 *)&action = 0x00;
+               action.op = NIX_RX_ACTIONOP_UCAST;
+               action.pf_func = pcifunc;
+       }
 
        entry.action = *(u64 *)&action;
        npc_config_mcam_entry(rvu, mcam, blkaddr, index,
                              NIX_INTF_RX, &entry, true);
 }
 
-void rvu_npc_disable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf)
+static void npc_enadis_promisc_entry(struct rvu *rvu, u16 pcifunc,
+                                    int nixlf, bool enable)
 {
        struct npc_mcam *mcam = &rvu->hw->mcam;
        int blkaddr, index;
@@ -362,7 +430,17 @@ void rvu_npc_disable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf)
 
        index = npc_get_nixlf_mcam_index(mcam, pcifunc,
                                         nixlf, NIXLF_PROMISC_ENTRY);
-       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable);
+}
+
+void rvu_npc_disable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       npc_enadis_promisc_entry(rvu, pcifunc, nixlf, false);
+}
+
+void rvu_npc_enable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       npc_enadis_promisc_entry(rvu, pcifunc, nixlf, true);
 }
 
 void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc,
@@ -390,9 +468,28 @@ void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc,
        index = npc_get_nixlf_mcam_index(mcam, pcifunc,
                                         nixlf, NIXLF_BCAST_ENTRY);
 
-       /* Check for L2B bit and LMAC channel */
-       entry.kw[0] = BIT_ULL(25) | chan;
-       entry.kw_mask[0] = BIT_ULL(25) | 0xFFFULL;
+       /* Check for L2B bit and LMAC channel
+        * NOTE: Since MKEX default profile(a reduced version intended to
+        * accommodate more capability but igoring few bits) a stap-gap
+        * approach.
+        * Since we care for L2B which by HRM NPC_PARSE_KEX_S at BIT_POS[25], So
+        * moved to BIT_POS[13], ignoring ERRCODE, ERRLEV as we'll loose out
+        * on capability features needed for CoS (/from ODP PoV) e.g: VLAN,
+        * DSCP.
+        *
+        * Reduced layout of MKEX default profile -
+        * Includes following are (i.e.CHAN, L2/3{B/M}, LA, LB, LC, LD):
+        *
+        * BIT_POS[31:28] : LD
+        * BIT_POS[27:24] : LC
+        * BIT_POS[23:20] : LB
+        * BIT_POS[19:16] : LA
+        * BIT_POS[15:12] : L3B, L3M, L2B, L2M
+        * BIT_POS[11:00] : CHAN
+        *
+        */
+       entry.kw[0] = BIT_ULL(13) | chan;
+       entry.kw_mask[0] = BIT_ULL(13) | 0xFFFULL;
 
        *(u64 *)&action = 0x00;
 #ifdef MCAST_MCE
@@ -454,51 +551,110 @@ void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf,
 
        rvu_write64(rvu, blkaddr,
                    NPC_AF_MCAMEX_BANKX_ACTION(index, bank), *(u64 *)&action);
+
+       index = npc_get_nixlf_mcam_index(mcam, pcifunc,
+                                        nixlf, NIXLF_PROMISC_ENTRY);
+
+       /* If PF's promiscuous entry is enabled,
+        * Set RSS action for that entry as well
+        */
+       if (is_mcam_entry_enabled(rvu, mcam, blkaddr, index)) {
+               bank = npc_get_bank(mcam, index);
+               index &= (mcam->banksize - 1);
+
+               rvu_write64(rvu, blkaddr,
+                           NPC_AF_MCAMEX_BANKX_ACTION(index, bank),
+                           *(u64 *)&action);
+       }
+
+       rvu_npc_update_rxvlan(rvu, pcifunc, nixlf);
 }
 
-void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf)
+static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc,
+                                      int nixlf, bool enable)
 {
        struct npc_mcam *mcam = &rvu->hw->mcam;
        struct nix_rx_action action;
-       int blkaddr, index, bank;
+       int index, bank, blkaddr;
 
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0)
                return;
 
-       /* Disable ucast MCAM match entry of this PF/VF */
+       /* Ucast MCAM match entry of this PF/VF */
        index = npc_get_nixlf_mcam_index(mcam, pcifunc,
                                         nixlf, NIXLF_UCAST_ENTRY);
-       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable);
 
-       /* For PF, disable promisc and bcast MCAM match entries */
-       if (!(pcifunc & RVU_PFVF_FUNC_MASK)) {
-               index = npc_get_nixlf_mcam_index(mcam, pcifunc,
-                                                nixlf, NIXLF_BCAST_ENTRY);
-               /* For bcast, disable only if it's action is not
-                * packet replication, incase if action is replication
-                * then this PF's nixlf is removed from bcast replication
-                * list.
-                */
-               bank = npc_get_bank(mcam, index);
-               index &= (mcam->banksize - 1);
-               *(u64 *)&action = rvu_read64(rvu, blkaddr,
-                                    NPC_AF_MCAMEX_BANKX_ACTION(index, bank));
-               if (action.op != NIX_RX_ACTIONOP_MCAST)
-                       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
+       /* For PF, ena/dis promisc and bcast MCAM match entries */
+       if (pcifunc & RVU_PFVF_FUNC_MASK)
+               return;
 
+       /* For bcast, enable/disable only if it's action is not
+        * packet replication, incase if action is replication
+        * then this PF's nixlf is removed from bcast replication
+        * list.
+        */
+       index = npc_get_nixlf_mcam_index(mcam, pcifunc,
+                                        nixlf, NIXLF_BCAST_ENTRY);
+       bank = npc_get_bank(mcam, index);
+       *(u64 *)&action = rvu_read64(rvu, blkaddr,
+            NPC_AF_MCAMEX_BANKX_ACTION(index & (mcam->banksize - 1), bank));
+       if (action.op != NIX_RX_ACTIONOP_MCAST)
+               npc_enable_mcam_entry(rvu, mcam,
+                                     blkaddr, index, enable);
+       if (enable)
+               rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf);
+       else
                rvu_npc_disable_promisc_entry(rvu, pcifunc, nixlf);
-       }
+
+       rvu_npc_update_rxvlan(rvu, pcifunc, nixlf);
+}
+
+void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       npc_enadis_default_entries(rvu, pcifunc, nixlf, false);
+}
+
+void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       npc_enadis_default_entries(rvu, pcifunc, nixlf, true);
+}
+
+void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return;
+
+       mutex_lock(&mcam->lock);
+
+       /* Disable and free all MCAM entries mapped to this 'pcifunc' */
+       npc_mcam_free_all_entries(rvu, mcam, blkaddr, pcifunc);
+
+       /* Free all MCAM counters mapped to this 'pcifunc' */
+       npc_mcam_free_all_counters(rvu, mcam, pcifunc);
+
+       mutex_unlock(&mcam->lock);
+
+       rvu_npc_disable_default_entries(rvu, pcifunc, nixlf);
 }
 
-#define LDATA_EXTRACT_CONFIG(intf, lid, ltype, ld, cfg) \
+#define SET_KEX_LD(intf, lid, ltype, ld, cfg)  \
        rvu_write64(rvu, blkaddr,                       \
                NPC_AF_INTFX_LIDX_LTX_LDX_CFG(intf, lid, ltype, ld), cfg)
 
-#define LDATA_FLAGS_CONFIG(intf, ld, flags, cfg)       \
+#define SET_KEX_LDFLAGS(intf, ld, flags, cfg)  \
        rvu_write64(rvu, blkaddr,                       \
                NPC_AF_INTFX_LDATAX_FLAGSX_CFG(intf, ld, flags), cfg)
 
+#define KEX_LD_CFG(bytesm1, hdr_ofs, ena, flags_ena, key_ofs)          \
+                       (((bytesm1) << 16) | ((hdr_ofs) << 8) | ((ena) << 7) | \
+                        ((flags_ena) << 6) | ((key_ofs) & 0x3F))
+
 static void npc_config_ldata_extract(struct rvu *rvu, int blkaddr)
 {
        struct npc_mcam *mcam = &rvu->hw->mcam;
@@ -514,28 +670,171 @@ static void npc_config_ldata_extract(struct rvu *rvu, int blkaddr)
         */
        for (lid = 0; lid < lid_count; lid++) {
                for (ltype = 0; ltype < 16; ltype++) {
-                       LDATA_EXTRACT_CONFIG(NIX_INTF_RX, lid, ltype, 0, 0ULL);
-                       LDATA_EXTRACT_CONFIG(NIX_INTF_RX, lid, ltype, 1, 0ULL);
-                       LDATA_EXTRACT_CONFIG(NIX_INTF_TX, lid, ltype, 0, 0ULL);
-                       LDATA_EXTRACT_CONFIG(NIX_INTF_TX, lid, ltype, 1, 0ULL);
-
-                       LDATA_FLAGS_CONFIG(NIX_INTF_RX, 0, ltype, 0ULL);
-                       LDATA_FLAGS_CONFIG(NIX_INTF_RX, 1, ltype, 0ULL);
-                       LDATA_FLAGS_CONFIG(NIX_INTF_TX, 0, ltype, 0ULL);
-                       LDATA_FLAGS_CONFIG(NIX_INTF_TX, 1, ltype, 0ULL);
+                       SET_KEX_LD(NIX_INTF_RX, lid, ltype, 0, 0ULL);
+                       SET_KEX_LD(NIX_INTF_RX, lid, ltype, 1, 0ULL);
+                       SET_KEX_LD(NIX_INTF_TX, lid, ltype, 0, 0ULL);
+                       SET_KEX_LD(NIX_INTF_TX, lid, ltype, 1, 0ULL);
+
+                       SET_KEX_LDFLAGS(NIX_INTF_RX, 0, ltype, 0ULL);
+                       SET_KEX_LDFLAGS(NIX_INTF_RX, 1, ltype, 0ULL);
+                       SET_KEX_LDFLAGS(NIX_INTF_TX, 0, ltype, 0ULL);
+                       SET_KEX_LDFLAGS(NIX_INTF_TX, 1, ltype, 0ULL);
                }
        }
 
-       /* If we plan to extract Outer IPv4 tuple for TCP/UDP pkts
-        * then 112bit key is not sufficient
-        */
        if (mcam->keysize != NPC_MCAM_KEY_X2)
                return;
 
-       /* Start placing extracted data/flags from 64bit onwards, for now */
-       /* Extract DMAC from the packet */
-       cfg = (0x05 << 16) | BIT_ULL(7) | NPC_PARSE_RESULT_DMAC_OFFSET;
-       LDATA_EXTRACT_CONFIG(NIX_INTF_RX, NPC_LID_LA, NPC_LT_LA_ETHER, 0, cfg);
+       /* Default MCAM KEX profile */
+       /* Layer A: Ethernet: */
+
+       /* DMAC: 6 bytes, KW1[47:0] */
+       cfg = KEX_LD_CFG(0x05, 0x0, 0x1, 0x0, NPC_PARSE_RESULT_DMAC_OFFSET);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LA, NPC_LT_LA_ETHER, 0, cfg);
+
+       /* Ethertype: 2 bytes, KW0[47:32] */
+       cfg = KEX_LD_CFG(0x01, 0xc, 0x1, 0x0, 0x4);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LA, NPC_LT_LA_ETHER, 1, cfg);
+
+       /* Layer B: Single VLAN (CTAG) */
+       /* CTAG VLAN[2..3] + Ethertype, 4 bytes, KW0[63:32] */
+       cfg = KEX_LD_CFG(0x03, 0x0, 0x1, 0x0, 0x4);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_CTAG, 0, cfg);
+
+       /* Layer B: Stacked VLAN (STAG|QinQ) */
+       /* CTAG VLAN[2..3] + Ethertype, 4 bytes, KW0[63:32] */
+       cfg = KEX_LD_CFG(0x03, 0x4, 0x1, 0x0, 0x4);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_STAG, 0, cfg);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_QINQ, 0, cfg);
+
+       /* Layer C: IPv4 */
+       /* SIP+DIP: 8 bytes, KW2[63:0] */
+       cfg = KEX_LD_CFG(0x07, 0xc, 0x1, 0x0, 0x10);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LC, NPC_LT_LC_IP, 0, cfg);
+       /* TOS: 1 byte, KW1[63:56] */
+       cfg = KEX_LD_CFG(0x0, 0x1, 0x1, 0x0, 0xf);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LC, NPC_LT_LC_IP, 1, cfg);
+
+       /* Layer D:UDP */
+       /* SPORT: 2 bytes, KW3[15:0] */
+       cfg = KEX_LD_CFG(0x1, 0x0, 0x1, 0x0, 0x18);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_UDP, 0, cfg);
+       /* DPORT: 2 bytes, KW3[31:16] */
+       cfg = KEX_LD_CFG(0x1, 0x2, 0x1, 0x0, 0x1a);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_UDP, 1, cfg);
+
+       /* Layer D:TCP */
+       /* SPORT: 2 bytes, KW3[15:0] */
+       cfg = KEX_LD_CFG(0x1, 0x0, 0x1, 0x0, 0x18);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_TCP, 0, cfg);
+       /* DPORT: 2 bytes, KW3[31:16] */
+       cfg = KEX_LD_CFG(0x1, 0x2, 0x1, 0x0, 0x1a);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_TCP, 1, cfg);
+}
+
+static void npc_program_mkex_profile(struct rvu *rvu, int blkaddr,
+                                    struct npc_mcam_kex *mkex)
+{
+       int lid, lt, ld, fl;
+
+       rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_RX),
+                   mkex->keyx_cfg[NIX_INTF_RX]);
+       rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_TX),
+                   mkex->keyx_cfg[NIX_INTF_TX]);
+
+       for (ld = 0; ld < NPC_MAX_LD; ld++)
+               rvu_write64(rvu, blkaddr, NPC_AF_KEX_LDATAX_FLAGS_CFG(ld),
+                           mkex->kex_ld_flags[ld]);
+
+       for (lid = 0; lid < NPC_MAX_LID; lid++) {
+               for (lt = 0; lt < NPC_MAX_LT; lt++) {
+                       for (ld = 0; ld < NPC_MAX_LD; ld++) {
+                               SET_KEX_LD(NIX_INTF_RX, lid, lt, ld,
+                                          mkex->intf_lid_lt_ld[NIX_INTF_RX]
+                                          [lid][lt][ld]);
+
+                               SET_KEX_LD(NIX_INTF_TX, lid, lt, ld,
+                                          mkex->intf_lid_lt_ld[NIX_INTF_TX]
+                                          [lid][lt][ld]);
+                       }
+               }
+       }
+
+       for (ld = 0; ld < NPC_MAX_LD; ld++) {
+               for (fl = 0; fl < NPC_MAX_LFL; fl++) {
+                       SET_KEX_LDFLAGS(NIX_INTF_RX, ld, fl,
+                                       mkex->intf_ld_flags[NIX_INTF_RX]
+                                       [ld][fl]);
+
+                       SET_KEX_LDFLAGS(NIX_INTF_TX, ld, fl,
+                                       mkex->intf_ld_flags[NIX_INTF_TX]
+                                       [ld][fl]);
+               }
+       }
+}
+
+/* strtoull of "mkexprof" with base:36 */
+#define MKEX_SIGN      0x19bbfdbd15f
+#define MKEX_END_SIGN  0xdeadbeef
+
+static void npc_load_mkex_profile(struct rvu *rvu, int blkaddr)
+{
+       const char *mkex_profile = rvu->mkex_pfl_name;
+       struct device *dev = &rvu->pdev->dev;
+       void __iomem *mkex_prfl_addr = NULL;
+       struct npc_mcam_kex *mcam_kex;
+       u64 prfl_addr;
+       u64 prfl_sz;
+
+       /* If user not selected mkex profile */
+       if (!strncmp(mkex_profile, "default", MKEX_NAME_LEN))
+               goto load_default;
+
+       if (cgx_get_mkex_prfl_info(&prfl_addr, &prfl_sz))
+               goto load_default;
+
+       if (!prfl_addr || !prfl_sz)
+               goto load_default;
+
+       mkex_prfl_addr = ioremap_wc(prfl_addr, prfl_sz);
+       if (!mkex_prfl_addr)
+               goto load_default;
+
+       mcam_kex = (struct npc_mcam_kex *)mkex_prfl_addr;
+
+       while (((s64)prfl_sz > 0) && (mcam_kex->mkex_sign != MKEX_END_SIGN)) {
+               /* Compare with mkex mod_param name string */
+               if (mcam_kex->mkex_sign == MKEX_SIGN &&
+                   !strncmp(mcam_kex->name, mkex_profile, MKEX_NAME_LEN)) {
+                       /* Due to an errata (35786) in A0 pass silicon,
+                        * parse nibble enable configuration has to be
+                        * identical for both Rx and Tx interfaces.
+                        */
+                       if (is_rvu_9xxx_A0(rvu) &&
+                           mcam_kex->keyx_cfg[NIX_INTF_RX] !=
+                           mcam_kex->keyx_cfg[NIX_INTF_TX])
+                               goto load_default;
+
+                       /* Program selected mkex profile */
+                       npc_program_mkex_profile(rvu, blkaddr, mcam_kex);
+
+                       goto unmap;
+               }
+
+               mcam_kex++;
+               prfl_sz -= sizeof(struct npc_mcam_kex);
+       }
+       dev_warn(dev, "Failed to load requested profile: %s\n",
+                rvu->mkex_pfl_name);
+
+load_default:
+       dev_info(rvu->dev, "Using default mkex profile\n");
+       /* Config packet data and flags extraction into PARSE result */
+       npc_config_ldata_extract(rvu, blkaddr);
+
+unmap:
+       if (mkex_prfl_addr)
+               iounmap(mkex_prfl_addr);
 }
 
 static void npc_config_kpuaction(struct rvu *rvu, int blkaddr,
@@ -690,13 +989,14 @@ static int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr)
 {
        int nixlf_count = rvu_get_nixlf_count(rvu);
        struct npc_mcam *mcam = &rvu->hw->mcam;
-       int rsvd;
+       int rsvd, err;
        u64 cfg;
 
        /* Get HW limits */
        cfg = rvu_read64(rvu, blkaddr, NPC_AF_CONST);
        mcam->banks = (cfg >> 44) & 0xF;
        mcam->banksize = (cfg >> 28) & 0xFFFF;
+       mcam->counters.max = (cfg >> 48) & 0xFFFF;
 
        /* Actual number of MCAM entries vary by entry size */
        cfg = (rvu_read64(rvu, blkaddr,
@@ -728,20 +1028,82 @@ static int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr)
                return -ENOMEM;
        }
 
-       mcam->entries = mcam->total_entries - rsvd;
-       mcam->nixlf_offset = mcam->entries;
+       mcam->bmap_entries = mcam->total_entries - rsvd;
+       mcam->nixlf_offset = mcam->bmap_entries;
        mcam->pf_offset = mcam->nixlf_offset + nixlf_count;
 
-       spin_lock_init(&mcam->lock);
+       /* Allocate bitmaps for managing MCAM entries */
+       mcam->bmap = devm_kcalloc(rvu->dev, BITS_TO_LONGS(mcam->bmap_entries),
+                                 sizeof(long), GFP_KERNEL);
+       if (!mcam->bmap)
+               return -ENOMEM;
+
+       mcam->bmap_reverse = devm_kcalloc(rvu->dev,
+                                         BITS_TO_LONGS(mcam->bmap_entries),
+                                         sizeof(long), GFP_KERNEL);
+       if (!mcam->bmap_reverse)
+               return -ENOMEM;
+
+       mcam->bmap_fcnt = mcam->bmap_entries;
+
+       /* Alloc memory for saving entry to RVU PFFUNC allocation mapping */
+       mcam->entry2pfvf_map = devm_kcalloc(rvu->dev, mcam->bmap_entries,
+                                           sizeof(u16), GFP_KERNEL);
+       if (!mcam->entry2pfvf_map)
+               return -ENOMEM;
+
+       /* Reserve 1/8th of MCAM entries at the bottom for low priority
+        * allocations and another 1/8th at the top for high priority
+        * allocations.
+        */
+       mcam->lprio_count = mcam->bmap_entries / 8;
+       if (mcam->lprio_count > BITS_PER_LONG)
+               mcam->lprio_count = round_down(mcam->lprio_count,
+                                              BITS_PER_LONG);
+       mcam->lprio_start = mcam->bmap_entries - mcam->lprio_count;
+       mcam->hprio_count = mcam->lprio_count;
+       mcam->hprio_end = mcam->hprio_count;
+
+       /* Allocate bitmap for managing MCAM counters and memory
+        * for saving counter to RVU PFFUNC allocation mapping.
+        */
+       err = rvu_alloc_bitmap(&mcam->counters);
+       if (err)
+               return err;
+
+       mcam->cntr2pfvf_map = devm_kcalloc(rvu->dev, mcam->counters.max,
+                                          sizeof(u16), GFP_KERNEL);
+       if (!mcam->cntr2pfvf_map)
+               goto free_mem;
+
+       /* Alloc memory for MCAM entry to counter mapping and for tracking
+        * counter's reference count.
+        */
+       mcam->entry2cntr_map = devm_kcalloc(rvu->dev, mcam->bmap_entries,
+                                           sizeof(u16), GFP_KERNEL);
+       if (!mcam->entry2cntr_map)
+               goto free_mem;
+
+       mcam->cntr_refcnt = devm_kcalloc(rvu->dev, mcam->counters.max,
+                                        sizeof(u16), GFP_KERNEL);
+       if (!mcam->cntr_refcnt)
+               goto free_mem;
+
+       mutex_init(&mcam->lock);
 
        return 0;
+
+free_mem:
+       kfree(mcam->counters.bmap);
+       return -ENOMEM;
 }
 
 int rvu_npc_init(struct rvu *rvu)
 {
        struct npc_pkind *pkind = &rvu->hw->pkind;
        u64 keyz = NPC_MCAM_KEY_X2;
-       int blkaddr, err;
+       int blkaddr, entry, bank, err;
+       u64 cfg, nibble_ena;
 
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0) {
@@ -749,6 +1111,14 @@ int rvu_npc_init(struct rvu *rvu)
                return -ENODEV;
        }
 
+       /* First disable all MCAM entries, to stop traffic towards NIXLFs */
+       cfg = rvu_read64(rvu, blkaddr, NPC_AF_CONST);
+       for (bank = 0; bank < ((cfg >> 44) & 0xF); bank++) {
+               for (entry = 0; entry < ((cfg >> 28) & 0xFFFF); entry++)
+                       rvu_write64(rvu, blkaddr,
+                                   NPC_AF_MCAMEX_BANKX_CFG(entry, bank), 0);
+       }
+
        /* Allocate resource bimap for pkind*/
        pkind->rsrc.max = (rvu_read64(rvu, blkaddr,
                                      NPC_AF_CONST1) >> 12) & 0xFF;
@@ -771,29 +1141,41 @@ int rvu_npc_init(struct rvu *rvu)
        rvu_write64(rvu, blkaddr, NPC_AF_PCK_DEF_OIP4,
                    (NPC_LID_LC << 8) | (NPC_LT_LC_IP << 4) | 0x0F);
 
+       /* Config Inner IPV4 NPC layer info */
+       rvu_write64(rvu, blkaddr, NPC_AF_PCK_DEF_IIP4,
+                   (NPC_LID_LF << 8) | (NPC_LT_LF_TU_IP << 4) | 0x0F);
+
        /* Enable below for Rx pkts.
         * - Outer IPv4 header checksum validation.
         * - Detect outer L2 broadcast address and set NPC_RESULT_S[L2M].
+        * - Inner IPv4 header checksum validation.
+        * - Set non zero checksum error code value
         */
        rvu_write64(rvu, blkaddr, NPC_AF_PCK_CFG,
                    rvu_read64(rvu, blkaddr, NPC_AF_PCK_CFG) |
-                   BIT_ULL(6) | BIT_ULL(2));
+                   BIT_ULL(32) | BIT_ULL(24) | BIT_ULL(6) |
+                   BIT_ULL(2) | BIT_ULL(1));
 
        /* Set RX and TX side MCAM search key size.
-        * Also enable parse key extract nibbles suchthat except
-        * layer E to H, rest of the key is included for MCAM search.
+        * LA..LD (ltype only) + Channel
         */
+       nibble_ena = 0x49247;
        rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_RX),
-                   ((keyz & 0x3) << 32) | ((1ULL << 20) - 1));
+                       ((keyz & 0x3) << 32) | nibble_ena);
+       /* Due to an errata (35786) in A0 pass silicon, parse nibble enable
+        * configuration has to be identical for both Rx and Tx interfaces.
+        */
+       if (!is_rvu_9xxx_A0(rvu))
+               nibble_ena = (1ULL << 19) - 1;
        rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_TX),
-                   ((keyz & 0x3) << 32) | ((1ULL << 20) - 1));
+                       ((keyz & 0x3) << 32) | nibble_ena);
 
        err = npc_mcam_rsrcs_init(rvu, blkaddr);
        if (err)
                return err;
 
-       /* Config packet data and flags extraction into PARSE result */
-       npc_config_ldata_extract(rvu, blkaddr);
+       /* Configure MKEX profile */
+       npc_load_mkex_profile(rvu, blkaddr);
 
        /* Set TX miss action to UCAST_DEFAULT i.e
         * transmit the packet on NIX LF SQ's default channel.
@@ -811,6 +1193,1020 @@ int rvu_npc_init(struct rvu *rvu)
 void rvu_npc_freemem(struct rvu *rvu)
 {
        struct npc_pkind *pkind = &rvu->hw->pkind;
+       struct npc_mcam *mcam = &rvu->hw->mcam;
 
        kfree(pkind->rsrc.bmap);
+       kfree(mcam->counters.bmap);
+       mutex_destroy(&mcam->lock);
+}
+
+static int npc_mcam_verify_entry(struct npc_mcam *mcam,
+                                u16 pcifunc, int entry)
+{
+       /* Verify if entry is valid and if it is indeed
+        * allocated to the requesting PFFUNC.
+        */
+       if (entry >= mcam->bmap_entries)
+               return NPC_MCAM_INVALID_REQ;
+
+       if (pcifunc != mcam->entry2pfvf_map[entry])
+               return NPC_MCAM_PERM_DENIED;
+
+       return 0;
+}
+
+static int npc_mcam_verify_counter(struct npc_mcam *mcam,
+                                  u16 pcifunc, int cntr)
+{
+       /* Verify if counter is valid and if it is indeed
+        * allocated to the requesting PFFUNC.
+        */
+       if (cntr >= mcam->counters.max)
+               return NPC_MCAM_INVALID_REQ;
+
+       if (pcifunc != mcam->cntr2pfvf_map[cntr])
+               return NPC_MCAM_PERM_DENIED;
+
+       return 0;
+}
+
+static void npc_map_mcam_entry_and_cntr(struct rvu *rvu, struct npc_mcam *mcam,
+                                       int blkaddr, u16 entry, u16 cntr)
+{
+       u16 index = entry & (mcam->banksize - 1);
+       u16 bank = npc_get_bank(mcam, entry);
+
+       /* Set mapping and increment counter's refcnt */
+       mcam->entry2cntr_map[entry] = cntr;
+       mcam->cntr_refcnt[cntr]++;
+       /* Enable stats */
+       rvu_write64(rvu, blkaddr,
+                   NPC_AF_MCAMEX_BANKX_STAT_ACT(index, bank),
+                   BIT_ULL(9) | cntr);
+}
+
+static void npc_unmap_mcam_entry_and_cntr(struct rvu *rvu,
+                                         struct npc_mcam *mcam,
+                                         int blkaddr, u16 entry, u16 cntr)
+{
+       u16 index = entry & (mcam->banksize - 1);
+       u16 bank = npc_get_bank(mcam, entry);
+
+       /* Remove mapping and reduce counter's refcnt */
+       mcam->entry2cntr_map[entry] = NPC_MCAM_INVALID_MAP;
+       mcam->cntr_refcnt[cntr]--;
+       /* Disable stats */
+       rvu_write64(rvu, blkaddr,
+                   NPC_AF_MCAMEX_BANKX_STAT_ACT(index, bank), 0x00);
+}
+
+/* Sets MCAM entry in bitmap as used. Update
+ * reverse bitmap too. Should be called with
+ * 'mcam->lock' held.
+ */
+static void npc_mcam_set_bit(struct npc_mcam *mcam, u16 index)
+{
+       u16 entry, rentry;
+
+       entry = index;
+       rentry = mcam->bmap_entries - index - 1;
+
+       __set_bit(entry, mcam->bmap);
+       __set_bit(rentry, mcam->bmap_reverse);
+       mcam->bmap_fcnt--;
+}
+
+/* Sets MCAM entry in bitmap as free. Update
+ * reverse bitmap too. Should be called with
+ * 'mcam->lock' held.
+ */
+static void npc_mcam_clear_bit(struct npc_mcam *mcam, u16 index)
+{
+       u16 entry, rentry;
+
+       entry = index;
+       rentry = mcam->bmap_entries - index - 1;
+
+       __clear_bit(entry, mcam->bmap);
+       __clear_bit(rentry, mcam->bmap_reverse);
+       mcam->bmap_fcnt++;
+}
+
+static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam,
+                                     int blkaddr, u16 pcifunc)
+{
+       u16 index, cntr;
+
+       /* Scan all MCAM entries and free the ones mapped to 'pcifunc' */
+       for (index = 0; index < mcam->bmap_entries; index++) {
+               if (mcam->entry2pfvf_map[index] == pcifunc) {
+                       mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP;
+                       /* Free the entry in bitmap */
+                       npc_mcam_clear_bit(mcam, index);
+                       /* Disable the entry */
+                       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
+
+                       /* Update entry2counter mapping */
+                       cntr = mcam->entry2cntr_map[index];
+                       if (cntr != NPC_MCAM_INVALID_MAP)
+                               npc_unmap_mcam_entry_and_cntr(rvu, mcam,
+                                                             blkaddr, index,
+                                                             cntr);
+               }
+       }
+}
+
+static void npc_mcam_free_all_counters(struct rvu *rvu, struct npc_mcam *mcam,
+                                      u16 pcifunc)
+{
+       u16 cntr;
+
+       /* Scan all MCAM counters and free the ones mapped to 'pcifunc' */
+       for (cntr = 0; cntr < mcam->counters.max; cntr++) {
+               if (mcam->cntr2pfvf_map[cntr] == pcifunc) {
+                       mcam->cntr2pfvf_map[cntr] = NPC_MCAM_INVALID_MAP;
+                       mcam->cntr_refcnt[cntr] = 0;
+                       rvu_free_rsrc(&mcam->counters, cntr);
+                       /* This API is expected to be called after freeing
+                        * MCAM entries, which inturn will remove
+                        * 'entry to counter' mapping.
+                        * No need to do it again.
+                        */
+               }
+       }
+}
+
+/* Find area of contiguous free entries of size 'nr'.
+ * If not found return max contiguous free entries available.
+ */
+static u16 npc_mcam_find_zero_area(unsigned long *map, u16 size, u16 start,
+                                  u16 nr, u16 *max_area)
+{
+       u16 max_area_start = 0;
+       u16 index, next, end;
+
+       *max_area = 0;
+
+again:
+       index = find_next_zero_bit(map, size, start);
+       if (index >= size)
+               return max_area_start;
+
+       end = ((index + nr) >= size) ? size : index + nr;
+       next = find_next_bit(map, end, index);
+       if (*max_area < (next - index)) {
+               *max_area = next - index;
+               max_area_start = index;
+       }
+
+       if (next < end) {
+               start = next + 1;
+               goto again;
+       }
+
+       return max_area_start;
+}
+
+/* Find number of free MCAM entries available
+ * within range i.e in between 'start' and 'end'.
+ */
+static u16 npc_mcam_get_free_count(unsigned long *map, u16 start, u16 end)
+{
+       u16 index, next;
+       u16 fcnt = 0;
+
+again:
+       if (start >= end)
+               return fcnt;
+
+       index = find_next_zero_bit(map, end, start);
+       if (index >= end)
+               return fcnt;
+
+       next = find_next_bit(map, end, index);
+       if (next <= end) {
+               fcnt += next - index;
+               start = next + 1;
+               goto again;
+       }
+
+       fcnt += end - index;
+       return fcnt;
+}
+
+static void
+npc_get_mcam_search_range_priority(struct npc_mcam *mcam,
+                                  struct npc_mcam_alloc_entry_req *req,
+                                  u16 *start, u16 *end, bool *reverse)
+{
+       u16 fcnt;
+
+       if (req->priority == NPC_MCAM_HIGHER_PRIO)
+               goto hprio;
+
+       /* For a low priority entry allocation
+        * - If reference entry is not in hprio zone then
+        *      search range: ref_entry to end.
+        * - If reference entry is in hprio zone and if
+        *   request can be accomodated in non-hprio zone then
+        *      search range: 'start of middle zone' to 'end'
+        * - else search in reverse, so that less number of hprio
+        *   zone entries are allocated.
+        */
+
+       *reverse = false;
+       *start = req->ref_entry + 1;
+       *end = mcam->bmap_entries;
+
+       if (req->ref_entry >= mcam->hprio_end)
+               return;
+
+       fcnt = npc_mcam_get_free_count(mcam->bmap,
+                                      mcam->hprio_end, mcam->bmap_entries);
+       if (fcnt > req->count)
+               *start = mcam->hprio_end;
+       else
+               *reverse = true;
+       return;
+
+hprio:
+       /* For a high priority entry allocation, search is always
+        * in reverse to preserve hprio zone entries.
+        * - If reference entry is not in lprio zone then
+        *      search range: 0 to ref_entry.
+        * - If reference entry is in lprio zone and if
+        *   request can be accomodated in middle zone then
+        *      search range: 'hprio_end' to 'lprio_start'
+        */
+
+       *reverse = true;
+       *start = 0;
+       *end = req->ref_entry;
+
+       if (req->ref_entry <= mcam->lprio_start)
+               return;
+
+       fcnt = npc_mcam_get_free_count(mcam->bmap,
+                                      mcam->hprio_end, mcam->lprio_start);
+       if (fcnt < req->count)
+               return;
+       *start = mcam->hprio_end;
+       *end = mcam->lprio_start;
+}
+
+static int npc_mcam_alloc_entries(struct npc_mcam *mcam, u16 pcifunc,
+                                 struct npc_mcam_alloc_entry_req *req,
+                                 struct npc_mcam_alloc_entry_rsp *rsp)
+{
+       u16 entry_list[NPC_MAX_NONCONTIG_ENTRIES];
+       u16 fcnt, hp_fcnt, lp_fcnt;
+       u16 start, end, index;
+       int entry, next_start;
+       bool reverse = false;
+       unsigned long *bmap;
+       u16 max_contig;
+
+       mutex_lock(&mcam->lock);
+
+       /* Check if there are any free entries */
+       if (!mcam->bmap_fcnt) {
+               mutex_unlock(&mcam->lock);
+               return NPC_MCAM_ALLOC_FAILED;
+       }
+
+       /* MCAM entries are divided into high priority, middle and
+        * low priority zones. Idea is to not allocate top and lower
+        * most entries as much as possible, this is to increase
+        * probability of honouring priority allocation requests.
+        *
+        * Two bitmaps are used for mcam entry management,
+        * mcam->bmap for forward search i.e '0 to mcam->bmap_entries'.
+        * mcam->bmap_reverse for reverse search i.e 'mcam->bmap_entries to 0'.
+        *
+        * Reverse bitmap is used to allocate entries
+        * - when a higher priority entry is requested
+        * - when available free entries are less.
+        * Lower priority ones out of avaialble free entries are always
+        * chosen when 'high vs low' question arises.
+        */
+
+       /* Get the search range for priority allocation request */
+       if (req->priority) {
+               npc_get_mcam_search_range_priority(mcam, req,
+                                                  &start, &end, &reverse);
+               goto alloc;
+       }
+
+       /* Find out the search range for non-priority allocation request
+        *
+        * Get MCAM free entry count in middle zone.
+        */
+       lp_fcnt = npc_mcam_get_free_count(mcam->bmap,
+                                         mcam->lprio_start,
+                                         mcam->bmap_entries);
+       hp_fcnt = npc_mcam_get_free_count(mcam->bmap, 0, mcam->hprio_end);
+       fcnt = mcam->bmap_fcnt - lp_fcnt - hp_fcnt;
+
+       /* Check if request can be accomodated in the middle zone */
+       if (fcnt > req->count) {
+               start = mcam->hprio_end;
+               end = mcam->lprio_start;
+       } else if ((fcnt + (hp_fcnt / 2) + (lp_fcnt / 2)) > req->count) {
+               /* Expand search zone from half of hprio zone to
+                * half of lprio zone.
+                */
+               start = mcam->hprio_end / 2;
+               end = mcam->bmap_entries - (mcam->lprio_count / 2);
+               reverse = true;
+       } else {
+               /* Not enough free entries, search all entries in reverse,
+                * so that low priority ones will get used up.
+                */
+               reverse = true;
+               start = 0;
+               end = mcam->bmap_entries;
+       }
+
+alloc:
+       if (reverse) {
+               bmap = mcam->bmap_reverse;
+               start = mcam->bmap_entries - start;
+               end = mcam->bmap_entries - end;
+               index = start;
+               start = end;
+               end = index;
+       } else {
+               bmap = mcam->bmap;
+       }
+
+       if (req->contig) {
+               /* Allocate requested number of contiguous entries, if
+                * unsuccessful find max contiguous entries available.
+                */
+               index = npc_mcam_find_zero_area(bmap, end, start,
+                                               req->count, &max_contig);
+               rsp->count = max_contig;
+               if (reverse)
+                       rsp->entry = mcam->bmap_entries - index - max_contig;
+               else
+                       rsp->entry = index;
+       } else {
+               /* Allocate requested number of non-contiguous entries,
+                * if unsuccessful allocate as many as possible.
+                */
+               rsp->count = 0;
+               next_start = start;
+               for (entry = 0; entry < req->count; entry++) {
+                       index = find_next_zero_bit(bmap, end, next_start);
+                       if (index >= end)
+                               break;
+
+                       next_start = start + (index - start) + 1;
+
+                       /* Save the entry's index */
+                       if (reverse)
+                               index = mcam->bmap_entries - index - 1;
+                       entry_list[entry] = index;
+                       rsp->count++;
+               }
+       }
+
+       /* If allocating requested no of entries is unsucessful,
+        * expand the search range to full bitmap length and retry.
+        */
+       if (!req->priority && (rsp->count < req->count) &&
+           ((end - start) != mcam->bmap_entries)) {
+               reverse = true;
+               start = 0;
+               end = mcam->bmap_entries;
+               goto alloc;
+       }
+
+       /* For priority entry allocation requests, if allocation is
+        * failed then expand search to max possible range and retry.
+        */
+       if (req->priority && rsp->count < req->count) {
+               if (req->priority == NPC_MCAM_LOWER_PRIO &&
+                   (start != (req->ref_entry + 1))) {
+                       start = req->ref_entry + 1;
+                       end = mcam->bmap_entries;
+                       reverse = false;
+                       goto alloc;
+               } else if ((req->priority == NPC_MCAM_HIGHER_PRIO) &&
+                          ((end - start) != req->ref_entry)) {
+                       start = 0;
+                       end = req->ref_entry;
+                       reverse = true;
+                       goto alloc;
+               }
+       }
+
+       /* Copy MCAM entry indices into mbox response entry_list.
+        * Requester always expects indices in ascending order, so
+        * so reverse the list if reverse bitmap is used for allocation.
+        */
+       if (!req->contig && rsp->count) {
+               index = 0;
+               for (entry = rsp->count - 1; entry >= 0; entry--) {
+                       if (reverse)
+                               rsp->entry_list[index++] = entry_list[entry];
+                       else
+                               rsp->entry_list[entry] = entry_list[entry];
+               }
+       }
+
+       /* Mark the allocated entries as used and set nixlf mapping */
+       for (entry = 0; entry < rsp->count; entry++) {
+               index = req->contig ?
+                       (rsp->entry + entry) : rsp->entry_list[entry];
+               npc_mcam_set_bit(mcam, index);
+               mcam->entry2pfvf_map[index] = pcifunc;
+               mcam->entry2cntr_map[index] = NPC_MCAM_INVALID_MAP;
+       }
+
+       /* Update available free count in mbox response */
+       rsp->free_count = mcam->bmap_fcnt;
+
+       mutex_unlock(&mcam->lock);
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_alloc_entry(struct rvu *rvu,
+                                         struct npc_mcam_alloc_entry_req *req,
+                                         struct npc_mcam_alloc_entry_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       rsp->entry = NPC_MCAM_ENTRY_INVALID;
+       rsp->free_count = 0;
+
+       /* Check if ref_entry is within range */
+       if (req->priority && req->ref_entry >= mcam->bmap_entries)
+               return NPC_MCAM_INVALID_REQ;
+
+       /* ref_entry can't be '0' if requested priority is high.
+        * Can't be last entry if requested priority is low.
+        */
+       if ((!req->ref_entry && req->priority == NPC_MCAM_HIGHER_PRIO) ||
+           ((req->ref_entry == (mcam->bmap_entries - 1)) &&
+            req->priority == NPC_MCAM_LOWER_PRIO))
+               return NPC_MCAM_INVALID_REQ;
+
+       /* Since list of allocated indices needs to be sent to requester,
+        * max number of non-contiguous entries per mbox msg is limited.
+        */
+       if (!req->contig && req->count > NPC_MAX_NONCONTIG_ENTRIES)
+               return NPC_MCAM_INVALID_REQ;
+
+       /* Alloc request from PFFUNC with no NIXLF attached should be denied */
+       if (!is_nixlf_attached(rvu, pcifunc))
+               return NPC_MCAM_ALLOC_DENIED;
+
+       return npc_mcam_alloc_entries(mcam, pcifunc, req, rsp);
+}
+
+int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu,
+                                        struct npc_mcam_free_entry_req *req,
+                                        struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr, rc = 0;
+       u16 cntr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       /* Free request from PFFUNC with no NIXLF attached, ignore */
+       if (!is_nixlf_attached(rvu, pcifunc))
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+
+       if (req->all)
+               goto free_all;
+
+       rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
+       if (rc)
+               goto exit;
+
+       mcam->entry2pfvf_map[req->entry] = 0;
+       npc_mcam_clear_bit(mcam, req->entry);
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, req->entry, false);
+
+       /* Update entry2counter mapping */
+       cntr = mcam->entry2cntr_map[req->entry];
+       if (cntr != NPC_MCAM_INVALID_MAP)
+               npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                             req->entry, cntr);
+
+       goto exit;
+
+free_all:
+       /* Free up all entries allocated to requesting PFFUNC */
+       npc_mcam_free_all_entries(rvu, mcam, blkaddr, pcifunc);
+exit:
+       mutex_unlock(&mcam->lock);
+       return rc;
+}
+
+int rvu_mbox_handler_npc_mcam_write_entry(struct rvu *rvu,
+                                         struct npc_mcam_write_entry_req *req,
+                                         struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
+       if (rc)
+               goto exit;
+
+       if (req->set_cntr &&
+           npc_mcam_verify_counter(mcam, pcifunc, req->cntr)) {
+               rc = NPC_MCAM_INVALID_REQ;
+               goto exit;
+       }
+
+       if (req->intf != NIX_INTF_RX && req->intf != NIX_INTF_TX) {
+               rc = NPC_MCAM_INVALID_REQ;
+               goto exit;
+       }
+
+       npc_config_mcam_entry(rvu, mcam, blkaddr, req->entry, req->intf,
+                             &req->entry_data, req->enable_entry);
+
+       if (req->set_cntr)
+               npc_map_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                           req->entry, req->cntr);
+
+       rc = 0;
+exit:
+       mutex_unlock(&mcam->lock);
+       return rc;
+}
+
+int rvu_mbox_handler_npc_mcam_ena_entry(struct rvu *rvu,
+                                       struct npc_mcam_ena_dis_entry_req *req,
+                                       struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
+       mutex_unlock(&mcam->lock);
+       if (rc)
+               return rc;
+
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, req->entry, true);
+
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_dis_entry(struct rvu *rvu,
+                                       struct npc_mcam_ena_dis_entry_req *req,
+                                       struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
+       mutex_unlock(&mcam->lock);
+       if (rc)
+               return rc;
+
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, req->entry, false);
+
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu,
+                                         struct npc_mcam_shift_entry_req *req,
+                                         struct npc_mcam_shift_entry_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       u16 old_entry, new_entry;
+       u16 index, cntr;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       if (req->shift_count > NPC_MCAM_MAX_SHIFTS)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       for (index = 0; index < req->shift_count; index++) {
+               old_entry = req->curr_entry[index];
+               new_entry = req->new_entry[index];
+
+               /* Check if both old and new entries are valid and
+                * does belong to this PFFUNC or not.
+                */
+               rc = npc_mcam_verify_entry(mcam, pcifunc, old_entry);
+               if (rc)
+                       break;
+
+               rc = npc_mcam_verify_entry(mcam, pcifunc, new_entry);
+               if (rc)
+                       break;
+
+               /* new_entry should not have a counter mapped */
+               if (mcam->entry2cntr_map[new_entry] != NPC_MCAM_INVALID_MAP) {
+                       rc = NPC_MCAM_PERM_DENIED;
+                       break;
+               }
+
+               /* Disable the new_entry */
+               npc_enable_mcam_entry(rvu, mcam, blkaddr, new_entry, false);
+
+               /* Copy rule from old entry to new entry */
+               npc_copy_mcam_entry(rvu, mcam, blkaddr, old_entry, new_entry);
+
+               /* Copy counter mapping, if any */
+               cntr = mcam->entry2cntr_map[old_entry];
+               if (cntr != NPC_MCAM_INVALID_MAP) {
+                       npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                                     old_entry, cntr);
+                       npc_map_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                                   new_entry, cntr);
+               }
+
+               /* Enable new_entry and disable old_entry */
+               npc_enable_mcam_entry(rvu, mcam, blkaddr, new_entry, true);
+               npc_enable_mcam_entry(rvu, mcam, blkaddr, old_entry, false);
+       }
+
+       /* If shift has failed then report the failed index */
+       if (index != req->shift_count) {
+               rc = NPC_MCAM_PERM_DENIED;
+               rsp->failed_entry_idx = index;
+       }
+
+       mutex_unlock(&mcam->lock);
+       return rc;
+}
+
+int rvu_mbox_handler_npc_mcam_alloc_counter(struct rvu *rvu,
+                       struct npc_mcam_alloc_counter_req *req,
+                       struct npc_mcam_alloc_counter_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       u16 max_contig, cntr;
+       int blkaddr, index;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       /* If the request is from a PFFUNC with no NIXLF attached, ignore */
+       if (!is_nixlf_attached(rvu, pcifunc))
+               return NPC_MCAM_INVALID_REQ;
+
+       /* Since list of allocated counter IDs needs to be sent to requester,
+        * max number of non-contiguous counters per mbox msg is limited.
+        */
+       if (!req->contig && req->count > NPC_MAX_NONCONTIG_COUNTERS)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+
+       /* Check if unused counters are available or not */
+       if (!rvu_rsrc_free_count(&mcam->counters)) {
+               mutex_unlock(&mcam->lock);
+               return NPC_MCAM_ALLOC_FAILED;
+       }
+
+       rsp->count = 0;
+
+       if (req->contig) {
+               /* Allocate requested number of contiguous counters, if
+                * unsuccessful find max contiguous entries available.
+                */
+               index = npc_mcam_find_zero_area(mcam->counters.bmap,
+                                               mcam->counters.max, 0,
+                                               req->count, &max_contig);
+               rsp->count = max_contig;
+               rsp->cntr = index;
+               for (cntr = index; cntr < (index + max_contig); cntr++) {
+                       __set_bit(cntr, mcam->counters.bmap);
+                       mcam->cntr2pfvf_map[cntr] = pcifunc;
+               }
+       } else {
+               /* Allocate requested number of non-contiguous counters,
+                * if unsuccessful allocate as many as possible.
+                */
+               for (cntr = 0; cntr < req->count; cntr++) {
+                       index = rvu_alloc_rsrc(&mcam->counters);
+                       if (index < 0)
+                               break;
+                       rsp->cntr_list[cntr] = index;
+                       rsp->count++;
+                       mcam->cntr2pfvf_map[index] = pcifunc;
+               }
+       }
+
+       mutex_unlock(&mcam->lock);
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_free_counter(struct rvu *rvu,
+               struct npc_mcam_oper_counter_req *req, struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 index, entry = 0;
+       int blkaddr, err;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       err = npc_mcam_verify_counter(mcam, req->hdr.pcifunc, req->cntr);
+       if (err) {
+               mutex_unlock(&mcam->lock);
+               return err;
+       }
+
+       /* Mark counter as free/unused */
+       mcam->cntr2pfvf_map[req->cntr] = NPC_MCAM_INVALID_MAP;
+       rvu_free_rsrc(&mcam->counters, req->cntr);
+
+       /* Disable all MCAM entry's stats which are using this counter */
+       while (entry < mcam->bmap_entries) {
+               if (!mcam->cntr_refcnt[req->cntr])
+                       break;
+
+               index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry);
+               if (index >= mcam->bmap_entries)
+                       break;
+               if (mcam->entry2cntr_map[index] != req->cntr)
+                       continue;
+
+               entry = index + 1;
+               npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                             index, req->cntr);
+       }
+
+       mutex_unlock(&mcam->lock);
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu,
+               struct npc_mcam_unmap_counter_req *req, struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 index, entry = 0;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       rc = npc_mcam_verify_counter(mcam, req->hdr.pcifunc, req->cntr);
+       if (rc)
+               goto exit;
+
+       /* Unmap the MCAM entry and counter */
+       if (!req->all) {
+               rc = npc_mcam_verify_entry(mcam, req->hdr.pcifunc, req->entry);
+               if (rc)
+                       goto exit;
+               npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                             req->entry, req->cntr);
+               goto exit;
+       }
+
+       /* Disable all MCAM entry's stats which are using this counter */
+       while (entry < mcam->bmap_entries) {
+               if (!mcam->cntr_refcnt[req->cntr])
+                       break;
+
+               index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry);
+               if (index >= mcam->bmap_entries)
+                       break;
+               if (mcam->entry2cntr_map[index] != req->cntr)
+                       continue;
+
+               entry = index + 1;
+               npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                             index, req->cntr);
+       }
+exit:
+       mutex_unlock(&mcam->lock);
+       return rc;
+}
+
+int rvu_mbox_handler_npc_mcam_clear_counter(struct rvu *rvu,
+               struct npc_mcam_oper_counter_req *req, struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       int blkaddr, err;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       err = npc_mcam_verify_counter(mcam, req->hdr.pcifunc, req->cntr);
+       mutex_unlock(&mcam->lock);
+       if (err)
+               return err;
+
+       rvu_write64(rvu, blkaddr, NPC_AF_MATCH_STATX(req->cntr), 0x00);
+
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_counter_stats(struct rvu *rvu,
+                       struct npc_mcam_oper_counter_req *req,
+                       struct npc_mcam_oper_counter_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       int blkaddr, err;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       err = npc_mcam_verify_counter(mcam, req->hdr.pcifunc, req->cntr);
+       mutex_unlock(&mcam->lock);
+       if (err)
+               return err;
+
+       rsp->stat = rvu_read64(rvu, blkaddr, NPC_AF_MATCH_STATX(req->cntr));
+       rsp->stat &= BIT_ULL(48) - 1;
+
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_alloc_and_write_entry(struct rvu *rvu,
+                         struct npc_mcam_alloc_and_write_entry_req *req,
+                         struct npc_mcam_alloc_and_write_entry_rsp *rsp)
+{
+       struct npc_mcam_alloc_counter_req cntr_req;
+       struct npc_mcam_alloc_counter_rsp cntr_rsp;
+       struct npc_mcam_alloc_entry_req entry_req;
+       struct npc_mcam_alloc_entry_rsp entry_rsp;
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 entry = NPC_MCAM_ENTRY_INVALID;
+       u16 cntr = NPC_MCAM_ENTRY_INVALID;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       if (req->intf != NIX_INTF_RX && req->intf != NIX_INTF_TX)
+               return NPC_MCAM_INVALID_REQ;
+
+       /* Try to allocate a MCAM entry */
+       entry_req.hdr.pcifunc = req->hdr.pcifunc;
+       entry_req.contig = true;
+       entry_req.priority = req->priority;
+       entry_req.ref_entry = req->ref_entry;
+       entry_req.count = 1;
+
+       rc = rvu_mbox_handler_npc_mcam_alloc_entry(rvu,
+                                                  &entry_req, &entry_rsp);
+       if (rc)
+               return rc;
+
+       if (!entry_rsp.count)
+               return NPC_MCAM_ALLOC_FAILED;
+
+       entry = entry_rsp.entry;
+
+       if (!req->alloc_cntr)
+               goto write_entry;
+
+       /* Now allocate counter */
+       cntr_req.hdr.pcifunc = req->hdr.pcifunc;
+       cntr_req.contig = true;
+       cntr_req.count = 1;
+
+       rc = rvu_mbox_handler_npc_mcam_alloc_counter(rvu, &cntr_req, &cntr_rsp);
+       if (rc) {
+               /* Free allocated MCAM entry */
+               mutex_lock(&mcam->lock);
+               mcam->entry2pfvf_map[entry] = 0;
+               npc_mcam_clear_bit(mcam, entry);
+               mutex_unlock(&mcam->lock);
+               return rc;
+       }
+
+       cntr = cntr_rsp.cntr;
+
+write_entry:
+       mutex_lock(&mcam->lock);
+       npc_config_mcam_entry(rvu, mcam, blkaddr, entry, req->intf,
+                             &req->entry_data, req->enable_entry);
+
+       if (req->alloc_cntr)
+               npc_map_mcam_entry_and_cntr(rvu, mcam, blkaddr, entry, cntr);
+       mutex_unlock(&mcam->lock);
+
+       rsp->entry = entry;
+       rsp->cntr = cntr;
+
+       return 0;
+}
+
+#define GET_KEX_CFG(intf) \
+       rvu_read64(rvu, BLKADDR_NPC, NPC_AF_INTFX_KEX_CFG(intf))
+
+#define GET_KEX_FLAGS(ld) \
+       rvu_read64(rvu, BLKADDR_NPC, NPC_AF_KEX_LDATAX_FLAGS_CFG(ld))
+
+#define GET_KEX_LD(intf, lid, lt, ld)  \
+       rvu_read64(rvu, BLKADDR_NPC,    \
+               NPC_AF_INTFX_LIDX_LTX_LDX_CFG(intf, lid, lt, ld))
+
+#define GET_KEX_LDFLAGS(intf, ld, fl)  \
+       rvu_read64(rvu, BLKADDR_NPC,    \
+               NPC_AF_INTFX_LDATAX_FLAGSX_CFG(intf, ld, fl))
+
+int rvu_mbox_handler_npc_get_kex_cfg(struct rvu *rvu, struct msg_req *req,
+                                    struct npc_get_kex_cfg_rsp *rsp)
+{
+       int lid, lt, ld, fl;
+
+       rsp->rx_keyx_cfg = GET_KEX_CFG(NIX_INTF_RX);
+       rsp->tx_keyx_cfg = GET_KEX_CFG(NIX_INTF_TX);
+       for (lid = 0; lid < NPC_MAX_LID; lid++) {
+               for (lt = 0; lt < NPC_MAX_LT; lt++) {
+                       for (ld = 0; ld < NPC_MAX_LD; ld++) {
+                               rsp->intf_lid_lt_ld[NIX_INTF_RX][lid][lt][ld] =
+                                       GET_KEX_LD(NIX_INTF_RX, lid, lt, ld);
+                               rsp->intf_lid_lt_ld[NIX_INTF_TX][lid][lt][ld] =
+                                       GET_KEX_LD(NIX_INTF_TX, lid, lt, ld);
+                       }
+               }
+       }
+       for (ld = 0; ld < NPC_MAX_LD; ld++)
+               rsp->kex_ld_flags[ld] = GET_KEX_FLAGS(ld);
+
+       for (ld = 0; ld < NPC_MAX_LD; ld++) {
+               for (fl = 0; fl < NPC_MAX_LFL; fl++) {
+                       rsp->intf_ld_flags[NIX_INTF_RX][ld][fl] =
+                                       GET_KEX_LDFLAGS(NIX_INTF_RX, ld, fl);
+                       rsp->intf_ld_flags[NIX_INTF_TX][ld][fl] =
+                                       GET_KEX_LDFLAGS(NIX_INTF_TX, ld, fl);
+               }
+       }
+       memcpy(rsp->mkex_pfl_name, rvu->mkex_pfl_name, MKEX_NAME_LEN);
+       return 0;
+}
+
+int rvu_npc_update_rxvlan(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       int blkaddr, index;
+       bool enable;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       if (!pfvf->rxvlan)
+               return 0;
+
+       index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf,
+                                        NIXLF_UCAST_ENTRY);
+       pfvf->entry.action = npc_get_mcam_action(rvu, mcam, blkaddr, index);
+       enable = is_mcam_entry_enabled(rvu, mcam, blkaddr, index);
+       npc_config_mcam_entry(rvu, mcam, blkaddr, pfvf->rxvlan_index,
+                             NIX_INTF_RX, &pfvf->entry, enable);
+
+       return 0;
 }
index 9c08c36..04fd1f1 100644 (file)
@@ -3732,19 +3732,7 @@ static int skge_debug_show(struct seq_file *seq, void *v)
 
        return 0;
 }
-
-static int skge_debug_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, skge_debug_show, inode->i_private);
-}
-
-static const struct file_operations skge_debug_fops = {
-       .owner          = THIS_MODULE,
-       .open           = skge_debug_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(skge_debug);
 
 /*
  * Use network device events to create/remove/rename
index c7cd008..f3a5fa8 100644 (file)
@@ -4621,19 +4621,7 @@ static int sky2_debug_show(struct seq_file *seq, void *v)
        napi_enable(&hw->napi);
        return 0;
 }
-
-static int sky2_debug_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, sky2_debug_show, inode->i_private);
-}
-
-static const struct file_operations sky2_debug_fops = {
-       .owner          = THIS_MODULE,
-       .open           = sky2_debug_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(sky2_debug);
 
 /*
  * Use network device events to create/remove/rename
index 36054e6..f200b8c 100644 (file)
@@ -5,7 +5,7 @@
 config MLX4_EN
        tristate "Mellanox Technologies 1/10/40Gbit Ethernet support"
        depends on MAY_USE_DEVLINK
-       depends on PCI
+       depends on PCI && NETDEVICES && ETHERNET && INET
        select MLX4_CORE
        imply PTP_1588_CLOCK
        ---help---
index deef5a9..9af34e0 100644 (file)
@@ -337,7 +337,7 @@ void mlx4_zone_allocator_destroy(struct mlx4_zone_allocator *zone_alloc)
 static u32 __mlx4_alloc_from_zone(struct mlx4_zone_entry *zone, int count,
                                  int align, u32 skip_mask, u32 *puid)
 {
-       u32 uid;
+       u32 uid = 0;
        u32 res;
        struct mlx4_zone_allocator *zone_alloc = zone->allocator;
        struct mlx4_zone_entry *curr_node;
index d8e9a32..db909b6 100644 (file)
@@ -144,9 +144,9 @@ void mlx4_cq_event(struct mlx4_dev *dev, u32 cqn, int event_type)
 }
 
 static int mlx4_SW2HW_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox,
-                        int cq_num)
+                        int cq_num, u8 opmod)
 {
-       return mlx4_cmd(dev, mailbox->dma, cq_num, 0,
+       return mlx4_cmd(dev, mailbox->dma, cq_num, opmod,
                        MLX4_CMD_SW2HW_CQ, MLX4_CMD_TIME_CLASS_A,
                        MLX4_CMD_WRAPPED);
 }
@@ -287,11 +287,61 @@ static void mlx4_cq_free_icm(struct mlx4_dev *dev, int cqn)
                __mlx4_cq_free_icm(dev, cqn);
 }
 
+static int mlx4_init_user_cqes(void *buf, int entries, int cqe_size)
+{
+       int entries_per_copy = PAGE_SIZE / cqe_size;
+       void *init_ents;
+       int err = 0;
+       int i;
+
+       init_ents = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!init_ents)
+               return -ENOMEM;
+
+       /* Populate a list of CQ entries to reduce the number of
+        * copy_to_user calls. 0xcc is the initialization value
+        * required by the FW.
+        */
+       memset(init_ents, 0xcc, PAGE_SIZE);
+
+       if (entries_per_copy < entries) {
+               for (i = 0; i < entries / entries_per_copy; i++) {
+                       err = copy_to_user(buf, init_ents, PAGE_SIZE);
+                       if (err)
+                               goto out;
+
+                       buf += PAGE_SIZE;
+               }
+       } else {
+               err = copy_to_user(buf, init_ents, entries * cqe_size);
+       }
+
+out:
+       kfree(init_ents);
+
+       return err;
+}
+
+static void mlx4_init_kernel_cqes(struct mlx4_buf *buf,
+                                 int entries,
+                                 int cqe_size)
+{
+       int i;
+
+       if (buf->nbufs == 1)
+               memset(buf->direct.buf, 0xcc, entries * cqe_size);
+       else
+               for (i = 0; i < buf->npages; i++)
+                       memset(buf->page_list[i].buf, 0xcc,
+                              1UL << buf->page_shift);
+}
+
 int mlx4_cq_alloc(struct mlx4_dev *dev, int nent,
                  struct mlx4_mtt *mtt, struct mlx4_uar *uar, u64 db_rec,
                  struct mlx4_cq *cq, unsigned vector, int collapsed,
-                 int timestamp_en)
+                 int timestamp_en, void *buf_addr, bool user_cq)
 {
+       bool sw_cq_init = dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_SW_CQ_INIT;
        struct mlx4_priv *priv = mlx4_priv(dev);
        struct mlx4_cq_table *cq_table = &priv->cq_table;
        struct mlx4_cmd_mailbox *mailbox;
@@ -336,7 +386,20 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent,
        cq_context->mtt_base_addr_l = cpu_to_be32(mtt_addr & 0xffffffff);
        cq_context->db_rec_addr     = cpu_to_be64(db_rec);
 
-       err = mlx4_SW2HW_CQ(dev, mailbox, cq->cqn);
+       if (sw_cq_init) {
+               if (user_cq) {
+                       err = mlx4_init_user_cqes(buf_addr, nent,
+                                                 dev->caps.cqe_size);
+                       if (err)
+                               sw_cq_init = false;
+               } else {
+                       mlx4_init_kernel_cqes(buf_addr, nent,
+                                             dev->caps.cqe_size);
+               }
+       }
+
+       err = mlx4_SW2HW_CQ(dev, mailbox, cq->cqn, sw_cq_init);
+
        mlx4_free_cmd_mailbox(dev, mailbox);
        if (err)
                goto err_radix;
index 1e487ac..74d4667 100644 (file)
@@ -54,11 +54,8 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv,
 
        cq = kzalloc_node(sizeof(*cq), GFP_KERNEL, node);
        if (!cq) {
-               cq = kzalloc(sizeof(*cq), GFP_KERNEL);
-               if (!cq) {
-                       en_err(priv, "Failed to allocate CQ structure\n");
-                       return -ENOMEM;
-               }
+               en_err(priv, "Failed to allocate CQ structure\n");
+               return -ENOMEM;
        }
 
        cq->size = entries;
@@ -143,7 +140,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
        cq->mcq.usage = MLX4_RES_USAGE_DRIVER;
        err = mlx4_cq_alloc(mdev->dev, cq->size, &cq->wqres.mtt,
                            &mdev->priv_uar, cq->wqres.db.dma, &cq->mcq,
-                           cq->vector, 0, timestamp_en);
+                           cq->vector, 0, timestamp_en, &cq->wqres.buf, false);
        if (err)
                goto free_eq;
 
index f11b450..d290f07 100644 (file)
@@ -1084,8 +1084,8 @@ static int mlx4_en_set_pauseparam(struct net_device *dev,
 
        tx_pause = !!(pause->tx_pause);
        rx_pause = !!(pause->rx_pause);
-       rx_ppp = priv->prof->rx_ppp && !(tx_pause || rx_pause);
-       tx_ppp = priv->prof->tx_ppp && !(tx_pause || rx_pause);
+       rx_ppp = (tx_pause || rx_pause) ? 0 : priv->prof->rx_ppp;
+       tx_ppp = (tx_pause || rx_pause) ? 0 : priv->prof->tx_ppp;
 
        err = mlx4_SET_PORT_general(mdev->dev, priv->port,
                                    priv->rx_skb_size + ETH_FCS_LEN,
index b744cd4..6b88881 100644 (file)
@@ -3493,8 +3493,8 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
                dev->gso_partial_features = NETIF_F_GSO_UDP_TUNNEL_CSUM;
        }
 
-       /* MTU range: 46 - hw-specific max */
-       dev->min_mtu = MLX4_EN_MIN_MTU;
+       /* MTU range: 68 - hw-specific max */
+       dev->min_mtu = ETH_MIN_MTU;
        dev->max_mtu = priv->max_mtu;
 
        mdev->pndev[port] = dev;
index fd09ba9..9a0881c 100644 (file)
@@ -271,11 +271,8 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
 
        ring = kzalloc_node(sizeof(*ring), GFP_KERNEL, node);
        if (!ring) {
-               ring = kzalloc(sizeof(*ring), GFP_KERNEL);
-               if (!ring) {
-                       en_err(priv, "Failed to allocate RX ring structure\n");
-                       return -ENOMEM;
-               }
+               en_err(priv, "Failed to allocate RX ring structure\n");
+               return -ENOMEM;
        }
 
        ring->prod = 0;
index 6f5153a..2cbd2bd 100644 (file)
@@ -57,11 +57,8 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
 
        ring = kzalloc_node(sizeof(*ring), GFP_KERNEL, node);
        if (!ring) {
-               ring = kzalloc(sizeof(*ring), GFP_KERNEL);
-               if (!ring) {
-                       en_err(priv, "Failed allocating TX ring\n");
-                       return -ENOMEM;
-               }
+               en_err(priv, "Failed allocating TX ring\n");
+               return -ENOMEM;
        }
 
        ring->size = size;
index babcfd9..7df728f 100644 (file)
@@ -166,6 +166,7 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags)
                [37] = "sl to vl mapping table change event support",
                [38] = "user MAC support",
                [39] = "Report driver version to FW support",
+               [40] = "SW CQ initialization support",
        };
        int i;
 
@@ -1098,6 +1099,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
                dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_FSM;
        if (field32 & (1 << 21))
                dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_80_VFS;
+       if (field32 & (1 << 23))
+               dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_SW_CQ_INIT;
 
        for (i = 1; i <= dev_cap->num_ports; i++) {
                err = mlx4_QUERY_PORT(dev, i, dev_cap->port_cap + i);
index 6a04603..bdb8dd1 100644 (file)
@@ -63,7 +63,7 @@ struct workqueue_struct *mlx4_wq;
 
 #ifdef CONFIG_MLX4_DEBUG
 
-int mlx4_debug_level = 0;
+int mlx4_debug_level; /* 0 by default */
 module_param_named(debug_level, mlx4_debug_level, int, 0644);
 MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0");
 
@@ -83,7 +83,7 @@ MODULE_PARM_DESC(msi_x, "0 - don't use MSI-X, 1 - use MSI-X, >1 - limit number o
 
 static uint8_t num_vfs[3] = {0, 0, 0};
 static int num_vfs_argc;
-module_param_array(num_vfs, byte , &num_vfs_argc, 0444);
+module_param_array(num_vfs, byte, &num_vfs_argc, 0444);
 MODULE_PARM_DESC(num_vfs, "enable #num_vfs functions if num_vfs > 0\n"
                          "num_vfs=port1,port2,port1+2");
 
@@ -313,7 +313,7 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
                for (i = 0; i < dev->caps.num_ports - 1; i++) {
                        if (port_type[i] != port_type[i + 1]) {
                                mlx4_err(dev, "Only same port types supported on this HCA, aborting\n");
-                               return -EINVAL;
+                               return -EOPNOTSUPP;
                        }
                }
        }
@@ -322,7 +322,7 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
                if (!(port_type[i] & dev->caps.supported_type[i+1])) {
                        mlx4_err(dev, "Requested port type for port %d is not supported on this HCA\n",
                                 i + 1);
-                       return -EINVAL;
+                       return -EOPNOTSUPP;
                }
        }
        return 0;
@@ -1188,8 +1188,7 @@ static int __set_port_type(struct mlx4_port_info *info,
                mlx4_err(mdev,
                         "Requested port type for port %d is not supported on this HCA\n",
                         info->port);
-               err = -EINVAL;
-               goto err_sup;
+               return -EOPNOTSUPP;
        }
 
        mlx4_stop_sense(mdev);
@@ -1211,7 +1210,7 @@ static int __set_port_type(struct mlx4_port_info *info,
                for (i = 1; i <= mdev->caps.num_ports; i++) {
                        if (mdev->caps.possible_type[i] == MLX4_PORT_TYPE_AUTO) {
                                mdev->caps.possible_type[i] = mdev->caps.port_type[i];
-                               err = -EINVAL;
+                               err = -EOPNOTSUPP;
                        }
                }
        }
@@ -1237,7 +1236,7 @@ static int __set_port_type(struct mlx4_port_info *info,
 out:
        mlx4_start_sense(mdev);
        mutex_unlock(&priv->port_mutex);
-err_sup:
+
        return err;
 }
 
@@ -3252,7 +3251,7 @@ disable_sriov:
 free_mem:
        dev->persist->num_vfs = 0;
        kfree(dev->dev_vfs);
-        dev->dev_vfs = NULL;
+       dev->dev_vfs = NULL;
        return dev_flags & ~MLX4_FLAG_MASTER;
 }
 
index ebcd277..23f1b5b 100644 (file)
@@ -540,8 +540,8 @@ struct slave_list {
 struct resource_allocator {
        spinlock_t alloc_lock; /* protect quotas */
        union {
-               int res_reserved;
-               int res_port_rsvd[MLX4_MAX_PORTS];
+               unsigned int res_reserved;
+               unsigned int res_port_rsvd[MLX4_MAX_PORTS];
        };
        union {
                int res_free;
index 485d856..8137454 100644 (file)
 #define MLX4_SELFTEST_LB_MIN_MTU (MLX4_LOOPBACK_TEST_PAYLOAD + NET_IP_ALIGN + \
                                  ETH_HLEN + PREAMBLE_LEN)
 
-#define MLX4_EN_MIN_MTU                46
 /* VLAN_HLEN is added twice,to support skb vlan tagged with multiple
  * headers. (For example: ETH_P_8021Q and ETH_P_8021AD).
  */
index 2e84f10..1a11bc0 100644 (file)
@@ -363,6 +363,7 @@ int mlx4_mr_hw_write_mpt(struct mlx4_dev *dev, struct mlx4_mr *mmr,
                        container_of((void *)mpt_entry, struct mlx4_cmd_mailbox,
                                     buf);
 
+               (*mpt_entry)->lkey = 0;
                err = mlx4_SW2HW_MPT(dev, mailbox, key);
        }
 
index d324a38..9de9aba 100644 (file)
@@ -12,17 +12,17 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o
 # mlx5 core basic
 #
 mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
-               health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.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 \
-               fs_counters.o rl.o lag.o dev.o wq.o lib/gid.o  \
-               diag/fs_tracepoint.o diag/fw_tracer.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
 
 #
 # Netdev basic
 #
 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_selftest.o en/port.o en/monitor_stats.o
 
 #
 # Netdev extra
@@ -30,7 +30,7 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \
 mlx5_core-$(CONFIG_MLX5_EN_ARFS)     += en_arfs.o
 mlx5_core-$(CONFIG_MLX5_EN_RXNFC)    += en_fs_ethtool.o
 mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o
-mlx5_core-$(CONFIG_MLX5_ESWITCH)     += en_rep.o en_tc.o
+mlx5_core-$(CONFIG_MLX5_ESWITCH)     += en_rep.o en_tc.o en/tc_tun.o
 
 #
 # Core extra
index a5a0823..d3125cd 100644 (file)
 #include <linux/random.h>
 #include <linux/io-mapping.h>
 #include <linux/mlx5/driver.h>
+#include <linux/mlx5/eq.h>
 #include <linux/debugfs.h>
 
 #include "mlx5_core.h"
+#include "lib/eq.h"
 
 enum {
        CMD_IF_REV = 5,
@@ -313,6 +315,7 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
        case MLX5_CMD_OP_FPGA_DESTROY_QP:
        case MLX5_CMD_OP_DESTROY_GENERAL_OBJECT:
        case MLX5_CMD_OP_DEALLOC_MEMIC:
+       case MLX5_CMD_OP_PAGE_FAULT_RESUME:
                return MLX5_CMD_STAT_OK;
 
        case MLX5_CMD_OP_QUERY_HCA_CAP:
@@ -326,7 +329,6 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
        case MLX5_CMD_OP_CREATE_MKEY:
        case MLX5_CMD_OP_QUERY_MKEY:
        case MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS:
-       case MLX5_CMD_OP_PAGE_FAULT_RESUME:
        case MLX5_CMD_OP_CREATE_EQ:
        case MLX5_CMD_OP_QUERY_EQ:
        case MLX5_CMD_OP_GEN_EQE:
@@ -371,6 +373,8 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
        case MLX5_CMD_OP_QUERY_VPORT_COUNTER:
        case MLX5_CMD_OP_ALLOC_Q_COUNTER:
        case MLX5_CMD_OP_QUERY_Q_COUNTER:
+       case MLX5_CMD_OP_SET_MONITOR_COUNTER:
+       case MLX5_CMD_OP_ARM_MONITOR_COUNTER:
        case MLX5_CMD_OP_SET_PP_RATE_LIMIT:
        case MLX5_CMD_OP_QUERY_RATE_LIMIT:
        case MLX5_CMD_OP_CREATE_SCHEDULING_ELEMENT:
@@ -520,6 +524,8 @@ const char *mlx5_command_str(int command)
        MLX5_COMMAND_STR_CASE(ALLOC_Q_COUNTER);
        MLX5_COMMAND_STR_CASE(DEALLOC_Q_COUNTER);
        MLX5_COMMAND_STR_CASE(QUERY_Q_COUNTER);
+       MLX5_COMMAND_STR_CASE(SET_MONITOR_COUNTER);
+       MLX5_COMMAND_STR_CASE(ARM_MONITOR_COUNTER);
        MLX5_COMMAND_STR_CASE(SET_PP_RATE_LIMIT);
        MLX5_COMMAND_STR_CASE(QUERY_RATE_LIMIT);
        MLX5_COMMAND_STR_CASE(CREATE_SCHEDULING_ELEMENT);
@@ -805,6 +811,8 @@ static u16 msg_to_opcode(struct mlx5_cmd_msg *in)
        return MLX5_GET(mbox_in, in->first.data, opcode);
 }
 
+static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced);
+
 static void cb_timeout_handler(struct work_struct *work)
 {
        struct delayed_work *dwork = container_of(work, struct delayed_work,
@@ -1412,14 +1420,32 @@ static void mlx5_cmd_change_mod(struct mlx5_core_dev *dev, int mode)
                up(&cmd->sem);
 }
 
+static int cmd_comp_notifier(struct notifier_block *nb,
+                            unsigned long type, void *data)
+{
+       struct mlx5_core_dev *dev;
+       struct mlx5_cmd *cmd;
+       struct mlx5_eqe *eqe;
+
+       cmd = mlx5_nb_cof(nb, struct mlx5_cmd, nb);
+       dev = container_of(cmd, struct mlx5_core_dev, cmd);
+       eqe = data;
+
+       mlx5_cmd_comp_handler(dev, be32_to_cpu(eqe->data.cmd.vector), false);
+
+       return NOTIFY_OK;
+}
 void mlx5_cmd_use_events(struct mlx5_core_dev *dev)
 {
+       MLX5_NB_INIT(&dev->cmd.nb, cmd_comp_notifier, CMD);
+       mlx5_eq_notifier_register(dev, &dev->cmd.nb);
        mlx5_cmd_change_mod(dev, CMD_MODE_EVENTS);
 }
 
 void mlx5_cmd_use_polling(struct mlx5_core_dev *dev)
 {
        mlx5_cmd_change_mod(dev, CMD_MODE_POLLING);
+       mlx5_eq_notifier_unregister(dev, &dev->cmd.nb);
 }
 
 static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg)
@@ -1435,7 +1461,7 @@ static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg)
        }
 }
 
-void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced)
+static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced)
 {
        struct mlx5_cmd *cmd = &dev->cmd;
        struct mlx5_cmd_work_ent *ent;
@@ -1533,7 +1559,29 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced)
                }
        }
 }
-EXPORT_SYMBOL(mlx5_cmd_comp_handler);
+
+void mlx5_cmd_trigger_completions(struct mlx5_core_dev *dev)
+{
+       unsigned long flags;
+       u64 vector;
+
+       /* wait for pending handlers to complete */
+       mlx5_eq_synchronize_cmd_irq(dev);
+       spin_lock_irqsave(&dev->cmd.alloc_lock, flags);
+       vector = ~dev->cmd.bitmask & ((1ul << (1 << dev->cmd.log_sz)) - 1);
+       if (!vector)
+               goto no_trig;
+
+       vector |= MLX5_TRIGGERED_CMD_COMP;
+       spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags);
+
+       mlx5_core_dbg(dev, "vector 0x%llx\n", vector);
+       mlx5_cmd_comp_handler(dev, vector, true);
+       return;
+
+no_trig:
+       spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags);
+}
 
 static int status_to_err(u8 status)
 {
index 4b85abb..713a17e 100644 (file)
@@ -38,6 +38,7 @@
 #include <rdma/ib_verbs.h>
 #include <linux/mlx5/cq.h>
 #include "mlx5_core.h"
+#include "lib/eq.h"
 
 #define TASKLET_MAX_TIME 2
 #define TASKLET_MAX_TIME_JIFFIES msecs_to_jiffies(TASKLET_MAX_TIME)
@@ -92,10 +93,10 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
        u32 dout[MLX5_ST_SZ_DW(destroy_cq_out)];
        u32 out[MLX5_ST_SZ_DW(create_cq_out)];
        u32 din[MLX5_ST_SZ_DW(destroy_cq_in)];
-       struct mlx5_eq *eq;
+       struct mlx5_eq_comp *eq;
        int err;
 
-       eq = mlx5_eqn2eq(dev, eqn);
+       eq = mlx5_eqn2comp_eq(dev, eqn);
        if (IS_ERR(eq))
                return PTR_ERR(eq);
 
@@ -119,12 +120,12 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
        INIT_LIST_HEAD(&cq->tasklet_ctx.list);
 
        /* Add to comp EQ CQ tree to recv comp events */
-       err = mlx5_eq_add_cq(eq, cq);
+       err = mlx5_eq_add_cq(&eq->core, cq);
        if (err)
                goto err_cmd;
 
        /* Add to async EQ CQ tree to recv async events */
-       err = mlx5_eq_add_cq(&dev->priv.eq_table.async_eq, cq);
+       err = mlx5_eq_add_cq(mlx5_get_async_eq(dev), cq);
        if (err)
                goto err_cq_add;
 
@@ -139,7 +140,7 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
        return 0;
 
 err_cq_add:
-       mlx5_eq_del_cq(eq, cq);
+       mlx5_eq_del_cq(&eq->core, cq);
 err_cmd:
        memset(din, 0, sizeof(din));
        memset(dout, 0, sizeof(dout));
@@ -157,11 +158,11 @@ int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq)
        u32 in[MLX5_ST_SZ_DW(destroy_cq_in)] = {0};
        int err;
 
-       err = mlx5_eq_del_cq(&dev->priv.eq_table.async_eq, cq);
+       err = mlx5_eq_del_cq(mlx5_get_async_eq(dev), cq);
        if (err)
                return err;
 
-       err = mlx5_eq_del_cq(cq->eq, cq);
+       err = mlx5_eq_del_cq(&cq->eq->core, cq);
        if (err)
                return err;
 
index 90fabd6..a11e22d 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/mlx5/cq.h>
 #include <linux/mlx5/driver.h>
 #include "mlx5_core.h"
+#include "lib/eq.h"
 
 enum {
        QP_PID,
@@ -349,6 +350,16 @@ out:
        return param;
 }
 
+static int mlx5_core_eq_query(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
+                             u32 *out, int outlen)
+{
+       u32 in[MLX5_ST_SZ_DW(query_eq_in)] = {};
+
+       MLX5_SET(query_eq_in, in, opcode, MLX5_CMD_OP_QUERY_EQ);
+       MLX5_SET(query_eq_in, in, eq_number, eq->eqn);
+       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
+}
+
 static u64 eq_read_field(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
                         int index)
 {
index 37ba7c7..ebc046f 100644 (file)
@@ -45,75 +45,11 @@ struct mlx5_device_context {
        unsigned long           state;
 };
 
-struct mlx5_delayed_event {
-       struct list_head        list;
-       struct mlx5_core_dev    *dev;
-       enum mlx5_dev_event     event;
-       unsigned long           param;
-};
-
 enum {
        MLX5_INTERFACE_ADDED,
        MLX5_INTERFACE_ATTACHED,
 };
 
-static void add_delayed_event(struct mlx5_priv *priv,
-                             struct mlx5_core_dev *dev,
-                             enum mlx5_dev_event event,
-                             unsigned long param)
-{
-       struct mlx5_delayed_event *delayed_event;
-
-       delayed_event = kzalloc(sizeof(*delayed_event), GFP_ATOMIC);
-       if (!delayed_event) {
-               mlx5_core_err(dev, "event %d is missed\n", event);
-               return;
-       }
-
-       mlx5_core_dbg(dev, "Accumulating event %d\n", event);
-       delayed_event->dev = dev;
-       delayed_event->event = event;
-       delayed_event->param = param;
-       list_add_tail(&delayed_event->list, &priv->waiting_events_list);
-}
-
-static void delayed_event_release(struct mlx5_device_context *dev_ctx,
-                                 struct mlx5_priv *priv)
-{
-       struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv);
-       struct mlx5_delayed_event *de;
-       struct mlx5_delayed_event *n;
-       struct list_head temp;
-
-       INIT_LIST_HEAD(&temp);
-
-       spin_lock_irq(&priv->ctx_lock);
-
-       priv->is_accum_events = false;
-       list_splice_init(&priv->waiting_events_list, &temp);
-       if (!dev_ctx->context)
-               goto out;
-       list_for_each_entry_safe(de, n, &temp, list)
-               dev_ctx->intf->event(dev, dev_ctx->context, de->event, de->param);
-
-out:
-       spin_unlock_irq(&priv->ctx_lock);
-
-       list_for_each_entry_safe(de, n, &temp, list) {
-               list_del(&de->list);
-               kfree(de);
-       }
-}
-
-/* accumulating events that can come after mlx5_ib calls to
- * ib_register_device, till adding that interface to the events list.
- */
-static void delayed_event_start(struct mlx5_priv *priv)
-{
-       spin_lock_irq(&priv->ctx_lock);
-       priv->is_accum_events = true;
-       spin_unlock_irq(&priv->ctx_lock);
-}
 
 void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
 {
@@ -129,8 +65,6 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
 
        dev_ctx->intf = intf;
 
-       delayed_event_start(priv);
-
        dev_ctx->context = intf->add(dev);
        if (dev_ctx->context) {
                set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state);
@@ -139,22 +73,9 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
 
                spin_lock_irq(&priv->ctx_lock);
                list_add_tail(&dev_ctx->list, &priv->ctx_list);
-
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-               if (dev_ctx->intf->pfault) {
-                       if (priv->pfault) {
-                               mlx5_core_err(dev, "multiple page fault handlers not supported");
-                       } else {
-                               priv->pfault_ctx = dev_ctx->context;
-                               priv->pfault = dev_ctx->intf->pfault;
-                       }
-               }
-#endif
                spin_unlock_irq(&priv->ctx_lock);
        }
 
-       delayed_event_release(dev_ctx, priv);
-
        if (!dev_ctx->context)
                kfree(dev_ctx);
 }
@@ -179,15 +100,6 @@ void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
        if (!dev_ctx)
                return;
 
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       spin_lock_irq(&priv->ctx_lock);
-       if (priv->pfault == dev_ctx->intf->pfault)
-               priv->pfault = NULL;
-       spin_unlock_irq(&priv->ctx_lock);
-
-       synchronize_srcu(&priv->pfault_srcu);
-#endif
-
        spin_lock_irq(&priv->ctx_lock);
        list_del(&dev_ctx->list);
        spin_unlock_irq(&priv->ctx_lock);
@@ -207,26 +119,20 @@ static void mlx5_attach_interface(struct mlx5_interface *intf, struct mlx5_priv
        if (!dev_ctx)
                return;
 
-       delayed_event_start(priv);
        if (intf->attach) {
                if (test_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state))
-                       goto out;
+                       return;
                if (intf->attach(dev, dev_ctx->context))
-                       goto out;
-
+                       return;
                set_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state);
        } else {
                if (test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state))
-                       goto out;
+                       return;
                dev_ctx->context = intf->add(dev);
                if (!dev_ctx->context)
-                       goto out;
-
+                       return;
                set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state);
        }
-
-out:
-       delayed_event_release(dev_ctx, priv);
 }
 
 void mlx5_attach_device(struct mlx5_core_dev *dev)
@@ -350,28 +256,6 @@ void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol)
        mutex_unlock(&mlx5_intf_mutex);
 }
 
-void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol)
-{
-       struct mlx5_priv *priv = &mdev->priv;
-       struct mlx5_device_context *dev_ctx;
-       unsigned long flags;
-       void *result = NULL;
-
-       spin_lock_irqsave(&priv->ctx_lock, flags);
-
-       list_for_each_entry(dev_ctx, &mdev->priv.ctx_list, list)
-               if ((dev_ctx->intf->protocol == protocol) &&
-                   dev_ctx->intf->get_dev) {
-                       result = dev_ctx->intf->get_dev(dev_ctx->context);
-                       break;
-               }
-
-       spin_unlock_irqrestore(&priv->ctx_lock, flags);
-
-       return result;
-}
-EXPORT_SYMBOL(mlx5_get_protocol_dev);
-
 /* Must be called with intf_mutex held */
 void mlx5_add_dev_by_protocol(struct mlx5_core_dev *dev, int protocol)
 {
@@ -422,44 +306,6 @@ struct mlx5_core_dev *mlx5_get_next_phys_dev(struct mlx5_core_dev *dev)
        return res;
 }
 
-void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
-                    unsigned long param)
-{
-       struct mlx5_priv *priv = &dev->priv;
-       struct mlx5_device_context *dev_ctx;
-       unsigned long flags;
-
-       spin_lock_irqsave(&priv->ctx_lock, flags);
-
-       if (priv->is_accum_events)
-               add_delayed_event(priv, dev, event, param);
-
-       /* After mlx5_detach_device, the dev_ctx->intf is still set and dev_ctx is
-        * still in priv->ctx_list. In this case, only notify the dev_ctx if its
-        * ADDED or ATTACHED bit are set.
-        */
-       list_for_each_entry(dev_ctx, &priv->ctx_list, list)
-               if (dev_ctx->intf->event &&
-                   (test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state) ||
-                    test_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state)))
-                       dev_ctx->intf->event(dev, dev_ctx->context, event, param);
-
-       spin_unlock_irqrestore(&priv->ctx_lock, flags);
-}
-
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-void mlx5_core_page_fault(struct mlx5_core_dev *dev,
-                         struct mlx5_pagefault *pfault)
-{
-       struct mlx5_priv *priv = &dev->priv;
-       int srcu_idx;
-
-       srcu_idx = srcu_read_lock(&priv->pfault_srcu);
-       if (priv->pfault)
-               priv->pfault(dev, priv->pfault_ctx, pfault);
-       srcu_read_unlock(&priv->pfault_srcu, srcu_idx);
-}
-#endif
 
 void mlx5_dev_list_lock(void)
 {
index 0f11fff..424457f 100644 (file)
@@ -161,10 +161,10 @@ static void print_misc_parameters_hdrs(struct trace_seq *p,
        PRINT_MASKED_VAL(name, p, format);                 \
 }
        DECLARE_MASK_VAL(u64, gre_key) = {
-               .m = MLX5_GET(fte_match_set_misc, mask, gre_key_h) << 8 |
-                    MLX5_GET(fte_match_set_misc, mask, gre_key_l),
-               .v = MLX5_GET(fte_match_set_misc, value, gre_key_h) << 8 |
-                    MLX5_GET(fte_match_set_misc, value, gre_key_l)};
+               .m = MLX5_GET(fte_match_set_misc, mask, gre_key.nvgre.hi) << 8 |
+                    MLX5_GET(fte_match_set_misc, mask, gre_key.nvgre.lo),
+               .v = MLX5_GET(fte_match_set_misc, value, gre_key.nvgre.hi) << 8 |
+                    MLX5_GET(fte_match_set_misc, value, gre_key.nvgre.lo)};
 
        PRINT_MASKED_VAL(gre_key, p, "%llu");
        PRINT_MASKED_VAL_MISC(u32, source_sqn, source_sqn, p, "%u");
index d4ec93b..6999f44 100644 (file)
@@ -30,6 +30,7 @@
  * SOFTWARE.
  */
 #define CREATE_TRACE_POINTS
+#include "lib/eq.h"
 #include "fw_tracer.h"
 #include "fw_tracer_tracepoint.h"
 
@@ -846,9 +847,9 @@ free_tracer:
        return ERR_PTR(err);
 }
 
-/* Create HW resources + start tracer
- * must be called before Async EQ is created
- */
+static int fw_tracer_event(struct notifier_block *nb, unsigned long action, void *data);
+
+/* Create HW resources + start tracer */
 int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer)
 {
        struct mlx5_core_dev *dev;
@@ -874,6 +875,9 @@ int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer)
                goto err_dealloc_pd;
        }
 
+       MLX5_NB_INIT(&tracer->nb, fw_tracer_event, DEVICE_TRACER);
+       mlx5_eq_notifier_register(dev, &tracer->nb);
+
        mlx5_fw_tracer_start(tracer);
 
        return 0;
@@ -883,9 +887,7 @@ err_dealloc_pd:
        return err;
 }
 
-/* Stop tracer + Cleanup HW resources
- * must be called after Async EQ is destroyed
- */
+/* Stop tracer + Cleanup HW resources */
 void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer)
 {
        if (IS_ERR_OR_NULL(tracer))
@@ -893,7 +895,7 @@ void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer)
 
        mlx5_core_dbg(tracer->dev, "FWTracer: Cleanup, is owner ? (%d)\n",
                      tracer->owner);
-
+       mlx5_eq_notifier_unregister(tracer->dev, &tracer->nb);
        cancel_work_sync(&tracer->ownership_change_work);
        cancel_work_sync(&tracer->handle_traces_work);
 
@@ -922,12 +924,11 @@ void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer)
        kfree(tracer);
 }
 
-void mlx5_fw_tracer_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe)
+static int fw_tracer_event(struct notifier_block *nb, unsigned long action, void *data)
 {
-       struct mlx5_fw_tracer *tracer = dev->tracer;
-
-       if (!tracer)
-               return;
+       struct mlx5_fw_tracer *tracer = mlx5_nb_cof(nb, struct mlx5_fw_tracer, nb);
+       struct mlx5_core_dev *dev = tracer->dev;
+       struct mlx5_eqe *eqe = data;
 
        switch (eqe->sub_type) {
        case MLX5_TRACER_SUBTYPE_OWNERSHIP_CHANGE:
@@ -942,6 +943,8 @@ void mlx5_fw_tracer_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe)
                mlx5_core_dbg(dev, "FWTracer: Event with unrecognized subtype: sub_type %d\n",
                              eqe->sub_type);
        }
+
+       return NOTIFY_OK;
 }
 
 EXPORT_TRACEPOINT_SYMBOL(mlx5_fw);
index 0347f2d..a8b8747 100644 (file)
@@ -55,6 +55,7 @@
 
 struct mlx5_fw_tracer {
        struct mlx5_core_dev *dev;
+       struct mlx5_nb        nb;
        bool owner;
        u8   trc_ver;
        struct workqueue_struct *work_queue;
@@ -170,6 +171,5 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev);
 int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer);
 void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer);
 void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer);
-void mlx5_fw_tracer_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe);
 
 #endif
index d7fbd5b..72cc204 100644 (file)
@@ -49,6 +49,7 @@
 #include <net/switchdev.h>
 #include <net/xdp.h>
 #include <linux/net_dim.h>
+#include <linux/bits.h>
 #include "wq.h"
 #include "mlx5_core.h"
 #include "en_stats.h"
@@ -147,9 +148,6 @@ struct page_pool;
               MLX5_UMR_MTT_ALIGNMENT))
 #define MLX5E_UMR_WQEBBS \
        (DIV_ROUND_UP(MLX5E_UMR_WQE_INLINE_SZ, MLX5_SEND_WQE_BB))
-#define MLX5E_ICOSQ_MAX_WQEBBS MLX5E_UMR_WQEBBS
-
-#define MLX5E_NUM_MAIN_GROUPS 9
 
 #define MLX5E_MSG_LEVEL                        NETIF_MSG_LINK
 
@@ -178,8 +176,7 @@ static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev)
 {
        return is_kdump_kernel() ?
                MLX5E_MIN_NUM_CHANNELS :
-               min_t(int, mdev->priv.eq_table.num_comp_vectors,
-                     MLX5E_MAX_NUM_CHANNELS);
+               min_t(int, mlx5_comp_vectors_count(mdev), MLX5E_MAX_NUM_CHANNELS);
 }
 
 /* Use this function to get max num channels after netdev was created */
@@ -214,22 +211,23 @@ struct mlx5e_umr_wqe {
 extern const char mlx5e_self_tests[][ETH_GSTRING_LEN];
 
 enum mlx5e_priv_flag {
-       MLX5E_PFLAG_RX_CQE_BASED_MODER = (1 << 0),
-       MLX5E_PFLAG_TX_CQE_BASED_MODER = (1 << 1),
-       MLX5E_PFLAG_RX_CQE_COMPRESS = (1 << 2),
-       MLX5E_PFLAG_RX_STRIDING_RQ = (1 << 3),
-       MLX5E_PFLAG_RX_NO_CSUM_COMPLETE = (1 << 4),
+       MLX5E_PFLAG_RX_CQE_BASED_MODER,
+       MLX5E_PFLAG_TX_CQE_BASED_MODER,
+       MLX5E_PFLAG_RX_CQE_COMPRESS,
+       MLX5E_PFLAG_RX_STRIDING_RQ,
+       MLX5E_PFLAG_RX_NO_CSUM_COMPLETE,
+       MLX5E_NUM_PFLAGS, /* Keep last */
 };
 
 #define MLX5E_SET_PFLAG(params, pflag, enable)                 \
        do {                                                    \
                if (enable)                                     \
-                       (params)->pflags |= (pflag);            \
+                       (params)->pflags |= BIT(pflag);         \
                else                                            \
-                       (params)->pflags &= ~(pflag);           \
+                       (params)->pflags &= ~(BIT(pflag));      \
        } while (0)
 
-#define MLX5E_GET_PFLAG(params, pflag) (!!((params)->pflags & (pflag)))
+#define MLX5E_GET_PFLAG(params, pflag) (!!((params)->pflags & (BIT(pflag))))
 
 #ifdef CONFIG_MLX5_CORE_EN_DCB
 #define MLX5E_MAX_BW_ALLOC 100 /* Max percentage of BW allocation */
@@ -247,9 +245,6 @@ struct mlx5e_params {
        bool lro_en;
        u32 lro_wqe_sz;
        u8  tx_min_inline_mode;
-       u8  rss_hfunc;
-       u8  toeplitz_hash_key[40];
-       u32 indirection_rqt[MLX5E_INDIR_RQT_SIZE];
        bool vlan_strip_disable;
        bool scatter_fcs_en;
        bool rx_dim_enabled;
@@ -569,6 +564,7 @@ struct mlx5e_rq {
 
        unsigned long          state;
        int                    ix;
+       unsigned int           hw_mtu;
 
        struct net_dim         dim; /* Dynamic Interrupt Moderation */
 
@@ -632,7 +628,6 @@ struct mlx5e_channel_stats {
 } ____cacheline_aligned_in_smp;
 
 enum {
-       MLX5E_STATE_ASYNC_EVENTS_ENABLED,
        MLX5E_STATE_OPENED,
        MLX5E_STATE_DESTROYING,
 };
@@ -653,6 +648,13 @@ enum {
        MLX5E_NIC_PRIO
 };
 
+struct mlx5e_rss_params {
+       u32     indirection_rqt[MLX5E_INDIR_RQT_SIZE];
+       u32     rx_hash_fields[MLX5E_NUM_INDIR_TIRS];
+       u8      toeplitz_hash_key[40];
+       u8      hfunc;
+};
+
 struct mlx5e_priv {
        /* priv data path fields - start */
        struct mlx5e_txqsq *txq2sq[MLX5E_MAX_NUM_CHANNELS * MLX5E_MAX_NUM_TC];
@@ -673,6 +675,7 @@ struct mlx5e_priv {
        struct mlx5e_tir           indir_tir[MLX5E_NUM_INDIR_TIRS];
        struct mlx5e_tir           inner_indir_tir[MLX5E_NUM_INDIR_TIRS];
        struct mlx5e_tir           direct_tir[MLX5E_MAX_NUM_CHANNELS];
+       struct mlx5e_rss_params    rss_params;
        u32                        tx_rates[MLX5E_MAX_NUM_SQS];
 
        struct mlx5e_flow_steering fs;
@@ -682,6 +685,8 @@ struct mlx5e_priv {
        struct work_struct         set_rx_mode_work;
        struct work_struct         tx_timeout_work;
        struct work_struct         update_stats_work;
+       struct work_struct         monitor_counters_work;
+       struct mlx5_nb             monitor_counters_nb;
 
        struct mlx5_core_dev      *mdev;
        struct net_device         *netdev;
@@ -691,6 +696,8 @@ struct mlx5e_priv {
        struct hwtstamp_config     tstamp;
        u16                        q_counter;
        u16                        drop_rq_q_counter;
+       struct notifier_block      events_nb;
+
 #ifdef CONFIG_MLX5_CORE_EN_DCB
        struct mlx5e_dcbx          dcbx;
 #endif
@@ -768,6 +775,7 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe,
                             struct mlx5e_wqe_frag_info *wi, u32 cqe_bcnt);
 
 void mlx5e_update_stats(struct mlx5e_priv *priv);
+void mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats);
 
 void mlx5e_init_l2_addr(struct mlx5e_priv *priv);
 int mlx5e_self_test_num(struct mlx5e_priv *priv);
@@ -798,9 +806,11 @@ struct mlx5e_redirect_rqt_param {
 
 int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz,
                       struct mlx5e_redirect_rqt_param rrp);
-void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params,
-                                   enum mlx5e_traffic_types tt,
+void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_rss_params *rss_params,
+                                   const struct mlx5e_tirc_config *ttconfig,
                                    void *tirc, bool inner);
+void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen);
+struct mlx5e_tirc_config mlx5e_tirc_get_default_config(enum mlx5e_traffic_types tt);
 
 int mlx5e_open_locked(struct net_device *netdev);
 int mlx5e_close_locked(struct net_device *netdev);
@@ -930,14 +940,16 @@ int mlx5e_create_tis(struct mlx5_core_dev *mdev, int tc,
 void mlx5e_destroy_tis(struct mlx5_core_dev *mdev, u32 tisn);
 
 int mlx5e_create_tises(struct mlx5e_priv *priv);
-void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv);
+void mlx5e_update_carrier(struct mlx5e_priv *priv);
 int mlx5e_close(struct net_device *netdev);
 int mlx5e_open(struct net_device *netdev);
+void mlx5e_update_ndo_stats(struct mlx5e_priv *priv);
 
 void mlx5e_queue_update_stats(struct mlx5e_priv *priv);
 int mlx5e_bits_invert(unsigned long a, int size);
 
 typedef int (*change_hw_mtu_cb)(struct mlx5e_priv *priv);
+int mlx5e_set_dev_port_mtu(struct mlx5e_priv *priv);
 int mlx5e_change_mtu(struct net_device *netdev, int new_mtu,
                     change_hw_mtu_cb set_mtu_cb);
 
@@ -961,12 +973,20 @@ int mlx5e_ethtool_get_coalesce(struct mlx5e_priv *priv,
                               struct ethtool_coalesce *coal);
 int mlx5e_ethtool_set_coalesce(struct mlx5e_priv *priv,
                               struct ethtool_coalesce *coal);
+int mlx5e_ethtool_get_link_ksettings(struct mlx5e_priv *priv,
+                                    struct ethtool_link_ksettings *link_ksettings);
+int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv,
+                                    const struct ethtool_link_ksettings *link_ksettings);
 u32 mlx5e_ethtool_get_rxfh_key_size(struct mlx5e_priv *priv);
 u32 mlx5e_ethtool_get_rxfh_indir_size(struct mlx5e_priv *priv);
 int mlx5e_ethtool_get_ts_info(struct mlx5e_priv *priv,
                              struct ethtool_ts_info *info);
 int mlx5e_ethtool_flash_device(struct mlx5e_priv *priv,
                               struct ethtool_flash *flash);
+void mlx5e_ethtool_get_pauseparam(struct mlx5e_priv *priv,
+                                 struct ethtool_pauseparam *pauseparam);
+int mlx5e_ethtool_set_pauseparam(struct mlx5e_priv *priv,
+                                struct ethtool_pauseparam *pauseparam);
 
 /* mlx5e generic netdev management API */
 int mlx5e_netdev_init(struct net_device *netdev,
@@ -982,12 +1002,26 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv);
 void mlx5e_detach_netdev(struct mlx5e_priv *priv);
 void mlx5e_destroy_netdev(struct mlx5e_priv *priv);
 void mlx5e_build_nic_params(struct mlx5_core_dev *mdev,
+                           struct mlx5e_rss_params *rss_params,
                            struct mlx5e_params *params,
                            u16 max_channels, u16 mtu);
 void mlx5e_build_rq_params(struct mlx5_core_dev *mdev,
                           struct mlx5e_params *params);
-void mlx5e_build_rss_params(struct mlx5e_params *params);
+void mlx5e_build_rss_params(struct mlx5e_rss_params *rss_params,
+                           u16 num_channels);
 u8 mlx5e_params_calculate_tx_min_inline(struct mlx5_core_dev *mdev);
 void mlx5e_rx_dim_work(struct work_struct *work);
 void mlx5e_tx_dim_work(struct work_struct *work);
+
+void mlx5e_add_vxlan_port(struct net_device *netdev, struct udp_tunnel_info *ti);
+void mlx5e_del_vxlan_port(struct net_device *netdev, struct udp_tunnel_info *ti);
+netdev_features_t mlx5e_features_check(struct sk_buff *skb,
+                                      struct net_device *netdev,
+                                      netdev_features_t features);
+#ifdef CONFIG_MLX5_ESWITCH
+int mlx5e_set_vf_mac(struct net_device *dev, int vf, u8 *mac);
+int mlx5e_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, int max_tx_rate);
+int mlx5e_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi);
+int mlx5e_get_vf_stats(struct net_device *dev, int vf, struct ifla_vf_stats *vf_stats);
+#endif
 #endif /* __MLX5_EN_H__ */
index 1431232..be5961f 100644 (file)
@@ -73,6 +73,22 @@ enum mlx5e_traffic_types {
        MLX5E_NUM_INDIR_TIRS = MLX5E_TT_ANY,
 };
 
+struct mlx5e_tirc_config {
+       u8 l3_prot_type;
+       u8 l4_prot_type;
+       u32 rx_hash_fields;
+};
+
+#define MLX5_HASH_IP           (MLX5_HASH_FIELD_SEL_SRC_IP   |\
+                                MLX5_HASH_FIELD_SEL_DST_IP)
+#define MLX5_HASH_IP_L4PORTS   (MLX5_HASH_FIELD_SEL_SRC_IP   |\
+                                MLX5_HASH_FIELD_SEL_DST_IP   |\
+                                MLX5_HASH_FIELD_SEL_L4_SPORT |\
+                                MLX5_HASH_FIELD_SEL_L4_DPORT)
+#define MLX5_HASH_IP_IPSEC_SPI (MLX5_HASH_FIELD_SEL_SRC_IP   |\
+                                MLX5_HASH_FIELD_SEL_DST_IP   |\
+                                MLX5_HASH_FIELD_SEL_IPSEC_SPI)
+
 enum mlx5e_tunnel_types {
        MLX5E_TT_IPV4_GRE,
        MLX5E_TT_IPV6_GRE,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.c
new file mode 100644 (file)
index 0000000..2ce4208
--- /dev/null
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Mellanox Technologies. */
+
+#include "en.h"
+#include "monitor_stats.h"
+#include "lib/eq.h"
+
+/* Driver will set the following watch counters list:
+ * Ppcnt.802_3:
+ * a_in_range_length_errors      Type: 0x0, Counter:  0x0, group_id = N/A
+ * a_out_of_range_length_field   Type: 0x0, Counter:  0x1, group_id = N/A
+ * a_frame_too_long_errors       Type: 0x0, Counter:  0x2, group_id = N/A
+ * a_frame_check_sequence_errors Type: 0x0, Counter:  0x3, group_id = N/A
+ * a_alignment_errors            Type: 0x0, Counter:  0x4, group_id = N/A
+ * if_out_discards               Type: 0x0, Counter:  0x5, group_id = N/A
+ * Q_Counters:
+ * Q[index].rx_out_of_buffer   Type: 0x1, Counter:  0x4, group_id = counter_ix
+ */
+
+#define NUM_REQ_PPCNT_COUNTER_S1 MLX5_CMD_SET_MONITOR_NUM_PPCNT_COUNTER_SET1
+#define NUM_REQ_Q_COUNTERS_S1    MLX5_CMD_SET_MONITOR_NUM_Q_COUNTERS_SET1
+
+int mlx5e_monitor_counter_supported(struct mlx5e_priv *priv)
+{
+       struct mlx5_core_dev *mdev = priv->mdev;
+
+       if (!MLX5_CAP_GEN(mdev, max_num_of_monitor_counters))
+               return false;
+       if (MLX5_CAP_PCAM_REG(mdev, ppcnt) &&
+           MLX5_CAP_GEN(mdev, num_ppcnt_monitor_counters) <
+           NUM_REQ_PPCNT_COUNTER_S1)
+               return false;
+       if (MLX5_CAP_GEN(mdev, num_q_monitor_counters) <
+           NUM_REQ_Q_COUNTERS_S1)
+               return false;
+       return true;
+}
+
+void mlx5e_monitor_counter_arm(struct mlx5e_priv *priv)
+{
+       u32  in[MLX5_ST_SZ_DW(arm_monitor_counter_in)]  = {};
+       u32 out[MLX5_ST_SZ_DW(arm_monitor_counter_out)] = {};
+
+       MLX5_SET(arm_monitor_counter_in, in, opcode,
+                MLX5_CMD_OP_ARM_MONITOR_COUNTER);
+       mlx5_cmd_exec(priv->mdev, in, sizeof(in), out, sizeof(out));
+}
+
+static void mlx5e_monitor_counters_work(struct work_struct *work)
+{
+       struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
+                                              monitor_counters_work);
+
+       mutex_lock(&priv->state_lock);
+       mlx5e_update_ndo_stats(priv);
+       mutex_unlock(&priv->state_lock);
+       mlx5e_monitor_counter_arm(priv);
+}
+
+static int mlx5e_monitor_event_handler(struct notifier_block *nb,
+                                      unsigned long event, void *eqe)
+{
+       struct mlx5e_priv *priv = mlx5_nb_cof(nb, struct mlx5e_priv,
+                                             monitor_counters_nb);
+       queue_work(priv->wq, &priv->monitor_counters_work);
+       return NOTIFY_OK;
+}
+
+void mlx5e_monitor_counter_start(struct mlx5e_priv *priv)
+{
+       MLX5_NB_INIT(&priv->monitor_counters_nb, mlx5e_monitor_event_handler,
+                    MONITOR_COUNTER);
+       mlx5_eq_notifier_register(priv->mdev, &priv->monitor_counters_nb);
+}
+
+static void mlx5e_monitor_counter_stop(struct mlx5e_priv *priv)
+{
+       mlx5_eq_notifier_unregister(priv->mdev, &priv->monitor_counters_nb);
+       cancel_work_sync(&priv->monitor_counters_work);
+}
+
+static int fill_monitor_counter_ppcnt_set1(int cnt, u32 *in)
+{
+       enum mlx5_monitor_counter_ppcnt ppcnt_cnt;
+
+       for (ppcnt_cnt = 0;
+            ppcnt_cnt < NUM_REQ_PPCNT_COUNTER_S1;
+            ppcnt_cnt++, cnt++) {
+               MLX5_SET(set_monitor_counter_in, in,
+                        monitor_counter[cnt].type,
+                        MLX5_QUERY_MONITOR_CNT_TYPE_PPCNT);
+               MLX5_SET(set_monitor_counter_in, in,
+                        monitor_counter[cnt].counter,
+                        ppcnt_cnt);
+       }
+       return ppcnt_cnt;
+}
+
+static int fill_monitor_counter_q_counter_set1(int cnt, int q_counter, u32 *in)
+{
+       MLX5_SET(set_monitor_counter_in, in,
+                monitor_counter[cnt].type,
+                MLX5_QUERY_MONITOR_CNT_TYPE_Q_COUNTER);
+       MLX5_SET(set_monitor_counter_in, in,
+                monitor_counter[cnt].counter,
+                MLX5_QUERY_MONITOR_Q_COUNTER_RX_OUT_OF_BUFFER);
+       MLX5_SET(set_monitor_counter_in, in,
+                monitor_counter[cnt].counter_group_id,
+                q_counter);
+       return 1;
+}
+
+/* check if mlx5e_monitor_counter_supported before calling this function*/
+static void mlx5e_set_monitor_counter(struct mlx5e_priv *priv)
+{
+       struct mlx5_core_dev *mdev = priv->mdev;
+       int max_num_of_counters = MLX5_CAP_GEN(mdev, max_num_of_monitor_counters);
+       int num_q_counters      = MLX5_CAP_GEN(mdev, num_q_monitor_counters);
+       int num_ppcnt_counters  = !MLX5_CAP_PCAM_REG(mdev, ppcnt) ? 0 :
+                                 MLX5_CAP_GEN(mdev, num_ppcnt_monitor_counters);
+       u32  in[MLX5_ST_SZ_DW(set_monitor_counter_in)]  = {};
+       u32 out[MLX5_ST_SZ_DW(set_monitor_counter_out)] = {};
+       int q_counter = priv->q_counter;
+       int cnt = 0;
+
+       if (num_ppcnt_counters  >=  NUM_REQ_PPCNT_COUNTER_S1 &&
+           max_num_of_counters >= (NUM_REQ_PPCNT_COUNTER_S1 + cnt))
+               cnt += fill_monitor_counter_ppcnt_set1(cnt, in);
+
+       if (num_q_counters      >=  NUM_REQ_Q_COUNTERS_S1 &&
+           max_num_of_counters >= (NUM_REQ_Q_COUNTERS_S1 + cnt) &&
+           q_counter)
+               cnt += fill_monitor_counter_q_counter_set1(cnt, q_counter, in);
+
+       MLX5_SET(set_monitor_counter_in, in, num_of_counters, cnt);
+       MLX5_SET(set_monitor_counter_in, in, opcode,
+                MLX5_CMD_OP_SET_MONITOR_COUNTER);
+
+       mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+}
+
+/* check if mlx5e_monitor_counter_supported before calling this function*/
+void mlx5e_monitor_counter_init(struct mlx5e_priv *priv)
+{
+       INIT_WORK(&priv->monitor_counters_work, mlx5e_monitor_counters_work);
+       mlx5e_monitor_counter_start(priv);
+       mlx5e_set_monitor_counter(priv);
+       mlx5e_monitor_counter_arm(priv);
+       queue_work(priv->wq, &priv->update_stats_work);
+}
+
+static void mlx5e_monitor_counter_disable(struct mlx5e_priv *priv)
+{
+       u32  in[MLX5_ST_SZ_DW(set_monitor_counter_in)]  = {};
+       u32 out[MLX5_ST_SZ_DW(set_monitor_counter_out)] = {};
+
+       MLX5_SET(set_monitor_counter_in, in, num_of_counters, 0);
+       MLX5_SET(set_monitor_counter_in, in, opcode,
+                MLX5_CMD_OP_SET_MONITOR_COUNTER);
+
+       mlx5_cmd_exec(priv->mdev, in, sizeof(in), out, sizeof(out));
+}
+
+/* check if mlx5e_monitor_counter_supported before calling this function*/
+void mlx5e_monitor_counter_cleanup(struct mlx5e_priv *priv)
+{
+       mlx5e_monitor_counter_disable(priv);
+       mlx5e_monitor_counter_stop(priv);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.h
new file mode 100644 (file)
index 0000000..e1ac4b3
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2018 Mellanox Technologies. */
+
+#ifndef __MLX5_MONITOR_H__
+#define __MLX5_MONITOR_H__
+
+int  mlx5e_monitor_counter_supported(struct mlx5e_priv *priv);
+void mlx5e_monitor_counter_init(struct mlx5e_priv *priv);
+void mlx5e_monitor_counter_cleanup(struct mlx5e_priv *priv);
+void mlx5e_monitor_counter_arm(struct mlx5e_priv *priv);
+
+#endif /* __MLX5_MONITOR_H__ */
index 023dc4b..4a37713 100644 (file)
@@ -88,10 +88,8 @@ int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
 
        eth_proto_oper = MLX5_GET(ptys_reg, out, eth_proto_oper);
        *speed = mlx5e_port_ptys2speed(eth_proto_oper);
-       if (!(*speed)) {
-               mlx5_core_warn(mdev, "cannot get port speed\n");
+       if (!(*speed))
                err = -EINVAL;
-       }
 
        return err;
 }
@@ -258,7 +256,7 @@ static int mlx5e_fec_admin_field(u32 *pplm,
        case 40000:
                if (!write)
                        *fec_policy = MLX5_GET(pplm_reg, pplm,
-                                              fec_override_cap_10g_40g);
+                                              fec_override_admin_10g_40g);
                else
                        MLX5_SET(pplm_reg, pplm,
                                 fec_override_admin_10g_40g, *fec_policy);
@@ -310,7 +308,7 @@ static int mlx5e_get_fec_cap_field(u32 *pplm,
        case 10000:
        case 40000:
                *fec_cap = MLX5_GET(pplm_reg, pplm,
-                                   fec_override_admin_10g_40g);
+                                   fec_override_cap_10g_40g);
                break;
        case 25000:
                *fec_cap = MLX5_GET(pplm_reg, pplm,
@@ -394,12 +392,12 @@ int mlx5e_get_fec_mode(struct mlx5_core_dev *dev, u32 *fec_mode_active,
 
 int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u8 fec_policy)
 {
+       u8 fec_policy_nofec = BIT(MLX5E_FEC_NOFEC);
        bool fec_mode_not_supp_in_speed = false;
-       u8 no_fec_policy = BIT(MLX5E_FEC_NOFEC);
        u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
        u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
        int sz = MLX5_ST_SZ_BYTES(pplm_reg);
-       u32 current_fec_speed;
+       u8 fec_policy_auto = 0;
        u8 fec_caps = 0;
        int err;
        int i;
@@ -415,23 +413,19 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u8 fec_policy)
        if (err)
                return err;
 
-       err = mlx5e_port_linkspeed(dev, &current_fec_speed);
-       if (err)
-               return err;
+       MLX5_SET(pplm_reg, out, local_port, 1);
 
-       memset(in, 0, sz);
-       MLX5_SET(pplm_reg, in, local_port, 1);
-       for (i = 0; i < MLX5E_FEC_SUPPORTED_SPEEDS && !!fec_policy; i++) {
+       for (i = 0; i < MLX5E_FEC_SUPPORTED_SPEEDS; i++) {
                mlx5e_get_fec_cap_field(out, &fec_caps, fec_supported_speeds[i]);
-               /* policy supported for link speed */
-               if (!!(fec_caps & fec_policy)) {
-                       mlx5e_fec_admin_field(in, &fec_policy, 1,
+               /* policy supported for link speed, or policy is auto */
+               if (fec_caps & fec_policy || fec_policy == fec_policy_auto) {
+                       mlx5e_fec_admin_field(out, &fec_policy, 1,
                                              fec_supported_speeds[i]);
                } else {
-                       if (fec_supported_speeds[i] == current_fec_speed)
-                               return -EOPNOTSUPP;
-                       mlx5e_fec_admin_field(in, &no_fec_policy, 1,
-                                             fec_supported_speeds[i]);
+                       /* turn off FEC if supported. Else, leave it the same */
+                       if (fec_caps & fec_policy_nofec)
+                               mlx5e_fec_admin_field(out, &fec_policy_nofec, 1,
+                                                     fec_supported_speeds[i]);
                        fec_mode_not_supp_in_speed = true;
                }
        }
@@ -441,5 +435,5 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u8 fec_policy)
                              "FEC policy 0x%x is not supported for some speeds",
                              fec_policy);
 
-       return mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 1);
+       return mlx5_core_access_reg(dev, out, sz, out, sz, MLX5_REG_PPLM, 0, 1);
 }
index c047da8..eac245a 100644 (file)
@@ -130,8 +130,10 @@ static u32 calculate_xoff(struct mlx5e_priv *priv, unsigned int mtu)
        int err;
 
        err = mlx5e_port_linkspeed(priv->mdev, &speed);
-       if (err)
+       if (err) {
+               mlx5_core_warn(priv->mdev, "cannot get port speed\n");
                return 0;
+       }
 
        xoff = (301 + 216 * priv->dcbx.cable_len / 100) * speed / 1000 + 272 * mtu / 100;
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c
new file mode 100644 (file)
index 0000000..046948e
--- /dev/null
@@ -0,0 +1,634 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2018 Mellanox Technologies. */
+
+#include <net/vxlan.h>
+#include <net/gre.h>
+#include "lib/vxlan.h"
+#include "en/tc_tun.h"
+
+static int get_route_and_out_devs(struct mlx5e_priv *priv,
+                                 struct net_device *dev,
+                                 struct net_device **route_dev,
+                                 struct net_device **out_dev)
+{
+       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+       struct net_device *uplink_dev, *uplink_upper;
+       bool dst_is_lag_dev;
+
+       uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH);
+       uplink_upper = netdev_master_upper_dev_get(uplink_dev);
+       dst_is_lag_dev = (uplink_upper &&
+                         netif_is_lag_master(uplink_upper) &&
+                         dev == uplink_upper &&
+                         mlx5_lag_is_sriov(priv->mdev));
+
+       /* 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) ||
+           dst_is_lag_dev) {
+               *route_dev = uplink_dev;
+               *out_dev = *route_dev;
+       } else {
+               *route_dev = dev;
+               if (is_vlan_dev(*route_dev))
+                       *out_dev = uplink_dev;
+               else if (mlx5e_eswitch_rep(dev))
+                       *out_dev = *route_dev;
+               else
+                       return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int mlx5e_route_lookup_ipv4(struct mlx5e_priv *priv,
+                                  struct net_device *mirred_dev,
+                                  struct net_device **out_dev,
+                                  struct net_device **route_dev,
+                                  struct flowi4 *fl4,
+                                  struct neighbour **out_n,
+                                  u8 *out_ttl)
+{
+       struct rtable *rt;
+       struct neighbour *n = NULL;
+
+#if IS_ENABLED(CONFIG_INET)
+       int ret;
+
+       rt = ip_route_output_key(dev_net(mirred_dev), fl4);
+       ret = PTR_ERR_OR_ZERO(rt);
+       if (ret)
+               return ret;
+#else
+       return -EOPNOTSUPP;
+#endif
+
+       ret = get_route_and_out_devs(priv, rt->dst.dev, route_dev, out_dev);
+       if (ret < 0)
+               return ret;
+
+       if (!(*out_ttl))
+               *out_ttl = ip4_dst_hoplimit(&rt->dst);
+       n = dst_neigh_lookup(&rt->dst, &fl4->daddr);
+       ip_rt_put(rt);
+       if (!n)
+               return -ENOMEM;
+
+       *out_n = n;
+       return 0;
+}
+
+static const char *mlx5e_netdev_kind(struct net_device *dev)
+{
+       if (dev->rtnl_link_ops)
+               return dev->rtnl_link_ops->kind;
+       else
+               return "";
+}
+
+static int mlx5e_route_lookup_ipv6(struct mlx5e_priv *priv,
+                                  struct net_device *mirred_dev,
+                                  struct net_device **out_dev,
+                                  struct net_device **route_dev,
+                                  struct flowi6 *fl6,
+                                  struct neighbour **out_n,
+                                  u8 *out_ttl)
+{
+       struct neighbour *n = NULL;
+       struct dst_entry *dst;
+
+#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
+       int ret;
+
+       ret = ipv6_stub->ipv6_dst_lookup(dev_net(mirred_dev), NULL, &dst,
+                                        fl6);
+       if (ret < 0)
+               return ret;
+
+       if (!(*out_ttl))
+               *out_ttl = ip6_dst_hoplimit(dst);
+
+       ret = get_route_and_out_devs(priv, dst->dev, route_dev, out_dev);
+       if (ret < 0)
+               return ret;
+#else
+       return -EOPNOTSUPP;
+#endif
+
+       n = dst_neigh_lookup(dst, &fl6->daddr);
+       dst_release(dst);
+       if (!n)
+               return -ENOMEM;
+
+       *out_n = n;
+       return 0;
+}
+
+static int mlx5e_gen_vxlan_header(char buf[], struct ip_tunnel_key *tun_key)
+{
+       __be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
+       struct udphdr *udp = (struct udphdr *)(buf);
+       struct vxlanhdr *vxh = (struct vxlanhdr *)
+                              ((char *)udp + sizeof(struct udphdr));
+
+       udp->dest = tun_key->tp_dst;
+       vxh->vx_flags = VXLAN_HF_VNI;
+       vxh->vx_vni = vxlan_vni_field(tun_id);
+
+       return 0;
+}
+
+static int mlx5e_gen_gre_header(char buf[], struct ip_tunnel_key *tun_key)
+{
+       __be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
+       int hdr_len;
+       struct gre_base_hdr *greh = (struct gre_base_hdr *)(buf);
+
+       /* the HW does not calculate GRE csum or sequences */
+       if (tun_key->tun_flags & (TUNNEL_CSUM | TUNNEL_SEQ))
+               return -EOPNOTSUPP;
+
+       greh->protocol = htons(ETH_P_TEB);
+
+       /* GRE key */
+       hdr_len = gre_calc_hlen(tun_key->tun_flags);
+       greh->flags = gre_tnl_flags_to_gre_flags(tun_key->tun_flags);
+       if (tun_key->tun_flags & TUNNEL_KEY) {
+               __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
+
+               *ptr = tun_id;
+       }
+
+       return 0;
+}
+
+static int mlx5e_gen_ip_tunnel_header(char buf[], __u8 *ip_proto,
+                                     struct mlx5e_encap_entry *e)
+{
+       int err = 0;
+       struct ip_tunnel_key *key = &e->tun_info.key;
+
+       if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
+               *ip_proto = IPPROTO_UDP;
+               err = mlx5e_gen_vxlan_header(buf, key);
+       } else if  (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
+               *ip_proto = IPPROTO_GRE;
+               err = mlx5e_gen_gre_header(buf, key);
+       } else {
+               pr_warn("mlx5: Cannot generate tunnel header for tunnel type (%d)\n"
+                       , e->tunnel_type);
+               err = -EOPNOTSUPP;
+       }
+
+       return err;
+}
+
+static char *gen_eth_tnl_hdr(char *buf, struct net_device *dev,
+                            struct mlx5e_encap_entry *e,
+                            u16 proto)
+{
+       struct ethhdr *eth = (struct ethhdr *)buf;
+       char *ip;
+
+       ether_addr_copy(eth->h_dest, e->h_dest);
+       ether_addr_copy(eth->h_source, dev->dev_addr);
+       if (is_vlan_dev(dev)) {
+               struct vlan_hdr *vlan = (struct vlan_hdr *)
+                                       ((char *)eth + ETH_HLEN);
+               ip = (char *)vlan + VLAN_HLEN;
+               eth->h_proto = vlan_dev_vlan_proto(dev);
+               vlan->h_vlan_TCI = htons(vlan_dev_vlan_id(dev));
+               vlan->h_vlan_encapsulated_proto = htons(proto);
+       } else {
+               eth->h_proto = htons(proto);
+               ip = (char *)eth + ETH_HLEN;
+       }
+
+       return ip;
+}
+
+int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv,
+                                   struct net_device *mirred_dev,
+                                   struct mlx5e_encap_entry *e)
+{
+       int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
+       struct ip_tunnel_key *tun_key = &e->tun_info.key;
+       struct net_device *out_dev, *route_dev;
+       struct neighbour *n = NULL;
+       struct flowi4 fl4 = {};
+       int ipv4_encap_size;
+       char *encap_header;
+       u8 nud_state, ttl;
+       struct iphdr *ip;
+       int err;
+
+       /* add the IP fields */
+       fl4.flowi4_tos = tun_key->tos;
+       fl4.daddr = tun_key->u.ipv4.dst;
+       fl4.saddr = tun_key->u.ipv4.src;
+       ttl = tun_key->ttl;
+
+       err = mlx5e_route_lookup_ipv4(priv, mirred_dev, &out_dev, &route_dev,
+                                     &fl4, &n, &ttl);
+       if (err)
+               return err;
+
+       ipv4_encap_size =
+               (is_vlan_dev(route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) +
+               sizeof(struct iphdr) +
+               e->tunnel_hlen;
+
+       if (max_encap_size < ipv4_encap_size) {
+               mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
+                              ipv4_encap_size, max_encap_size);
+               return -EOPNOTSUPP;
+       }
+
+       encap_header = kzalloc(ipv4_encap_size, GFP_KERNEL);
+       if (!encap_header)
+               return -ENOMEM;
+
+       /* used by mlx5e_detach_encap to lookup a neigh hash table
+        * entry in the neigh hash table when a user deletes a rule
+        */
+       e->m_neigh.dev = n->dev;
+       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;
+
+       /* 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
+        * neigh changes it's validity state, we would find the relevant neigh
+        * in the hash.
+        */
+       err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e);
+       if (err)
+               goto free_encap;
+
+       read_lock_bh(&n->lock);
+       nud_state = n->nud_state;
+       ether_addr_copy(e->h_dest, n->ha);
+       read_unlock_bh(&n->lock);
+
+       /* add ethernet header */
+       ip = (struct iphdr *)gen_eth_tnl_hdr(encap_header, route_dev, e,
+                                            ETH_P_IP);
+
+       /* add ip header */
+       ip->tos = tun_key->tos;
+       ip->version = 0x4;
+       ip->ihl = 0x5;
+       ip->ttl = ttl;
+       ip->daddr = fl4.daddr;
+       ip->saddr = fl4.saddr;
+
+       /* add tunneling protocol header */
+       err = mlx5e_gen_ip_tunnel_header((char *)ip + sizeof(struct iphdr),
+                                        &ip->protocol, e);
+       if (err)
+               goto destroy_neigh_entry;
+
+       e->encap_size = ipv4_encap_size;
+       e->encap_header = encap_header;
+
+       if (!(nud_state & NUD_VALID)) {
+               neigh_event_send(n, NULL);
+               err = -EAGAIN;
+               goto out;
+       }
+
+       err = mlx5_packet_reformat_alloc(priv->mdev,
+                                        e->reformat_type,
+                                        ipv4_encap_size, encap_header,
+                                        MLX5_FLOW_NAMESPACE_FDB,
+                                        &e->encap_id);
+       if (err)
+               goto destroy_neigh_entry;
+
+       e->flags |= MLX5_ENCAP_ENTRY_VALID;
+       mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev));
+       neigh_release(n);
+       return err;
+
+destroy_neigh_entry:
+       mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
+free_encap:
+       kfree(encap_header);
+out:
+       if (n)
+               neigh_release(n);
+       return err;
+}
+
+int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
+                                   struct net_device *mirred_dev,
+                                   struct mlx5e_encap_entry *e)
+{
+       int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
+       struct ip_tunnel_key *tun_key = &e->tun_info.key;
+       struct net_device *out_dev, *route_dev;
+       struct neighbour *n = NULL;
+       struct flowi6 fl6 = {};
+       struct ipv6hdr *ip6h;
+       int ipv6_encap_size;
+       char *encap_header;
+       u8 nud_state, ttl;
+       int err;
+
+       ttl = tun_key->ttl;
+
+       fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label);
+       fl6.daddr = tun_key->u.ipv6.dst;
+       fl6.saddr = tun_key->u.ipv6.src;
+
+       err = mlx5e_route_lookup_ipv6(priv, mirred_dev, &out_dev, &route_dev,
+                                     &fl6, &n, &ttl);
+       if (err)
+               return err;
+
+       ipv6_encap_size =
+               (is_vlan_dev(route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) +
+               sizeof(struct ipv6hdr) +
+               e->tunnel_hlen;
+
+       if (max_encap_size < ipv6_encap_size) {
+               mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
+                              ipv6_encap_size, max_encap_size);
+               return -EOPNOTSUPP;
+       }
+
+       encap_header = kzalloc(ipv6_encap_size, GFP_KERNEL);
+       if (!encap_header)
+               return -ENOMEM;
+
+       /* used by mlx5e_detach_encap to lookup a neigh hash table
+        * entry in the neigh hash table when a user deletes a rule
+        */
+       e->m_neigh.dev = n->dev;
+       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;
+
+       /* 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
+        * neigh changes it's validity state, we would find the relevant neigh
+        * in the hash.
+        */
+       err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e);
+       if (err)
+               goto free_encap;
+
+       read_lock_bh(&n->lock);
+       nud_state = n->nud_state;
+       ether_addr_copy(e->h_dest, n->ha);
+       read_unlock_bh(&n->lock);
+
+       /* add ethernet header */
+       ip6h = (struct ipv6hdr *)gen_eth_tnl_hdr(encap_header, route_dev, e,
+                                                ETH_P_IPV6);
+
+       /* add ip header */
+       ip6_flow_hdr(ip6h, tun_key->tos, 0);
+       /* the HW fills up ipv6 payload len */
+       ip6h->hop_limit   = ttl;
+       ip6h->daddr       = fl6.daddr;
+       ip6h->saddr       = fl6.saddr;
+
+       /* add tunneling protocol header */
+       err = mlx5e_gen_ip_tunnel_header((char *)ip6h + sizeof(struct ipv6hdr),
+                                        &ip6h->nexthdr, e);
+       if (err)
+               goto destroy_neigh_entry;
+
+       e->encap_size = ipv6_encap_size;
+       e->encap_header = encap_header;
+
+       if (!(nud_state & NUD_VALID)) {
+               neigh_event_send(n, NULL);
+               err = -EAGAIN;
+               goto out;
+       }
+
+       err = mlx5_packet_reformat_alloc(priv->mdev,
+                                        e->reformat_type,
+                                        ipv6_encap_size, encap_header,
+                                        MLX5_FLOW_NAMESPACE_FDB,
+                                        &e->encap_id);
+       if (err)
+               goto destroy_neigh_entry;
+
+       e->flags |= MLX5_ENCAP_ENTRY_VALID;
+       mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev));
+       neigh_release(n);
+       return err;
+
+destroy_neigh_entry:
+       mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
+free_encap:
+       kfree(encap_header);
+out:
+       if (n)
+               neigh_release(n);
+       return err;
+}
+
+int mlx5e_tc_tun_get_type(struct net_device *tunnel_dev)
+{
+       if (netif_is_vxlan(tunnel_dev))
+               return MLX5E_TC_TUNNEL_TYPE_VXLAN;
+       else if (netif_is_gretap(tunnel_dev) ||
+                netif_is_ip6gretap(tunnel_dev))
+               return MLX5E_TC_TUNNEL_TYPE_GRETAP;
+       else
+               return MLX5E_TC_TUNNEL_TYPE_UNKNOWN;
+}
+
+bool mlx5e_tc_tun_device_to_offload(struct mlx5e_priv *priv,
+                                   struct net_device *netdev)
+{
+       int tunnel_type = mlx5e_tc_tun_get_type(netdev);
+
+       if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN &&
+           MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap))
+               return true;
+       else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP &&
+                MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap))
+               return true;
+       else
+               return false;
+}
+
+int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev,
+                                struct mlx5e_priv *priv,
+                                struct mlx5e_encap_entry *e,
+                                struct netlink_ext_ack *extack)
+{
+       e->tunnel_type = mlx5e_tc_tun_get_type(tunnel_dev);
+
+       if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
+               int dst_port =  be16_to_cpu(e->tun_info.key.tp_dst);
+
+               if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, dst_port)) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "vxlan udp dport was not registered with the HW");
+                       netdev_warn(priv->netdev,
+                                   "%d isn't an offloaded vxlan udp dport\n",
+                                   dst_port);
+                       return -EOPNOTSUPP;
+               }
+               e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN;
+               e->tunnel_hlen = VXLAN_HLEN;
+       } else if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
+               e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_NVGRE;
+               e->tunnel_hlen = gre_calc_hlen(e->tun_info.key.tun_flags);
+       } else {
+               e->reformat_type = -1;
+               e->tunnel_hlen = -1;
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
+                                   struct mlx5_flow_spec *spec,
+                                   struct tc_cls_flower_offload *f,
+                                   void *headers_c,
+                                   void *headers_v)
+{
+       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);
+
+       /* 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))) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "VXLAN decap filter must include enc_dst_port condition");
+               netdev_warn(priv->netdev,
+                           "VXLAN decap filter must include enc_dst_port condition\n");
+               return -EOPNOTSUPP;
+       }
+
+       /* udp dst port must be knonwn as a VXLAN port */
+       if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(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));
+               return -EOPNOTSUPP;
+       }
+
+       /* dst UDP port is valid here */
+       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_sport, ntohs(mask->src));
+       MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_sport, ntohs(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);
+               MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
+                        be32_to_cpu(mask->keyid));
+               MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
+                        be32_to_cpu(key->keyid));
+       }
+       return 0;
+}
+
+static int mlx5e_tc_tun_parse_gretap(struct mlx5e_priv *priv,
+                                    struct mlx5_flow_spec *spec,
+                                    struct tc_cls_flower_offload *f,
+                                    void *outer_headers_c,
+                                    void *outer_headers_v)
+{
+       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);
+
+       if (!MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap)) {
+               NL_SET_ERR_MSG_MOD(f->common.extack,
+                                  "GRE HW offloading is not supported");
+               netdev_warn(priv->netdev, "GRE HW offloading is not supported\n");
+               return -EOPNOTSUPP;
+       }
+
+       MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol);
+       MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+                ip_protocol, IPPROTO_GRE);
+
+       /* gre protocol*/
+       MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, gre_protocol);
+       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;
+
+               mask = skb_flow_dissector_target(f->dissector,
+                                                FLOW_DISSECTOR_KEY_ENC_KEYID,
+                                                f->mask);
+               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);
+               MLX5_SET(fte_match_set_misc, misc_v,
+                        gre_key.key, be32_to_cpu(key->keyid));
+       }
+
+       return 0;
+}
+
+int mlx5e_tc_tun_parse(struct net_device *filter_dev,
+                      struct mlx5e_priv *priv,
+                      struct mlx5_flow_spec *spec,
+                      struct tc_cls_flower_offload *f,
+                      void *headers_c,
+                      void *headers_v)
+{
+       int tunnel_type;
+       int err = 0;
+
+       tunnel_type = mlx5e_tc_tun_get_type(filter_dev);
+       if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
+               err = mlx5e_tc_tun_parse_vxlan(priv, spec, f,
+                                              headers_c, headers_v);
+       } else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
+               err = mlx5e_tc_tun_parse_gretap(priv, spec, f,
+                                               headers_c, headers_v);
+       } else {
+               netdev_warn(priv->netdev,
+                           "decapsulation offload is not supported for %s net device (%d)\n",
+                           mlx5e_netdev_kind(filter_dev), tunnel_type);
+               return -EOPNOTSUPP;
+       }
+       return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h
new file mode 100644 (file)
index 0000000..706ce7b
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2018 Mellanox Technologies. */
+
+#ifndef __MLX5_EN_TC_TUNNEL_H__
+#define __MLX5_EN_TC_TUNNEL_H__
+
+#include <linux/netdevice.h>
+#include <linux/mlx5/fs.h>
+#include <net/pkt_cls.h>
+#include <linux/netlink.h>
+#include "en.h"
+#include "en_rep.h"
+
+enum {
+       MLX5E_TC_TUNNEL_TYPE_UNKNOWN,
+       MLX5E_TC_TUNNEL_TYPE_VXLAN,
+       MLX5E_TC_TUNNEL_TYPE_GRETAP
+};
+
+int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev,
+                                struct mlx5e_priv *priv,
+                                struct mlx5e_encap_entry *e,
+                                struct netlink_ext_ack *extack);
+
+int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv,
+                                   struct net_device *mirred_dev,
+                                   struct mlx5e_encap_entry *e);
+
+int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
+                                   struct net_device *mirred_dev,
+                                   struct mlx5e_encap_entry *e);
+
+int mlx5e_tc_tun_get_type(struct net_device *tunnel_dev);
+bool mlx5e_tc_tun_device_to_offload(struct mlx5e_priv *priv,
+                                   struct net_device *netdev);
+
+int mlx5e_tc_tun_parse(struct net_device *filter_dev,
+                      struct mlx5e_priv *priv,
+                      struct mlx5_flow_spec *spec,
+                      struct tc_cls_flower_offload *f,
+                      void *headers_c,
+                      void *headers_v);
+
+#endif //__MLX5_EN_TC_TUNNEL_H__
index 128a82b..53608af 100644 (file)
@@ -254,11 +254,13 @@ struct sk_buff *mlx5e_ipsec_handle_tx_skb(struct net_device *netdev,
        struct mlx5e_ipsec_metadata *mdata;
        struct mlx5e_ipsec_sa_entry *sa_entry;
        struct xfrm_state *x;
+       struct sec_path *sp;
 
        if (!xo)
                return skb;
 
-       if (unlikely(skb->sp->len != 1)) {
+       sp = skb_sec_path(skb);
+       if (unlikely(sp->len != 1)) {
                atomic64_inc(&priv->ipsec->sw_stats.ipsec_tx_drop_bundle);
                goto drop;
        }
@@ -305,10 +307,11 @@ mlx5e_ipsec_build_sp(struct net_device *netdev, struct sk_buff *skb,
        struct mlx5e_priv *priv = netdev_priv(netdev);
        struct xfrm_offload *xo;
        struct xfrm_state *xs;
+       struct sec_path *sp;
        u32 sa_handle;
 
-       skb->sp = secpath_dup(skb->sp);
-       if (unlikely(!skb->sp)) {
+       sp = secpath_set(skb);
+       if (unlikely(!sp)) {
                atomic64_inc(&priv->ipsec->sw_stats.ipsec_rx_drop_sp_alloc);
                return NULL;
        }
@@ -320,8 +323,9 @@ mlx5e_ipsec_build_sp(struct net_device *netdev, struct sk_buff *skb,
                return NULL;
        }
 
-       skb->sp->xvec[skb->sp->len++] = xs;
-       skb->sp->olen++;
+       sp = skb_sec_path(skb);
+       sp->xvec[sp->len++] = xs;
+       sp->olen++;
 
        xo = xfrm_offload(skb);
        xo->flags = CRYPTO_DONE;
@@ -372,10 +376,11 @@ struct sk_buff *mlx5e_ipsec_handle_rx_skb(struct net_device *netdev,
 bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev,
                               netdev_features_t features)
 {
+       struct sec_path *sp = skb_sec_path(skb);
        struct xfrm_state *x;
 
-       if (skb->sp && skb->sp->len) {
-               x = skb->sp->xvec[0];
+       if (sp && sp->len) {
+               x = sp->xvec[0];
                if (x && x->xso.offload_handle)
                        return true;
        }
index 3e770ab..6e10120 100644 (file)
@@ -135,14 +135,15 @@ void mlx5e_build_ptys2ethtool_map(void)
                                       ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT);
 }
 
-static const char mlx5e_priv_flags[][ETH_GSTRING_LEN] = {
-       "rx_cqe_moder",
-       "tx_cqe_moder",
-       "rx_cqe_compress",
-       "rx_striding_rq",
-       "rx_no_csum_complete",
+typedef int (*mlx5e_pflag_handler)(struct net_device *netdev, bool enable);
+
+struct pflag_desc {
+       char name[ETH_GSTRING_LEN];
+       mlx5e_pflag_handler handler;
 };
 
+static const struct pflag_desc mlx5e_priv_flags[MLX5E_NUM_PFLAGS];
+
 int mlx5e_ethtool_get_sset_count(struct mlx5e_priv *priv, int sset)
 {
        int i, num_stats = 0;
@@ -153,7 +154,7 @@ int mlx5e_ethtool_get_sset_count(struct mlx5e_priv *priv, int sset)
                        num_stats += mlx5e_stats_grps[i].get_num_stats(priv);
                return num_stats;
        case ETH_SS_PRIV_FLAGS:
-               return ARRAY_SIZE(mlx5e_priv_flags);
+               return MLX5E_NUM_PFLAGS;
        case ETH_SS_TEST:
                return mlx5e_self_test_num(priv);
        /* fallthrough */
@@ -183,8 +184,9 @@ void mlx5e_ethtool_get_strings(struct mlx5e_priv *priv, u32 stringset, u8 *data)
 
        switch (stringset) {
        case ETH_SS_PRIV_FLAGS:
-               for (i = 0; i < ARRAY_SIZE(mlx5e_priv_flags); i++)
-                       strcpy(data + i * ETH_GSTRING_LEN, mlx5e_priv_flags[i]);
+               for (i = 0; i < MLX5E_NUM_PFLAGS; i++)
+                       strcpy(data + i * ETH_GSTRING_LEN,
+                              mlx5e_priv_flags[i].name);
                break;
 
        case ETH_SS_TEST:
@@ -353,7 +355,7 @@ 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(new_channels.params.indirection_rqt,
+               mlx5e_build_default_indir_rqt(priv->rss_params.indirection_rqt,
                                              MLX5E_INDIR_RQT_SIZE, count);
 
        if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
@@ -785,10 +787,9 @@ static void get_lp_advertising(u32 eth_proto_lp,
        ptys2ethtool_adver_link(lp_advertising, eth_proto_lp);
 }
 
-static int mlx5e_get_link_ksettings(struct net_device *netdev,
-                                   struct ethtool_link_ksettings *link_ksettings)
+int mlx5e_ethtool_get_link_ksettings(struct mlx5e_priv *priv,
+                                    struct ethtool_link_ksettings *link_ksettings)
 {
-       struct mlx5e_priv *priv    = netdev_priv(netdev);
        struct mlx5_core_dev *mdev = priv->mdev;
        u32 out[MLX5_ST_SZ_DW(ptys_reg)] = {0};
        u32 rx_pause = 0;
@@ -804,7 +805,7 @@ static int mlx5e_get_link_ksettings(struct net_device *netdev,
 
        err = mlx5_query_port_ptys(mdev, out, sizeof(out), MLX5_PTYS_EN, 1);
        if (err) {
-               netdev_err(netdev, "%s: query port ptys failed: %d\n",
+               netdev_err(priv->netdev, "%s: query port ptys failed: %d\n",
                           __func__, err);
                goto err_query_regs;
        }
@@ -824,7 +825,7 @@ static int mlx5e_get_link_ksettings(struct net_device *netdev,
 
        get_supported(eth_proto_cap, link_ksettings);
        get_advertising(eth_proto_admin, tx_pause, rx_pause, link_ksettings);
-       get_speed_duplex(netdev, eth_proto_oper, link_ksettings);
+       get_speed_duplex(priv->netdev, eth_proto_oper, link_ksettings);
 
        eth_proto_oper = eth_proto_oper ? eth_proto_oper : eth_proto_cap;
 
@@ -843,9 +844,8 @@ static int mlx5e_get_link_ksettings(struct net_device *netdev,
        ethtool_link_ksettings_add_link_mode(link_ksettings, supported,
                                             Autoneg);
 
-       err = get_fec_supported_advertised(mdev, link_ksettings);
-       if (err)
-               netdev_dbg(netdev, "%s: FEC caps query failed: %d\n",
+       if (get_fec_supported_advertised(mdev, link_ksettings))
+               netdev_dbg(priv->netdev, "%s: FEC caps query failed: %d\n",
                           __func__, err);
 
        if (!an_disable_admin)
@@ -856,6 +856,14 @@ err_query_regs:
        return err;
 }
 
+static int mlx5e_get_link_ksettings(struct net_device *netdev,
+                                   struct ethtool_link_ksettings *link_ksettings)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       return mlx5e_ethtool_get_link_ksettings(priv, link_ksettings);
+}
+
 static u32 mlx5e_ethtool2ptys_adver_link(const unsigned long *link_modes)
 {
        u32 i, ptys_modes = 0;
@@ -870,10 +878,9 @@ static u32 mlx5e_ethtool2ptys_adver_link(const unsigned long *link_modes)
        return ptys_modes;
 }
 
-static int mlx5e_set_link_ksettings(struct net_device *netdev,
-                                   const struct ethtool_link_ksettings *link_ksettings)
+int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv,
+                                    const struct ethtool_link_ksettings *link_ksettings)
 {
-       struct mlx5e_priv *priv    = netdev_priv(netdev);
        struct mlx5_core_dev *mdev = priv->mdev;
        u32 eth_proto_cap, eth_proto_admin;
        bool an_changes = false;
@@ -893,14 +900,14 @@ static int mlx5e_set_link_ksettings(struct net_device *netdev,
 
        err = mlx5_query_port_proto_cap(mdev, &eth_proto_cap, MLX5_PTYS_EN);
        if (err) {
-               netdev_err(netdev, "%s: query port eth proto cap failed: %d\n",
+               netdev_err(priv->netdev, "%s: query port eth proto cap failed: %d\n",
                           __func__, err);
                goto out;
        }
 
        link_modes = link_modes & eth_proto_cap;
        if (!link_modes) {
-               netdev_err(netdev, "%s: Not supported link mode(s) requested",
+               netdev_err(priv->netdev, "%s: Not supported link mode(s) requested",
                           __func__);
                err = -EINVAL;
                goto out;
@@ -908,7 +915,7 @@ static int mlx5e_set_link_ksettings(struct net_device *netdev,
 
        err = mlx5_query_port_proto_admin(mdev, &eth_proto_admin, MLX5_PTYS_EN);
        if (err) {
-               netdev_err(netdev, "%s: query port eth proto admin failed: %d\n",
+               netdev_err(priv->netdev, "%s: query port eth proto admin failed: %d\n",
                           __func__, err);
                goto out;
        }
@@ -930,9 +937,17 @@ out:
        return err;
 }
 
+static int mlx5e_set_link_ksettings(struct net_device *netdev,
+                                   const struct ethtool_link_ksettings *link_ksettings)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       return mlx5e_ethtool_set_link_ksettings(priv, link_ksettings);
+}
+
 u32 mlx5e_ethtool_get_rxfh_key_size(struct mlx5e_priv *priv)
 {
-       return sizeof(priv->channels.params.toeplitz_hash_key);
+       return sizeof(priv->rss_params.toeplitz_hash_key);
 }
 
 static u32 mlx5e_get_rxfh_key_size(struct net_device *netdev)
@@ -958,50 +973,27 @@ static int mlx5e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
                          u8 *hfunc)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_rss_params *rss = &priv->rss_params;
 
        if (indir)
-               memcpy(indir, priv->channels.params.indirection_rqt,
-                      sizeof(priv->channels.params.indirection_rqt));
+               memcpy(indir, rss->indirection_rqt,
+                      sizeof(rss->indirection_rqt));
 
        if (key)
-               memcpy(key, priv->channels.params.toeplitz_hash_key,
-                      sizeof(priv->channels.params.toeplitz_hash_key));
+               memcpy(key, rss->toeplitz_hash_key,
+                      sizeof(rss->toeplitz_hash_key));
 
        if (hfunc)
-               *hfunc = priv->channels.params.rss_hfunc;
+               *hfunc = rss->hfunc;
 
        return 0;
 }
 
-static void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen)
-{
-       void *tirc = MLX5_ADDR_OF(modify_tir_in, in, ctx);
-       struct mlx5_core_dev *mdev = priv->mdev;
-       int ctxlen = MLX5_ST_SZ_BYTES(tirc);
-       int tt;
-
-       MLX5_SET(modify_tir_in, in, bitmask.hash, 1);
-
-       for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
-               memset(tirc, 0, ctxlen);
-               mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, false);
-               mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in, inlen);
-       }
-
-       if (!mlx5e_tunnel_inner_ft_supported(priv->mdev))
-               return;
-
-       for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
-               memset(tirc, 0, ctxlen);
-               mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, true);
-               mlx5_core_modify_tir(mdev, priv->inner_indir_tir[tt].tirn, in, inlen);
-       }
-}
-
 static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
                          const u8 *key, const u8 hfunc)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
+       struct mlx5e_rss_params *rss = &priv->rss_params;
        int inlen = MLX5_ST_SZ_BYTES(modify_tir_in);
        bool hash_changed = false;
        void *in;
@@ -1017,15 +1009,14 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
 
        mutex_lock(&priv->state_lock);
 
-       if (hfunc != ETH_RSS_HASH_NO_CHANGE &&
-           hfunc != priv->channels.params.rss_hfunc) {
-               priv->channels.params.rss_hfunc = hfunc;
+       if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != rss->hfunc) {
+               rss->hfunc = hfunc;
                hash_changed = true;
        }
 
        if (indir) {
-               memcpy(priv->channels.params.indirection_rqt, indir,
-                      sizeof(priv->channels.params.indirection_rqt));
+               memcpy(rss->indirection_rqt, indir,
+                      sizeof(rss->indirection_rqt));
 
                if (test_bit(MLX5E_STATE_OPENED, &priv->state)) {
                        u32 rqtn = priv->indir_rqt.rqtn;
@@ -1033,7 +1024,7 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
                                .is_rss = true,
                                {
                                        .rss = {
-                                               .hfunc = priv->channels.params.rss_hfunc,
+                                               .hfunc = rss->hfunc,
                                                .channels  = &priv->channels,
                                        },
                                },
@@ -1044,10 +1035,9 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
        }
 
        if (key) {
-               memcpy(priv->channels.params.toeplitz_hash_key, key,
-                      sizeof(priv->channels.params.toeplitz_hash_key));
-               hash_changed = hash_changed ||
-                              priv->channels.params.rss_hfunc == ETH_RSS_HASH_TOP;
+               memcpy(rss->toeplitz_hash_key, key,
+                      sizeof(rss->toeplitz_hash_key));
+               hash_changed = hash_changed || rss->hfunc == ETH_RSS_HASH_TOP;
        }
 
        if (hash_changed)
@@ -1151,25 +1141,31 @@ static int mlx5e_set_tunable(struct net_device *dev,
        return err;
 }
 
-static void mlx5e_get_pauseparam(struct net_device *netdev,
-                                struct ethtool_pauseparam *pauseparam)
+void mlx5e_ethtool_get_pauseparam(struct mlx5e_priv *priv,
+                                 struct ethtool_pauseparam *pauseparam)
 {
-       struct mlx5e_priv *priv    = netdev_priv(netdev);
        struct mlx5_core_dev *mdev = priv->mdev;
        int err;
 
        err = mlx5_query_port_pause(mdev, &pauseparam->rx_pause,
                                    &pauseparam->tx_pause);
        if (err) {
-               netdev_err(netdev, "%s: mlx5_query_port_pause failed:0x%x\n",
+               netdev_err(priv->netdev, "%s: mlx5_query_port_pause failed:0x%x\n",
                           __func__, err);
        }
 }
 
-static int mlx5e_set_pauseparam(struct net_device *netdev,
-                               struct ethtool_pauseparam *pauseparam)
+static void mlx5e_get_pauseparam(struct net_device *netdev,
+                                struct ethtool_pauseparam *pauseparam)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       mlx5e_ethtool_get_pauseparam(priv, pauseparam);
+}
+
+int mlx5e_ethtool_set_pauseparam(struct mlx5e_priv *priv,
+                                struct ethtool_pauseparam *pauseparam)
 {
-       struct mlx5e_priv *priv    = netdev_priv(netdev);
        struct mlx5_core_dev *mdev = priv->mdev;
        int err;
 
@@ -1180,22 +1176,25 @@ static int mlx5e_set_pauseparam(struct net_device *netdev,
                                  pauseparam->rx_pause ? 1 : 0,
                                  pauseparam->tx_pause ? 1 : 0);
        if (err) {
-               netdev_err(netdev, "%s: mlx5_set_port_pause failed:0x%x\n",
+               netdev_err(priv->netdev, "%s: mlx5_set_port_pause failed:0x%x\n",
                           __func__, err);
        }
 
        return err;
 }
 
+static int mlx5e_set_pauseparam(struct net_device *netdev,
+                               struct ethtool_pauseparam *pauseparam)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       return mlx5e_ethtool_set_pauseparam(priv, pauseparam);
+}
+
 int mlx5e_ethtool_get_ts_info(struct mlx5e_priv *priv,
                              struct ethtool_ts_info *info)
 {
        struct mlx5_core_dev *mdev = priv->mdev;
-       int ret;
-
-       ret = ethtool_op_get_ts_info(priv->netdev, info);
-       if (ret)
-               return ret;
 
        info->phc_index = mlx5_clock_get_ptp_index(mdev);
 
@@ -1203,9 +1202,9 @@ int mlx5e_ethtool_get_ts_info(struct mlx5e_priv *priv,
            info->phc_index == -1)
                return 0;
 
-       info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE |
-                                SOF_TIMESTAMPING_RX_HARDWARE |
-                                SOF_TIMESTAMPING_RAW_HARDWARE;
+       info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+                               SOF_TIMESTAMPING_RX_HARDWARE |
+                               SOF_TIMESTAMPING_RAW_HARDWARE;
 
        info->tx_types = BIT(HWTSTAMP_TX_OFF) |
                         BIT(HWTSTAMP_TX_ON);
@@ -1511,8 +1510,6 @@ static int mlx5e_get_module_eeprom(struct net_device *netdev,
        return 0;
 }
 
-typedef int (*mlx5e_pflag_handler)(struct net_device *netdev, bool enable);
-
 static int set_pflag_cqe_based_moder(struct net_device *netdev, bool enable,
                                     bool is_rx_cq)
 {
@@ -1675,23 +1672,30 @@ static int set_pflag_rx_no_csum_complete(struct net_device *netdev, bool enable)
        return 0;
 }
 
+static const struct pflag_desc mlx5e_priv_flags[MLX5E_NUM_PFLAGS] = {
+       { "rx_cqe_moder",        set_pflag_rx_cqe_based_moder },
+       { "tx_cqe_moder",        set_pflag_tx_cqe_based_moder },
+       { "rx_cqe_compress",     set_pflag_rx_cqe_compress },
+       { "rx_striding_rq",      set_pflag_rx_striding_rq },
+       { "rx_no_csum_complete", set_pflag_rx_no_csum_complete },
+};
+
 static int mlx5e_handle_pflag(struct net_device *netdev,
                              u32 wanted_flags,
-                             enum mlx5e_priv_flag flag,
-                             mlx5e_pflag_handler pflag_handler)
+                             enum mlx5e_priv_flag flag)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
-       bool enable = !!(wanted_flags & flag);
+       bool enable = !!(wanted_flags & BIT(flag));
        u32 changes = wanted_flags ^ priv->channels.params.pflags;
        int err;
 
-       if (!(changes & flag))
+       if (!(changes & BIT(flag)))
                return 0;
 
-       err = pflag_handler(netdev, enable);
+       err = mlx5e_priv_flags[flag].handler(netdev, enable);
        if (err) {
-               netdev_err(netdev, "%s private flag 0x%x failed err %d\n",
-                          enable ? "Enable" : "Disable", flag, err);
+               netdev_err(netdev, "%s private flag '%s' failed err %d\n",
+                          enable ? "Enable" : "Disable", mlx5e_priv_flags[flag].name, err);
                return err;
        }
 
@@ -1702,38 +1706,17 @@ static int mlx5e_handle_pflag(struct net_device *netdev,
 static int mlx5e_set_priv_flags(struct net_device *netdev, u32 pflags)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
+       enum mlx5e_priv_flag pflag;
        int err;
 
        mutex_lock(&priv->state_lock);
-       err = mlx5e_handle_pflag(netdev, pflags,
-                                MLX5E_PFLAG_RX_CQE_BASED_MODER,
-                                set_pflag_rx_cqe_based_moder);
-       if (err)
-               goto out;
-
-       err = mlx5e_handle_pflag(netdev, pflags,
-                                MLX5E_PFLAG_TX_CQE_BASED_MODER,
-                                set_pflag_tx_cqe_based_moder);
-       if (err)
-               goto out;
 
-       err = mlx5e_handle_pflag(netdev, pflags,
-                                MLX5E_PFLAG_RX_CQE_COMPRESS,
-                                set_pflag_rx_cqe_compress);
-       if (err)
-               goto out;
-
-       err = mlx5e_handle_pflag(netdev, pflags,
-                                MLX5E_PFLAG_RX_STRIDING_RQ,
-                                set_pflag_rx_striding_rq);
-       if (err)
-               goto out;
-
-       err = mlx5e_handle_pflag(netdev, pflags,
-                                MLX5E_PFLAG_RX_NO_CSUM_COMPLETE,
-                                set_pflag_rx_no_csum_complete);
+       for (pflag = 0; pflag < MLX5E_NUM_PFLAGS; pflag++) {
+               err = mlx5e_handle_pflag(netdev, pflags, pflag);
+               if (err)
+                       break;
+       }
 
-out:
        mutex_unlock(&priv->state_lock);
 
        /* Need to fix some features.. */
index c18dceb..4421c10 100644 (file)
@@ -771,6 +771,112 @@ void mlx5e_ethtool_init_steering(struct mlx5e_priv *priv)
        INIT_LIST_HEAD(&priv->fs.ethtool.rules);
 }
 
+static enum mlx5e_traffic_types flow_type_to_traffic_type(u32 flow_type)
+{
+       switch (flow_type) {
+       case TCP_V4_FLOW:
+               return  MLX5E_TT_IPV4_TCP;
+       case TCP_V6_FLOW:
+               return MLX5E_TT_IPV6_TCP;
+       case UDP_V4_FLOW:
+               return MLX5E_TT_IPV4_UDP;
+       case UDP_V6_FLOW:
+               return MLX5E_TT_IPV6_UDP;
+       case AH_V4_FLOW:
+               return MLX5E_TT_IPV4_IPSEC_AH;
+       case AH_V6_FLOW:
+               return MLX5E_TT_IPV6_IPSEC_AH;
+       case ESP_V4_FLOW:
+               return MLX5E_TT_IPV4_IPSEC_ESP;
+       case ESP_V6_FLOW:
+               return MLX5E_TT_IPV6_IPSEC_ESP;
+       case IPV4_FLOW:
+               return MLX5E_TT_IPV4;
+       case IPV6_FLOW:
+               return MLX5E_TT_IPV6;
+       default:
+               return MLX5E_NUM_INDIR_TIRS;
+       }
+}
+
+static int mlx5e_set_rss_hash_opt(struct mlx5e_priv *priv,
+                                 struct ethtool_rxnfc *nfc)
+{
+       int inlen = MLX5_ST_SZ_BYTES(modify_tir_in);
+       enum mlx5e_traffic_types tt;
+       u8 rx_hash_field = 0;
+       void *in;
+
+       tt = flow_type_to_traffic_type(nfc->flow_type);
+       if (tt == MLX5E_NUM_INDIR_TIRS)
+               return -EINVAL;
+
+       /*  RSS does not support anything other than hashing to queues
+        *  on src IP, dest IP, TCP/UDP src port and TCP/UDP dest
+        *  port.
+        */
+       if (nfc->flow_type != TCP_V4_FLOW &&
+           nfc->flow_type != TCP_V6_FLOW &&
+           nfc->flow_type != UDP_V4_FLOW &&
+           nfc->flow_type != UDP_V6_FLOW)
+               return -EOPNOTSUPP;
+
+       if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
+                         RXH_L4_B_0_1 | RXH_L4_B_2_3))
+               return -EOPNOTSUPP;
+
+       if (nfc->data & RXH_IP_SRC)
+               rx_hash_field |= MLX5_HASH_FIELD_SEL_SRC_IP;
+       if (nfc->data & RXH_IP_DST)
+               rx_hash_field |= MLX5_HASH_FIELD_SEL_DST_IP;
+       if (nfc->data & RXH_L4_B_0_1)
+               rx_hash_field |= MLX5_HASH_FIELD_SEL_L4_SPORT;
+       if (nfc->data & RXH_L4_B_2_3)
+               rx_hash_field |= MLX5_HASH_FIELD_SEL_L4_DPORT;
+
+       in = kvzalloc(inlen, GFP_KERNEL);
+       if (!in)
+               return -ENOMEM;
+
+       mutex_lock(&priv->state_lock);
+
+       if (rx_hash_field == priv->rss_params.rx_hash_fields[tt])
+               goto out;
+
+       priv->rss_params.rx_hash_fields[tt] = rx_hash_field;
+       mlx5e_modify_tirs_hash(priv, in, inlen);
+
+out:
+       mutex_unlock(&priv->state_lock);
+       kvfree(in);
+       return 0;
+}
+
+static int mlx5e_get_rss_hash_opt(struct mlx5e_priv *priv,
+                                 struct ethtool_rxnfc *nfc)
+{
+       enum mlx5e_traffic_types tt;
+       u32 hash_field = 0;
+
+       tt = flow_type_to_traffic_type(nfc->flow_type);
+       if (tt == MLX5E_NUM_INDIR_TIRS)
+               return -EINVAL;
+
+       hash_field = priv->rss_params.rx_hash_fields[tt];
+       nfc->data = 0;
+
+       if (hash_field & MLX5_HASH_FIELD_SEL_SRC_IP)
+               nfc->data |= RXH_IP_SRC;
+       if (hash_field & MLX5_HASH_FIELD_SEL_DST_IP)
+               nfc->data |= RXH_IP_DST;
+       if (hash_field & MLX5_HASH_FIELD_SEL_L4_SPORT)
+               nfc->data |= RXH_L4_B_0_1;
+       if (hash_field & MLX5_HASH_FIELD_SEL_L4_DPORT)
+               nfc->data |= RXH_L4_B_2_3;
+
+       return 0;
+}
+
 int mlx5e_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
 {
        int err = 0;
@@ -783,6 +889,9 @@ int mlx5e_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
        case ETHTOOL_SRXCLSRLDEL:
                err = mlx5e_ethtool_flow_remove(priv, cmd->fs.location);
                break;
+       case ETHTOOL_SRXFH:
+               err = mlx5e_set_rss_hash_opt(priv, cmd);
+               break;
        default:
                err = -EOPNOTSUPP;
                break;
@@ -810,6 +919,9 @@ int mlx5e_get_rxnfc(struct net_device *dev,
        case ETHTOOL_GRXCLSRLALL:
                err = mlx5e_ethtool_get_all_flows(priv, info, rule_locs);
                break;
+       case ETHTOOL_GRXFH:
+               err =  mlx5e_get_rss_hash_opt(priv, info);
+               break;
        default:
                err = -EOPNOTSUPP;
                break;
index 1243edb..bc79140 100644 (file)
@@ -49,6 +49,8 @@
 #include "lib/clock.h"
 #include "en/port.h"
 #include "en/xdp.h"
+#include "lib/eq.h"
+#include "en/monitor_stats.h"
 
 struct mlx5e_rq_param {
        u32                     rqc[MLX5_ST_SZ_DW(rqc)];
@@ -128,6 +130,8 @@ static bool mlx5e_rx_is_linear_skb(struct mlx5_core_dev *mdev,
        return !params->lro_en && frag_sz <= PAGE_SIZE;
 }
 
+#define MLX5_MAX_MPWQE_LOG_WQE_STRIDE_SZ ((BIT(__mlx5_bit_sz(wq, log_wqe_stride_size)) - 1) + \
+                                         MLX5_MPWQE_LOG_STRIDE_SZ_BASE)
 static bool mlx5e_rx_mpwqe_is_linear_skb(struct mlx5_core_dev *mdev,
                                         struct mlx5e_params *params)
 {
@@ -138,6 +142,9 @@ static bool mlx5e_rx_mpwqe_is_linear_skb(struct mlx5_core_dev *mdev,
        if (!mlx5e_rx_is_linear_skb(mdev, params))
                return false;
 
+       if (order_base_2(frag_sz) > MLX5_MAX_MPWQE_LOG_WQE_STRIDE_SZ)
+               return false;
+
        if (MLX5_CAP_GEN(mdev, ext_stride_num_range))
                return true;
 
@@ -223,7 +230,7 @@ void mlx5e_set_rq_type(struct mlx5_core_dev *mdev, struct mlx5e_params *params)
                MLX5_WQ_TYPE_CYCLIC;
 }
 
-static void mlx5e_update_carrier(struct mlx5e_priv *priv)
+void mlx5e_update_carrier(struct mlx5e_priv *priv)
 {
        struct mlx5_core_dev *mdev = priv->mdev;
        u8 port_state;
@@ -262,7 +269,7 @@ void mlx5e_update_stats(struct mlx5e_priv *priv)
                        mlx5e_stats_grps[i].update_stats(priv);
 }
 
-static void mlx5e_update_ndo_stats(struct mlx5e_priv *priv)
+void mlx5e_update_ndo_stats(struct mlx5e_priv *priv)
 {
        int i;
 
@@ -293,33 +300,35 @@ void mlx5e_queue_update_stats(struct mlx5e_priv *priv)
        queue_work(priv->wq, &priv->update_stats_work);
 }
 
-static void mlx5e_async_event(struct mlx5_core_dev *mdev, void *vpriv,
-                             enum mlx5_dev_event event, unsigned long param)
+static int async_event(struct notifier_block *nb, unsigned long event, void *data)
 {
-       struct mlx5e_priv *priv = vpriv;
+       struct mlx5e_priv *priv = container_of(nb, struct mlx5e_priv, events_nb);
+       struct mlx5_eqe   *eqe = data;
 
-       if (!test_bit(MLX5E_STATE_ASYNC_EVENTS_ENABLED, &priv->state))
-               return;
+       if (event != MLX5_EVENT_TYPE_PORT_CHANGE)
+               return NOTIFY_DONE;
 
-       switch (event) {
-       case MLX5_DEV_EVENT_PORT_UP:
-       case MLX5_DEV_EVENT_PORT_DOWN:
+       switch (eqe->sub_type) {
+       case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
+       case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
                queue_work(priv->wq, &priv->update_carrier_work);
                break;
        default:
-               break;
+               return NOTIFY_DONE;
        }
+
+       return NOTIFY_OK;
 }
 
 static void mlx5e_enable_async_events(struct mlx5e_priv *priv)
 {
-       set_bit(MLX5E_STATE_ASYNC_EVENTS_ENABLED, &priv->state);
+       priv->events_nb.notifier_call = async_event;
+       mlx5_notifier_register(priv->mdev, &priv->events_nb);
 }
 
 static void mlx5e_disable_async_events(struct mlx5e_priv *priv)
 {
-       clear_bit(MLX5E_STATE_ASYNC_EVENTS_ENABLED, &priv->state);
-       synchronize_irq(pci_irq_vector(priv->mdev->pdev, MLX5_EQ_VEC_ASYNC));
+       mlx5_notifier_unregister(priv->mdev, &priv->events_nb);
 }
 
 static inline void mlx5e_build_umr_wqe(struct mlx5e_rq *rq,
@@ -502,6 +511,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c,
        rq->channel = c;
        rq->ix      = c->ix;
        rq->mdev    = mdev;
+       rq->hw_mtu  = MLX5E_SW2HW_MTU(params, params->sw_mtu);
        rq->stats   = &c->priv->channel_stats[c->ix].rq;
 
        rq->xdp_prog = params->xdp_prog ? bpf_prog_inc(params->xdp_prog) : NULL;
@@ -1395,6 +1405,7 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq)
        struct mlx5_core_dev *mdev = c->mdev;
        struct mlx5_rate_limit rl = {0};
 
+       cancel_work_sync(&sq->dim.work);
        mlx5e_destroy_sq(mdev, sq->sqn);
        if (sq->rate_limit) {
                rl.rate = sq->rate_limit;
@@ -1623,13 +1634,15 @@ static int mlx5e_alloc_cq_common(struct mlx5_core_dev *mdev,
        int err;
        u32 i;
 
+       err = mlx5_vector2eqn(mdev, param->eq_ix, &eqn_not_used, &irqn);
+       if (err)
+               return err;
+
        err = mlx5_cqwq_create(mdev, &param->wq, param->cqc, &cq->wq,
                               &cq->wq_ctrl);
        if (err)
                return err;
 
-       mlx5_vector2eqn(mdev, param->eq_ix, &eqn_not_used, &irqn);
-
        mcq->cqe_sz     = 64;
        mcq->set_ci_db  = cq->wq_ctrl.db.db;
        mcq->arm_db     = cq->wq_ctrl.db.db + 1;
@@ -1687,6 +1700,10 @@ static int mlx5e_create_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param)
        int eqn;
        int err;
 
+       err = mlx5_vector2eqn(mdev, param->eq_ix, &eqn, &irqn_not_used);
+       if (err)
+               return err;
+
        inlen = MLX5_ST_SZ_BYTES(create_cq_in) +
                sizeof(u64) * cq->wq_ctrl.buf.npages;
        in = kvzalloc(inlen, GFP_KERNEL);
@@ -1700,8 +1717,6 @@ static int mlx5e_create_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param)
        mlx5_fill_page_frag_array(&cq->wq_ctrl.buf,
                                  (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas));
 
-       mlx5_vector2eqn(mdev, param->eq_ix, &eqn, &irqn_not_used);
-
        MLX5_SET(cqc,   cqc, cq_period_mode, param->cq_period_mode);
        MLX5_SET(cqc,   cqc, c_eqn,         eqn);
        MLX5_SET(cqc,   cqc, uar_page,      mdev->priv.uar->index);
@@ -1758,11 +1773,6 @@ static void mlx5e_close_cq(struct mlx5e_cq *cq)
        mlx5e_free_cq(cq);
 }
 
-static int mlx5e_get_cpu(struct mlx5e_priv *priv, int ix)
-{
-       return cpumask_first(priv->mdev->priv.irq_info[ix].mask);
-}
-
 static int mlx5e_open_tx_cqs(struct mlx5e_channel *c,
                             struct mlx5e_params *params,
                             struct mlx5e_channel_param *cparam)
@@ -1913,14 +1923,18 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix,
                              struct mlx5e_channel_param *cparam,
                              struct mlx5e_channel **cp)
 {
+       int cpu = cpumask_first(mlx5_comp_irq_get_affinity_mask(priv->mdev, ix));
        struct net_dim_cq_moder icocq_moder = {0, 0};
        struct net_device *netdev = priv->netdev;
-       int cpu = mlx5e_get_cpu(priv, ix);
        struct mlx5e_channel *c;
        unsigned int irq;
        int err;
        int eqn;
 
+       err = mlx5_vector2eqn(priv->mdev, ix, &eqn, &irq);
+       if (err)
+               return err;
+
        c = kvzalloc_node(sizeof(*c), GFP_KERNEL, cpu_to_node(cpu));
        if (!c)
                return -ENOMEM;
@@ -1937,7 +1951,6 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix,
        c->xdp      = !!params->xdp_prog;
        c->stats    = &priv->channel_stats[ix].ch;
 
-       mlx5_vector2eqn(priv->mdev, ix, &eqn, &irq);
        c->irq_desc = irq_to_desc(irq);
 
        netif_napi_add(netdev, &c->napi, mlx5e_napi_poll, 64);
@@ -2218,6 +2231,8 @@ static void mlx5e_build_common_cq_param(struct mlx5e_priv *priv,
        void *cqc = param->cqc;
 
        MLX5_SET(cqc, cqc, uar_page, priv->mdev->priv.uar->index);
+       if (MLX5_CAP_GEN(priv->mdev, cqe_128_always) && cache_line_size() >= 128)
+               MLX5_SET(cqc, cqc, cqe_sz, CQE_STRIDE_128_PAD);
 }
 
 static void mlx5e_build_rx_cq_param(struct mlx5e_priv *priv,
@@ -2496,7 +2511,7 @@ static void mlx5e_fill_rqt_rqns(struct mlx5e_priv *priv, int sz,
                        if (rrp.rss.hfunc == ETH_RSS_HASH_XOR)
                                ix = mlx5e_bits_invert(i, ilog2(sz));
 
-                       ix = priv->channels.params.indirection_rqt[ix];
+                       ix = priv->rss_params.indirection_rqt[ix];
                        rqn = rrp.rss.channels->c[ix]->rq.rqn;
                } else {
                        rqn = rrp.rqn;
@@ -2579,7 +2594,7 @@ static void mlx5e_redirect_rqts_to_channels(struct mlx5e_priv *priv,
                {
                        .rss = {
                                .channels  = chs,
-                               .hfunc     = chs->params.rss_hfunc,
+                               .hfunc     = priv->rss_params.hfunc,
                        }
                },
        };
@@ -2599,6 +2614,54 @@ static void mlx5e_redirect_rqts_to_drop(struct mlx5e_priv *priv)
        mlx5e_redirect_rqts(priv, drop_rrp);
 }
 
+static const struct mlx5e_tirc_config tirc_default_config[MLX5E_NUM_INDIR_TIRS] = {
+       [MLX5E_TT_IPV4_TCP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
+                               .l4_prot_type = MLX5_L4_PROT_TYPE_TCP,
+                               .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
+       },
+       [MLX5E_TT_IPV6_TCP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
+                               .l4_prot_type = MLX5_L4_PROT_TYPE_TCP,
+                               .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
+       },
+       [MLX5E_TT_IPV4_UDP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
+                               .l4_prot_type = MLX5_L4_PROT_TYPE_UDP,
+                               .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
+       },
+       [MLX5E_TT_IPV6_UDP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
+                               .l4_prot_type = MLX5_L4_PROT_TYPE_UDP,
+                               .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
+       },
+       [MLX5E_TT_IPV4_IPSEC_AH] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
+                                    .l4_prot_type = 0,
+                                    .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
+       },
+       [MLX5E_TT_IPV6_IPSEC_AH] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
+                                    .l4_prot_type = 0,
+                                    .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
+       },
+       [MLX5E_TT_IPV4_IPSEC_ESP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
+                                     .l4_prot_type = 0,
+                                     .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
+       },
+       [MLX5E_TT_IPV6_IPSEC_ESP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
+                                     .l4_prot_type = 0,
+                                     .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
+       },
+       [MLX5E_TT_IPV4] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
+                           .l4_prot_type = 0,
+                           .rx_hash_fields = MLX5_HASH_IP,
+       },
+       [MLX5E_TT_IPV6] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
+                           .l4_prot_type = 0,
+                           .rx_hash_fields = MLX5_HASH_IP,
+       },
+};
+
+struct mlx5e_tirc_config mlx5e_tirc_get_default_config(enum mlx5e_traffic_types tt)
+{
+       return tirc_default_config[tt];
+}
+
 static void mlx5e_build_tir_ctx_lro(struct mlx5e_params *params, void *tirc)
 {
        if (!params->lro_en)
@@ -2614,116 +2677,68 @@ static void mlx5e_build_tir_ctx_lro(struct mlx5e_params *params, void *tirc)
        MLX5_SET(tirc, tirc, lro_timeout_period_usecs, params->lro_timeout);
 }
 
-void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params,
-                                   enum mlx5e_traffic_types tt,
+void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_rss_params *rss_params,
+                                   const struct mlx5e_tirc_config *ttconfig,
                                    void *tirc, bool inner)
 {
        void *hfso = inner ? MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_inner) :
                             MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer);
 
-#define MLX5_HASH_IP            (MLX5_HASH_FIELD_SEL_SRC_IP   |\
-                                MLX5_HASH_FIELD_SEL_DST_IP)
-
-#define MLX5_HASH_IP_L4PORTS    (MLX5_HASH_FIELD_SEL_SRC_IP   |\
-                                MLX5_HASH_FIELD_SEL_DST_IP   |\
-                                MLX5_HASH_FIELD_SEL_L4_SPORT |\
-                                MLX5_HASH_FIELD_SEL_L4_DPORT)
-
-#define MLX5_HASH_IP_IPSEC_SPI  (MLX5_HASH_FIELD_SEL_SRC_IP   |\
-                                MLX5_HASH_FIELD_SEL_DST_IP   |\
-                                MLX5_HASH_FIELD_SEL_IPSEC_SPI)
-
-       MLX5_SET(tirc, tirc, rx_hash_fn, mlx5e_rx_hash_fn(params->rss_hfunc));
-       if (params->rss_hfunc == ETH_RSS_HASH_TOP) {
+       MLX5_SET(tirc, tirc, rx_hash_fn, mlx5e_rx_hash_fn(rss_params->hfunc));
+       if (rss_params->hfunc == ETH_RSS_HASH_TOP) {
                void *rss_key = MLX5_ADDR_OF(tirc, tirc,
                                             rx_hash_toeplitz_key);
                size_t len = MLX5_FLD_SZ_BYTES(tirc,
                                               rx_hash_toeplitz_key);
 
                MLX5_SET(tirc, tirc, rx_hash_symmetric, 1);
-               memcpy(rss_key, params->toeplitz_hash_key, len);
+               memcpy(rss_key, rss_params->toeplitz_hash_key, len);
        }
+       MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
+                ttconfig->l3_prot_type);
+       MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
+                ttconfig->l4_prot_type);
+       MLX5_SET(rx_hash_field_select, hfso, selected_fields,
+                ttconfig->rx_hash_fields);
+}
 
-       switch (tt) {
-       case MLX5E_TT_IPV4_TCP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV4);
-               MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
-                        MLX5_L4_PROT_TYPE_TCP);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_L4PORTS);
-               break;
-
-       case MLX5E_TT_IPV6_TCP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV6);
-               MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
-                        MLX5_L4_PROT_TYPE_TCP);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_L4PORTS);
-               break;
-
-       case MLX5E_TT_IPV4_UDP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV4);
-               MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
-                        MLX5_L4_PROT_TYPE_UDP);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_L4PORTS);
-               break;
-
-       case MLX5E_TT_IPV6_UDP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV6);
-               MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
-                        MLX5_L4_PROT_TYPE_UDP);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_L4PORTS);
-               break;
-
-       case MLX5E_TT_IPV4_IPSEC_AH:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV4);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_IPSEC_SPI);
-               break;
+static void mlx5e_update_rx_hash_fields(struct mlx5e_tirc_config *ttconfig,
+                                       enum mlx5e_traffic_types tt,
+                                       u32 rx_hash_fields)
+{
+       *ttconfig                = tirc_default_config[tt];
+       ttconfig->rx_hash_fields = rx_hash_fields;
+}
 
-       case MLX5E_TT_IPV6_IPSEC_AH:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV6);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_IPSEC_SPI);
-               break;
+void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen)
+{
+       void *tirc = MLX5_ADDR_OF(modify_tir_in, in, ctx);
+       struct mlx5e_rss_params *rss = &priv->rss_params;
+       struct mlx5_core_dev *mdev = priv->mdev;
+       int ctxlen = MLX5_ST_SZ_BYTES(tirc);
+       struct mlx5e_tirc_config ttconfig;
+       int tt;
 
-       case MLX5E_TT_IPV4_IPSEC_ESP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV4);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_IPSEC_SPI);
-               break;
+       MLX5_SET(modify_tir_in, in, bitmask.hash, 1);
 
-       case MLX5E_TT_IPV6_IPSEC_ESP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV6);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_IPSEC_SPI);
-               break;
+       for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
+               memset(tirc, 0, ctxlen);
+               mlx5e_update_rx_hash_fields(&ttconfig, tt,
+                                           rss->rx_hash_fields[tt]);
+               mlx5e_build_indir_tir_ctx_hash(rss, &ttconfig, tirc, false);
+               mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in, inlen);
+       }
 
-       case MLX5E_TT_IPV4:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV4);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP);
-               break;
+       if (!mlx5e_tunnel_inner_ft_supported(priv->mdev))
+               return;
 
-       case MLX5E_TT_IPV6:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV6);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP);
-               break;
-       default:
-               WARN_ONCE(true, "%s: bad traffic type!\n", __func__);
+       for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
+               memset(tirc, 0, ctxlen);
+               mlx5e_update_rx_hash_fields(&ttconfig, tt,
+                                           rss->rx_hash_fields[tt]);
+               mlx5e_build_indir_tir_ctx_hash(rss, &ttconfig, tirc, true);
+               mlx5_core_modify_tir(mdev, priv->inner_indir_tir[tt].tirn, in,
+                                    inlen);
        }
 }
 
@@ -2780,7 +2795,8 @@ static void mlx5e_build_inner_indir_tir_ctx(struct mlx5e_priv *priv,
        MLX5_SET(tirc, tirc, indirect_table, priv->indir_rqt.rqtn);
        MLX5_SET(tirc, tirc, tunneled_offload_en, 0x1);
 
-       mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, true);
+       mlx5e_build_indir_tir_ctx_hash(&priv->rss_params,
+                                      &tirc_default_config[tt], tirc, true);
 }
 
 static int mlx5e_set_mtu(struct mlx5_core_dev *mdev,
@@ -2811,7 +2827,7 @@ static void mlx5e_query_mtu(struct mlx5_core_dev *mdev,
        *mtu = MLX5E_HW2SW_MTU(params, hw_mtu);
 }
 
-static int mlx5e_set_dev_port_mtu(struct mlx5e_priv *priv)
+int mlx5e_set_dev_port_mtu(struct mlx5e_priv *priv)
 {
        struct mlx5e_params *params = &priv->channels.params;
        struct net_device *netdev = priv->netdev;
@@ -2891,7 +2907,7 @@ void mlx5e_activate_priv_channels(struct mlx5e_priv *priv)
        mlx5e_activate_channels(&priv->channels);
        netif_tx_start_all_queues(priv->netdev);
 
-       if (MLX5_ESWITCH_MANAGER(priv->mdev))
+       if (mlx5e_is_vport_rep(priv))
                mlx5e_add_sqs_fwd_rules(priv);
 
        mlx5e_wait_channels_min_rx_wqes(&priv->channels);
@@ -2902,7 +2918,7 @@ void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv)
 {
        mlx5e_redirect_rqts_to_drop(priv);
 
-       if (MLX5_ESWITCH_MANAGER(priv->mdev))
+       if (mlx5e_is_vport_rep(priv))
                mlx5e_remove_sqs_fwd_rules(priv);
 
        /* FIXME: This is a W/A only for tx timeout watch dog false alarm when
@@ -3154,7 +3170,7 @@ err_close_tises:
        return err;
 }
 
-void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv)
+static void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv)
 {
        int tc;
 
@@ -3172,7 +3188,9 @@ static void mlx5e_build_indir_tir_ctx(struct mlx5e_priv *priv,
 
        MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT);
        MLX5_SET(tirc, tirc, indirect_table, priv->indir_rqt.rqtn);
-       mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, false);
+
+       mlx5e_build_indir_tir_ctx_hash(&priv->rss_params,
+                                      &tirc_default_config[tt], tirc, false);
 }
 
 static void mlx5e_build_direct_tir_ctx(struct mlx5e_priv *priv, u32 rqtn, u32 *tirc)
@@ -3377,11 +3395,14 @@ static int mlx5e_setup_tc_cls_flower(struct mlx5e_priv *priv,
 {
        switch (cls_flower->command) {
        case TC_CLSFLOWER_REPLACE:
-               return mlx5e_configure_flower(priv, cls_flower, flags);
+               return mlx5e_configure_flower(priv->netdev, priv, cls_flower,
+                                             flags);
        case TC_CLSFLOWER_DESTROY:
-               return mlx5e_delete_flower(priv, cls_flower, flags);
+               return mlx5e_delete_flower(priv->netdev, priv, cls_flower,
+                                          flags);
        case TC_CLSFLOWER_STATS:
-               return mlx5e_stats_flower(priv, cls_flower, flags);
+               return mlx5e_stats_flower(priv->netdev, priv, cls_flower,
+                                         flags);
        default:
                return -EOPNOTSUPP;
        }
@@ -3394,7 +3415,8 @@ static int mlx5e_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
 
        switch (type) {
        case TC_SETUP_CLSFLOWER:
-               return mlx5e_setup_tc_cls_flower(priv, type_data, MLX5E_TC_INGRESS);
+               return mlx5e_setup_tc_cls_flower(priv, type_data, MLX5E_TC_INGRESS |
+                                                MLX5E_TC_NIC_OFFLOAD);
        default:
                return -EOPNOTSUPP;
        }
@@ -3437,7 +3459,7 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
        }
 }
 
-static void
+void
 mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
@@ -3445,8 +3467,10 @@ mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
        struct mlx5e_vport_stats *vstats = &priv->stats.vport;
        struct mlx5e_pport_stats *pstats = &priv->stats.pport;
 
-       /* update HW stats in background for next time */
-       mlx5e_queue_update_stats(priv);
+       if (!mlx5e_monitor_counter_supported(priv)) {
+               /* update HW stats in background for next time */
+               mlx5e_queue_update_stats(priv);
+       }
 
        if (mlx5e_is_uplink_rep(priv)) {
                stats->rx_packets = PPORT_802_3_GET(pstats, a_frames_received_ok);
@@ -3574,11 +3598,12 @@ static int set_feature_cvlan_filter(struct net_device *netdev, bool enable)
        return 0;
 }
 
+#ifdef CONFIG_MLX5_ESWITCH
 static int set_feature_tc_num_filters(struct net_device *netdev, bool enable)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
 
-       if (!enable && mlx5e_tc_num_filters(priv)) {
+       if (!enable && mlx5e_tc_num_filters(priv, MLX5E_TC_NIC_OFFLOAD)) {
                netdev_err(netdev,
                           "Active offloaded tc filters, can't turn hw_tc_offload off\n");
                return -EINVAL;
@@ -3586,6 +3611,7 @@ static int set_feature_tc_num_filters(struct net_device *netdev, bool enable)
 
        return 0;
 }
+#endif
 
 static int set_feature_rx_all(struct net_device *netdev, bool enable)
 {
@@ -3684,7 +3710,9 @@ static int mlx5e_set_features(struct net_device *netdev,
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_LRO, set_feature_lro);
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_FILTER,
                                    set_feature_cvlan_filter);
+#ifdef CONFIG_MLX5_ESWITCH
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_TC, set_feature_tc_num_filters);
+#endif
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXALL, set_feature_rx_all);
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXFCS, set_feature_rx_fcs);
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_RX, set_feature_rx_vlan);
@@ -3755,10 +3783,11 @@ int mlx5e_change_mtu(struct net_device *netdev, int new_mtu,
        }
 
        if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) {
+               bool is_linear = mlx5e_rx_mpwqe_is_linear_skb(priv->mdev, &new_channels.params);
                u8 ppw_old = mlx5e_mpwqe_log_pkts_per_wqe(params);
                u8 ppw_new = mlx5e_mpwqe_log_pkts_per_wqe(&new_channels.params);
 
-               reset = reset && (ppw_old != ppw_new);
+               reset = reset && (is_linear || (ppw_old != ppw_new));
        }
 
        if (!reset) {
@@ -3876,7 +3905,7 @@ static int mlx5e_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 }
 
 #ifdef CONFIG_MLX5_ESWITCH
-static int mlx5e_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
+int mlx5e_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
        struct mlx5_core_dev *mdev = priv->mdev;
@@ -3913,8 +3942,8 @@ static int mlx5e_set_vf_trust(struct net_device *dev, int vf, bool setting)
        return mlx5_eswitch_set_vport_trust(mdev->priv.eswitch, vf + 1, setting);
 }
 
-static int mlx5e_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate,
-                            int max_tx_rate)
+int mlx5e_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate,
+                     int max_tx_rate)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
        struct mlx5_core_dev *mdev = priv->mdev;
@@ -3955,8 +3984,8 @@ static int mlx5e_set_vf_link_state(struct net_device *dev, int vf,
                                            mlx5_ifla_link2vport(link_state));
 }
 
-static int mlx5e_get_vf_config(struct net_device *dev,
-                              int vf, struct ifla_vf_info *ivi)
+int mlx5e_get_vf_config(struct net_device *dev,
+                       int vf, struct ifla_vf_info *ivi)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
        struct mlx5_core_dev *mdev = priv->mdev;
@@ -3969,8 +3998,8 @@ static int mlx5e_get_vf_config(struct net_device *dev,
        return 0;
 }
 
-static int mlx5e_get_vf_stats(struct net_device *dev,
-                             int vf, struct ifla_vf_stats *vf_stats)
+int mlx5e_get_vf_stats(struct net_device *dev,
+                      int vf, struct ifla_vf_stats *vf_stats)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
        struct mlx5_core_dev *mdev = priv->mdev;
@@ -4031,8 +4060,7 @@ static void mlx5e_vxlan_queue_work(struct mlx5e_priv *priv, u16 port, int add)
        queue_work(priv->wq, &vxlan_work->work);
 }
 
-static void mlx5e_add_vxlan_port(struct net_device *netdev,
-                                struct udp_tunnel_info *ti)
+void mlx5e_add_vxlan_port(struct net_device *netdev, struct udp_tunnel_info *ti)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
 
@@ -4045,8 +4073,7 @@ static void mlx5e_add_vxlan_port(struct net_device *netdev,
        mlx5e_vxlan_queue_work(priv, be16_to_cpu(ti->port), 1);
 }
 
-static void mlx5e_del_vxlan_port(struct net_device *netdev,
-                                struct udp_tunnel_info *ti)
+void mlx5e_del_vxlan_port(struct net_device *netdev, struct udp_tunnel_info *ti)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
 
@@ -4096,9 +4123,9 @@ out:
        return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
 }
 
-static netdev_features_t mlx5e_features_check(struct sk_buff *skb,
-                                             struct net_device *netdev,
-                                             netdev_features_t features)
+netdev_features_t mlx5e_features_check(struct sk_buff *skb,
+                                      struct net_device *netdev,
+                                      netdev_features_t features)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
 
@@ -4121,17 +4148,17 @@ static netdev_features_t mlx5e_features_check(struct sk_buff *skb,
 static bool mlx5e_tx_timeout_eq_recover(struct net_device *dev,
                                        struct mlx5e_txqsq *sq)
 {
-       struct mlx5_eq *eq = sq->cq.mcq.eq;
+       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->eqn, eq->cons_index, eq->irqn);
+                  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->eqn);
+       netdev_err(dev, "Recover %d eqes on EQ 0x%x\n", eqe_count, eq->core.eqn);
        sq->channel->stats->eq_rearm++;
        return true;
 }
@@ -4358,8 +4385,6 @@ const struct net_device_ops mlx5e_netdev_ops = {
        .ndo_get_vf_config       = mlx5e_get_vf_config,
        .ndo_set_vf_link_state   = mlx5e_set_vf_link_state,
        .ndo_get_vf_stats        = mlx5e_get_vf_stats,
-       .ndo_has_offload_stats   = mlx5e_has_offload_stats,
-       .ndo_get_offload_stats   = mlx5e_get_offload_stats,
 #endif
 };
 
@@ -4505,15 +4530,23 @@ void mlx5e_build_rq_params(struct mlx5_core_dev *mdev,
        mlx5e_init_rq_type_params(mdev, params);
 }
 
-void mlx5e_build_rss_params(struct mlx5e_params *params)
+void mlx5e_build_rss_params(struct mlx5e_rss_params *rss_params,
+                           u16 num_channels)
 {
-       params->rss_hfunc = ETH_RSS_HASH_XOR;
-       netdev_rss_key_fill(params->toeplitz_hash_key, sizeof(params->toeplitz_hash_key));
-       mlx5e_build_default_indir_rqt(params->indirection_rqt,
-                                     MLX5E_INDIR_RQT_SIZE, params->num_channels);
+       enum mlx5e_traffic_types tt;
+
+       rss_params->hfunc = ETH_RSS_HASH_XOR;
+       netdev_rss_key_fill(rss_params->toeplitz_hash_key,
+                           sizeof(rss_params->toeplitz_hash_key));
+       mlx5e_build_default_indir_rqt(rss_params->indirection_rqt,
+                                     MLX5E_INDIR_RQT_SIZE, num_channels);
+       for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++)
+               rss_params->rx_hash_fields[tt] =
+                       tirc_default_config[tt].rx_hash_fields;
 }
 
 void mlx5e_build_nic_params(struct mlx5_core_dev *mdev,
+                           struct mlx5e_rss_params *rss_params,
                            struct mlx5e_params *params,
                            u16 max_channels, u16 mtu)
 {
@@ -4562,7 +4595,7 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev,
        params->tx_min_inline_mode = mlx5e_params_calculate_tx_min_inline(mdev);
 
        /* RSS */
-       mlx5e_build_rss_params(params);
+       mlx5e_build_rss_params(rss_params, params->num_channels);
 }
 
 static void mlx5e_set_netdev_dev_addr(struct net_device *netdev)
@@ -4577,12 +4610,6 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev)
        }
 }
 
-#if IS_ENABLED(CONFIG_MLX5_ESWITCH)
-static const struct switchdev_ops mlx5e_switchdev_ops = {
-       .switchdev_port_attr_get        = mlx5e_attr_get,
-};
-#endif
-
 static void mlx5e_build_nic_netdev(struct net_device *netdev)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
@@ -4678,7 +4705,9 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev)
            FT_CAP(modify_root) &&
            FT_CAP(identified_miss_table_mode) &&
            FT_CAP(flow_table_modify)) {
+#ifdef CONFIG_MLX5_ESWITCH
                netdev->hw_features      |= NETIF_F_HW_TC;
+#endif
 #ifdef CONFIG_MLX5_EN_ARFS
                netdev->hw_features      |= NETIF_F_NTUPLE;
 #endif
@@ -4690,12 +4719,6 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev)
        netdev->priv_flags       |= IFF_UNICAST_FLT;
 
        mlx5e_set_netdev_dev_addr(netdev);
-
-#if IS_ENABLED(CONFIG_MLX5_ESWITCH)
-       if (MLX5_ESWITCH_MANAGER(mdev))
-               netdev->switchdev_ops = &mlx5e_switchdev_ops;
-#endif
-
        mlx5e_ipsec_build_netdev(priv);
        mlx5e_tls_build_netdev(priv);
 }
@@ -4733,14 +4756,16 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
                          void *ppriv)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_rss_params *rss = &priv->rss_params;
        int err;
 
        err = mlx5e_netdev_init(netdev, priv, mdev, profile, ppriv);
        if (err)
                return err;
 
-       mlx5e_build_nic_params(mdev, &priv->channels.params,
-                              mlx5e_get_netdev_max_channels(netdev), netdev->mtu);
+       mlx5e_build_nic_params(mdev, rss, &priv->channels.params,
+                              mlx5e_get_netdev_max_channels(netdev),
+                              netdev->mtu);
 
        mlx5e_timestamp_init(priv);
 
@@ -4870,9 +4895,8 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv)
        mlx5_lag_add(mdev, netdev);
 
        mlx5e_enable_async_events(priv);
-
-       if (MLX5_ESWITCH_MANAGER(priv->mdev))
-               mlx5e_register_vport_reps(priv);
+       if (mlx5e_monitor_counter_supported(priv))
+               mlx5e_monitor_counter_init(priv);
 
        if (netdev->reg_state != NETREG_REGISTERED)
                return;
@@ -4906,8 +4930,8 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)
 
        queue_work(priv->wq, &priv->set_rx_mode_work);
 
-       if (MLX5_ESWITCH_MANAGER(priv->mdev))
-               mlx5e_unregister_vport_reps(priv);
+       if (mlx5e_monitor_counter_supported(priv))
+               mlx5e_monitor_counter_cleanup(priv);
 
        mlx5e_disable_async_events(priv);
        mlx5_lag_remove(mdev);
@@ -4960,7 +4984,7 @@ int mlx5e_netdev_init(struct net_device *netdev,
        netif_carrier_off(netdev);
 
 #ifdef CONFIG_MLX5_EN_ARFS
-       netdev->rx_cpu_rmap = mdev->rmap;
+       netdev->rx_cpu_rmap =  mlx5_eq_table_get_rmap(mdev);
 #endif
 
        return 0;
@@ -5004,11 +5028,21 @@ err_free_netdev:
 int mlx5e_attach_netdev(struct mlx5e_priv *priv)
 {
        const struct mlx5e_profile *profile;
+       int max_nch;
        int err;
 
        profile = priv->profile;
        clear_bit(MLX5E_STATE_DESTROYING, &priv->state);
 
+       /* max number of channels may have changed */
+       max_nch = mlx5e_get_max_num_channels(priv->mdev);
+       if (priv->channels.params.num_channels > max_nch) {
+               mlx5_core_warn(priv->mdev, "MLX5E: Reducing number of channels to %d\n", max_nch);
+               priv->channels.params.num_channels = max_nch;
+               mlx5e_build_default_indir_rqt(priv->rss_params.indirection_rqt,
+                                             MLX5E_INDIR_RQT_SIZE, max_nch);
+       }
+
        err = profile->init_tx(priv);
        if (err)
                goto out;
@@ -5094,7 +5128,6 @@ static void mlx5e_detach(struct mlx5_core_dev *mdev, void *vpriv)
 static void *mlx5e_add(struct mlx5_core_dev *mdev)
 {
        struct net_device *netdev;
-       void *rpriv = NULL;
        void *priv;
        int err;
        int nch;
@@ -5104,20 +5137,18 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev)
                return NULL;
 
 #ifdef CONFIG_MLX5_ESWITCH
-       if (MLX5_ESWITCH_MANAGER(mdev)) {
-               rpriv = mlx5e_alloc_nic_rep_priv(mdev);
-               if (!rpriv) {
-                       mlx5_core_warn(mdev, "Failed to alloc NIC rep priv data\n");
-                       return NULL;
-               }
+       if (MLX5_ESWITCH_MANAGER(mdev) &&
+           mlx5_eswitch_mode(mdev->priv.eswitch) == SRIOV_OFFLOADS) {
+               mlx5e_rep_register_vport_reps(mdev);
+               return mdev;
        }
 #endif
 
        nch = mlx5e_get_max_num_channels(mdev);
-       netdev = mlx5e_create_netdev(mdev, &mlx5e_nic_profile, nch, rpriv);
+       netdev = mlx5e_create_netdev(mdev, &mlx5e_nic_profile, nch, NULL);
        if (!netdev) {
                mlx5_core_err(mdev, "mlx5e_create_netdev failed\n");
-               goto err_free_rpriv;
+               return NULL;
        }
 
        priv = netdev_priv(netdev);
@@ -5143,30 +5174,26 @@ err_detach:
        mlx5e_detach(mdev, priv);
 err_destroy_netdev:
        mlx5e_destroy_netdev(priv);
-err_free_rpriv:
-       kfree(rpriv);
        return NULL;
 }
 
 static void mlx5e_remove(struct mlx5_core_dev *mdev, void *vpriv)
 {
-       struct mlx5e_priv *priv = vpriv;
-       void *ppriv = priv->ppriv;
+       struct mlx5e_priv *priv;
 
+#ifdef CONFIG_MLX5_ESWITCH
+       if (MLX5_ESWITCH_MANAGER(mdev) && vpriv == mdev) {
+               mlx5e_rep_unregister_vport_reps(mdev);
+               return;
+       }
+#endif
+       priv = vpriv;
 #ifdef CONFIG_MLX5_CORE_EN_DCB
        mlx5e_dcbnl_delete_app(priv);
 #endif
        unregister_netdev(priv->netdev);
        mlx5e_detach(mdev, vpriv);
        mlx5e_destroy_netdev(priv);
-       kfree(ppriv);
-}
-
-static void *mlx5e_get_netdev(void *vpriv)
-{
-       struct mlx5e_priv *priv = vpriv;
-
-       return priv->netdev;
 }
 
 static struct mlx5_interface mlx5e_interface = {
@@ -5174,9 +5201,7 @@ static struct mlx5_interface mlx5e_interface = {
        .remove    = mlx5e_remove,
        .attach    = mlx5e_attach,
        .detach    = mlx5e_detach,
-       .event     = mlx5e_async_event,
        .protocol  = MLX5_INTERFACE_PROTOCOL_ETH,
-       .get_dev   = mlx5e_get_netdev,
 };
 
 void mlx5e_init(void)
index c3c6575..dbbdbdf 100644 (file)
 #include "en.h"
 #include "en_rep.h"
 #include "en_tc.h"
+#include "en/tc_tun.h"
 #include "fs_core.h"
 
-#define MLX5E_REP_PARAMS_LOG_SQ_SIZE \
-       max(0x6, MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE)
+#define MLX5E_REP_PARAMS_DEF_LOG_SQ_SIZE \
+        max(0x7, MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE)
+#define MLX5E_REP_PARAMS_DEF_NUM_CHANNELS 1
 
 static const char mlx5e_rep_driver_name[] = "mlx5e_rep";
 
+struct mlx5e_rep_indr_block_priv {
+       struct net_device *netdev;
+       struct mlx5e_rep_priv *rpriv;
+
+       struct list_head list;
+};
+
+static void mlx5e_rep_indr_unregister_block(struct net_device *netdev);
+
 static void mlx5e_rep_get_drvinfo(struct net_device *dev,
                                  struct ethtool_drvinfo *drvinfo)
 {
@@ -98,7 +109,7 @@ static void mlx5e_rep_get_strings(struct net_device *dev,
        }
 }
 
-static void mlx5e_rep_update_hw_counters(struct mlx5e_priv *priv)
+static void mlx5e_vf_rep_update_hw_counters(struct mlx5e_priv *priv)
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
@@ -121,6 +132,32 @@ static void mlx5e_rep_update_hw_counters(struct mlx5e_priv *priv)
        vport_stats->tx_bytes   = vf_stats.rx_bytes;
 }
 
+static void mlx5e_uplink_rep_update_hw_counters(struct mlx5e_priv *priv)
+{
+       struct mlx5e_pport_stats *pstats = &priv->stats.pport;
+       struct rtnl_link_stats64 *vport_stats;
+
+       mlx5e_grp_802_3_update_stats(priv);
+
+       vport_stats = &priv->stats.vf_vport;
+
+       vport_stats->rx_packets = PPORT_802_3_GET(pstats, a_frames_received_ok);
+       vport_stats->rx_bytes   = PPORT_802_3_GET(pstats, a_octets_received_ok);
+       vport_stats->tx_packets = PPORT_802_3_GET(pstats, a_frames_transmitted_ok);
+       vport_stats->tx_bytes   = PPORT_802_3_GET(pstats, a_octets_transmitted_ok);
+}
+
+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)
+               mlx5e_uplink_rep_update_hw_counters(priv);
+       else
+               mlx5e_vf_rep_update_hw_counters(priv);
+}
+
 static void mlx5e_rep_update_sw_counters(struct mlx5e_priv *priv)
 {
        struct mlx5e_sw_stats *s = &priv->stats.sw;
@@ -256,6 +293,22 @@ static int mlx5e_rep_set_channels(struct net_device *dev,
        return 0;
 }
 
+static int mlx5e_rep_get_coalesce(struct net_device *netdev,
+                                 struct ethtool_coalesce *coal)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       return mlx5e_ethtool_get_coalesce(priv, coal);
+}
+
+static int mlx5e_rep_set_coalesce(struct net_device *netdev,
+                                 struct ethtool_coalesce *coal)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       return mlx5e_ethtool_set_coalesce(priv, coal);
+}
+
 static u32 mlx5e_rep_get_rxfh_key_size(struct net_device *netdev)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
@@ -270,7 +323,55 @@ static u32 mlx5e_rep_get_rxfh_indir_size(struct net_device *netdev)
        return mlx5e_ethtool_get_rxfh_indir_size(priv);
 }
 
-static const struct ethtool_ops mlx5e_rep_ethtool_ops = {
+static void mlx5e_uplink_rep_get_pauseparam(struct net_device *netdev,
+                                           struct ethtool_pauseparam *pauseparam)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       mlx5e_ethtool_get_pauseparam(priv, pauseparam);
+}
+
+static int mlx5e_uplink_rep_set_pauseparam(struct net_device *netdev,
+                                          struct ethtool_pauseparam *pauseparam)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       return mlx5e_ethtool_set_pauseparam(priv, pauseparam);
+}
+
+static int mlx5e_uplink_rep_get_link_ksettings(struct net_device *netdev,
+                                              struct ethtool_link_ksettings *link_ksettings)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       return mlx5e_ethtool_get_link_ksettings(priv, link_ksettings);
+}
+
+static int mlx5e_uplink_rep_set_link_ksettings(struct net_device *netdev,
+                                              const struct ethtool_link_ksettings *link_ksettings)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       return mlx5e_ethtool_set_link_ksettings(priv, link_ksettings);
+}
+
+static const struct ethtool_ops mlx5e_vf_rep_ethtool_ops = {
+       .get_drvinfo       = mlx5e_rep_get_drvinfo,
+       .get_link          = ethtool_op_get_link,
+       .get_strings       = mlx5e_rep_get_strings,
+       .get_sset_count    = mlx5e_rep_get_sset_count,
+       .get_ethtool_stats = mlx5e_rep_get_ethtool_stats,
+       .get_ringparam     = mlx5e_rep_get_ringparam,
+       .set_ringparam     = mlx5e_rep_set_ringparam,
+       .get_channels      = mlx5e_rep_get_channels,
+       .set_channels      = mlx5e_rep_set_channels,
+       .get_coalesce      = mlx5e_rep_get_coalesce,
+       .set_coalesce      = mlx5e_rep_set_coalesce,
+       .get_rxfh_key_size   = mlx5e_rep_get_rxfh_key_size,
+       .get_rxfh_indir_size = mlx5e_rep_get_rxfh_indir_size,
+};
+
+static const struct ethtool_ops mlx5e_uplink_rep_ethtool_ops = {
        .get_drvinfo       = mlx5e_rep_get_drvinfo,
        .get_link          = ethtool_op_get_link,
        .get_strings       = mlx5e_rep_get_strings,
@@ -280,24 +381,44 @@ static const struct ethtool_ops mlx5e_rep_ethtool_ops = {
        .set_ringparam     = mlx5e_rep_set_ringparam,
        .get_channels      = mlx5e_rep_get_channels,
        .set_channels      = mlx5e_rep_set_channels,
+       .get_coalesce      = mlx5e_rep_get_coalesce,
+       .set_coalesce      = mlx5e_rep_set_coalesce,
+       .get_link_ksettings = mlx5e_uplink_rep_get_link_ksettings,
+       .set_link_ksettings = mlx5e_uplink_rep_set_link_ksettings,
        .get_rxfh_key_size   = mlx5e_rep_get_rxfh_key_size,
        .get_rxfh_indir_size = mlx5e_rep_get_rxfh_indir_size,
+       .get_pauseparam    = mlx5e_uplink_rep_get_pauseparam,
+       .set_pauseparam    = mlx5e_uplink_rep_set_pauseparam,
 };
 
-int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr)
+static int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
-       struct mlx5e_rep_priv *rpriv = priv->ppriv;
-       struct mlx5_eswitch_rep *rep = rpriv->rep;
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+       struct net_device *uplink_upper = NULL;
+       struct mlx5e_priv *uplink_priv = NULL;
+       struct net_device *uplink_dev;
 
        if (esw->mode == SRIOV_NONE)
                return -EOPNOTSUPP;
 
+       uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH);
+       if (uplink_dev) {
+               uplink_upper = netdev_master_upper_dev_get(uplink_dev);
+               uplink_priv = netdev_priv(uplink_dev);
+       }
+
        switch (attr->id) {
        case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
                attr->u.ppid.id_len = ETH_ALEN;
-               ether_addr_copy(attr->u.ppid.id, rep->hw_id);
+               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;
+
+                       ether_addr_copy(attr->u.ppid.id, rep->hw_id);
+               }
                break;
        default:
                return -EOPNOTSUPP;
@@ -466,8 +587,8 @@ static void mlx5e_rep_update_flows(struct mlx5e_priv *priv,
 
        ASSERT_RTNL();
 
-       if ((!neigh_connected && (e->flags & MLX5_ENCAP_ENTRY_VALID)) ||
-           !ether_addr_equal(e->h_dest, ha))
+       if ((e->flags & MLX5_ENCAP_ENTRY_VALID) &&
+           (!neigh_connected || !ether_addr_equal(e->h_dest, ha)))
                mlx5e_tc_encap_flows_del(priv, e);
 
        if (neigh_connected && !(e->flags & MLX5_ENCAP_ENTRY_VALID)) {
@@ -518,6 +639,184 @@ static void mlx5e_rep_neigh_update(struct work_struct *work)
        neigh_release(n);
 }
 
+static struct mlx5e_rep_indr_block_priv *
+mlx5e_rep_indr_block_priv_lookup(struct mlx5e_rep_priv *rpriv,
+                                struct net_device *netdev)
+{
+       struct mlx5e_rep_indr_block_priv *cb_priv;
+
+       /* All callback list access should be protected by RTNL. */
+       ASSERT_RTNL();
+
+       list_for_each_entry(cb_priv,
+                           &rpriv->uplink_priv.tc_indr_block_priv_list,
+                           list)
+               if (cb_priv->netdev == netdev)
+                       return cb_priv;
+
+       return NULL;
+}
+
+static void mlx5e_rep_indr_clean_block_privs(struct mlx5e_rep_priv *rpriv)
+{
+       struct mlx5e_rep_indr_block_priv *cb_priv, *temp;
+       struct list_head *head = &rpriv->uplink_priv.tc_indr_block_priv_list;
+
+       list_for_each_entry_safe(cb_priv, temp, head, list) {
+               mlx5e_rep_indr_unregister_block(cb_priv->netdev);
+               kfree(cb_priv);
+       }
+}
+
+static int
+mlx5e_rep_indr_offload(struct net_device *netdev,
+                      struct tc_cls_flower_offload *flower,
+                      struct mlx5e_rep_indr_block_priv *indr_priv)
+{
+       struct mlx5e_priv *priv = netdev_priv(indr_priv->rpriv->netdev);
+       int flags = MLX5E_TC_EGRESS | MLX5E_TC_ESW_OFFLOAD;
+       int err = 0;
+
+       switch (flower->command) {
+       case TC_CLSFLOWER_REPLACE:
+               err = mlx5e_configure_flower(netdev, priv, flower, flags);
+               break;
+       case TC_CLSFLOWER_DESTROY:
+               err = mlx5e_delete_flower(netdev, priv, flower, flags);
+               break;
+       case TC_CLSFLOWER_STATS:
+               err = mlx5e_stats_flower(netdev, priv, flower, flags);
+               break;
+       default:
+               err = -EOPNOTSUPP;
+       }
+
+       return err;
+}
+
+static int mlx5e_rep_indr_setup_block_cb(enum tc_setup_type type,
+                                        void *type_data, void *indr_priv)
+{
+       struct mlx5e_rep_indr_block_priv *priv = indr_priv;
+
+       switch (type) {
+       case TC_SETUP_CLSFLOWER:
+               return mlx5e_rep_indr_offload(priv->netdev, type_data, priv);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int
+mlx5e_rep_indr_setup_tc_block(struct net_device *netdev,
+                             struct mlx5e_rep_priv *rpriv,
+                             struct tc_block_offload *f)
+{
+       struct mlx5e_rep_indr_block_priv *indr_priv;
+       int err = 0;
+
+       if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+               return -EOPNOTSUPP;
+
+       switch (f->command) {
+       case TC_BLOCK_BIND:
+               indr_priv = mlx5e_rep_indr_block_priv_lookup(rpriv, netdev);
+               if (indr_priv)
+                       return -EEXIST;
+
+               indr_priv = kmalloc(sizeof(*indr_priv), GFP_KERNEL);
+               if (!indr_priv)
+                       return -ENOMEM;
+
+               indr_priv->netdev = netdev;
+               indr_priv->rpriv = rpriv;
+               list_add(&indr_priv->list,
+                        &rpriv->uplink_priv.tc_indr_block_priv_list);
+
+               err = tcf_block_cb_register(f->block,
+                                           mlx5e_rep_indr_setup_block_cb,
+                                           netdev, indr_priv, f->extack);
+               if (err) {
+                       list_del(&indr_priv->list);
+                       kfree(indr_priv);
+               }
+
+               return err;
+       case TC_BLOCK_UNBIND:
+               tcf_block_cb_unregister(f->block,
+                                       mlx5e_rep_indr_setup_block_cb,
+                                       netdev);
+               indr_priv = mlx5e_rep_indr_block_priv_lookup(rpriv, netdev);
+               if (indr_priv) {
+                       list_del(&indr_priv->list);
+                       kfree(indr_priv);
+               }
+
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static
+int mlx5e_rep_indr_setup_tc_cb(struct net_device *netdev, void *cb_priv,
+                              enum tc_setup_type type, void *type_data)
+{
+       switch (type) {
+       case TC_SETUP_BLOCK:
+               return mlx5e_rep_indr_setup_tc_block(netdev, cb_priv,
+                                                     type_data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int mlx5e_rep_indr_register_block(struct mlx5e_rep_priv *rpriv,
+                                        struct net_device *netdev)
+{
+       int err;
+
+       err = __tc_indr_block_cb_register(netdev, rpriv,
+                                         mlx5e_rep_indr_setup_tc_cb,
+                                         netdev);
+       if (err) {
+               struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
+
+               mlx5_core_err(priv->mdev, "Failed to register remote block notifier for %s err=%d\n",
+                             netdev_name(netdev), err);
+       }
+       return err;
+}
+
+static void mlx5e_rep_indr_unregister_block(struct net_device *netdev)
+{
+       __tc_indr_block_cb_unregister(netdev, mlx5e_rep_indr_setup_tc_cb,
+                                     netdev);
+}
+
+static int mlx5e_nic_rep_netdevice_event(struct notifier_block *nb,
+                                        unsigned long event, void *ptr)
+{
+       struct mlx5e_rep_priv *rpriv = container_of(nb, struct mlx5e_rep_priv,
+                                                    uplink_priv.netdevice_nb);
+       struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
+       struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+
+       if (!mlx5e_tc_tun_device_to_offload(priv, netdev))
+               return NOTIFY_OK;
+
+       switch (event) {
+       case NETDEV_REGISTER:
+               mlx5e_rep_indr_register_block(rpriv, netdev);
+               break;
+       case NETDEV_UNREGISTER:
+               mlx5e_rep_indr_unregister_block(netdev);
+               break;
+       }
+       return NOTIFY_OK;
+}
+
 static struct mlx5e_neigh_hash_entry *
 mlx5e_rep_neigh_entry_lookup(struct mlx5e_priv *priv,
                             struct mlx5e_neigh *m_neigh);
@@ -779,7 +1078,7 @@ void mlx5e_rep_encap_entry_detach(struct mlx5e_priv *priv,
                mlx5e_rep_neigh_entry_destroy(priv, nhe);
 }
 
-static int mlx5e_rep_open(struct net_device *dev)
+static int mlx5e_vf_rep_open(struct net_device *dev)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
@@ -801,7 +1100,7 @@ unlock:
        return err;
 }
 
-static int mlx5e_rep_close(struct net_device *dev)
+static int mlx5e_vf_rep_close(struct net_device *dev)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
@@ -838,24 +1137,14 @@ mlx5e_rep_setup_tc_cls_flower(struct mlx5e_priv *priv,
 {
        switch (cls_flower->command) {
        case TC_CLSFLOWER_REPLACE:
-               return mlx5e_configure_flower(priv, cls_flower, flags);
+               return mlx5e_configure_flower(priv->netdev, priv, cls_flower,
+                                             flags);
        case TC_CLSFLOWER_DESTROY:
-               return mlx5e_delete_flower(priv, cls_flower, flags);
+               return mlx5e_delete_flower(priv->netdev, priv, cls_flower,
+                                          flags);
        case TC_CLSFLOWER_STATS:
-               return mlx5e_stats_flower(priv, cls_flower, flags);
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-static int mlx5e_rep_setup_tc_cb_egdev(enum tc_setup_type type, void *type_data,
-                                      void *cb_priv)
-{
-       struct mlx5e_priv *priv = cb_priv;
-
-       switch (type) {
-       case TC_SETUP_CLSFLOWER:
-               return mlx5e_rep_setup_tc_cls_flower(priv, type_data, MLX5E_TC_EGRESS);
+               return mlx5e_stats_flower(priv->netdev, priv, cls_flower,
+                                         flags);
        default:
                return -EOPNOTSUPP;
        }
@@ -868,7 +1157,8 @@ static int mlx5e_rep_setup_tc_cb(enum tc_setup_type type, void *type_data,
 
        switch (type) {
        case TC_SETUP_CLSFLOWER:
-               return mlx5e_rep_setup_tc_cls_flower(priv, type_data, MLX5E_TC_INGRESS);
+               return mlx5e_rep_setup_tc_cls_flower(priv, type_data, MLX5E_TC_INGRESS |
+                                                    MLX5E_TC_ESW_OFFLOAD);
        default:
                return -EOPNOTSUPP;
        }
@@ -907,43 +1197,23 @@ static int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type,
 
 bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv)
 {
-       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
        struct mlx5_eswitch_rep *rep;
 
        if (!MLX5_ESWITCH_MANAGER(priv->mdev))
                return false;
 
-       rep = rpriv->rep;
-       if (esw->mode == SRIOV_OFFLOADS &&
-           rep && rep->vport == FDB_UPLINK_VPORT)
-               return true;
-
-       return false;
-}
-
-static bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv)
-{
-       struct mlx5e_rep_priv *rpriv = priv->ppriv;
-       struct mlx5_eswitch_rep *rep;
-
-       if (!MLX5_ESWITCH_MANAGER(priv->mdev))
+       if (!rpriv) /* non vport rep mlx5e instances don't use this field */
                return false;
 
        rep = rpriv->rep;
-       if (rep && rep->vport != FDB_UPLINK_VPORT)
-               return true;
-
-       return false;
+       return (rep->vport == FDB_UPLINK_VPORT);
 }
 
-bool mlx5e_has_offload_stats(const struct net_device *dev, int attr_id)
+static bool mlx5e_rep_has_offload_stats(const struct net_device *dev, int attr_id)
 {
-       struct mlx5e_priv *priv = netdev_priv(dev);
-
        switch (attr_id) {
        case IFLA_OFFLOAD_XSTATS_CPU_HIT:
-               if (mlx5e_is_vf_vport_rep(priv) || mlx5e_is_uplink_rep(priv))
                        return true;
        }
 
@@ -969,8 +1239,8 @@ mlx5e_get_sw_stats64(const struct net_device *dev,
        return 0;
 }
 
-int mlx5e_get_offload_stats(int attr_id, const struct net_device *dev,
-                           void *sp)
+static int mlx5e_rep_get_offload_stats(int attr_id, const struct net_device *dev,
+                                      void *sp)
 {
        switch (attr_id) {
        case IFLA_OFFLOAD_XSTATS_CPU_HIT:
@@ -981,7 +1251,7 @@ int mlx5e_get_offload_stats(int attr_id, const struct net_device *dev,
 }
 
 static void
-mlx5e_rep_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
+mlx5e_vf_rep_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
 
@@ -990,37 +1260,93 @@ mlx5e_rep_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
        memcpy(stats, &priv->stats.vf_vport, sizeof(*stats));
 }
 
+static int mlx5e_vf_rep_change_mtu(struct net_device *netdev, int new_mtu)
+{
+       return mlx5e_change_mtu(netdev, new_mtu, NULL);
+}
+
+static int mlx5e_uplink_rep_change_mtu(struct net_device *netdev, int new_mtu)
+{
+       return mlx5e_change_mtu(netdev, new_mtu, mlx5e_set_dev_port_mtu);
+}
+
+static int mlx5e_uplink_rep_set_mac(struct net_device *netdev, void *addr)
+{
+       struct sockaddr *saddr = addr;
+
+       if (!is_valid_ether_addr(saddr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       ether_addr_copy(netdev->dev_addr, saddr->sa_data);
+       return 0;
+}
+
 static const struct switchdev_ops mlx5e_rep_switchdev_ops = {
        .switchdev_port_attr_get        = mlx5e_attr_get,
 };
 
-static int mlx5e_change_rep_mtu(struct net_device *netdev, int new_mtu)
-{
-       return mlx5e_change_mtu(netdev, new_mtu, NULL);
-}
+static const struct net_device_ops mlx5e_netdev_ops_vf_rep = {
+       .ndo_open                = mlx5e_vf_rep_open,
+       .ndo_stop                = mlx5e_vf_rep_close,
+       .ndo_start_xmit          = mlx5e_xmit,
+       .ndo_get_phys_port_name  = mlx5e_rep_get_phys_port_name,
+       .ndo_setup_tc            = mlx5e_rep_setup_tc,
+       .ndo_get_stats64         = mlx5e_vf_rep_get_stats,
+       .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,
+};
 
-static const struct net_device_ops mlx5e_netdev_ops_rep = {
-       .ndo_open                = mlx5e_rep_open,
-       .ndo_stop                = mlx5e_rep_close,
+static const struct net_device_ops mlx5e_netdev_ops_uplink_rep = {
+       .ndo_open                = mlx5e_open,
+       .ndo_stop                = mlx5e_close,
        .ndo_start_xmit          = mlx5e_xmit,
+       .ndo_set_mac_address     = mlx5e_uplink_rep_set_mac,
        .ndo_get_phys_port_name  = mlx5e_rep_get_phys_port_name,
        .ndo_setup_tc            = mlx5e_rep_setup_tc,
-       .ndo_get_stats64         = mlx5e_rep_get_stats,
-       .ndo_has_offload_stats   = mlx5e_has_offload_stats,
-       .ndo_get_offload_stats   = mlx5e_get_offload_stats,
-       .ndo_change_mtu          = mlx5e_change_rep_mtu,
+       .ndo_get_stats64         = mlx5e_get_stats,
+       .ndo_has_offload_stats   = mlx5e_rep_has_offload_stats,
+       .ndo_get_offload_stats   = mlx5e_rep_get_offload_stats,
+       .ndo_change_mtu          = mlx5e_uplink_rep_change_mtu,
+       .ndo_udp_tunnel_add      = mlx5e_add_vxlan_port,
+       .ndo_udp_tunnel_del      = mlx5e_del_vxlan_port,
+       .ndo_features_check      = mlx5e_features_check,
+       .ndo_set_vf_mac          = mlx5e_set_vf_mac,
+       .ndo_set_vf_rate         = mlx5e_set_vf_rate,
+       .ndo_get_vf_config       = mlx5e_get_vf_config,
+       .ndo_get_vf_stats        = mlx5e_get_vf_stats,
 };
 
-static void mlx5e_build_rep_params(struct mlx5_core_dev *mdev,
-                                  struct mlx5e_params *params, u16 mtu)
+bool mlx5e_eswitch_rep(struct net_device *netdev)
+{
+       if (netdev->netdev_ops == &mlx5e_netdev_ops_vf_rep ||
+           netdev->netdev_ops == &mlx5e_netdev_ops_uplink_rep)
+               return true;
+
+       return false;
+}
+
+static void mlx5e_build_rep_params(struct net_device *netdev)
 {
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
+       struct mlx5_core_dev *mdev = priv->mdev;
+       struct mlx5e_params *params;
+
        u8 cq_period_mode = MLX5_CAP_GEN(mdev, cq_period_start_from_cqe) ?
                                         MLX5_CQ_PERIOD_MODE_START_FROM_CQE :
                                         MLX5_CQ_PERIOD_MODE_START_FROM_EQE;
 
+       params = &priv->channels.params;
        params->hard_mtu    = MLX5E_ETH_HARD_MTU;
-       params->sw_mtu      = mtu;
-       params->log_sq_size = MLX5E_REP_PARAMS_LOG_SQ_SIZE;
+       params->sw_mtu      = netdev->mtu;
+
+       /* SQ */
+       if (rep->vport == FDB_UPLINK_VPORT)
+               params->log_sq_size = MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE;
+       else
+               params->log_sq_size = MLX5E_REP_PARAMS_DEF_LOG_SQ_SIZE;;
 
        /* RQ */
        mlx5e_build_rq_params(mdev, params);
@@ -1034,24 +1360,38 @@ static void mlx5e_build_rep_params(struct mlx5_core_dev *mdev,
        mlx5_query_min_inline(mdev, &params->tx_min_inline_mode);
 
        /* RSS */
-       mlx5e_build_rss_params(params);
+       mlx5e_build_rss_params(&priv->rss_params, params->num_channels);
 }
 
 static void mlx5e_build_rep_netdev(struct net_device *netdev)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
        struct mlx5_core_dev *mdev = priv->mdev;
-       u16 max_mtu;
 
-       netdev->netdev_ops = &mlx5e_netdev_ops_rep;
+       if (rep->vport == FDB_UPLINK_VPORT) {
+               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 */
+               mlx5_query_nic_vport_mac_address(mdev, 0, netdev->dev_addr);
+               netdev->ethtool_ops = &mlx5e_uplink_rep_ethtool_ops;
+#ifdef CONFIG_MLX5_CORE_EN_DCB
+               if (MLX5_CAP_GEN(mdev, qos))
+                       netdev->dcbnl_ops = &mlx5e_dcbnl_ops;
+#endif
+       } else {
+               netdev->netdev_ops = &mlx5e_netdev_ops_vf_rep;
+               eth_hw_addr_random(netdev);
+               netdev->ethtool_ops = &mlx5e_vf_rep_ethtool_ops;
+       }
 
        netdev->watchdog_timeo    = 15 * HZ;
 
-       netdev->ethtool_ops       = &mlx5e_rep_ethtool_ops;
 
        netdev->switchdev_ops = &mlx5e_rep_switchdev_ops;
 
-       netdev->features         |= NETIF_F_VLAN_CHALLENGED | NETIF_F_HW_TC | NETIF_F_NETNS_LOCAL;
+       netdev->features         |= NETIF_F_HW_TC | NETIF_F_NETNS_LOCAL;
        netdev->hw_features      |= NETIF_F_HW_TC;
 
        netdev->hw_features    |= NETIF_F_SG;
@@ -1062,13 +1402,10 @@ static void mlx5e_build_rep_netdev(struct net_device *netdev)
        netdev->hw_features    |= NETIF_F_TSO6;
        netdev->hw_features    |= NETIF_F_RXCSUM;
 
-       netdev->features |= netdev->hw_features;
-
-       eth_hw_addr_random(netdev);
+       if (rep->vport != FDB_UPLINK_VPORT)
+               netdev->features |= NETIF_F_VLAN_CHALLENGED;
 
-       netdev->min_mtu = ETH_MIN_MTU;
-       mlx5_query_port_max_mtu(mdev, &max_mtu, 1);
-       netdev->max_mtu = MLX5E_HW2SW_MTU(&priv->channels.params, max_mtu);
+       netdev->features |= netdev->hw_features;
 }
 
 static int mlx5e_init_rep(struct mlx5_core_dev *mdev,
@@ -1083,11 +1420,9 @@ static int mlx5e_init_rep(struct mlx5_core_dev *mdev,
        if (err)
                return err;
 
+       priv->channels.params.num_channels = MLX5E_REP_PARAMS_DEF_NUM_CHANNELS;
 
-       priv->channels.params.num_channels =
-                               mlx5e_get_netdev_max_channels(netdev);
-
-       mlx5e_build_rep_params(mdev, &priv->channels.params, netdev->mtu);
+       mlx5e_build_rep_params(netdev);
        mlx5e_build_rep_netdev(netdev);
 
        mlx5e_timestamp_init(priv);
@@ -1210,94 +1545,173 @@ static void mlx5e_cleanup_rep_rx(struct mlx5e_priv *priv)
 
 static int mlx5e_init_rep_tx(struct mlx5e_priv *priv)
 {
-       int err;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_rep_uplink_priv *uplink_priv;
+       int tc, err;
 
        err = mlx5e_create_tises(priv);
        if (err) {
                mlx5_core_warn(priv->mdev, "create tises failed, %d\n", err);
                return err;
        }
-       return 0;
-}
 
-static const struct mlx5e_profile mlx5e_rep_profile = {
-       .init                   = mlx5e_init_rep,
-       .cleanup                = mlx5e_cleanup_rep,
-       .init_rx                = mlx5e_init_rep_rx,
-       .cleanup_rx             = mlx5e_cleanup_rep_rx,
-       .init_tx                = mlx5e_init_rep_tx,
-       .cleanup_tx             = mlx5e_cleanup_nic_tx,
-       .update_stats           = mlx5e_rep_update_hw_counters,
-       .update_carrier         = NULL,
-       .rx_handlers.handle_rx_cqe       = mlx5e_handle_rx_cqe_rep,
-       .rx_handlers.handle_rx_cqe_mpwqe = mlx5e_handle_rx_cqe_mpwrq,
-       .max_tc                 = 1,
-};
+       if (rpriv->rep->vport == FDB_UPLINK_VPORT) {
+               uplink_priv = &rpriv->uplink_priv;
 
-/* e-Switch vport representors */
+               /* init shared tc flow table */
+               err = mlx5e_tc_esw_init(&uplink_priv->tc_ht);
+               if (err)
+                       goto destroy_tises;
+
+               /* init indirect block notifications */
+               INIT_LIST_HEAD(&uplink_priv->tc_indr_block_priv_list);
+               uplink_priv->netdevice_nb.notifier_call = mlx5e_nic_rep_netdevice_event;
+               err = register_netdevice_notifier(&uplink_priv->netdevice_nb);
+               if (err) {
+                       mlx5_core_err(priv->mdev, "Failed to register netdev notifier\n");
+                       goto tc_esw_cleanup;
+               }
+       }
 
-static int
-mlx5e_nic_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
+       return 0;
+
+tc_esw_cleanup:
+       mlx5e_tc_esw_cleanup(&uplink_priv->tc_ht);
+destroy_tises:
+       for (tc = 0; tc < priv->profile->max_tc; tc++)
+               mlx5e_destroy_tis(priv->mdev, priv->tisn[tc]);
+       return err;
+}
+
+static void mlx5e_cleanup_rep_tx(struct mlx5e_priv *priv)
 {
-       struct mlx5e_rep_priv *rpriv = mlx5e_rep_to_rep_priv(rep);
-       struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       int tc;
 
-       int err;
+       for (tc = 0; tc < priv->profile->max_tc; tc++)
+               mlx5e_destroy_tis(priv->mdev, priv->tisn[tc]);
 
-       if (test_bit(MLX5E_STATE_OPENED, &priv->state)) {
-               err = mlx5e_add_sqs_fwd_rules(priv);
-               if (err)
-                       return err;
+       if (rpriv->rep->vport == FDB_UPLINK_VPORT) {
+               /* clean indirect TC block notifications */
+               unregister_netdevice_notifier(&rpriv->uplink_priv.netdevice_nb);
+               mlx5e_rep_indr_clean_block_privs(rpriv);
+
+               /* delete shared tc flow table */
+               mlx5e_tc_esw_cleanup(&rpriv->uplink_priv.tc_ht);
        }
+}
 
-       err = mlx5e_rep_neigh_init(rpriv);
-       if (err)
-               goto err_remove_sqs;
+static void mlx5e_vf_rep_enable(struct mlx5e_priv *priv)
+{
+       struct net_device *netdev = priv->netdev;
+       struct mlx5_core_dev *mdev = priv->mdev;
+       u16 max_mtu;
 
-       /* init shared tc flow table */
-       err = mlx5e_tc_esw_init(&rpriv->tc_ht);
-       if (err)
-               goto  err_neigh_cleanup;
+       netdev->min_mtu = ETH_MIN_MTU;
+       mlx5_query_port_max_mtu(mdev, &max_mtu, 1);
+       netdev->max_mtu = MLX5E_HW2SW_MTU(&priv->channels.params, max_mtu);
+}
 
-       return 0;
+static int uplink_rep_async_event(struct notifier_block *nb, unsigned long event, void *data)
+{
+       struct mlx5e_priv *priv = container_of(nb, struct mlx5e_priv, events_nb);
+       struct mlx5_eqe   *eqe = data;
 
-err_neigh_cleanup:
-       mlx5e_rep_neigh_cleanup(rpriv);
-err_remove_sqs:
-       mlx5e_remove_sqs_fwd_rules(priv);
-       return err;
+       if (event != MLX5_EVENT_TYPE_PORT_CHANGE)
+               return NOTIFY_DONE;
+
+       switch (eqe->sub_type) {
+       case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
+       case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
+               queue_work(priv->wq, &priv->update_carrier_work);
+               break;
+       default:
+               return NOTIFY_DONE;
+       }
+
+       return NOTIFY_OK;
 }
 
-static void
-mlx5e_nic_rep_unload(struct mlx5_eswitch_rep *rep)
+static void mlx5e_uplink_rep_enable(struct mlx5e_priv *priv)
 {
-       struct mlx5e_rep_priv *rpriv = mlx5e_rep_to_rep_priv(rep);
-       struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
+       struct net_device *netdev = priv->netdev;
+       struct mlx5_core_dev *mdev = priv->mdev;
+       u16 max_mtu;
 
-       if (test_bit(MLX5E_STATE_OPENED, &priv->state))
-               mlx5e_remove_sqs_fwd_rules(priv);
+       netdev->min_mtu = ETH_MIN_MTU;
+       mlx5_query_port_max_mtu(priv->mdev, &max_mtu, 1);
+       netdev->max_mtu = MLX5E_HW2SW_MTU(&priv->channels.params, max_mtu);
+       mlx5e_set_dev_port_mtu(priv);
+
+       mlx5_lag_add(mdev, netdev);
+       priv->events_nb.notifier_call = uplink_rep_async_event;
+       mlx5_notifier_register(mdev, &priv->events_nb);
+#ifdef CONFIG_MLX5_CORE_EN_DCB
+       mlx5e_dcbnl_initialize(priv);
+       mlx5e_dcbnl_init_app(priv);
+#endif
+}
 
-       /* clean uplink offloaded TC rules, delete shared tc flow table */
-       mlx5e_tc_esw_cleanup(&rpriv->tc_ht);
+static void mlx5e_uplink_rep_disable(struct mlx5e_priv *priv)
+{
+       struct mlx5_core_dev *mdev = priv->mdev;
 
-       mlx5e_rep_neigh_cleanup(rpriv);
+#ifdef CONFIG_MLX5_CORE_EN_DCB
+       mlx5e_dcbnl_delete_app(priv);
+#endif
+       mlx5_notifier_unregister(mdev, &priv->events_nb);
+       mlx5_lag_remove(mdev);
 }
 
+static const struct mlx5e_profile mlx5e_vf_rep_profile = {
+       .init                   = mlx5e_init_rep,
+       .cleanup                = mlx5e_cleanup_rep,
+       .init_rx                = mlx5e_init_rep_rx,
+       .cleanup_rx             = mlx5e_cleanup_rep_rx,
+       .init_tx                = mlx5e_init_rep_tx,
+       .cleanup_tx             = mlx5e_cleanup_rep_tx,
+       .enable                 = mlx5e_vf_rep_enable,
+       .update_stats           = mlx5e_vf_rep_update_hw_counters,
+       .rx_handlers.handle_rx_cqe       = mlx5e_handle_rx_cqe_rep,
+       .rx_handlers.handle_rx_cqe_mpwqe = mlx5e_handle_rx_cqe_mpwrq,
+       .max_tc                 = 1,
+};
+
+static const struct mlx5e_profile mlx5e_uplink_rep_profile = {
+       .init                   = mlx5e_init_rep,
+       .cleanup                = mlx5e_cleanup_rep,
+       .init_rx                = mlx5e_init_rep_rx,
+       .cleanup_rx             = mlx5e_cleanup_rep_rx,
+       .init_tx                = mlx5e_init_rep_tx,
+       .cleanup_tx             = mlx5e_cleanup_rep_tx,
+       .enable                 = mlx5e_uplink_rep_enable,
+       .disable                = mlx5e_uplink_rep_disable,
+       .update_stats           = mlx5e_uplink_rep_update_hw_counters,
+       .update_carrier         = mlx5e_update_carrier,
+       .rx_handlers.handle_rx_cqe       = mlx5e_handle_rx_cqe_rep,
+       .rx_handlers.handle_rx_cqe_mpwqe = mlx5e_handle_rx_cqe_mpwrq,
+       .max_tc                 = MLX5E_MAX_NUM_TC,
+};
+
+/* e-Switch vport representors */
 static int
 mlx5e_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
 {
-       struct mlx5e_rep_priv *uplink_rpriv;
+       const struct mlx5e_profile *profile;
        struct mlx5e_rep_priv *rpriv;
        struct net_device *netdev;
-       struct mlx5e_priv *upriv;
        int nch, err;
 
        rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL);
        if (!rpriv)
                return -ENOMEM;
 
+       /* rpriv->rep to be looked up when profile->init() is called */
+       rpriv->rep = rep;
+
        nch = mlx5e_get_max_num_channels(dev);
-       netdev = mlx5e_create_netdev(dev, &mlx5e_rep_profile, nch, rpriv);
+       profile = (rep->vport == FDB_UPLINK_VPORT) ? &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",
                        rep->vport);
@@ -1306,15 +1720,20 @@ mlx5e_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
        }
 
        rpriv->netdev = netdev;
-       rpriv->rep = rep;
        rep->rep_if[REP_ETH].priv = rpriv;
        INIT_LIST_HEAD(&rpriv->vport_sqs_list);
 
+       if (rep->vport == FDB_UPLINK_VPORT) {
+               err = mlx5e_create_mdev_resources(dev);
+               if (err)
+                       goto err_destroy_netdev;
+       }
+
        err = mlx5e_attach_netdev(netdev_priv(netdev));
        if (err) {
                pr_warn("Failed to attach representor netdev for vport %d\n",
                        rep->vport);
-               goto err_destroy_netdev;
+               goto err_destroy_mdev_resources;
        }
 
        err = mlx5e_rep_neigh_init(rpriv);
@@ -1324,32 +1743,25 @@ mlx5e_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
                goto err_detach_netdev;
        }
 
-       uplink_rpriv = mlx5_eswitch_get_uplink_priv(dev->priv.eswitch, REP_ETH);
-       upriv = netdev_priv(uplink_rpriv->netdev);
-       err = tc_setup_cb_egdev_register(netdev, mlx5e_rep_setup_tc_cb_egdev,
-                                        upriv);
-       if (err)
-               goto err_neigh_cleanup;
-
        err = register_netdev(netdev);
        if (err) {
                pr_warn("Failed to register representor netdev for vport %d\n",
                        rep->vport);
-               goto err_egdev_cleanup;
+               goto err_neigh_cleanup;
        }
 
        return 0;
 
-err_egdev_cleanup:
-       tc_setup_cb_egdev_unregister(netdev, mlx5e_rep_setup_tc_cb_egdev,
-                                    upriv);
-
 err_neigh_cleanup:
        mlx5e_rep_neigh_cleanup(rpriv);
 
 err_detach_netdev:
        mlx5e_detach_netdev(netdev_priv(netdev));
 
+err_destroy_mdev_resources:
+       if (rep->vport == FDB_UPLINK_VPORT)
+               mlx5e_destroy_mdev_resources(dev);
+
 err_destroy_netdev:
        mlx5e_destroy_netdev(netdev_priv(netdev));
        kfree(rpriv);
@@ -1362,18 +1774,13 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch_rep *rep)
        struct mlx5e_rep_priv *rpriv = mlx5e_rep_to_rep_priv(rep);
        struct net_device *netdev = rpriv->netdev;
        struct mlx5e_priv *priv = netdev_priv(netdev);
-       struct mlx5e_rep_priv *uplink_rpriv;
        void *ppriv = priv->ppriv;
-       struct mlx5e_priv *upriv;
 
        unregister_netdev(netdev);
-       uplink_rpriv = mlx5_eswitch_get_uplink_priv(priv->mdev->priv.eswitch,
-                                                   REP_ETH);
-       upriv = netdev_priv(uplink_rpriv->netdev);
-       tc_setup_cb_egdev_unregister(netdev, mlx5e_rep_setup_tc_cb_egdev,
-                                    upriv);
        mlx5e_rep_neigh_cleanup(rpriv);
        mlx5e_detach_netdev(priv);
+       if (rep->vport == FDB_UPLINK_VPORT)
+               mlx5e_destroy_mdev_resources(priv->mdev);
        mlx5e_destroy_netdev(priv);
        kfree(ppriv); /* mlx5e_rep_priv */
 }
@@ -1387,14 +1794,13 @@ static void *mlx5e_vport_rep_get_proto_dev(struct mlx5_eswitch_rep *rep)
        return rpriv->netdev;
 }
 
-static void mlx5e_rep_register_vf_vports(struct mlx5e_priv *priv)
+void mlx5e_rep_register_vport_reps(struct mlx5_core_dev *mdev)
 {
-       struct mlx5_core_dev *mdev = priv->mdev;
-       struct mlx5_eswitch *esw   = mdev->priv.eswitch;
+       struct mlx5_eswitch *esw = mdev->priv.eswitch;
        int total_vfs = MLX5_TOTAL_VPORTS(mdev);
        int vport;
 
-       for (vport = 1; vport < total_vfs; vport++) {
+       for (vport = 0; vport < total_vfs; vport++) {
                struct mlx5_eswitch_rep_if rep_if = {};
 
                rep_if.load = mlx5e_vport_rep_load;
@@ -1404,55 +1810,12 @@ static void mlx5e_rep_register_vf_vports(struct mlx5e_priv *priv)
        }
 }
 
-static void mlx5e_rep_unregister_vf_vports(struct mlx5e_priv *priv)
+void mlx5e_rep_unregister_vport_reps(struct mlx5_core_dev *mdev)
 {
-       struct mlx5_core_dev *mdev = priv->mdev;
        struct mlx5_eswitch *esw = mdev->priv.eswitch;
        int total_vfs = MLX5_TOTAL_VPORTS(mdev);
        int vport;
 
-       for (vport = 1; vport < total_vfs; vport++)
+       for (vport = total_vfs - 1; vport >= 0; vport--)
                mlx5_eswitch_unregister_vport_rep(esw, vport, REP_ETH);
 }
-
-void mlx5e_register_vport_reps(struct mlx5e_priv *priv)
-{
-       struct mlx5_core_dev *mdev = priv->mdev;
-       struct mlx5_eswitch *esw   = mdev->priv.eswitch;
-       struct mlx5_eswitch_rep_if rep_if;
-       struct mlx5e_rep_priv *rpriv;
-
-       rpriv = priv->ppriv;
-       rpriv->netdev = priv->netdev;
-
-       rep_if.load = mlx5e_nic_rep_load;
-       rep_if.unload = mlx5e_nic_rep_unload;
-       rep_if.get_proto_dev = mlx5e_vport_rep_get_proto_dev;
-       rep_if.priv = rpriv;
-       INIT_LIST_HEAD(&rpriv->vport_sqs_list);
-       mlx5_eswitch_register_vport_rep(esw, 0, &rep_if, REP_ETH); /* UPLINK PF vport*/
-
-       mlx5e_rep_register_vf_vports(priv); /* VFs vports */
-}
-
-void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv)
-{
-       struct mlx5_core_dev *mdev = priv->mdev;
-       struct mlx5_eswitch *esw   = mdev->priv.eswitch;
-
-       mlx5e_rep_unregister_vf_vports(priv); /* VFs vports */
-       mlx5_eswitch_unregister_vport_rep(esw, 0, REP_ETH); /* UPLINK PF*/
-}
-
-void *mlx5e_alloc_nic_rep_priv(struct mlx5_core_dev *mdev)
-{
-       struct mlx5_eswitch *esw = mdev->priv.eswitch;
-       struct mlx5e_rep_priv *rpriv;
-
-       rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL);
-       if (!rpriv)
-               return NULL;
-
-       rpriv->rep = &esw->offloads.vport_reps[0];
-       return rpriv;
-}
index 844d32d..edd7228 100644 (file)
@@ -53,13 +53,33 @@ struct mlx5e_neigh_update_table {
        unsigned long           min_interval; /* jiffies */
 };
 
+struct mlx5_rep_uplink_priv {
+       /* Filters DB - instantiated by the uplink representor and shared by
+        * the uplink's VFs
+        */
+       struct rhashtable  tc_ht;
+
+       /* indirect block callbacks are invoked on bind/unbind events
+        * on registered higher level devices (e.g. tunnel devices)
+        *
+        * tc_indr_block_cb_priv_list is used to lookup indirect callback
+        * private data
+        *
+        * netdevice_nb is the netdev events notifier - used to register
+        * tunnel devices for block events
+        *
+        */
+       struct list_head            tc_indr_block_priv_list;
+       struct notifier_block       netdevice_nb;
+};
+
 struct mlx5e_rep_priv {
        struct mlx5_eswitch_rep *rep;
        struct mlx5e_neigh_update_table neigh_update;
        struct net_device      *netdev;
        struct mlx5_flow_handle *vport_rx_rule;
        struct list_head       vport_sqs_list;
-       struct rhashtable      tc_ht; /* valid for uplink rep */
+       struct mlx5_rep_uplink_priv uplink_priv; /* valid for uplink rep */
 };
 
 static inline
@@ -129,6 +149,8 @@ struct mlx5e_encap_entry {
 
        struct net_device *out_dev;
        int tunnel_type;
+       int tunnel_hlen;
+       int reformat_type;
        u8 flags;
        char *encap_header;
        int encap_size;
@@ -140,16 +162,12 @@ struct mlx5e_rep_sq {
 };
 
 void *mlx5e_alloc_nic_rep_priv(struct mlx5_core_dev *mdev);
-void mlx5e_register_vport_reps(struct mlx5e_priv *priv);
-void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv);
+void mlx5e_rep_register_vport_reps(struct mlx5_core_dev *mdev);
+void mlx5e_rep_unregister_vport_reps(struct mlx5_core_dev *mdev);
 bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv);
 int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv);
 void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv);
 
-int mlx5e_get_offload_stats(int attr_id, const struct net_device *dev, void *sp);
-bool mlx5e_has_offload_stats(const struct net_device *dev, int attr_id);
-
-int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr);
 void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe);
 
 int mlx5e_rep_encap_entry_attach(struct mlx5e_priv *priv,
@@ -158,12 +176,17 @@ void mlx5e_rep_encap_entry_detach(struct mlx5e_priv *priv,
                                  struct mlx5e_encap_entry *e);
 
 void mlx5e_rep_queue_neigh_stats_work(struct mlx5e_priv *priv);
+
+bool mlx5e_eswitch_rep(struct net_device *netdev);
+
 #else /* CONFIG_MLX5_ESWITCH */
-static inline void mlx5e_register_vport_reps(struct mlx5e_priv *priv) {}
-static inline void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv) {}
 static inline bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv) { return false; }
 static inline int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) { return 0; }
 static inline void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) {}
 #endif
 
+static inline bool mlx5e_is_vport_rep(struct mlx5e_priv *priv)
+{
+       return (MLX5_ESWITCH_MANAGER(priv->mdev) && priv->ppriv);
+}
 #endif /* __MLX5E_REP_H__ */
index 79638dc..cdce30a 100644 (file)
@@ -554,9 +554,9 @@ static inline void mlx5e_poll_ico_single_cqe(struct mlx5e_cq *cq,
 
        mlx5_cqwq_pop(&cq->wq);
 
-       if (unlikely((cqe->op_own >> 4) != MLX5_CQE_REQ)) {
+       if (unlikely(get_cqe_opcode(cqe) != MLX5_CQE_REQ)) {
                netdev_WARN_ONCE(cq->channel->netdev,
-                                "Bad OP in ICOSQ CQE: 0x%x\n", cqe->op_own);
+                                "Bad OP in ICOSQ CQE: 0x%x\n", get_cqe_opcode(cqe));
                return;
        }
 
@@ -724,9 +724,9 @@ static u32 mlx5e_get_fcs(const struct sk_buff *skb)
        return __get_unaligned_cpu32(fcs_bytes);
 }
 
-static u8 get_ip_proto(struct sk_buff *skb, __be16 proto)
+static u8 get_ip_proto(struct sk_buff *skb, int network_depth, __be16 proto)
 {
-       void *ip_p = skb->data + sizeof(struct ethhdr);
+       void *ip_p = skb->data + network_depth;
 
        return (proto == htons(ETH_P_IP)) ? ((struct iphdr *)ip_p)->protocol :
                                            ((struct ipv6hdr *)ip_p)->nexthdr;
@@ -755,7 +755,7 @@ static inline void mlx5e_handle_csum(struct net_device *netdev,
                goto csum_unnecessary;
 
        if (likely(is_last_ethertype_ip(skb, &network_depth, &proto))) {
-               if (unlikely(get_ip_proto(skb, proto) == IPPROTO_SCTP))
+               if (unlikely(get_ip_proto(skb, network_depth, proto) == IPPROTO_SCTP))
                        goto csum_unnecessary;
 
                skb->ip_summed = CHECKSUM_COMPLETE;
@@ -898,7 +898,7 @@ mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe,
        prefetchw(va); /* xdp_frame data area */
        prefetch(data);
 
-       if (unlikely((cqe->op_own >> 4) != MLX5_CQE_RESP_SEND)) {
+       if (unlikely(get_cqe_opcode(cqe) != MLX5_CQE_RESP_SEND)) {
                rq->stats->wqe_err++;
                return NULL;
        }
@@ -930,7 +930,7 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe,
        u16 byte_cnt     = cqe_bcnt - headlen;
        struct sk_buff *skb;
 
-       if (unlikely((cqe->op_own >> 4) != MLX5_CQE_RESP_SEND)) {
+       if (unlikely(get_cqe_opcode(cqe) != MLX5_CQE_RESP_SEND)) {
                rq->stats->wqe_err++;
                return NULL;
        }
@@ -1104,6 +1104,12 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
        u32 frag_size;
        bool consumed;
 
+       /* Check packet size. Note LRO doesn't use linear SKB */
+       if (unlikely(cqe_bcnt > rq->hw_mtu)) {
+               rq->stats->oversize_pkts_sw_drop++;
+               return NULL;
+       }
+
        va             = page_address(di->page) + head_offset;
        data           = va + rx_headroom;
        frag_size      = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt32);
@@ -1148,7 +1154,7 @@ void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
 
        wi->consumed_strides += cstrides;
 
-       if (unlikely((cqe->op_own >> 4) != MLX5_CQE_RESP_SEND)) {
+       if (unlikely(get_cqe_opcode(cqe) != MLX5_CQE_RESP_SEND)) {
                rq->stats->wqe_err++;
                goto mpwrq_cqe_out;
        }
@@ -1184,7 +1190,7 @@ mpwrq_cqe_out:
 int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
 {
        struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq);
-       struct mlx5e_xdpsq *xdpsq;
+       struct mlx5e_xdpsq *xdpsq = &rq->xdpsq;
        struct mlx5_cqe64 *cqe;
        int work_done = 0;
 
@@ -1195,10 +1201,11 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
                work_done += mlx5e_decompress_cqes_cont(rq, cq, 0, budget);
 
        cqe = mlx5_cqwq_get_cqe(&cq->wq);
-       if (!cqe)
+       if (!cqe) {
+               if (unlikely(work_done))
+                       goto out;
                return 0;
-
-       xdpsq = &rq->xdpsq;
+       }
 
        do {
                if (mlx5_get_cqe_format(cqe) == MLX5_COMPRESSED) {
@@ -1213,6 +1220,7 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
                rq->handle_rx_cqe(rq, cqe);
        } while ((++work_done < budget) && (cqe = mlx5_cqwq_get_cqe(&cq->wq)));
 
+out:
        if (xdpsq->doorbell) {
                mlx5e_xmit_xdp_doorbell(xdpsq);
                xdpsq->doorbell = false;
index 35ded91..4382ef8 100644 (file)
@@ -98,18 +98,17 @@ static int mlx5e_test_link_speed(struct mlx5e_priv *priv)
        return 1;
 }
 
-#ifdef CONFIG_INET
-/* loopback test */
-#define MLX5E_TEST_PKT_SIZE (MLX5E_RX_MAX_HEAD - NET_IP_ALIGN)
-static const char mlx5e_test_text[ETH_GSTRING_LEN] = "MLX5E SELF TEST";
-#define MLX5E_TEST_MAGIC 0x5AEED15C001ULL
-
 struct mlx5ehdr {
        __be32 version;
        __be64 magic;
-       char   text[ETH_GSTRING_LEN];
 };
 
+#ifdef CONFIG_INET
+/* loopback test */
+#define MLX5E_TEST_PKT_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) +\
+                            sizeof(struct udphdr) + sizeof(struct mlx5ehdr))
+#define MLX5E_TEST_MAGIC 0x5AEED15C001ULL
+
 static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv)
 {
        struct sk_buff *skb = NULL;
@@ -117,10 +116,7 @@ static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv)
        struct ethhdr *ethh;
        struct udphdr *udph;
        struct iphdr *iph;
-       int datalen, iplen;
-
-       datalen = MLX5E_TEST_PKT_SIZE -
-                 (sizeof(*ethh) + sizeof(*iph) + sizeof(*udph));
+       int    iplen;
 
        skb = netdev_alloc_skb(priv->netdev, MLX5E_TEST_PKT_SIZE);
        if (!skb) {
@@ -149,7 +145,7 @@ static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv)
        /* Fill UDP header */
        udph->source = htons(9);
        udph->dest = htons(9); /* Discard Protocol */
-       udph->len = htons(datalen + sizeof(struct udphdr));
+       udph->len = htons(sizeof(struct mlx5ehdr) + sizeof(struct udphdr));
        udph->check = 0;
 
        /* Fill IP header */
@@ -157,7 +153,8 @@ static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv)
        iph->ttl = 32;
        iph->version = 4;
        iph->protocol = IPPROTO_UDP;
-       iplen = sizeof(struct iphdr) + sizeof(struct udphdr) + datalen;
+       iplen = sizeof(struct iphdr) + sizeof(struct udphdr) +
+               sizeof(struct mlx5ehdr);
        iph->tot_len = htons(iplen);
        iph->frag_off = 0;
        iph->saddr = 0;
@@ -170,9 +167,6 @@ static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv)
        mlxh = skb_put(skb, sizeof(*mlxh));
        mlxh->version = 0;
        mlxh->magic = cpu_to_be64(MLX5E_TEST_MAGIC);
-       strlcpy(mlxh->text, mlx5e_test_text, sizeof(mlxh->text));
-       datalen -= sizeof(*mlxh);
-       skb_put_zero(skb, datalen);
 
        skb->csum = 0;
        skb->ip_summed = CHECKSUM_PARTIAL;
index 1e55b9c..d3fe48f 100644 (file)
@@ -30,6 +30,7 @@
  * SOFTWARE.
  */
 
+#include "lib/mlx5.h"
 #include "en.h"
 #include "en_accel/ipsec.h"
 #include "en_accel/tls.h"
@@ -74,7 +75,6 @@ static const struct counter_desc sw_stats_desc[] = {
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_recover) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_cqes) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_queue_wake) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_udp_seg_rem) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_cqe_err) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_xdp_xmit) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_xdp_full) },
@@ -83,6 +83,7 @@ static const struct counter_desc sw_stats_desc[] = {
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_wqe_err) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_mpwqe_filler_cqes) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_mpwqe_filler_strides) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_oversize_pkts_sw_drop) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_buff_alloc_err) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cqe_compress_blks) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cqe_compress_pkts) },
@@ -161,6 +162,7 @@ void mlx5e_grp_sw_update_stats(struct mlx5e_priv *priv)
                s->rx_wqe_err   += rq_stats->wqe_err;
                s->rx_mpwqe_filler_cqes    += rq_stats->mpwqe_filler_cqes;
                s->rx_mpwqe_filler_strides += rq_stats->mpwqe_filler_strides;
+               s->rx_oversize_pkts_sw_drop += rq_stats->oversize_pkts_sw_drop;
                s->rx_buff_alloc_err += rq_stats->buff_alloc_err;
                s->rx_cqe_compress_blks += rq_stats->cqe_compress_blks;
                s->rx_cqe_compress_pkts += rq_stats->cqe_compress_pkts;
@@ -196,7 +198,6 @@ void mlx5e_grp_sw_update_stats(struct mlx5e_priv *priv)
                        s->tx_nop               += sq_stats->nop;
                        s->tx_queue_stopped     += sq_stats->stopped;
                        s->tx_queue_wake        += sq_stats->wake;
-                       s->tx_udp_seg_rem       += sq_stats->udp_seg_rem;
                        s->tx_queue_dropped     += sq_stats->dropped;
                        s->tx_cqe_err           += sq_stats->cqe_err;
                        s->tx_recover           += sq_stats->recover;
@@ -480,7 +481,10 @@ static int mlx5e_grp_802_3_fill_stats(struct mlx5e_priv *priv, u64 *data,
        return idx;
 }
 
-static void mlx5e_grp_802_3_update_stats(struct mlx5e_priv *priv)
+#define MLX5_BASIC_PPCNT_SUPPORTED(mdev) \
+       (MLX5_CAP_GEN(mdev, pcam_reg) ? MLX5_CAP_PCAM_REG(mdev, ppcnt) : 1)
+
+void mlx5e_grp_802_3_update_stats(struct mlx5e_priv *priv)
 {
        struct mlx5e_pport_stats *pstats = &priv->stats.pport;
        struct mlx5_core_dev *mdev = priv->mdev;
@@ -488,6 +492,9 @@ static void mlx5e_grp_802_3_update_stats(struct mlx5e_priv *priv)
        int sz = MLX5_ST_SZ_BYTES(ppcnt_reg);
        void *out;
 
+       if (!MLX5_BASIC_PPCNT_SUPPORTED(mdev))
+               return;
+
        MLX5_SET(ppcnt_reg, in, local_port, 1);
        out = pstats->IEEE_802_3_counters;
        MLX5_SET(ppcnt_reg, in, grp, MLX5_IEEE_802_3_COUNTERS_GROUP);
@@ -600,6 +607,9 @@ static void mlx5e_grp_2819_update_stats(struct mlx5e_priv *priv)
        int sz = MLX5_ST_SZ_BYTES(ppcnt_reg);
        void *out;
 
+       if (!MLX5_BASIC_PPCNT_SUPPORTED(mdev))
+               return;
+
        MLX5_SET(ppcnt_reg, in, local_port, 1);
        out = pstats->RFC_2819_counters;
        MLX5_SET(ppcnt_reg, in, grp, MLX5_RFC_2819_COUNTERS_GROUP);
@@ -934,7 +944,7 @@ static const struct counter_desc pport_per_prio_pfc_stats_desc[] = {
 };
 
 static const struct counter_desc pport_pfc_stall_stats_desc[] = {
-       { "tx_pause_storm_warning_events ", PPORT_PER_PRIO_OFF(device_stall_minor_watermark_cnt) },
+       { "tx_pause_storm_warning_events", PPORT_PER_PRIO_OFF(device_stall_minor_watermark_cnt) },
        { "tx_pause_storm_error_events", PPORT_PER_PRIO_OFF(device_stall_critical_watermark_cnt) },
 };
 
@@ -1075,6 +1085,9 @@ static void mlx5e_grp_per_prio_update_stats(struct mlx5e_priv *priv)
        int prio;
        void *out;
 
+       if (!MLX5_BASIC_PPCNT_SUPPORTED(mdev))
+               return;
+
        MLX5_SET(ppcnt_reg, in, local_port, 1);
        MLX5_SET(ppcnt_reg, in, grp, MLX5_PER_PRIORITY_COUNTERS_GROUP);
        for (prio = 0; prio < NUM_PPORT_PRIO; prio++) {
@@ -1086,13 +1099,13 @@ static void mlx5e_grp_per_prio_update_stats(struct mlx5e_priv *priv)
 }
 
 static const struct counter_desc mlx5e_pme_status_desc[] = {
-       { "module_unplug", 8 },
+       { "module_unplug",       sizeof(u64) * MLX5_MODULE_STATUS_UNPLUGGED },
 };
 
 static const struct counter_desc mlx5e_pme_error_desc[] = {
-       { "module_bus_stuck", 16 },       /* bus stuck (I2C or data shorted) */
-       { "module_high_temp", 48 },       /* high temperature */
-       { "module_bad_shorted", 56 },    /* bad or shorted cable/module */
+       { "module_bus_stuck",    sizeof(u64) * MLX5_MODULE_EVENT_ERROR_BUS_STUCK },
+       { "module_high_temp",    sizeof(u64) * MLX5_MODULE_EVENT_ERROR_HIGH_TEMPERATURE },
+       { "module_bad_shorted",  sizeof(u64) * MLX5_MODULE_EVENT_ERROR_BAD_CABLE },
 };
 
 #define NUM_PME_STATUS_STATS           ARRAY_SIZE(mlx5e_pme_status_desc)
@@ -1120,15 +1133,17 @@ static int mlx5e_grp_pme_fill_strings(struct mlx5e_priv *priv, u8 *data,
 static int mlx5e_grp_pme_fill_stats(struct mlx5e_priv *priv, u64 *data,
                                    int idx)
 {
-       struct mlx5_priv *mlx5_priv = &priv->mdev->priv;
+       struct mlx5_pme_stats pme_stats;
        int i;
 
+       mlx5_get_pme_stats(priv->mdev, &pme_stats);
+
        for (i = 0; i < NUM_PME_STATUS_STATS; i++)
-               data[idx++] = MLX5E_READ_CTR64_CPU(mlx5_priv->pme_stats.status_counters,
+               data[idx++] = MLX5E_READ_CTR64_CPU(pme_stats.status_counters,
                                                   mlx5e_pme_status_desc, i);
 
        for (i = 0; i < NUM_PME_ERR_STATS; i++)
-               data[idx++] = MLX5E_READ_CTR64_CPU(mlx5_priv->pme_stats.error_counters,
+               data[idx++] = MLX5E_READ_CTR64_CPU(pme_stats.error_counters,
                                                   mlx5e_pme_error_desc, i);
 
        return idx;
@@ -1189,6 +1204,7 @@ static const struct counter_desc rq_stats_desc[] = {
        { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, wqe_err) },
        { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, mpwqe_filler_cqes) },
        { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, mpwqe_filler_strides) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, oversize_pkts_sw_drop) },
        { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, buff_alloc_err) },
        { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cqe_compress_blks) },
        { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cqe_compress_pkts) },
index 77f74ce..fe91ec0 100644 (file)
@@ -87,7 +87,6 @@ struct mlx5e_sw_stats {
        u64 tx_recover;
        u64 tx_cqes;
        u64 tx_queue_wake;
-       u64 tx_udp_seg_rem;
        u64 tx_cqe_err;
        u64 tx_xdp_xmit;
        u64 tx_xdp_full;
@@ -96,6 +95,7 @@ struct mlx5e_sw_stats {
        u64 rx_wqe_err;
        u64 rx_mpwqe_filler_cqes;
        u64 rx_mpwqe_filler_strides;
+       u64 rx_oversize_pkts_sw_drop;
        u64 rx_buff_alloc_err;
        u64 rx_cqe_compress_blks;
        u64 rx_cqe_compress_pkts;
@@ -193,6 +193,7 @@ struct mlx5e_rq_stats {
        u64 wqe_err;
        u64 mpwqe_filler_cqes;
        u64 mpwqe_filler_strides;
+       u64 oversize_pkts_sw_drop;
        u64 buff_alloc_err;
        u64 cqe_compress_blks;
        u64 cqe_compress_pkts;
@@ -219,7 +220,6 @@ struct mlx5e_sq_stats {
        u64 csum_partial_inner;
        u64 added_vlan_packets;
        u64 nop;
-       u64 udp_seg_rem;
 #ifdef CONFIG_MLX5_EN_TLS
        u64 tls_ooo;
        u64 tls_resync_bytes;
@@ -278,5 +278,6 @@ extern const struct mlx5e_stats_grp mlx5e_stats_grps[];
 extern const int mlx5e_num_stats_grps;
 
 void mlx5e_grp_sw_update_stats(struct mlx5e_priv *priv);
+void mlx5e_grp_802_3_update_stats(struct mlx5e_priv *priv);
 
 #endif /* __MLX5_EN_STATS_H__ */
index 608025c..4d6c975 100644 (file)
 #include <net/tc_act/tc_tunnel_key.h>
 #include <net/tc_act/tc_pedit.h>
 #include <net/tc_act/tc_csum.h>
-#include <net/vxlan.h>
 #include <net/arp.h>
 #include "en.h"
 #include "en_rep.h"
 #include "en_tc.h"
 #include "eswitch.h"
-#include "lib/vxlan.h"
 #include "fs_core.h"
 #include "en/port.h"
+#include "en/tc_tun.h"
+#include "lib/devcom.h"
 
 struct mlx5_nic_flow_attr {
        u32 action;
@@ -69,25 +69,54 @@ struct mlx5_nic_flow_attr {
 enum {
        MLX5E_TC_FLOW_INGRESS   = MLX5E_TC_INGRESS,
        MLX5E_TC_FLOW_EGRESS    = MLX5E_TC_EGRESS,
-       MLX5E_TC_FLOW_ESWITCH   = BIT(MLX5E_TC_FLOW_BASE),
-       MLX5E_TC_FLOW_NIC       = BIT(MLX5E_TC_FLOW_BASE + 1),
-       MLX5E_TC_FLOW_OFFLOADED = BIT(MLX5E_TC_FLOW_BASE + 2),
-       MLX5E_TC_FLOW_HAIRPIN   = BIT(MLX5E_TC_FLOW_BASE + 3),
-       MLX5E_TC_FLOW_HAIRPIN_RSS = BIT(MLX5E_TC_FLOW_BASE + 4),
-       MLX5E_TC_FLOW_SLOW        = BIT(MLX5E_TC_FLOW_BASE + 5),
+       MLX5E_TC_FLOW_ESWITCH   = MLX5E_TC_ESW_OFFLOAD,
+       MLX5E_TC_FLOW_NIC       = MLX5E_TC_NIC_OFFLOAD,
+       MLX5E_TC_FLOW_OFFLOADED = BIT(MLX5E_TC_FLOW_BASE),
+       MLX5E_TC_FLOW_HAIRPIN   = BIT(MLX5E_TC_FLOW_BASE + 1),
+       MLX5E_TC_FLOW_HAIRPIN_RSS = BIT(MLX5E_TC_FLOW_BASE + 2),
+       MLX5E_TC_FLOW_SLOW        = BIT(MLX5E_TC_FLOW_BASE + 3),
+       MLX5E_TC_FLOW_DUP         = BIT(MLX5E_TC_FLOW_BASE + 4),
 };
 
 #define MLX5E_TC_MAX_SPLITS 1
 
+/* Helper struct for accessing a struct containing list_head array.
+ * Containing struct
+ *   |- Helper array
+ *      [0] Helper item 0
+ *          |- list_head item 0
+ *          |- index (0)
+ *      [1] Helper item 1
+ *          |- list_head item 1
+ *          |- index (1)
+ * To access the containing struct from one of the list_head items:
+ * 1. Get the helper item from the list_head item using
+ *    helper item =
+ *        container_of(list_head item, helper struct type, list_head field)
+ * 2. Get the contining struct from the helper item and its index in the array:
+ *    containing struct =
+ *        container_of(helper item, containing struct type, helper field[index])
+ */
+struct encap_flow_item {
+       struct list_head list;
+       int index;
+};
+
 struct mlx5e_tc_flow {
        struct rhash_head       node;
        struct mlx5e_priv       *priv;
        u64                     cookie;
        u16                     flags;
        struct mlx5_flow_handle *rule[MLX5E_TC_MAX_SPLITS + 1];
-       struct list_head        encap;   /* flows sharing the same encap ID */
+       /* Flow can be associated with multiple encap IDs.
+        * The number of encaps is bounded by the number of supported
+        * destinations.
+        */
+       struct encap_flow_item encaps[MLX5_MAX_FLOW_FWD_VPORTS];
+       struct mlx5e_tc_flow    *peer_flow;
        struct list_head        mod_hdr; /* flows sharing the same mod hdr ID */
        struct list_head        hairpin; /* flows sharing the same hairpin */
+       struct list_head        peer;    /* flows with peer flow */
        union {
                struct mlx5_esw_flow_attr esw_attr[0];
                struct mlx5_nic_flow_attr nic_attr[0];
@@ -95,11 +124,12 @@ struct mlx5e_tc_flow {
 };
 
 struct mlx5e_tc_flow_parse_attr {
-       struct ip_tunnel_info tun_info;
+       struct ip_tunnel_info tun_info[MLX5_MAX_FLOW_FWD_VPORTS];
+       struct net_device *filter_dev;
        struct mlx5_flow_spec spec;
        int num_mod_hdr_actions;
        void *mod_hdr_actions;
-       int mirred_ifindex;
+       int mirred_ifindex[MLX5_MAX_FLOW_FWD_VPORTS];
 };
 
 #define MLX5E_TC_TABLE_NUM_GROUPS 4
@@ -316,7 +346,7 @@ static void mlx5e_hairpin_fill_rqt_rqns(struct mlx5e_hairpin *hp, void *rqtc)
 
        for (i = 0; i < sz; i++) {
                ix = i;
-               if (priv->channels.params.rss_hfunc == ETH_RSS_HASH_XOR)
+               if (priv->rss_params.hfunc == ETH_RSS_HASH_XOR)
                        ix = mlx5e_bits_invert(i, ilog2(sz));
                ix = indirection_rqt[ix];
                rqn = hp->pair->rqn[ix];
@@ -360,13 +390,15 @@ static int mlx5e_hairpin_create_indirect_tirs(struct mlx5e_hairpin *hp)
        void *tirc;
 
        for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
+               struct mlx5e_tirc_config ttconfig = mlx5e_tirc_get_default_config(tt);
+
                memset(in, 0, MLX5_ST_SZ_BYTES(create_tir_in));
                tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
 
                MLX5_SET(tirc, tirc, transport_domain, hp->tdn);
                MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT);
                MLX5_SET(tirc, tirc, indirect_table, hp->indir_rqt.rqtn);
-               mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, false);
+               mlx5e_build_indir_tir_ctx_hash(&priv->rss_params, &ttconfig, tirc, false);
 
                err = mlx5_core_create_tir(hp->func_mdev, in,
                                           MLX5_ST_SZ_BYTES(create_tir_in), &hp->indir_tirn[tt]);
@@ -569,7 +601,7 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv,
                                  struct mlx5e_tc_flow_parse_attr *parse_attr,
                                  struct netlink_ext_ack *extack)
 {
-       int peer_ifindex = parse_attr->mirred_ifindex;
+       int peer_ifindex = parse_attr->mirred_ifindex[0];
        struct mlx5_hairpin_params params;
        struct mlx5_core_dev *peer_mdev;
        struct mlx5e_hairpin_entry *hpe;
@@ -802,7 +834,7 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
        mlx5_del_flow_rules(flow->rule[0]);
        mlx5_fc_destroy(priv->mdev, counter);
 
-       if (!mlx5e_tc_num_filters(priv) && priv->fs.tc.t) {
+       if (!mlx5e_tc_num_filters(priv, MLX5E_TC_NIC_OFFLOAD)  && priv->fs.tc.t) {
                mlx5_destroy_flow_table(priv->fs.tc.t);
                priv->fs.tc.t = NULL;
        }
@@ -815,14 +847,15 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
 }
 
 static void mlx5e_detach_encap(struct mlx5e_priv *priv,
-                              struct mlx5e_tc_flow *flow);
+                              struct mlx5e_tc_flow *flow, int out_index);
 
 static int mlx5e_attach_encap(struct mlx5e_priv *priv,
                              struct ip_tunnel_info *tun_info,
                              struct net_device *mirred_dev,
                              struct net_device **encap_dev,
                              struct mlx5e_tc_flow *flow,
-                             struct netlink_ext_ack *extack);
+                             struct netlink_ext_ack *extack,
+                             int out_index);
 
 static struct mlx5_flow_handle *
 mlx5e_tc_offload_fdb_rules(struct mlx5_eswitch *esw,
@@ -836,7 +869,7 @@ mlx5e_tc_offload_fdb_rules(struct mlx5_eswitch *esw,
        if (IS_ERR(rule))
                return rule;
 
-       if (attr->mirror_count) {
+       if (attr->split_count) {
                flow->rule[1] = mlx5_eswitch_add_fwd_rule(esw, spec, attr);
                if (IS_ERR(flow->rule[1])) {
                        mlx5_eswitch_del_offloaded_rule(esw, rule, attr);
@@ -855,7 +888,7 @@ mlx5e_tc_unoffload_fdb_rules(struct mlx5_eswitch *esw,
 {
        flow->flags &= ~MLX5E_TC_FLOW_OFFLOADED;
 
-       if (attr->mirror_count)
+       if (attr->split_count)
                mlx5_eswitch_del_fwd_rule(esw, flow->rule[1], attr);
 
        mlx5_eswitch_del_offloaded_rule(esw, flow->rule[0], attr);
@@ -870,9 +903,9 @@ mlx5e_tc_offload_to_slow_path(struct mlx5_eswitch *esw,
        struct mlx5_flow_handle *rule;
 
        memcpy(slow_attr, flow->esw_attr, sizeof(*slow_attr));
-       slow_attr->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
-       slow_attr->mirror_count = 0,
-       slow_attr->dest_chain = FDB_SLOW_PATH_CHAIN,
+       slow_attr->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+       slow_attr->split_count = 0;
+       slow_attr->dest_chain = FDB_SLOW_PATH_CHAIN;
 
        rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, slow_attr);
        if (!IS_ERR(rule))
@@ -887,6 +920,9 @@ mlx5e_tc_unoffload_from_slow_path(struct mlx5_eswitch *esw,
                                  struct mlx5_esw_flow_attr *slow_attr)
 {
        memcpy(slow_attr, flow->esw_attr, sizeof(*slow_attr));
+       slow_attr->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+       slow_attr->split_count = 0;
+       slow_attr->dest_chain = FDB_SLOW_PATH_CHAIN;
        mlx5e_tc_unoffload_fdb_rules(esw, flow, slow_attr);
        flow->flags &= ~MLX5E_TC_FLOW_SLOW;
 }
@@ -906,12 +942,12 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
        struct mlx5e_rep_priv *rpriv;
        struct mlx5e_priv *out_priv;
        int err = 0, encap_err = 0;
+       int out_index;
 
-       /* if prios are not supported, keep the old behaviour of using same prio
-        * for all offloaded rules.
-        */
-       if (!mlx5_eswitch_prios_supported(esw))
-               attr->prio = 1;
+       if (!mlx5_eswitch_prios_supported(esw) && attr->prio != 1) {
+               NL_SET_ERR_MSG(extack, "E-switch priorities unsupported, upgrade FW");
+               return -EOPNOTSUPP;
+       }
 
        if (attr->chain > max_chain) {
                NL_SET_ERR_MSG(extack, "Requested chain is out of supported range");
@@ -925,20 +961,27 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
                goto err_max_prio_chain;
        }
 
-       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT) {
+       for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) {
+               int mirred_ifindex;
+
+               if (!(attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP))
+                       continue;
+
+               mirred_ifindex = attr->parse_attr->mirred_ifindex[out_index];
                out_dev = __dev_get_by_index(dev_net(priv->netdev),
-                                            attr->parse_attr->mirred_ifindex);
-               encap_err = mlx5e_attach_encap(priv, &parse_attr->tun_info,
-                                              out_dev, &encap_dev, flow,
-                                              extack);
-               if (encap_err && encap_err != -EAGAIN) {
-                       err = encap_err;
+                                            mirred_ifindex);
+               err = mlx5e_attach_encap(priv,
+                                        &parse_attr->tun_info[out_index],
+                                        out_dev, &encap_dev, flow,
+                                        extack, out_index);
+               if (err && err != -EAGAIN)
                        goto err_attach_encap;
-               }
+               if (err == -EAGAIN)
+                       encap_err = err;
                out_priv = netdev_priv(encap_dev);
                rpriv = out_priv->ppriv;
-               attr->out_rep[attr->out_count] = rpriv->rep;
-               attr->out_mdev[attr->out_count++] = out_priv->mdev;
+               attr->dests[out_index].rep = rpriv->rep;
+               attr->dests[out_index].mdev = out_priv->mdev;
        }
 
        err = mlx5_eswitch_add_vlan_action(esw, attr);
@@ -953,7 +996,7 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
        }
 
        if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
-               counter = mlx5_fc_create(esw->dev, true);
+               counter = mlx5_fc_create(attr->counter_dev, true);
                if (IS_ERR(counter)) {
                        err = PTR_ERR(counter);
                        goto err_create_counter;
@@ -982,15 +1025,16 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
        return 0;
 
 err_add_rule:
-       mlx5_fc_destroy(esw->dev, counter);
+       mlx5_fc_destroy(attr->counter_dev, counter);
 err_create_counter:
        if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
                mlx5e_detach_mod_hdr(priv, flow);
 err_mod_hdr:
        mlx5_eswitch_del_vlan_action(esw, attr);
 err_add_vlan:
-       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT)
-               mlx5e_detach_encap(priv, flow);
+       for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++)
+               if (attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP)
+                       mlx5e_detach_encap(priv, flow, out_index);
 err_attach_encap:
 err_max_prio_chain:
        return err;
@@ -1002,6 +1046,7 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        struct mlx5_esw_flow_attr *attr = flow->esw_attr;
        struct mlx5_esw_flow_attr slow_attr;
+       int out_index;
 
        if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) {
                if (flow->flags & MLX5E_TC_FLOW_SLOW)
@@ -1012,16 +1057,16 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
 
        mlx5_eswitch_del_vlan_action(esw, attr);
 
-       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT) {
-               mlx5e_detach_encap(priv, flow);
-               kvfree(attr->parse_attr);
-       }
+       for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++)
+               if (attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP)
+                       mlx5e_detach_encap(priv, flow, out_index);
+       kvfree(attr->parse_attr);
 
        if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
                mlx5e_detach_mod_hdr(priv, flow);
 
        if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT)
-               mlx5_fc_destroy(esw->dev, attr->counter);
+               mlx5_fc_destroy(attr->counter_dev, attr->counter);
 }
 
 void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv,
@@ -1031,10 +1076,12 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv,
        struct mlx5_esw_flow_attr slow_attr, *esw_attr;
        struct mlx5_flow_handle *rule;
        struct mlx5_flow_spec *spec;
+       struct encap_flow_item *efi;
        struct mlx5e_tc_flow *flow;
        int err;
 
-       err = mlx5_packet_reformat_alloc(priv->mdev, e->tunnel_type,
+       err = mlx5_packet_reformat_alloc(priv->mdev,
+                                        e->reformat_type,
                                         e->encap_size, e->encap_header,
                                         MLX5_FLOW_NAMESPACE_FDB,
                                         &e->encap_id);
@@ -1046,11 +1093,31 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv,
        e->flags |= MLX5_ENCAP_ENTRY_VALID;
        mlx5e_rep_queue_neigh_stats_work(priv);
 
-       list_for_each_entry(flow, &e->flows, encap) {
+       list_for_each_entry(efi, &e->flows, list) {
+               bool all_flow_encaps_valid = true;
+               int i;
+
+               flow = container_of(efi, struct mlx5e_tc_flow, encaps[efi->index]);
                esw_attr = flow->esw_attr;
-               esw_attr->encap_id = e->encap_id;
                spec = &esw_attr->parse_attr->spec;
 
+               esw_attr->dests[efi->index].encap_id = e->encap_id;
+               esw_attr->dests[efi->index].flags |= MLX5_ESW_DEST_ENCAP_VALID;
+               /* Flow can be associated with multiple encap entries.
+                * Before offloading the flow verify that all of them have
+                * a valid neighbour.
+                */
+               for (i = 0; i < MLX5_MAX_FLOW_FWD_VPORTS; i++) {
+                       if (!(esw_attr->dests[i].flags & MLX5_ESW_DEST_ENCAP))
+                               continue;
+                       if (!(esw_attr->dests[i].flags & MLX5_ESW_DEST_ENCAP_VALID)) {
+                               all_flow_encaps_valid = false;
+                               break;
+                       }
+               }
+               /* Do not offload flows with unresolved neighbors */
+               if (!all_flow_encaps_valid)
+                       continue;
                /* update from slow path rule to encap rule */
                rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, esw_attr);
                if (IS_ERR(rule)) {
@@ -1073,14 +1140,18 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv,
        struct mlx5_esw_flow_attr slow_attr;
        struct mlx5_flow_handle *rule;
        struct mlx5_flow_spec *spec;
+       struct encap_flow_item *efi;
        struct mlx5e_tc_flow *flow;
        int err;
 
-       list_for_each_entry(flow, &e->flows, encap) {
+       list_for_each_entry(efi, &e->flows, list) {
+               flow = container_of(efi, struct mlx5e_tc_flow, encaps[efi->index]);
                spec = &flow->esw_attr->parse_attr->spec;
 
                /* update from encap rule to slow path rule */
                rule = mlx5e_tc_offload_to_slow_path(esw, flow, spec, &slow_attr);
+               /* mark the flow's encap dest as non-valid */
+               flow->esw_attr->dests[efi->index].flags &= ~MLX5_ESW_DEST_ENCAP_VALID;
 
                if (IS_ERR(rule)) {
                        err = PTR_ERR(rule);
@@ -1094,10 +1165,9 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv,
                flow->rule[0] = rule;
        }
 
-       if (e->flags & MLX5_ENCAP_ENTRY_VALID) {
-               e->flags &= ~MLX5_ENCAP_ENTRY_VALID;
-               mlx5_packet_reformat_dealloc(priv->mdev, e->encap_id);
-       }
+       /* we know that the encap is valid */
+       e->flags &= ~MLX5_ENCAP_ENTRY_VALID;
+       mlx5_packet_reformat_dealloc(priv->mdev, e->encap_id);
 }
 
 static struct mlx5_fc *mlx5e_tc_get_counter(struct mlx5e_tc_flow *flow)
@@ -1129,9 +1199,12 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
                return;
 
        list_for_each_entry(e, &nhe->encap_list, encap_list) {
+               struct encap_flow_item *efi;
                if (!(e->flags & MLX5_ENCAP_ENTRY_VALID))
                        continue;
-               list_for_each_entry(flow, &e->flows, encap) {
+               list_for_each_entry(efi, &e->flows, list) {
+                       flow = container_of(efi, struct mlx5e_tc_flow,
+                                           encaps[efi->index]);
                        if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) {
                                counter = mlx5e_tc_get_counter(flow);
                                mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse);
@@ -1161,11 +1234,11 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
 }
 
 static void mlx5e_detach_encap(struct mlx5e_priv *priv,
-                              struct mlx5e_tc_flow *flow)
+                              struct mlx5e_tc_flow *flow, int out_index)
 {
-       struct list_head *next = flow->encap.next;
+       struct list_head *next = flow->encaps[out_index].list.next;
 
-       list_del(&flow->encap);
+       list_del(&flow->encaps[out_index].list);
        if (list_empty(next)) {
                struct mlx5e_encap_entry *e;
 
@@ -1181,49 +1254,55 @@ static void mlx5e_detach_encap(struct mlx5e_priv *priv,
        }
 }
 
-static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
-                             struct mlx5e_tc_flow *flow)
+static void __mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow)
 {
-       if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
-               mlx5e_tc_del_fdb_flow(priv, flow);
-       else
-               mlx5e_tc_del_nic_flow(priv, flow);
+       struct mlx5_eswitch *esw = flow->priv->mdev->priv.eswitch;
+
+       if (!(flow->flags & MLX5E_TC_FLOW_ESWITCH) ||
+           !(flow->flags & MLX5E_TC_FLOW_DUP))
+               return;
+
+       mutex_lock(&esw->offloads.peer_mutex);
+       list_del(&flow->peer);
+       mutex_unlock(&esw->offloads.peer_mutex);
+
+       flow->flags &= ~MLX5E_TC_FLOW_DUP;
+
+       mlx5e_tc_del_fdb_flow(flow->peer_flow->priv, flow->peer_flow);
+       kvfree(flow->peer_flow);
+       flow->peer_flow = NULL;
 }
 
-static void parse_vxlan_attr(struct mlx5_flow_spec *spec,
-                            struct tc_cls_flower_offload *f)
+static void mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow)
 {
-       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);
-       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 mlx5_core_dev *dev = flow->priv->mdev;
+       struct mlx5_devcom *devcom = dev->priv.devcom;
+       struct mlx5_eswitch *peer_esw;
 
-       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);
+       peer_esw = mlx5_devcom_get_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
+       if (!peer_esw)
+               return;
 
-       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);
-               MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
-                        be32_to_cpu(mask->keyid));
-               MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
-                        be32_to_cpu(key->keyid));
+       __mlx5e_tc_del_fdb_peer_flow(flow);
+       mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
+}
+
+static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
+                             struct mlx5e_tc_flow *flow)
+{
+       if (flow->flags & MLX5E_TC_FLOW_ESWITCH) {
+               mlx5e_tc_del_fdb_peer_flow(flow);
+               mlx5e_tc_del_fdb_flow(priv, flow);
+       } else {
+               mlx5e_tc_del_nic_flow(priv, flow);
        }
 }
 
+
 static int parse_tunnel_attr(struct mlx5e_priv *priv,
                             struct mlx5_flow_spec *spec,
-                            struct tc_cls_flower_offload *f)
+                            struct tc_cls_flower_offload *f,
+                            struct net_device *filter_dev)
 {
        struct netlink_ext_ack *extack = f->common.extack;
        void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
@@ -1235,48 +1314,14 @@ static int parse_tunnel_attr(struct mlx5e_priv *priv,
                skb_flow_dissector_target(f->dissector,
                                          FLOW_DISSECTOR_KEY_ENC_CONTROL,
                                          f->key);
+       int err = 0;
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) {
-               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);
-
-               /* Full udp dst port must be given */
-               if (memchr_inv(&mask->dst, 0xff, sizeof(mask->dst)))
-                       goto vxlan_match_offload_err;
-
-               if (mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(key->dst)) &&
-                   MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap))
-                       parse_vxlan_attr(spec, f);
-               else {
-                       NL_SET_ERR_MSG_MOD(extack,
-                                          "port isn't an offloaded vxlan udp dport");
-                       netdev_warn(priv->netdev,
-                                   "%d isn't an offloaded vxlan udp dport\n", be16_to_cpu(key->dst));
-                       return -EOPNOTSUPP;
-               }
-
-               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_sport, ntohs(mask->src));
-               MLX5_SET(fte_match_set_lyr_2_4, headers_v,
-                        udp_sport, ntohs(key->src));
-       } else { /* udp dst port must be given */
-vxlan_match_offload_err:
+       err = mlx5e_tc_tun_parse(filter_dev, priv, spec, f,
+                                headers_c, headers_v);
+       if (err) {
                NL_SET_ERR_MSG_MOD(extack,
-                                  "IP tunnel decap offload supported only for vxlan, must set UDP dport");
-               netdev_warn(priv->netdev,
-                           "IP tunnel decap offload supported only for vxlan, must set UDP dport\n");
-               return -EOPNOTSUPP;
+                                  "failed to parse tunnel attributes");
+               return err;
        }
 
        if (enc_control->addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
@@ -1380,6 +1425,7 @@ vxlan_match_offload_err:
 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)
 {
        struct netlink_ext_ack *extack = f->common.extack;
@@ -1431,7 +1477,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                switch (key->addr_type) {
                case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
                case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
-                       if (parse_tunnel_attr(priv, spec, f))
+                       if (parse_tunnel_attr(priv, spec, f, filter_dev))
                                return -EOPNOTSUPP;
                        break;
                default:
@@ -1447,31 +1493,21 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                                         inner_headers);
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
-               struct flow_dissector_key_eth_addrs *key =
+       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_ETH_ADDRS,
+                                                 FLOW_DISSECTOR_KEY_BASIC,
                                                  f->key);
-               struct flow_dissector_key_eth_addrs *mask =
+               struct flow_dissector_key_basic *mask =
                        skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
+                                                 FLOW_DISSECTOR_KEY_BASIC,
                                                  f->mask);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype,
+                        ntohs(mask->n_proto));
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype,
+                        ntohs(key->n_proto));
 
-               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
-                                            dmac_47_16),
-                               mask->dst);
-               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
-                                            dmac_47_16),
-                               key->dst);
-
-               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
-                                            smac_47_16),
-                               mask->src);
-               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
-                                            smac_47_16),
-                               key->src);
-
-               if (!is_zero_ether_addr(mask->src) || !is_zero_ether_addr(mask->dst))
+               if (mask->n_proto)
                        *match_level = MLX5_MATCH_L2;
        }
 
@@ -1505,9 +1541,10 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
 
                        *match_level = MLX5_MATCH_L2;
                }
-       } else {
+       } else if (*match_level != MLX5_MATCH_NONE) {
                MLX5_SET(fte_match_set_lyr_2_4, headers_c, svlan_tag, 1);
                MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1);
+               *match_level = MLX5_MATCH_L2;
        }
 
        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CVLAN)) {
@@ -1545,21 +1582,31 @@ 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 =
+       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_BASIC,
+                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
                                                  f->key);
-               struct flow_dissector_key_basic *mask =
+               struct flow_dissector_key_eth_addrs *mask =
                        skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
+                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
                                                  f->mask);
-               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype,
-                        ntohs(mask->n_proto));
-               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype,
-                        ntohs(key->n_proto));
 
-               if (mask->n_proto)
+               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
+                                            dmac_47_16),
+                               mask->dst);
+               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
+                                            dmac_47_16),
+                               key->dst);
+
+               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
+                                            smac_47_16),
+                               mask->src);
+               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
+                                            smac_47_16),
+                               key->src);
+
+               if (!is_zero_ether_addr(mask->src) || !is_zero_ether_addr(mask->dst))
                        *match_level = MLX5_MATCH_L2;
        }
 
@@ -1586,10 +1633,10 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
 
                        /* the HW doesn't need L3 inline to match on frag=no */
                        if (!(key->flags & FLOW_DIS_IS_FRAGMENT))
-                               *match_level = MLX5_INLINE_MODE_L2;
+                               *match_level = MLX5_MATCH_L2;
        /* ***  L2 attributes parsing up to here *** */
                        else
-                               *match_level = MLX5_INLINE_MODE_IP;
+                               *match_level = MLX5_MATCH_L3;
                }
        }
 
@@ -1772,7 +1819,8 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
 static int parse_cls_flower(struct mlx5e_priv *priv,
                            struct mlx5e_tc_flow *flow,
                            struct mlx5_flow_spec *spec,
-                           struct tc_cls_flower_offload *f)
+                           struct tc_cls_flower_offload *f,
+                           struct net_device *filter_dev)
 {
        struct netlink_ext_ack *extack = f->common.extack;
        struct mlx5_core_dev *dev = priv->mdev;
@@ -1782,7 +1830,7 @@ static int parse_cls_flower(struct mlx5e_priv *priv,
        u8 match_level;
        int err;
 
-       err = __parse_cls_flower(priv, spec, f, &match_level);
+       err = __parse_cls_flower(priv, spec, f, filter_dev, &match_level);
 
        if (!err && (flow->flags & MLX5E_TC_FLOW_ESWITCH)) {
                rep = rpriv->rep;
@@ -2267,7 +2315,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
 
                        if (priv->netdev->netdev_ops == peer_dev->netdev_ops &&
                            same_hw_devs(priv, netdev_priv(peer_dev))) {
-                               parse_attr->mirred_ifindex = peer_dev->ifindex;
+                               parse_attr->mirred_ifindex[0] = peer_dev->ifindex;
                                flow->flags |= MLX5E_TC_FLOW_HAIRPIN;
                                action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
                                          MLX5_FLOW_CONTEXT_ACTION_COUNT;
@@ -2316,45 +2364,6 @@ static inline int hash_encap_info(struct ip_tunnel_key *key)
        return jhash(key, sizeof(*key), 0);
 }
 
-static int mlx5e_route_lookup_ipv4(struct mlx5e_priv *priv,
-                                  struct net_device *mirred_dev,
-                                  struct net_device **out_dev,
-                                  struct flowi4 *fl4,
-                                  struct neighbour **out_n,
-                                  u8 *out_ttl)
-{
-       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
-       struct mlx5e_rep_priv *uplink_rpriv;
-       struct rtable *rt;
-       struct neighbour *n = NULL;
-
-#if IS_ENABLED(CONFIG_INET)
-       int ret;
-
-       rt = ip_route_output_key(dev_net(mirred_dev), fl4);
-       ret = PTR_ERR_OR_ZERO(rt);
-       if (ret)
-               return ret;
-#else
-       return -EOPNOTSUPP;
-#endif
-       uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
-       /* if the egress device isn't on the same HW e-switch, we use the uplink */
-       if (!switchdev_port_same_parent_id(priv->netdev, rt->dst.dev))
-               *out_dev = uplink_rpriv->netdev;
-       else
-               *out_dev = rt->dst.dev;
-
-       if (!(*out_ttl))
-               *out_ttl = ip4_dst_hoplimit(&rt->dst);
-       n = dst_neigh_lookup(&rt->dst, &fl4->daddr);
-       ip_rt_put(rt);
-       if (!n)
-               return -ENOMEM;
-
-       *out_n = n;
-       return 0;
-}
 
 static bool is_merged_eswitch_dev(struct mlx5e_priv *priv,
                                  struct net_device *peer_netdev)
@@ -2370,377 +2379,24 @@ static bool is_merged_eswitch_dev(struct mlx5e_priv *priv,
                (peer_priv->mdev->priv.eswitch->mode == SRIOV_OFFLOADS));
 }
 
-static int mlx5e_route_lookup_ipv6(struct mlx5e_priv *priv,
-                                  struct net_device *mirred_dev,
-                                  struct net_device **out_dev,
-                                  struct flowi6 *fl6,
-                                  struct neighbour **out_n,
-                                  u8 *out_ttl)
-{
-       struct neighbour *n = NULL;
-       struct dst_entry *dst;
-
-#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
-       struct mlx5e_rep_priv *uplink_rpriv;
-       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
-       int ret;
-
-       ret = ipv6_stub->ipv6_dst_lookup(dev_net(mirred_dev), NULL, &dst,
-                                        fl6);
-       if (ret < 0)
-               return ret;
-
-       if (!(*out_ttl))
-               *out_ttl = ip6_dst_hoplimit(dst);
-
-       uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
-       /* if the egress device isn't on the same HW e-switch, we use the uplink */
-       if (!switchdev_port_same_parent_id(priv->netdev, dst->dev))
-               *out_dev = uplink_rpriv->netdev;
-       else
-               *out_dev = dst->dev;
-#else
-       return -EOPNOTSUPP;
-#endif
-
-       n = dst_neigh_lookup(dst, &fl6->daddr);
-       dst_release(dst);
-       if (!n)
-               return -ENOMEM;
-
-       *out_n = n;
-       return 0;
-}
-
-static void gen_vxlan_header_ipv4(struct net_device *out_dev,
-                                 char buf[], int encap_size,
-                                 unsigned char h_dest[ETH_ALEN],
-                                 u8 tos, u8 ttl,
-                                 __be32 daddr,
-                                 __be32 saddr,
-                                 __be16 udp_dst_port,
-                                 __be32 vx_vni)
-{
-       struct ethhdr *eth = (struct ethhdr *)buf;
-       struct iphdr  *ip = (struct iphdr *)((char *)eth + sizeof(struct ethhdr));
-       struct udphdr *udp = (struct udphdr *)((char *)ip + sizeof(struct iphdr));
-       struct vxlanhdr *vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr));
-
-       memset(buf, 0, encap_size);
-
-       ether_addr_copy(eth->h_dest, h_dest);
-       ether_addr_copy(eth->h_source, out_dev->dev_addr);
-       eth->h_proto = htons(ETH_P_IP);
-
-       ip->daddr = daddr;
-       ip->saddr = saddr;
-
-       ip->tos = tos;
-       ip->ttl = ttl;
-       ip->protocol = IPPROTO_UDP;
-       ip->version = 0x4;
-       ip->ihl = 0x5;
-
-       udp->dest = udp_dst_port;
-       vxh->vx_flags = VXLAN_HF_VNI;
-       vxh->vx_vni = vxlan_vni_field(vx_vni);
-}
-
-static void gen_vxlan_header_ipv6(struct net_device *out_dev,
-                                 char buf[], int encap_size,
-                                 unsigned char h_dest[ETH_ALEN],
-                                 u8 tos, u8 ttl,
-                                 struct in6_addr *daddr,
-                                 struct in6_addr *saddr,
-                                 __be16 udp_dst_port,
-                                 __be32 vx_vni)
-{
-       struct ethhdr *eth = (struct ethhdr *)buf;
-       struct ipv6hdr *ip6h = (struct ipv6hdr *)((char *)eth + sizeof(struct ethhdr));
-       struct udphdr *udp = (struct udphdr *)((char *)ip6h + sizeof(struct ipv6hdr));
-       struct vxlanhdr *vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr));
-
-       memset(buf, 0, encap_size);
-
-       ether_addr_copy(eth->h_dest, h_dest);
-       ether_addr_copy(eth->h_source, out_dev->dev_addr);
-       eth->h_proto = htons(ETH_P_IPV6);
-
-       ip6_flow_hdr(ip6h, tos, 0);
-       /* the HW fills up ipv6 payload len */
-       ip6h->nexthdr     = IPPROTO_UDP;
-       ip6h->hop_limit   = ttl;
-       ip6h->daddr       = *daddr;
-       ip6h->saddr       = *saddr;
-
-       udp->dest = udp_dst_port;
-       vxh->vx_flags = VXLAN_HF_VNI;
-       vxh->vx_vni = vxlan_vni_field(vx_vni);
-}
-
-static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv,
-                                         struct net_device *mirred_dev,
-                                         struct mlx5e_encap_entry *e)
-{
-       int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
-       int ipv4_encap_size = ETH_HLEN + sizeof(struct iphdr) + VXLAN_HLEN;
-       struct ip_tunnel_key *tun_key = &e->tun_info.key;
-       struct net_device *out_dev;
-       struct neighbour *n = NULL;
-       struct flowi4 fl4 = {};
-       u8 nud_state, tos, ttl;
-       char *encap_header;
-       int err;
-
-       if (max_encap_size < ipv4_encap_size) {
-               mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
-                              ipv4_encap_size, max_encap_size);
-               return -EOPNOTSUPP;
-       }
-
-       encap_header = kzalloc(ipv4_encap_size, GFP_KERNEL);
-       if (!encap_header)
-               return -ENOMEM;
-
-       switch (e->tunnel_type) {
-       case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
-               fl4.flowi4_proto = IPPROTO_UDP;
-               fl4.fl4_dport = tun_key->tp_dst;
-               break;
-       default:
-               err = -EOPNOTSUPP;
-               goto free_encap;
-       }
-
-       tos = tun_key->tos;
-       ttl = tun_key->ttl;
 
-       fl4.flowi4_tos = tun_key->tos;
-       fl4.daddr = tun_key->u.ipv4.dst;
-       fl4.saddr = tun_key->u.ipv4.src;
-
-       err = mlx5e_route_lookup_ipv4(priv, mirred_dev, &out_dev,
-                                     &fl4, &n, &ttl);
-       if (err)
-               goto free_encap;
-
-       /* used by mlx5e_detach_encap to lookup a neigh hash table
-        * entry in the neigh hash table when a user deletes a rule
-        */
-       e->m_neigh.dev = n->dev;
-       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;
-
-       /* 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
-        * neigh changes it's validity state, we would find the relevant neigh
-        * in the hash.
-        */
-       err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e);
-       if (err)
-               goto free_encap;
-
-       read_lock_bh(&n->lock);
-       nud_state = n->nud_state;
-       ether_addr_copy(e->h_dest, n->ha);
-       read_unlock_bh(&n->lock);
-
-       switch (e->tunnel_type) {
-       case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
-               gen_vxlan_header_ipv4(out_dev, encap_header,
-                                     ipv4_encap_size, e->h_dest, tos, ttl,
-                                     fl4.daddr,
-                                     fl4.saddr, tun_key->tp_dst,
-                                     tunnel_id_to_key32(tun_key->tun_id));
-               break;
-       default:
-               err = -EOPNOTSUPP;
-               goto destroy_neigh_entry;
-       }
-       e->encap_size = ipv4_encap_size;
-       e->encap_header = encap_header;
-
-       if (!(nud_state & NUD_VALID)) {
-               neigh_event_send(n, NULL);
-               err = -EAGAIN;
-               goto out;
-       }
-
-       err = mlx5_packet_reformat_alloc(priv->mdev, e->tunnel_type,
-                                        ipv4_encap_size, encap_header,
-                                        MLX5_FLOW_NAMESPACE_FDB,
-                                        &e->encap_id);
-       if (err)
-               goto destroy_neigh_entry;
-
-       e->flags |= MLX5_ENCAP_ENTRY_VALID;
-       mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev));
-       neigh_release(n);
-       return err;
-
-destroy_neigh_entry:
-       mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
-free_encap:
-       kfree(encap_header);
-out:
-       if (n)
-               neigh_release(n);
-       return err;
-}
-
-static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv,
-                                         struct net_device *mirred_dev,
-                                         struct mlx5e_encap_entry *e)
-{
-       int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
-       int ipv6_encap_size = ETH_HLEN + sizeof(struct ipv6hdr) + VXLAN_HLEN;
-       struct ip_tunnel_key *tun_key = &e->tun_info.key;
-       struct net_device *out_dev;
-       struct neighbour *n = NULL;
-       struct flowi6 fl6 = {};
-       u8 nud_state, tos, ttl;
-       char *encap_header;
-       int err;
-
-       if (max_encap_size < ipv6_encap_size) {
-               mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
-                              ipv6_encap_size, max_encap_size);
-               return -EOPNOTSUPP;
-       }
-
-       encap_header = kzalloc(ipv6_encap_size, GFP_KERNEL);
-       if (!encap_header)
-               return -ENOMEM;
-
-       switch (e->tunnel_type) {
-       case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
-               fl6.flowi6_proto = IPPROTO_UDP;
-               fl6.fl6_dport = tun_key->tp_dst;
-               break;
-       default:
-               err = -EOPNOTSUPP;
-               goto free_encap;
-       }
-
-       tos = tun_key->tos;
-       ttl = tun_key->ttl;
-
-       fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label);
-       fl6.daddr = tun_key->u.ipv6.dst;
-       fl6.saddr = tun_key->u.ipv6.src;
-
-       err = mlx5e_route_lookup_ipv6(priv, mirred_dev, &out_dev,
-                                     &fl6, &n, &ttl);
-       if (err)
-               goto free_encap;
-
-       /* used by mlx5e_detach_encap to lookup a neigh hash table
-        * entry in the neigh hash table when a user deletes a rule
-        */
-       e->m_neigh.dev = n->dev;
-       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;
-
-       /* 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
-        * neigh changes it's validity state, we would find the relevant neigh
-        * in the hash.
-        */
-       err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e);
-       if (err)
-               goto free_encap;
-
-       read_lock_bh(&n->lock);
-       nud_state = n->nud_state;
-       ether_addr_copy(e->h_dest, n->ha);
-       read_unlock_bh(&n->lock);
-
-       switch (e->tunnel_type) {
-       case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
-               gen_vxlan_header_ipv6(out_dev, encap_header,
-                                     ipv6_encap_size, e->h_dest, tos, ttl,
-                                     &fl6.daddr,
-                                     &fl6.saddr, tun_key->tp_dst,
-                                     tunnel_id_to_key32(tun_key->tun_id));
-               break;
-       default:
-               err = -EOPNOTSUPP;
-               goto destroy_neigh_entry;
-       }
-
-       e->encap_size = ipv6_encap_size;
-       e->encap_header = encap_header;
-
-       if (!(nud_state & NUD_VALID)) {
-               neigh_event_send(n, NULL);
-               err = -EAGAIN;
-               goto out;
-       }
-
-       err = mlx5_packet_reformat_alloc(priv->mdev, e->tunnel_type,
-                                        ipv6_encap_size, encap_header,
-                                        MLX5_FLOW_NAMESPACE_FDB,
-                                        &e->encap_id);
-       if (err)
-               goto destroy_neigh_entry;
-
-       e->flags |= MLX5_ENCAP_ENTRY_VALID;
-       mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev));
-       neigh_release(n);
-       return err;
-
-destroy_neigh_entry:
-       mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
-free_encap:
-       kfree(encap_header);
-out:
-       if (n)
-               neigh_release(n);
-       return err;
-}
 
 static int mlx5e_attach_encap(struct mlx5e_priv *priv,
                              struct ip_tunnel_info *tun_info,
                              struct net_device *mirred_dev,
                              struct net_device **encap_dev,
                              struct mlx5e_tc_flow *flow,
-                             struct netlink_ext_ack *extack)
+                             struct netlink_ext_ack *extack,
+                             int out_index)
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        unsigned short family = ip_tunnel_info_af(tun_info);
        struct mlx5_esw_flow_attr *attr = flow->esw_attr;
        struct ip_tunnel_key *key = &tun_info->key;
        struct mlx5e_encap_entry *e;
-       int tunnel_type, err = 0;
        uintptr_t hash_key;
        bool found = false;
-
-       /* udp dst port must be set */
-       if (!memchr_inv(&key->tp_dst, 0, sizeof(key->tp_dst)))
-               goto vxlan_encap_offload_err;
-
-       /* setting udp src port isn't supported */
-       if (memchr_inv(&key->tp_src, 0, sizeof(key->tp_src))) {
-vxlan_encap_offload_err:
-               NL_SET_ERR_MSG_MOD(extack,
-                                  "must set udp dst port and not set udp src port");
-               netdev_warn(priv->netdev,
-                           "must set udp dst port and not set udp src port\n");
-               return -EOPNOTSUPP;
-       }
-
-       if (mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(key->tp_dst)) &&
-           MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap)) {
-               tunnel_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN;
-       } else {
-               NL_SET_ERR_MSG_MOD(extack,
-                                  "port isn't an offloaded vxlan udp dport");
-               netdev_warn(priv->netdev,
-                           "%d isn't an offloaded vxlan udp dport\n", be16_to_cpu(key->tp_dst));
-               return -EOPNOTSUPP;
-       }
+       int err = 0;
 
        hash_key = hash_encap_info(key);
 
@@ -2761,13 +2417,16 @@ vxlan_encap_offload_err:
                return -ENOMEM;
 
        e->tun_info = *tun_info;
-       e->tunnel_type = tunnel_type;
+       err = mlx5e_tc_tun_init_encap_attr(mirred_dev, priv, e, extack);
+       if (err)
+               goto out_err;
+
        INIT_LIST_HEAD(&e->flows);
 
        if (family == AF_INET)
-               err = mlx5e_create_encap_header_ipv4(priv, mirred_dev, e);
+               err = mlx5e_tc_tun_create_header_ipv4(priv, mirred_dev, e);
        else if (family == AF_INET6)
-               err = mlx5e_create_encap_header_ipv6(priv, mirred_dev, e);
+               err = mlx5e_tc_tun_create_header_ipv6(priv, mirred_dev, e);
 
        if (err && err != -EAGAIN)
                goto out_err;
@@ -2775,12 +2434,15 @@ vxlan_encap_offload_err:
        hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key);
 
 attach_flow:
-       list_add(&flow->encap, &e->flows);
+       list_add(&flow->encaps[out_index].list, &e->flows);
+       flow->encaps[out_index].index = out_index;
        *encap_dev = e->out_dev;
-       if (e->flags & MLX5_ENCAP_ENTRY_VALID)
-               attr->encap_id = e->encap_id;
-       else
+       if (e->flags & MLX5_ENCAP_ENTRY_VALID) {
+               attr->dests[out_index].encap_id = e->encap_id;
+               attr->dests[out_index].flags |= MLX5_ESW_DEST_ENCAP_VALID;
+       } else {
                err = -EAGAIN;
+       }
 
        return err;
 
@@ -2874,7 +2536,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                                return err;
 
                        action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
-                       attr->mirror_count = attr->out_count;
+                       attr->split_count = attr->out_count;
                        continue;
                }
 
@@ -2892,6 +2554,13 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                        struct net_device *out_dev;
 
                        out_dev = tcf_mirred_dev(a);
+                       if (!out_dev) {
+                               /* out_dev is NULL when filters with
+                                * non-existing mirred device are replayed to
+                                * the driver.
+                                */
+                               return -EINVAL;
+                       }
 
                        if (attr->out_count >= MLX5_MAX_FLOW_FWD_VPORTS) {
                                NL_SET_ERR_MSG_MOD(extack,
@@ -2901,23 +2570,47 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                                return -EOPNOTSUPP;
                        }
 
+                       action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
+                                 MLX5_FLOW_CONTEXT_ACTION_COUNT;
                        if (switchdev_port_same_parent_id(priv->netdev,
                                                          out_dev) ||
                            is_merged_eswitch_dev(priv, out_dev)) {
-                               action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
-                                         MLX5_FLOW_CONTEXT_ACTION_COUNT;
+                               struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+                               struct net_device *uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH);
+                               struct net_device *uplink_upper = netdev_master_upper_dev_get(uplink_dev);
+
+                               if (uplink_upper &&
+                                   netif_is_lag_master(uplink_upper) &&
+                                   uplink_upper == out_dev)
+                                       out_dev = uplink_dev;
+
+                               if (!mlx5e_eswitch_rep(out_dev))
+                                       return -EOPNOTSUPP;
+
                                out_priv = netdev_priv(out_dev);
                                rpriv = out_priv->ppriv;
-                               attr->out_rep[attr->out_count] = rpriv->rep;
-                               attr->out_mdev[attr->out_count++] = out_priv->mdev;
+                               attr->dests[attr->out_count].rep = rpriv->rep;
+                               attr->dests[attr->out_count].mdev = out_priv->mdev;
+                               attr->out_count++;
                        } else if (encap) {
-                               parse_attr->mirred_ifindex = out_dev->ifindex;
-                               parse_attr->tun_info = *info;
+                               parse_attr->mirred_ifindex[attr->out_count] =
+                                       out_dev->ifindex;
+                               parse_attr->tun_info[attr->out_count] = *info;
+                               encap = false;
                                attr->parse_attr = parse_attr;
-                               action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT |
-                                         MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
-                                         MLX5_FLOW_CONTEXT_ACTION_COUNT;
-                               /* attr->out_rep is resolved when we handle encap */
+                               attr->dests[attr->out_count].flags |=
+                                       MLX5_ESW_DEST_ENCAP;
+                               attr->out_count++;
+                               /* attr->dests[].rep is resolved when we
+                                * handle encap
+                                */
+                       } else if (parse_attr->filter_dev != priv->netdev) {
+                               /* All mlx5 devices are called to configure
+                                * high level device filters. Therefore, the
+                                * *attempt* to  install a filter on invalid
+                                * eswitch should not trigger an explicit error
+                                */
+                               return -EINVAL;
                        } else {
                                NL_SET_ERR_MSG_MOD(extack,
                                                   "devices are not on same switch HW, can't offload forwarding");
@@ -2934,7 +2627,6 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                                encap = true;
                        else
                                return -EOPNOTSUPP;
-                       attr->mirror_count = attr->out_count;
                        continue;
                }
 
@@ -2944,7 +2636,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                        if (err)
                                return err;
 
-                       attr->mirror_count = attr->out_count;
+                       attr->split_count = attr->out_count;
                        continue;
                }
 
@@ -2965,8 +2657,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                                NL_SET_ERR_MSG(extack, "Requested destination chain is out of supported range");
                                return -EOPNOTSUPP;
                        }
-                       action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
-                                 MLX5_FLOW_CONTEXT_ACTION_COUNT;
+                       action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
                        attr->dest_chain = dest_chain;
 
                        continue;
@@ -2979,7 +2670,15 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
        if (!actions_match_supported(priv, exts, parse_attr, flow, extack))
                return -EOPNOTSUPP;
 
-       if (attr->out_count > 1 && !mlx5_esw_has_fwd_fdb(priv->mdev)) {
+       if (attr->dest_chain) {
+               if (attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
+                       NL_SET_ERR_MSG(extack, "Mirroring goto chain rules isn't supported");
+                       return -EOPNOTSUPP;
+               }
+               attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+       }
+
+       if (attr->split_count > 0 && !mlx5_esw_has_fwd_fdb(priv->mdev)) {
                NL_SET_ERR_MSG_MOD(extack,
                                   "current firmware doesn't support split rule for port mirroring");
                netdev_warn_once(priv->netdev, "current firmware doesn't support split rule for port mirroring\n");
@@ -2998,6 +2697,11 @@ static void get_flags(int flags, u16 *flow_flags)
        if (flags & MLX5E_TC_EGRESS)
                __flow_flags |= MLX5E_TC_FLOW_EGRESS;
 
+       if (flags & MLX5E_TC_ESW_OFFLOAD)
+               __flow_flags |= MLX5E_TC_FLOW_ESWITCH;
+       if (flags & MLX5E_TC_NIC_OFFLOAD)
+               __flow_flags |= MLX5E_TC_FLOW_NIC;
+
        *flow_flags = __flow_flags;
 }
 
@@ -3008,18 +2712,32 @@ static const struct rhashtable_params tc_ht_params = {
        .automatic_shrinking = true,
 };
 
-static struct rhashtable *get_tc_ht(struct mlx5e_priv *priv)
+static struct rhashtable *get_tc_ht(struct mlx5e_priv *priv, int flags)
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        struct mlx5e_rep_priv *uplink_rpriv;
 
-       if (MLX5_VPORT_MANAGER(priv->mdev) && esw->mode == SRIOV_OFFLOADS) {
+       if (flags & MLX5E_TC_ESW_OFFLOAD) {
                uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
-               return &uplink_rpriv->tc_ht;
-       } else
+               return &uplink_rpriv->uplink_priv.tc_ht;
+       } else /* NIC offload */
                return &priv->fs.tc.ht;
 }
 
+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 &&
+                             flow->flags & MLX5E_TC_FLOW_INGRESS;
+       bool act_is_encap = !!(attr->action &
+                              MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT);
+       bool esw_paired = mlx5_devcom_is_paired(attr->in_mdev->priv.devcom,
+                                               MLX5_DEVCOM_ESW_OFFLOADS);
+
+       return esw_paired && mlx5_lag_is_sriov(attr->in_mdev) &&
+              (is_rep_ingress || act_is_encap);
+}
+
 static int
 mlx5e_alloc_flow(struct mlx5e_priv *priv, int attr_size,
                 struct tc_cls_flower_offload *f, u16 flow_flags,
@@ -3041,10 +2759,6 @@ mlx5e_alloc_flow(struct mlx5e_priv *priv, int attr_size,
        flow->flags = flow_flags;
        flow->priv = priv;
 
-       err = parse_cls_flower(priv, flow, &parse_attr->spec, f);
-       if (err)
-               goto err_free;
-
        *__flow = flow;
        *__parse_attr = parse_attr;
 
@@ -3057,12 +2771,16 @@ err_free:
 }
 
 static int
-mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
-                  struct tc_cls_flower_offload *f,
-                  u16 flow_flags,
-                  struct mlx5e_tc_flow **__flow)
+__mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
+                    struct tc_cls_flower_offload *f,
+                    u16 flow_flags,
+                    struct net_device *filter_dev,
+                    struct mlx5_eswitch_rep *in_rep,
+                    struct mlx5_core_dev *in_mdev,
+                    struct mlx5e_tc_flow **__flow)
 {
        struct netlink_ext_ack *extack = f->common.extack;
+       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        struct mlx5e_tc_flow_parse_attr *parse_attr;
        struct mlx5e_tc_flow *flow;
        int attr_size, err;
@@ -3073,6 +2791,12 @@ mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
                               &parse_attr, &flow);
        if (err)
                goto out;
+       parse_attr->filter_dev = filter_dev;
+       flow->esw_attr->parse_attr = parse_attr;
+       err = parse_cls_flower(flow->priv, flow, &parse_attr->spec,
+                              f, filter_dev);
+       if (err)
+               goto err_free;
 
        flow->esw_attr->chain = f->common.chain_index;
        flow->esw_attr->prio = TC_H_MAJ(f->common.prio) >> 16;
@@ -3080,14 +2804,19 @@ mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
        if (err)
                goto err_free;
 
+       flow->esw_attr->in_rep = in_rep;
+       flow->esw_attr->in_mdev = in_mdev;
+
+       if (MLX5_CAP_ESW(esw->dev, counter_eswitch_affinity) ==
+           MLX5_COUNTER_SOURCE_ESWITCH)
+               flow->esw_attr->counter_dev = in_mdev;
+       else
+               flow->esw_attr->counter_dev = priv->mdev;
+
        err = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow, extack);
        if (err)
                goto err_free;
 
-       if (!(flow->esw_attr->action &
-             MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT))
-               kvfree(parse_attr);
-
        *__flow = flow;
 
        return 0;
@@ -3099,10 +2828,92 @@ out:
        return err;
 }
 
+static int mlx5e_tc_add_fdb_peer_flow(struct tc_cls_flower_offload *f,
+                                     struct mlx5e_tc_flow *flow)
+{
+       struct mlx5e_priv *priv = flow->priv, *peer_priv;
+       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch, *peer_esw;
+       struct mlx5_devcom *devcom = priv->mdev->priv.devcom;
+       struct mlx5e_tc_flow_parse_attr *parse_attr;
+       struct mlx5e_rep_priv *peer_urpriv;
+       struct mlx5e_tc_flow *peer_flow;
+       struct mlx5_core_dev *in_mdev;
+       int err = 0;
+
+       peer_esw = mlx5_devcom_get_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
+       if (!peer_esw)
+               return -ENODEV;
+
+       peer_urpriv = mlx5_eswitch_get_uplink_priv(peer_esw, REP_ETH);
+       peer_priv = netdev_priv(peer_urpriv->netdev);
+
+       /* in_mdev is assigned of which the packet originated from.
+        * So packets redirected to uplink use the same mdev of the
+        * original flow and packets redirected from uplink use the
+        * peer mdev.
+        */
+       if (flow->esw_attr->in_rep->vport == FDB_UPLINK_VPORT)
+               in_mdev = peer_priv->mdev;
+       else
+               in_mdev = priv->mdev;
+
+       parse_attr = flow->esw_attr->parse_attr;
+       err = __mlx5e_add_fdb_flow(peer_priv, f, flow->flags,
+                                  parse_attr->filter_dev,
+                                  flow->esw_attr->in_rep, in_mdev, &peer_flow);
+       if (err)
+               goto out;
+
+       flow->peer_flow = peer_flow;
+       flow->flags |= MLX5E_TC_FLOW_DUP;
+       mutex_lock(&esw->offloads.peer_mutex);
+       list_add_tail(&flow->peer, &esw->offloads.peer_flows);
+       mutex_unlock(&esw->offloads.peer_mutex);
+
+out:
+       mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
+       return err;
+}
+
+static int
+mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
+                  struct tc_cls_flower_offload *f,
+                  u16 flow_flags,
+                  struct net_device *filter_dev,
+                  struct mlx5e_tc_flow **__flow)
+{
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *in_rep = rpriv->rep;
+       struct mlx5_core_dev *in_mdev = priv->mdev;
+       struct mlx5e_tc_flow *flow;
+       int err;
+
+       err = __mlx5e_add_fdb_flow(priv, f, flow_flags, filter_dev, in_rep,
+                                  in_mdev, &flow);
+       if (err)
+               goto out;
+
+       if (is_peer_flow_needed(flow)) {
+               err = mlx5e_tc_add_fdb_peer_flow(f, flow);
+               if (err) {
+                       mlx5e_tc_del_fdb_flow(priv, flow);
+                       goto out;
+               }
+       }
+
+       *__flow = flow;
+
+       return 0;
+
+out:
+       return err;
+}
+
 static int
 mlx5e_add_nic_flow(struct mlx5e_priv *priv,
                   struct tc_cls_flower_offload *f,
                   u16 flow_flags,
+                  struct net_device *filter_dev,
                   struct mlx5e_tc_flow **__flow)
 {
        struct netlink_ext_ack *extack = f->common.extack;
@@ -3121,6 +2932,12 @@ mlx5e_add_nic_flow(struct mlx5e_priv *priv,
        if (err)
                goto out;
 
+       parse_attr->filter_dev = filter_dev;
+       err = parse_cls_flower(flow->priv, flow, &parse_attr->spec,
+                              f, filter_dev);
+       if (err)
+               goto err_free;
+
        err = parse_tc_nic_actions(priv, f->exts, parse_attr, flow, extack);
        if (err)
                goto err_free;
@@ -3146,6 +2963,7 @@ static int
 mlx5e_tc_add_flow(struct mlx5e_priv *priv,
                  struct tc_cls_flower_offload *f,
                  int flags,
+                 struct net_device *filter_dev,
                  struct mlx5e_tc_flow **flow)
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
@@ -3158,18 +2976,20 @@ mlx5e_tc_add_flow(struct mlx5e_priv *priv,
                return -EOPNOTSUPP;
 
        if (esw && esw->mode == SRIOV_OFFLOADS)
-               err = mlx5e_add_fdb_flow(priv, f, flow_flags, flow);
+               err = mlx5e_add_fdb_flow(priv, f, flow_flags,
+                                        filter_dev, flow);
        else
-               err = mlx5e_add_nic_flow(priv, f, flow_flags, flow);
+               err = mlx5e_add_nic_flow(priv, f, flow_flags,
+                                        filter_dev, flow);
 
        return err;
 }
 
-int mlx5e_configure_flower(struct mlx5e_priv *priv,
+int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,
                           struct tc_cls_flower_offload *f, int flags)
 {
        struct netlink_ext_ack *extack = f->common.extack;
-       struct rhashtable *tc_ht = get_tc_ht(priv);
+       struct rhashtable *tc_ht = get_tc_ht(priv, flags);
        struct mlx5e_tc_flow *flow;
        int err = 0;
 
@@ -3183,7 +3003,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv,
                goto out;
        }
 
-       err = mlx5e_tc_add_flow(priv, f, flags, &flow);
+       err = mlx5e_tc_add_flow(priv, f, flags, dev, &flow);
        if (err)
                goto out;
 
@@ -3211,10 +3031,10 @@ static bool same_flow_direction(struct mlx5e_tc_flow *flow, int flags)
        return false;
 }
 
-int mlx5e_delete_flower(struct mlx5e_priv *priv,
+int mlx5e_delete_flower(struct net_device *dev, struct mlx5e_priv *priv,
                        struct tc_cls_flower_offload *f, int flags)
 {
-       struct rhashtable *tc_ht = get_tc_ht(priv);
+       struct rhashtable *tc_ht = get_tc_ht(priv, flags);
        struct mlx5e_tc_flow *flow;
 
        flow = rhashtable_lookup_fast(tc_ht, &f->cookie, tc_ht_params);
@@ -3230,10 +3050,12 @@ int mlx5e_delete_flower(struct mlx5e_priv *priv,
        return 0;
 }
 
-int mlx5e_stats_flower(struct mlx5e_priv *priv,
+int mlx5e_stats_flower(struct net_device *dev, struct mlx5e_priv *priv,
                       struct tc_cls_flower_offload *f, int flags)
 {
-       struct rhashtable *tc_ht = get_tc_ht(priv);
+       struct mlx5_devcom *devcom = priv->mdev->priv.devcom;
+       struct rhashtable *tc_ht = get_tc_ht(priv, flags);
+       struct mlx5_eswitch *peer_esw;
        struct mlx5e_tc_flow *flow;
        struct mlx5_fc *counter;
        u64 bytes;
@@ -3253,6 +3075,27 @@ int mlx5e_stats_flower(struct mlx5e_priv *priv,
 
        mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse);
 
+       peer_esw = mlx5_devcom_get_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
+       if (!peer_esw)
+               goto out;
+
+       if ((flow->flags & MLX5E_TC_FLOW_DUP) &&
+           (flow->peer_flow->flags & MLX5E_TC_FLOW_OFFLOADED)) {
+               u64 bytes2;
+               u64 packets2;
+               u64 lastuse2;
+
+               counter = mlx5e_tc_get_counter(flow->peer_flow);
+               mlx5_fc_query_cached(counter, &bytes2, &packets2, &lastuse2);
+
+               bytes += bytes2;
+               packets += packets2;
+               lastuse = max_t(u64, lastuse, lastuse2);
+       }
+
+       mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
+
+out:
        tcf_exts_stats_update(f->exts, bytes, packets, lastuse);
 
        return 0;
@@ -3341,7 +3184,7 @@ void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv)
        if (tc->netdevice_nb.notifier_call)
                unregister_netdevice_notifier(&tc->netdevice_nb);
 
-       rhashtable_free_and_destroy(&tc->ht, _mlx5e_tc_del_flow, NULL);
+       rhashtable_destroy(&tc->ht);
 
        if (!IS_ERR_OR_NULL(tc->t)) {
                mlx5_destroy_flow_table(tc->t);
@@ -3359,9 +3202,17 @@ void mlx5e_tc_esw_cleanup(struct rhashtable *tc_ht)
        rhashtable_free_and_destroy(tc_ht, _mlx5e_tc_del_flow, NULL);
 }
 
-int mlx5e_tc_num_filters(struct mlx5e_priv *priv)
+int mlx5e_tc_num_filters(struct mlx5e_priv *priv, int flags)
 {
-       struct rhashtable *tc_ht = get_tc_ht(priv);
+       struct rhashtable *tc_ht = get_tc_ht(priv, flags);
 
        return atomic_read(&tc_ht->nelems);
 }
+
+void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw)
+{
+       struct mlx5e_tc_flow *flow, *tmp;
+
+       list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows, peer)
+               __mlx5e_tc_del_fdb_peer_flow(flow);
+}
index 49436bf..d2d87f9 100644 (file)
@@ -42,7 +42,9 @@
 enum {
        MLX5E_TC_INGRESS = BIT(0),
        MLX5E_TC_EGRESS  = BIT(1),
-       MLX5E_TC_LAST_EXPORTED_BIT = 1,
+       MLX5E_TC_NIC_OFFLOAD = BIT(2),
+       MLX5E_TC_ESW_OFFLOAD = BIT(3),
+       MLX5E_TC_LAST_EXPORTED_BIT = 3,
 };
 
 int mlx5e_tc_nic_init(struct mlx5e_priv *priv);
@@ -51,12 +53,12 @@ void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv);
 int mlx5e_tc_esw_init(struct rhashtable *tc_ht);
 void mlx5e_tc_esw_cleanup(struct rhashtable *tc_ht);
 
-int mlx5e_configure_flower(struct mlx5e_priv *priv,
+int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,
                           struct tc_cls_flower_offload *f, int flags);
-int mlx5e_delete_flower(struct mlx5e_priv *priv,
+int mlx5e_delete_flower(struct net_device *dev, struct mlx5e_priv *priv,
                        struct tc_cls_flower_offload *f, int flags);
 
-int mlx5e_stats_flower(struct mlx5e_priv *priv,
+int mlx5e_stats_flower(struct net_device *dev, struct mlx5e_priv *priv,
                       struct tc_cls_flower_offload *f, int flags);
 
 struct mlx5e_encap_entry;
@@ -68,12 +70,13 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv,
 struct mlx5e_neigh_hash_entry;
 void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe);
 
-int mlx5e_tc_num_filters(struct mlx5e_priv *priv);
+int mlx5e_tc_num_filters(struct mlx5e_priv *priv, int flags);
+
 
 #else /* CONFIG_MLX5_ESWITCH */
 static inline int  mlx5e_tc_nic_init(struct mlx5e_priv *priv) { return 0; }
 static inline void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv) {}
-static inline int  mlx5e_tc_num_filters(struct mlx5e_priv *priv) { return 0; }
+static inline int  mlx5e_tc_num_filters(struct mlx5e_priv *priv, int flags) { return 0; }
 #endif
 
 #endif /* __MLX5_EN_TC_H__ */
index 6dacaeb..f201965 100644 (file)
@@ -127,7 +127,7 @@ u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb,
        else
 #endif
                if (skb_vlan_tag_present(skb))
-                       up = skb->vlan_tci >> VLAN_PRIO_SHIFT;
+                       up = skb_vlan_tag_get_prio(skb);
 
        /* channel_ix can be larger than num_channels since
         * dev->num_real_tx_queues = num_channels * num_tc
@@ -507,7 +507,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
 
                wqe_counter = be16_to_cpu(cqe->wqe_counter);
 
-               if (unlikely(cqe->op_own >> 4 == MLX5_CQE_REQ_ERR)) {
+               if (unlikely(get_cqe_opcode(cqe) == MLX5_CQE_REQ_ERR)) {
                        if (!test_and_set_bit(MLX5E_SQ_STATE_RECOVERING,
                                              &sq->state)) {
                                mlx5e_dump_error_cqe(sq,
index c1e1a16..ee04aab 100644 (file)
  */
 
 #include <linux/interrupt.h>
+#include <linux/notifier.h>
 #include <linux/module.h>
 #include <linux/mlx5/driver.h>
+#include <linux/mlx5/eq.h>
 #include <linux/mlx5/cmd.h>
 #ifdef CONFIG_RFS_ACCEL
 #include <linux/cpu_rmap.h>
 #endif
 #include "mlx5_core.h"
+#include "lib/eq.h"
 #include "fpga/core.h"
 #include "eswitch.h"
 #include "lib/clock.h"
 #include "diag/fw_tracer.h"
 
 enum {
-       MLX5_EQE_SIZE           = sizeof(struct mlx5_eqe),
        MLX5_EQE_OWNER_INIT_VAL = 0x1,
 };
 
@@ -55,14 +57,32 @@ enum {
 };
 
 enum {
-       MLX5_NUM_SPARE_EQE      = 0x80,
-       MLX5_NUM_ASYNC_EQE      = 0x1000,
-       MLX5_NUM_CMD_EQE        = 32,
-       MLX5_NUM_PF_DRAIN       = 64,
+       MLX5_EQ_DOORBEL_OFFSET  = 0x40,
 };
 
-enum {
-       MLX5_EQ_DOORBEL_OFFSET  = 0x40,
+struct mlx5_irq_info {
+       cpumask_var_t mask;
+       char name[MLX5_MAX_IRQ_NAME];
+       void *context; /* dev_id provided to request_irq */
+};
+
+struct mlx5_eq_table {
+       struct list_head        comp_eqs_list;
+       struct mlx5_eq          pages_eq;
+       struct mlx5_eq          cmd_eq;
+       struct mlx5_eq          async_eq;
+
+       struct atomic_notifier_head nh[MLX5_EVENT_TYPE_MAX];
+
+       /* Since CQ DB is stored in async_eq */
+       struct mlx5_nb          cq_err_nb;
+
+       struct mutex            lock; /* sync async eqs creations */
+       int                     num_comp_vectors;
+       struct mlx5_irq_info    *irq_info;
+#ifdef CONFIG_RFS_ACCEL
+       struct cpu_rmap         *rmap;
+#endif
 };
 
 #define MLX5_ASYNC_EVENT_MASK ((1ull << MLX5_EVENT_TYPE_PATH_MIG)          | \
@@ -78,17 +98,6 @@ enum {
                               (1ull << MLX5_EVENT_TYPE_SRQ_LAST_WQE)       | \
                               (1ull << MLX5_EVENT_TYPE_SRQ_RQ_LIMIT))
 
-struct map_eq_in {
-       u64     mask;
-       u32     reserved;
-       u32     unmap_eqn;
-};
-
-struct cre_des_eq {
-       u8      reserved[15];
-       u8      eqn;
-};
-
 static int mlx5_cmd_destroy_eq(struct mlx5_core_dev *dev, u8 eqn)
 {
        u32 out[MLX5_ST_SZ_DW(destroy_eq_out)] = {0};
@@ -99,213 +108,56 @@ static int mlx5_cmd_destroy_eq(struct mlx5_core_dev *dev, u8 eqn)
        return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
 }
 
-static struct mlx5_eqe *get_eqe(struct mlx5_eq *eq, u32 entry)
-{
-       return mlx5_buf_offset(&eq->buf, entry * MLX5_EQE_SIZE);
-}
-
-static struct mlx5_eqe *next_eqe_sw(struct mlx5_eq *eq)
-{
-       struct mlx5_eqe *eqe = get_eqe(eq, eq->cons_index & (eq->nent - 1));
-
-       return ((eqe->owner & 1) ^ !!(eq->cons_index & eq->nent)) ? NULL : eqe;
-}
-
-static const char *eqe_type_str(u8 type)
-{
-       switch (type) {
-       case MLX5_EVENT_TYPE_COMP:
-               return "MLX5_EVENT_TYPE_COMP";
-       case MLX5_EVENT_TYPE_PATH_MIG:
-               return "MLX5_EVENT_TYPE_PATH_MIG";
-       case MLX5_EVENT_TYPE_COMM_EST:
-               return "MLX5_EVENT_TYPE_COMM_EST";
-       case MLX5_EVENT_TYPE_SQ_DRAINED:
-               return "MLX5_EVENT_TYPE_SQ_DRAINED";
-       case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
-               return "MLX5_EVENT_TYPE_SRQ_LAST_WQE";
-       case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT:
-               return "MLX5_EVENT_TYPE_SRQ_RQ_LIMIT";
-       case MLX5_EVENT_TYPE_CQ_ERROR:
-               return "MLX5_EVENT_TYPE_CQ_ERROR";
-       case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
-               return "MLX5_EVENT_TYPE_WQ_CATAS_ERROR";
-       case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
-               return "MLX5_EVENT_TYPE_PATH_MIG_FAILED";
-       case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
-               return "MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR";
-       case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
-               return "MLX5_EVENT_TYPE_WQ_ACCESS_ERROR";
-       case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR:
-               return "MLX5_EVENT_TYPE_SRQ_CATAS_ERROR";
-       case MLX5_EVENT_TYPE_INTERNAL_ERROR:
-               return "MLX5_EVENT_TYPE_INTERNAL_ERROR";
-       case MLX5_EVENT_TYPE_PORT_CHANGE:
-               return "MLX5_EVENT_TYPE_PORT_CHANGE";
-       case MLX5_EVENT_TYPE_GPIO_EVENT:
-               return "MLX5_EVENT_TYPE_GPIO_EVENT";
-       case MLX5_EVENT_TYPE_PORT_MODULE_EVENT:
-               return "MLX5_EVENT_TYPE_PORT_MODULE_EVENT";
-       case MLX5_EVENT_TYPE_TEMP_WARN_EVENT:
-               return "MLX5_EVENT_TYPE_TEMP_WARN_EVENT";
-       case MLX5_EVENT_TYPE_REMOTE_CONFIG:
-               return "MLX5_EVENT_TYPE_REMOTE_CONFIG";
-       case MLX5_EVENT_TYPE_DB_BF_CONGESTION:
-               return "MLX5_EVENT_TYPE_DB_BF_CONGESTION";
-       case MLX5_EVENT_TYPE_STALL_EVENT:
-               return "MLX5_EVENT_TYPE_STALL_EVENT";
-       case MLX5_EVENT_TYPE_CMD:
-               return "MLX5_EVENT_TYPE_CMD";
-       case MLX5_EVENT_TYPE_PAGE_REQUEST:
-               return "MLX5_EVENT_TYPE_PAGE_REQUEST";
-       case MLX5_EVENT_TYPE_PAGE_FAULT:
-               return "MLX5_EVENT_TYPE_PAGE_FAULT";
-       case MLX5_EVENT_TYPE_PPS_EVENT:
-               return "MLX5_EVENT_TYPE_PPS_EVENT";
-       case MLX5_EVENT_TYPE_NIC_VPORT_CHANGE:
-               return "MLX5_EVENT_TYPE_NIC_VPORT_CHANGE";
-       case MLX5_EVENT_TYPE_FPGA_ERROR:
-               return "MLX5_EVENT_TYPE_FPGA_ERROR";
-       case MLX5_EVENT_TYPE_FPGA_QP_ERROR:
-               return "MLX5_EVENT_TYPE_FPGA_QP_ERROR";
-       case MLX5_EVENT_TYPE_GENERAL_EVENT:
-               return "MLX5_EVENT_TYPE_GENERAL_EVENT";
-       case MLX5_EVENT_TYPE_DEVICE_TRACER:
-               return "MLX5_EVENT_TYPE_DEVICE_TRACER";
-       default:
-               return "Unrecognized event";
-       }
-}
-
-static enum mlx5_dev_event port_subtype_event(u8 subtype)
-{
-       switch (subtype) {
-       case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
-               return MLX5_DEV_EVENT_PORT_DOWN;
-       case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
-               return MLX5_DEV_EVENT_PORT_UP;
-       case MLX5_PORT_CHANGE_SUBTYPE_INITIALIZED:
-               return MLX5_DEV_EVENT_PORT_INITIALIZED;
-       case MLX5_PORT_CHANGE_SUBTYPE_LID:
-               return MLX5_DEV_EVENT_LID_CHANGE;
-       case MLX5_PORT_CHANGE_SUBTYPE_PKEY:
-               return MLX5_DEV_EVENT_PKEY_CHANGE;
-       case MLX5_PORT_CHANGE_SUBTYPE_GUID:
-               return MLX5_DEV_EVENT_GUID_CHANGE;
-       case MLX5_PORT_CHANGE_SUBTYPE_CLIENT_REREG:
-               return MLX5_DEV_EVENT_CLIENT_REREG;
-       }
-       return -1;
-}
-
-static void eq_update_ci(struct mlx5_eq *eq, int arm)
+/* caller must eventually call mlx5_cq_put on the returned cq */
+static struct mlx5_core_cq *mlx5_eq_cq_get(struct mlx5_eq *eq, u32 cqn)
 {
-       __be32 __iomem *addr = eq->doorbell + (arm ? 0 : 2);
-       u32 val = (eq->cons_index & 0xffffff) | (eq->eqn << 24);
-
-       __raw_writel((__force u32)cpu_to_be32(val), addr);
-       /* We still want ordering, just not swabbing, so add a barrier */
-       mb();
-}
+       struct mlx5_cq_table *table = &eq->cq_table;
+       struct mlx5_core_cq *cq = NULL;
 
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-static void eqe_pf_action(struct work_struct *work)
-{
-       struct mlx5_pagefault *pfault = container_of(work,
-                                                    struct mlx5_pagefault,
-                                                    work);
-       struct mlx5_eq *eq = pfault->eq;
+       spin_lock(&table->lock);
+       cq = radix_tree_lookup(&table->tree, cqn);
+       if (likely(cq))
+               mlx5_cq_hold(cq);
+       spin_unlock(&table->lock);
 
-       mlx5_core_page_fault(eq->dev, pfault);
-       mempool_free(pfault, eq->pf_ctx.pool);
+       return cq;
 }
 
-static void eq_pf_process(struct mlx5_eq *eq)
+static irqreturn_t mlx5_eq_comp_int(int irq, void *eq_ptr)
 {
-       struct mlx5_core_dev *dev = eq->dev;
-       struct mlx5_eqe_page_fault *pf_eqe;
-       struct mlx5_pagefault *pfault;
+       struct mlx5_eq_comp *eq_comp = eq_ptr;
+       struct mlx5_eq *eq = eq_ptr;
        struct mlx5_eqe *eqe;
        int set_ci = 0;
+       u32 cqn = -1;
 
        while ((eqe = next_eqe_sw(eq))) {
-               pfault = mempool_alloc(eq->pf_ctx.pool, GFP_ATOMIC);
-               if (!pfault) {
-                       schedule_work(&eq->pf_ctx.work);
-                       break;
-               }
-
+               struct mlx5_core_cq *cq;
+               /* Make sure we read EQ entry contents after we've
+                * checked the ownership bit.
+                */
                dma_rmb();
-               pf_eqe = &eqe->data.page_fault;
-               pfault->event_subtype = eqe->sub_type;
-               pfault->bytes_committed = be32_to_cpu(pf_eqe->bytes_committed);
-
-               mlx5_core_dbg(dev,
-                             "PAGE_FAULT: subtype: 0x%02x, bytes_committed: 0x%06x\n",
-                             eqe->sub_type, pfault->bytes_committed);
-
-               switch (eqe->sub_type) {
-               case MLX5_PFAULT_SUBTYPE_RDMA:
-                       /* RDMA based event */
-                       pfault->type =
-                               be32_to_cpu(pf_eqe->rdma.pftype_token) >> 24;
-                       pfault->token =
-                               be32_to_cpu(pf_eqe->rdma.pftype_token) &
-                               MLX5_24BIT_MASK;
-                       pfault->rdma.r_key =
-                               be32_to_cpu(pf_eqe->rdma.r_key);
-                       pfault->rdma.packet_size =
-                               be16_to_cpu(pf_eqe->rdma.packet_length);
-                       pfault->rdma.rdma_op_len =
-                               be32_to_cpu(pf_eqe->rdma.rdma_op_len);
-                       pfault->rdma.rdma_va =
-                               be64_to_cpu(pf_eqe->rdma.rdma_va);
-                       mlx5_core_dbg(dev,
-                                     "PAGE_FAULT: type:0x%x, token: 0x%06x, r_key: 0x%08x\n",
-                                     pfault->type, pfault->token,
-                                     pfault->rdma.r_key);
-                       mlx5_core_dbg(dev,
-                                     "PAGE_FAULT: rdma_op_len: 0x%08x, rdma_va: 0x%016llx\n",
-                                     pfault->rdma.rdma_op_len,
-                                     pfault->rdma.rdma_va);
-                       break;
-
-               case MLX5_PFAULT_SUBTYPE_WQE:
-                       /* WQE based event */
-                       pfault->type =
-                               (be32_to_cpu(pf_eqe->wqe.pftype_wq) >> 24) & 0x7;
-                       pfault->token =
-                               be32_to_cpu(pf_eqe->wqe.token);
-                       pfault->wqe.wq_num =
-                               be32_to_cpu(pf_eqe->wqe.pftype_wq) &
-                               MLX5_24BIT_MASK;
-                       pfault->wqe.wqe_index =
-                               be16_to_cpu(pf_eqe->wqe.wqe_index);
-                       pfault->wqe.packet_size =
-                               be16_to_cpu(pf_eqe->wqe.packet_length);
-                       mlx5_core_dbg(dev,
-                                     "PAGE_FAULT: type:0x%x, token: 0x%06x, wq_num: 0x%06x, wqe_index: 0x%04x\n",
-                                     pfault->type, pfault->token,
-                                     pfault->wqe.wq_num,
-                                     pfault->wqe.wqe_index);
-                       break;
-
-               default:
-                       mlx5_core_warn(dev,
-                                      "Unsupported page fault event sub-type: 0x%02hhx\n",
-                                      eqe->sub_type);
-                       /* Unsupported page faults should still be
-                        * resolved by the page fault handler
-                        */
+               /* Assume (eqe->type) is always MLX5_EVENT_TYPE_COMP */
+               cqn = be32_to_cpu(eqe->data.comp.cqn) & 0xffffff;
+
+               cq = mlx5_eq_cq_get(eq, cqn);
+               if (likely(cq)) {
+                       ++cq->arm_sn;
+                       cq->comp(cq);
+                       mlx5_cq_put(cq);
+               } else {
+                       mlx5_core_warn(eq->dev, "Completion event for bogus CQ 0x%x\n", cqn);
                }
 
-               pfault->eq = eq;
-               INIT_WORK(&pfault->work, eqe_pf_action);
-               queue_work(eq->pf_ctx.wq, &pfault->work);
-
                ++eq->cons_index;
                ++set_ci;
 
+               /* The HCA will think the queue has overflowed if we
+                * don't tell it we've been processing events.  We
+                * create our EQs with MLX5_NUM_SPARE_EQE extra
+                * entries, so we must update our consumer index at
+                * least that often.
+                */
                if (unlikely(set_ci >= MLX5_NUM_SPARE_EQE)) {
                        eq_update_ci(eq, 0);
                        set_ci = 0;
@@ -313,165 +165,41 @@ static void eq_pf_process(struct mlx5_eq *eq)
        }
 
        eq_update_ci(eq, 1);
-}
 
-static irqreturn_t mlx5_eq_pf_int(int irq, void *eq_ptr)
-{
-       struct mlx5_eq *eq = eq_ptr;
-       unsigned long flags;
-
-       if (spin_trylock_irqsave(&eq->pf_ctx.lock, flags)) {
-               eq_pf_process(eq);
-               spin_unlock_irqrestore(&eq->pf_ctx.lock, flags);
-       } else {
-               schedule_work(&eq->pf_ctx.work);
-       }
+       if (cqn != -1)
+               tasklet_schedule(&eq_comp->tasklet_ctx.task);
 
        return IRQ_HANDLED;
 }
 
-/* mempool_refill() was proposed but unfortunately wasn't accepted
- * http://lkml.iu.edu/hypermail/linux/kernel/1512.1/05073.html
- * Chip workaround.
+/* Some architectures don't latch interrupts when they are disabled, so using
+ * mlx5_eq_poll_irq_disabled could end up losing interrupts while trying to
+ * avoid losing them.  It is not recommended to use it, unless this is the last
+ * resort.
  */
-static void mempool_refill(mempool_t *pool)
-{
-       while (pool->curr_nr < pool->min_nr)
-               mempool_free(mempool_alloc(pool, GFP_KERNEL), pool);
-}
-
-static void eq_pf_action(struct work_struct *work)
-{
-       struct mlx5_eq *eq = container_of(work, struct mlx5_eq, pf_ctx.work);
-
-       mempool_refill(eq->pf_ctx.pool);
-
-       spin_lock_irq(&eq->pf_ctx.lock);
-       eq_pf_process(eq);
-       spin_unlock_irq(&eq->pf_ctx.lock);
-}
-
-static int init_pf_ctx(struct mlx5_eq_pagefault *pf_ctx, const char *name)
-{
-       spin_lock_init(&pf_ctx->lock);
-       INIT_WORK(&pf_ctx->work, eq_pf_action);
-
-       pf_ctx->wq = alloc_ordered_workqueue(name,
-                                            WQ_MEM_RECLAIM);
-       if (!pf_ctx->wq)
-               return -ENOMEM;
-
-       pf_ctx->pool = mempool_create_kmalloc_pool
-               (MLX5_NUM_PF_DRAIN, sizeof(struct mlx5_pagefault));
-       if (!pf_ctx->pool)
-               goto err_wq;
-
-       return 0;
-err_wq:
-       destroy_workqueue(pf_ctx->wq);
-       return -ENOMEM;
-}
-
-int mlx5_core_page_fault_resume(struct mlx5_core_dev *dev, u32 token,
-                               u32 wq_num, u8 type, int error)
-{
-       u32 out[MLX5_ST_SZ_DW(page_fault_resume_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(page_fault_resume_in)]   = {0};
-
-       MLX5_SET(page_fault_resume_in, in, opcode,
-                MLX5_CMD_OP_PAGE_FAULT_RESUME);
-       MLX5_SET(page_fault_resume_in, in, error, !!error);
-       MLX5_SET(page_fault_resume_in, in, page_fault_type, type);
-       MLX5_SET(page_fault_resume_in, in, wq_number, wq_num);
-       MLX5_SET(page_fault_resume_in, in, token, token);
-
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-EXPORT_SYMBOL_GPL(mlx5_core_page_fault_resume);
-#endif
-
-static void general_event_handler(struct mlx5_core_dev *dev,
-                                 struct mlx5_eqe *eqe)
-{
-       switch (eqe->sub_type) {
-       case MLX5_GENERAL_SUBTYPE_DELAY_DROP_TIMEOUT:
-               if (dev->event)
-                       dev->event(dev, MLX5_DEV_EVENT_DELAY_DROP_TIMEOUT, 0);
-               break;
-       default:
-               mlx5_core_dbg(dev, "General event with unrecognized subtype: sub_type %d\n",
-                             eqe->sub_type);
-       }
-}
-
-static void mlx5_temp_warning_event(struct mlx5_core_dev *dev,
-                                   struct mlx5_eqe *eqe)
-{
-       u64 value_lsb;
-       u64 value_msb;
-
-       value_lsb = be64_to_cpu(eqe->data.temp_warning.sensor_warning_lsb);
-       value_msb = be64_to_cpu(eqe->data.temp_warning.sensor_warning_msb);
-
-       mlx5_core_warn(dev,
-                      "High temperature on sensors with bit set %llx %llx",
-                      value_msb, value_lsb);
-}
-
-/* caller must eventually call mlx5_cq_put on the returned cq */
-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);
-       cq = radix_tree_lookup(&table->tree, cqn);
-       if (likely(cq))
-               mlx5_cq_hold(cq);
-       spin_unlock(&table->lock);
-
-       return cq;
-}
-
-static void mlx5_eq_cq_completion(struct mlx5_eq *eq, u32 cqn)
-{
-       struct mlx5_core_cq *cq = mlx5_eq_cq_get(eq, cqn);
-
-       if (unlikely(!cq)) {
-               mlx5_core_warn(eq->dev, "Completion event for bogus CQ 0x%x\n", cqn);
-               return;
-       }
-
-       ++cq->arm_sn;
-
-       cq->comp(cq);
-
-       mlx5_cq_put(cq);
-}
-
-static void mlx5_eq_cq_event(struct mlx5_eq *eq, u32 cqn, int event_type)
+u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq_comp *eq)
 {
-       struct mlx5_core_cq *cq = mlx5_eq_cq_get(eq, cqn);
-
-       if (unlikely(!cq)) {
-               mlx5_core_warn(eq->dev, "Async event for bogus CQ 0x%x\n", cqn);
-               return;
-       }
+       u32 count_eqe;
 
-       cq->event(cq, event_type);
+       disable_irq(eq->core.irqn);
+       count_eqe = eq->core.cons_index;
+       mlx5_eq_comp_int(eq->core.irqn, eq);
+       count_eqe = eq->core.cons_index - count_eqe;
+       enable_irq(eq->core.irqn);
 
-       mlx5_cq_put(cq);
+       return count_eqe;
 }
 
-static irqreturn_t mlx5_eq_int(int irq, void *eq_ptr)
+static irqreturn_t mlx5_eq_async_int(int irq, void *eq_ptr)
 {
        struct mlx5_eq *eq = eq_ptr;
-       struct mlx5_core_dev *dev = eq->dev;
+       struct mlx5_eq_table *eqt;
+       struct mlx5_core_dev *dev;
        struct mlx5_eqe *eqe;
        int set_ci = 0;
-       u32 cqn = -1;
-       u32 rsn;
-       u8 port;
+
+       dev = eq->dev;
+       eqt = dev->priv.eq_table;
 
        while ((eqe = next_eqe_sw(eq))) {
                /*
@@ -480,116 +208,12 @@ static irqreturn_t mlx5_eq_int(int irq, void *eq_ptr)
                 */
                dma_rmb();
 
-               mlx5_core_dbg(eq->dev, "eqn %d, eqe type %s\n",
-                             eq->eqn, eqe_type_str(eqe->type));
-               switch (eqe->type) {
-               case MLX5_EVENT_TYPE_COMP:
-                       cqn = be32_to_cpu(eqe->data.comp.cqn) & 0xffffff;
-                       mlx5_eq_cq_completion(eq, cqn);
-                       break;
-               case MLX5_EVENT_TYPE_DCT_DRAINED:
-                       rsn = be32_to_cpu(eqe->data.dct.dctn) & 0xffffff;
-                       rsn |= (MLX5_RES_DCT << MLX5_USER_INDEX_LEN);
-                       mlx5_rsc_event(dev, rsn, eqe->type);
-                       break;
-               case MLX5_EVENT_TYPE_PATH_MIG:
-               case MLX5_EVENT_TYPE_COMM_EST:
-               case MLX5_EVENT_TYPE_SQ_DRAINED:
-               case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
-               case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
-               case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
-               case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
-               case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
-                       rsn = be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff;
-                       rsn |= (eqe->data.qp_srq.type << MLX5_USER_INDEX_LEN);
-                       mlx5_core_dbg(dev, "event %s(%d) arrived on resource 0x%x\n",
-                                     eqe_type_str(eqe->type), eqe->type, rsn);
-                       mlx5_rsc_event(dev, rsn, eqe->type);
-                       break;
-
-               case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT:
-               case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR:
-                       rsn = be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff;
-                       mlx5_core_dbg(dev, "SRQ event %s(%d): srqn 0x%x\n",
-                                     eqe_type_str(eqe->type), eqe->type, rsn);
-                       mlx5_srq_event(dev, rsn, eqe->type);
-                       break;
-
-               case MLX5_EVENT_TYPE_CMD:
-                       mlx5_cmd_comp_handler(dev, be32_to_cpu(eqe->data.cmd.vector), false);
-                       break;
+               if (likely(eqe->type < MLX5_EVENT_TYPE_MAX))
+                       atomic_notifier_call_chain(&eqt->nh[eqe->type], eqe->type, eqe);
+               else
+                       mlx5_core_warn_once(dev, "notifier_call_chain is not setup for eqe: %d\n", eqe->type);
 
-               case MLX5_EVENT_TYPE_PORT_CHANGE:
-                       port = (eqe->data.port.port >> 4) & 0xf;
-                       switch (eqe->sub_type) {
-                       case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
-                       case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
-                       case MLX5_PORT_CHANGE_SUBTYPE_LID:
-                       case MLX5_PORT_CHANGE_SUBTYPE_PKEY:
-                       case MLX5_PORT_CHANGE_SUBTYPE_GUID:
-                       case MLX5_PORT_CHANGE_SUBTYPE_CLIENT_REREG:
-                       case MLX5_PORT_CHANGE_SUBTYPE_INITIALIZED:
-                               if (dev->event)
-                                       dev->event(dev, port_subtype_event(eqe->sub_type),
-                                                  (unsigned long)port);
-                               break;
-                       default:
-                               mlx5_core_warn(dev, "Port event with unrecognized subtype: port %d, sub_type %d\n",
-                                              port, eqe->sub_type);
-                       }
-                       break;
-               case MLX5_EVENT_TYPE_CQ_ERROR:
-                       cqn = be32_to_cpu(eqe->data.cq_err.cqn) & 0xffffff;
-                       mlx5_core_warn(dev, "CQ error on CQN 0x%x, syndrome 0x%x\n",
-                                      cqn, eqe->data.cq_err.syndrome);
-                       mlx5_eq_cq_event(eq, cqn, eqe->type);
-                       break;
-
-               case MLX5_EVENT_TYPE_PAGE_REQUEST:
-                       {
-                               u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id);
-                               s32 npages = be32_to_cpu(eqe->data.req_pages.num_pages);
-
-                               mlx5_core_dbg(dev, "page request for func 0x%x, npages %d\n",
-                                             func_id, npages);
-                               mlx5_core_req_pages_handler(dev, func_id, npages);
-                       }
-                       break;
-
-               case MLX5_EVENT_TYPE_NIC_VPORT_CHANGE:
-                       mlx5_eswitch_vport_event(dev->priv.eswitch, eqe);
-                       break;
-
-               case MLX5_EVENT_TYPE_PORT_MODULE_EVENT:
-                       mlx5_port_module_event(dev, eqe);
-                       break;
-
-               case MLX5_EVENT_TYPE_PPS_EVENT:
-                       mlx5_pps_event(dev, eqe);
-                       break;
-
-               case MLX5_EVENT_TYPE_FPGA_ERROR:
-               case MLX5_EVENT_TYPE_FPGA_QP_ERROR:
-                       mlx5_fpga_event(dev, eqe->type, &eqe->data.raw);
-                       break;
-
-               case MLX5_EVENT_TYPE_TEMP_WARN_EVENT:
-                       mlx5_temp_warning_event(dev, eqe);
-                       break;
-
-               case MLX5_EVENT_TYPE_GENERAL_EVENT:
-                       general_event_handler(dev, eqe);
-                       break;
-
-               case MLX5_EVENT_TYPE_DEVICE_TRACER:
-                       mlx5_fw_tracer_event(dev, eqe);
-                       break;
-
-               default:
-                       mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n",
-                                      eqe->type, eq->eqn);
-                       break;
-               }
+               atomic_notifier_call_chain(&eqt->nh[MLX5_EVENT_TYPE_NOTIFY_ANY], eqe->type, eqe);
 
                ++eq->cons_index;
                ++set_ci;
@@ -608,30 +232,9 @@ static irqreturn_t mlx5_eq_int(int irq, void *eq_ptr)
 
        eq_update_ci(eq, 1);
 
-       if (cqn != -1)
-               tasklet_schedule(&eq->tasklet_ctx.task);
-
        return IRQ_HANDLED;
 }
 
-/* Some architectures don't latch interrupts when they are disabled, so using
- * mlx5_eq_poll_irq_disabled could end up losing interrupts while trying to
- * avoid losing them.  It is not recommended to use it, unless this is the last
- * resort.
- */
-u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq *eq)
-{
-       u32 count_eqe;
-
-       disable_irq(eq->irqn);
-       count_eqe = eq->cons_index;
-       mlx5_eq_int(eq->irqn, eq);
-       count_eqe = eq->cons_index - count_eqe;
-       enable_irq(eq->irqn);
-
-       return count_eqe;
-}
-
 static void init_eq_buf(struct mlx5_eq *eq)
 {
        struct mlx5_eqe *eqe;
@@ -643,39 +246,35 @@ static void init_eq_buf(struct mlx5_eq *eq)
        }
 }
 
-int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
-                      int nent, u64 mask, const char *name,
-                      enum mlx5_eq_type type)
+static int
+create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, const char *name,
+             struct mlx5_eq_param *param)
 {
+       struct mlx5_eq_table *eq_table = dev->priv.eq_table;
        struct mlx5_cq_table *cq_table = &eq->cq_table;
        u32 out[MLX5_ST_SZ_DW(create_eq_out)] = {0};
        struct mlx5_priv *priv = &dev->priv;
-       irq_handler_t handler;
+       u8 vecidx = param->index;
        __be64 *pas;
        void *eqc;
        int inlen;
        u32 *in;
        int err;
 
+       if (eq_table->irq_info[vecidx].context)
+               return -EEXIST;
+
        /* Init CQ table */
        memset(cq_table, 0, sizeof(*cq_table));
        spin_lock_init(&cq_table->lock);
        INIT_RADIX_TREE(&cq_table->tree, GFP_ATOMIC);
 
-       eq->type = type;
-       eq->nent = roundup_pow_of_two(nent + MLX5_NUM_SPARE_EQE);
+       eq->nent = roundup_pow_of_two(param->nent + MLX5_NUM_SPARE_EQE);
        eq->cons_index = 0;
        err = mlx5_buf_alloc(dev, eq->nent * MLX5_EQE_SIZE, &eq->buf);
        if (err)
                return err;
 
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       if (type == MLX5_EQ_TYPE_PF)
-               handler = mlx5_eq_pf_int;
-       else
-#endif
-               handler = mlx5_eq_int;
-
        init_eq_buf(eq);
 
        inlen = MLX5_ST_SZ_BYTES(create_eq_in) +
@@ -691,7 +290,7 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
        mlx5_fill_page_array(&eq->buf, pas);
 
        MLX5_SET(create_eq_in, in, opcode, MLX5_CMD_OP_CREATE_EQ);
-       MLX5_SET64(create_eq_in, in, event_bitmask, mask);
+       MLX5_SET64(create_eq_in, in, event_bitmask, param->mask);
 
        eqc = MLX5_ADDR_OF(create_eq_in, in, eq_context_entry);
        MLX5_SET(eqc, eqc, log_eq_size, ilog2(eq->nent));
@@ -704,15 +303,17 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
        if (err)
                goto err_in;
 
-       snprintf(priv->irq_info[vecidx].name, MLX5_MAX_IRQ_NAME, "%s@pci:%s",
+       snprintf(eq_table->irq_info[vecidx].name, MLX5_MAX_IRQ_NAME, "%s@pci:%s",
                 name, pci_name(dev->pdev));
+       eq_table->irq_info[vecidx].context = param->context;
 
+       eq->vecidx = vecidx;
        eq->eqn = MLX5_GET(create_eq_out, out, eq_number);
        eq->irqn = pci_irq_vector(dev->pdev, vecidx);
        eq->dev = dev;
        eq->doorbell = priv->uar->map + MLX5_EQ_DOORBEL_OFFSET;
-       err = request_irq(eq->irqn, handler, 0,
-                         priv->irq_info[vecidx].name, eq);
+       err = request_irq(eq->irqn, param->handler, 0,
+                         eq_table->irq_info[vecidx].name, param->context);
        if (err)
                goto err_eq;
 
@@ -720,21 +321,6 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
        if (err)
                goto err_irq;
 
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       if (type == MLX5_EQ_TYPE_PF) {
-               err = init_pf_ctx(&eq->pf_ctx, name);
-               if (err)
-                       goto err_irq;
-       } else
-#endif
-       {
-               INIT_LIST_HEAD(&eq->tasklet_ctx.list);
-               INIT_LIST_HEAD(&eq->tasklet_ctx.process_list);
-               spin_lock_init(&eq->tasklet_ctx.lock);
-               tasklet_init(&eq->tasklet_ctx.task, mlx5_cq_tasklet_cb,
-                            (unsigned long)&eq->tasklet_ctx);
-       }
-
        /* EQs are created in ARMED state
         */
        eq_update_ci(eq, 1);
@@ -756,27 +342,25 @@ err_buf:
        return err;
 }
 
-int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
+static int destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
 {
+       struct mlx5_eq_table *eq_table = dev->priv.eq_table;
+       struct mlx5_irq_info *irq_info;
        int err;
 
+       irq_info = &eq_table->irq_info[eq->vecidx];
+
        mlx5_debug_eq_remove(dev, eq);
-       free_irq(eq->irqn, eq);
+
+       free_irq(eq->irqn, irq_info->context);
+       irq_info->context = NULL;
+
        err = mlx5_cmd_destroy_eq(dev, eq->eqn);
        if (err)
                mlx5_core_warn(dev, "failed to destroy a previously created eq: eqn %d\n",
                               eq->eqn);
        synchronize_irq(eq->irqn);
 
-       if (eq->type == MLX5_EQ_TYPE_COMP) {
-               tasklet_disable(&eq->tasklet_ctx.task);
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       } else if (eq->type == MLX5_EQ_TYPE_PF) {
-               cancel_work_sync(&eq->pf_ctx.work);
-               destroy_workqueue(eq->pf_ctx.wq);
-               mempool_destroy(eq->pf_ctx.pool);
-#endif
-       }
        mlx5_buf_free(dev, &eq->buf);
 
        return err;
@@ -816,28 +400,106 @@ int mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq)
        return 0;
 }
 
-int mlx5_eq_init(struct mlx5_core_dev *dev)
+int mlx5_eq_table_init(struct mlx5_core_dev *dev)
 {
-       int err;
+       struct mlx5_eq_table *eq_table;
+       int i, err;
 
-       spin_lock_init(&dev->priv.eq_table.lock);
+       eq_table = kvzalloc(sizeof(*eq_table), GFP_KERNEL);
+       if (!eq_table)
+               return -ENOMEM;
+
+       dev->priv.eq_table = eq_table;
 
        err = mlx5_eq_debugfs_init(dev);
+       if (err)
+               goto kvfree_eq_table;
+
+       mutex_init(&eq_table->lock);
+       for (i = 0; i < MLX5_EVENT_TYPE_MAX; i++)
+               ATOMIC_INIT_NOTIFIER_HEAD(&eq_table->nh[i]);
 
+       return 0;
+
+kvfree_eq_table:
+       kvfree(eq_table);
+       dev->priv.eq_table = NULL;
        return err;
 }
 
-void mlx5_eq_cleanup(struct mlx5_core_dev *dev)
+void mlx5_eq_table_cleanup(struct mlx5_core_dev *dev)
 {
        mlx5_eq_debugfs_cleanup(dev);
+       kvfree(dev->priv.eq_table);
 }
 
-int mlx5_start_eqs(struct mlx5_core_dev *dev)
+/* Async EQs */
+
+static int create_async_eq(struct mlx5_core_dev *dev, const char *name,
+                          struct mlx5_eq *eq, struct mlx5_eq_param *param)
 {
-       struct mlx5_eq_table *table = &dev->priv.eq_table;
-       u64 async_event_mask = MLX5_ASYNC_EVENT_MASK;
+       struct mlx5_eq_table *eq_table = dev->priv.eq_table;
+       int err;
+
+       mutex_lock(&eq_table->lock);
+       if (param->index >= MLX5_EQ_MAX_ASYNC_EQS) {
+               err = -ENOSPC;
+               goto unlock;
+       }
+
+       err = create_map_eq(dev, eq, name, param);
+unlock:
+       mutex_unlock(&eq_table->lock);
+       return err;
+}
+
+static int destroy_async_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
+{
+       struct mlx5_eq_table *eq_table = dev->priv.eq_table;
        int err;
 
+       mutex_lock(&eq_table->lock);
+       err = destroy_unmap_eq(dev, eq);
+       mutex_unlock(&eq_table->lock);
+       return err;
+}
+
+static int cq_err_event_notifier(struct notifier_block *nb,
+                                unsigned long type, void *data)
+{
+       struct mlx5_eq_table *eqt;
+       struct mlx5_core_cq *cq;
+       struct mlx5_eqe *eqe;
+       struct mlx5_eq *eq;
+       u32 cqn;
+
+       /* type == MLX5_EVENT_TYPE_CQ_ERROR */
+
+       eqt = mlx5_nb_cof(nb, struct mlx5_eq_table, cq_err_nb);
+       eq  = &eqt->async_eq;
+       eqe = data;
+
+       cqn = be32_to_cpu(eqe->data.cq_err.cqn) & 0xffffff;
+       mlx5_core_warn(eq->dev, "CQ error on CQN 0x%x, syndrome 0x%x\n",
+                      cqn, eqe->data.cq_err.syndrome);
+
+       cq = mlx5_eq_cq_get(eq, cqn);
+       if (unlikely(!cq)) {
+               mlx5_core_warn(eq->dev, "Async event for bogus CQ 0x%x\n", cqn);
+               return NOTIFY_OK;
+       }
+
+       cq->event(cq, type);
+
+       mlx5_cq_put(cq);
+
+       return NOTIFY_OK;
+}
+
+static u64 gather_async_events_mask(struct mlx5_core_dev *dev)
+{
+       u64 async_event_mask = MLX5_ASYNC_EVENT_MASK;
+
        if (MLX5_VPORT_MANAGER(dev))
                async_event_mask |= (1ull << MLX5_EVENT_TYPE_NIC_VPORT_CHANGE);
 
@@ -865,127 +527,521 @@ int mlx5_start_eqs(struct mlx5_core_dev *dev)
        if (MLX5_CAP_MCAM_REG(dev, tracer_registers))
                async_event_mask |= (1ull << MLX5_EVENT_TYPE_DEVICE_TRACER);
 
-       err = mlx5_create_map_eq(dev, &table->cmd_eq, MLX5_EQ_VEC_CMD,
-                                MLX5_NUM_CMD_EQE, 1ull << MLX5_EVENT_TYPE_CMD,
-                                "mlx5_cmd_eq", MLX5_EQ_TYPE_ASYNC);
+       if (MLX5_CAP_GEN(dev, max_num_of_monitor_counters))
+               async_event_mask |= (1ull << MLX5_EVENT_TYPE_MONITOR_COUNTER);
+
+       return async_event_mask;
+}
+
+static int create_async_eqs(struct mlx5_core_dev *dev)
+{
+       struct mlx5_eq_table *table = dev->priv.eq_table;
+       struct mlx5_eq_param param = {};
+       int err;
+
+       MLX5_NB_INIT(&table->cq_err_nb, cq_err_event_notifier, CQ_ERROR);
+       mlx5_eq_notifier_register(dev, &table->cq_err_nb);
+
+       param = (struct mlx5_eq_param) {
+               .index = MLX5_EQ_CMD_IDX,
+               .mask = 1ull << MLX5_EVENT_TYPE_CMD,
+               .nent = MLX5_NUM_CMD_EQE,
+               .context = &table->cmd_eq,
+               .handler = mlx5_eq_async_int,
+       };
+       err = create_async_eq(dev, "mlx5_cmd_eq", &table->cmd_eq, &param);
        if (err) {
                mlx5_core_warn(dev, "failed to create cmd EQ %d\n", err);
-               return err;
+               goto err0;
        }
 
        mlx5_cmd_use_events(dev);
 
-       err = mlx5_create_map_eq(dev, &table->async_eq, MLX5_EQ_VEC_ASYNC,
-                                MLX5_NUM_ASYNC_EQE, async_event_mask,
-                                "mlx5_async_eq", MLX5_EQ_TYPE_ASYNC);
+       param = (struct mlx5_eq_param) {
+               .index = MLX5_EQ_ASYNC_IDX,
+               .mask = gather_async_events_mask(dev),
+               .nent = MLX5_NUM_ASYNC_EQE,
+               .context = &table->async_eq,
+               .handler = mlx5_eq_async_int,
+       };
+       err = create_async_eq(dev, "mlx5_async_eq", &table->async_eq, &param);
        if (err) {
                mlx5_core_warn(dev, "failed to create async EQ %d\n", err);
                goto err1;
        }
 
-       err = mlx5_create_map_eq(dev, &table->pages_eq,
-                                MLX5_EQ_VEC_PAGES,
-                                /* TODO: sriov max_vf + */ 1,
-                                1 << MLX5_EVENT_TYPE_PAGE_REQUEST, "mlx5_pages_eq",
-                                MLX5_EQ_TYPE_ASYNC);
+       param = (struct mlx5_eq_param) {
+               .index = MLX5_EQ_PAGEREQ_IDX,
+               .mask =  1 << MLX5_EVENT_TYPE_PAGE_REQUEST,
+               .nent = /* TODO: sriov max_vf + */ 1,
+               .context = &table->pages_eq,
+               .handler = mlx5_eq_async_int,
+       };
+       err = create_async_eq(dev, "mlx5_pages_eq", &table->pages_eq, &param);
        if (err) {
                mlx5_core_warn(dev, "failed to create pages EQ %d\n", err);
                goto err2;
        }
 
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       if (MLX5_CAP_GEN(dev, pg)) {
-               err = mlx5_create_map_eq(dev, &table->pfault_eq,
-                                        MLX5_EQ_VEC_PFAULT,
-                                        MLX5_NUM_ASYNC_EQE,
-                                        1 << MLX5_EVENT_TYPE_PAGE_FAULT,
-                                        "mlx5_page_fault_eq",
-                                        MLX5_EQ_TYPE_PF);
-               if (err) {
-                       mlx5_core_warn(dev, "failed to create page fault EQ %d\n",
-                                      err);
-                       goto err3;
-               }
-       }
-
-       return err;
-err3:
-       mlx5_destroy_unmap_eq(dev, &table->pages_eq);
-#else
        return err;
-#endif
 
 err2:
-       mlx5_destroy_unmap_eq(dev, &table->async_eq);
+       destroy_async_eq(dev, &table->async_eq);
 
 err1:
        mlx5_cmd_use_polling(dev);
-       mlx5_destroy_unmap_eq(dev, &table->cmd_eq);
+       destroy_async_eq(dev, &table->cmd_eq);
+err0:
+       mlx5_eq_notifier_unregister(dev, &table->cq_err_nb);
        return err;
 }
 
-void mlx5_stop_eqs(struct mlx5_core_dev *dev)
+static void destroy_async_eqs(struct mlx5_core_dev *dev)
 {
-       struct mlx5_eq_table *table = &dev->priv.eq_table;
+       struct mlx5_eq_table *table = dev->priv.eq_table;
        int err;
 
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       if (MLX5_CAP_GEN(dev, pg)) {
-               err = mlx5_destroy_unmap_eq(dev, &table->pfault_eq);
-               if (err)
-                       mlx5_core_err(dev, "failed to destroy page fault eq, err(%d)\n",
-                                     err);
-       }
-#endif
-
-       err = mlx5_destroy_unmap_eq(dev, &table->pages_eq);
+       err = destroy_async_eq(dev, &table->pages_eq);
        if (err)
                mlx5_core_err(dev, "failed to destroy pages eq, err(%d)\n",
                              err);
 
-       err = mlx5_destroy_unmap_eq(dev, &table->async_eq);
+       err = destroy_async_eq(dev, &table->async_eq);
        if (err)
                mlx5_core_err(dev, "failed to destroy async eq, err(%d)\n",
                              err);
+
        mlx5_cmd_use_polling(dev);
 
-       err = mlx5_destroy_unmap_eq(dev, &table->cmd_eq);
+       err = destroy_async_eq(dev, &table->cmd_eq);
        if (err)
                mlx5_core_err(dev, "failed to destroy command eq, err(%d)\n",
                              err);
+
+       mlx5_eq_notifier_unregister(dev, &table->cq_err_nb);
 }
 
-int mlx5_core_eq_query(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
-                      u32 *out, int outlen)
+struct mlx5_eq *mlx5_get_async_eq(struct mlx5_core_dev *dev)
 {
-       u32 in[MLX5_ST_SZ_DW(query_eq_in)] = {0};
+       return &dev->priv.eq_table->async_eq;
+}
 
-       MLX5_SET(query_eq_in, in, opcode, MLX5_CMD_OP_QUERY_EQ);
-       MLX5_SET(query_eq_in, in, eq_number, eq->eqn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
+void mlx5_eq_synchronize_async_irq(struct mlx5_core_dev *dev)
+{
+       synchronize_irq(dev->priv.eq_table->async_eq.irqn);
+}
+
+void mlx5_eq_synchronize_cmd_irq(struct mlx5_core_dev *dev)
+{
+       synchronize_irq(dev->priv.eq_table->cmd_eq.irqn);
+}
+
+/* Generic EQ API for mlx5_core consumers
+ * Needed For RDMA ODP EQ for now
+ */
+struct mlx5_eq *
+mlx5_eq_create_generic(struct mlx5_core_dev *dev, const char *name,
+                      struct mlx5_eq_param *param)
+{
+       struct mlx5_eq *eq = kvzalloc(sizeof(*eq), GFP_KERNEL);
+       int err;
+
+       if (!eq)
+               return ERR_PTR(-ENOMEM);
+
+       err = create_async_eq(dev, name, eq, param);
+       if (err) {
+               kvfree(eq);
+               eq = ERR_PTR(err);
+       }
+
+       return eq;
+}
+EXPORT_SYMBOL(mlx5_eq_create_generic);
+
+int mlx5_eq_destroy_generic(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
+{
+       int err;
+
+       if (IS_ERR(eq))
+               return -EINVAL;
+
+       err = destroy_async_eq(dev, eq);
+       if (err)
+               goto out;
+
+       kvfree(eq);
+out:
+       return err;
+}
+EXPORT_SYMBOL(mlx5_eq_destroy_generic);
+
+struct mlx5_eqe *mlx5_eq_get_eqe(struct mlx5_eq *eq, u32 cc)
+{
+       u32 ci = eq->cons_index + cc;
+       struct mlx5_eqe *eqe;
+
+       eqe = get_eqe(eq, ci & (eq->nent - 1));
+       eqe = ((eqe->owner & 1) ^ !!(ci & eq->nent)) ? NULL : eqe;
+       /* Make sure we read EQ entry contents after we've
+        * checked the ownership bit.
+        */
+       if (eqe)
+               dma_rmb();
+
+       return eqe;
+}
+EXPORT_SYMBOL(mlx5_eq_get_eqe);
+
+void mlx5_eq_update_ci(struct mlx5_eq *eq, u32 cc, bool arm)
+{
+       __be32 __iomem *addr = eq->doorbell + (arm ? 0 : 2);
+       u32 val;
+
+       eq->cons_index += cc;
+       val = (eq->cons_index & 0xffffff) | (eq->eqn << 24);
+
+       __raw_writel((__force u32)cpu_to_be32(val), addr);
+       /* We still want ordering, just not swabbing, so add a barrier */
+       mb();
+}
+EXPORT_SYMBOL(mlx5_eq_update_ci);
+
+/* Completion EQs */
+
+static int set_comp_irq_affinity_hint(struct mlx5_core_dev *mdev, int i)
+{
+       struct mlx5_priv *priv  = &mdev->priv;
+       int vecidx = MLX5_EQ_VEC_COMP_BASE + i;
+       int irq = pci_irq_vector(mdev->pdev, vecidx);
+       struct mlx5_irq_info *irq_info = &priv->eq_table->irq_info[vecidx];
+
+       if (!zalloc_cpumask_var(&irq_info->mask, GFP_KERNEL)) {
+               mlx5_core_warn(mdev, "zalloc_cpumask_var failed");
+               return -ENOMEM;
+       }
+
+       cpumask_set_cpu(cpumask_local_spread(i, priv->numa_node),
+                       irq_info->mask);
+
+       if (IS_ENABLED(CONFIG_SMP) &&
+           irq_set_affinity_hint(irq, irq_info->mask))
+               mlx5_core_warn(mdev, "irq_set_affinity_hint failed, irq 0x%.4x", irq);
+
+       return 0;
+}
+
+static void clear_comp_irq_affinity_hint(struct mlx5_core_dev *mdev, int i)
+{
+       int vecidx = MLX5_EQ_VEC_COMP_BASE + i;
+       struct mlx5_priv *priv  = &mdev->priv;
+       int irq = pci_irq_vector(mdev->pdev, vecidx);
+       struct mlx5_irq_info *irq_info = &priv->eq_table->irq_info[vecidx];
+
+       irq_set_affinity_hint(irq, NULL);
+       free_cpumask_var(irq_info->mask);
+}
+
+static int set_comp_irq_affinity_hints(struct mlx5_core_dev *mdev)
+{
+       int err;
+       int i;
+
+       for (i = 0; i < mdev->priv.eq_table->num_comp_vectors; i++) {
+               err = set_comp_irq_affinity_hint(mdev, i);
+               if (err)
+                       goto err_out;
+       }
+
+       return 0;
+
+err_out:
+       for (i--; i >= 0; i--)
+               clear_comp_irq_affinity_hint(mdev, i);
+
+       return err;
+}
+
+static void clear_comp_irqs_affinity_hints(struct mlx5_core_dev *mdev)
+{
+       int i;
+
+       for (i = 0; i < mdev->priv.eq_table->num_comp_vectors; i++)
+               clear_comp_irq_affinity_hint(mdev, i);
+}
+
+static void destroy_comp_eqs(struct mlx5_core_dev *dev)
+{
+       struct mlx5_eq_table *table = dev->priv.eq_table;
+       struct mlx5_eq_comp *eq, *n;
+
+       clear_comp_irqs_affinity_hints(dev);
+
+#ifdef CONFIG_RFS_ACCEL
+       if (table->rmap) {
+               free_irq_cpu_rmap(table->rmap);
+               table->rmap = NULL;
+       }
+#endif
+       list_for_each_entry_safe(eq, n, &table->comp_eqs_list, list) {
+               list_del(&eq->list);
+               if (destroy_unmap_eq(dev, &eq->core))
+                       mlx5_core_warn(dev, "failed to destroy comp EQ 0x%x\n",
+                                      eq->core.eqn);
+               tasklet_disable(&eq->tasklet_ctx.task);
+               kfree(eq);
+       }
+}
+
+static int create_comp_eqs(struct mlx5_core_dev *dev)
+{
+       struct mlx5_eq_table *table = dev->priv.eq_table;
+       char name[MLX5_MAX_IRQ_NAME];
+       struct mlx5_eq_comp *eq;
+       int ncomp_vec;
+       int nent;
+       int err;
+       int i;
+
+       INIT_LIST_HEAD(&table->comp_eqs_list);
+       ncomp_vec = table->num_comp_vectors;
+       nent = MLX5_COMP_EQ_SIZE;
+#ifdef CONFIG_RFS_ACCEL
+       table->rmap = alloc_irq_cpu_rmap(ncomp_vec);
+       if (!table->rmap)
+               return -ENOMEM;
+#endif
+       for (i = 0; i < ncomp_vec; i++) {
+               int vecidx = i + MLX5_EQ_VEC_COMP_BASE;
+               struct mlx5_eq_param param = {};
+
+               eq = kzalloc(sizeof(*eq), GFP_KERNEL);
+               if (!eq) {
+                       err = -ENOMEM;
+                       goto clean;
+               }
+
+               INIT_LIST_HEAD(&eq->tasklet_ctx.list);
+               INIT_LIST_HEAD(&eq->tasklet_ctx.process_list);
+               spin_lock_init(&eq->tasklet_ctx.lock);
+               tasklet_init(&eq->tasklet_ctx.task, mlx5_cq_tasklet_cb,
+                            (unsigned long)&eq->tasklet_ctx);
+
+#ifdef CONFIG_RFS_ACCEL
+               irq_cpu_rmap_add(table->rmap, pci_irq_vector(dev->pdev, vecidx));
+#endif
+               snprintf(name, MLX5_MAX_IRQ_NAME, "mlx5_comp%d", i);
+               param = (struct mlx5_eq_param) {
+                       .index = vecidx,
+                       .mask = 0,
+                       .nent = nent,
+                       .context = &eq->core,
+                       .handler = mlx5_eq_comp_int
+               };
+               err = create_map_eq(dev, &eq->core, name, &param);
+               if (err) {
+                       kfree(eq);
+                       goto clean;
+               }
+               mlx5_core_dbg(dev, "allocated completion EQN %d\n", eq->core.eqn);
+               /* add tail, to keep the list ordered, for mlx5_vector2eqn to work */
+               list_add_tail(&eq->list, &table->comp_eqs_list);
+       }
+
+       err = set_comp_irq_affinity_hints(dev);
+       if (err) {
+               mlx5_core_err(dev, "Failed to alloc affinity hint cpumask\n");
+               goto clean;
+       }
+
+       return 0;
+
+clean:
+       destroy_comp_eqs(dev);
+       return err;
+}
+
+int mlx5_vector2eqn(struct mlx5_core_dev *dev, int vector, int *eqn,
+                   unsigned int *irqn)
+{
+       struct mlx5_eq_table *table = dev->priv.eq_table;
+       struct mlx5_eq_comp *eq, *n;
+       int err = -ENOENT;
+       int i = 0;
+
+       list_for_each_entry_safe(eq, n, &table->comp_eqs_list, list) {
+               if (i++ == vector) {
+                       *eqn = eq->core.eqn;
+                       *irqn = eq->core.irqn;
+                       err = 0;
+                       break;
+               }
+       }
+
+       return err;
+}
+EXPORT_SYMBOL(mlx5_vector2eqn);
+
+unsigned int mlx5_comp_vectors_count(struct mlx5_core_dev *dev)
+{
+       return dev->priv.eq_table->num_comp_vectors;
+}
+EXPORT_SYMBOL(mlx5_comp_vectors_count);
+
+struct cpumask *
+mlx5_comp_irq_get_affinity_mask(struct mlx5_core_dev *dev, int vector)
+{
+       /* TODO: consider irq_get_affinity_mask(irq) */
+       return dev->priv.eq_table->irq_info[vector + MLX5_EQ_VEC_COMP_BASE].mask;
+}
+EXPORT_SYMBOL(mlx5_comp_irq_get_affinity_mask);
+
+struct cpu_rmap *mlx5_eq_table_get_rmap(struct mlx5_core_dev *dev)
+{
+#ifdef CONFIG_RFS_ACCEL
+       return dev->priv.eq_table->rmap;
+#else
+       return NULL;
+#endif
+}
+
+struct mlx5_eq_comp *mlx5_eqn2comp_eq(struct mlx5_core_dev *dev, int eqn)
+{
+       struct mlx5_eq_table *table = dev->priv.eq_table;
+       struct mlx5_eq_comp *eq;
+
+       list_for_each_entry(eq, &table->comp_eqs_list, list) {
+               if (eq->core.eqn == eqn)
+                       return eq;
+       }
+
+       return ERR_PTR(-ENOENT);
 }
 
 /* This function should only be called after mlx5_cmd_force_teardown_hca */
 void mlx5_core_eq_free_irqs(struct mlx5_core_dev *dev)
 {
-       struct mlx5_eq_table *table = &dev->priv.eq_table;
-       struct mlx5_eq *eq;
+       struct mlx5_eq_table *table = dev->priv.eq_table;
+       int i, max_eqs;
+
+       clear_comp_irqs_affinity_hints(dev);
 
 #ifdef CONFIG_RFS_ACCEL
-       if (dev->rmap) {
-               free_irq_cpu_rmap(dev->rmap);
-               dev->rmap = NULL;
+       if (table->rmap) {
+               free_irq_cpu_rmap(table->rmap);
+               table->rmap = NULL;
        }
 #endif
-       list_for_each_entry(eq, &table->comp_eqs_list, list)
-               free_irq(eq->irqn, eq);
-
-       free_irq(table->pages_eq.irqn, &table->pages_eq);
-       free_irq(table->async_eq.irqn, &table->async_eq);
-       free_irq(table->cmd_eq.irqn, &table->cmd_eq);
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       if (MLX5_CAP_GEN(dev, pg))
-               free_irq(table->pfault_eq.irqn, &table->pfault_eq);
-#endif
+
+       mutex_lock(&table->lock); /* sync with create/destroy_async_eq */
+       max_eqs = table->num_comp_vectors + MLX5_EQ_VEC_COMP_BASE;
+       for (i = max_eqs - 1; i >= 0; i--) {
+               if (!table->irq_info[i].context)
+                       continue;
+               free_irq(pci_irq_vector(dev->pdev, i), table->irq_info[i].context);
+               table->irq_info[i].context = NULL;
+       }
+       mutex_unlock(&table->lock);
+       pci_free_irq_vectors(dev->pdev);
+}
+
+static int alloc_irq_vectors(struct mlx5_core_dev *dev)
+{
+       struct mlx5_priv *priv = &dev->priv;
+       struct mlx5_eq_table *table = priv->eq_table;
+       int num_eqs = MLX5_CAP_GEN(dev, max_num_eqs) ?
+                     MLX5_CAP_GEN(dev, max_num_eqs) :
+                     1 << MLX5_CAP_GEN(dev, log_max_eq);
+       int nvec;
+       int err;
+
+       nvec = MLX5_CAP_GEN(dev, num_ports) * num_online_cpus() +
+              MLX5_EQ_VEC_COMP_BASE;
+       nvec = min_t(int, nvec, num_eqs);
+       if (nvec <= MLX5_EQ_VEC_COMP_BASE)
+               return -ENOMEM;
+
+       table->irq_info = kcalloc(nvec, sizeof(*table->irq_info), GFP_KERNEL);
+       if (!table->irq_info)
+               return -ENOMEM;
+
+       nvec = pci_alloc_irq_vectors(dev->pdev, MLX5_EQ_VEC_COMP_BASE + 1,
+                                    nvec, PCI_IRQ_MSIX);
+       if (nvec < 0) {
+               err = nvec;
+               goto err_free_irq_info;
+       }
+
+       table->num_comp_vectors = nvec - MLX5_EQ_VEC_COMP_BASE;
+
+       return 0;
+
+err_free_irq_info:
+       kfree(table->irq_info);
+       return err;
+}
+
+static void free_irq_vectors(struct mlx5_core_dev *dev)
+{
+       struct mlx5_priv *priv = &dev->priv;
+
        pci_free_irq_vectors(dev->pdev);
+       kfree(priv->eq_table->irq_info);
+}
+
+int mlx5_eq_table_create(struct mlx5_core_dev *dev)
+{
+       int err;
+
+       err = alloc_irq_vectors(dev);
+       if (err) {
+               mlx5_core_err(dev, "alloc irq vectors failed\n");
+               return err;
+       }
+
+       err = create_async_eqs(dev);
+       if (err) {
+               mlx5_core_err(dev, "Failed to create async EQs\n");
+               goto err_async_eqs;
+       }
+
+       err = create_comp_eqs(dev);
+       if (err) {
+               mlx5_core_err(dev, "Failed to create completion EQs\n");
+               goto err_comp_eqs;
+       }
+
+       return 0;
+err_comp_eqs:
+       destroy_async_eqs(dev);
+err_async_eqs:
+       free_irq_vectors(dev);
+       return err;
+}
+
+void mlx5_eq_table_destroy(struct mlx5_core_dev *dev)
+{
+       destroy_comp_eqs(dev);
+       destroy_async_eqs(dev);
+       free_irq_vectors(dev);
+}
+
+int mlx5_eq_notifier_register(struct mlx5_core_dev *dev, struct mlx5_nb *nb)
+{
+       struct mlx5_eq_table *eqt = dev->priv.eq_table;
+
+       if (nb->event_type >= MLX5_EVENT_TYPE_MAX)
+               return -EINVAL;
+
+       return atomic_notifier_chain_register(&eqt->nh[nb->event_type], &nb->nb);
+}
+
+int mlx5_eq_notifier_unregister(struct mlx5_core_dev *dev, struct mlx5_nb *nb)
+{
+       struct mlx5_eq_table *eqt = dev->priv.eq_table;
+
+       if (nb->event_type >= MLX5_EVENT_TYPE_MAX)
+               return -EINVAL;
+
+       return atomic_notifier_chain_unregister(&eqt->nh[nb->event_type], &nb->nb);
 }
index d004957..a44ea7b 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/mlx5/vport.h>
 #include <linux/mlx5/fs.h>
 #include "mlx5_core.h"
+#include "lib/eq.h"
 #include "eswitch.h"
 #include "fs_core.h"
 
@@ -1567,7 +1568,6 @@ static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num)
        /* Mark this vport as disabled to discard new events */
        vport->enabled = false;
 
-       synchronize_irq(pci_irq_vector(esw->dev->pdev, MLX5_EQ_VEC_ASYNC));
        /* Wait for current already scheduled events to complete */
        flush_workqueue(esw->work_queue);
        /* Disable events from this vport */
@@ -1593,10 +1593,25 @@ static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num)
        mutex_unlock(&esw->state_lock);
 }
 
+static int eswitch_vport_event(struct notifier_block *nb,
+                              unsigned long type, void *data)
+{
+       struct mlx5_eswitch *esw = mlx5_nb_cof(nb, struct mlx5_eswitch, nb);
+       struct mlx5_eqe *eqe = data;
+       struct mlx5_vport *vport;
+       u16 vport_num;
+
+       vport_num = be16_to_cpu(eqe->data.vport_change.vport_num);
+       vport = &esw->vports[vport_num];
+       if (vport->enabled)
+               queue_work(esw->work_queue, &vport->vport_change_handler);
+
+       return NOTIFY_OK;
+}
+
 /* Public E-Switch API */
 #define ESW_ALLOWED(esw) ((esw) && MLX5_ESWITCH_MANAGER((esw)->dev))
 
-
 int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode)
 {
        int err;
@@ -1615,13 +1630,16 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode)
                esw_warn(esw->dev, "E-Switch engress ACL is not supported by FW\n");
 
        esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d) mode (%d)\n", nvfs, mode);
+
        esw->mode = mode;
 
+       mlx5_lag_update(esw->dev);
+
        if (mode == SRIOV_LEGACY) {
                err = esw_create_legacy_fdb_table(esw);
        } 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);
        }
 
@@ -1640,6 +1658,11 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode)
        for (i = 0; i <= nvfs; i++)
                esw_enable_vport(esw, i, enabled_events);
 
+       if (mode == SRIOV_LEGACY) {
+               MLX5_NB_INIT(&esw->nb, eswitch_vport_event, NIC_VPORT_CHANGE);
+               mlx5_eq_notifier_register(esw->dev, &esw->nb);
+       }
+
        esw_info(esw->dev, "SRIOV enabled: active vports(%d)\n",
                 esw->enabled_vports);
        return 0;
@@ -1647,8 +1670,10 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode)
 abort:
        esw->mode = SRIOV_NONE;
 
-       if (mode == SRIOV_OFFLOADS)
+       if (mode == SRIOV_OFFLOADS) {
                mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+               mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_ETH);
+       }
 
        return err;
 }
@@ -1669,6 +1694,9 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
        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);
 
@@ -1685,8 +1713,12 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
        old_mode = esw->mode;
        esw->mode = SRIOV_NONE;
 
-       if (old_mode == SRIOV_OFFLOADS)
+       mlx5_lag_update(esw->dev);
+
+       if (old_mode == SRIOV_OFFLOADS) {
                mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+               mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_ETH);
+       }
 }
 
 int mlx5_eswitch_init(struct mlx5_core_dev *dev)
@@ -1777,23 +1809,6 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw)
        kfree(esw);
 }
 
-void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe)
-{
-       struct mlx5_eqe_vport_change *vc_eqe = &eqe->data.vport_change;
-       u16 vport_num = be16_to_cpu(vc_eqe->vport_num);
-       struct mlx5_vport *vport;
-
-       if (!esw) {
-               pr_warn("MLX5 E-Switch: vport %d got an event while eswitch is not initialized\n",
-                       vport_num);
-               return;
-       }
-
-       vport = &esw->vports[vport_num];
-       if (vport->enabled)
-               queue_work(esw->work_queue, &vport->vport_change_handler);
-}
-
 /* Vport Administration */
 #define LEGAL_VPORT(esw, vport) (vport >= 0 && vport < esw->total_vports)
 
@@ -2219,3 +2234,14 @@ u8 mlx5_eswitch_mode(struct mlx5_eswitch *esw)
        return ESW_ALLOWED(esw) ? esw->mode : SRIOV_NONE;
 }
 EXPORT_SYMBOL_GPL(mlx5_eswitch_mode);
+
+bool mlx5_esw_lag_prereq(struct mlx5_core_dev *dev0, struct mlx5_core_dev *dev1)
+{
+       if ((dev0->priv.eswitch->mode == SRIOV_NONE &&
+            dev1->priv.eswitch->mode == SRIOV_NONE) ||
+           (dev0->priv.eswitch->mode == SRIOV_OFFLOADS &&
+            dev1->priv.eswitch->mode == SRIOV_OFFLOADS))
+               return true;
+
+       return false;
+}
index aaafc9f..9c89eea 100644 (file)
@@ -143,6 +143,8 @@ struct mlx5_eswitch_fdb {
                struct offloads_fdb {
                        struct mlx5_flow_table *slow_fdb;
                        struct mlx5_flow_group *send_to_vport_grp;
+                       struct mlx5_flow_group *peer_miss_grp;
+                       struct mlx5_flow_handle **peer_miss_rules;
                        struct mlx5_flow_group *miss_grp;
                        struct mlx5_flow_handle *miss_rule_uni;
                        struct mlx5_flow_handle *miss_rule_multi;
@@ -165,6 +167,8 @@ struct mlx5_esw_offload {
        struct mlx5_flow_table *ft_offloads;
        struct mlx5_flow_group *vport_rx_group;
        struct mlx5_eswitch_rep *vport_reps;
+       struct list_head peer_flows;
+       struct mutex peer_mutex;
        DECLARE_HASHTABLE(encap_tbl, 8);
        DECLARE_HASHTABLE(mod_hdr_tbl, 8);
        u8 inline_mode;
@@ -181,6 +185,7 @@ struct esw_mc_addr { /* SRIOV only */
 
 struct mlx5_eswitch {
        struct mlx5_core_dev    *dev;
+       struct mlx5_nb          nb;
        struct mlx5_eswitch_fdb fdb_table;
        struct hlist_head       mc_table[MLX5_L2_ADDR_HASH_SIZE];
        struct workqueue_struct *work_queue;
@@ -211,7 +216,6 @@ int esw_offloads_init_reps(struct mlx5_eswitch *esw);
 /* E-Switch API */
 int mlx5_eswitch_init(struct mlx5_core_dev *dev);
 void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw);
-void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe);
 int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode);
 void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw);
 int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
@@ -281,13 +285,17 @@ enum mlx5_flow_match_level {
 /* current maximum for flow based vport multicasting */
 #define MLX5_MAX_FLOW_FWD_VPORTS 2
 
+enum {
+       MLX5_ESW_DEST_ENCAP         = BIT(0),
+       MLX5_ESW_DEST_ENCAP_VALID   = BIT(1),
+};
+
 struct mlx5_esw_flow_attr {
        struct mlx5_eswitch_rep *in_rep;
-       struct mlx5_eswitch_rep *out_rep[MLX5_MAX_FLOW_FWD_VPORTS];
-       struct mlx5_core_dev    *out_mdev[MLX5_MAX_FLOW_FWD_VPORTS];
        struct mlx5_core_dev    *in_mdev;
+       struct mlx5_core_dev    *counter_dev;
 
-       int mirror_count;
+       int split_count;
        int out_count;
 
        int     action;
@@ -296,7 +304,12 @@ struct mlx5_esw_flow_attr {
        u8      vlan_prio[MLX5_FS_VLAN_DEPTH];
        u8      total_vlan;
        bool    vlan_handled;
-       u32     encap_id;
+       struct {
+               u32 flags;
+               struct mlx5_eswitch_rep *rep;
+               struct mlx5_core_dev *mdev;
+               u32 encap_id;
+       } dests[MLX5_MAX_FLOW_FWD_VPORTS];
        u32     mod_hdr_id;
        u8      match_level;
        struct mlx5_fc *counter;
@@ -338,6 +351,9 @@ static inline bool mlx5_eswitch_vlan_actions_supported(struct mlx5_core_dev *dev
                MLX5_CAP_ESW_FLOWTABLE_FDB(dev, push_vlan_2);
 }
 
+bool mlx5_esw_lag_prereq(struct mlx5_core_dev *dev0,
+                        struct mlx5_core_dev *dev1);
+
 #define MLX5_DEBUG_ESWITCH_MASK BIT(3)
 
 #define esw_info(dev, format, ...)                             \
@@ -352,9 +368,9 @@ static inline bool mlx5_eswitch_vlan_actions_supported(struct mlx5_core_dev *dev
 /* eswitch API stubs */
 static inline int  mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; }
 static inline void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) {}
-static inline void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe) {}
 static inline int  mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) { return 0; }
 static inline void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) {}
+static inline bool mlx5_esw_lag_prereq(struct mlx5_core_dev *dev0, struct mlx5_core_dev *dev1) { return true; }
 
 #define FDB_MAX_CHAIN 1
 #define FDB_SLOW_PATH_CHAIN (FDB_MAX_CHAIN + 1)
index 9eac137..53065b6 100644 (file)
@@ -39,6 +39,7 @@
 #include "eswitch.h"
 #include "en.h"
 #include "fs_core.h"
+#include "lib/devcom.h"
 
 enum {
        FDB_FAST_PATH = 0,
@@ -81,7 +82,7 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
 {
        struct mlx5_flow_destination dest[MLX5_MAX_FLOW_FWD_VPORTS + 1] = {};
        struct mlx5_flow_act flow_act = { .flags = FLOW_ACT_NO_APPEND, };
-       bool mirror = !!(attr->mirror_count);
+       bool split = !!(attr->split_count);
        struct mlx5_flow_handle *rule;
        struct mlx5_flow_table *fdb;
        int j, i = 0;
@@ -120,13 +121,21 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
                        dest[i].ft = ft;
                        i++;
                } else {
-                       for (j = attr->mirror_count; j < attr->out_count; j++) {
+                       for (j = attr->split_count; j < attr->out_count; j++) {
                                dest[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
-                               dest[i].vport.num = attr->out_rep[j]->vport;
+                               dest[i].vport.num = attr->dests[j].rep->vport;
                                dest[i].vport.vhca_id =
-                                       MLX5_CAP_GEN(attr->out_mdev[j], vhca_id);
-                               dest[i].vport.vhca_id_valid =
-                                       !!MLX5_CAP_ESW(esw->dev, merged_eswitch);
+                                       MLX5_CAP_GEN(attr->dests[j].mdev, vhca_id);
+                               if (MLX5_CAP_ESW(esw->dev, merged_eswitch))
+                                       dest[i].vport.flags |=
+                                               MLX5_FLOW_DEST_VPORT_VHCA_ID;
+                               if (attr->dests[j].flags & MLX5_ESW_DEST_ENCAP) {
+                                       flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
+                                       flow_act.reformat_id = attr->dests[j].encap_id;
+                                       dest[i].vport.flags |= MLX5_FLOW_DEST_VPORT_REFORMAT_ID;
+                                       dest[i].vport.reformat_id =
+                                               attr->dests[j].encap_id;
+                               }
                                i++;
                        }
                }
@@ -163,10 +172,7 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
        if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
                flow_act.modify_id = attr->mod_hdr_id;
 
-       if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT)
-               flow_act.reformat_id = attr->encap_id;
-
-       fdb = esw_get_prio_table(esw, attr->chain, attr->prio, !!mirror);
+       fdb = esw_get_prio_table(esw, attr->chain, attr->prio, !!split);
        if (IS_ERR(fdb)) {
                rule = ERR_CAST(fdb);
                goto err_esw_get;
@@ -181,7 +187,7 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
        return rule;
 
 err_add_rule:
-       esw_put_prio_table(esw, attr->chain, attr->prio, !!mirror);
+       esw_put_prio_table(esw, attr->chain, attr->prio, !!split);
 err_esw_get:
        if (attr->dest_chain)
                esw_put_prio_table(esw, attr->dest_chain, 1, 0);
@@ -215,12 +221,17 @@ mlx5_eswitch_add_fwd_rule(struct mlx5_eswitch *esw,
        }
 
        flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
-       for (i = 0; i < attr->mirror_count; i++) {
+       for (i = 0; i < attr->split_count; i++) {
                dest[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
-               dest[i].vport.num = attr->out_rep[i]->vport;
+               dest[i].vport.num = attr->dests[i].rep->vport;
                dest[i].vport.vhca_id =
-                       MLX5_CAP_GEN(attr->out_mdev[i], vhca_id);
-               dest[i].vport.vhca_id_valid = !!MLX5_CAP_ESW(esw->dev, merged_eswitch);
+                       MLX5_CAP_GEN(attr->dests[i].mdev, vhca_id);
+               if (MLX5_CAP_ESW(esw->dev, merged_eswitch))
+                       dest[i].vport.flags |= MLX5_FLOW_DEST_VPORT_VHCA_ID;
+               if (attr->dests[i].flags & MLX5_ESW_DEST_ENCAP) {
+                       dest[i].vport.flags |= MLX5_FLOW_DEST_VPORT_REFORMAT_ID;
+                       dest[i].vport.reformat_id = attr->dests[i].encap_id;
+               }
        }
        dest[i].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
        dest[i].ft = fwd_fdb,
@@ -268,7 +279,7 @@ __mlx5_eswitch_del_rule(struct mlx5_eswitch *esw,
                        struct mlx5_esw_flow_attr *attr,
                        bool fwd_rule)
 {
-       bool mirror = (attr->mirror_count > 0);
+       bool split = (attr->split_count > 0);
 
        mlx5_del_flow_rules(rule);
        esw->offloads.num_flows--;
@@ -277,7 +288,7 @@ __mlx5_eswitch_del_rule(struct mlx5_eswitch *esw,
                esw_put_prio_table(esw, attr->chain, attr->prio, 1);
                esw_put_prio_table(esw, attr->chain, attr->prio, 0);
        } else {
-               esw_put_prio_table(esw, attr->chain, attr->prio, !!mirror);
+               esw_put_prio_table(esw, attr->chain, attr->prio, !!split);
                if (attr->dest_chain)
                        esw_put_prio_table(esw, attr->dest_chain, 1, 0);
        }
@@ -325,7 +336,7 @@ esw_vlan_action_get_vport(struct mlx5_esw_flow_attr *attr, bool push, bool pop)
        struct mlx5_eswitch_rep *in_rep, *out_rep, *vport = NULL;
 
        in_rep  = attr->in_rep;
-       out_rep = attr->out_rep[0];
+       out_rep = attr->dests[0].rep;
 
        if (push)
                vport = in_rep;
@@ -346,7 +357,7 @@ static int esw_add_vlan_action_check(struct mlx5_esw_flow_attr *attr,
                goto out_notsupp;
 
        in_rep  = attr->in_rep;
-       out_rep = attr->out_rep[0];
+       out_rep = attr->dests[0].rep;
 
        if (push && in_rep->vport == FDB_UPLINK_VPORT)
                goto out_notsupp;
@@ -398,7 +409,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->out_rep[0]->vport == FDB_UPLINK_VPORT) {
+               if (attr->dests[0].rep->vport == FDB_UPLINK_VPORT) {
                        vport->vlan_refcount++;
                        attr->vlan_handled = true;
                }
@@ -458,7 +469,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->out_rep[0]->vport == FDB_UPLINK_VPORT)
+               if (attr->dests[0].rep->vport == FDB_UPLINK_VPORT)
                        vport->vlan_refcount--;
 
                return 0;
@@ -531,6 +542,98 @@ void mlx5_eswitch_del_send_to_vport_rule(struct mlx5_flow_handle *rule)
        mlx5_del_flow_rules(rule);
 }
 
+static void peer_miss_rules_setup(struct mlx5_core_dev *peer_dev,
+                                 struct mlx5_flow_spec *spec,
+                                 struct mlx5_flow_destination *dest)
+{
+       void *misc = MLX5_ADDR_OF(fte_match_param, spec->match_value,
+                                 misc_parameters);
+
+       MLX5_SET(fte_match_set_misc, misc, source_eswitch_owner_vhca_id,
+                MLX5_CAP_GEN(peer_dev, vhca_id));
+
+       spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
+
+       misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+                           misc_parameters);
+       MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
+       MLX5_SET_TO_ONES(fte_match_set_misc, misc,
+                        source_eswitch_owner_vhca_id);
+
+       dest->type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
+       dest->vport.num = 0;
+       dest->vport.vhca_id = MLX5_CAP_GEN(peer_dev, vhca_id);
+       dest->vport.flags |= MLX5_FLOW_DEST_VPORT_VHCA_ID;
+}
+
+static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw,
+                                      struct mlx5_core_dev *peer_dev)
+{
+       struct mlx5_flow_destination dest = {};
+       struct mlx5_flow_act flow_act = {0};
+       struct mlx5_flow_handle **flows;
+       struct mlx5_flow_handle *flow;
+       struct mlx5_flow_spec *spec;
+       /* total vports is the same for both e-switches */
+       int nvports = esw->total_vports;
+       void *misc;
+       int err, i;
+
+       spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+
+       peer_miss_rules_setup(peer_dev, spec, &dest);
+
+       flows = kvzalloc(nvports * sizeof(*flows), GFP_KERNEL);
+       if (!flows) {
+               err = -ENOMEM;
+               goto alloc_flows_err;
+       }
+
+       flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+       misc = MLX5_ADDR_OF(fte_match_param, spec->match_value,
+                           misc_parameters);
+
+       for (i = 1; i < nvports; i++) {
+               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;
+               }
+               flows[i] = flow;
+       }
+
+       esw->fdb_table.offloads.peer_miss_rules = flows;
+
+       kvfree(spec);
+       return 0;
+
+add_flow_err:
+       for (i--; i > 0; i--)
+               mlx5_del_flow_rules(flows[i]);
+       kvfree(flows);
+alloc_flows_err:
+       kvfree(spec);
+       return err;
+}
+
+static void esw_del_fdb_peer_miss_rules(struct mlx5_eswitch *esw)
+{
+       struct mlx5_flow_handle **flows;
+       int i;
+
+       flows = esw->fdb_table.offloads.peer_miss_rules;
+
+       for (i = 1; i < esw->total_vports; i++)
+               mlx5_del_flow_rules(flows[i]);
+
+       kvfree(flows);
+}
+
 static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
 {
        struct mlx5_flow_act flow_act = {0};
@@ -801,7 +904,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;
+       table_size = nvports * MAX_SQ_NVPORTS + MAX_PF_SQ + 2 +
+               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.
@@ -856,6 +960,34 @@ static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw, int nvports)
        }
        esw->fdb_table.offloads.send_to_vport_grp = g;
 
+       /* create peer esw miss group */
+       memset(flow_group_in, 0, inlen);
+       MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
+                MLX5_MATCH_MISC_PARAMETERS);
+
+       match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in,
+                                     match_criteria);
+
+       MLX5_SET_TO_ONES(fte_match_param, match_criteria,
+                        misc_parameters.source_port);
+       MLX5_SET_TO_ONES(fte_match_param, match_criteria,
+                        misc_parameters.source_eswitch_owner_vhca_id);
+
+       MLX5_SET(create_flow_group_in, flow_group_in,
+                source_eswitch_owner_vhca_id_valid, 1);
+       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 + esw->total_vports - 1);
+       ix += esw->total_vports;
+
+       g = mlx5_create_flow_group(fdb, flow_group_in);
+       if (IS_ERR(g)) {
+               err = PTR_ERR(g);
+               esw_warn(dev, "Failed to create peer miss flow group err(%d)\n", err);
+               goto peer_miss_err;
+       }
+       esw->fdb_table.offloads.peer_miss_grp = g;
+
        /* create miss group */
        memset(flow_group_in, 0, inlen);
        MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
@@ -888,6 +1020,8 @@ static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw, int nvports)
 miss_rule_err:
        mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp);
 miss_err:
+       mlx5_destroy_flow_group(esw->fdb_table.offloads.peer_miss_grp);
+peer_miss_err:
        mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp);
 send_vport_err:
        esw_destroy_offloads_fast_fdb_tables(esw);
@@ -907,6 +1041,7 @@ static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw)
        mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule_multi);
        mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule_uni);
        mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp);
+       mlx5_destroy_flow_group(esw->fdb_table.offloads.peer_miss_grp);
        mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp);
 
        mlx5_destroy_flow_table(esw->fdb_table.offloads.slow_fdb);
@@ -1163,6 +1298,105 @@ err_reps:
        return err;
 }
 
+#define ESW_OFFLOADS_DEVCOM_PAIR       (0)
+#define ESW_OFFLOADS_DEVCOM_UNPAIR     (1)
+
+static int mlx5_esw_offloads_pair(struct mlx5_eswitch *esw,
+                                 struct mlx5_eswitch *peer_esw)
+{
+       int err;
+
+       err = esw_add_fdb_peer_miss_rules(esw, peer_esw->dev);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw);
+
+static void mlx5_esw_offloads_unpair(struct mlx5_eswitch *esw)
+{
+       mlx5e_tc_clean_fdb_peer_flows(esw);
+       esw_del_fdb_peer_miss_rules(esw);
+}
+
+static int mlx5_esw_offloads_devcom_event(int event,
+                                         void *my_data,
+                                         void *event_data)
+{
+       struct mlx5_eswitch *esw = my_data;
+       struct mlx5_eswitch *peer_esw = event_data;
+       struct mlx5_devcom *devcom = esw->dev->priv.devcom;
+       int err;
+
+       switch (event) {
+       case ESW_OFFLOADS_DEVCOM_PAIR:
+               err = mlx5_esw_offloads_pair(esw, peer_esw);
+               if (err)
+                       goto err_out;
+
+               err = mlx5_esw_offloads_pair(peer_esw, esw);
+               if (err)
+                       goto err_pair;
+
+               mlx5_devcom_set_paired(devcom, MLX5_DEVCOM_ESW_OFFLOADS, true);
+               break;
+
+       case ESW_OFFLOADS_DEVCOM_UNPAIR:
+               if (!mlx5_devcom_is_paired(devcom, MLX5_DEVCOM_ESW_OFFLOADS))
+                       break;
+
+               mlx5_devcom_set_paired(devcom, MLX5_DEVCOM_ESW_OFFLOADS, false);
+               mlx5_esw_offloads_unpair(peer_esw);
+               mlx5_esw_offloads_unpair(esw);
+               break;
+       }
+
+       return 0;
+
+err_pair:
+       mlx5_esw_offloads_unpair(esw);
+
+err_out:
+       mlx5_core_err(esw->dev, "esw offloads devcom event failure, event %u err %d",
+                     event, err);
+       return err;
+}
+
+static void esw_offloads_devcom_init(struct mlx5_eswitch *esw)
+{
+       struct mlx5_devcom *devcom = esw->dev->priv.devcom;
+
+       INIT_LIST_HEAD(&esw->offloads.peer_flows);
+       mutex_init(&esw->offloads.peer_mutex);
+
+       if (!MLX5_CAP_ESW(esw->dev, merged_eswitch))
+               return;
+
+       mlx5_devcom_register_component(devcom,
+                                      MLX5_DEVCOM_ESW_OFFLOADS,
+                                      mlx5_esw_offloads_devcom_event,
+                                      esw);
+
+       mlx5_devcom_send_event(devcom,
+                              MLX5_DEVCOM_ESW_OFFLOADS,
+                              ESW_OFFLOADS_DEVCOM_PAIR, esw);
+}
+
+static void esw_offloads_devcom_cleanup(struct mlx5_eswitch *esw)
+{
+       struct mlx5_devcom *devcom = esw->dev->priv.devcom;
+
+       if (!MLX5_CAP_ESW(esw->dev, merged_eswitch))
+               return;
+
+       mlx5_devcom_send_event(devcom, MLX5_DEVCOM_ESW_OFFLOADS,
+                              ESW_OFFLOADS_DEVCOM_UNPAIR, esw);
+
+       mlx5_devcom_unregister_component(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
+}
+
 int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
 {
        int err;
@@ -1185,6 +1419,7 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
        if (err)
                goto err_reps;
 
+       esw_offloads_devcom_init(esw);
        return 0;
 
 err_reps:
@@ -1215,14 +1450,12 @@ static int esw_offloads_stop(struct mlx5_eswitch *esw,
                }
        }
 
-       /* enable back PF RoCE */
-       mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
-
        return err;
 }
 
 void esw_offloads_cleanup(struct mlx5_eswitch *esw, int nvports)
 {
+       esw_offloads_devcom_cleanup(esw);
        esw_offloads_unload_reps(esw, nvports);
        esw_destroy_vport_rx_group(esw);
        esw_destroy_offloads_table(esw);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/events.c b/drivers/net/ethernet/mellanox/mlx5/core/events.c
new file mode 100644 (file)
index 0000000..fbc42b7
--- /dev/null
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+// Copyright (c) 2018 Mellanox Technologies
+
+#include <linux/mlx5/driver.h>
+
+#include "mlx5_core.h"
+#include "lib/eq.h"
+#include "lib/mlx5.h"
+
+struct mlx5_event_nb {
+       struct mlx5_nb  nb;
+       void           *ctx;
+};
+
+/* General events handlers for the low level mlx5_core driver
+ *
+ * Other Major feature specific events such as
+ * clock/eswitch/fpga/FW trace and many others, are handled elsewhere, with
+ * separate notifiers callbacks, specifically by those mlx5 components.
+ */
+static int any_notifier(struct notifier_block *, unsigned long, void *);
+static int temp_warn(struct notifier_block *, unsigned long, void *);
+static int port_module(struct notifier_block *, unsigned long, void *);
+
+/* handler which forwards the event to events->nh, driver notifiers */
+static int forward_event(struct notifier_block *, unsigned long, void *);
+
+static struct mlx5_nb events_nbs_ref[] = {
+       /* Events to be proccessed by mlx5_core */
+       {.nb.notifier_call = any_notifier,  .event_type = MLX5_EVENT_TYPE_NOTIFY_ANY },
+       {.nb.notifier_call = temp_warn,     .event_type = MLX5_EVENT_TYPE_TEMP_WARN_EVENT },
+       {.nb.notifier_call = port_module,   .event_type = MLX5_EVENT_TYPE_PORT_MODULE_EVENT },
+
+       /* Events to be forwarded (as is) to mlx5 core interfaces (mlx5e/mlx5_ib) */
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_PORT_CHANGE },
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_GENERAL_EVENT },
+       /* QP/WQ resource events to forward */
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_DCT_DRAINED },
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_PATH_MIG },
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_COMM_EST },
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_SQ_DRAINED },
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_SRQ_LAST_WQE },
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_WQ_CATAS_ERROR },
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_PATH_MIG_FAILED },
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR },
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_WQ_ACCESS_ERROR },
+       /* SRQ events */
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_SRQ_CATAS_ERROR },
+       {.nb.notifier_call = forward_event,   .event_type = MLX5_EVENT_TYPE_SRQ_RQ_LIMIT },
+};
+
+struct mlx5_events {
+       struct mlx5_core_dev *dev;
+       struct mlx5_event_nb  notifiers[ARRAY_SIZE(events_nbs_ref)];
+       /* driver notifier chain */
+       struct atomic_notifier_head nh;
+       /* port module events stats */
+       struct mlx5_pme_stats pme_stats;
+};
+
+static const char *eqe_type_str(u8 type)
+{
+       switch (type) {
+       case MLX5_EVENT_TYPE_COMP:
+               return "MLX5_EVENT_TYPE_COMP";
+       case MLX5_EVENT_TYPE_PATH_MIG:
+               return "MLX5_EVENT_TYPE_PATH_MIG";
+       case MLX5_EVENT_TYPE_COMM_EST:
+               return "MLX5_EVENT_TYPE_COMM_EST";
+       case MLX5_EVENT_TYPE_SQ_DRAINED:
+               return "MLX5_EVENT_TYPE_SQ_DRAINED";
+       case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
+               return "MLX5_EVENT_TYPE_SRQ_LAST_WQE";
+       case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT:
+               return "MLX5_EVENT_TYPE_SRQ_RQ_LIMIT";
+       case MLX5_EVENT_TYPE_CQ_ERROR:
+               return "MLX5_EVENT_TYPE_CQ_ERROR";
+       case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
+               return "MLX5_EVENT_TYPE_WQ_CATAS_ERROR";
+       case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
+               return "MLX5_EVENT_TYPE_PATH_MIG_FAILED";
+       case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
+               return "MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR";
+       case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
+               return "MLX5_EVENT_TYPE_WQ_ACCESS_ERROR";
+       case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR:
+               return "MLX5_EVENT_TYPE_SRQ_CATAS_ERROR";
+       case MLX5_EVENT_TYPE_INTERNAL_ERROR:
+               return "MLX5_EVENT_TYPE_INTERNAL_ERROR";
+       case MLX5_EVENT_TYPE_PORT_CHANGE:
+               return "MLX5_EVENT_TYPE_PORT_CHANGE";
+       case MLX5_EVENT_TYPE_GPIO_EVENT:
+               return "MLX5_EVENT_TYPE_GPIO_EVENT";
+       case MLX5_EVENT_TYPE_PORT_MODULE_EVENT:
+               return "MLX5_EVENT_TYPE_PORT_MODULE_EVENT";
+       case MLX5_EVENT_TYPE_TEMP_WARN_EVENT:
+               return "MLX5_EVENT_TYPE_TEMP_WARN_EVENT";
+       case MLX5_EVENT_TYPE_REMOTE_CONFIG:
+               return "MLX5_EVENT_TYPE_REMOTE_CONFIG";
+       case MLX5_EVENT_TYPE_DB_BF_CONGESTION:
+               return "MLX5_EVENT_TYPE_DB_BF_CONGESTION";
+       case MLX5_EVENT_TYPE_STALL_EVENT:
+               return "MLX5_EVENT_TYPE_STALL_EVENT";
+       case MLX5_EVENT_TYPE_CMD:
+               return "MLX5_EVENT_TYPE_CMD";
+       case MLX5_EVENT_TYPE_PAGE_REQUEST:
+               return "MLX5_EVENT_TYPE_PAGE_REQUEST";
+       case MLX5_EVENT_TYPE_PAGE_FAULT:
+               return "MLX5_EVENT_TYPE_PAGE_FAULT";
+       case MLX5_EVENT_TYPE_PPS_EVENT:
+               return "MLX5_EVENT_TYPE_PPS_EVENT";
+       case MLX5_EVENT_TYPE_NIC_VPORT_CHANGE:
+               return "MLX5_EVENT_TYPE_NIC_VPORT_CHANGE";
+       case MLX5_EVENT_TYPE_FPGA_ERROR:
+               return "MLX5_EVENT_TYPE_FPGA_ERROR";
+       case MLX5_EVENT_TYPE_FPGA_QP_ERROR:
+               return "MLX5_EVENT_TYPE_FPGA_QP_ERROR";
+       case MLX5_EVENT_TYPE_GENERAL_EVENT:
+               return "MLX5_EVENT_TYPE_GENERAL_EVENT";
+       case MLX5_EVENT_TYPE_MONITOR_COUNTER:
+               return "MLX5_EVENT_TYPE_MONITOR_COUNTER";
+       case MLX5_EVENT_TYPE_DEVICE_TRACER:
+               return "MLX5_EVENT_TYPE_DEVICE_TRACER";
+       default:
+               return "Unrecognized event";
+       }
+}
+
+/* handles all FW events, type == eqe->type */
+static int any_notifier(struct notifier_block *nb,
+                       unsigned long type, void *data)
+{
+       struct mlx5_event_nb *event_nb = mlx5_nb_cof(nb, struct mlx5_event_nb, nb);
+       struct mlx5_events   *events   = event_nb->ctx;
+       struct mlx5_eqe      *eqe      = data;
+
+       mlx5_core_dbg(events->dev, "Async eqe type %s, subtype (%d)\n",
+                     eqe_type_str(eqe->type), eqe->sub_type);
+       return NOTIFY_OK;
+}
+
+/* type == MLX5_EVENT_TYPE_TEMP_WARN_EVENT */
+static int temp_warn(struct notifier_block *nb, unsigned long type, void *data)
+{
+       struct mlx5_event_nb *event_nb = mlx5_nb_cof(nb, struct mlx5_event_nb, nb);
+       struct mlx5_events   *events   = event_nb->ctx;
+       struct mlx5_eqe      *eqe      = data;
+       u64 value_lsb;
+       u64 value_msb;
+
+       value_lsb = be64_to_cpu(eqe->data.temp_warning.sensor_warning_lsb);
+       value_msb = be64_to_cpu(eqe->data.temp_warning.sensor_warning_msb);
+
+       mlx5_core_warn(events->dev,
+                      "High temperature on sensors with bit set %llx %llx",
+                      value_msb, value_lsb);
+
+       return NOTIFY_OK;
+}
+
+/* MLX5_EVENT_TYPE_PORT_MODULE_EVENT */
+static const char *mlx5_pme_status_to_string(enum port_module_event_status_type status)
+{
+       switch (status) {
+       case MLX5_MODULE_STATUS_PLUGGED:
+               return "Cable plugged";
+       case MLX5_MODULE_STATUS_UNPLUGGED:
+               return "Cable unplugged";
+       case MLX5_MODULE_STATUS_ERROR:
+               return "Cable error";
+       case MLX5_MODULE_STATUS_DISABLED:
+               return "Cable disabled";
+       default:
+               return "Unknown status";
+       }
+}
+
+static const char *mlx5_pme_error_to_string(enum port_module_event_error_type error)
+{
+       switch (error) {
+       case MLX5_MODULE_EVENT_ERROR_POWER_BUDGET_EXCEEDED:
+               return "Power budget exceeded";
+       case MLX5_MODULE_EVENT_ERROR_LONG_RANGE_FOR_NON_MLNX:
+               return "Long Range for non MLNX cable";
+       case MLX5_MODULE_EVENT_ERROR_BUS_STUCK:
+               return "Bus stuck (I2C or data shorted)";
+       case MLX5_MODULE_EVENT_ERROR_NO_EEPROM_RETRY_TIMEOUT:
+               return "No EEPROM/retry timeout";
+       case MLX5_MODULE_EVENT_ERROR_ENFORCE_PART_NUMBER_LIST:
+               return "Enforce part number list";
+       case MLX5_MODULE_EVENT_ERROR_UNKNOWN_IDENTIFIER:
+               return "Unknown identifier";
+       case MLX5_MODULE_EVENT_ERROR_HIGH_TEMPERATURE:
+               return "High Temperature";
+       case MLX5_MODULE_EVENT_ERROR_BAD_CABLE:
+               return "Bad or shorted cable/module";
+       case MLX5_MODULE_EVENT_ERROR_PCIE_POWER_SLOT_EXCEEDED:
+               return "One or more network ports have been powered down due to insufficient/unadvertised power on the PCIe slot";
+       default:
+               return "Unknown error";
+       }
+}
+
+/* type == MLX5_EVENT_TYPE_PORT_MODULE_EVENT */
+static int port_module(struct notifier_block *nb, unsigned long type, void *data)
+{
+       struct mlx5_event_nb *event_nb = mlx5_nb_cof(nb, struct mlx5_event_nb, nb);
+       struct mlx5_events   *events   = event_nb->ctx;
+       struct mlx5_eqe      *eqe      = 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;
+       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 &
+                    PORT_MODULE_EVENT_ERROR_TYPE_MASK;
+
+       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 (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)
+               mlx5_core_err(events->dev,
+                             "Port module event[error]: module %u, %s, %s\n",
+                             module_num, status_str, error_str);
+       else
+               mlx5_core_info(events->dev,
+                              "Port module event: module %u, %s\n",
+                              module_num, status_str);
+
+       return NOTIFY_OK;
+}
+
+void mlx5_get_pme_stats(struct mlx5_core_dev *dev, struct mlx5_pme_stats *stats)
+{
+       *stats = dev->priv.events->pme_stats;
+}
+
+/* forward event as is to registered interfaces (mlx5e/mlx5_ib) */
+static int forward_event(struct notifier_block *nb, unsigned long event, void *data)
+{
+       struct mlx5_event_nb *event_nb = mlx5_nb_cof(nb, struct mlx5_event_nb, nb);
+       struct mlx5_events   *events   = event_nb->ctx;
+       struct mlx5_eqe      *eqe      = data;
+
+       mlx5_core_dbg(events->dev, "Async eqe type %s, subtype (%d) forward to interfaces\n",
+                     eqe_type_str(eqe->type), eqe->sub_type);
+       atomic_notifier_call_chain(&events->nh, event, data);
+       return NOTIFY_OK;
+}
+
+int mlx5_events_init(struct mlx5_core_dev *dev)
+{
+       struct mlx5_events *events = kzalloc(sizeof(*events), GFP_KERNEL);
+
+       if (!events)
+               return -ENOMEM;
+
+       ATOMIC_INIT_NOTIFIER_HEAD(&events->nh);
+       events->dev = dev;
+       dev->priv.events = events;
+       return 0;
+}
+
+void mlx5_events_cleanup(struct mlx5_core_dev *dev)
+{
+       kvfree(dev->priv.events);
+}
+
+void mlx5_events_start(struct mlx5_core_dev *dev)
+{
+       struct mlx5_events *events = dev->priv.events;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(events_nbs_ref); i++) {
+               events->notifiers[i].nb  = events_nbs_ref[i];
+               events->notifiers[i].ctx = events;
+               mlx5_eq_notifier_register(dev, &events->notifiers[i].nb);
+       }
+}
+
+void mlx5_events_stop(struct mlx5_core_dev *dev)
+{
+       struct mlx5_events *events = dev->priv.events;
+       int i;
+
+       for (i = ARRAY_SIZE(events_nbs_ref) - 1; i >= 0 ; i--)
+               mlx5_eq_notifier_unregister(dev, &events->notifiers[i].nb);
+}
+
+int mlx5_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb)
+{
+       struct mlx5_events *events = dev->priv.events;
+
+       return atomic_notifier_chain_register(&events->nh, nb);
+}
+EXPORT_SYMBOL(mlx5_notifier_register);
+
+int mlx5_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb)
+{
+       struct mlx5_events *events = dev->priv.events;
+
+       return atomic_notifier_chain_unregister(&events->nh, nb);
+}
+EXPORT_SYMBOL(mlx5_notifier_unregister);
+
+int mlx5_notifier_call_chain(struct mlx5_events *events, unsigned int event, void *data)
+{
+       return atomic_notifier_call_chain(&events->nh, event, data);
+}
index 8ca1d19..873541e 100644 (file)
@@ -334,7 +334,7 @@ static void mlx5_fpga_conn_handle_cqe(struct mlx5_fpga_conn *conn,
 {
        u8 opcode, status = 0;
 
-       opcode = cqe->op_own >> 4;
+       opcode = get_cqe_opcode(cqe);
 
        switch (opcode) {
        case MLX5_CQE_REQ_ERR:
index 436a813..27c5f6c 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "mlx5_core.h"
 #include "lib/mlx5.h"
+#include "lib/eq.h"
 #include "fpga/core.h"
 #include "fpga/conn.h"
 
@@ -145,6 +146,22 @@ static int mlx5_fpga_device_brb(struct mlx5_fpga_device *fdev)
        return 0;
 }
 
+static int mlx5_fpga_event(struct mlx5_fpga_device *, unsigned long, void *);
+
+static int fpga_err_event(struct notifier_block *nb, unsigned long event, void *eqe)
+{
+       struct mlx5_fpga_device *fdev = mlx5_nb_cof(nb, struct mlx5_fpga_device, fpga_err_nb);
+
+       return mlx5_fpga_event(fdev, event, eqe);
+}
+
+static int fpga_qp_err_event(struct notifier_block *nb, unsigned long event, void *eqe)
+{
+       struct mlx5_fpga_device *fdev = mlx5_nb_cof(nb, struct mlx5_fpga_device, fpga_qp_err_nb);
+
+       return mlx5_fpga_event(fdev, event, eqe);
+}
+
 int mlx5_fpga_device_start(struct mlx5_core_dev *mdev)
 {
        struct mlx5_fpga_device *fdev = mdev->fpga;
@@ -185,6 +202,11 @@ int mlx5_fpga_device_start(struct mlx5_core_dev *mdev)
        if (err)
                goto out;
 
+       MLX5_NB_INIT(&fdev->fpga_err_nb, fpga_err_event, FPGA_ERROR);
+       MLX5_NB_INIT(&fdev->fpga_qp_err_nb, fpga_qp_err_event, FPGA_QP_ERROR);
+       mlx5_eq_notifier_register(fdev->mdev, &fdev->fpga_err_nb);
+       mlx5_eq_notifier_register(fdev->mdev, &fdev->fpga_qp_err_nb);
+
        err = mlx5_fpga_conn_device_init(fdev);
        if (err)
                goto err_rsvd_gid;
@@ -201,6 +223,8 @@ err_conn_init:
        mlx5_fpga_conn_device_cleanup(fdev);
 
 err_rsvd_gid:
+       mlx5_eq_notifier_unregister(fdev->mdev, &fdev->fpga_err_nb);
+       mlx5_eq_notifier_unregister(fdev->mdev, &fdev->fpga_qp_err_nb);
        mlx5_core_unreserve_gids(mdev, max_num_qps);
 out:
        spin_lock_irqsave(&fdev->state_lock, flags);
@@ -256,6 +280,9 @@ void mlx5_fpga_device_stop(struct mlx5_core_dev *mdev)
        }
 
        mlx5_fpga_conn_device_cleanup(fdev);
+       mlx5_eq_notifier_unregister(fdev->mdev, &fdev->fpga_err_nb);
+       mlx5_eq_notifier_unregister(fdev->mdev, &fdev->fpga_qp_err_nb);
+
        max_num_qps = MLX5_CAP_FPGA(mdev, shell_caps.max_num_qps);
        mlx5_core_unreserve_gids(mdev, max_num_qps);
 }
@@ -283,9 +310,10 @@ static const char *mlx5_fpga_qp_syndrome_to_string(u8 syndrome)
        return "Unknown";
 }
 
-void mlx5_fpga_event(struct mlx5_core_dev *mdev, u8 event, void *data)
+static int mlx5_fpga_event(struct mlx5_fpga_device *fdev,
+                          unsigned long event, void *eqe)
 {
-       struct mlx5_fpga_device *fdev = mdev->fpga;
+       void *data = ((struct mlx5_eqe *)eqe)->data.raw;
        const char *event_name;
        bool teardown = false;
        unsigned long flags;
@@ -303,9 +331,7 @@ void mlx5_fpga_event(struct mlx5_core_dev *mdev, u8 event, void *data)
                fpga_qpn = MLX5_GET(fpga_qp_error_event, data, fpga_qpn);
                break;
        default:
-               mlx5_fpga_warn_ratelimited(fdev, "Unexpected event %u\n",
-                                          event);
-               return;
+               return NOTIFY_DONE;
        }
 
        spin_lock_irqsave(&fdev->state_lock, flags);
@@ -326,4 +352,6 @@ void mlx5_fpga_event(struct mlx5_core_dev *mdev, u8 event, void *data)
         */
        if (teardown)
                mlx5_trigger_health_work(fdev->mdev);
+
+       return NOTIFY_OK;
 }
index 3e2355c..7e2e871 100644 (file)
 
 #ifdef CONFIG_MLX5_FPGA
 
+#include <linux/mlx5/eq.h>
+
+#include "lib/eq.h"
 #include "fpga/cmd.h"
 
 /* Represents an Innova device */
 struct mlx5_fpga_device {
        struct mlx5_core_dev *mdev;
+       struct mlx5_nb fpga_err_nb;
+       struct mlx5_nb fpga_qp_err_nb;
        spinlock_t state_lock; /* Protects state transitions */
        enum mlx5_fpga_status state;
        enum mlx5_fpga_image last_admin_image;
@@ -82,7 +87,6 @@ int mlx5_fpga_init(struct mlx5_core_dev *mdev);
 void mlx5_fpga_cleanup(struct mlx5_core_dev *mdev);
 int mlx5_fpga_device_start(struct mlx5_core_dev *mdev);
 void mlx5_fpga_device_stop(struct mlx5_core_dev *mdev);
-void mlx5_fpga_event(struct mlx5_core_dev *mdev, u8 event, void *data);
 
 #else
 
@@ -104,11 +108,6 @@ static inline void mlx5_fpga_device_stop(struct mlx5_core_dev *mdev)
 {
 }
 
-static inline void mlx5_fpga_event(struct mlx5_core_dev *mdev, u8 event,
-                                  void *data)
-{
-}
-
 #endif
 
 #endif /* __MLX5_FPGA_CORE_H__ */
index 515e3d6..5a22c58 100644 (file)
@@ -83,8 +83,14 @@ struct mlx5_fpga_ipsec_rule {
 };
 
 static const struct rhashtable_params rhash_sa = {
-       .key_len = FIELD_SIZEOF(struct mlx5_fpga_ipsec_sa_ctx, hw_sa),
-       .key_offset = offsetof(struct mlx5_fpga_ipsec_sa_ctx, hw_sa),
+       /* Keep out "cmd" field from the key as it's
+        * value is not constant during the lifetime
+        * of the key object.
+        */
+       .key_len = FIELD_SIZEOF(struct mlx5_fpga_ipsec_sa_ctx, hw_sa) -
+                  FIELD_SIZEOF(struct mlx5_ifc_fpga_ipsec_sa_v1, cmd),
+       .key_offset = offsetof(struct mlx5_fpga_ipsec_sa_ctx, hw_sa) +
+                     FIELD_SIZEOF(struct mlx5_ifc_fpga_ipsec_sa_v1, cmd),
        .head_offset = offsetof(struct mlx5_fpga_ipsec_sa_ctx, hash),
        .automatic_shrinking = true,
        .min_size = 1,
index 08a891f..c44ccb6 100644 (file)
@@ -308,22 +308,68 @@ static int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev,
        return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
 }
 
+static int mlx5_set_extended_dest(struct mlx5_core_dev *dev,
+                                 struct fs_fte *fte, bool *extended_dest)
+{
+       int fw_log_max_fdb_encap_uplink =
+               MLX5_CAP_ESW(dev, log_max_fdb_encap_uplink);
+       int num_fwd_destinations = 0;
+       struct mlx5_flow_rule *dst;
+       int num_encap = 0;
+
+       *extended_dest = false;
+       if (!(fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST))
+               return 0;
+
+       list_for_each_entry(dst, &fte->node.children, node.list) {
+               if (dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_COUNTER)
+                       continue;
+               if (dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_VPORT &&
+                   dst->dest_attr.vport.flags & MLX5_FLOW_DEST_VPORT_REFORMAT_ID)
+                       num_encap++;
+               num_fwd_destinations++;
+       }
+       if (num_fwd_destinations > 1 && num_encap > 0)
+               *extended_dest = true;
+
+       if (*extended_dest && !fw_log_max_fdb_encap_uplink) {
+               mlx5_core_warn(dev, "FW does not support extended destination");
+               return -EOPNOTSUPP;
+       }
+       if (num_encap > (1 << fw_log_max_fdb_encap_uplink)) {
+               mlx5_core_warn(dev, "FW does not support more than %d encaps",
+                              1 << fw_log_max_fdb_encap_uplink);
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
 static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
                            int opmod, int modify_mask,
                            struct mlx5_flow_table *ft,
                            unsigned group_id,
                            struct fs_fte *fte)
 {
-       unsigned int inlen = MLX5_ST_SZ_BYTES(set_fte_in) +
-               fte->dests_size * MLX5_ST_SZ_BYTES(dest_format_struct);
        u32 out[MLX5_ST_SZ_DW(set_fte_out)] = {0};
+       bool extended_dest = false;
        struct mlx5_flow_rule *dst;
        void *in_flow_context, *vlan;
        void *in_match_value;
+       unsigned int inlen;
+       int dst_cnt_size;
        void *in_dests;
        u32 *in;
        int err;
 
+       if (mlx5_set_extended_dest(dev, fte, &extended_dest))
+               return -EOPNOTSUPP;
+
+       if (!extended_dest)
+               dst_cnt_size = MLX5_ST_SZ_BYTES(dest_format_struct);
+       else
+               dst_cnt_size = MLX5_ST_SZ_BYTES(extended_dest_format);
+
+       inlen = MLX5_ST_SZ_BYTES(set_fte_in) + fte->dests_size * dst_cnt_size;
        in = kvzalloc(inlen, GFP_KERNEL);
        if (!in)
                return -ENOMEM;
@@ -343,9 +389,20 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
        MLX5_SET(flow_context, in_flow_context, group_id, group_id);
 
        MLX5_SET(flow_context, in_flow_context, flow_tag, fte->action.flow_tag);
-       MLX5_SET(flow_context, in_flow_context, action, fte->action.action);
-       MLX5_SET(flow_context, in_flow_context, packet_reformat_id,
-                fte->action.reformat_id);
+       MLX5_SET(flow_context, in_flow_context, extended_destination,
+                extended_dest);
+       if (extended_dest) {
+               u32 action;
+
+               action = fte->action.action &
+                       ~MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
+               MLX5_SET(flow_context, in_flow_context, action, action);
+       } else {
+               MLX5_SET(flow_context, in_flow_context, action,
+                        fte->action.action);
+               MLX5_SET(flow_context, in_flow_context, packet_reformat_id,
+                        fte->action.reformat_id);
+       }
        MLX5_SET(flow_context, in_flow_context, modify_header_id,
                 fte->action.modify_id);
 
@@ -387,10 +444,20 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
                                id = dst->dest_attr.vport.num;
                                MLX5_SET(dest_format_struct, in_dests,
                                         destination_eswitch_owner_vhca_id_valid,
-                                        dst->dest_attr.vport.vhca_id_valid);
+                                        !!(dst->dest_attr.vport.flags &
+                                           MLX5_FLOW_DEST_VPORT_VHCA_ID));
                                MLX5_SET(dest_format_struct, in_dests,
                                         destination_eswitch_owner_vhca_id,
                                         dst->dest_attr.vport.vhca_id);
+                               if (extended_dest) {
+                                       MLX5_SET(dest_format_struct, in_dests,
+                                                packet_reformat,
+                                                !!(dst->dest_attr.vport.flags &
+                                                   MLX5_FLOW_DEST_VPORT_REFORMAT_ID));
+                                       MLX5_SET(extended_dest_format, in_dests,
+                                                packet_reformat_id,
+                                                dst->dest_attr.vport.reformat_id);
+                               }
                                break;
                        default:
                                id = dst->dest_attr.tir_num;
@@ -399,7 +466,7 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
                        MLX5_SET(dest_format_struct, in_dests, destination_type,
                                 type);
                        MLX5_SET(dest_format_struct, in_dests, destination_id, id);
-                       in_dests += MLX5_ST_SZ_BYTES(dest_format_struct);
+                       in_dests += dst_cnt_size;
                        list_size++;
                }
 
@@ -420,7 +487,7 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
 
                        MLX5_SET(flow_counter_list, in_dests, flow_counter_id,
                                 dst->dest_attr.counter_id);
-                       in_dests += MLX5_ST_SZ_BYTES(dest_format_struct);
+                       in_dests += dst_cnt_size;
                        list_size++;
                }
                if (list_size > max_list_size) {
index 9d73eb9..79f122b 100644 (file)
@@ -452,7 +452,7 @@ static void del_sw_hw_rule(struct fs_node *node)
 
        if ((fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) &&
            --fte->dests_size) {
-               modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST),
+               modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST);
                update_fte = true;
        }
 out:
@@ -1373,7 +1373,10 @@ static bool mlx5_flow_dests_cmp(struct mlx5_flow_destination *d1,
 {
        if (d1->type == d2->type) {
                if ((d1->type == MLX5_FLOW_DESTINATION_TYPE_VPORT &&
-                    d1->vport.num == d2->vport.num) ||
+                    d1->vport.num == d2->vport.num &&
+                    d1->vport.flags == d2->vport.flags &&
+                    ((d1->vport.flags & MLX5_FLOW_DEST_VPORT_REFORMAT_ID) ?
+                     (d1->vport.reformat_id == d2->vport.reformat_id) : true)) ||
                    (d1->type == MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE &&
                     d1->ft == d2->ft) ||
                    (d1->type == MLX5_FLOW_DESTINATION_TYPE_TIR &&
index b51ad21..2dc8634 100644 (file)
@@ -145,29 +145,6 @@ struct mlx5_flow_table {
        struct rhltable                 fgs_hash;
 };
 
-struct mlx5_fc_cache {
-       u64 packets;
-       u64 bytes;
-       u64 lastuse;
-};
-
-struct mlx5_fc {
-       struct list_head list;
-       struct llist_node addlist;
-       struct llist_node dellist;
-
-       /* last{packets,bytes} members are used when calculating the delta since
-        * last reading
-        */
-       u64 lastpackets;
-       u64 lastbytes;
-
-       u32 id;
-       bool aging;
-
-       struct mlx5_fc_cache cache ____cacheline_aligned_in_smp;
-};
-
 struct mlx5_ft_underlay_qp {
        struct list_head list;
        u32 qpn;
index 32accd6..c6c28f5 100644 (file)
 /* Max number of counters to query in bulk read is 32K */
 #define MLX5_SW_MAX_COUNTERS_BULK BIT(15)
 
+struct mlx5_fc_cache {
+       u64 packets;
+       u64 bytes;
+       u64 lastuse;
+};
+
+struct mlx5_fc {
+       struct list_head list;
+       struct llist_node addlist;
+       struct llist_node dellist;
+
+       /* last{packets,bytes} members are used when calculating the delta since
+        * last reading
+        */
+       u64 lastpackets;
+       u64 lastbytes;
+
+       u32 id;
+       bool aging;
+
+       struct mlx5_fc_cache cache ____cacheline_aligned_in_smp;
+};
+
 /* locking scheme:
  *
  * It is the responsibility of the user to prevent concurrent calls or bad
index 43118de..196c073 100644 (file)
@@ -38,6 +38,8 @@
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
+#include "lib/eq.h"
+#include "lib/mlx5.h"
 
 enum {
        MLX5_HEALTH_POLL_INTERVAL       = 2 * HZ,
@@ -78,29 +80,6 @@ void mlx5_set_nic_state(struct mlx5_core_dev *dev, u8 state)
                    &dev->iseg->cmdq_addr_l_sz);
 }
 
-static void trigger_cmd_completions(struct mlx5_core_dev *dev)
-{
-       unsigned long flags;
-       u64 vector;
-
-       /* wait for pending handlers to complete */
-       synchronize_irq(pci_irq_vector(dev->pdev, MLX5_EQ_VEC_CMD));
-       spin_lock_irqsave(&dev->cmd.alloc_lock, flags);
-       vector = ~dev->cmd.bitmask & ((1ul << (1 << dev->cmd.log_sz)) - 1);
-       if (!vector)
-               goto no_trig;
-
-       vector |= MLX5_TRIGGERED_CMD_COMP;
-       spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags);
-
-       mlx5_core_dbg(dev, "vector 0x%llx\n", vector);
-       mlx5_cmd_comp_handler(dev, vector, true);
-       return;
-
-no_trig:
-       spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags);
-}
-
 static int in_fatal(struct mlx5_core_dev *dev)
 {
        struct mlx5_core_health *health = &dev->priv.health;
@@ -124,10 +103,10 @@ 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;
-               trigger_cmd_completions(dev);
+               mlx5_cmd_trigger_completions(dev);
        }
 
-       mlx5_core_event(dev, MLX5_DEV_EVENT_SYS_ERROR, 1);
+       mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_SYS_ERROR, (void *)1);
        mlx5_core_err(dev, "end\n");
 
 unlock:
index b59953d..bfc0f65 100644 (file)
@@ -87,7 +87,7 @@ int mlx5i_init(struct mlx5_core_dev *mdev,
        mlx5_query_port_max_mtu(mdev, &max_mtu, 1);
        netdev->mtu = max_mtu;
 
-       mlx5e_build_nic_params(mdev, &priv->channels.params,
+       mlx5e_build_nic_params(mdev, &priv->rss_params, &priv->channels.params,
                               mlx5e_get_netdev_max_channels(netdev),
                               netdev->mtu);
        mlx5i_build_nic_params(mdev, &priv->channels.params);
@@ -560,9 +560,9 @@ static int mlx5i_close(struct net_device *netdev)
 
        netif_carrier_off(epriv->netdev);
        mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn);
-       mlx5i_uninit_underlay_qp(epriv);
        mlx5e_deactivate_priv_channels(epriv);
        mlx5e_close_channels(&epriv->channels);
+       mlx5i_uninit_underlay_qp(epriv);
 unlock:
        mutex_unlock(&epriv->state_lock);
        return 0;
index 582b2f1..3a6baed 100644 (file)
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/vport.h>
 #include "mlx5_core.h"
+#include "eswitch.h"
 
 enum {
-       MLX5_LAG_FLAG_BONDED = 1 << 0,
+       MLX5_LAG_FLAG_ROCE   = 1 << 0,
+       MLX5_LAG_FLAG_SRIOV  = 1 << 1,
 };
 
+#define MLX5_LAG_MODE_FLAGS (MLX5_LAG_FLAG_ROCE | MLX5_LAG_FLAG_SRIOV)
+
 struct lag_func {
        struct mlx5_core_dev *dev;
        struct net_device    *netdev;
@@ -61,11 +65,6 @@ struct mlx5_lag {
        struct lag_tracker        tracker;
        struct delayed_work       bond_work;
        struct notifier_block     nb;
-
-       /* Admin state. Allow lag only if allowed is true
-        * even if network conditions for lag were met
-        */
-       bool                      allowed;
 };
 
 /* General purpose, use for short periods of time.
@@ -165,9 +164,19 @@ static int mlx5_lag_dev_get_netdev_idx(struct mlx5_lag *ldev,
        return -1;
 }
 
-static bool mlx5_lag_is_bonded(struct mlx5_lag *ldev)
+static bool __mlx5_lag_is_roce(struct mlx5_lag *ldev)
+{
+       return !!(ldev->flags & MLX5_LAG_FLAG_ROCE);
+}
+
+static bool __mlx5_lag_is_sriov(struct mlx5_lag *ldev)
+{
+       return !!(ldev->flags & MLX5_LAG_FLAG_SRIOV);
+}
+
+static bool __mlx5_lag_is_active(struct mlx5_lag *ldev)
 {
-       return !!(ldev->flags & MLX5_LAG_FLAG_BONDED);
+       return !!(ldev->flags & MLX5_LAG_MODE_FLAGS);
 }
 
 static void mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
@@ -186,36 +195,131 @@ static void mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
                *port2 = 1;
 }
 
-static void mlx5_activate_lag(struct mlx5_lag *ldev,
-                             struct lag_tracker *tracker)
+static void mlx5_modify_lag(struct mlx5_lag *ldev,
+                           struct lag_tracker *tracker)
 {
        struct mlx5_core_dev *dev0 = ldev->pf[0].dev;
+       u8 v2p_port1, v2p_port2;
        int err;
 
-       ldev->flags |= MLX5_LAG_FLAG_BONDED;
+       mlx5_infer_tx_affinity_mapping(tracker, &v2p_port1,
+                                      &v2p_port2);
+
+       if (v2p_port1 != ldev->v2p_map[0] ||
+           v2p_port2 != ldev->v2p_map[1]) {
+               ldev->v2p_map[0] = v2p_port1;
+               ldev->v2p_map[1] = v2p_port2;
+
+               mlx5_core_info(dev0, "modify lag map port 1:%d port 2:%d",
+                              ldev->v2p_map[0], ldev->v2p_map[1]);
+
+               err = mlx5_cmd_modify_lag(dev0, v2p_port1, v2p_port2);
+               if (err)
+                       mlx5_core_err(dev0,
+                                     "Failed to modify LAG (%d)\n",
+                                     err);
+       }
+}
+
+static int mlx5_create_lag(struct mlx5_lag *ldev,
+                          struct lag_tracker *tracker)
+{
+       struct mlx5_core_dev *dev0 = ldev->pf[0].dev;
+       int err;
 
        mlx5_infer_tx_affinity_mapping(tracker, &ldev->v2p_map[0],
                                       &ldev->v2p_map[1]);
 
+       mlx5_core_info(dev0, "lag map port 1:%d port 2:%d",
+                      ldev->v2p_map[0], ldev->v2p_map[1]);
+
        err = mlx5_cmd_create_lag(dev0, ldev->v2p_map[0], ldev->v2p_map[1]);
        if (err)
                mlx5_core_err(dev0,
                              "Failed to create LAG (%d)\n",
                              err);
+       return err;
+}
+
+static int mlx5_activate_lag(struct mlx5_lag *ldev,
+                            struct lag_tracker *tracker,
+                            u8 flags)
+{
+       bool roce_lag = !!(flags & MLX5_LAG_FLAG_ROCE);
+       struct mlx5_core_dev *dev0 = ldev->pf[0].dev;
+       int err;
+
+       err = mlx5_create_lag(ldev, tracker);
+       if (err) {
+               if (roce_lag) {
+                       mlx5_core_err(dev0,
+                                     "Failed to activate RoCE LAG\n");
+               } else {
+                       mlx5_core_err(dev0,
+                                     "Failed to activate VF LAG\n"
+                                     "Make sure all VFs are unbound prior to VF LAG activation or deactivation\n");
+               }
+               return err;
+       }
+
+       ldev->flags |= flags;
+       return 0;
 }
 
-static void mlx5_deactivate_lag(struct mlx5_lag *ldev)
+static int mlx5_deactivate_lag(struct mlx5_lag *ldev)
 {
        struct mlx5_core_dev *dev0 = ldev->pf[0].dev;
+       bool roce_lag = __mlx5_lag_is_roce(ldev);
        int err;
 
-       ldev->flags &= ~MLX5_LAG_FLAG_BONDED;
+       ldev->flags &= ~MLX5_LAG_MODE_FLAGS;
 
        err = mlx5_cmd_destroy_lag(dev0);
-       if (err)
-               mlx5_core_err(dev0,
-                             "Failed to destroy LAG (%d)\n",
-                             err);
+       if (err) {
+               if (roce_lag) {
+                       mlx5_core_err(dev0,
+                                     "Failed to deactivate RoCE LAG; driver restart required\n");
+               } else {
+                       mlx5_core_err(dev0,
+                                     "Failed to deactivate VF LAG; driver restart required\n"
+                                     "Make sure all VFs are unbound prior to VF LAG activation or deactivation\n");
+               }
+       }
+
+       return err;
+}
+
+static bool mlx5_lag_check_prereq(struct mlx5_lag *ldev)
+{
+       if (!ldev->pf[0].dev || !ldev->pf[1].dev)
+               return false;
+
+#ifdef CONFIG_MLX5_ESWITCH
+       return mlx5_esw_lag_prereq(ldev->pf[0].dev, ldev->pf[1].dev);
+#else
+       return (!mlx5_sriov_is_enabled(ldev->pf[0].dev) &&
+               !mlx5_sriov_is_enabled(ldev->pf[1].dev));
+#endif
+}
+
+static void mlx5_lag_add_ib_devices(struct mlx5_lag *ldev)
+{
+       int i;
+
+       for (i = 0; i < MLX5_MAX_PORTS; i++)
+               if (ldev->pf[i].dev)
+                       mlx5_add_dev_by_protocol(ldev->pf[i].dev,
+                                                MLX5_INTERFACE_PROTOCOL_IB);
+}
+
+static void mlx5_lag_remove_ib_devices(struct mlx5_lag *ldev)
+{
+       int i;
+
+       for (i = 0; i < MLX5_MAX_PORTS; i++)
+               if (ldev->pf[i].dev)
+                       mlx5_remove_dev_by_protocol(ldev->pf[i].dev,
+                                                   MLX5_INTERFACE_PROTOCOL_IB);
 }
 
 static void mlx5_do_bond(struct mlx5_lag *ldev)
@@ -223,9 +327,8 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)
        struct mlx5_core_dev *dev0 = ldev->pf[0].dev;
        struct mlx5_core_dev *dev1 = ldev->pf[1].dev;
        struct lag_tracker tracker;
-       u8 v2p_port1, v2p_port2;
-       int i, err;
-       bool do_bond;
+       bool do_bond, roce_lag;
+       int err;
 
        if (!dev0 || !dev1)
                return;
@@ -234,42 +337,45 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)
        tracker = ldev->tracker;
        mutex_unlock(&lag_mutex);
 
-       do_bond = tracker.is_bonded && ldev->allowed;
+       do_bond = tracker.is_bonded && mlx5_lag_check_prereq(ldev);
 
-       if (do_bond && !mlx5_lag_is_bonded(ldev)) {
-               for (i = 0; i < MLX5_MAX_PORTS; i++)
-                       mlx5_remove_dev_by_protocol(ldev->pf[i].dev,
-                                                   MLX5_INTERFACE_PROTOCOL_IB);
+       if (do_bond && !__mlx5_lag_is_active(ldev)) {
+               roce_lag = !mlx5_sriov_is_enabled(dev0) &&
+                          !mlx5_sriov_is_enabled(dev1);
 
-               mlx5_activate_lag(ldev, &tracker);
+               if (roce_lag)
+                       mlx5_lag_remove_ib_devices(ldev);
 
-               mlx5_add_dev_by_protocol(dev0, MLX5_INTERFACE_PROTOCOL_IB);
-               mlx5_nic_vport_enable_roce(dev1);
-       } else if (do_bond && mlx5_lag_is_bonded(ldev)) {
-               mlx5_infer_tx_affinity_mapping(&tracker, &v2p_port1,
-                                              &v2p_port2);
+               err = mlx5_activate_lag(ldev, &tracker,
+                                       roce_lag ? MLX5_LAG_FLAG_ROCE :
+                                       MLX5_LAG_FLAG_SRIOV);
+               if (err) {
+                       if (roce_lag)
+                               mlx5_lag_add_ib_devices(ldev);
 
-               if ((v2p_port1 != ldev->v2p_map[0]) ||
-                   (v2p_port2 != ldev->v2p_map[1])) {
-                       ldev->v2p_map[0] = v2p_port1;
-                       ldev->v2p_map[1] = v2p_port2;
+                       return;
+               }
 
-                       err = mlx5_cmd_modify_lag(dev0, v2p_port1, v2p_port2);
-                       if (err)
-                               mlx5_core_err(dev0,
-                                             "Failed to modify LAG (%d)\n",
-                                             err);
+               if (roce_lag) {
+                       mlx5_add_dev_by_protocol(dev0, MLX5_INTERFACE_PROTOCOL_IB);
+                       mlx5_nic_vport_enable_roce(dev1);
+               }
+       } else if (do_bond && __mlx5_lag_is_active(ldev)) {
+               mlx5_modify_lag(ldev, &tracker);
+       } else if (!do_bond && __mlx5_lag_is_active(ldev)) {
+               roce_lag = __mlx5_lag_is_roce(ldev);
+
+               if (roce_lag) {
+                       mlx5_remove_dev_by_protocol(dev0, MLX5_INTERFACE_PROTOCOL_IB);
+                       mlx5_nic_vport_disable_roce(dev1);
                }
-       } else if (!do_bond && mlx5_lag_is_bonded(ldev)) {
-               mlx5_remove_dev_by_protocol(dev0, MLX5_INTERFACE_PROTOCOL_IB);
-               mlx5_nic_vport_disable_roce(dev1);
 
-               mlx5_deactivate_lag(ldev);
+               err = mlx5_deactivate_lag(ldev);
+               if (err)
+                       return;
 
-               for (i = 0; i < MLX5_MAX_PORTS; i++)
-                       if (ldev->pf[i].dev)
-                               mlx5_add_dev_by_protocol(ldev->pf[i].dev,
-                                                        MLX5_INTERFACE_PROTOCOL_IB);
+               if (roce_lag)
+                       mlx5_lag_add_ib_devices(ldev);
        }
 }
 
@@ -419,15 +525,6 @@ static int mlx5_lag_netdev_event(struct notifier_block *this,
        return NOTIFY_DONE;
 }
 
-static bool mlx5_lag_check_prereq(struct mlx5_lag *ldev)
-{
-       if ((ldev->pf[0].dev && mlx5_sriov_is_enabled(ldev->pf[0].dev)) ||
-           (ldev->pf[1].dev && mlx5_sriov_is_enabled(ldev->pf[1].dev)))
-               return false;
-       else
-               return true;
-}
-
 static struct mlx5_lag *mlx5_lag_dev_alloc(void)
 {
        struct mlx5_lag *ldev;
@@ -437,7 +534,6 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(void)
                return NULL;
 
        INIT_DELAYED_WORK(&ldev->bond_work, mlx5_do_bond_work);
-       ldev->allowed = mlx5_lag_check_prereq(ldev);
 
        return ldev;
 }
@@ -462,7 +558,6 @@ static void mlx5_lag_dev_add_pf(struct mlx5_lag *ldev,
        ldev->tracker.netdev_state[fn].link_up = 0;
        ldev->tracker.netdev_state[fn].tx_enabled = 0;
 
-       ldev->allowed = mlx5_lag_check_prereq(ldev);
        dev->priv.lag = ldev;
 
        mutex_unlock(&lag_mutex);
@@ -484,7 +579,6 @@ static void mlx5_lag_dev_remove_pf(struct mlx5_lag *ldev,
        memset(&ldev->pf[i], 0, sizeof(*ldev->pf));
 
        dev->priv.lag = NULL;
-       ldev->allowed = mlx5_lag_check_prereq(ldev);
        mutex_unlock(&lag_mutex);
 }
 
@@ -532,7 +626,7 @@ void mlx5_lag_remove(struct mlx5_core_dev *dev)
        if (!ldev)
                return;
 
-       if (mlx5_lag_is_bonded(ldev))
+       if (__mlx5_lag_is_active(ldev))
                mlx5_deactivate_lag(ldev);
 
        mlx5_lag_dev_remove_pf(ldev, dev);
@@ -549,56 +643,61 @@ void mlx5_lag_remove(struct mlx5_core_dev *dev)
        }
 }
 
-bool mlx5_lag_is_active(struct mlx5_core_dev *dev)
+bool mlx5_lag_is_roce(struct mlx5_core_dev *dev)
 {
        struct mlx5_lag *ldev;
        bool res;
 
        mutex_lock(&lag_mutex);
        ldev = mlx5_lag_dev_get(dev);
-       res  = ldev && mlx5_lag_is_bonded(ldev);
+       res  = ldev && __mlx5_lag_is_roce(ldev);
        mutex_unlock(&lag_mutex);
 
        return res;
 }
-EXPORT_SYMBOL(mlx5_lag_is_active);
+EXPORT_SYMBOL(mlx5_lag_is_roce);
 
-static int mlx5_lag_set_state(struct mlx5_core_dev *dev, bool allow)
+bool mlx5_lag_is_active(struct mlx5_core_dev *dev)
 {
        struct mlx5_lag *ldev;
-       int ret = 0;
-       bool lag_active;
-
-       mlx5_dev_list_lock();
+       bool res;
 
+       mutex_lock(&lag_mutex);
        ldev = mlx5_lag_dev_get(dev);
-       if (!ldev) {
-               ret = -ENODEV;
-               goto unlock;
-       }
-       lag_active = mlx5_lag_is_bonded(ldev);
-       if (!mlx5_lag_check_prereq(ldev) && allow) {
-               ret = -EINVAL;
-               goto unlock;
-       }
-       if (ldev->allowed == allow)
-               goto unlock;
-       ldev->allowed = allow;
-       if ((lag_active && !allow) || allow)
-               mlx5_do_bond(ldev);
-unlock:
-       mlx5_dev_list_unlock();
-       return ret;
+       res  = ldev && __mlx5_lag_is_active(ldev);
+       mutex_unlock(&lag_mutex);
+
+       return res;
 }
+EXPORT_SYMBOL(mlx5_lag_is_active);
 
-int mlx5_lag_forbid(struct mlx5_core_dev *dev)
+bool mlx5_lag_is_sriov(struct mlx5_core_dev *dev)
 {
-       return mlx5_lag_set_state(dev, false);
+       struct mlx5_lag *ldev;
+       bool res;
+
+       mutex_lock(&lag_mutex);
+       ldev = mlx5_lag_dev_get(dev);
+       res  = ldev && __mlx5_lag_is_sriov(ldev);
+       mutex_unlock(&lag_mutex);
+
+       return res;
 }
+EXPORT_SYMBOL(mlx5_lag_is_sriov);
 
-int mlx5_lag_allow(struct mlx5_core_dev *dev)
+void mlx5_lag_update(struct mlx5_core_dev *dev)
 {
-       return mlx5_lag_set_state(dev, true);
+       struct mlx5_lag *ldev;
+
+       mlx5_dev_list_lock();
+       ldev = mlx5_lag_dev_get(dev);
+       if (!ldev)
+               goto unlock;
+
+       mlx5_do_bond(ldev);
+
+unlock:
+       mlx5_dev_list_unlock();
 }
 
 struct net_device *mlx5_lag_get_roce_netdev(struct mlx5_core_dev *dev)
@@ -609,7 +708,7 @@ struct net_device *mlx5_lag_get_roce_netdev(struct mlx5_core_dev *dev)
        mutex_lock(&lag_mutex);
        ldev = mlx5_lag_dev_get(dev);
 
-       if (!(ldev && mlx5_lag_is_bonded(ldev)))
+       if (!(ldev && __mlx5_lag_is_roce(ldev)))
                goto unlock;
 
        if (ldev->tracker.tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) {
@@ -638,7 +737,7 @@ bool mlx5_lag_intf_add(struct mlx5_interface *intf, struct mlx5_priv *priv)
                return true;
 
        ldev = mlx5_lag_dev_get(dev);
-       if (!ldev || !mlx5_lag_is_bonded(ldev) || ldev->pf[0].dev == dev)
+       if (!ldev || !__mlx5_lag_is_roce(ldev) || ldev->pf[0].dev == dev)
                return true;
 
        /* If bonded, we do not add an IB device for PF1. */
@@ -665,7 +764,7 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,
 
        mutex_lock(&lag_mutex);
        ldev = mlx5_lag_dev_get(dev);
-       if (ldev && mlx5_lag_is_bonded(ldev)) {
+       if (ldev && __mlx5_lag_is_roce(ldev)) {
                num_ports = MLX5_MAX_PORTS;
                mdev[0] = ldev->pf[0].dev;
                mdev[1] = ldev->pf[1].dev;
index 0d90b1b..ca0ee99 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/clocksource.h>
 #include <linux/highmem.h>
 #include <rdma/mlx5-abi.h>
+#include "lib/eq.h"
 #include "en.h"
 #include "clock.h"
 
@@ -71,7 +72,7 @@ static u64 read_internal_timer(const struct cyclecounter *cc)
        struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev,
                                                  clock);
 
-       return mlx5_read_internal_timer(mdev) & cc->mask;
+       return mlx5_read_internal_timer(mdev, NULL) & cc->mask;
 }
 
 static void mlx5_update_clock_info_page(struct mlx5_core_dev *mdev)
@@ -155,15 +156,19 @@ static int mlx5_ptp_settime(struct ptp_clock_info *ptp,
        return 0;
 }
 
-static int mlx5_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+static int mlx5_ptp_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts,
+                            struct ptp_system_timestamp *sts)
 {
        struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock,
                                                ptp_info);
-       u64 ns;
+       struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev,
+                                                 clock);
        unsigned long flags;
+       u64 cycles, ns;
 
        write_seqlock_irqsave(&clock->lock, flags);
-       ns = timecounter_read(&clock->tc);
+       cycles = mlx5_read_internal_timer(mdev, sts);
+       ns = timecounter_cyc2time(&clock->tc, cycles);
        write_sequnlock_irqrestore(&clock->lock, flags);
 
        *ts = ns_to_timespec64(ns);
@@ -306,7 +311,7 @@ static int mlx5_perout_configure(struct ptp_clock_info *ptp,
                ts.tv_sec = rq->perout.start.sec;
                ts.tv_nsec = rq->perout.start.nsec;
                ns = timespec64_to_ns(&ts);
-               cycles_now = mlx5_read_internal_timer(mdev);
+               cycles_now = mlx5_read_internal_timer(mdev, NULL);
                write_seqlock_irqsave(&clock->lock, flags);
                nsec_now = timecounter_cyc2time(&clock->tc, cycles_now);
                nsec_delta = ns - nsec_now;
@@ -383,7 +388,7 @@ static const struct ptp_clock_info mlx5_ptp_clock_info = {
        .pps            = 0,
        .adjfreq        = mlx5_ptp_adjfreq,
        .adjtime        = mlx5_ptp_adjtime,
-       .gettime64      = mlx5_ptp_gettime,
+       .gettimex64     = mlx5_ptp_gettimex,
        .settime64      = mlx5_ptp_settime,
        .enable         = NULL,
        .verify         = NULL,
@@ -439,16 +444,17 @@ static void mlx5_get_pps_caps(struct mlx5_core_dev *mdev)
        clock->pps_info.pin_caps[7] = MLX5_GET(mtpps_reg, out, cap_pin_7_mode);
 }
 
-void mlx5_pps_event(struct mlx5_core_dev *mdev,
-                   struct mlx5_eqe *eqe)
+static int mlx5_pps_event(struct notifier_block *nb,
+                         unsigned long type, void *data)
 {
-       struct mlx5_clock *clock = &mdev->clock;
+       struct mlx5_clock *clock = mlx5_nb_cof(nb, struct mlx5_clock, pps_nb);
+       struct mlx5_core_dev *mdev = clock->mdev;
        struct ptp_clock_event ptp_event;
-       struct timespec64 ts;
-       u64 nsec_now, nsec_delta;
        u64 cycles_now, cycles_delta;
+       u64 nsec_now, nsec_delta, ns;
+       struct mlx5_eqe *eqe = data;
        int pin = eqe->data.pps.pin;
-       s64 ns;
+       struct timespec64 ts;
        unsigned long flags;
 
        switch (clock->ptp_info.pin_config[pin].func) {
@@ -463,11 +469,12 @@ void mlx5_pps_event(struct mlx5_core_dev *mdev,
                } else {
                        ptp_event.type = PTP_CLOCK_EXTTS;
                }
+               /* TODOL clock->ptp can be NULL if ptp_clock_register failes */
                ptp_clock_event(clock->ptp, &ptp_event);
                break;
        case PTP_PF_PEROUT:
-               mlx5_ptp_gettime(&clock->ptp_info, &ts);
-               cycles_now = mlx5_read_internal_timer(mdev);
+               mlx5_ptp_gettimex(&clock->ptp_info, &ts, NULL);
+               cycles_now = mlx5_read_internal_timer(mdev, NULL);
                ts.tv_sec += 1;
                ts.tv_nsec = 0;
                ns = timespec64_to_ns(&ts);
@@ -481,8 +488,11 @@ void mlx5_pps_event(struct mlx5_core_dev *mdev,
                write_sequnlock_irqrestore(&clock->lock, flags);
                break;
        default:
-               mlx5_core_err(mdev, " Unhandled event\n");
+               mlx5_core_err(mdev, " Unhandled clock PPS event, func %d\n",
+                             clock->ptp_info.pin_config[pin].func);
        }
+
+       return NOTIFY_OK;
 }
 
 void mlx5_init_clock(struct mlx5_core_dev *mdev)
@@ -511,14 +521,14 @@ void mlx5_init_clock(struct mlx5_core_dev *mdev)
                         ktime_to_ns(ktime_get_real()));
 
        /* Calculate period in seconds to call the overflow watchdog - to make
-        * sure counter is checked at least once every wrap around.
+        * sure counter is checked at least twice every wrap around.
         * The period is calculated as the minimum between max HW cycles count
         * (The clock source mask) and max amount of cycles that can be
         * multiplied by clock multiplier where the result doesn't exceed
         * 64bits.
         */
        overflow_cycles = div64_u64(~0ULL >> 1, clock->cycles.mult);
-       overflow_cycles = min(overflow_cycles, clock->cycles.mask >> 1);
+       overflow_cycles = min(overflow_cycles, div_u64(clock->cycles.mask, 3));
 
        ns = cyclecounter_cyc2ns(&clock->cycles, overflow_cycles,
                                 frac, &frac);
@@ -567,6 +577,9 @@ void mlx5_init_clock(struct mlx5_core_dev *mdev)
                               PTR_ERR(clock->ptp));
                clock->ptp = NULL;
        }
+
+       MLX5_NB_INIT(&clock->pps_nb, mlx5_pps_event, PPS_EVENT);
+       mlx5_eq_notifier_register(mdev, &clock->pps_nb);
 }
 
 void mlx5_cleanup_clock(struct mlx5_core_dev *mdev)
@@ -576,6 +589,7 @@ void mlx5_cleanup_clock(struct mlx5_core_dev *mdev)
        if (!MLX5_CAP_GEN(mdev, device_frequency_khz))
                return;
 
+       mlx5_eq_notifier_unregister(mdev, &clock->pps_nb);
        if (clock->ptp) {
                ptp_clock_unregister(clock->ptp);
                clock->ptp = NULL;
index 263cb6e..3160092 100644 (file)
@@ -36,7 +36,6 @@
 #if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
 void mlx5_init_clock(struct mlx5_core_dev *mdev);
 void mlx5_cleanup_clock(struct mlx5_core_dev *mdev);
-void mlx5_pps_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe);
 
 static inline int mlx5_clock_get_ptp_index(struct mlx5_core_dev *mdev)
 {
@@ -60,8 +59,6 @@ static inline ktime_t mlx5_timecounter_cyc2time(struct mlx5_clock *clock,
 #else
 static inline void mlx5_init_clock(struct mlx5_core_dev *mdev) {}
 static inline void mlx5_cleanup_clock(struct mlx5_core_dev *mdev) {}
-static inline void mlx5_pps_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe) {}
-
 static inline int mlx5_clock_get_ptp_index(struct mlx5_core_dev *mdev)
 {
        return -1;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c
new file mode 100644 (file)
index 0000000..bced2ef
--- /dev/null
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2018 Mellanox Technologies */
+
+#include <linux/mlx5/vport.h>
+#include "lib/devcom.h"
+
+static LIST_HEAD(devcom_list);
+
+#define devcom_for_each_component(priv, comp, iter) \
+       for (iter = 0; \
+            comp = &(priv)->components[iter], iter < MLX5_DEVCOM_NUM_COMPONENTS; \
+            iter++)
+
+struct mlx5_devcom_component {
+       struct {
+               void *data;
+       } device[MLX5_MAX_PORTS];
+
+       mlx5_devcom_event_handler_t handler;
+       struct rw_semaphore sem;
+       bool paired;
+};
+
+struct mlx5_devcom_list {
+       struct list_head list;
+
+       struct mlx5_devcom_component components[MLX5_DEVCOM_NUM_COMPONENTS];
+       struct mlx5_core_dev *devs[MLX5_MAX_PORTS];
+};
+
+struct mlx5_devcom {
+       struct mlx5_devcom_list *priv;
+       int idx;
+};
+
+static struct mlx5_devcom_list *mlx5_devcom_list_alloc(void)
+{
+       struct mlx5_devcom_component *comp;
+       struct mlx5_devcom_list *priv;
+       int i;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return NULL;
+
+       devcom_for_each_component(priv, comp, i)
+               init_rwsem(&comp->sem);
+
+       return priv;
+}
+
+static struct mlx5_devcom *mlx5_devcom_alloc(struct mlx5_devcom_list *priv,
+                                            u8 idx)
+{
+       struct mlx5_devcom *devcom;
+
+       devcom = kzalloc(sizeof(*devcom), GFP_KERNEL);
+       if (!devcom)
+               return NULL;
+
+       devcom->priv = priv;
+       devcom->idx = idx;
+       return devcom;
+}
+
+/* Must be called with intf_mutex held */
+struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev)
+{
+       struct mlx5_devcom_list *priv = NULL, *iter;
+       struct mlx5_devcom *devcom = NULL;
+       bool new_priv = false;
+       u64 sguid0, sguid1;
+       int idx, i;
+
+       if (!mlx5_core_is_pf(dev))
+               return NULL;
+
+       sguid0 = mlx5_query_nic_system_image_guid(dev);
+       list_for_each_entry(iter, &devcom_list, list) {
+               struct mlx5_core_dev *tmp_dev = NULL;
+
+               idx = -1;
+               for (i = 0; i < MLX5_MAX_PORTS; i++) {
+                       if (iter->devs[i])
+                               tmp_dev = iter->devs[i];
+                       else
+                               idx = i;
+               }
+
+               if (idx == -1)
+                       continue;
+
+               sguid1 = mlx5_query_nic_system_image_guid(tmp_dev);
+               if (sguid0 != sguid1)
+                       continue;
+
+               priv = iter;
+               break;
+       }
+
+       if (!priv) {
+               priv = mlx5_devcom_list_alloc();
+               if (!priv)
+                       return ERR_PTR(-ENOMEM);
+
+               idx = 0;
+               new_priv = true;
+       }
+
+       priv->devs[idx] = dev;
+       devcom = mlx5_devcom_alloc(priv, idx);
+       if (!devcom) {
+               kfree(priv);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       if (new_priv)
+               list_add(&priv->list, &devcom_list);
+
+       return devcom;
+}
+
+/* Must be called with intf_mutex held */
+void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom)
+{
+       struct mlx5_devcom_list *priv;
+       int i;
+
+       if (IS_ERR_OR_NULL(devcom))
+               return;
+
+       priv = devcom->priv;
+       priv->devs[devcom->idx] = NULL;
+
+       kfree(devcom);
+
+       for (i = 0; i < MLX5_MAX_PORTS; i++)
+               if (priv->devs[i])
+                       break;
+
+       if (i != MLX5_MAX_PORTS)
+               return;
+
+       list_del(&priv->list);
+       kfree(priv);
+}
+
+void mlx5_devcom_register_component(struct mlx5_devcom *devcom,
+                                   enum mlx5_devcom_components id,
+                                   mlx5_devcom_event_handler_t handler,
+                                   void *data)
+{
+       struct mlx5_devcom_component *comp;
+
+       if (IS_ERR_OR_NULL(devcom))
+               return;
+
+       WARN_ON(!data);
+
+       comp = &devcom->priv->components[id];
+       down_write(&comp->sem);
+       comp->handler = handler;
+       comp->device[devcom->idx].data = data;
+       up_write(&comp->sem);
+}
+
+void mlx5_devcom_unregister_component(struct mlx5_devcom *devcom,
+                                     enum mlx5_devcom_components id)
+{
+       struct mlx5_devcom_component *comp;
+
+       if (IS_ERR_OR_NULL(devcom))
+               return;
+
+       comp = &devcom->priv->components[id];
+       down_write(&comp->sem);
+       comp->device[devcom->idx].data = NULL;
+       up_write(&comp->sem);
+}
+
+int mlx5_devcom_send_event(struct mlx5_devcom *devcom,
+                          enum mlx5_devcom_components id,
+                          int event,
+                          void *event_data)
+{
+       struct mlx5_devcom_component *comp;
+       int err = -ENODEV, i;
+
+       if (IS_ERR_OR_NULL(devcom))
+               return err;
+
+       comp = &devcom->priv->components[id];
+       down_write(&comp->sem);
+       for (i = 0; i < MLX5_MAX_PORTS; i++)
+               if (i != devcom->idx && comp->device[i].data) {
+                       err = comp->handler(event, comp->device[i].data,
+                                           event_data);
+                       break;
+               }
+
+       up_write(&comp->sem);
+       return err;
+}
+
+void mlx5_devcom_set_paired(struct mlx5_devcom *devcom,
+                           enum mlx5_devcom_components id,
+                           bool paired)
+{
+       struct mlx5_devcom_component *comp;
+
+       comp = &devcom->priv->components[id];
+       WARN_ON(!rwsem_is_locked(&comp->sem));
+
+       comp->paired = paired;
+}
+
+bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom,
+                          enum mlx5_devcom_components id)
+{
+       if (IS_ERR_OR_NULL(devcom))
+               return false;
+
+       return devcom->priv->components[id].paired;
+}
+
+void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom,
+                               enum mlx5_devcom_components id)
+{
+       struct mlx5_devcom_component *comp;
+       int i;
+
+       if (IS_ERR_OR_NULL(devcom))
+               return NULL;
+
+       comp = &devcom->priv->components[id];
+       down_read(&comp->sem);
+       if (!comp->paired) {
+               up_read(&comp->sem);
+               return NULL;
+       }
+
+       for (i = 0; i < MLX5_MAX_PORTS; i++)
+               if (i != devcom->idx)
+                       break;
+
+       return comp->device[i].data;
+}
+
+void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom,
+                                  enum mlx5_devcom_components id)
+{
+       struct mlx5_devcom_component *comp = &devcom->priv->components[id];
+
+       up_read(&comp->sem);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h
new file mode 100644 (file)
index 0000000..939d5bf
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2018 Mellanox Technologies */
+
+#ifndef __LIB_MLX5_DEVCOM_H__
+#define __LIB_MLX5_DEVCOM_H__
+
+#include <linux/mlx5/driver.h>
+
+enum mlx5_devcom_components {
+       MLX5_DEVCOM_ESW_OFFLOADS,
+
+       MLX5_DEVCOM_NUM_COMPONENTS,
+};
+
+typedef int (*mlx5_devcom_event_handler_t)(int event,
+                                          void *my_data,
+                                          void *event_data);
+
+struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev);
+void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom);
+
+void mlx5_devcom_register_component(struct mlx5_devcom *devcom,
+                                   enum mlx5_devcom_components id,
+                                   mlx5_devcom_event_handler_t handler,
+                                   void *data);
+void mlx5_devcom_unregister_component(struct mlx5_devcom *devcom,
+                                     enum mlx5_devcom_components id);
+
+int mlx5_devcom_send_event(struct mlx5_devcom *devcom,
+                          enum mlx5_devcom_components id,
+                          int event,
+                          void *event_data);
+
+void mlx5_devcom_set_paired(struct mlx5_devcom *devcom,
+                           enum mlx5_devcom_components id,
+                           bool paired);
+bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom,
+                          enum mlx5_devcom_components id);
+
+void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom,
+                               enum mlx5_devcom_components id);
+void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom,
+                                  enum mlx5_devcom_components id);
+
+#endif
+
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h
new file mode 100644 (file)
index 0000000..c0fb6d7
--- /dev/null
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2018 Mellanox Technologies */
+
+#ifndef __LIB_MLX5_EQ_H__
+#define __LIB_MLX5_EQ_H__
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/eq.h>
+#include <linux/mlx5/cq.h>
+
+#define MLX5_MAX_IRQ_NAME   (32)
+#define MLX5_EQE_SIZE       (sizeof(struct mlx5_eqe))
+
+struct mlx5_eq_tasklet {
+       struct list_head      list;
+       struct list_head      process_list;
+       struct tasklet_struct task;
+       spinlock_t            lock; /* lock completion tasklet list */
+};
+
+struct mlx5_cq_table {
+       spinlock_t              lock;   /* protect radix tree */
+       struct radix_tree_root  tree;
+};
+
+struct mlx5_eq {
+       struct mlx5_core_dev    *dev;
+       struct mlx5_cq_table    cq_table;
+       __be32 __iomem          *doorbell;
+       u32                     cons_index;
+       struct mlx5_frag_buf    buf;
+       int                     size;
+       unsigned int            vecidx;
+       unsigned int            irqn;
+       u8                      eqn;
+       int                     nent;
+       struct mlx5_rsc_debug   *dbg;
+};
+
+struct mlx5_eq_comp {
+       struct mlx5_eq          core; /* Must be first */
+       struct mlx5_eq_tasklet  tasklet_ctx;
+       struct list_head        list;
+};
+
+static inline struct mlx5_eqe *get_eqe(struct mlx5_eq *eq, u32 entry)
+{
+       return mlx5_buf_offset(&eq->buf, entry * MLX5_EQE_SIZE);
+}
+
+static inline struct mlx5_eqe *next_eqe_sw(struct mlx5_eq *eq)
+{
+       struct mlx5_eqe *eqe = get_eqe(eq, eq->cons_index & (eq->nent - 1));
+
+       return ((eqe->owner & 1) ^ !!(eq->cons_index & eq->nent)) ? NULL : eqe;
+}
+
+static inline void eq_update_ci(struct mlx5_eq *eq, int arm)
+{
+       __be32 __iomem *addr = eq->doorbell + (arm ? 0 : 2);
+       u32 val = (eq->cons_index & 0xffffff) | (eq->eqn << 24);
+
+       __raw_writel((__force u32)cpu_to_be32(val), addr);
+       /* We still want ordering, just not swabbing, so add a barrier */
+       mb();
+}
+
+int mlx5_eq_table_init(struct mlx5_core_dev *dev);
+void mlx5_eq_table_cleanup(struct mlx5_core_dev *dev);
+int mlx5_eq_table_create(struct mlx5_core_dev *dev);
+void mlx5_eq_table_destroy(struct mlx5_core_dev *dev);
+
+int mlx5_eq_add_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq);
+int mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq);
+struct mlx5_eq_comp *mlx5_eqn2comp_eq(struct mlx5_core_dev *dev, int eqn);
+struct mlx5_eq *mlx5_get_async_eq(struct mlx5_core_dev *dev);
+void mlx5_cq_tasklet_cb(unsigned long data);
+struct cpumask *mlx5_eq_comp_cpumask(struct mlx5_core_dev *dev, int ix);
+
+u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq_comp *eq);
+void mlx5_eq_synchronize_async_irq(struct mlx5_core_dev *dev);
+void mlx5_eq_synchronize_cmd_irq(struct mlx5_core_dev *dev);
+
+int mlx5_debug_eq_add(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
+void mlx5_debug_eq_remove(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
+int mlx5_eq_debugfs_init(struct mlx5_core_dev *dev);
+void mlx5_eq_debugfs_cleanup(struct mlx5_core_dev *dev);
+
+/* This function should only be called after mlx5_cmd_force_teardown_hca */
+void mlx5_core_eq_free_irqs(struct mlx5_core_dev *dev);
+
+#ifdef CONFIG_RFS_ACCEL
+struct cpu_rmap *mlx5_eq_table_get_rmap(struct mlx5_core_dev *dev);
+#endif
+
+int mlx5_eq_notifier_register(struct mlx5_core_dev *dev, struct mlx5_nb *nb);
+int mlx5_eq_notifier_unregister(struct mlx5_core_dev *dev, struct mlx5_nb *nb);
+
+#endif
index 7550b1c..397a284 100644 (file)
@@ -33,6 +33,8 @@
 #ifndef __LIB_MLX5_H__
 #define __LIB_MLX5_H__
 
+#include "mlx5_core.h"
+
 void mlx5_init_reserved_gids(struct mlx5_core_dev *dev);
 void mlx5_cleanup_reserved_gids(struct mlx5_core_dev *dev);
 int  mlx5_core_reserve_gids(struct mlx5_core_dev *dev, unsigned int count);
@@ -40,4 +42,38 @@ void mlx5_core_unreserve_gids(struct mlx5_core_dev *dev, unsigned int count);
 int  mlx5_core_reserved_gid_alloc(struct mlx5_core_dev *dev, int *gid_index);
 void mlx5_core_reserved_gid_free(struct mlx5_core_dev *dev, int gid_index);
 
+/* TODO move to lib/events.h */
+
+#define PORT_MODULE_EVENT_MODULE_STATUS_MASK 0xF
+#define PORT_MODULE_EVENT_ERROR_TYPE_MASK    0xF
+
+enum port_module_event_status_type {
+       MLX5_MODULE_STATUS_PLUGGED   = 0x1,
+       MLX5_MODULE_STATUS_UNPLUGGED = 0x2,
+       MLX5_MODULE_STATUS_ERROR     = 0x3,
+       MLX5_MODULE_STATUS_DISABLED  = 0x4,
+       MLX5_MODULE_STATUS_NUM,
+};
+
+enum  port_module_event_error_type {
+       MLX5_MODULE_EVENT_ERROR_POWER_BUDGET_EXCEEDED    = 0x0,
+       MLX5_MODULE_EVENT_ERROR_LONG_RANGE_FOR_NON_MLNX  = 0x1,
+       MLX5_MODULE_EVENT_ERROR_BUS_STUCK                = 0x2,
+       MLX5_MODULE_EVENT_ERROR_NO_EEPROM_RETRY_TIMEOUT  = 0x3,
+       MLX5_MODULE_EVENT_ERROR_ENFORCE_PART_NUMBER_LIST = 0x4,
+       MLX5_MODULE_EVENT_ERROR_UNKNOWN_IDENTIFIER       = 0x5,
+       MLX5_MODULE_EVENT_ERROR_HIGH_TEMPERATURE         = 0x6,
+       MLX5_MODULE_EVENT_ERROR_BAD_CABLE                = 0x7,
+       MLX5_MODULE_EVENT_ERROR_PCIE_POWER_SLOT_EXCEEDED = 0xc,
+       MLX5_MODULE_EVENT_ERROR_NUM,
+};
+
+struct mlx5_pme_stats {
+       u64 status_counters[MLX5_MODULE_STATUS_NUM];
+       u64 error_counters[MLX5_MODULE_EVENT_ERROR_NUM];
+};
+
+void mlx5_get_pme_stats(struct mlx5_core_dev *dev, struct mlx5_pme_stats *stats);
+int mlx5_notifier_call_chain(struct mlx5_events *events, unsigned int event, void *data);
+
 #endif
index 28132c7..77896c1 100644 (file)
@@ -43,7 +43,6 @@
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/cq.h>
 #include <linux/mlx5/qp.h>
-#include <linux/mlx5/srq.h>
 #include <linux/debugfs.h>
 #include <linux/kmod.h>
 #include <linux/mlx5/mlx5_ifc.h>
@@ -53,6 +52,7 @@
 #endif
 #include <net/devlink.h>
 #include "mlx5_core.h"
+#include "lib/eq.h"
 #include "fs_core.h"
 #include "lib/mpfs.h"
 #include "eswitch.h"
@@ -63,6 +63,7 @@
 #include "accel/tls.h"
 #include "lib/clock.h"
 #include "lib/vxlan.h"
+#include "lib/devcom.h"
 #include "diag/fw_tracer.h"
 
 MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
@@ -319,51 +320,6 @@ static void release_bar(struct pci_dev *pdev)
        pci_release_regions(pdev);
 }
 
-static int mlx5_alloc_irq_vectors(struct mlx5_core_dev *dev)
-{
-       struct mlx5_priv *priv = &dev->priv;
-       struct mlx5_eq_table *table = &priv->eq_table;
-       int num_eqs = MLX5_CAP_GEN(dev, max_num_eqs) ?
-                     MLX5_CAP_GEN(dev, max_num_eqs) :
-                     1 << MLX5_CAP_GEN(dev, log_max_eq);
-       int nvec;
-       int err;
-
-       nvec = MLX5_CAP_GEN(dev, num_ports) * num_online_cpus() +
-              MLX5_EQ_VEC_COMP_BASE;
-       nvec = min_t(int, nvec, num_eqs);
-       if (nvec <= MLX5_EQ_VEC_COMP_BASE)
-               return -ENOMEM;
-
-       priv->irq_info = kcalloc(nvec, sizeof(*priv->irq_info), GFP_KERNEL);
-       if (!priv->irq_info)
-               return -ENOMEM;
-
-       nvec = pci_alloc_irq_vectors(dev->pdev,
-                       MLX5_EQ_VEC_COMP_BASE + 1, nvec,
-                       PCI_IRQ_MSIX);
-       if (nvec < 0) {
-               err = nvec;
-               goto err_free_irq_info;
-       }
-
-       table->num_comp_vectors = nvec - MLX5_EQ_VEC_COMP_BASE;
-
-       return 0;
-
-err_free_irq_info:
-       kfree(priv->irq_info);
-       return err;
-}
-
-static void mlx5_free_irq_vectors(struct mlx5_core_dev *dev)
-{
-       struct mlx5_priv *priv = &dev->priv;
-
-       pci_free_irq_vectors(dev->pdev);
-       kfree(priv->irq_info);
-}
-
 struct mlx5_reg_host_endianness {
        u8      he;
        u8      rsvd[15];
@@ -624,188 +580,24 @@ int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id)
        return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
 }
 
-u64 mlx5_read_internal_timer(struct mlx5_core_dev *dev)
+u64 mlx5_read_internal_timer(struct mlx5_core_dev *dev,
+                            struct ptp_system_timestamp *sts)
 {
        u32 timer_h, timer_h1, timer_l;
 
        timer_h = ioread32be(&dev->iseg->internal_timer_h);
+       ptp_read_system_prets(sts);
        timer_l = ioread32be(&dev->iseg->internal_timer_l);
+       ptp_read_system_postts(sts);
        timer_h1 = ioread32be(&dev->iseg->internal_timer_h);
-       if (timer_h != timer_h1) /* wrap around */
+       if (timer_h != timer_h1) {
+               /* wrap around */
+               ptp_read_system_prets(sts);
                timer_l = ioread32be(&dev->iseg->internal_timer_l);
-
-       return (u64)timer_l | (u64)timer_h1 << 32;
-}
-
-static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i)
-{
-       struct mlx5_priv *priv  = &mdev->priv;
-       int irq = pci_irq_vector(mdev->pdev, MLX5_EQ_VEC_COMP_BASE + i);
-
-       if (!zalloc_cpumask_var(&priv->irq_info[i].mask, GFP_KERNEL)) {
-               mlx5_core_warn(mdev, "zalloc_cpumask_var failed");
-               return -ENOMEM;
+               ptp_read_system_postts(sts);
        }
 
-       cpumask_set_cpu(cpumask_local_spread(i, priv->numa_node),
-                       priv->irq_info[i].mask);
-
-       if (IS_ENABLED(CONFIG_SMP) &&
-           irq_set_affinity_hint(irq, priv->irq_info[i].mask))
-               mlx5_core_warn(mdev, "irq_set_affinity_hint failed, irq 0x%.4x", irq);
-
-       return 0;
-}
-
-static void mlx5_irq_clear_affinity_hint(struct mlx5_core_dev *mdev, int i)
-{
-       struct mlx5_priv *priv  = &mdev->priv;
-       int irq = pci_irq_vector(mdev->pdev, MLX5_EQ_VEC_COMP_BASE + i);
-
-       irq_set_affinity_hint(irq, NULL);
-       free_cpumask_var(priv->irq_info[i].mask);
-}
-
-static int mlx5_irq_set_affinity_hints(struct mlx5_core_dev *mdev)
-{
-       int err;
-       int i;
-
-       for (i = 0; i < mdev->priv.eq_table.num_comp_vectors; i++) {
-               err = mlx5_irq_set_affinity_hint(mdev, i);
-               if (err)
-                       goto err_out;
-       }
-
-       return 0;
-
-err_out:
-       for (i--; i >= 0; i--)
-               mlx5_irq_clear_affinity_hint(mdev, i);
-
-       return err;
-}
-
-static void mlx5_irq_clear_affinity_hints(struct mlx5_core_dev *mdev)
-{
-       int i;
-
-       for (i = 0; i < mdev->priv.eq_table.num_comp_vectors; i++)
-               mlx5_irq_clear_affinity_hint(mdev, i);
-}
-
-int mlx5_vector2eqn(struct mlx5_core_dev *dev, int vector, int *eqn,
-                   unsigned int *irqn)
-{
-       struct mlx5_eq_table *table = &dev->priv.eq_table;
-       struct mlx5_eq *eq, *n;
-       int err = -ENOENT;
-
-       spin_lock(&table->lock);
-       list_for_each_entry_safe(eq, n, &table->comp_eqs_list, list) {
-               if (eq->index == vector) {
-                       *eqn = eq->eqn;
-                       *irqn = eq->irqn;
-                       err = 0;
-                       break;
-               }
-       }
-       spin_unlock(&table->lock);
-
-       return err;
-}
-EXPORT_SYMBOL(mlx5_vector2eqn);
-
-struct mlx5_eq *mlx5_eqn2eq(struct mlx5_core_dev *dev, int eqn)
-{
-       struct mlx5_eq_table *table = &dev->priv.eq_table;
-       struct mlx5_eq *eq;
-
-       spin_lock(&table->lock);
-       list_for_each_entry(eq, &table->comp_eqs_list, list)
-               if (eq->eqn == eqn) {
-                       spin_unlock(&table->lock);
-                       return eq;
-               }
-
-       spin_unlock(&table->lock);
-
-       return ERR_PTR(-ENOENT);
-}
-
-static void free_comp_eqs(struct mlx5_core_dev *dev)
-{
-       struct mlx5_eq_table *table = &dev->priv.eq_table;
-       struct mlx5_eq *eq, *n;
-
-#ifdef CONFIG_RFS_ACCEL
-       if (dev->rmap) {
-               free_irq_cpu_rmap(dev->rmap);
-               dev->rmap = NULL;
-       }
-#endif
-       spin_lock(&table->lock);
-       list_for_each_entry_safe(eq, n, &table->comp_eqs_list, list) {
-               list_del(&eq->list);
-               spin_unlock(&table->lock);
-               if (mlx5_destroy_unmap_eq(dev, eq))
-                       mlx5_core_warn(dev, "failed to destroy EQ 0x%x\n",
-                                      eq->eqn);
-               kfree(eq);
-               spin_lock(&table->lock);
-       }
-       spin_unlock(&table->lock);
-}
-
-static int alloc_comp_eqs(struct mlx5_core_dev *dev)
-{
-       struct mlx5_eq_table *table = &dev->priv.eq_table;
-       char name[MLX5_MAX_IRQ_NAME];
-       struct mlx5_eq *eq;
-       int ncomp_vec;
-       int nent;
-       int err;
-       int i;
-
-       INIT_LIST_HEAD(&table->comp_eqs_list);
-       ncomp_vec = table->num_comp_vectors;
-       nent = MLX5_COMP_EQ_SIZE;
-#ifdef CONFIG_RFS_ACCEL
-       dev->rmap = alloc_irq_cpu_rmap(ncomp_vec);
-       if (!dev->rmap)
-               return -ENOMEM;
-#endif
-       for (i = 0; i < ncomp_vec; i++) {
-               eq = kzalloc(sizeof(*eq), GFP_KERNEL);
-               if (!eq) {
-                       err = -ENOMEM;
-                       goto clean;
-               }
-
-#ifdef CONFIG_RFS_ACCEL
-               irq_cpu_rmap_add(dev->rmap, pci_irq_vector(dev->pdev,
-                                MLX5_EQ_VEC_COMP_BASE + i));
-#endif
-               snprintf(name, MLX5_MAX_IRQ_NAME, "mlx5_comp%d", i);
-               err = mlx5_create_map_eq(dev, eq,
-                                        i + MLX5_EQ_VEC_COMP_BASE, nent, 0,
-                                        name, MLX5_EQ_TYPE_COMP);
-               if (err) {
-                       kfree(eq);
-                       goto clean;
-               }
-               mlx5_core_dbg(dev, "allocated completion EQN %d\n", eq->eqn);
-               eq->index = i;
-               spin_lock(&table->lock);
-               list_add_tail(&eq->list, &table->comp_eqs_list);
-               spin_unlock(&table->lock);
-       }
-
-       return 0;
-
-clean:
-       free_comp_eqs(dev);
-       return err;
+       return (u64)timer_l | (u64)timer_h1 << 32;
 }
 
 static int mlx5_core_set_issi(struct mlx5_core_dev *dev)
@@ -938,28 +730,37 @@ static int mlx5_init_once(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
        struct pci_dev *pdev = dev->pdev;
        int err;
 
+       priv->devcom = mlx5_devcom_register_device(dev);
+       if (IS_ERR(priv->devcom))
+               dev_err(&pdev->dev, "failed to register with devcom (0x%p)\n",
+                       priv->devcom);
+
        err = mlx5_query_board_id(dev);
        if (err) {
                dev_err(&pdev->dev, "query board id failed\n");
-               goto out;
+               goto err_devcom;
        }
 
-       err = mlx5_eq_init(dev);
+       err = mlx5_eq_table_init(dev);
        if (err) {
                dev_err(&pdev->dev, "failed to initialize eq\n");
-               goto out;
+               goto err_devcom;
+       }
+
+       err = mlx5_events_init(dev);
+       if (err) {
+               dev_err(&pdev->dev, "failed to initialize events\n");
+               goto err_eq_cleanup;
        }
 
        err = mlx5_cq_debugfs_init(dev);
        if (err) {
                dev_err(&pdev->dev, "failed to initialize cq debugfs\n");
-               goto err_eq_cleanup;
+               goto err_events_cleanup;
        }
 
        mlx5_init_qp_table(dev);
 
-       mlx5_init_srq_table(dev);
-
        mlx5_init_mkey_table(dev);
 
        mlx5_init_reserved_gids(dev);
@@ -1013,14 +814,15 @@ err_rl_cleanup:
 err_tables_cleanup:
        mlx5_vxlan_destroy(dev->vxlan);
        mlx5_cleanup_mkey_table(dev);
-       mlx5_cleanup_srq_table(dev);
        mlx5_cleanup_qp_table(dev);
        mlx5_cq_debugfs_cleanup(dev);
-
+err_events_cleanup:
+       mlx5_events_cleanup(dev);
 err_eq_cleanup:
-       mlx5_eq_cleanup(dev);
+       mlx5_eq_table_cleanup(dev);
+err_devcom:
+       mlx5_devcom_unregister_device(dev->priv.devcom);
 
-out:
        return err;
 }
 
@@ -1036,10 +838,11 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev)
        mlx5_cleanup_clock(dev);
        mlx5_cleanup_reserved_gids(dev);
        mlx5_cleanup_mkey_table(dev);
-       mlx5_cleanup_srq_table(dev);
        mlx5_cleanup_qp_table(dev);
        mlx5_cq_debugfs_cleanup(dev);
-       mlx5_eq_cleanup(dev);
+       mlx5_events_cleanup(dev);
+       mlx5_eq_table_cleanup(dev);
+       mlx5_devcom_unregister_device(dev->priv.devcom);
 }
 
 static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
@@ -1131,16 +934,10 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
                goto reclaim_boot_pages;
        }
 
-       err = mlx5_pagealloc_start(dev);
-       if (err) {
-               dev_err(&pdev->dev, "mlx5_pagealloc_start failed\n");
-               goto reclaim_boot_pages;
-       }
-
        err = mlx5_cmd_init_hca(dev, sw_owner_id);
        if (err) {
                dev_err(&pdev->dev, "init hca failed\n");
-               goto err_pagealloc_stop;
+               goto reclaim_boot_pages;
        }
 
        mlx5_set_driver_version(dev);
@@ -1161,23 +958,20 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
                }
        }
 
-       err = mlx5_alloc_irq_vectors(dev);
-       if (err) {
-               dev_err(&pdev->dev, "alloc irq vectors failed\n");
-               goto err_cleanup_once;
-       }
-
        dev->priv.uar = mlx5_get_uars_page(dev);
        if (IS_ERR(dev->priv.uar)) {
                dev_err(&pdev->dev, "Failed allocating uar, aborting\n");
                err = PTR_ERR(dev->priv.uar);
-               goto err_disable_msix;
+               goto err_get_uars;
        }
 
-       err = mlx5_start_eqs(dev);
+       mlx5_events_start(dev);
+       mlx5_pagealloc_start(dev);
+
+       err = mlx5_eq_table_create(dev);
        if (err) {
-               dev_err(&pdev->dev, "Failed to start pages and async EQs\n");
-               goto err_put_uars;
+               dev_err(&pdev->dev, "Failed to create EQs\n");
+               goto err_eq_table;
        }
 
        err = mlx5_fw_tracer_init(dev->tracer);
@@ -1186,18 +980,6 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
                goto err_fw_tracer;
        }
 
-       err = alloc_comp_eqs(dev);
-       if (err) {
-               dev_err(&pdev->dev, "Failed to alloc completion EQs\n");
-               goto err_comp_eqs;
-       }
-
-       err = mlx5_irq_set_affinity_hints(dev);
-       if (err) {
-               dev_err(&pdev->dev, "Failed to alloc affinity hint cpumask\n");
-               goto err_affinity_hints;
-       }
-
        err = mlx5_fpga_device_start(dev);
        if (err) {
                dev_err(&pdev->dev, "fpga device start failed %d\n", err);
@@ -1266,24 +1048,17 @@ err_ipsec_start:
        mlx5_fpga_device_stop(dev);
 
 err_fpga_start:
-       mlx5_irq_clear_affinity_hints(dev);
-
-err_affinity_hints:
-       free_comp_eqs(dev);
-
-err_comp_eqs:
        mlx5_fw_tracer_cleanup(dev->tracer);
 
 err_fw_tracer:
-       mlx5_stop_eqs(dev);
+       mlx5_eq_table_destroy(dev);
 
-err_put_uars:
+err_eq_table:
+       mlx5_pagealloc_stop(dev);
+       mlx5_events_stop(dev);
        mlx5_put_uars_page(dev, priv->uar);
 
-err_disable_msix:
-       mlx5_free_irq_vectors(dev);
-
-err_cleanup_once:
+err_get_uars:
        if (boot)
                mlx5_cleanup_once(dev);
 
@@ -1294,9 +1069,6 @@ err_stop_poll:
                goto out_err;
        }
 
-err_pagealloc_stop:
-       mlx5_pagealloc_stop(dev);
-
 reclaim_boot_pages:
        mlx5_reclaim_startup_pages(dev);
 
@@ -1340,21 +1112,20 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
        mlx5_accel_ipsec_cleanup(dev);
        mlx5_accel_tls_cleanup(dev);
        mlx5_fpga_device_stop(dev);
-       mlx5_irq_clear_affinity_hints(dev);
-       free_comp_eqs(dev);
        mlx5_fw_tracer_cleanup(dev->tracer);
-       mlx5_stop_eqs(dev);
+       mlx5_eq_table_destroy(dev);
+       mlx5_pagealloc_stop(dev);
+       mlx5_events_stop(dev);
        mlx5_put_uars_page(dev, priv->uar);
-       mlx5_free_irq_vectors(dev);
        if (cleanup)
                mlx5_cleanup_once(dev);
        mlx5_stop_health_poll(dev, cleanup);
+
        err = mlx5_cmd_teardown_hca(dev);
        if (err) {
                dev_err(&dev->pdev->dev, "tear_down_hca failed, skip cleanup\n");
                goto out;
        }
-       mlx5_pagealloc_stop(dev);
        mlx5_reclaim_startup_pages(dev);
        mlx5_core_disable_hca(dev, 0);
        mlx5_cmd_cleanup(dev);
@@ -1364,12 +1135,6 @@ out:
        return err;
 }
 
-struct mlx5_core_event_handler {
-       void (*event)(struct mlx5_core_dev *dev,
-                     enum mlx5_dev_event event,
-                     void *data);
-};
-
 static const struct devlink_ops mlx5_devlink_ops = {
 #ifdef CONFIG_MLX5_ESWITCH
        .eswitch_mode_set = mlx5_devlink_eswitch_mode_set,
@@ -1403,7 +1168,6 @@ static int init_one(struct pci_dev *pdev,
        pci_set_drvdata(pdev, dev);
 
        dev->pdev = pdev;
-       dev->event = mlx5_core_event;
        dev->profile = &profile[prof_sel];
 
        INIT_LIST_HEAD(&priv->ctx_list);
@@ -1411,17 +1175,6 @@ static int init_one(struct pci_dev *pdev,
        mutex_init(&dev->pci_status_mutex);
        mutex_init(&dev->intf_state_mutex);
 
-       INIT_LIST_HEAD(&priv->waiting_events_list);
-       priv->is_accum_events = false;
-
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       err = init_srcu_struct(&priv->pfault_srcu);
-       if (err) {
-               dev_err(&pdev->dev, "init_srcu_struct failed with error code %d\n",
-                       err);
-               goto clean_dev;
-       }
-#endif
        mutex_init(&priv->bfregs.reg_head.lock);
        mutex_init(&priv->bfregs.wc_head.lock);
        INIT_LIST_HEAD(&priv->bfregs.reg_head.list);
@@ -1430,7 +1183,7 @@ static int init_one(struct pci_dev *pdev,
        err = mlx5_pci_init(dev, priv);
        if (err) {
                dev_err(&pdev->dev, "mlx5_pci_init failed with error code %d\n", err);
-               goto clean_srcu;
+               goto clean_dev;
        }
 
        err = mlx5_health_init(dev);
@@ -1439,12 +1192,14 @@ static int init_one(struct pci_dev *pdev,
                goto close_pci;
        }
 
-       mlx5_pagealloc_init(dev);
+       err = mlx5_pagealloc_init(dev);
+       if (err)
+               goto err_pagealloc_init;
 
        err = mlx5_load_one(dev, priv, true);
        if (err) {
                dev_err(&pdev->dev, "mlx5_load_one failed with error code %d\n", err);
-               goto clean_health;
+               goto err_load_one;
        }
 
        request_module_nowait(MLX5_IB_MOD);
@@ -1458,16 +1213,13 @@ static int init_one(struct pci_dev *pdev,
 
 clean_load:
        mlx5_unload_one(dev, priv, true);
-clean_health:
+err_load_one:
        mlx5_pagealloc_cleanup(dev);
+err_pagealloc_init:
        mlx5_health_cleanup(dev);
 close_pci:
        mlx5_pci_close(dev, priv);
-clean_srcu:
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       cleanup_srcu_struct(&priv->pfault_srcu);
 clean_dev:
-#endif
        devlink_free(devlink);
 
        return err;
@@ -1491,9 +1243,6 @@ static void remove_one(struct pci_dev *pdev)
        mlx5_pagealloc_cleanup(dev);
        mlx5_health_cleanup(dev);
        mlx5_pci_close(dev, priv);
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       cleanup_srcu_struct(&priv->pfault_srcu);
-#endif
        devlink_free(devlink);
 }
 
@@ -1637,7 +1386,6 @@ succeed:
         * kexec. There is no need to cleanup the mlx5_core software
         * contexts.
         */
-       mlx5_irq_clear_affinity_hints(dev);
        mlx5_core_eq_free_irqs(dev);
 
        return 0;
index 0594d09..c68dcea 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/sched.h>
 #include <linux/if_link.h>
 #include <linux/firmware.h>
+#include <linux/ptp_clock_kernel.h>
 #include <linux/mlx5/cq.h>
 #include <linux/mlx5/fs.h>
 
@@ -78,6 +79,11 @@ do {                                                                 \
                 __func__, __LINE__, current->pid,                      \
                ##__VA_ARGS__)
 
+#define mlx5_core_warn_once(__dev, format, ...)                                \
+       dev_warn_once(&(__dev)->pdev->dev, "%s:%d:(pid %d): " format,   \
+                     __func__, __LINE__, current->pid,                 \
+                     ##__VA_ARGS__)
+
 #define mlx5_core_info(__dev, format, ...)                             \
        dev_info(&(__dev)->pdev->dev, format, ##__VA_ARGS__)
 
@@ -97,12 +103,6 @@ int mlx5_cmd_init_hca(struct mlx5_core_dev *dev, uint32_t *sw_owner_id);
 int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev);
 int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev);
 int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev);
-
-void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
-                    unsigned long param);
-void mlx5_core_page_fault(struct mlx5_core_dev *dev,
-                         struct mlx5_pagefault *pfault);
-void mlx5_port_module_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe);
 void mlx5_enter_error_state(struct mlx5_core_dev *dev, bool force);
 void mlx5_disable_device(struct mlx5_core_dev *dev);
 void mlx5_recover_device(struct mlx5_core_dev *dev);
@@ -122,30 +122,10 @@ int mlx5_modify_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
 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);
-u64 mlx5_read_internal_timer(struct mlx5_core_dev *dev);
-
-int mlx5_eq_init(struct mlx5_core_dev *dev);
-void mlx5_eq_cleanup(struct mlx5_core_dev *dev);
-int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
-                      int nent, u64 mask, const char *name,
-                      enum mlx5_eq_type type);
-int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
-int mlx5_eq_add_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq);
-int mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq);
-int mlx5_core_eq_query(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
-                      u32 *out, int outlen);
-int mlx5_start_eqs(struct mlx5_core_dev *dev);
-void mlx5_stop_eqs(struct mlx5_core_dev *dev);
-/* This function should only be called after mlx5_cmd_force_teardown_hca */
-void mlx5_core_eq_free_irqs(struct mlx5_core_dev *dev);
-struct mlx5_eq *mlx5_eqn2eq(struct mlx5_core_dev *dev, int eqn);
-u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq *eq);
-void mlx5_cq_tasklet_cb(unsigned long data);
-void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced);
-int mlx5_debug_eq_add(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
-void mlx5_debug_eq_remove(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
-int mlx5_eq_debugfs_init(struct mlx5_core_dev *dev);
-void mlx5_eq_debugfs_cleanup(struct mlx5_core_dev *dev);
+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);
 int mlx5_cq_debugfs_init(struct mlx5_core_dev *dev);
 void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev);
 
@@ -159,6 +139,11 @@ int mlx5_query_qcam_reg(struct mlx5_core_dev *mdev, u32 *qcam,
 void mlx5_lag_add(struct mlx5_core_dev *dev, struct net_device *netdev);
 void mlx5_lag_remove(struct mlx5_core_dev *dev);
 
+int mlx5_events_init(struct mlx5_core_dev *dev);
+void mlx5_events_cleanup(struct mlx5_core_dev *dev);
+void mlx5_events_start(struct mlx5_core_dev *dev);
+void mlx5_events_stop(struct mlx5_core_dev *dev);
+
 void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv);
 void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv);
 void mlx5_attach_device(struct mlx5_core_dev *dev);
@@ -202,10 +187,8 @@ static inline int mlx5_lag_is_lacp_owner(struct mlx5_core_dev *dev)
                    MLX5_CAP_GEN(dev, lag_master);
 }
 
-int mlx5_lag_allow(struct mlx5_core_dev *dev);
-int mlx5_lag_forbid(struct mlx5_core_dev *dev);
-
 void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol);
+void mlx5_lag_update(struct mlx5_core_dev *dev);
 
 enum {
        MLX5_NIC_IFC_FULL               = 0,
index e36d3e3..a83b517 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
+#include "lib/eq.h"
 
 enum {
        MLX5_PAGES_CANT_GIVE    = 0,
@@ -433,15 +434,28 @@ static void pages_work_handler(struct work_struct *work)
        kfree(req);
 }
 
-void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id,
-                                s32 npages)
+static int req_pages_handler(struct notifier_block *nb,
+                            unsigned long type, void *data)
 {
        struct mlx5_pages_req *req;
-
+       struct mlx5_core_dev *dev;
+       struct mlx5_priv *priv;
+       struct mlx5_eqe *eqe;
+       u16 func_id;
+       s32 npages;
+
+       priv = mlx5_nb_cof(nb, struct mlx5_priv, pg_nb);
+       dev  = container_of(priv, struct mlx5_core_dev, priv);
+       eqe  = data;
+
+       func_id = be16_to_cpu(eqe->data.req_pages.func_id);
+       npages  = be32_to_cpu(eqe->data.req_pages.num_pages);
+       mlx5_core_dbg(dev, "page request for func 0x%x, npages %d\n",
+                     func_id, npages);
        req = kzalloc(sizeof(*req), GFP_ATOMIC);
        if (!req) {
                mlx5_core_warn(dev, "failed to allocate pages request\n");
-               return;
+               return NOTIFY_DONE;
        }
 
        req->dev = dev;
@@ -449,6 +463,7 @@ void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id,
        req->npages = npages;
        INIT_WORK(&req->work, pages_work_handler);
        queue_work(dev->priv.pg_wq, &req->work);
+       return NOTIFY_OK;
 }
 
 int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot)
@@ -524,29 +539,32 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
        return 0;
 }
 
-void mlx5_pagealloc_init(struct mlx5_core_dev *dev)
+int mlx5_pagealloc_init(struct mlx5_core_dev *dev)
 {
        dev->priv.page_root = RB_ROOT;
        INIT_LIST_HEAD(&dev->priv.free_list);
+       dev->priv.pg_wq = create_singlethread_workqueue("mlx5_page_allocator");
+       if (!dev->priv.pg_wq)
+               return -ENOMEM;
+
+       return 0;
 }
 
 void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev)
 {
-       /* nothing */
+       destroy_workqueue(dev->priv.pg_wq);
 }
 
-int mlx5_pagealloc_start(struct mlx5_core_dev *dev)
+void mlx5_pagealloc_start(struct mlx5_core_dev *dev)
 {
-       dev->priv.pg_wq = create_singlethread_workqueue("mlx5_page_allocator");
-       if (!dev->priv.pg_wq)
-               return -ENOMEM;
-
-       return 0;
+       MLX5_NB_INIT(&dev->priv.pg_nb, req_pages_handler, PAGE_REQUEST);
+       mlx5_eq_notifier_register(dev, &dev->priv.pg_nb);
 }
 
 void mlx5_pagealloc_stop(struct mlx5_core_dev *dev)
 {
-       destroy_workqueue(dev->priv.pg_wq);
+       mlx5_eq_notifier_unregister(dev, &dev->priv.pg_nb);
+       flush_workqueue(dev->priv.pg_wq);
 }
 
 int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev)
index 31a9cbd..2b82f35 100644 (file)
@@ -915,63 +915,6 @@ void mlx5_query_port_fcs(struct mlx5_core_dev *mdev, bool *supported,
        *enabled = !!(MLX5_GET(pcmr_reg, out, fcs_chk));
 }
 
-static const char *mlx5_pme_status[MLX5_MODULE_STATUS_NUM] = {
-       "Cable plugged",   /* MLX5_MODULE_STATUS_PLUGGED    = 0x1 */
-       "Cable unplugged", /* MLX5_MODULE_STATUS_UNPLUGGED  = 0x2 */
-       "Cable error",     /* MLX5_MODULE_STATUS_ERROR      = 0x3 */
-};
-
-static const char *mlx5_pme_error[MLX5_MODULE_EVENT_ERROR_NUM] = {
-       "Power budget exceeded",
-       "Long Range for non MLNX cable",
-       "Bus stuck(I2C or data shorted)",
-       "No EEPROM/retry timeout",
-       "Enforce part number list",
-       "Unknown identifier",
-       "High Temperature",
-       "Bad or shorted cable/module",
-       "Unknown status",
-};
-
-void mlx5_port_module_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe)
-{
-       enum port_module_event_status_type module_status;
-       enum port_module_event_error_type error_type;
-       struct mlx5_eqe_port_module *module_event_eqe;
-       struct mlx5_priv *priv = &dev->priv;
-       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 &
-                    PORT_MODULE_EVENT_ERROR_TYPE_MASK;
-
-       if (module_status < MLX5_MODULE_STATUS_ERROR) {
-               priv->pme_stats.status_counters[module_status - 1]++;
-       } else if (module_status == MLX5_MODULE_STATUS_ERROR) {
-               if (error_type >= MLX5_MODULE_EVENT_ERROR_UNKNOWN)
-                       /* Unknown error type */
-                       error_type = MLX5_MODULE_EVENT_ERROR_UNKNOWN;
-               priv->pme_stats.error_counters[error_type]++;
-       }
-
-       if (!printk_ratelimit())
-               return;
-
-       if (module_status < MLX5_MODULE_STATUS_ERROR)
-               mlx5_core_info(dev,
-                              "Port module event: module %u, %s\n",
-                              module_num, mlx5_pme_status[module_status - 1]);
-
-       else if (module_status == MLX5_MODULE_STATUS_ERROR)
-               mlx5_core_info(dev,
-                              "Port module event[error]: module %u, %s, %s\n",
-                              module_num, mlx5_pme_status[module_status - 1],
-                              mlx5_pme_error[error_type]);
-}
-
 int mlx5_query_mtpps(struct mlx5_core_dev *mdev, u32 *mtpps, u32 mtpps_size)
 {
        u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0};
index 91b8139..388f205 100644 (file)
 #include <linux/mlx5/transobj.h>
 
 #include "mlx5_core.h"
+#include "lib/eq.h"
 
-static struct mlx5_core_rsc_common *mlx5_get_rsc(struct mlx5_core_dev *dev,
-                                                u32 rsn)
+static struct mlx5_core_rsc_common *
+mlx5_get_rsc(struct mlx5_qp_table *table, u32 rsn)
 {
-       struct mlx5_qp_table *table = &dev->priv.qp_table;
        struct mlx5_core_rsc_common *common;
 
        spin_lock(&table->lock);
@@ -53,11 +53,6 @@ static struct mlx5_core_rsc_common *mlx5_get_rsc(struct mlx5_core_dev *dev,
 
        spin_unlock(&table->lock);
 
-       if (!common) {
-               mlx5_core_warn(dev, "Async event for bogus resource 0x%x\n",
-                              rsn);
-               return NULL;
-       }
        return common;
 }
 
@@ -120,19 +115,57 @@ static bool is_event_type_allowed(int rsc_type, int event_type)
        }
 }
 
-void mlx5_rsc_event(struct mlx5_core_dev *dev, u32 rsn, int event_type)
+static int rsc_event_notifier(struct notifier_block *nb,
+                             unsigned long type, void *data)
 {
-       struct mlx5_core_rsc_common *common = mlx5_get_rsc(dev, rsn);
+       struct mlx5_core_rsc_common *common;
+       struct mlx5_qp_table *table;
+       struct mlx5_core_dev *dev;
        struct mlx5_core_dct *dct;
+       u8 event_type = (u8)type;
        struct mlx5_core_qp *qp;
+       struct mlx5_priv *priv;
+       struct mlx5_eqe *eqe;
+       u32 rsn;
+
+       switch (event_type) {
+       case MLX5_EVENT_TYPE_DCT_DRAINED:
+               eqe = data;
+               rsn = be32_to_cpu(eqe->data.dct.dctn) & 0xffffff;
+               rsn |= (MLX5_RES_DCT << MLX5_USER_INDEX_LEN);
+               break;
+       case MLX5_EVENT_TYPE_PATH_MIG:
+       case MLX5_EVENT_TYPE_COMM_EST:
+       case MLX5_EVENT_TYPE_SQ_DRAINED:
+       case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
+       case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
+       case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
+       case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
+       case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
+               eqe = data;
+               rsn = be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff;
+               rsn |= (eqe->data.qp_srq.type << MLX5_USER_INDEX_LEN);
+               break;
+       default:
+               return NOTIFY_DONE;
+       }
+
+       table = container_of(nb, struct mlx5_qp_table, nb);
+       priv  = container_of(table, struct mlx5_priv, qp_table);
+       dev   = container_of(priv, struct mlx5_core_dev, priv);
+
+       mlx5_core_dbg(dev, "event (%d) arrived on resource 0x%x\n", eqe->type, rsn);
 
-       if (!common)
-               return;
+       common = mlx5_get_rsc(table, rsn);
+       if (!common) {
+               mlx5_core_warn(dev, "Async event for bogus resource 0x%x\n", rsn);
+               return NOTIFY_OK;
+       }
 
        if (!is_event_type_allowed((rsn >> MLX5_USER_INDEX_LEN), event_type)) {
                mlx5_core_warn(dev, "event 0x%.2x is not allowed on resource 0x%.8x\n",
                               event_type, rsn);
-               return;
+               goto out;
        }
 
        switch (common->res) {
@@ -150,8 +183,10 @@ void mlx5_rsc_event(struct mlx5_core_dev *dev, u32 rsn, int event_type)
        default:
                mlx5_core_warn(dev, "invalid resource type for 0x%x\n", rsn);
        }
-
+out:
        mlx5_core_put_rsc(common);
+
+       return NOTIFY_OK;
 }
 
 static int create_resource_common(struct mlx5_core_dev *dev,
@@ -487,10 +522,16 @@ void mlx5_init_qp_table(struct mlx5_core_dev *dev)
        spin_lock_init(&table->lock);
        INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
        mlx5_qp_debugfs_init(dev);
+
+       table->nb.notifier_call = rsc_event_notifier;
+       mlx5_notifier_register(dev, &table->nb);
 }
 
 void mlx5_cleanup_qp_table(struct mlx5_core_dev *dev)
 {
+       struct mlx5_qp_table *table = &dev->priv.qp_table;
+
+       mlx5_notifier_unregister(dev, &table->nb);
        mlx5_qp_debugfs_cleanup(dev);
 }
 
@@ -670,3 +711,20 @@ int mlx5_core_query_q_counter(struct mlx5_core_dev *dev, u16 counter_id,
        return mlx5_cmd_exec(dev, in, sizeof(in), out, out_size);
 }
 EXPORT_SYMBOL_GPL(mlx5_core_query_q_counter);
+
+struct mlx5_core_rsc_common *mlx5_core_res_hold(struct mlx5_core_dev *dev,
+                                               int res_num,
+                                               enum mlx5_res_type res_type)
+{
+       u32 rsn = res_num | (res_type << MLX5_USER_INDEX_LEN);
+       struct mlx5_qp_table *table = &dev->priv.qp_table;
+
+       return mlx5_get_rsc(table, rsn);
+}
+EXPORT_SYMBOL_GPL(mlx5_core_res_hold);
+
+void mlx5_core_res_put(struct mlx5_core_rsc_common *res)
+{
+       mlx5_core_put_rsc(res);
+}
+EXPORT_SYMBOL_GPL(mlx5_core_res_put);
index a067496..6e17803 100644 (file)
@@ -216,20 +216,10 @@ int mlx5_core_sriov_configure(struct pci_dev *pdev, int num_vfs)
        if (!mlx5_core_is_pf(dev))
                return -EPERM;
 
-       if (num_vfs) {
-               int ret;
-
-               ret = mlx5_lag_forbid(dev);
-               if (ret && (ret != -ENODEV))
-                       return ret;
-       }
-
-       if (num_vfs) {
+       if (num_vfs)
                err = mlx5_sriov_enable(pdev, num_vfs);
-       } else {
+       else
                mlx5_sriov_disable(pdev);
-               mlx5_lag_allow(dev);
-       }
 
        return err ? err : num_vfs;
 }
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/srq.c b/drivers/net/ethernet/mellanox/mlx5/core/srq.c
deleted file mode 100644 (file)
index 6a6fc9b..0000000
+++ /dev/null
@@ -1,716 +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 <linux/mlx5/srq.h>
-#include <rdma/ib_verbs.h>
-#include "mlx5_core.h"
-#include <linux/mlx5/transobj.h>
-
-void mlx5_srq_event(struct mlx5_core_dev *dev, u32 srqn, int event_type)
-{
-       struct mlx5_srq_table *table = &dev->priv.srq_table;
-       struct mlx5_core_srq *srq;
-
-       spin_lock(&table->lock);
-
-       srq = radix_tree_lookup(&table->tree, srqn);
-       if (srq)
-               atomic_inc(&srq->refcount);
-
-       spin_unlock(&table->lock);
-
-       if (!srq) {
-               mlx5_core_warn(dev, "Async event for bogus SRQ 0x%08x\n", srqn);
-               return;
-       }
-
-       srq->event(srq, event_type);
-
-       if (atomic_dec_and_test(&srq->refcount))
-               complete(&srq->free);
-}
-
-static int get_pas_size(struct mlx5_srq_attr *in)
-{
-       u32 log_page_size = in->log_page_size + 12;
-       u32 log_srq_size  = in->log_size;
-       u32 log_rq_stride = in->wqe_shift;
-       u32 page_offset   = in->page_offset;
-       u32 po_quanta     = 1 << (log_page_size - 6);
-       u32 rq_sz         = 1 << (log_srq_size + 4 + log_rq_stride);
-       u32 page_size     = 1 << log_page_size;
-       u32 rq_sz_po      = rq_sz + (page_offset * po_quanta);
-       u32 rq_num_pas    = DIV_ROUND_UP(rq_sz_po, page_size);
-
-       return rq_num_pas * sizeof(u64);
-}
-
-static void set_wq(void *wq, struct mlx5_srq_attr *in)
-{
-       MLX5_SET(wq,   wq, wq_signature,  !!(in->flags
-                & MLX5_SRQ_FLAG_WQ_SIG));
-       MLX5_SET(wq,   wq, log_wq_pg_sz,  in->log_page_size);
-       MLX5_SET(wq,   wq, log_wq_stride, in->wqe_shift + 4);
-       MLX5_SET(wq,   wq, log_wq_sz,     in->log_size);
-       MLX5_SET(wq,   wq, page_offset,   in->page_offset);
-       MLX5_SET(wq,   wq, lwm,           in->lwm);
-       MLX5_SET(wq,   wq, pd,            in->pd);
-       MLX5_SET64(wq, wq, dbr_addr,      in->db_record);
-}
-
-static void set_srqc(void *srqc, struct mlx5_srq_attr *in)
-{
-       MLX5_SET(srqc,   srqc, wq_signature,  !!(in->flags
-                & MLX5_SRQ_FLAG_WQ_SIG));
-       MLX5_SET(srqc,   srqc, log_page_size, in->log_page_size);
-       MLX5_SET(srqc,   srqc, log_rq_stride, in->wqe_shift);
-       MLX5_SET(srqc,   srqc, log_srq_size,  in->log_size);
-       MLX5_SET(srqc,   srqc, page_offset,   in->page_offset);
-       MLX5_SET(srqc,   srqc, lwm,           in->lwm);
-       MLX5_SET(srqc,   srqc, pd,            in->pd);
-       MLX5_SET64(srqc, srqc, dbr_addr,      in->db_record);
-       MLX5_SET(srqc,   srqc, xrcd,          in->xrcd);
-       MLX5_SET(srqc,   srqc, cqn,           in->cqn);
-}
-
-static void get_wq(void *wq, struct mlx5_srq_attr *in)
-{
-       if (MLX5_GET(wq, wq, wq_signature))
-               in->flags &= MLX5_SRQ_FLAG_WQ_SIG;
-       in->log_page_size = MLX5_GET(wq,   wq, log_wq_pg_sz);
-       in->wqe_shift     = MLX5_GET(wq,   wq, log_wq_stride) - 4;
-       in->log_size      = MLX5_GET(wq,   wq, log_wq_sz);
-       in->page_offset   = MLX5_GET(wq,   wq, page_offset);
-       in->lwm           = MLX5_GET(wq,   wq, lwm);
-       in->pd            = MLX5_GET(wq,   wq, pd);
-       in->db_record     = MLX5_GET64(wq, wq, dbr_addr);
-}
-
-static void get_srqc(void *srqc, struct mlx5_srq_attr *in)
-{
-       if (MLX5_GET(srqc, srqc, wq_signature))
-               in->flags &= MLX5_SRQ_FLAG_WQ_SIG;
-       in->log_page_size = MLX5_GET(srqc,   srqc, log_page_size);
-       in->wqe_shift     = MLX5_GET(srqc,   srqc, log_rq_stride);
-       in->log_size      = MLX5_GET(srqc,   srqc, log_srq_size);
-       in->page_offset   = MLX5_GET(srqc,   srqc, page_offset);
-       in->lwm           = MLX5_GET(srqc,   srqc, lwm);
-       in->pd            = MLX5_GET(srqc,   srqc, pd);
-       in->db_record     = MLX5_GET64(srqc, srqc, dbr_addr);
-}
-
-struct mlx5_core_srq *mlx5_core_get_srq(struct mlx5_core_dev *dev, u32 srqn)
-{
-       struct mlx5_srq_table *table = &dev->priv.srq_table;
-       struct mlx5_core_srq *srq;
-
-       spin_lock(&table->lock);
-
-       srq = radix_tree_lookup(&table->tree, srqn);
-       if (srq)
-               atomic_inc(&srq->refcount);
-
-       spin_unlock(&table->lock);
-
-       return srq;
-}
-EXPORT_SYMBOL(mlx5_core_get_srq);
-
-static int create_srq_cmd(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                         struct mlx5_srq_attr *in)
-{
-       u32 create_out[MLX5_ST_SZ_DW(create_srq_out)] = {0};
-       void *create_in;
-       void *srqc;
-       void *pas;
-       int pas_size;
-       int inlen;
-       int err;
-
-       pas_size  = get_pas_size(in);
-       inlen     = MLX5_ST_SZ_BYTES(create_srq_in) + pas_size;
-       create_in = kvzalloc(inlen, GFP_KERNEL);
-       if (!create_in)
-               return -ENOMEM;
-
-       MLX5_SET(create_srq_in, create_in, uid, in->uid);
-       srqc = MLX5_ADDR_OF(create_srq_in, create_in, srq_context_entry);
-       pas = MLX5_ADDR_OF(create_srq_in, create_in, pas);
-
-       set_srqc(srqc, in);
-       memcpy(pas, in->pas, pas_size);
-
-       MLX5_SET(create_srq_in, create_in, opcode,
-                MLX5_CMD_OP_CREATE_SRQ);
-
-       err = mlx5_cmd_exec(dev, create_in, inlen, create_out,
-                           sizeof(create_out));
-       kvfree(create_in);
-       if (!err) {
-               srq->srqn = MLX5_GET(create_srq_out, create_out, srqn);
-               srq->uid = in->uid;
-       }
-
-       return err;
-}
-
-static int destroy_srq_cmd(struct mlx5_core_dev *dev,
-                          struct mlx5_core_srq *srq)
-{
-       u32 srq_in[MLX5_ST_SZ_DW(destroy_srq_in)] = {0};
-       u32 srq_out[MLX5_ST_SZ_DW(destroy_srq_out)] = {0};
-
-       MLX5_SET(destroy_srq_in, srq_in, opcode,
-                MLX5_CMD_OP_DESTROY_SRQ);
-       MLX5_SET(destroy_srq_in, srq_in, srqn, srq->srqn);
-       MLX5_SET(destroy_srq_in, srq_in, uid, srq->uid);
-
-       return mlx5_cmd_exec(dev, srq_in, sizeof(srq_in),
-                            srq_out, sizeof(srq_out));
-}
-
-static int arm_srq_cmd(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                      u16 lwm, int is_srq)
-{
-       u32 srq_in[MLX5_ST_SZ_DW(arm_rq_in)] = {0};
-       u32 srq_out[MLX5_ST_SZ_DW(arm_rq_out)] = {0};
-
-       MLX5_SET(arm_rq_in, srq_in, opcode, MLX5_CMD_OP_ARM_RQ);
-       MLX5_SET(arm_rq_in, srq_in, op_mod, MLX5_ARM_RQ_IN_OP_MOD_SRQ);
-       MLX5_SET(arm_rq_in, srq_in, srq_number, srq->srqn);
-       MLX5_SET(arm_rq_in, srq_in, lwm,      lwm);
-       MLX5_SET(arm_rq_in, srq_in, uid, srq->uid);
-
-       return  mlx5_cmd_exec(dev, srq_in, sizeof(srq_in),
-                             srq_out, sizeof(srq_out));
-}
-
-static int query_srq_cmd(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                        struct mlx5_srq_attr *out)
-{
-       u32 srq_in[MLX5_ST_SZ_DW(query_srq_in)] = {0};
-       u32 *srq_out;
-       void *srqc;
-       int err;
-
-       srq_out = kvzalloc(MLX5_ST_SZ_BYTES(query_srq_out), GFP_KERNEL);
-       if (!srq_out)
-               return -ENOMEM;
-
-       MLX5_SET(query_srq_in, srq_in, opcode,
-                MLX5_CMD_OP_QUERY_SRQ);
-       MLX5_SET(query_srq_in, srq_in, srqn, srq->srqn);
-       err =  mlx5_cmd_exec(dev, srq_in, sizeof(srq_in),
-                            srq_out, MLX5_ST_SZ_BYTES(query_srq_out));
-       if (err)
-               goto out;
-
-       srqc = MLX5_ADDR_OF(query_srq_out, srq_out, srq_context_entry);
-       get_srqc(srqc, out);
-       if (MLX5_GET(srqc, srqc, state) != MLX5_SRQC_STATE_GOOD)
-               out->flags |= MLX5_SRQ_FLAG_ERR;
-out:
-       kvfree(srq_out);
-       return err;
-}
-
-static int create_xrc_srq_cmd(struct mlx5_core_dev *dev,
-                             struct mlx5_core_srq *srq,
-                             struct mlx5_srq_attr *in)
-{
-       u32 create_out[MLX5_ST_SZ_DW(create_xrc_srq_out)];
-       void *create_in;
-       void *xrc_srqc;
-       void *pas;
-       int pas_size;
-       int inlen;
-       int err;
-
-       pas_size  = get_pas_size(in);
-       inlen     = MLX5_ST_SZ_BYTES(create_xrc_srq_in) + pas_size;
-       create_in = kvzalloc(inlen, GFP_KERNEL);
-       if (!create_in)
-               return -ENOMEM;
-
-       MLX5_SET(create_xrc_srq_in, create_in, uid, in->uid);
-       xrc_srqc = MLX5_ADDR_OF(create_xrc_srq_in, create_in,
-                               xrc_srq_context_entry);
-       pas      = MLX5_ADDR_OF(create_xrc_srq_in, create_in, pas);
-
-       set_srqc(xrc_srqc, in);
-       MLX5_SET(xrc_srqc, xrc_srqc, user_index, in->user_index);
-       memcpy(pas, in->pas, pas_size);
-       MLX5_SET(create_xrc_srq_in, create_in, opcode,
-                MLX5_CMD_OP_CREATE_XRC_SRQ);
-
-       memset(create_out, 0, sizeof(create_out));
-       err = mlx5_cmd_exec(dev, create_in, inlen, create_out,
-                           sizeof(create_out));
-       if (err)
-               goto out;
-
-       srq->srqn = MLX5_GET(create_xrc_srq_out, create_out, xrc_srqn);
-       srq->uid = in->uid;
-out:
-       kvfree(create_in);
-       return err;
-}
-
-static int destroy_xrc_srq_cmd(struct mlx5_core_dev *dev,
-                              struct mlx5_core_srq *srq)
-{
-       u32 xrcsrq_in[MLX5_ST_SZ_DW(destroy_xrc_srq_in)]   = {0};
-       u32 xrcsrq_out[MLX5_ST_SZ_DW(destroy_xrc_srq_out)] = {0};
-
-       MLX5_SET(destroy_xrc_srq_in, xrcsrq_in, opcode,
-                MLX5_CMD_OP_DESTROY_XRC_SRQ);
-       MLX5_SET(destroy_xrc_srq_in, xrcsrq_in, xrc_srqn, srq->srqn);
-       MLX5_SET(destroy_xrc_srq_in, xrcsrq_in, uid, srq->uid);
-
-       return mlx5_cmd_exec(dev, xrcsrq_in, sizeof(xrcsrq_in),
-                            xrcsrq_out, sizeof(xrcsrq_out));
-}
-
-static int arm_xrc_srq_cmd(struct mlx5_core_dev *dev,
-                          struct mlx5_core_srq *srq, u16 lwm)
-{
-       u32 xrcsrq_in[MLX5_ST_SZ_DW(arm_xrc_srq_in)]   = {0};
-       u32 xrcsrq_out[MLX5_ST_SZ_DW(arm_xrc_srq_out)] = {0};
-
-       MLX5_SET(arm_xrc_srq_in, xrcsrq_in, opcode,   MLX5_CMD_OP_ARM_XRC_SRQ);
-       MLX5_SET(arm_xrc_srq_in, xrcsrq_in, op_mod,   MLX5_ARM_XRC_SRQ_IN_OP_MOD_XRC_SRQ);
-       MLX5_SET(arm_xrc_srq_in, xrcsrq_in, xrc_srqn, srq->srqn);
-       MLX5_SET(arm_xrc_srq_in, xrcsrq_in, lwm,      lwm);
-       MLX5_SET(arm_xrc_srq_in, xrcsrq_in, uid, srq->uid);
-
-       return  mlx5_cmd_exec(dev, xrcsrq_in, sizeof(xrcsrq_in),
-                             xrcsrq_out, sizeof(xrcsrq_out));
-}
-
-static int query_xrc_srq_cmd(struct mlx5_core_dev *dev,
-                            struct mlx5_core_srq *srq,
-                            struct mlx5_srq_attr *out)
-{
-       u32 xrcsrq_in[MLX5_ST_SZ_DW(query_xrc_srq_in)];
-       u32 *xrcsrq_out;
-       void *xrc_srqc;
-       int err;
-
-       xrcsrq_out = kvzalloc(MLX5_ST_SZ_BYTES(query_xrc_srq_out), GFP_KERNEL);
-       if (!xrcsrq_out)
-               return -ENOMEM;
-       memset(xrcsrq_in, 0, sizeof(xrcsrq_in));
-
-       MLX5_SET(query_xrc_srq_in, xrcsrq_in, opcode,
-                MLX5_CMD_OP_QUERY_XRC_SRQ);
-       MLX5_SET(query_xrc_srq_in, xrcsrq_in, xrc_srqn, srq->srqn);
-
-       err =  mlx5_cmd_exec(dev, xrcsrq_in, sizeof(xrcsrq_in), xrcsrq_out,
-                            MLX5_ST_SZ_BYTES(query_xrc_srq_out));
-       if (err)
-               goto out;
-
-       xrc_srqc = MLX5_ADDR_OF(query_xrc_srq_out, xrcsrq_out,
-                               xrc_srq_context_entry);
-       get_srqc(xrc_srqc, out);
-       if (MLX5_GET(xrc_srqc, xrc_srqc, state) != MLX5_XRC_SRQC_STATE_GOOD)
-               out->flags |= MLX5_SRQ_FLAG_ERR;
-
-out:
-       kvfree(xrcsrq_out);
-       return err;
-}
-
-static int create_rmp_cmd(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                         struct mlx5_srq_attr *in)
-{
-       void *create_in;
-       void *rmpc;
-       void *wq;
-       int pas_size;
-       int inlen;
-       int err;
-
-       pas_size = get_pas_size(in);
-       inlen = MLX5_ST_SZ_BYTES(create_rmp_in) + pas_size;
-       create_in = kvzalloc(inlen, GFP_KERNEL);
-       if (!create_in)
-               return -ENOMEM;
-
-       rmpc = MLX5_ADDR_OF(create_rmp_in, create_in, ctx);
-       wq = MLX5_ADDR_OF(rmpc, rmpc, wq);
-
-       MLX5_SET(rmpc, rmpc, state, MLX5_RMPC_STATE_RDY);
-       MLX5_SET(create_rmp_in, create_in, uid, in->uid);
-       set_wq(wq, in);
-       memcpy(MLX5_ADDR_OF(rmpc, rmpc, wq.pas), in->pas, pas_size);
-
-       err = mlx5_core_create_rmp(dev, create_in, inlen, &srq->srqn);
-       if (!err)
-               srq->uid = in->uid;
-
-       kvfree(create_in);
-       return err;
-}
-
-static int destroy_rmp_cmd(struct mlx5_core_dev *dev,
-                          struct mlx5_core_srq *srq)
-{
-       u32 in[MLX5_ST_SZ_DW(destroy_rmp_in)]   = {};
-       u32 out[MLX5_ST_SZ_DW(destroy_rmp_out)] = {};
-
-       MLX5_SET(destroy_rmp_in, in, opcode, MLX5_CMD_OP_DESTROY_RMP);
-       MLX5_SET(destroy_rmp_in, in, rmpn, srq->srqn);
-       MLX5_SET(destroy_rmp_in, in, uid, srq->uid);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-
-static int arm_rmp_cmd(struct mlx5_core_dev *dev,
-                      struct mlx5_core_srq *srq,
-                      u16 lwm)
-{
-       void *in;
-       void *rmpc;
-       void *wq;
-       void *bitmask;
-       int err;
-
-       in = kvzalloc(MLX5_ST_SZ_BYTES(modify_rmp_in), GFP_KERNEL);
-       if (!in)
-               return -ENOMEM;
-
-       rmpc =    MLX5_ADDR_OF(modify_rmp_in,   in,   ctx);
-       bitmask = MLX5_ADDR_OF(modify_rmp_in,   in,   bitmask);
-       wq   =    MLX5_ADDR_OF(rmpc,            rmpc, wq);
-
-       MLX5_SET(modify_rmp_in, in,      rmp_state, MLX5_RMPC_STATE_RDY);
-       MLX5_SET(modify_rmp_in, in,      rmpn,      srq->srqn);
-       MLX5_SET(modify_rmp_in, in, uid, srq->uid);
-       MLX5_SET(wq,            wq,      lwm,       lwm);
-       MLX5_SET(rmp_bitmask,   bitmask, lwm,       1);
-       MLX5_SET(rmpc, rmpc, state, MLX5_RMPC_STATE_RDY);
-
-       err = mlx5_core_modify_rmp(dev, in, MLX5_ST_SZ_BYTES(modify_rmp_in));
-
-       kvfree(in);
-       return err;
-}
-
-static int query_rmp_cmd(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                        struct mlx5_srq_attr *out)
-{
-       u32 *rmp_out;
-       void *rmpc;
-       int err;
-
-       rmp_out =  kvzalloc(MLX5_ST_SZ_BYTES(query_rmp_out), GFP_KERNEL);
-       if (!rmp_out)
-               return -ENOMEM;
-
-       err = mlx5_core_query_rmp(dev, srq->srqn, rmp_out);
-       if (err)
-               goto out;
-
-       rmpc = MLX5_ADDR_OF(query_rmp_out, rmp_out, rmp_context);
-       get_wq(MLX5_ADDR_OF(rmpc, rmpc, wq), out);
-       if (MLX5_GET(rmpc, rmpc, state) != MLX5_RMPC_STATE_RDY)
-               out->flags |= MLX5_SRQ_FLAG_ERR;
-
-out:
-       kvfree(rmp_out);
-       return err;
-}
-
-static int create_xrq_cmd(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                         struct mlx5_srq_attr *in)
-{
-       u32 create_out[MLX5_ST_SZ_DW(create_xrq_out)] = {0};
-       void *create_in;
-       void *xrqc;
-       void *wq;
-       int pas_size;
-       int inlen;
-       int err;
-
-       pas_size = get_pas_size(in);
-       inlen = MLX5_ST_SZ_BYTES(create_xrq_in) + pas_size;
-       create_in = kvzalloc(inlen, GFP_KERNEL);
-       if (!create_in)
-               return -ENOMEM;
-
-       xrqc = MLX5_ADDR_OF(create_xrq_in, create_in, xrq_context);
-       wq = MLX5_ADDR_OF(xrqc, xrqc, wq);
-
-       set_wq(wq, in);
-       memcpy(MLX5_ADDR_OF(xrqc, xrqc, wq.pas), in->pas, pas_size);
-
-       if (in->type == IB_SRQT_TM) {
-               MLX5_SET(xrqc, xrqc, topology, MLX5_XRQC_TOPOLOGY_TAG_MATCHING);
-               if (in->flags & MLX5_SRQ_FLAG_RNDV)
-                       MLX5_SET(xrqc, xrqc, offload, MLX5_XRQC_OFFLOAD_RNDV);
-               MLX5_SET(xrqc, xrqc,
-                        tag_matching_topology_context.log_matching_list_sz,
-                        in->tm_log_list_size);
-       }
-       MLX5_SET(xrqc, xrqc, user_index, in->user_index);
-       MLX5_SET(xrqc, xrqc, cqn, in->cqn);
-       MLX5_SET(create_xrq_in, create_in, opcode, MLX5_CMD_OP_CREATE_XRQ);
-       MLX5_SET(create_xrq_in, create_in, uid, in->uid);
-       err = mlx5_cmd_exec(dev, create_in, inlen, create_out,
-                           sizeof(create_out));
-       kvfree(create_in);
-       if (!err) {
-               srq->srqn = MLX5_GET(create_xrq_out, create_out, xrqn);
-               srq->uid = in->uid;
-       }
-
-       return err;
-}
-
-static int destroy_xrq_cmd(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq)
-{
-       u32 in[MLX5_ST_SZ_DW(destroy_xrq_in)] = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_xrq_out)] = {0};
-
-       MLX5_SET(destroy_xrq_in, in, opcode, MLX5_CMD_OP_DESTROY_XRQ);
-       MLX5_SET(destroy_xrq_in, in, xrqn,   srq->srqn);
-       MLX5_SET(destroy_xrq_in, in, uid, srq->uid);
-
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-
-static int arm_xrq_cmd(struct mlx5_core_dev *dev,
-                      struct mlx5_core_srq *srq,
-                      u16 lwm)
-{
-       u32 out[MLX5_ST_SZ_DW(arm_rq_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(arm_rq_in)] = {0};
-
-       MLX5_SET(arm_rq_in, in, opcode,     MLX5_CMD_OP_ARM_RQ);
-       MLX5_SET(arm_rq_in, in, op_mod,     MLX5_ARM_RQ_IN_OP_MOD_XRQ);
-       MLX5_SET(arm_rq_in, in, srq_number, srq->srqn);
-       MLX5_SET(arm_rq_in, in, lwm,        lwm);
-       MLX5_SET(arm_rq_in, in, uid, srq->uid);
-
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-
-static int query_xrq_cmd(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                        struct mlx5_srq_attr *out)
-{
-       u32 in[MLX5_ST_SZ_DW(query_xrq_in)] = {0};
-       u32 *xrq_out;
-       int outlen = MLX5_ST_SZ_BYTES(query_xrq_out);
-       void *xrqc;
-       int err;
-
-       xrq_out = kvzalloc(outlen, GFP_KERNEL);
-       if (!xrq_out)
-               return -ENOMEM;
-
-       MLX5_SET(query_xrq_in, in, opcode, MLX5_CMD_OP_QUERY_XRQ);
-       MLX5_SET(query_xrq_in, in, xrqn, srq->srqn);
-
-       err = mlx5_cmd_exec(dev, in, sizeof(in), xrq_out, outlen);
-       if (err)
-               goto out;
-
-       xrqc = MLX5_ADDR_OF(query_xrq_out, xrq_out, xrq_context);
-       get_wq(MLX5_ADDR_OF(xrqc, xrqc, wq), out);
-       if (MLX5_GET(xrqc, xrqc, state) != MLX5_XRQC_STATE_GOOD)
-               out->flags |= MLX5_SRQ_FLAG_ERR;
-       out->tm_next_tag =
-               MLX5_GET(xrqc, xrqc,
-                        tag_matching_topology_context.append_next_index);
-       out->tm_hw_phase_cnt =
-               MLX5_GET(xrqc, xrqc,
-                        tag_matching_topology_context.hw_phase_cnt);
-       out->tm_sw_phase_cnt =
-               MLX5_GET(xrqc, xrqc,
-                        tag_matching_topology_context.sw_phase_cnt);
-
-out:
-       kvfree(xrq_out);
-       return err;
-}
-
-static int create_srq_split(struct mlx5_core_dev *dev,
-                           struct mlx5_core_srq *srq,
-                           struct mlx5_srq_attr *in)
-{
-       if (!dev->issi)
-               return create_srq_cmd(dev, srq, in);
-       switch (srq->common.res) {
-       case MLX5_RES_XSRQ:
-               return create_xrc_srq_cmd(dev, srq, in);
-       case MLX5_RES_XRQ:
-               return create_xrq_cmd(dev, srq, in);
-       default:
-               return create_rmp_cmd(dev, srq, in);
-       }
-}
-
-static int destroy_srq_split(struct mlx5_core_dev *dev,
-                            struct mlx5_core_srq *srq)
-{
-       if (!dev->issi)
-               return destroy_srq_cmd(dev, srq);
-       switch (srq->common.res) {
-       case MLX5_RES_XSRQ:
-               return destroy_xrc_srq_cmd(dev, srq);
-       case MLX5_RES_XRQ:
-               return destroy_xrq_cmd(dev, srq);
-       default:
-               return destroy_rmp_cmd(dev, srq);
-       }
-}
-
-int mlx5_core_create_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                        struct mlx5_srq_attr *in)
-{
-       int err;
-       struct mlx5_srq_table *table = &dev->priv.srq_table;
-
-       switch (in->type) {
-       case IB_SRQT_XRC:
-               srq->common.res = MLX5_RES_XSRQ;
-               break;
-       case IB_SRQT_TM:
-               srq->common.res = MLX5_RES_XRQ;
-               break;
-       default:
-               srq->common.res = MLX5_RES_SRQ;
-       }
-
-       err = create_srq_split(dev, srq, in);
-       if (err)
-               return err;
-
-       atomic_set(&srq->refcount, 1);
-       init_completion(&srq->free);
-
-       spin_lock_irq(&table->lock);
-       err = radix_tree_insert(&table->tree, srq->srqn, srq);
-       spin_unlock_irq(&table->lock);
-       if (err) {
-               mlx5_core_warn(dev, "err %d, srqn 0x%x\n", err, srq->srqn);
-               goto err_destroy_srq_split;
-       }
-
-       return 0;
-
-err_destroy_srq_split:
-       destroy_srq_split(dev, srq);
-
-       return err;
-}
-EXPORT_SYMBOL(mlx5_core_create_srq);
-
-int mlx5_core_destroy_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq)
-{
-       struct mlx5_srq_table *table = &dev->priv.srq_table;
-       struct mlx5_core_srq *tmp;
-       int err;
-
-       spin_lock_irq(&table->lock);
-       tmp = radix_tree_delete(&table->tree, srq->srqn);
-       spin_unlock_irq(&table->lock);
-       if (!tmp) {
-               mlx5_core_warn(dev, "srq 0x%x not found in tree\n", srq->srqn);
-               return -EINVAL;
-       }
-       if (tmp != srq) {
-               mlx5_core_warn(dev, "corruption on srqn 0x%x\n", srq->srqn);
-               return -EINVAL;
-       }
-
-       err = destroy_srq_split(dev, srq);
-       if (err)
-               return err;
-
-       if (atomic_dec_and_test(&srq->refcount))
-               complete(&srq->free);
-       wait_for_completion(&srq->free);
-
-       return 0;
-}
-EXPORT_SYMBOL(mlx5_core_destroy_srq);
-
-int mlx5_core_query_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                       struct mlx5_srq_attr *out)
-{
-       if (!dev->issi)
-               return query_srq_cmd(dev, srq, out);
-       switch (srq->common.res) {
-       case MLX5_RES_XSRQ:
-               return query_xrc_srq_cmd(dev, srq, out);
-       case MLX5_RES_XRQ:
-               return query_xrq_cmd(dev, srq, out);
-       default:
-               return query_rmp_cmd(dev, srq, out);
-       }
-}
-EXPORT_SYMBOL(mlx5_core_query_srq);
-
-int mlx5_core_arm_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                     u16 lwm, int is_srq)
-{
-       if (!dev->issi)
-               return arm_srq_cmd(dev, srq, lwm, is_srq);
-       switch (srq->common.res) {
-       case MLX5_RES_XSRQ:
-               return arm_xrc_srq_cmd(dev, srq, lwm);
-       case MLX5_RES_XRQ:
-               return arm_xrq_cmd(dev, srq, lwm);
-       default:
-               return arm_rmp_cmd(dev, srq, lwm);
-       }
-}
-EXPORT_SYMBOL(mlx5_core_arm_srq);
-
-void mlx5_init_srq_table(struct mlx5_core_dev *dev)
-{
-       struct mlx5_srq_table *table = &dev->priv.srq_table;
-
-       memset(table, 0, sizeof(*table));
-       spin_lock_init(&table->lock);
-       INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
-}
-
-void mlx5_cleanup_srq_table(struct mlx5_core_dev *dev)
-{
-       /* nothing */
-}
index a1ee9a8..c4d4b76 100644 (file)
@@ -258,115 +258,6 @@ void mlx5_core_destroy_tis(struct mlx5_core_dev *dev, u32 tisn)
 }
 EXPORT_SYMBOL(mlx5_core_destroy_tis);
 
-int mlx5_core_create_rmp(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                        u32 *rmpn)
-{
-       u32 out[MLX5_ST_SZ_DW(create_rmp_out)] = {0};
-       int err;
-
-       MLX5_SET(create_rmp_in, in, opcode, MLX5_CMD_OP_CREATE_RMP);
-       err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
-       if (!err)
-               *rmpn = MLX5_GET(create_rmp_out, out, rmpn);
-
-       return err;
-}
-
-int mlx5_core_modify_rmp(struct mlx5_core_dev *dev, u32 *in, int inlen)
-{
-       u32 out[MLX5_ST_SZ_DW(modify_rmp_out)] = {0};
-
-       MLX5_SET(modify_rmp_in, in, opcode, MLX5_CMD_OP_MODIFY_RMP);
-       return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
-}
-
-int mlx5_core_destroy_rmp(struct mlx5_core_dev *dev, u32 rmpn)
-{
-       u32 in[MLX5_ST_SZ_DW(destroy_rmp_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_rmp_out)] = {0};
-
-       MLX5_SET(destroy_rmp_in, in, opcode, MLX5_CMD_OP_DESTROY_RMP);
-       MLX5_SET(destroy_rmp_in, in, rmpn, rmpn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out,
-                                         sizeof(out));
-}
-
-int mlx5_core_query_rmp(struct mlx5_core_dev *dev, u32 rmpn, u32 *out)
-{
-       u32 in[MLX5_ST_SZ_DW(query_rmp_in)] = {0};
-       int outlen = MLX5_ST_SZ_BYTES(query_rmp_out);
-
-       MLX5_SET(query_rmp_in, in, opcode, MLX5_CMD_OP_QUERY_RMP);
-       MLX5_SET(query_rmp_in, in, rmpn,   rmpn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
-}
-
-int mlx5_core_arm_rmp(struct mlx5_core_dev *dev, u32 rmpn, u16 lwm)
-{
-       void *in;
-       void *rmpc;
-       void *wq;
-       void *bitmask;
-       int  err;
-
-       in = kvzalloc(MLX5_ST_SZ_BYTES(modify_rmp_in), GFP_KERNEL);
-       if (!in)
-               return -ENOMEM;
-
-       rmpc    = MLX5_ADDR_OF(modify_rmp_in,   in,   ctx);
-       bitmask = MLX5_ADDR_OF(modify_rmp_in,   in,   bitmask);
-       wq      = MLX5_ADDR_OF(rmpc,            rmpc, wq);
-
-       MLX5_SET(modify_rmp_in, in,      rmp_state, MLX5_RMPC_STATE_RDY);
-       MLX5_SET(modify_rmp_in, in,      rmpn,      rmpn);
-       MLX5_SET(wq,            wq,      lwm,       lwm);
-       MLX5_SET(rmp_bitmask,   bitmask, lwm,       1);
-       MLX5_SET(rmpc,          rmpc,    state,     MLX5_RMPC_STATE_RDY);
-
-       err =  mlx5_core_modify_rmp(dev, in, MLX5_ST_SZ_BYTES(modify_rmp_in));
-
-       kvfree(in);
-
-       return err;
-}
-
-int mlx5_core_create_xsrq(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                         u32 *xsrqn)
-{
-       u32 out[MLX5_ST_SZ_DW(create_xrc_srq_out)] = {0};
-       int err;
-
-       MLX5_SET(create_xrc_srq_in, in, opcode,     MLX5_CMD_OP_CREATE_XRC_SRQ);
-       err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
-       if (!err)
-               *xsrqn = MLX5_GET(create_xrc_srq_out, out, xrc_srqn);
-
-       return err;
-}
-
-int mlx5_core_destroy_xsrq(struct mlx5_core_dev *dev, u32 xsrqn)
-{
-       u32 in[MLX5_ST_SZ_DW(destroy_xrc_srq_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_xrc_srq_out)] = {0};
-
-       MLX5_SET(destroy_xrc_srq_in, in, opcode,   MLX5_CMD_OP_DESTROY_XRC_SRQ);
-       MLX5_SET(destroy_xrc_srq_in, in, xrc_srqn, xsrqn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-
-int mlx5_core_arm_xsrq(struct mlx5_core_dev *dev, u32 xsrqn, u16 lwm)
-{
-       u32 in[MLX5_ST_SZ_DW(arm_xrc_srq_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(arm_xrc_srq_out)] = {0};
-
-       MLX5_SET(arm_xrc_srq_in, in, opcode,   MLX5_CMD_OP_ARM_XRC_SRQ);
-       MLX5_SET(arm_xrc_srq_in, in, xrc_srqn, xsrqn);
-       MLX5_SET(arm_xrc_srq_in, in, lwm,      lwm);
-       MLX5_SET(arm_xrc_srq_in, in, op_mod,
-                MLX5_ARM_XRC_SRQ_IN_OP_MOD_XRC_SRQ);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-
 int mlx5_core_create_rqt(struct mlx5_core_dev *dev, u32 *in, int inlen,
                         u32 *rqtn)
 {
index cfbea66..9b150ce 100644 (file)
@@ -1204,9 +1204,19 @@ EXPORT_SYMBOL_GPL(mlx5_nic_vport_unaffiliate_multiport);
 
 u64 mlx5_query_nic_system_image_guid(struct mlx5_core_dev *mdev)
 {
-       if (!mdev->sys_image_guid)
-               mlx5_query_nic_vport_system_image_guid(mdev, &mdev->sys_image_guid);
+       int port_type_cap = MLX5_CAP_GEN(mdev, port_type);
+       u64 tmp = 0;
 
-       return mdev->sys_image_guid;
+       if (mdev->sys_image_guid)
+               return mdev->sys_image_guid;
+
+       if (port_type_cap == MLX5_CAP_PORT_TYPE_ETH)
+               mlx5_query_nic_vport_system_image_guid(mdev, &tmp);
+       else
+               mlx5_query_hca_vport_system_image_guid(mdev, &tmp);
+
+       mdev->sys_image_guid = tmp;
+
+       return tmp;
 }
 EXPORT_SYMBOL_GPL(mlx5_query_nic_system_image_guid);
index 2dcbf1e..953cc8e 100644 (file)
@@ -155,7 +155,8 @@ int mlx5_cqwq_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param,
                     void *cqc, struct mlx5_cqwq *wq,
                     struct mlx5_wq_ctrl *wq_ctrl)
 {
-       u8 log_wq_stride = MLX5_GET(cqc, cqc, cqe_sz) + 6;
+       /* CQE_STRIDE_128 and CQE_STRIDE_128_PAD both mean 128B stride */
+       u8 log_wq_stride = MLX5_GET(cqc, cqc, cqe_sz) == CQE_STRIDE_64 ? 6 : 7;
        u8 log_wq_sz     = MLX5_GET(cqc, cqc, log_cq_size);
        int err;
 
index b1293d1..ea934a4 100644 (file)
@@ -177,9 +177,14 @@ static inline u32 mlx5_cqwq_get_ci(struct mlx5_cqwq *wq)
        return mlx5_cqwq_ctr2ix(wq, wq->cc);
 }
 
-static inline void *mlx5_cqwq_get_wqe(struct mlx5_cqwq *wq, u32 ix)
+static inline struct mlx5_cqe64 *mlx5_cqwq_get_wqe(struct mlx5_cqwq *wq, u32 ix)
 {
-       return mlx5_frag_buf_get_wqe(&wq->fbc, ix);
+       struct mlx5_cqe64 *cqe = mlx5_frag_buf_get_wqe(&wq->fbc, ix);
+
+       /* For 128B CQEs the data is in the last 64B */
+       cqe += wq->fbc.log_stride == 7;
+
+       return cqe;
 }
 
 static inline u32 mlx5_cqwq_get_ctr_wrap_cnt(struct mlx5_cqwq *wq, u32 ctr)
index 8a291eb..080ddd1 100644 (file)
@@ -80,6 +80,7 @@ config MLXSW_SPECTRUM
        depends on IPV6_GRE || IPV6_GRE=n
        select GENERIC_ALLOCATOR
        select PARMAN
+       select OBJAGG
        select MLXFW
        default m
        ---help---
index 1f77e97..bbf45f1 100644 (file)
@@ -20,7 +20,7 @@ mlxsw_spectrum-objs           := spectrum.o spectrum_buffers.o \
                                   spectrum_acl_tcam.o spectrum_acl_ctcam.o \
                                   spectrum_acl_atcam.o spectrum_acl_erp.o \
                                   spectrum1_acl_tcam.o spectrum2_acl_tcam.o \
-                                  spectrum_acl.o \
+                                  spectrum_acl_bloom_filter.o spectrum_acl.o \
                                   spectrum_flower.o spectrum_cnt.o \
                                   spectrum_fid.o spectrum_ipip.o \
                                   spectrum_acl_flex_actions.o \
index 30f751e..ddedf8a 100644 (file)
@@ -81,6 +81,7 @@ struct mlxsw_core {
        struct mlxsw_core_port *ports;
        unsigned int max_ports;
        bool reload_fail;
+       bool fw_flash_in_progress;
        unsigned long driver_priv[0];
        /* driver_priv has to be always the last item */
 };
@@ -428,12 +429,16 @@ struct mlxsw_reg_trans {
        struct rcu_head rcu;
 };
 
-#define MLXSW_EMAD_TIMEOUT_MS 200
+#define MLXSW_EMAD_TIMEOUT_DURING_FW_FLASH_MS  3000
+#define MLXSW_EMAD_TIMEOUT_MS                  200
 
 static void mlxsw_emad_trans_timeout_schedule(struct mlxsw_reg_trans *trans)
 {
        unsigned long timeout = msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_MS);
 
+       if (trans->core->fw_flash_in_progress)
+               timeout = msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_DURING_FW_FLASH_MS);
+
        queue_delayed_work(trans->core->emad_wq, &trans->timeout_dw, timeout);
 }
 
@@ -965,10 +970,11 @@ static const struct devlink_ops mlxsw_devlink_ops = {
        .sb_occ_tc_port_bind_get        = mlxsw_devlink_sb_occ_tc_port_bind_get,
 };
 
-int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
-                                  const struct mlxsw_bus *mlxsw_bus,
-                                  void *bus_priv, bool reload,
-                                  struct devlink *devlink)
+static int
+__mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
+                                const struct mlxsw_bus *mlxsw_bus,
+                                void *bus_priv, bool reload,
+                                struct devlink *devlink)
 {
        const char *device_kind = mlxsw_bus_info->device_kind;
        struct mlxsw_core *mlxsw_core;
@@ -1035,6 +1041,12 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
                        goto err_devlink_register;
        }
 
+       if (mlxsw_driver->params_register && !reload) {
+               err = mlxsw_driver->params_register(mlxsw_core);
+               if (err)
+                       goto err_register_params;
+       }
+
        err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon);
        if (err)
                goto err_hwmon_init;
@@ -1057,6 +1069,9 @@ err_driver_init:
 err_thermal_init:
        mlxsw_hwmon_fini(mlxsw_core->hwmon);
 err_hwmon_init:
+       if (mlxsw_driver->params_unregister && !reload)
+               mlxsw_driver->params_unregister(mlxsw_core);
+err_register_params:
        if (!reload)
                devlink_unregister(devlink);
 err_devlink_register:
@@ -1076,6 +1091,29 @@ err_bus_init:
 err_devlink_alloc:
        return err;
 }
+
+int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
+                                  const struct mlxsw_bus *mlxsw_bus,
+                                  void *bus_priv, bool reload,
+                                  struct devlink *devlink)
+{
+       bool called_again = false;
+       int err;
+
+again:
+       err = __mlxsw_core_bus_device_register(mlxsw_bus_info, mlxsw_bus,
+                                              bus_priv, reload, devlink);
+       /* -EAGAIN is returned in case the FW was updated. FW needs
+        * a reset, so lets try to call __mlxsw_core_bus_device_register()
+        * again.
+        */
+       if (err == -EAGAIN && !called_again) {
+               called_again = true;
+               goto again;
+       }
+
+       return err;
+}
 EXPORT_SYMBOL(mlxsw_core_bus_device_register);
 
 void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
@@ -1097,6 +1135,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
                mlxsw_core->driver->fini(mlxsw_core);
        mlxsw_thermal_fini(mlxsw_core->thermal);
        mlxsw_hwmon_fini(mlxsw_core->hwmon);
+       if (mlxsw_core->driver->params_unregister && !reload)
+               mlxsw_core->driver->params_unregister(mlxsw_core);
        if (!reload)
                devlink_unregister(devlink);
        mlxsw_emad_fini(mlxsw_core);
@@ -1109,6 +1149,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
        return;
 
 reload_fail_deinit:
+       if (mlxsw_core->driver->params_unregister)
+               mlxsw_core->driver->params_unregister(mlxsw_core);
        devlink_unregister(devlink);
        devlink_resources_unregister(devlink, NULL);
        devlink_free(devlink);
@@ -1854,6 +1896,18 @@ int mlxsw_core_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
 }
 EXPORT_SYMBOL(mlxsw_core_kvd_sizes_get);
 
+void mlxsw_core_fw_flash_start(struct mlxsw_core *mlxsw_core)
+{
+       mlxsw_core->fw_flash_in_progress = true;
+}
+EXPORT_SYMBOL(mlxsw_core_fw_flash_start);
+
+void mlxsw_core_fw_flash_end(struct mlxsw_core *mlxsw_core)
+{
+       mlxsw_core->fw_flash_in_progress = false;
+}
+EXPORT_SYMBOL(mlxsw_core_fw_flash_end);
+
 static int __init mlxsw_core_module_init(void)
 {
        int err;
index c35be47..4e114f3 100644 (file)
@@ -282,6 +282,8 @@ struct mlxsw_driver {
                             const struct mlxsw_config_profile *profile,
                             u64 *p_single_size, u64 *p_double_size,
                             u64 *p_linear_size);
+       int (*params_register)(struct mlxsw_core *mlxsw_core);
+       void (*params_unregister)(struct mlxsw_core *mlxsw_core);
        u8 txhdr_len;
        const struct mlxsw_config_profile *profile;
        bool res_query_enabled;
@@ -292,6 +294,9 @@ int mlxsw_core_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
                             u64 *p_single_size, u64 *p_double_size,
                             u64 *p_linear_size);
 
+void mlxsw_core_fw_flash_start(struct mlxsw_core *mlxsw_core);
+void mlxsw_core_fw_flash_end(struct mlxsw_core *mlxsw_core);
+
 bool mlxsw_core_res_valid(struct mlxsw_core *mlxsw_core,
                          enum mlxsw_res_id res_id);
 
index 785bf01..df78d23 100644 (file)
@@ -426,15 +426,17 @@ mlxsw_sp_afk_encode_one(const struct mlxsw_afk_element_inst *elinst,
 void mlxsw_afk_encode(struct mlxsw_afk *mlxsw_afk,
                      struct mlxsw_afk_key_info *key_info,
                      struct mlxsw_afk_element_values *values,
-                     char *key, char *mask, int block_start, int block_end)
+                     char *key, char *mask)
 {
+       unsigned int blocks_count =
+                       mlxsw_afk_key_info_blocks_count_get(key_info);
        char block_mask[MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE];
        char block_key[MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE];
        const struct mlxsw_afk_element_inst *elinst;
        enum mlxsw_afk_element element;
        int block_index, i;
 
-       for (i = block_start; i <= block_end; i++) {
+       for (i = 0; i < blocks_count; i++) {
                memset(block_key, 0, MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE);
                memset(block_mask, 0, MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE);
 
@@ -451,10 +453,18 @@ void mlxsw_afk_encode(struct mlxsw_afk *mlxsw_afk,
                                                values->storage.mask);
                }
 
-               if (key)
-                       mlxsw_afk->ops->encode_block(block_key, i, key);
-               if (mask)
-                       mlxsw_afk->ops->encode_block(block_mask, i, mask);
+               mlxsw_afk->ops->encode_block(key, i, block_key);
+               mlxsw_afk->ops->encode_block(mask, i, block_mask);
        }
 }
 EXPORT_SYMBOL(mlxsw_afk_encode);
+
+void mlxsw_afk_clear(struct mlxsw_afk *mlxsw_afk, char *key,
+                    int block_start, int block_end)
+{
+       int i;
+
+       for (i = block_start; i <= block_end; i++)
+               mlxsw_afk->ops->clear_block(key, i);
+}
+EXPORT_SYMBOL(mlxsw_afk_clear);
index c29c045..4a625cd 100644 (file)
@@ -33,6 +33,8 @@ enum mlxsw_afk_element {
        MLXSW_AFK_ELEMENT_IP_TTL_,
        MLXSW_AFK_ELEMENT_IP_ECN,
        MLXSW_AFK_ELEMENT_IP_DSCP,
+       MLXSW_AFK_ELEMENT_VIRT_ROUTER_8_10,
+       MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_7,
        MLXSW_AFK_ELEMENT_MAX,
 };
 
@@ -87,6 +89,8 @@ static const struct mlxsw_afk_element_info mlxsw_afk_element_infos[] = {
        MLXSW_AFK_ELEMENT_INFO_U32(IP_TTL_, 0x18, 0, 8),
        MLXSW_AFK_ELEMENT_INFO_U32(IP_ECN, 0x18, 9, 2),
        MLXSW_AFK_ELEMENT_INFO_U32(IP_DSCP, 0x18, 11, 6),
+       MLXSW_AFK_ELEMENT_INFO_U32(VIRT_ROUTER_8_10, 0x18, 17, 3),
+       MLXSW_AFK_ELEMENT_INFO_U32(VIRT_ROUTER_0_7, 0x18, 20, 8),
        MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP_96_127, 0x20, 4),
        MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP_64_95, 0x24, 4),
        MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP_32_63, 0x28, 4),
@@ -188,7 +192,8 @@ struct mlxsw_afk;
 struct mlxsw_afk_ops {
        const struct mlxsw_afk_block *blocks;
        unsigned int blocks_count;
-       void (*encode_block)(char *block, int block_index, char *output);
+       void (*encode_block)(char *output, int block_index, char *block);
+       void (*clear_block)(char *output, int block_index);
 };
 
 struct mlxsw_afk *mlxsw_afk_create(unsigned int max_blocks,
@@ -228,6 +233,8 @@ void mlxsw_afk_values_add_buf(struct mlxsw_afk_element_values *values,
 void mlxsw_afk_encode(struct mlxsw_afk *mlxsw_afk,
                      struct mlxsw_afk_key_info *key_info,
                      struct mlxsw_afk_element_values *values,
-                     char *key, char *mask, int block_start, int block_end);
+                     char *key, char *mask);
+void mlxsw_afk_clear(struct mlxsw_afk *mlxsw_afk, char *key,
+                    int block_start, int block_end);
 
 #endif
index 6d29dc4..61f897b 100644 (file)
 #define MLXSW_THERMAL_MAX_TEMP 110000  /* 110C */
 #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
+ * MLXSW_THERMAL_MAX_STATE + x, where x is between 2 and 10 are used for
+ * setting fan speed dynamic minimum. For example, if value is set to 14 (40%)
+ * cooling levels vector will be set to 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10 to
+ * introduce PWM speed in percent: 40, 40, 40, 40, 40, 50, 60. 70, 80, 90, 100.
+ */
+#define MLXSW_THERMAL_SPEED_MIN                (MLXSW_THERMAL_MAX_STATE + 2)
+#define MLXSW_THERMAL_SPEED_MAX                (MLXSW_THERMAL_MAX_STATE * 2)
+#define MLXSW_THERMAL_SPEED_MIN_LEVEL  2               /* 20% */
 
 struct mlxsw_thermal_trip {
        int     type;
@@ -68,6 +77,7 @@ struct mlxsw_thermal {
        const struct mlxsw_bus_info *bus_info;
        struct thermal_zone_device *tzdev;
        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;
 };
@@ -285,12 +295,51 @@ static int mlxsw_thermal_set_cur_state(struct thermal_cooling_device *cdev,
        struct mlxsw_thermal *thermal = cdev->devdata;
        struct device *dev = thermal->bus_info->dev;
        char mfsc_pl[MLXSW_REG_MFSC_LEN];
-       int err, idx;
+       unsigned long cur_state, i;
+       int idx;
+       u8 duty;
+       int err;
 
        idx = mlxsw_get_cooling_device_idx(thermal, cdev);
        if (idx < 0)
                return idx;
 
+       /* Verify if this request is for changing allowed fan dynamical
+        * minimum. If it is - update cooling levels accordingly and update
+        * state, if current state is below the newly requested minimum state.
+        * For example, if current state is 5, and minimal state is to be
+        * changed from 4 to 6, thermal->cooling_levels[0 to 5] will be changed
+        * all from 4 to 6. And state 5 (thermal->cooling_levels[4]) should be
+        * overwritten.
+        */
+       if (state >= MLXSW_THERMAL_SPEED_MIN &&
+           state <= MLXSW_THERMAL_SPEED_MAX) {
+               state -= MLXSW_THERMAL_MAX_STATE;
+               for (i = 0; i <= MLXSW_THERMAL_MAX_STATE; i++)
+                       thermal->cooling_levels[i] = max(state, i);
+
+               mlxsw_reg_mfsc_pack(mfsc_pl, idx, 0);
+               err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfsc), mfsc_pl);
+               if (err)
+                       return err;
+
+               duty = mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl);
+               cur_state = mlxsw_duty_to_state(duty);
+
+               /* If current fan state is lower than requested dynamical
+                * minimum, increase fan speed up to dynamical minimum.
+                */
+               if (state < cur_state)
+                       return 0;
+
+               state = cur_state;
+       }
+
+       if (state > MLXSW_THERMAL_MAX_STATE)
+               return -EINVAL;
+
+       /* Normalize the state to the valid speed range. */
+       state = thermal->cooling_levels[state];
        mlxsw_reg_mfsc_pack(mfsc_pl, idx, mlxsw_state_to_duty(state));
        err = mlxsw_reg_write(thermal->core, MLXSW_REG(mfsc), mfsc_pl);
        if (err) {
@@ -369,6 +418,11 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
                }
        }
 
+       /* Initialize cooling levels per PWM state. */
+       for (i = 0; i < MLXSW_THERMAL_MAX_STATE; i++)
+               thermal->cooling_levels[i] = max(MLXSW_THERMAL_SPEED_MIN_LEVEL,
+                                                i);
+
        thermal->tzdev = thermal_zone_device_register("mlxsw",
                                                      MLXSW_THERMAL_NUM_TRIPS,
                                                      MLXSW_THERMAL_TRIP_MASK,
index 5890fdf..66b8098 100644 (file)
@@ -1720,7 +1720,6 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        const char *driver_name = pdev->driver->name;
        struct mlxsw_pci *mlxsw_pci;
-       bool called_again = false;
        int err;
 
        mlxsw_pci = kzalloc(sizeof(*mlxsw_pci), GFP_KERNEL);
@@ -1777,18 +1776,10 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        mlxsw_pci->bus_info.dev = &pdev->dev;
        mlxsw_pci->id = id;
 
-again:
        err = mlxsw_core_bus_device_register(&mlxsw_pci->bus_info,
                                             &mlxsw_pci_bus, mlxsw_pci, false,
                                             NULL);
-       /* -EAGAIN is returned in case the FW was updated. FW needs
-        * a reset, so lets try to call mlxsw_core_bus_device_register()
-        * again.
-        */
-       if (err == -EAGAIN && !called_again) {
-               called_again = true;
-               goto again;
-       } else if (err) {
+       if (err) {
                dev_err(&pdev->dev, "cannot register bus device\n");
                goto err_bus_device_register;
        }
index db3d279..9b48dff 100644 (file)
@@ -641,6 +641,10 @@ enum mlxsw_reg_sfn_rec_type {
        MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC = 0x7,
        /* Aged-out MAC address on a LAG port. */
        MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG = 0x8,
+       /* Learned unicast tunnel record. */
+       MLXSW_REG_SFN_REC_TYPE_LEARNED_UNICAST_TUNNEL = 0xD,
+       /* Aged-out unicast tunnel record. */
+       MLXSW_REG_SFN_REC_TYPE_AGED_OUT_UNICAST_TUNNEL = 0xE,
 };
 
 /* reg_sfn_rec_type
@@ -704,6 +708,66 @@ static inline void mlxsw_reg_sfn_mac_lag_unpack(char *payload, int rec_index,
        *p_lag_id = mlxsw_reg_sfn_mac_lag_lag_id_get(payload, rec_index);
 }
 
+/* reg_sfn_uc_tunnel_uip_msb
+ * When protocol is IPv4, the most significant byte of the underlay IPv4
+ * address of the remote VTEP.
+ * When protocol is IPv6, reserved.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, uc_tunnel_uip_msb, MLXSW_REG_SFN_BASE_LEN, 24,
+                    8, MLXSW_REG_SFN_REC_LEN, 0x08, false);
+
+enum mlxsw_reg_sfn_uc_tunnel_protocol {
+       MLXSW_REG_SFN_UC_TUNNEL_PROTOCOL_IPV4,
+       MLXSW_REG_SFN_UC_TUNNEL_PROTOCOL_IPV6,
+};
+
+/* reg_sfn_uc_tunnel_protocol
+ * IP protocol.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, uc_tunnel_protocol, MLXSW_REG_SFN_BASE_LEN, 27,
+                    1, MLXSW_REG_SFN_REC_LEN, 0x0C, false);
+
+/* reg_sfn_uc_tunnel_uip_lsb
+ * When protocol is IPv4, the least significant bytes of the underlay
+ * IPv4 address of the remote VTEP.
+ * When protocol is IPv6, ipv6_id to be queried from TNIPSD.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, uc_tunnel_uip_lsb, MLXSW_REG_SFN_BASE_LEN, 0,
+                    24, MLXSW_REG_SFN_REC_LEN, 0x0C, false);
+
+enum mlxsw_reg_sfn_tunnel_port {
+       MLXSW_REG_SFN_TUNNEL_PORT_NVE,
+       MLXSW_REG_SFN_TUNNEL_PORT_VPLS,
+       MLXSW_REG_SFN_TUNNEL_FLEX_TUNNEL0,
+       MLXSW_REG_SFN_TUNNEL_FLEX_TUNNEL1,
+};
+
+/* reg_sfn_uc_tunnel_port
+ * Tunnel port.
+ * Reserved on Spectrum.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, tunnel_port, MLXSW_REG_SFN_BASE_LEN, 0, 4,
+                    MLXSW_REG_SFN_REC_LEN, 0x10, false);
+
+static inline void
+mlxsw_reg_sfn_uc_tunnel_unpack(char *payload, int rec_index, char *mac,
+                              u16 *p_fid, u32 *p_uip,
+                              enum mlxsw_reg_sfn_uc_tunnel_protocol *p_proto)
+{
+       u32 uip_msb, uip_lsb;
+
+       mlxsw_reg_sfn_rec_mac_memcpy_from(payload, rec_index, mac);
+       *p_fid = mlxsw_reg_sfn_mac_fid_get(payload, rec_index);
+       uip_msb = mlxsw_reg_sfn_uc_tunnel_uip_msb_get(payload, rec_index);
+       uip_lsb = mlxsw_reg_sfn_uc_tunnel_uip_lsb_get(payload, rec_index);
+       *p_uip = uip_msb << 24 | uip_lsb;
+       *p_proto = mlxsw_reg_sfn_uc_tunnel_protocol_get(payload, rec_index);
+}
+
 /* SPMS - Switch Port MSTP/RSTP State Register
  * -------------------------------------------
  * Configures the spanning tree state of a physical port.
@@ -2431,6 +2495,43 @@ static inline void mlxsw_reg_pefa_unpack(char *payload, bool *p_a)
        *p_a = mlxsw_reg_pefa_a_get(payload);
 }
 
+/* PEMRBT - Policy-Engine Multicast Router Binding Table Register
+ * --------------------------------------------------------------
+ * This register is used for binding Multicast router to an ACL group
+ * that serves the MC router.
+ * This register is not supported by SwitchX/-2 and Spectrum.
+ */
+#define MLXSW_REG_PEMRBT_ID 0x3014
+#define MLXSW_REG_PEMRBT_LEN 0x14
+
+MLXSW_REG_DEFINE(pemrbt, MLXSW_REG_PEMRBT_ID, MLXSW_REG_PEMRBT_LEN);
+
+enum mlxsw_reg_pemrbt_protocol {
+       MLXSW_REG_PEMRBT_PROTO_IPV4,
+       MLXSW_REG_PEMRBT_PROTO_IPV6,
+};
+
+/* reg_pemrbt_protocol
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pemrbt, protocol, 0x00, 0, 1);
+
+/* reg_pemrbt_group_id
+ * ACL group identifier.
+ * Range 0..cap_max_acl_groups-1
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, pemrbt, group_id, 0x10, 0, 16);
+
+static inline void
+mlxsw_reg_pemrbt_pack(char *payload, enum mlxsw_reg_pemrbt_protocol protocol,
+                     u16 group_id)
+{
+       MLXSW_REG_ZERO(pemrbt, payload);
+       mlxsw_reg_pemrbt_protocol_set(payload, protocol);
+       mlxsw_reg_pemrbt_group_id_set(payload, group_id);
+}
+
 /* PTCE-V2 - Policy-Engine TCAM Entry Register Version 2
  * -----------------------------------------------------
  * This register is used for accessing rules within a TCAM region.
@@ -2642,7 +2743,7 @@ mlxsw_reg_perpt_pack(char *payload, u8 erpt_bank, u8 erpt_index,
        mlxsw_reg_perpt_erpt_bank_set(payload, erpt_bank);
        mlxsw_reg_perpt_erpt_index_set(payload, erpt_index);
        mlxsw_reg_perpt_key_size_set(payload, key_size);
-       mlxsw_reg_perpt_bf_bypass_set(payload, true);
+       mlxsw_reg_perpt_bf_bypass_set(payload, false);
        mlxsw_reg_perpt_erp_id_set(payload, erp_id);
        mlxsw_reg_perpt_erpt_base_bank_set(payload, erpt_base_bank);
        mlxsw_reg_perpt_erpt_base_index_set(payload, erpt_base_index);
@@ -2834,8 +2935,9 @@ static inline void mlxsw_reg_ptce3_pack(char *payload, bool valid,
                                        u32 priority,
                                        const char *tcam_region_info,
                                        const char *key, u8 erp_id,
-                                       bool large_exists, u32 lkey_id,
-                                       u32 action_pointer)
+                                       u16 delta_start, u8 delta_mask,
+                                       u8 delta_value, bool large_exists,
+                                       u32 lkey_id, u32 action_pointer)
 {
        MLXSW_REG_ZERO(ptce3, payload);
        mlxsw_reg_ptce3_v_set(payload, valid);
@@ -2844,6 +2946,9 @@ static inline void mlxsw_reg_ptce3_pack(char *payload, bool valid,
        mlxsw_reg_ptce3_tcam_region_info_memcpy_to(payload, tcam_region_info);
        mlxsw_reg_ptce3_flex2_key_blocks_memcpy_to(payload, key);
        mlxsw_reg_ptce3_erp_id_set(payload, erp_id);
+       mlxsw_reg_ptce3_delta_start_set(payload, delta_start);
+       mlxsw_reg_ptce3_delta_mask_set(payload, delta_mask);
+       mlxsw_reg_ptce3_delta_value_set(payload, delta_value);
        mlxsw_reg_ptce3_large_exists_set(payload, large_exists);
        mlxsw_reg_ptce3_large_entry_key_id_set(payload, lkey_id);
        mlxsw_reg_ptce3_action_pointer_set(payload, action_pointer);
@@ -2901,7 +3006,7 @@ static inline void mlxsw_reg_percr_pack(char *payload, u16 region_id)
        mlxsw_reg_percr_region_id_set(payload, region_id);
        mlxsw_reg_percr_atcam_ignore_prune_set(payload, false);
        mlxsw_reg_percr_ctcam_ignore_prune_set(payload, false);
-       mlxsw_reg_percr_bf_bypass_set(payload, true);
+       mlxsw_reg_percr_bf_bypass_set(payload, false);
 }
 
 /* PERERP - Policy-Engine Region eRP Register
@@ -2990,6 +3095,72 @@ static inline void mlxsw_reg_pererp_pack(char *payload, u16 region_id,
        mlxsw_reg_pererp_master_rp_id_set(payload, master_rp_id);
 }
 
+/* PEABFE - Policy-Engine Algorithmic Bloom Filter Entries Register
+ * ----------------------------------------------------------------
+ * This register configures the Bloom filter entries.
+ */
+#define MLXSW_REG_PEABFE_ID 0x3022
+#define MLXSW_REG_PEABFE_BASE_LEN 0x10
+#define MLXSW_REG_PEABFE_BF_REC_LEN 0x4
+#define MLXSW_REG_PEABFE_BF_REC_MAX_COUNT 256
+#define MLXSW_REG_PEABFE_LEN (MLXSW_REG_PEABFE_BASE_LEN + \
+                             MLXSW_REG_PEABFE_BF_REC_LEN * \
+                             MLXSW_REG_PEABFE_BF_REC_MAX_COUNT)
+
+MLXSW_REG_DEFINE(peabfe, MLXSW_REG_PEABFE_ID, MLXSW_REG_PEABFE_LEN);
+
+/* reg_peabfe_size
+ * Number of BF entries to be updated.
+ * Range 1..256
+ * Access: Op
+ */
+MLXSW_ITEM32(reg, peabfe, size, 0x00, 0, 9);
+
+/* reg_peabfe_bf_entry_state
+ * Bloom filter state
+ * 0 - Clear
+ * 1 - Set
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, peabfe, bf_entry_state,
+                    MLXSW_REG_PEABFE_BASE_LEN, 31, 1,
+                    MLXSW_REG_PEABFE_BF_REC_LEN, 0x00, false);
+
+/* reg_peabfe_bf_entry_bank
+ * Bloom filter bank ID
+ * Range 0..cap_max_erp_table_banks-1
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, peabfe, bf_entry_bank,
+                    MLXSW_REG_PEABFE_BASE_LEN, 24, 4,
+                    MLXSW_REG_PEABFE_BF_REC_LEN, 0x00, false);
+
+/* reg_peabfe_bf_entry_index
+ * Bloom filter entry index
+ * Range 0..2^cap_max_bf_log-1
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, peabfe, bf_entry_index,
+                    MLXSW_REG_PEABFE_BASE_LEN, 0, 24,
+                    MLXSW_REG_PEABFE_BF_REC_LEN, 0x00, false);
+
+static inline void mlxsw_reg_peabfe_pack(char *payload)
+{
+       MLXSW_REG_ZERO(peabfe, payload);
+}
+
+static inline void mlxsw_reg_peabfe_rec_pack(char *payload, int rec_index,
+                                            u8 state, u8 bank, u32 bf_index)
+{
+       u8 num_rec = mlxsw_reg_peabfe_size_get(payload);
+
+       if (rec_index >= num_rec)
+               mlxsw_reg_peabfe_size_set(payload, rec_index + 1);
+       mlxsw_reg_peabfe_bf_entry_state_set(payload, rec_index, state);
+       mlxsw_reg_peabfe_bf_entry_bank_set(payload, rec_index, bank);
+       mlxsw_reg_peabfe_bf_entry_index_set(payload, rec_index, bf_index);
+}
+
 /* IEDR - Infrastructure Entry Delete Register
  * ----------------------------------------------------
  * This register is used for deleting entries from the entry tables.
@@ -4231,8 +4402,11 @@ MLXSW_ITEM32(reg, ppcnt, pnat, 0x00, 14, 2);
 
 enum mlxsw_reg_ppcnt_grp {
        MLXSW_REG_PPCNT_IEEE_8023_CNT = 0x0,
+       MLXSW_REG_PPCNT_RFC_2863_CNT = 0x1,
        MLXSW_REG_PPCNT_RFC_2819_CNT = 0x2,
+       MLXSW_REG_PPCNT_RFC_3635_CNT = 0x3,
        MLXSW_REG_PPCNT_EXT_CNT = 0x5,
+       MLXSW_REG_PPCNT_DISCARD_CNT = 0x6,
        MLXSW_REG_PPCNT_PRIO_CNT = 0x10,
        MLXSW_REG_PPCNT_TC_CNT = 0x11,
        MLXSW_REG_PPCNT_TC_CONG_TC = 0x13,
@@ -4247,6 +4421,7 @@ enum mlxsw_reg_ppcnt_grp {
  * 0x2: RFC 2819 Counters
  * 0x3: RFC 3635 Counters
  * 0x5: Ethernet Extended Counters
+ * 0x6: Ethernet Discard Counters
  * 0x8: Link Level Retransmission Counters
  * 0x10: Per Priority Counters
  * 0x11: Per Traffic Class Counters
@@ -4390,8 +4565,46 @@ MLXSW_ITEM64(reg, ppcnt, a_pause_mac_ctrl_frames_received,
 MLXSW_ITEM64(reg, ppcnt, a_pause_mac_ctrl_frames_transmitted,
             MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x90, 0, 64);
 
+/* Ethernet RFC 2863 Counter Group */
+
+/* reg_ppcnt_if_in_discards
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, if_in_discards,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x10, 0, 64);
+
+/* reg_ppcnt_if_out_discards
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, if_out_discards,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x38, 0, 64);
+
+/* reg_ppcnt_if_out_errors
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, if_out_errors,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x40, 0, 64);
+
 /* Ethernet RFC 2819 Counter Group */
 
+/* reg_ppcnt_ether_stats_undersize_pkts
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ether_stats_undersize_pkts,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x30, 0, 64);
+
+/* reg_ppcnt_ether_stats_oversize_pkts
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ether_stats_oversize_pkts,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x38, 0, 64);
+
+/* reg_ppcnt_ether_stats_fragments
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ether_stats_fragments,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x40, 0, 64);
+
 /* reg_ppcnt_ether_stats_pkts64octets
  * Access: RO
  */
@@ -4452,6 +4665,32 @@ MLXSW_ITEM64(reg, ppcnt, ether_stats_pkts4096to8191octets,
 MLXSW_ITEM64(reg, ppcnt, ether_stats_pkts8192to10239octets,
             MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0xA0, 0, 64);
 
+/* Ethernet RFC 3635 Counter Group */
+
+/* reg_ppcnt_dot3stats_fcs_errors
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, dot3stats_fcs_errors,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x08, 0, 64);
+
+/* reg_ppcnt_dot3stats_symbol_errors
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, dot3stats_symbol_errors,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x60, 0, 64);
+
+/* reg_ppcnt_dot3control_in_unknown_opcodes
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, dot3control_in_unknown_opcodes,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x68, 0, 64);
+
+/* reg_ppcnt_dot3in_pause_frames
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, dot3in_pause_frames,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x70, 0, 64);
+
 /* Ethernet Extended Counter Group Counters */
 
 /* reg_ppcnt_ecn_marked
@@ -4460,6 +4699,80 @@ MLXSW_ITEM64(reg, ppcnt, ether_stats_pkts8192to10239octets,
 MLXSW_ITEM64(reg, ppcnt, ecn_marked,
             MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x08, 0, 64);
 
+/* Ethernet Discard Counter Group Counters */
+
+/* reg_ppcnt_ingress_general
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ingress_general,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x00, 0, 64);
+
+/* reg_ppcnt_ingress_policy_engine
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ingress_policy_engine,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x08, 0, 64);
+
+/* reg_ppcnt_ingress_vlan_membership
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ingress_vlan_membership,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x10, 0, 64);
+
+/* reg_ppcnt_ingress_tag_frame_type
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ingress_tag_frame_type,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x18, 0, 64);
+
+/* reg_ppcnt_egress_vlan_membership
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_vlan_membership,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x20, 0, 64);
+
+/* reg_ppcnt_loopback_filter
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, loopback_filter,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x28, 0, 64);
+
+/* reg_ppcnt_egress_general
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_general,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x30, 0, 64);
+
+/* reg_ppcnt_egress_hoq
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_hoq,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x40, 0, 64);
+
+/* reg_ppcnt_egress_policy_engine
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_policy_engine,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x50, 0, 64);
+
+/* reg_ppcnt_ingress_tx_link_down
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ingress_tx_link_down,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x58, 0, 64);
+
+/* reg_ppcnt_egress_stp_filter
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_stp_filter,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x60, 0, 64);
+
+/* reg_ppcnt_egress_sll
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_sll,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x70, 0, 64);
+
 /* Ethernet Per Priority Group Counters */
 
 /* reg_ppcnt_rx_octets
@@ -4862,6 +5175,7 @@ enum mlxsw_reg_htgt_trap_group {
        MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT,
        MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD,
        MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND,
+       MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR,
 };
 
 /* reg_htgt_trap_group
@@ -9357,8 +9671,10 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
        MLXSW_REG(ppbs),
        MLXSW_REG(prcr),
        MLXSW_REG(pefa),
+       MLXSW_REG(pemrbt),
        MLXSW_REG(ptce2),
        MLXSW_REG(perpt),
+       MLXSW_REG(peabfe),
        MLXSW_REG(perar),
        MLXSW_REG(ptce3),
        MLXSW_REG(percr),
index 99b3415..b8b3a01 100644 (file)
@@ -41,6 +41,7 @@ enum mlxsw_res_id {
        MLXSW_RES_ID_ACL_ERPT_ENTRIES_4KB,
        MLXSW_RES_ID_ACL_ERPT_ENTRIES_8KB,
        MLXSW_RES_ID_ACL_ERPT_ENTRIES_12KB,
+       MLXSW_RES_ID_ACL_MAX_BF_LOG,
        MLXSW_RES_ID_MAX_CPU_POLICERS,
        MLXSW_RES_ID_MAX_VRS,
        MLXSW_RES_ID_MAX_RIFS,
@@ -93,6 +94,7 @@ static u16 mlxsw_res_ids[] = {
        [MLXSW_RES_ID_ACL_ERPT_ENTRIES_4KB] = 0x2951,
        [MLXSW_RES_ID_ACL_ERPT_ENTRIES_8KB] = 0x2952,
        [MLXSW_RES_ID_ACL_ERPT_ENTRIES_12KB] = 0x2953,
+       [MLXSW_RES_ID_ACL_MAX_BF_LOG] = 0x2960,
        [MLXSW_RES_ID_MAX_CPU_POLICERS] = 0x2A13,
        [MLXSW_RES_ID_MAX_VRS] = 0x2C01,
        [MLXSW_RES_ID_MAX_RIFS] = 0x2C02,
index 9bec940..c742a58 100644 (file)
@@ -45,8 +45,8 @@
 #define MLXSW_SP_FWREV_MINOR_TO_BRANCH(minor) ((minor) / 100)
 
 #define MLXSW_SP1_FWREV_MAJOR 13
-#define MLXSW_SP1_FWREV_MINOR 1703
-#define MLXSW_SP1_FWREV_SUBMINOR 4
+#define MLXSW_SP1_FWREV_MINOR 1910
+#define MLXSW_SP1_FWREV_SUBMINOR 622
 #define MLXSW_SP1_FWREV_CAN_RESET_MINOR 1702
 
 static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = {
@@ -65,6 +65,13 @@ static const char mlxsw_sp1_driver_name[] = "mlxsw_spectrum";
 static const char mlxsw_sp2_driver_name[] = "mlxsw_spectrum2";
 static const char mlxsw_sp_driver_version[] = "1.0";
 
+static const unsigned char mlxsw_sp1_mac_mask[ETH_ALEN] = {
+       0xff, 0xff, 0xff, 0xff, 0xfc, 0x00
+};
+static const unsigned char mlxsw_sp2_mac_mask[ETH_ALEN] = {
+       0xff, 0xff, 0xff, 0xff, 0xf0, 0x00
+};
+
 /* tx_hdr_version
  * Tx header version.
  * Must be set to 1.
@@ -309,8 +316,13 @@ static int mlxsw_sp_firmware_flash(struct mlxsw_sp *mlxsw_sp,
                },
                .mlxsw_sp = mlxsw_sp
        };
+       int err;
 
-       return mlxfw_firmware_flash(&mlxsw_sp_mlxfw_dev.mlxfw_dev, firmware);
+       mlxsw_core_fw_flash_start(mlxsw_sp->core);
+       err = mlxfw_firmware_flash(&mlxsw_sp_mlxfw_dev.mlxfw_dev, firmware);
+       mlxsw_core_fw_flash_end(mlxsw_sp->core);
+
+       return err;
 }
 
 static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp)
@@ -318,6 +330,7 @@ static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp)
        const struct mlxsw_fw_rev *rev = &mlxsw_sp->bus_info->fw_rev;
        const struct mlxsw_fw_rev *req_rev = mlxsw_sp->req_rev;
        const char *fw_filename = mlxsw_sp->fw_filename;
+       union devlink_param_value value;
        const struct firmware *firmware;
        int err;
 
@@ -325,6 +338,15 @@ static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp)
        if (!req_rev || !fw_filename)
                return 0;
 
+       /* Don't check if devlink 'fw_load_policy' param is 'flash' */
+       err = devlink_param_driverinit_value_get(priv_to_devlink(mlxsw_sp->core),
+                                                DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
+                                                &value);
+       if (err)
+               return err;
+       if (value.vu8 == DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH)
+               return 0;
+
        /* Validate driver & FW are compatible */
        if (rev->major != req_rev->major) {
                WARN(1, "Mismatch in major FW version [%d:%d] is never expected; Please contact support\n",
@@ -1118,22 +1140,40 @@ int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin,
        return 0;
 }
 
-static void mlxsw_sp_port_vlan_flush(struct mlxsw_sp_port *mlxsw_sp_port)
+static void mlxsw_sp_port_vlan_flush(struct mlxsw_sp_port *mlxsw_sp_port,
+                                    bool flush_default)
 {
        struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, *tmp;
 
        list_for_each_entry_safe(mlxsw_sp_port_vlan, tmp,
-                                &mlxsw_sp_port->vlans_list, list)
-               mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
+                                &mlxsw_sp_port->vlans_list, list) {
+               if (!flush_default &&
+                   mlxsw_sp_port_vlan->vid == MLXSW_SP_DEFAULT_VID)
+                       continue;
+               mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
+       }
 }
 
-static struct mlxsw_sp_port_vlan *
+static void
+mlxsw_sp_port_vlan_cleanup(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
+{
+       if (mlxsw_sp_port_vlan->bridge_port)
+               mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
+       else if (mlxsw_sp_port_vlan->fid)
+               mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
+}
+
+struct mlxsw_sp_port_vlan *
 mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
 {
        struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
-       bool untagged = vid == 1;
+       bool untagged = vid == MLXSW_SP_DEFAULT_VID;
        int err;
 
+       mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
+       if (mlxsw_sp_port_vlan)
+               return ERR_PTR(-EEXIST);
+
        err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, untagged);
        if (err)
                return ERR_PTR(err);
@@ -1145,7 +1185,6 @@ mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
        }
 
        mlxsw_sp_port_vlan->mlxsw_sp_port = mlxsw_sp_port;
-       mlxsw_sp_port_vlan->ref_count = 1;
        mlxsw_sp_port_vlan->vid = vid;
        list_add(&mlxsw_sp_port_vlan->list, &mlxsw_sp_port->vlans_list);
 
@@ -1156,46 +1195,17 @@ err_port_vlan_alloc:
        return ERR_PTR(err);
 }
 
-static void
-mlxsw_sp_port_vlan_destroy(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
+void mlxsw_sp_port_vlan_destroy(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
        u16 vid = mlxsw_sp_port_vlan->vid;
 
+       mlxsw_sp_port_vlan_cleanup(mlxsw_sp_port_vlan);
        list_del(&mlxsw_sp_port_vlan->list);
        kfree(mlxsw_sp_port_vlan);
        mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
 }
 
-struct mlxsw_sp_port_vlan *
-mlxsw_sp_port_vlan_get(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
-{
-       struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
-
-       mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
-       if (mlxsw_sp_port_vlan) {
-               mlxsw_sp_port_vlan->ref_count++;
-               return mlxsw_sp_port_vlan;
-       }
-
-       return mlxsw_sp_port_vlan_create(mlxsw_sp_port, vid);
-}
-
-void mlxsw_sp_port_vlan_put(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
-{
-       struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
-
-       if (--mlxsw_sp_port_vlan->ref_count != 0)
-               return;
-
-       if (mlxsw_sp_port_vlan->bridge_port)
-               mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
-       else if (fid)
-               mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
-
-       mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
-}
-
 static int mlxsw_sp_port_add_vid(struct net_device *dev,
                                 __be16 __always_unused proto, u16 vid)
 {
@@ -1207,7 +1217,7 @@ static int mlxsw_sp_port_add_vid(struct net_device *dev,
        if (!vid)
                return 0;
 
-       return PTR_ERR_OR_ZERO(mlxsw_sp_port_vlan_get(mlxsw_sp_port, vid));
+       return PTR_ERR_OR_ZERO(mlxsw_sp_port_vlan_create(mlxsw_sp_port, vid));
 }
 
 static int mlxsw_sp_port_kill_vid(struct net_device *dev,
@@ -1225,7 +1235,7 @@ static int mlxsw_sp_port_kill_vid(struct net_device *dev,
        mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
        if (!mlxsw_sp_port_vlan)
                return 0;
-       mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
+       mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
 
        return 0;
 }
@@ -1876,7 +1886,37 @@ static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = {
 
 #define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats)
 
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2863_stats[] = {
+       {
+               .str = "if_in_discards",
+               .getter = mlxsw_reg_ppcnt_if_in_discards_get,
+       },
+       {
+               .str = "if_out_discards",
+               .getter = mlxsw_reg_ppcnt_if_out_discards_get,
+       },
+       {
+               .str = "if_out_errors",
+               .getter = mlxsw_reg_ppcnt_if_out_errors_get,
+       },
+};
+
+#define MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN \
+       ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2863_stats)
+
 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2819_stats[] = {
+       {
+               .str = "ether_stats_undersize_pkts",
+               .getter = mlxsw_reg_ppcnt_ether_stats_undersize_pkts_get,
+       },
+       {
+               .str = "ether_stats_oversize_pkts",
+               .getter = mlxsw_reg_ppcnt_ether_stats_oversize_pkts_get,
+       },
+       {
+               .str = "ether_stats_fragments",
+               .getter = mlxsw_reg_ppcnt_ether_stats_fragments_get,
+       },
        {
                .str = "ether_pkts64octets",
                .getter = mlxsw_reg_ppcnt_ether_stats_pkts64octets_get,
@@ -1922,6 +1962,82 @@ static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2819_stats[] = {
 #define MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN \
        ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2819_stats)
 
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_3635_stats[] = {
+       {
+               .str = "dot3stats_fcs_errors",
+               .getter = mlxsw_reg_ppcnt_dot3stats_fcs_errors_get,
+       },
+       {
+               .str = "dot3stats_symbol_errors",
+               .getter = mlxsw_reg_ppcnt_dot3stats_symbol_errors_get,
+       },
+       {
+               .str = "dot3control_in_unknown_opcodes",
+               .getter = mlxsw_reg_ppcnt_dot3control_in_unknown_opcodes_get,
+       },
+       {
+               .str = "dot3in_pause_frames",
+               .getter = mlxsw_reg_ppcnt_dot3in_pause_frames_get,
+       },
+};
+
+#define MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN \
+       ARRAY_SIZE(mlxsw_sp_port_hw_rfc_3635_stats)
+
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_discard_stats[] = {
+       {
+               .str = "discard_ingress_general",
+               .getter = mlxsw_reg_ppcnt_ingress_general_get,
+       },
+       {
+               .str = "discard_ingress_policy_engine",
+               .getter = mlxsw_reg_ppcnt_ingress_policy_engine_get,
+       },
+       {
+               .str = "discard_ingress_vlan_membership",
+               .getter = mlxsw_reg_ppcnt_ingress_vlan_membership_get,
+       },
+       {
+               .str = "discard_ingress_tag_frame_type",
+               .getter = mlxsw_reg_ppcnt_ingress_tag_frame_type_get,
+       },
+       {
+               .str = "discard_egress_vlan_membership",
+               .getter = mlxsw_reg_ppcnt_egress_vlan_membership_get,
+       },
+       {
+               .str = "discard_loopback_filter",
+               .getter = mlxsw_reg_ppcnt_loopback_filter_get,
+       },
+       {
+               .str = "discard_egress_general",
+               .getter = mlxsw_reg_ppcnt_egress_general_get,
+       },
+       {
+               .str = "discard_egress_hoq",
+               .getter = mlxsw_reg_ppcnt_egress_hoq_get,
+       },
+       {
+               .str = "discard_egress_policy_engine",
+               .getter = mlxsw_reg_ppcnt_egress_policy_engine_get,
+       },
+       {
+               .str = "discard_ingress_tx_link_down",
+               .getter = mlxsw_reg_ppcnt_ingress_tx_link_down_get,
+       },
+       {
+               .str = "discard_egress_stp_filter",
+               .getter = mlxsw_reg_ppcnt_egress_stp_filter_get,
+       },
+       {
+               .str = "discard_egress_sll",
+               .getter = mlxsw_reg_ppcnt_egress_sll_get,
+       },
+};
+
+#define MLXSW_SP_PORT_HW_DISCARD_STATS_LEN \
+       ARRAY_SIZE(mlxsw_sp_port_hw_discard_stats)
+
 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_prio_stats[] = {
        {
                .str = "rx_octets_prio",
@@ -1974,7 +2090,10 @@ static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_tc_stats[] = {
 #define MLXSW_SP_PORT_HW_TC_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_tc_stats)
 
 #define MLXSW_SP_PORT_ETHTOOL_STATS_LEN (MLXSW_SP_PORT_HW_STATS_LEN + \
+                                        MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN + \
                                         MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN + \
+                                        MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN + \
+                                        MLXSW_SP_PORT_HW_DISCARD_STATS_LEN + \
                                         (MLXSW_SP_PORT_HW_PRIO_STATS_LEN * \
                                          IEEE_8021QAZ_MAX_TCS) + \
                                         (MLXSW_SP_PORT_HW_TC_STATS_LEN * \
@@ -2015,12 +2134,31 @@ static void mlxsw_sp_port_get_strings(struct net_device *dev,
                               ETH_GSTRING_LEN);
                        p += ETH_GSTRING_LEN;
                }
+
+               for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN; i++) {
+                       memcpy(p, mlxsw_sp_port_hw_rfc_2863_stats[i].str,
+                              ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+
                for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN; i++) {
                        memcpy(p, mlxsw_sp_port_hw_rfc_2819_stats[i].str,
                               ETH_GSTRING_LEN);
                        p += ETH_GSTRING_LEN;
                }
 
+               for (i = 0; i < MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN; i++) {
+                       memcpy(p, mlxsw_sp_port_hw_rfc_3635_stats[i].str,
+                              ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+
+               for (i = 0; i < MLXSW_SP_PORT_HW_DISCARD_STATS_LEN; i++) {
+                       memcpy(p, mlxsw_sp_port_hw_discard_stats[i].str,
+                              ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+
                for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
                        mlxsw_sp_port_get_prio_strings(&p, i);
 
@@ -2063,10 +2201,22 @@ mlxsw_sp_get_hw_stats_by_group(struct mlxsw_sp_port_hw_stats **p_hw_stats,
                *p_hw_stats = mlxsw_sp_port_hw_stats;
                *p_len = MLXSW_SP_PORT_HW_STATS_LEN;
                break;
+       case MLXSW_REG_PPCNT_RFC_2863_CNT:
+               *p_hw_stats = mlxsw_sp_port_hw_rfc_2863_stats;
+               *p_len = MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN;
+               break;
        case MLXSW_REG_PPCNT_RFC_2819_CNT:
                *p_hw_stats = mlxsw_sp_port_hw_rfc_2819_stats;
                *p_len = MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN;
                break;
+       case MLXSW_REG_PPCNT_RFC_3635_CNT:
+               *p_hw_stats = mlxsw_sp_port_hw_rfc_3635_stats;
+               *p_len = MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN;
+               break;
+       case MLXSW_REG_PPCNT_DISCARD_CNT:
+               *p_hw_stats = mlxsw_sp_port_hw_discard_stats;
+               *p_len = MLXSW_SP_PORT_HW_DISCARD_STATS_LEN;
+               break;
        case MLXSW_REG_PPCNT_PRIO_CNT:
                *p_hw_stats = mlxsw_sp_port_hw_prio_stats;
                *p_len = MLXSW_SP_PORT_HW_PRIO_STATS_LEN;
@@ -2116,11 +2266,26 @@ static void mlxsw_sp_port_get_stats(struct net_device *dev,
                                  data, data_index);
        data_index = MLXSW_SP_PORT_HW_STATS_LEN;
 
+       /* RFC 2863 Counters */
+       __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2863_CNT, 0,
+                                 data, data_index);
+       data_index += MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN;
+
        /* RFC 2819 Counters */
        __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2819_CNT, 0,
                                  data, data_index);
        data_index += MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN;
 
+       /* RFC 3635 Counters */
+       __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_3635_CNT, 0,
+                                 data, data_index);
+       data_index += MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN;
+
+       /* Discard Counters */
+       __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_DISCARD_CNT, 0,
+                                 data, data_index);
+       data_index += MLXSW_SP_PORT_HW_DISCARD_STATS_LEN;
+
        /* Per-Priority Counters */
        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
                __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_PRIO_CNT, i,
@@ -2887,7 +3052,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
        mlxsw_sp_port->dev = dev;
        mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
        mlxsw_sp_port->local_port = local_port;
-       mlxsw_sp_port->pvid = 1;
+       mlxsw_sp_port->pvid = MLXSW_SP_DEFAULT_VID;
        mlxsw_sp_port->split = split;
        mlxsw_sp_port->mapping.module = module;
        mlxsw_sp_port->mapping.width = width;
@@ -3026,13 +3191,22 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
                goto err_port_nve_init;
        }
 
-       mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_get(mlxsw_sp_port, 1);
+       err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set PVID\n",
+                       mlxsw_sp_port->local_port);
+               goto err_port_pvid_set;
+       }
+
+       mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port,
+                                                      MLXSW_SP_DEFAULT_VID);
        if (IS_ERR(mlxsw_sp_port_vlan)) {
                dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to create VID 1\n",
                        mlxsw_sp_port->local_port);
                err = PTR_ERR(mlxsw_sp_port_vlan);
-               goto err_port_vlan_get;
+               goto err_port_vlan_create;
        }
+       mlxsw_sp_port->default_vlan = mlxsw_sp_port_vlan;
 
        mlxsw_sp_port_switchdev_init(mlxsw_sp_port);
        mlxsw_sp->ports[local_port] = mlxsw_sp_port;
@@ -3052,8 +3226,9 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
 err_register_netdev:
        mlxsw_sp->ports[local_port] = NULL;
        mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
-       mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
-err_port_vlan_get:
+       mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
+err_port_vlan_create:
+err_port_pvid_set:
        mlxsw_sp_port_nve_fini(mlxsw_sp_port);
 err_port_nve_init:
        mlxsw_sp_tc_qdisc_fini(mlxsw_sp_port);
@@ -3094,7 +3269,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
        unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */
        mlxsw_sp->ports[local_port] = NULL;
        mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
-       mlxsw_sp_port_vlan_flush(mlxsw_sp_port);
+       mlxsw_sp_port_vlan_flush(mlxsw_sp_port, true);
        mlxsw_sp_port_nve_fini(mlxsw_sp_port);
        mlxsw_sp_tc_qdisc_fini(mlxsw_sp_port);
        mlxsw_sp_port_fids_fini(mlxsw_sp_port);
@@ -3389,10 +3564,10 @@ static void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u8 local_port,
        return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv);
 }
 
-static void mlxsw_sp_rx_listener_mr_mark_func(struct sk_buff *skb,
+static void mlxsw_sp_rx_listener_l3_mark_func(struct sk_buff *skb,
                                              u8 local_port, void *priv)
 {
-       skb->offload_mr_fwd_mark = 1;
+       skb->offload_l3_fwd_mark = 1;
        skb->offload_fwd_mark = 1;
        return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv);
 }
@@ -3440,8 +3615,8 @@ out:
        MLXSW_RXL(mlxsw_sp_rx_listener_mark_func, _trap_id, _action,    \
                _is_ctrl, SP_##_trap_group, DISCARD)
 
-#define MLXSW_SP_RXL_MR_MARK(_trap_id, _action, _trap_group, _is_ctrl) \
-       MLXSW_RXL(mlxsw_sp_rx_listener_mr_mark_func, _trap_id, _action, \
+#define MLXSW_SP_RXL_L3_MARK(_trap_id, _action, _trap_group, _is_ctrl) \
+       MLXSW_RXL(mlxsw_sp_rx_listener_l3_mark_func, _trap_id, _action, \
                _is_ctrl, SP_##_trap_group, DISCARD)
 
 #define MLXSW_SP_EVENTL(_func, _trap_id)               \
@@ -3474,7 +3649,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = {
        /* L3 traps */
        MLXSW_SP_RXL_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false),
        MLXSW_SP_RXL_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false),
-       MLXSW_SP_RXL_MARK(LBERROR, TRAP_TO_CPU, ROUTER_EXP, false),
+       MLXSW_SP_RXL_L3_MARK(LBERROR, MIRROR_TO_CPU, LBERROR, false),
        MLXSW_SP_RXL_MARK(IP2ME, TRAP_TO_CPU, IP2ME, false),
        MLXSW_SP_RXL_MARK(IPV6_UNSPECIFIED_ADDRESS, TRAP_TO_CPU, ROUTER_EXP,
                          false),
@@ -3518,9 +3693,10 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = {
        MLXSW_SP_RXL_MARK(IPV6_PIM, TRAP_TO_CPU, PIM, false),
        MLXSW_SP_RXL_MARK(RPF, TRAP_TO_CPU, RPF, false),
        MLXSW_SP_RXL_MARK(ACL1, TRAP_TO_CPU, MULTICAST, false),
-       MLXSW_SP_RXL_MR_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false),
+       MLXSW_SP_RXL_L3_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false),
        /* NVE traps */
        MLXSW_SP_RXL_MARK(NVE_ENCAP_ARP, TRAP_TO_CPU, ARP, false),
+       MLXSW_SP_RXL_NO_MARK(NVE_DECAP_ARP, TRAP_TO_CPU, ARP, false),
 };
 
 static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)
@@ -3548,6 +3724,7 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF:
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_PIM:
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_RPF:
+               case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR:
                        rate = 128;
                        burst_size = 7;
                        break;
@@ -3633,6 +3810,7 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core)
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP:
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE:
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST:
+               case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR:
                        priority = 1;
                        tc = 1;
                        break;
@@ -3835,6 +4013,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
                goto err_nve_init;
        }
 
+       err = mlxsw_sp_acl_init(mlxsw_sp);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize ACL\n");
+               goto err_acl_init;
+       }
+
        err = mlxsw_sp_router_init(mlxsw_sp);
        if (err) {
                dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize router\n");
@@ -3852,12 +4036,6 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
                goto err_netdev_notifier;
        }
 
-       err = mlxsw_sp_acl_init(mlxsw_sp);
-       if (err) {
-               dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize ACL\n");
-               goto err_acl_init;
-       }
-
        err = mlxsw_sp_dpipe_init(mlxsw_sp);
        if (err) {
                dev_err(mlxsw_sp->bus_info->dev, "Failed to init pipeline debug\n");
@@ -3875,12 +4053,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
 err_ports_create:
        mlxsw_sp_dpipe_fini(mlxsw_sp);
 err_dpipe_init:
-       mlxsw_sp_acl_fini(mlxsw_sp);
-err_acl_init:
        unregister_netdevice_notifier(&mlxsw_sp->netdevice_nb);
 err_netdev_notifier:
        mlxsw_sp_router_fini(mlxsw_sp);
 err_router_init:
+       mlxsw_sp_acl_fini(mlxsw_sp);
+err_acl_init:
        mlxsw_sp_nve_fini(mlxsw_sp);
 err_nve_init:
        mlxsw_sp_afa_fini(mlxsw_sp);
@@ -3916,6 +4094,7 @@ static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core,
        mlxsw_sp->mr_tcam_ops = &mlxsw_sp1_mr_tcam_ops;
        mlxsw_sp->acl_tcam_ops = &mlxsw_sp1_acl_tcam_ops;
        mlxsw_sp->nve_ops_arr = mlxsw_sp1_nve_ops_arr;
+       mlxsw_sp->mac_mask = mlxsw_sp1_mac_mask;
 
        return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info);
 }
@@ -3931,6 +4110,7 @@ static int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core,
        mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops;
        mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
        mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
+       mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
 
        return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info);
 }
@@ -3941,9 +4121,9 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
 
        mlxsw_sp_ports_remove(mlxsw_sp);
        mlxsw_sp_dpipe_fini(mlxsw_sp);
-       mlxsw_sp_acl_fini(mlxsw_sp);
        unregister_netdevice_notifier(&mlxsw_sp->netdevice_nb);
        mlxsw_sp_router_fini(mlxsw_sp);
+       mlxsw_sp_acl_fini(mlxsw_sp);
        mlxsw_sp_nve_fini(mlxsw_sp);
        mlxsw_sp_afa_fini(mlxsw_sp);
        mlxsw_sp_counter_pool_fini(mlxsw_sp);
@@ -3956,16 +4136,20 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
        mlxsw_sp_kvdl_fini(mlxsw_sp);
 }
 
+/* Per-FID flood tables are used for both "true" 802.1D FIDs and emulated
+ * 802.1Q FIDs
+ */
+#define MLXSW_SP_FID_FLOOD_TABLE_SIZE  (MLXSW_SP_FID_8021D_MAX + \
+                                        VLAN_VID_MASK - 1)
+
 static const struct mlxsw_config_profile mlxsw_sp1_config_profile = {
        .used_max_mid                   = 1,
        .max_mid                        = MLXSW_SP_MID_MAX,
        .used_flood_tables              = 1,
        .used_flood_mode                = 1,
        .flood_mode                     = 3,
-       .max_fid_offset_flood_tables    = 3,
-       .fid_offset_flood_table_size    = VLAN_N_VID - 1,
        .max_fid_flood_tables           = 3,
-       .fid_flood_table_size           = MLXSW_SP_FID_8021D_MAX,
+       .fid_flood_table_size           = MLXSW_SP_FID_FLOOD_TABLE_SIZE,
        .used_max_ib_mc                 = 1,
        .max_ib_mc                      = 0,
        .used_max_pkey                  = 1,
@@ -3988,10 +4172,8 @@ static const struct mlxsw_config_profile mlxsw_sp2_config_profile = {
        .used_flood_tables              = 1,
        .used_flood_mode                = 1,
        .flood_mode                     = 3,
-       .max_fid_offset_flood_tables    = 3,
-       .fid_offset_flood_table_size    = VLAN_N_VID - 1,
        .max_fid_flood_tables           = 3,
-       .fid_flood_table_size           = MLXSW_SP_FID_8021D_MAX,
+       .fid_flood_table_size           = MLXSW_SP_FID_FLOOD_TABLE_SIZE,
        .used_max_ib_mc                 = 1,
        .max_ib_mc                      = 0,
        .used_max_pkey                  = 1,
@@ -4171,6 +4353,52 @@ static int mlxsw_sp_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
        return 0;
 }
 
+static int
+mlxsw_sp_devlink_param_fw_load_policy_validate(struct devlink *devlink, u32 id,
+                                              union devlink_param_value val,
+                                              struct netlink_ext_ack *extack)
+{
+       if ((val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER) &&
+           (val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH)) {
+               NL_SET_ERR_MSG_MOD(extack, "'fw_load_policy' must be 'driver' or 'flash'");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct devlink_param mlxsw_sp_devlink_params[] = {
+       DEVLINK_PARAM_GENERIC(FW_LOAD_POLICY,
+                             BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
+                             NULL, NULL,
+                             mlxsw_sp_devlink_param_fw_load_policy_validate),
+};
+
+static int mlxsw_sp_params_register(struct mlxsw_core *mlxsw_core)
+{
+       struct devlink *devlink = priv_to_devlink(mlxsw_core);
+       union devlink_param_value value;
+       int err;
+
+       err = devlink_params_register(devlink, mlxsw_sp_devlink_params,
+                                     ARRAY_SIZE(mlxsw_sp_devlink_params));
+       if (err)
+               return err;
+
+       value.vu8 = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER;
+       devlink_param_driverinit_value_set(devlink,
+                                          DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
+                                          value);
+       return 0;
+}
+
+static void mlxsw_sp_params_unregister(struct mlxsw_core *mlxsw_core)
+{
+       devlink_params_unregister(priv_to_devlink(mlxsw_core),
+                                 mlxsw_sp_devlink_params,
+                                 ARRAY_SIZE(mlxsw_sp_devlink_params));
+}
+
 static struct mlxsw_driver mlxsw_sp1_driver = {
        .kind                           = mlxsw_sp1_driver_name,
        .priv_size                      = sizeof(struct mlxsw_sp),
@@ -4192,6 +4420,8 @@ static struct mlxsw_driver mlxsw_sp1_driver = {
        .txhdr_construct                = mlxsw_sp_txhdr_construct,
        .resources_register             = mlxsw_sp1_resources_register,
        .kvd_sizes_get                  = mlxsw_sp_kvd_sizes_get,
+       .params_register                = mlxsw_sp_params_register,
+       .params_unregister              = mlxsw_sp_params_unregister,
        .txhdr_len                      = MLXSW_TXHDR_LEN,
        .profile                        = &mlxsw_sp1_config_profile,
        .res_query_enabled              = true,
@@ -4217,6 +4447,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,
        .txhdr_len                      = MLXSW_TXHDR_LEN,
        .profile                        = &mlxsw_sp2_config_profile,
        .res_query_enabled              = true,
@@ -4292,6 +4524,25 @@ void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port)
        dev_put(mlxsw_sp_port->dev);
 }
 
+static void
+mlxsw_sp_port_lag_uppers_cleanup(struct mlxsw_sp_port *mlxsw_sp_port,
+                                struct net_device *lag_dev)
+{
+       struct net_device *br_dev = netdev_master_upper_dev_get(lag_dev);
+       struct net_device *upper_dev;
+       struct list_head *iter;
+
+       if (netif_is_bridge_port(lag_dev))
+               mlxsw_sp_port_bridge_leave(mlxsw_sp_port, lag_dev, br_dev);
+
+       netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
+               if (!netif_is_bridge_port(upper_dev))
+                       continue;
+               br_dev = netdev_master_upper_dev_get(upper_dev);
+               mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev, br_dev);
+       }
+}
+
 static int mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
 {
        char sldr_pl[MLXSW_REG_SLDR_LEN];
@@ -4419,7 +4670,6 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
                                  struct net_device *lag_dev)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-       struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
        struct mlxsw_sp_upper *lag;
        u16 lag_id;
        u8 port_index;
@@ -4453,9 +4703,8 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
        lag->ref_count++;
 
        /* Port is no longer usable as a router interface */
-       mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, 1);
-       if (mlxsw_sp_port_vlan->fid)
-               mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
+       if (mlxsw_sp_port->default_vlan->fid)
+               mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port->default_vlan);
 
        return 0;
 
@@ -4483,7 +4732,12 @@ static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
        mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
 
        /* Any VLANs configured on the port are no longer valid */
-       mlxsw_sp_port_vlan_flush(mlxsw_sp_port);
+       mlxsw_sp_port_vlan_flush(mlxsw_sp_port, false);
+       mlxsw_sp_port_vlan_cleanup(mlxsw_sp_port->default_vlan);
+       /* Make the LAG and its directly linked uppers leave bridges they
+        * are memeber in
+        */
+       mlxsw_sp_port_lag_uppers_cleanup(mlxsw_sp_port, lag_dev);
 
        if (lag->ref_count == 1)
                mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
@@ -4493,9 +4747,8 @@ static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
        mlxsw_sp_port->lagged = 0;
        lag->ref_count--;
 
-       mlxsw_sp_port_vlan_get(mlxsw_sp_port, 1);
        /* Make sure untagged frames are allowed to ingress */
-       mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1);
+       mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID);
 }
 
 static int mlxsw_sp_lag_dist_port_add(struct mlxsw_sp_port *mlxsw_sp_port,
@@ -4573,7 +4826,7 @@ static int mlxsw_sp_port_ovs_join(struct mlxsw_sp_port *mlxsw_sp_port)
        err = mlxsw_sp_port_stp_set(mlxsw_sp_port, true);
        if (err)
                goto err_port_stp_set;
-       err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, 2, VLAN_N_VID - 1,
+       err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, 1, VLAN_N_VID - 2,
                                     true, false);
        if (err)
                goto err_port_vlan_set;
@@ -4605,7 +4858,7 @@ static void mlxsw_sp_port_ovs_leave(struct mlxsw_sp_port *mlxsw_sp_port)
                mlxsw_sp_port_vid_learning_set(mlxsw_sp_port,
                                               vid, true);
 
-       mlxsw_sp_port_vlan_set(mlxsw_sp_port, 2, VLAN_N_VID - 1,
+       mlxsw_sp_port_vlan_set(mlxsw_sp_port, 1, VLAN_N_VID - 2,
                               false, false);
        mlxsw_sp_port_stp_set(mlxsw_sp_port, false);
        mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
@@ -4625,6 +4878,30 @@ static bool mlxsw_sp_bridge_has_multiple_vxlans(struct net_device *br_dev)
        return num_vxlans > 1;
 }
 
+static bool mlxsw_sp_bridge_vxlan_vlan_is_valid(struct net_device *br_dev)
+{
+       DECLARE_BITMAP(vlans, VLAN_N_VID) = {0};
+       struct net_device *dev;
+       struct list_head *iter;
+
+       netdev_for_each_lower_dev(br_dev, dev, iter) {
+               u16 pvid;
+               int err;
+
+               if (!netif_is_vxlan(dev))
+                       continue;
+
+               err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid);
+               if (err || !pvid)
+                       continue;
+
+               if (test_and_set_bit(pvid, vlans))
+                       return false;
+       }
+
+       return true;
+}
+
 static bool mlxsw_sp_bridge_vxlan_is_valid(struct net_device *br_dev,
                                           struct netlink_ext_ack *extack)
 {
@@ -4633,13 +4910,15 @@ static bool mlxsw_sp_bridge_vxlan_is_valid(struct net_device *br_dev,
                return false;
        }
 
-       if (br_vlan_enabled(br_dev)) {
-               NL_SET_ERR_MSG_MOD(extack, "VLAN filtering can not be enabled on a bridge with a VxLAN device");
+       if (!br_vlan_enabled(br_dev) &&
+           mlxsw_sp_bridge_has_multiple_vxlans(br_dev)) {
+               NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices are not supported in a VLAN-unaware bridge");
                return false;
        }
 
-       if (mlxsw_sp_bridge_has_multiple_vxlans(br_dev)) {
-               NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices are not supported in a VLAN-unaware bridge");
+       if (br_vlan_enabled(br_dev) &&
+           !mlxsw_sp_bridge_vxlan_vlan_is_valid(br_dev)) {
+               NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices cannot have the same VLAN as PVID and egress untagged");
                return false;
        }
 
@@ -4713,11 +4992,6 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
                        NL_SET_ERR_MSG_MOD(extack, "Can not put a VLAN on an OVS port");
                        return -EINVAL;
                }
-               if (is_vlan_dev(upper_dev) &&
-                   vlan_dev_vlan_id(upper_dev) == 1) {
-                       NL_SET_ERR_MSG_MOD(extack, "Creating a VLAN device with VID 1 is unsupported: VLAN 1 carries untagged traffic");
-                       return -EINVAL;
-               }
                break;
        case NETDEV_CHANGEUPPER:
                upper_dev = info->upper_dev;
@@ -4746,6 +5020,16 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
                } else if (netif_is_macvlan(upper_dev)) {
                        if (!info->linking)
                                mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
+               } else if (is_vlan_dev(upper_dev)) {
+                       struct net_device *br_dev;
+
+                       if (!netif_is_bridge_port(upper_dev))
+                               break;
+                       if (info->linking)
+                               break;
+                       br_dev = netdev_master_upper_dev_get(upper_dev);
+                       mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev,
+                                                  br_dev);
                }
                break;
        }
@@ -4902,6 +5186,48 @@ static int mlxsw_sp_netdevice_lag_port_vlan_event(struct net_device *vlan_dev,
        return 0;
 }
 
+static int mlxsw_sp_netdevice_bridge_vlan_event(struct net_device *vlan_dev,
+                                               struct net_device *br_dev,
+                                               unsigned long event, void *ptr,
+                                               u16 vid)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(vlan_dev);
+       struct netdev_notifier_changeupper_info *info = ptr;
+       struct netlink_ext_ack *extack;
+       struct net_device *upper_dev;
+
+       if (!mlxsw_sp)
+               return 0;
+
+       extack = netdev_notifier_info_to_extack(&info->info);
+
+       switch (event) {
+       case NETDEV_PRECHANGEUPPER:
+               upper_dev = info->upper_dev;
+               if (!netif_is_macvlan(upper_dev)) {
+                       NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
+                       return -EOPNOTSUPP;
+               }
+               if (!info->linking)
+                       break;
+               if (netif_is_macvlan(upper_dev) &&
+                   !mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan_dev)) {
+                       NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
+                       return -EOPNOTSUPP;
+               }
+               break;
+       case NETDEV_CHANGEUPPER:
+               upper_dev = info->upper_dev;
+               if (info->linking)
+                       break;
+               if (netif_is_macvlan(upper_dev))
+                       mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
+               break;
+       }
+
+       return 0;
+}
+
 static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
                                         unsigned long event, void *ptr)
 {
@@ -4915,6 +5241,9 @@ static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
                return mlxsw_sp_netdevice_lag_port_vlan_event(vlan_dev,
                                                              real_dev, event,
                                                              ptr, vid);
+       else if (netif_is_bridge_master(real_dev))
+               return mlxsw_sp_netdevice_bridge_vlan_event(vlan_dev, real_dev,
+                                                           event, ptr, vid);
 
        return 0;
 }
@@ -5014,10 +5343,21 @@ static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
                if (cu_info->linking) {
                        if (!netif_running(dev))
                                return 0;
+                       /* When the bridge is VLAN-aware, the VNI of the VxLAN
+                        * device needs to be mapped to a VLAN, but at this
+                        * point no VLANs are configured on the VxLAN device
+                        */
+                       if (br_vlan_enabled(upper_dev))
+                               return 0;
                        return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev,
-                                                         dev, extack);
+                                                         dev, 0, extack);
                } else {
-                       mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, upper_dev, dev);
+                       /* VLANs were already flushed, which triggered the
+                        * necessary cleanup
+                        */
+                       if (br_vlan_enabled(upper_dev))
+                               return 0;
+                       mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, dev);
                }
                break;
        case NETDEV_PRE_UP:
@@ -5028,7 +5368,7 @@ static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
                        return 0;
                if (!mlxsw_sp_lower_get(upper_dev))
                        return 0;
-               return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev, dev,
+               return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev, dev, 0,
                                                  extack);
        case NETDEV_DOWN:
                upper_dev = netdev_master_upper_dev_get(dev);
@@ -5038,7 +5378,7 @@ static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
                        return 0;
                if (!mlxsw_sp_lower_get(upper_dev))
                        return 0;
-               mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, upper_dev, dev);
+               mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, dev);
                break;
        }
 
@@ -5069,8 +5409,10 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *nb,
        else if (mlxsw_sp_netdev_is_ipip_ul(mlxsw_sp, dev))
                err = mlxsw_sp_netdevice_ipip_ul_event(mlxsw_sp, dev,
                                                       event, ptr);
-       else if (event == NETDEV_CHANGEADDR || event == NETDEV_CHANGEMTU)
-               err = mlxsw_sp_netdevice_router_port_event(dev);
+       else if (event == NETDEV_PRE_CHANGEADDR ||
+                event == NETDEV_CHANGEADDR ||
+                event == NETDEV_CHANGEMTU)
+               err = mlxsw_sp_netdevice_router_port_event(dev, event, ptr);
        else if (mlxsw_sp_is_vrf_event(event, ptr))
                err = mlxsw_sp_netdevice_vrf_event(dev, event, ptr);
        else if (mlxsw_sp_port_dev_check(dev))
@@ -5091,18 +5433,10 @@ static struct notifier_block mlxsw_sp_inetaddr_valid_nb __read_mostly = {
        .notifier_call = mlxsw_sp_inetaddr_valid_event,
 };
 
-static struct notifier_block mlxsw_sp_inetaddr_nb __read_mostly = {
-       .notifier_call = mlxsw_sp_inetaddr_event,
-};
-
 static struct notifier_block mlxsw_sp_inet6addr_valid_nb __read_mostly = {
        .notifier_call = mlxsw_sp_inet6addr_valid_event,
 };
 
-static struct notifier_block mlxsw_sp_inet6addr_nb __read_mostly = {
-       .notifier_call = mlxsw_sp_inet6addr_event,
-};
-
 static const struct pci_device_id mlxsw_sp1_pci_id_table[] = {
        {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0},
        {0, },
@@ -5128,9 +5462,7 @@ static int __init mlxsw_sp_module_init(void)
        int err;
 
        register_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
-       register_inetaddr_notifier(&mlxsw_sp_inetaddr_nb);
        register_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
-       register_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
 
        err = mlxsw_core_driver_register(&mlxsw_sp1_driver);
        if (err)
@@ -5157,9 +5489,7 @@ err_sp1_pci_driver_register:
 err_sp2_core_driver_register:
        mlxsw_core_driver_unregister(&mlxsw_sp1_driver);
 err_sp1_core_driver_register:
-       unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
        unregister_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
-       unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb);
        unregister_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
        return err;
 }
@@ -5170,9 +5500,7 @@ static void __exit mlxsw_sp_module_exit(void)
        mlxsw_pci_driver_unregister(&mlxsw_sp1_pci_driver);
        mlxsw_core_driver_unregister(&mlxsw_sp2_driver);
        mlxsw_core_driver_unregister(&mlxsw_sp1_driver);
-       unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
        unregister_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
-       unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb);
        unregister_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
 }
 
index 0875a79..a1c32a8 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/netdevice.h>
 #include <linux/rhashtable.h>
 #include <linux/bitops.h>
+#include <linux/if_bridge.h>
 #include <linux/if_vlan.h>
 #include <linux/list.h>
 #include <linux/dcbnl.h>
@@ -24,6 +25,8 @@
 #include "core_acl_flex_actions.h"
 #include "reg.h"
 
+#define MLXSW_SP_DEFAULT_VID (VLAN_N_VID - 1)
+
 #define MLXSW_SP_FID_8021D_MAX 1024
 
 #define MLXSW_SP_MID_MAX 7000
@@ -80,6 +83,10 @@ enum mlxsw_sp_fid_type {
        MLXSW_SP_FID_TYPE_MAX,
 };
 
+enum mlxsw_sp_nve_type {
+       MLXSW_SP_NVE_TYPE_VXLAN,
+};
+
 struct mlxsw_sp_mid {
        struct list_head list;
        unsigned char addr[ETH_ALEN];
@@ -127,6 +134,7 @@ struct mlxsw_sp {
        struct mlxsw_core *core;
        const struct mlxsw_bus_info *bus_info;
        unsigned char base_mac[ETH_ALEN];
+       const unsigned char *mac_mask;
        struct mlxsw_sp_upper *lags;
        int *port_to_module;
        struct mlxsw_sp_sb *sb;
@@ -184,7 +192,6 @@ struct mlxsw_sp_port_vlan {
        struct list_head list;
        struct mlxsw_sp_port *mlxsw_sp_port;
        struct mlxsw_sp_fid *fid;
-       unsigned int ref_count;
        u16 vid;
        struct mlxsw_sp_bridge_port *bridge_port;
        struct list_head bridge_vlan_node;
@@ -235,6 +242,7 @@ struct mlxsw_sp_port {
        } periodic_hw_stats;
        struct mlxsw_sp_port_sample *sample;
        struct list_head vlans_list;
+       struct mlxsw_sp_port_vlan *default_vlan;
        struct mlxsw_sp_qdisc *root_qdisc;
        struct mlxsw_sp_qdisc *tclass_qdiscs;
        unsigned acl_rule_count;
@@ -261,6 +269,26 @@ static inline bool mlxsw_sp_bridge_has_vxlan(struct net_device *br_dev)
        return !!mlxsw_sp_bridge_vxlan_dev_find(br_dev);
 }
 
+static inline int
+mlxsw_sp_vxlan_mapped_vid(const struct net_device *vxlan_dev, u16 *p_vid)
+{
+       struct bridge_vlan_info vinfo;
+       u16 vid = 0;
+       int err;
+
+       err = br_vlan_get_pvid(vxlan_dev, &vid);
+       if (err || !vid)
+               goto out;
+
+       err = br_vlan_get_info(vxlan_dev, vid, &vinfo);
+       if (err || !(vinfo.flags & BRIDGE_VLAN_INFO_UNTAGGED))
+               vid = 0;
+
+out:
+       *p_vid = vid;
+       return err;
+}
+
 static inline bool
 mlxsw_sp_port_is_pause_en(const struct mlxsw_sp_port *mlxsw_sp_port)
 {
@@ -358,11 +386,15 @@ bool mlxsw_sp_bridge_device_is_offloaded(const struct mlxsw_sp *mlxsw_sp,
                                         const struct net_device *br_dev);
 int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
                               const struct net_device *br_dev,
-                              const struct net_device *vxlan_dev,
+                              const struct net_device *vxlan_dev, u16 vid,
                               struct netlink_ext_ack *extack);
 void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
-                                const struct net_device *br_dev,
                                 const struct net_device *vxlan_dev);
+struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp,
+                                            const struct net_device *br_dev,
+                                            u16 vid,
+                                            struct netlink_ext_ack *extack);
+extern struct notifier_block mlxsw_sp_switchdev_notifier;
 
 /* spectrum.c */
 int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
@@ -384,8 +416,8 @@ int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
                                   bool learn_enable);
 int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
 struct mlxsw_sp_port_vlan *
-mlxsw_sp_port_vlan_get(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
-void mlxsw_sp_port_vlan_put(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan);
+mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
+void mlxsw_sp_port_vlan_destroy(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan);
 int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin,
                           u16 vid_end, bool is_member, bool untagged);
 int mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp,
@@ -429,15 +461,12 @@ union mlxsw_sp_l3addr {
 
 int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp);
 void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp);
-int mlxsw_sp_netdevice_router_port_event(struct net_device *dev);
+int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
+                                        unsigned long event, void *ptr);
 void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
                              const struct net_device *macvlan_dev);
-int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
-                           unsigned long event, void *ptr);
 int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused,
                                  unsigned long event, void *ptr);
-int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
-                            unsigned long event, void *ptr);
 int mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused,
                                   unsigned long event, void *ptr);
 int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
@@ -457,7 +486,6 @@ mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,
                                 struct netdev_notifier_info *info);
 void
 mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan);
-void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif);
 void mlxsw_sp_rif_destroy_by_dev(struct mlxsw_sp *mlxsw_sp,
                                 struct net_device *dev);
 struct mlxsw_sp_rif *mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
@@ -538,6 +566,7 @@ struct mlxsw_sp_acl_rule_info {
        unsigned int priority;
        struct mlxsw_afk_element_values values;
        struct mlxsw_afa_block *act_block;
+       u8 action_created:1;
        unsigned int counter_index;
 };
 
@@ -547,6 +576,7 @@ struct mlxsw_sp_acl_ruleset;
 /* spectrum_acl.c */
 enum mlxsw_sp_acl_profile {
        MLXSW_SP_ACL_PROFILE_FLOWER,
+       MLXSW_SP_ACL_PROFILE_MR,
 };
 
 struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl);
@@ -581,7 +611,8 @@ void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp,
 u16 mlxsw_sp_acl_ruleset_group_id(struct mlxsw_sp_acl_ruleset *ruleset);
 
 struct mlxsw_sp_acl_rule_info *
-mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl);
+mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl,
+                         struct mlxsw_afa_block *afa_block);
 void mlxsw_sp_acl_rulei_destroy(struct mlxsw_sp_acl_rule_info *rulei);
 int mlxsw_sp_acl_rulei_commit(struct mlxsw_sp_acl_rule_info *rulei);
 void mlxsw_sp_acl_rulei_priority(struct mlxsw_sp_acl_rule_info *rulei,
@@ -625,6 +656,7 @@ struct mlxsw_sp_acl_rule *
 mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
                         struct mlxsw_sp_acl_ruleset *ruleset,
                         unsigned long cookie,
+                        struct mlxsw_afa_block *afa_block,
                         struct netlink_ext_ack *extack);
 void mlxsw_sp_acl_rule_destroy(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_acl_rule *rule);
@@ -632,6 +664,9 @@ int mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp,
                          struct mlxsw_sp_acl_rule *rule);
 void mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp,
                           struct mlxsw_sp_acl_rule *rule);
+int mlxsw_sp_acl_rule_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                    struct mlxsw_sp_acl_rule *rule,
+                                    struct mlxsw_afa_block *afa_block);
 struct mlxsw_sp_acl_rule *
 mlxsw_sp_acl_rule_lookup(struct mlxsw_sp *mlxsw_sp,
                         struct mlxsw_sp_acl_ruleset *ruleset,
@@ -676,6 +711,10 @@ struct mlxsw_sp_acl_tcam_ops {
        void (*entry_del)(struct mlxsw_sp *mlxsw_sp,
                          void *region_priv, void *chunk_priv,
                          void *entry_priv);
+       int (*entry_action_replace)(struct mlxsw_sp *mlxsw_sp,
+                                   void *region_priv, void *chunk_priv,
+                                   void *entry_priv,
+                                   struct mlxsw_sp_acl_rule_info *rulei);
        int (*entry_activity_get)(struct mlxsw_sp *mlxsw_sp,
                                  void *region_priv, void *entry_priv,
                                  bool *activity);
@@ -721,6 +760,12 @@ int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
                           struct tc_prio_qopt_offload *p);
 
 /* spectrum_fid.c */
+bool mlxsw_sp_fid_lag_vid_valid(const struct mlxsw_sp_fid *fid);
+struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_index(struct mlxsw_sp *mlxsw_sp,
+                                                 u16 fid_index);
+int mlxsw_sp_fid_nve_ifindex(const struct mlxsw_sp_fid *fid, int *nve_ifindex);
+int mlxsw_sp_fid_nve_type(const struct mlxsw_sp_fid *fid,
+                         enum mlxsw_sp_nve_type *p_type);
 struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp,
                                                __be32 vni);
 int mlxsw_sp_fid_vni(const struct mlxsw_sp_fid *fid, __be32 *vni);
@@ -728,9 +773,12 @@ int mlxsw_sp_fid_nve_flood_index_set(struct mlxsw_sp_fid *fid,
                                     u32 nve_flood_index);
 void mlxsw_sp_fid_nve_flood_index_clear(struct mlxsw_sp_fid *fid);
 bool mlxsw_sp_fid_nve_flood_index_is_set(const struct mlxsw_sp_fid *fid);
-int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, __be32 vni);
+int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, enum mlxsw_sp_nve_type type,
+                        __be32 vni, int nve_ifindex);
 void mlxsw_sp_fid_vni_clear(struct mlxsw_sp_fid *fid);
 bool mlxsw_sp_fid_vni_is_set(const struct mlxsw_sp_fid *fid);
+void mlxsw_sp_fid_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
+                                   const struct net_device *nve_dev);
 int mlxsw_sp_fid_flood_set(struct mlxsw_sp_fid *fid,
                           enum mlxsw_sp_flood_type packet_type, u8 local_port,
                           bool member);
@@ -738,10 +786,10 @@ int mlxsw_sp_fid_port_vid_map(struct mlxsw_sp_fid *fid,
                              struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
 void mlxsw_sp_fid_port_vid_unmap(struct mlxsw_sp_fid *fid,
                                 struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
-enum mlxsw_sp_rif_type mlxsw_sp_fid_rif_type(const struct mlxsw_sp_fid *fid);
 u16 mlxsw_sp_fid_index(const struct mlxsw_sp_fid *fid);
 enum mlxsw_sp_fid_type mlxsw_sp_fid_type(const struct mlxsw_sp_fid *fid);
 void mlxsw_sp_fid_rif_set(struct mlxsw_sp_fid *fid, struct mlxsw_sp_rif *rif);
+struct mlxsw_sp_rif *mlxsw_sp_fid_rif(const struct mlxsw_sp_fid *fid);
 enum mlxsw_sp_rif_type
 mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp,
                           enum mlxsw_sp_fid_type type);
@@ -749,6 +797,8 @@ u16 mlxsw_sp_fid_8021q_vid(const struct mlxsw_sp_fid *fid);
 struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_get(struct mlxsw_sp *mlxsw_sp, u16 vid);
 struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_get(struct mlxsw_sp *mlxsw_sp,
                                            int br_ifindex);
+struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_lookup(struct mlxsw_sp *mlxsw_sp,
+                                              u16 vid);
 struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_lookup(struct mlxsw_sp *mlxsw_sp,
                                               int br_ifindex);
 struct mlxsw_sp_fid *mlxsw_sp_fid_rfid_get(struct mlxsw_sp *mlxsw_sp,
@@ -797,10 +847,6 @@ extern const struct mlxsw_sp_mr_tcam_ops mlxsw_sp1_mr_tcam_ops;
 extern const struct mlxsw_sp_mr_tcam_ops mlxsw_sp2_mr_tcam_ops;
 
 /* spectrum_nve.c */
-enum mlxsw_sp_nve_type {
-       MLXSW_SP_NVE_TYPE_VXLAN,
-};
-
 struct mlxsw_sp_nve_params {
        enum mlxsw_sp_nve_type type;
        __be32 vni;
@@ -810,6 +856,9 @@ struct mlxsw_sp_nve_params {
 extern const struct mlxsw_sp_nve_ops *mlxsw_sp1_nve_ops_arr[];
 extern const struct mlxsw_sp_nve_ops *mlxsw_sp2_nve_ops_arr[];
 
+int mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip,
+                                   enum mlxsw_sp_l3proto proto,
+                                   union mlxsw_sp_l3addr *addr);
 int mlxsw_sp_nve_flood_ip_add(struct mlxsw_sp *mlxsw_sp,
                              struct mlxsw_sp_fid *fid,
                              enum mlxsw_sp_l3proto proto,
index 2a9eac9..fe270c1 100644 (file)
@@ -67,7 +67,7 @@ mlxsw_sp1_acl_ctcam_region_catchall_add(struct mlxsw_sp *mlxsw_sp,
        mlxsw_sp_acl_ctcam_chunk_init(&region->cregion,
                                      &region->catchall.cchunk,
                                      MLXSW_SP_ACL_TCAM_CATCHALL_PRIO);
-       rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl);
+       rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl, NULL);
        if (IS_ERR(rulei)) {
                err = PTR_ERR(rulei);
                goto err_rulei_create;
@@ -192,6 +192,15 @@ static void mlxsw_sp1_acl_tcam_entry_del(struct mlxsw_sp *mlxsw_sp,
                                     &chunk->cchunk, &entry->centry);
 }
 
+static int
+mlxsw_sp1_acl_tcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                       void *region_priv, void *chunk_priv,
+                                       void *entry_priv,
+                                       struct mlxsw_sp_acl_rule_info *rulei)
+{
+       return -EOPNOTSUPP;
+}
+
 static int
 mlxsw_sp1_acl_tcam_region_entry_activity_get(struct mlxsw_sp *mlxsw_sp,
                                             struct mlxsw_sp_acl_tcam_region *_region,
@@ -240,5 +249,6 @@ const struct mlxsw_sp_acl_tcam_ops mlxsw_sp1_acl_tcam_ops = {
        .entry_priv_size        = sizeof(struct mlxsw_sp1_acl_tcam_entry),
        .entry_add              = mlxsw_sp1_acl_tcam_entry_add,
        .entry_del              = mlxsw_sp1_acl_tcam_entry_del,
+       .entry_action_replace   = mlxsw_sp1_acl_tcam_entry_action_replace,
        .entry_activity_get     = mlxsw_sp1_acl_tcam_entry_activity_get,
 };
index 8ca77f3..234ab51 100644 (file)
@@ -34,15 +34,15 @@ mlxsw_sp2_acl_ctcam_region_entry_insert(struct mlxsw_sp_acl_ctcam_region *cregio
 {
        struct mlxsw_sp_acl_atcam_region *aregion;
        struct mlxsw_sp_acl_atcam_entry *aentry;
-       struct mlxsw_sp_acl_erp *erp;
+       struct mlxsw_sp_acl_erp_mask *erp_mask;
 
        aregion = mlxsw_sp_acl_tcam_cregion_aregion(cregion);
        aentry = mlxsw_sp_acl_tcam_centry_aentry(centry);
 
-       erp = mlxsw_sp_acl_erp_get(aregion, mask, true);
-       if (IS_ERR(erp))
-               return PTR_ERR(erp);
-       aentry->erp = erp;
+       erp_mask = mlxsw_sp_acl_erp_mask_get(aregion, mask, true);
+       if (IS_ERR(erp_mask))
+               return PTR_ERR(erp_mask);
+       aentry->erp_mask = erp_mask;
 
        return 0;
 }
@@ -57,7 +57,7 @@ mlxsw_sp2_acl_ctcam_region_entry_remove(struct mlxsw_sp_acl_ctcam_region *cregio
        aregion = mlxsw_sp_acl_tcam_cregion_aregion(cregion);
        aentry = mlxsw_sp_acl_tcam_centry_aentry(centry);
 
-       mlxsw_sp_acl_erp_put(aregion, aentry->erp);
+       mlxsw_sp_acl_erp_mask_put(aregion, aentry->erp_mask);
 }
 
 static const struct mlxsw_sp_acl_ctcam_region_ops
@@ -210,6 +210,23 @@ static void mlxsw_sp2_acl_tcam_entry_del(struct mlxsw_sp *mlxsw_sp,
                                     &entry->aentry);
 }
 
+static int
+mlxsw_sp2_acl_tcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                       void *region_priv, void *chunk_priv,
+                                       void *entry_priv,
+                                       struct mlxsw_sp_acl_rule_info *rulei)
+{
+       struct mlxsw_sp2_acl_tcam_region *region = region_priv;
+       struct mlxsw_sp2_acl_tcam_chunk *chunk = chunk_priv;
+       struct mlxsw_sp2_acl_tcam_entry *entry = entry_priv;
+
+       entry->act_block = rulei->act_block;
+       return mlxsw_sp_acl_atcam_entry_action_replace(mlxsw_sp,
+                                                      &region->aregion,
+                                                      &chunk->achunk,
+                                                      &entry->aentry, rulei);
+}
+
 static int
 mlxsw_sp2_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp,
                                      void *region_priv, void *entry_priv,
@@ -235,5 +252,6 @@ const struct mlxsw_sp_acl_tcam_ops mlxsw_sp2_acl_tcam_ops = {
        .entry_priv_size        = sizeof(struct mlxsw_sp2_acl_tcam_entry),
        .entry_add              = mlxsw_sp2_acl_tcam_entry_add,
        .entry_del              = mlxsw_sp2_acl_tcam_entry_del,
+       .entry_action_replace   = mlxsw_sp2_acl_tcam_entry_action_replace,
        .entry_activity_get     = mlxsw_sp2_acl_tcam_entry_activity_get,
 };
index 4dd6247..e31ec75 100644 (file)
@@ -7,6 +7,201 @@
 #include "spectrum.h"
 #include "spectrum_mr.h"
 
+struct mlxsw_sp2_mr_tcam {
+       struct mlxsw_sp *mlxsw_sp;
+       struct mlxsw_sp_acl_block *acl_block;
+       struct mlxsw_sp_acl_ruleset *ruleset4;
+       struct mlxsw_sp_acl_ruleset *ruleset6;
+};
+
+struct mlxsw_sp2_mr_route {
+       struct mlxsw_sp2_mr_tcam *mr_tcam;
+};
+
+static struct mlxsw_sp_acl_ruleset *
+mlxsw_sp2_mr_tcam_proto_ruleset(struct mlxsw_sp2_mr_tcam *mr_tcam,
+                               enum mlxsw_sp_l3proto proto)
+{
+       switch (proto) {
+       case MLXSW_SP_L3_PROTO_IPV4:
+               return mr_tcam->ruleset4;
+       case MLXSW_SP_L3_PROTO_IPV6:
+               return mr_tcam->ruleset6;
+       }
+       return NULL;
+}
+
+static int mlxsw_sp2_mr_tcam_bind_group(struct mlxsw_sp *mlxsw_sp,
+                                       enum mlxsw_reg_pemrbt_protocol protocol,
+                                       struct mlxsw_sp_acl_ruleset *ruleset)
+{
+       char pemrbt_pl[MLXSW_REG_PEMRBT_LEN];
+       u16 group_id;
+
+       group_id = mlxsw_sp_acl_ruleset_group_id(ruleset);
+
+       mlxsw_reg_pemrbt_pack(pemrbt_pl, protocol, group_id);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pemrbt), pemrbt_pl);
+}
+
+static const enum mlxsw_afk_element mlxsw_sp2_mr_tcam_usage_ipv4[] = {
+               MLXSW_AFK_ELEMENT_VIRT_ROUTER_8_10,
+               MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_7,
+               MLXSW_AFK_ELEMENT_SRC_IP_0_31,
+               MLXSW_AFK_ELEMENT_DST_IP_0_31,
+};
+
+static int mlxsw_sp2_mr_tcam_ipv4_init(struct mlxsw_sp2_mr_tcam *mr_tcam)
+{
+       struct mlxsw_afk_element_usage elusage;
+       int err;
+
+       /* Initialize IPv4 ACL group. */
+       mlxsw_afk_element_usage_fill(&elusage,
+                                    mlxsw_sp2_mr_tcam_usage_ipv4,
+                                    ARRAY_SIZE(mlxsw_sp2_mr_tcam_usage_ipv4));
+       mr_tcam->ruleset4 = mlxsw_sp_acl_ruleset_get(mr_tcam->mlxsw_sp,
+                                                    mr_tcam->acl_block,
+                                                    MLXSW_SP_L3_PROTO_IPV4,
+                                                    MLXSW_SP_ACL_PROFILE_MR,
+                                                    &elusage);
+
+       if (IS_ERR(mr_tcam->ruleset4))
+               return PTR_ERR(mr_tcam->ruleset4);
+
+       /* MC Router groups should be bound before routes are inserted. */
+       err = mlxsw_sp2_mr_tcam_bind_group(mr_tcam->mlxsw_sp,
+                                          MLXSW_REG_PEMRBT_PROTO_IPV4,
+                                          mr_tcam->ruleset4);
+       if (err)
+               goto err_bind_group;
+
+       return 0;
+
+err_bind_group:
+       mlxsw_sp_acl_ruleset_put(mr_tcam->mlxsw_sp, mr_tcam->ruleset4);
+       return err;
+}
+
+static void mlxsw_sp2_mr_tcam_ipv4_fini(struct mlxsw_sp2_mr_tcam *mr_tcam)
+{
+       mlxsw_sp_acl_ruleset_put(mr_tcam->mlxsw_sp, mr_tcam->ruleset4);
+}
+
+static const enum mlxsw_afk_element mlxsw_sp2_mr_tcam_usage_ipv6[] = {
+               MLXSW_AFK_ELEMENT_VIRT_ROUTER_8_10,
+               MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_7,
+               MLXSW_AFK_ELEMENT_SRC_IP_96_127,
+               MLXSW_AFK_ELEMENT_SRC_IP_64_95,
+               MLXSW_AFK_ELEMENT_SRC_IP_32_63,
+               MLXSW_AFK_ELEMENT_SRC_IP_0_31,
+               MLXSW_AFK_ELEMENT_DST_IP_96_127,
+               MLXSW_AFK_ELEMENT_DST_IP_64_95,
+               MLXSW_AFK_ELEMENT_DST_IP_32_63,
+               MLXSW_AFK_ELEMENT_DST_IP_0_31,
+};
+
+static int mlxsw_sp2_mr_tcam_ipv6_init(struct mlxsw_sp2_mr_tcam *mr_tcam)
+{
+       struct mlxsw_afk_element_usage elusage;
+       int err;
+
+       /* Initialize IPv6 ACL group */
+       mlxsw_afk_element_usage_fill(&elusage,
+                                    mlxsw_sp2_mr_tcam_usage_ipv6,
+                                    ARRAY_SIZE(mlxsw_sp2_mr_tcam_usage_ipv6));
+       mr_tcam->ruleset6 = mlxsw_sp_acl_ruleset_get(mr_tcam->mlxsw_sp,
+                                                    mr_tcam->acl_block,
+                                                    MLXSW_SP_L3_PROTO_IPV6,
+                                                    MLXSW_SP_ACL_PROFILE_MR,
+                                                    &elusage);
+
+       if (IS_ERR(mr_tcam->ruleset6))
+               return PTR_ERR(mr_tcam->ruleset6);
+
+       /* MC Router groups should be bound before routes are inserted. */
+       err = mlxsw_sp2_mr_tcam_bind_group(mr_tcam->mlxsw_sp,
+                                          MLXSW_REG_PEMRBT_PROTO_IPV6,
+                                          mr_tcam->ruleset6);
+       if (err)
+               goto err_bind_group;
+
+       return 0;
+
+err_bind_group:
+       mlxsw_sp_acl_ruleset_put(mr_tcam->mlxsw_sp, mr_tcam->ruleset6);
+       return err;
+}
+
+static void mlxsw_sp2_mr_tcam_ipv6_fini(struct mlxsw_sp2_mr_tcam *mr_tcam)
+{
+       mlxsw_sp_acl_ruleset_put(mr_tcam->mlxsw_sp, mr_tcam->ruleset6);
+}
+
+static void
+mlxsw_sp2_mr_tcam_rule_parse4(struct mlxsw_sp_acl_rule_info *rulei,
+                             struct mlxsw_sp_mr_route_key *key)
+{
+       mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
+                                      (char *) &key->source.addr4,
+                                      (char *) &key->source_mask.addr4, 4);
+       mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
+                                      (char *) &key->group.addr4,
+                                      (char *) &key->group_mask.addr4, 4);
+}
+
+static void
+mlxsw_sp2_mr_tcam_rule_parse6(struct mlxsw_sp_acl_rule_info *rulei,
+                             struct mlxsw_sp_mr_route_key *key)
+{
+       mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_96_127,
+                                      &key->source.addr6.s6_addr[0x0],
+                                      &key->source_mask.addr6.s6_addr[0x0], 4);
+       mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_64_95,
+                                      &key->source.addr6.s6_addr[0x4],
+                                      &key->source_mask.addr6.s6_addr[0x4], 4);
+       mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_32_63,
+                                      &key->source.addr6.s6_addr[0x8],
+                                      &key->source_mask.addr6.s6_addr[0x8], 4);
+       mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
+                                      &key->source.addr6.s6_addr[0xc],
+                                      &key->source_mask.addr6.s6_addr[0xc], 4);
+       mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_96_127,
+                                      &key->group.addr6.s6_addr[0x0],
+                                      &key->group_mask.addr6.s6_addr[0x0], 4);
+       mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_64_95,
+                                      &key->group.addr6.s6_addr[0x4],
+                                      &key->group_mask.addr6.s6_addr[0x4], 4);
+       mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_32_63,
+                                      &key->group.addr6.s6_addr[0x8],
+                                      &key->group_mask.addr6.s6_addr[0x8], 4);
+       mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
+                                      &key->group.addr6.s6_addr[0xc],
+                                      &key->group_mask.addr6.s6_addr[0xc], 4);
+}
+
+static void
+mlxsw_sp2_mr_tcam_rule_parse(struct mlxsw_sp_acl_rule *rule,
+                            struct mlxsw_sp_mr_route_key *key,
+                            unsigned int priority)
+{
+       struct mlxsw_sp_acl_rule_info *rulei;
+
+       rulei = mlxsw_sp_acl_rule_rulei(rule);
+       rulei->priority = priority;
+       mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_7,
+                                      key->vrid, GENMASK(7, 0));
+       mlxsw_sp_acl_rulei_keymask_u32(rulei,
+                                      MLXSW_AFK_ELEMENT_VIRT_ROUTER_8_10,
+                                      key->vrid >> 8, GENMASK(2, 0));
+       switch (key->proto) {
+       case MLXSW_SP_L3_PROTO_IPV4:
+               return mlxsw_sp2_mr_tcam_rule_parse4(rulei, key);
+       case MLXSW_SP_L3_PROTO_IPV6:
+               return mlxsw_sp2_mr_tcam_rule_parse6(rulei, key);
+       }
+}
+
 static int
 mlxsw_sp2_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv,
                               void *route_priv,
@@ -14,7 +209,33 @@ mlxsw_sp2_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv,
                               struct mlxsw_afa_block *afa_block,
                               enum mlxsw_sp_mr_route_prio prio)
 {
+       struct mlxsw_sp2_mr_route *mr_route = route_priv;
+       struct mlxsw_sp2_mr_tcam *mr_tcam = priv;
+       struct mlxsw_sp_acl_ruleset *ruleset;
+       struct mlxsw_sp_acl_rule *rule;
+       int err;
+
+       mr_route->mr_tcam = mr_tcam;
+       ruleset = mlxsw_sp2_mr_tcam_proto_ruleset(mr_tcam, key->proto);
+       if (WARN_ON(!ruleset))
+               return -EINVAL;
+
+       rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset,
+                                       (unsigned long) route_priv, afa_block,
+                                       NULL);
+       if (IS_ERR(rule))
+               return PTR_ERR(rule);
+
+       mlxsw_sp2_mr_tcam_rule_parse(rule, key, prio);
+       err = mlxsw_sp_acl_rule_add(mlxsw_sp, rule);
+       if (err)
+               goto err_rule_add;
+
        return 0;
+
+err_rule_add:
+       mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
+       return err;
 }
 
 static void
@@ -22,6 +243,21 @@ mlxsw_sp2_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp, void *priv,
                                void *route_priv,
                                struct mlxsw_sp_mr_route_key *key)
 {
+       struct mlxsw_sp2_mr_tcam *mr_tcam = priv;
+       struct mlxsw_sp_acl_ruleset *ruleset;
+       struct mlxsw_sp_acl_rule *rule;
+
+       ruleset = mlxsw_sp2_mr_tcam_proto_ruleset(mr_tcam, key->proto);
+       if (WARN_ON(!ruleset))
+               return;
+
+       rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset,
+                                       (unsigned long) route_priv);
+       if (WARN_ON(!rule))
+               return;
+
+       mlxsw_sp_acl_rule_del(mlxsw_sp, rule);
+       mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
 }
 
 static int
@@ -30,21 +266,64 @@ mlxsw_sp2_mr_tcam_route_update(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_mr_route_key *key,
                               struct mlxsw_afa_block *afa_block)
 {
-       return 0;
+       struct mlxsw_sp2_mr_route *mr_route = route_priv;
+       struct mlxsw_sp2_mr_tcam *mr_tcam = mr_route->mr_tcam;
+       struct mlxsw_sp_acl_ruleset *ruleset;
+       struct mlxsw_sp_acl_rule *rule;
+
+       ruleset = mlxsw_sp2_mr_tcam_proto_ruleset(mr_tcam, key->proto);
+       if (WARN_ON(!ruleset))
+               return -EINVAL;
+
+       rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset,
+                                       (unsigned long) route_priv);
+       if (WARN_ON(!rule))
+               return -EINVAL;
+
+       return mlxsw_sp_acl_rule_action_replace(mlxsw_sp, rule, afa_block);
 }
 
 static int mlxsw_sp2_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)
 {
+       struct mlxsw_sp2_mr_tcam *mr_tcam = priv;
+       int err;
+
+       mr_tcam->mlxsw_sp = mlxsw_sp;
+       mr_tcam->acl_block = mlxsw_sp_acl_block_create(mlxsw_sp, NULL);
+       if (!mr_tcam->acl_block)
+               return -ENOMEM;
+
+       err = mlxsw_sp2_mr_tcam_ipv4_init(mr_tcam);
+       if (err)
+               goto err_ipv4_init;
+
+       err = mlxsw_sp2_mr_tcam_ipv6_init(mr_tcam);
+       if (err)
+               goto err_ipv6_init;
+
        return 0;
+
+err_ipv6_init:
+       mlxsw_sp2_mr_tcam_ipv4_fini(mr_tcam);
+err_ipv4_init:
+       mlxsw_sp_acl_block_destroy(mr_tcam->acl_block);
+       return err;
 }
 
 static void mlxsw_sp2_mr_tcam_fini(void *priv)
 {
+       struct mlxsw_sp2_mr_tcam *mr_tcam = priv;
+
+       mlxsw_sp2_mr_tcam_ipv6_fini(mr_tcam);
+       mlxsw_sp2_mr_tcam_ipv4_fini(mr_tcam);
+       mlxsw_sp_acl_block_destroy(mr_tcam->acl_block);
 }
 
 const struct mlxsw_sp_mr_tcam_ops mlxsw_sp2_mr_tcam_ops = {
+       .priv_size = sizeof(struct mlxsw_sp2_mr_tcam),
        .init = mlxsw_sp2_mr_tcam_init,
        .fini = mlxsw_sp2_mr_tcam_fini,
+       .route_priv_size = sizeof(struct mlxsw_sp2_mr_route),
        .route_create = mlxsw_sp2_mr_tcam_route_create,
        .route_destroy = mlxsw_sp2_mr_tcam_route_destroy,
        .route_update = mlxsw_sp2_mr_tcam_route_update,
index c4f9238..695d333 100644 (file)
@@ -435,7 +435,8 @@ u16 mlxsw_sp_acl_ruleset_group_id(struct mlxsw_sp_acl_ruleset *ruleset)
 }
 
 struct mlxsw_sp_acl_rule_info *
-mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl)
+mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl,
+                         struct mlxsw_afa_block *afa_block)
 {
        struct mlxsw_sp_acl_rule_info *rulei;
        int err;
@@ -443,11 +444,18 @@ mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl)
        rulei = kzalloc(sizeof(*rulei), GFP_KERNEL);
        if (!rulei)
                return NULL;
+
+       if (afa_block) {
+               rulei->act_block = afa_block;
+               return rulei;
+       }
+
        rulei->act_block = mlxsw_afa_block_create(acl->mlxsw_sp->afa);
        if (IS_ERR(rulei->act_block)) {
                err = PTR_ERR(rulei->act_block);
                goto err_afa_block_create;
        }
+       rulei->action_created = 1;
        return rulei;
 
 err_afa_block_create:
@@ -457,7 +465,8 @@ err_afa_block_create:
 
 void mlxsw_sp_acl_rulei_destroy(struct mlxsw_sp_acl_rule_info *rulei)
 {
-       mlxsw_afa_block_destroy(rulei->act_block);
+       if (rulei->action_created)
+               mlxsw_afa_block_destroy(rulei->act_block);
        kfree(rulei);
 }
 
@@ -623,6 +632,7 @@ struct mlxsw_sp_acl_rule *
 mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
                         struct mlxsw_sp_acl_ruleset *ruleset,
                         unsigned long cookie,
+                        struct mlxsw_afa_block *afa_block,
                         struct netlink_ext_ack *extack)
 {
        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
@@ -639,7 +649,7 @@ mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
        rule->cookie = cookie;
        rule->ruleset = ruleset;
 
-       rule->rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl);
+       rule->rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl, afa_block);
        if (IS_ERR(rule->rulei)) {
                err = PTR_ERR(rule->rulei);
                goto err_rulei_create;
@@ -721,6 +731,21 @@ void mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp,
        ops->rule_del(mlxsw_sp, rule->priv);
 }
 
+int mlxsw_sp_acl_rule_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                    struct mlxsw_sp_acl_rule *rule,
+                                    struct mlxsw_afa_block *afa_block)
+{
+       struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
+       const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
+       struct mlxsw_sp_acl_rule_info *rulei;
+
+       rulei = mlxsw_sp_acl_rule_rulei(rule);
+       rulei->act_block = afa_block;
+
+       return ops->rule_action_replace(mlxsw_sp, ruleset->priv, rule->priv,
+                                       rule->rulei);
+}
+
 struct mlxsw_sp_acl_rule *
 mlxsw_sp_acl_rule_lookup(struct mlxsw_sp *mlxsw_sp,
                         struct mlxsw_sp_acl_ruleset *ruleset,
index 2dda028..80fb268 100644 (file)
@@ -14,8 +14,8 @@
 #include "spectrum_acl_tcam.h"
 #include "core_acl_flex_keys.h"
 
-#define MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_START 6
-#define MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_END   11
+#define MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_CLEAR_START   0
+#define MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_CLEAR_END     5
 
 struct mlxsw_sp_acl_atcam_lkey_id_ht_key {
        char enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* MSB blocks */
@@ -34,7 +34,7 @@ struct mlxsw_sp_acl_atcam_region_ops {
        void (*fini)(struct mlxsw_sp_acl_atcam_region *aregion);
        struct mlxsw_sp_acl_atcam_lkey_id *
                (*lkey_id_get)(struct mlxsw_sp_acl_atcam_region *aregion,
-                              struct mlxsw_sp_acl_rule_info *rulei, u8 erp_id);
+                              char *enc_key, u8 erp_id);
        void (*lkey_id_put)(struct mlxsw_sp_acl_atcam_region *aregion,
                            struct mlxsw_sp_acl_atcam_lkey_id *lkey_id);
 };
@@ -64,7 +64,7 @@ static const struct rhashtable_params mlxsw_sp_acl_atcam_entries_ht_params = {
 static bool
 mlxsw_sp_acl_atcam_is_centry(const struct mlxsw_sp_acl_atcam_entry *aentry)
 {
-       return mlxsw_sp_acl_erp_is_ctcam_erp(aentry->erp);
+       return mlxsw_sp_acl_erp_mask_is_ctcam(aentry->erp_mask);
 }
 
 static int
@@ -90,8 +90,7 @@ mlxsw_sp_acl_atcam_region_generic_fini(struct mlxsw_sp_acl_atcam_region *aregion
 
 static struct mlxsw_sp_acl_atcam_lkey_id *
 mlxsw_sp_acl_atcam_generic_lkey_id_get(struct mlxsw_sp_acl_atcam_region *aregion,
-                                      struct mlxsw_sp_acl_rule_info *rulei,
-                                      u8 erp_id)
+                                      char *enc_key, u8 erp_id)
 {
        struct mlxsw_sp_acl_atcam_region_generic *region_generic;
 
@@ -220,8 +219,7 @@ mlxsw_sp_acl_atcam_lkey_id_destroy(struct mlxsw_sp_acl_atcam_region *aregion,
 
 static struct mlxsw_sp_acl_atcam_lkey_id *
 mlxsw_sp_acl_atcam_12kb_lkey_id_get(struct mlxsw_sp_acl_atcam_region *aregion,
-                                   struct mlxsw_sp_acl_rule_info *rulei,
-                                   u8 erp_id)
+                                   char *enc_key, u8 erp_id)
 {
        struct mlxsw_sp_acl_atcam_region_12kb *region_12kb = aregion->priv;
        struct mlxsw_sp_acl_tcam_region *region = aregion->region;
@@ -230,9 +228,10 @@ mlxsw_sp_acl_atcam_12kb_lkey_id_get(struct mlxsw_sp_acl_atcam_region *aregion,
        struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);
        struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
 
-       mlxsw_afk_encode(afk, region->key_info, &rulei->values, ht_key.enc_key,
-                        NULL, MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_START,
-                        MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_END);
+       memcpy(ht_key.enc_key, enc_key, sizeof(ht_key.enc_key));
+       mlxsw_afk_clear(afk, ht_key.enc_key,
+                       MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_CLEAR_START,
+                       MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_CLEAR_END);
        ht_key.erp_id = erp_id;
        lkey_id = rhashtable_lookup_fast(&region_12kb->lkey_ht, &ht_key,
                                         mlxsw_sp_acl_atcam_lkey_id_ht_params);
@@ -324,6 +323,7 @@ mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
        aregion->region = region;
        aregion->atcam = atcam;
        mlxsw_sp_acl_atcam_region_type_init(aregion);
+       INIT_LIST_HEAD(&aregion->entries_list);
 
        err = rhashtable_init(&aregion->entries_ht,
                              &mlxsw_sp_acl_atcam_entries_ht_params);
@@ -357,6 +357,7 @@ void mlxsw_sp_acl_atcam_region_fini(struct mlxsw_sp_acl_atcam_region *aregion)
        mlxsw_sp_acl_erp_region_fini(aregion);
        aregion->ops->fini(aregion);
        rhashtable_destroy(&aregion->entries_ht);
+       WARN_ON(!list_empty(&aregion->entries_list));
 }
 
 void mlxsw_sp_acl_atcam_chunk_init(struct mlxsw_sp_acl_atcam_region *aregion,
@@ -379,7 +380,7 @@ mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
                                       struct mlxsw_sp_acl_rule_info *rulei)
 {
        struct mlxsw_sp_acl_tcam_region *region = aregion->region;
-       u8 erp_id = mlxsw_sp_acl_erp_id(aentry->erp);
+       u8 erp_id = mlxsw_sp_acl_erp_mask_erp_id(aentry->erp_mask);
        struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
        char ptce3_pl[MLXSW_REG_PTCE3_LEN];
        u32 kvdl_index, priority;
@@ -389,7 +390,8 @@ mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
        if (err)
                return err;
 
-       lkey_id = aregion->ops->lkey_id_get(aregion, rulei, erp_id);
+       lkey_id = aregion->ops->lkey_id_get(aregion, aentry->ht_key.enc_key,
+                                           erp_id);
        if (IS_ERR(lkey_id))
                return PTR_ERR(lkey_id);
        aentry->lkey_id = lkey_id;
@@ -398,6 +400,9 @@ mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
        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->delta_info.start,
+                            aentry->delta_info.mask,
+                            aentry->delta_info.value,
                             refcount_read(&lkey_id->refcnt) != 1, lkey_id->id,
                             kvdl_index);
        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce3), ptce3_pl);
@@ -418,17 +423,50 @@ 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_id(aentry->erp);
+       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, aentry->ht_key.enc_key,
-                            erp_id, refcount_read(&lkey_id->refcnt) != 1,
+                            region->tcam_region_info,
+                            enc_key, erp_id,
+                            aentry->delta_info.start,
+                            aentry->delta_info.mask,
+                            aentry->delta_info.value,
+                            refcount_read(&lkey_id->refcnt) != 1,
                             lkey_id->id, 0);
        mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce3), ptce3_pl);
        aregion->ops->lkey_id_put(aregion, lkey_id);
 }
 
+static int
+mlxsw_sp_acl_atcam_region_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                              struct mlxsw_sp_acl_atcam_region *aregion,
+                                              struct mlxsw_sp_acl_atcam_entry *aentry,
+                                              struct mlxsw_sp_acl_rule_info *rulei)
+{
+       struct mlxsw_sp_acl_atcam_lkey_id *lkey_id = aentry->lkey_id;
+       u8 erp_id = mlxsw_sp_acl_erp_mask_erp_id(aentry->erp_mask);
+       struct mlxsw_sp_acl_tcam_region *region = aregion->region;
+       char ptce3_pl[MLXSW_REG_PTCE3_LEN];
+       u32 kvdl_index, priority;
+       int err;
+
+       err = mlxsw_sp_acl_tcam_priority_get(mlxsw_sp, rulei, &priority, true);
+       if (err)
+               return err;
+       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->delta_info.start,
+                            aentry->delta_info.mask,
+                            aentry->delta_info.value,
+                            refcount_read(&lkey_id->refcnt) != 1, lkey_id->id,
+                            kvdl_index);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce3), ptce3_pl);
+}
+
 static int
 __mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_acl_atcam_region *aregion,
@@ -438,19 +476,36 @@ __mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
        struct mlxsw_sp_acl_tcam_region *region = aregion->region;
        char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN] = { 0 };
        struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);
-       struct mlxsw_sp_acl_erp *erp;
-       unsigned int blocks_count;
+       const struct mlxsw_sp_acl_erp_delta *delta;
+       struct mlxsw_sp_acl_erp_mask *erp_mask;
        int err;
 
-       blocks_count = mlxsw_afk_key_info_blocks_count_get(region->key_info);
        mlxsw_afk_encode(afk, region->key_info, &rulei->values,
-                        aentry->ht_key.enc_key, mask, 0, blocks_count - 1);
-
-       erp = mlxsw_sp_acl_erp_get(aregion, mask, false);
-       if (IS_ERR(erp))
-               return PTR_ERR(erp);
-       aentry->erp = erp;
-       aentry->ht_key.erp_id = mlxsw_sp_acl_erp_id(erp);
+                        aentry->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));
+
+       /* Compute all needed delta information and clear the delta bits
+        * from the encrypted key.
+        */
+       delta = mlxsw_sp_acl_erp_delta(aentry->erp_mask);
+       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);
+
+       /* Add rule to the list of A-TCAM rules, assuming this
+        * rule is intended to A-TCAM. In case this rule does
+        * not fit into A-TCAM it will be removed from the list.
+        */
+       list_add(&aentry->list, &aregion->entries_list);
 
        /* We can't insert identical rules into the A-TCAM, so fail and
         * let the rule spill into C-TCAM
@@ -461,6 +516,13 @@ __mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_rhashtable_insert;
 
+       /* Bloom filter must be updated here, before inserting the rule into
+        * the A-TCAM.
+        */
+       err = mlxsw_sp_acl_erp_bf_insert(mlxsw_sp, aregion, erp_mask, aentry);
+       if (err)
+               goto err_bf_insert;
+
        err = mlxsw_sp_acl_atcam_region_entry_insert(mlxsw_sp, aregion, aentry,
                                                     rulei);
        if (err)
@@ -469,10 +531,13 @@ __mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
        return 0;
 
 err_rule_insert:
+       mlxsw_sp_acl_erp_bf_remove(mlxsw_sp, aregion, erp_mask, aentry);
+err_bf_insert:
        rhashtable_remove_fast(&aregion->entries_ht, &aentry->ht_node,
                               mlxsw_sp_acl_atcam_entries_ht_params);
 err_rhashtable_insert:
-       mlxsw_sp_acl_erp_put(aregion, erp);
+       list_del(&aentry->list);
+       mlxsw_sp_acl_erp_mask_put(aregion, erp_mask);
        return err;
 }
 
@@ -482,9 +547,21 @@ __mlxsw_sp_acl_atcam_entry_del(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_acl_atcam_entry *aentry)
 {
        mlxsw_sp_acl_atcam_region_entry_remove(mlxsw_sp, aregion, aentry);
+       mlxsw_sp_acl_erp_bf_remove(mlxsw_sp, aregion, aentry->erp_mask, aentry);
        rhashtable_remove_fast(&aregion->entries_ht, &aentry->ht_node,
                               mlxsw_sp_acl_atcam_entries_ht_params);
-       mlxsw_sp_acl_erp_put(aregion, aentry->erp);
+       list_del(&aentry->list);
+       mlxsw_sp_acl_erp_mask_put(aregion, aentry->erp_mask);
+}
+
+static int
+__mlxsw_sp_acl_atcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                         struct mlxsw_sp_acl_atcam_region *aregion,
+                                         struct mlxsw_sp_acl_atcam_entry *aentry,
+                                         struct mlxsw_sp_acl_rule_info *rulei)
+{
+       return mlxsw_sp_acl_atcam_region_entry_action_replace(mlxsw_sp, aregion,
+                                                             aentry, rulei);
 }
 
 int mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
@@ -523,6 +600,29 @@ void mlxsw_sp_acl_atcam_entry_del(struct mlxsw_sp *mlxsw_sp,
                __mlxsw_sp_acl_atcam_entry_del(mlxsw_sp, aregion, aentry);
 }
 
+int
+mlxsw_sp_acl_atcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                       struct mlxsw_sp_acl_atcam_region *aregion,
+                                       struct mlxsw_sp_acl_atcam_chunk *achunk,
+                                       struct mlxsw_sp_acl_atcam_entry *aentry,
+                                       struct mlxsw_sp_acl_rule_info *rulei)
+{
+       int err;
+
+       if (mlxsw_sp_acl_atcam_is_centry(aentry))
+               err = mlxsw_sp_acl_ctcam_entry_action_replace(mlxsw_sp,
+                                                             &aregion->cregion,
+                                                             &achunk->cchunk,
+                                                             &aentry->centry,
+                                                             rulei);
+       else
+               err = __mlxsw_sp_acl_atcam_entry_action_replace(mlxsw_sp,
+                                                               aregion, aentry,
+                                                               rulei);
+
+       return err;
+}
+
 int mlxsw_sp_acl_atcam_init(struct mlxsw_sp *mlxsw_sp,
                            struct mlxsw_sp_acl_atcam *atcam)
 {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_bloom_filter.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_bloom_filter.c
new file mode 100644 (file)
index 0000000..505b878
--- /dev/null
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/refcount.h>
+
+#include "spectrum.h"
+#include "spectrum_acl_tcam.h"
+
+struct mlxsw_sp_acl_bf {
+       unsigned int bank_size;
+       refcount_t refcnt[0];
+};
+
+/* Bloom filter uses a crc-16 hash over chunks of data which contain 4 key
+ * blocks, eRP ID and region ID. In Spectrum-2, region key is combined of up to
+ * 12 key blocks, so there can be up to 3 chunks in the Bloom filter key,
+ * depending on the actual number of key blocks used in the region.
+ * The layout of the Bloom filter key is as follows:
+ *
+ * +-------------------------+------------------------+------------------------+
+ * | Chunk 2 Key blocks 11-8 | Chunk 1 Key blocks 7-4 | Chunk 0 Key blocks 3-0 |
+ * +-------------------------+------------------------+------------------------+
+ */
+#define MLXSW_BLOOM_KEY_CHUNKS 3
+#define MLXSW_BLOOM_KEY_LEN 69
+
+/* Each chunk size is 23 bytes. 18 bytes of it contain 4 key blocks, each is
+ * 36 bits, 2 bytes which hold eRP ID and region ID, and 3 bytes of zero
+ * padding.
+ * The layout of each chunk is as follows:
+ *
+ * +---------+----------------------+-----------------------------------+
+ * | 3 bytes |        2 bytes       |              18 bytes             |
+ * +---------+-----------+----------+-----------------------------------+
+ * | 183:158 |  157:148  | 147:144  |               143:0               |
+ * +---------+-----------+----------+-----------------------------------+
+ * |    0    | region ID |  eRP ID  |      4 Key blocks (18 Bytes)      |
+ * +---------+-----------+----------+-----------------------------------+
+ */
+#define MLXSW_BLOOM_CHUNK_PAD_BYTES 3
+#define MLXSW_BLOOM_CHUNK_KEY_BYTES 18
+#define MLXSW_BLOOM_KEY_CHUNK_BYTES 23
+
+/* The offset of the key block within a chunk is 5 bytes as it comes after
+ * 3 bytes of zero padding and 16 bits of region ID and eRP ID.
+ */
+#define MLXSW_BLOOM_CHUNK_KEY_OFFSET 5
+
+/* Each chunk contains 4 key blocks. Chunk 2 uses key blocks 11-8,
+ * and we need to populate it with 4 key blocks copied from the entry encoded
+ * key. Since the encoded key contains a padding, key block 11 starts at offset
+ * 2. block 7 that is used in chunk 1 starts at offset 20 as 4 key blocks take
+ * 18 bytes.
+ * This array defines key offsets for easy access when copying key blocks from
+ * entry key to Bloom filter chunk.
+ */
+static const u8 chunk_key_offsets[MLXSW_BLOOM_KEY_CHUNKS] = {2, 20, 38};
+
+/* This table is just the CRC of each possible byte. It is
+ * computed, Msbit first, for the Bloom filter polynomial
+ * which is 0x8529 (1 + x^3 + x^5 + x^8 + x^10 + x^15 and
+ * the implicit x^16).
+ */
+static const u16 mlxsw_sp_acl_bf_crc_tab[256] = {
+0x0000, 0x8529, 0x8f7b, 0x0a52, 0x9bdf, 0x1ef6, 0x14a4, 0x918d,
+0xb297, 0x37be, 0x3dec, 0xb8c5, 0x2948, 0xac61, 0xa633, 0x231a,
+0xe007, 0x652e, 0x6f7c, 0xea55, 0x7bd8, 0xfef1, 0xf4a3, 0x718a,
+0x5290, 0xd7b9, 0xddeb, 0x58c2, 0xc94f, 0x4c66, 0x4634, 0xc31d,
+0x4527, 0xc00e, 0xca5c, 0x4f75, 0xdef8, 0x5bd1, 0x5183, 0xd4aa,
+0xf7b0, 0x7299, 0x78cb, 0xfde2, 0x6c6f, 0xe946, 0xe314, 0x663d,
+0xa520, 0x2009, 0x2a5b, 0xaf72, 0x3eff, 0xbbd6, 0xb184, 0x34ad,
+0x17b7, 0x929e, 0x98cc, 0x1de5, 0x8c68, 0x0941, 0x0313, 0x863a,
+0x8a4e, 0x0f67, 0x0535, 0x801c, 0x1191, 0x94b8, 0x9eea, 0x1bc3,
+0x38d9, 0xbdf0, 0xb7a2, 0x328b, 0xa306, 0x262f, 0x2c7d, 0xa954,
+0x6a49, 0xef60, 0xe532, 0x601b, 0xf196, 0x74bf, 0x7eed, 0xfbc4,
+0xd8de, 0x5df7, 0x57a5, 0xd28c, 0x4301, 0xc628, 0xcc7a, 0x4953,
+0xcf69, 0x4a40, 0x4012, 0xc53b, 0x54b6, 0xd19f, 0xdbcd, 0x5ee4,
+0x7dfe, 0xf8d7, 0xf285, 0x77ac, 0xe621, 0x6308, 0x695a, 0xec73,
+0x2f6e, 0xaa47, 0xa015, 0x253c, 0xb4b1, 0x3198, 0x3bca, 0xbee3,
+0x9df9, 0x18d0, 0x1282, 0x97ab, 0x0626, 0x830f, 0x895d, 0x0c74,
+0x91b5, 0x149c, 0x1ece, 0x9be7, 0x0a6a, 0x8f43, 0x8511, 0x0038,
+0x2322, 0xa60b, 0xac59, 0x2970, 0xb8fd, 0x3dd4, 0x3786, 0xb2af,
+0x71b2, 0xf49b, 0xfec9, 0x7be0, 0xea6d, 0x6f44, 0x6516, 0xe03f,
+0xc325, 0x460c, 0x4c5e, 0xc977, 0x58fa, 0xddd3, 0xd781, 0x52a8,
+0xd492, 0x51bb, 0x5be9, 0xdec0, 0x4f4d, 0xca64, 0xc036, 0x451f,
+0x6605, 0xe32c, 0xe97e, 0x6c57, 0xfdda, 0x78f3, 0x72a1, 0xf788,
+0x3495, 0xb1bc, 0xbbee, 0x3ec7, 0xaf4a, 0x2a63, 0x2031, 0xa518,
+0x8602, 0x032b, 0x0979, 0x8c50, 0x1ddd, 0x98f4, 0x92a6, 0x178f,
+0x1bfb, 0x9ed2, 0x9480, 0x11a9, 0x8024, 0x050d, 0x0f5f, 0x8a76,
+0xa96c, 0x2c45, 0x2617, 0xa33e, 0x32b3, 0xb79a, 0xbdc8, 0x38e1,
+0xfbfc, 0x7ed5, 0x7487, 0xf1ae, 0x6023, 0xe50a, 0xef58, 0x6a71,
+0x496b, 0xcc42, 0xc610, 0x4339, 0xd2b4, 0x579d, 0x5dcf, 0xd8e6,
+0x5edc, 0xdbf5, 0xd1a7, 0x548e, 0xc503, 0x402a, 0x4a78, 0xcf51,
+0xec4b, 0x6962, 0x6330, 0xe619, 0x7794, 0xf2bd, 0xf8ef, 0x7dc6,
+0xbedb, 0x3bf2, 0x31a0, 0xb489, 0x2504, 0xa02d, 0xaa7f, 0x2f56,
+0x0c4c, 0x8965, 0x8337, 0x061e, 0x9793, 0x12ba, 0x18e8, 0x9dc1,
+};
+
+static u16 mlxsw_sp_acl_bf_crc_byte(u16 crc, u8 c)
+{
+       return (crc << 8) ^ mlxsw_sp_acl_bf_crc_tab[(crc >> 8) ^ c];
+}
+
+static u16 mlxsw_sp_acl_bf_crc(const u8 *buffer, size_t len)
+{
+       u16 crc = 0;
+
+       while (len--)
+               crc = mlxsw_sp_acl_bf_crc_byte(crc, *buffer++);
+       return crc;
+}
+
+static void
+mlxsw_sp_acl_bf_key_encode(struct mlxsw_sp_acl_atcam_region *aregion,
+                          struct mlxsw_sp_acl_atcam_entry *aentry,
+                          char *output, u8 *len)
+{
+       struct mlxsw_afk_key_info *key_info = aregion->region->key_info;
+       u8 chunk_index, chunk_count, block_count;
+       char *chunk = output;
+       __be16 erp_region_id;
+
+       block_count = mlxsw_afk_key_info_blocks_count_get(key_info);
+       chunk_count = 1 + ((block_count - 1) >> 2);
+       erp_region_id = cpu_to_be16(aentry->ht_key.erp_id |
+                                  (aregion->region->id << 4));
+       for (chunk_index = MLXSW_BLOOM_KEY_CHUNKS - chunk_count;
+            chunk_index < MLXSW_BLOOM_KEY_CHUNKS; chunk_index++) {
+               memset(chunk, 0, MLXSW_BLOOM_CHUNK_PAD_BYTES);
+               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]],
+                      MLXSW_BLOOM_CHUNK_KEY_BYTES);
+               chunk += MLXSW_BLOOM_KEY_CHUNK_BYTES;
+       }
+       *len = chunk_count * MLXSW_BLOOM_KEY_CHUNK_BYTES;
+}
+
+static unsigned int
+mlxsw_sp_acl_bf_rule_count_index_get(struct mlxsw_sp_acl_bf *bf,
+                                    unsigned int erp_bank,
+                                    unsigned int bf_index)
+{
+       return erp_bank * bf->bank_size + bf_index;
+}
+
+static unsigned int
+mlxsw_sp_acl_bf_index_get(struct mlxsw_sp_acl_bf *bf,
+                         struct mlxsw_sp_acl_atcam_region *aregion,
+                         struct mlxsw_sp_acl_atcam_entry *aentry)
+{
+       char bf_key[MLXSW_BLOOM_KEY_LEN];
+       u8 bf_size;
+
+       mlxsw_sp_acl_bf_key_encode(aregion, aentry, bf_key, &bf_size);
+       return mlxsw_sp_acl_bf_crc(bf_key, bf_size);
+}
+
+int
+mlxsw_sp_acl_bf_entry_add(struct mlxsw_sp *mlxsw_sp,
+                         struct mlxsw_sp_acl_bf *bf,
+                         struct mlxsw_sp_acl_atcam_region *aregion,
+                         unsigned int erp_bank,
+                         struct mlxsw_sp_acl_atcam_entry *aentry)
+{
+       unsigned int rule_index;
+       char *peabfe_pl;
+       u16 bf_index;
+       int err;
+
+       bf_index = mlxsw_sp_acl_bf_index_get(bf, aregion, aentry);
+       rule_index = mlxsw_sp_acl_bf_rule_count_index_get(bf, erp_bank,
+                                                         bf_index);
+
+       if (refcount_inc_not_zero(&bf->refcnt[rule_index]))
+               return 0;
+
+       peabfe_pl = kmalloc(MLXSW_REG_PEABFE_LEN, GFP_KERNEL);
+       if (!peabfe_pl)
+               return -ENOMEM;
+
+       mlxsw_reg_peabfe_pack(peabfe_pl);
+       mlxsw_reg_peabfe_rec_pack(peabfe_pl, 0, 1, erp_bank, bf_index);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(peabfe), peabfe_pl);
+       kfree(peabfe_pl);
+       if (err)
+               return err;
+
+       refcount_set(&bf->refcnt[rule_index], 1);
+       return 0;
+}
+
+void
+mlxsw_sp_acl_bf_entry_del(struct mlxsw_sp *mlxsw_sp,
+                         struct mlxsw_sp_acl_bf *bf,
+                         struct mlxsw_sp_acl_atcam_region *aregion,
+                         unsigned int erp_bank,
+                         struct mlxsw_sp_acl_atcam_entry *aentry)
+{
+       unsigned int rule_index;
+       char *peabfe_pl;
+       u16 bf_index;
+
+       bf_index = mlxsw_sp_acl_bf_index_get(bf, aregion, aentry);
+       rule_index = mlxsw_sp_acl_bf_rule_count_index_get(bf, erp_bank,
+                                                         bf_index);
+
+       if (refcount_dec_and_test(&bf->refcnt[rule_index])) {
+               peabfe_pl = kmalloc(MLXSW_REG_PEABFE_LEN, GFP_KERNEL);
+               if (!peabfe_pl)
+                       return;
+
+               mlxsw_reg_peabfe_pack(peabfe_pl);
+               mlxsw_reg_peabfe_rec_pack(peabfe_pl, 0, 0, erp_bank, bf_index);
+               mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(peabfe), peabfe_pl);
+               kfree(peabfe_pl);
+       }
+}
+
+struct mlxsw_sp_acl_bf *
+mlxsw_sp_acl_bf_init(struct mlxsw_sp *mlxsw_sp, unsigned int num_erp_banks)
+{
+       struct mlxsw_sp_acl_bf *bf;
+       unsigned int bf_bank_size;
+
+       if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_BF_LOG))
+               return ERR_PTR(-EIO);
+
+       /* Bloom filter size per erp_table_bank
+        * is 2^ACL_MAX_BF_LOG
+        */
+       bf_bank_size = 1 << MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_BF_LOG);
+       bf = kzalloc(sizeof(*bf) + bf_bank_size * num_erp_banks *
+                    sizeof(*bf->refcnt), GFP_KERNEL);
+       if (!bf)
+               return ERR_PTR(-ENOMEM);
+
+       bf->bank_size = bf_bank_size;
+       return bf;
+}
+
+void mlxsw_sp_acl_bf_fini(struct mlxsw_sp_acl_bf *bf)
+{
+       kfree(bf);
+}
index e3c6fe8..b0f2d8e 100644 (file)
@@ -46,7 +46,6 @@ mlxsw_sp_acl_ctcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
        struct mlxsw_sp_acl_tcam_region *region = cregion->region;
        struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);
        char ptce2_pl[MLXSW_REG_PTCE2_LEN];
-       unsigned int blocks_count;
        char *act_set;
        u32 priority;
        char *mask;
@@ -63,9 +62,7 @@ mlxsw_sp_acl_ctcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
                             centry->parman_item.index, priority);
        key = mlxsw_reg_ptce2_flex_key_blocks_data(ptce2_pl);
        mask = mlxsw_reg_ptce2_mask_data(ptce2_pl);
-       blocks_count = mlxsw_afk_key_info_blocks_count_get(region->key_info);
-       mlxsw_afk_encode(afk, region->key_info, &rulei->values, key, mask, 0,
-                        blocks_count - 1);
+       mlxsw_afk_encode(afk, region->key_info, &rulei->values, key, mask);
 
        err = cregion->ops->entry_insert(cregion, centry, mask);
        if (err)
@@ -92,6 +89,27 @@ mlxsw_sp_acl_ctcam_region_entry_remove(struct mlxsw_sp *mlxsw_sp,
        cregion->ops->entry_remove(cregion, centry);
 }
 
+static int
+mlxsw_sp_acl_ctcam_region_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                              struct mlxsw_sp_acl_ctcam_region *cregion,
+                                              struct mlxsw_sp_acl_ctcam_entry *centry,
+                                              struct mlxsw_afa_block *afa_block,
+                                              unsigned int priority)
+{
+       char ptce2_pl[MLXSW_REG_PTCE2_LEN];
+       char *act_set;
+
+       mlxsw_reg_ptce2_pack(ptce2_pl, true, MLXSW_REG_PTCE2_OP_WRITE_UPDATE,
+                            cregion->region->tcam_region_info,
+                            centry->parman_item.index, priority);
+
+       act_set = mlxsw_afa_block_first_set(afa_block);
+       mlxsw_reg_ptce2_flex_action_set_memcpy_to(ptce2_pl, act_set);
+
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce2), ptce2_pl);
+}
+
+
 static int mlxsw_sp_acl_ctcam_region_parman_resize(void *priv,
                                                   unsigned long new_count)
 {
@@ -194,3 +212,15 @@ void mlxsw_sp_acl_ctcam_entry_del(struct mlxsw_sp *mlxsw_sp,
        parman_item_remove(cregion->parman, &cchunk->parman_prio,
                           &centry->parman_item);
 }
+
+int mlxsw_sp_acl_ctcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                           struct mlxsw_sp_acl_ctcam_region *cregion,
+                                           struct mlxsw_sp_acl_ctcam_chunk *cchunk,
+                                           struct mlxsw_sp_acl_ctcam_entry *centry,
+                                           struct mlxsw_sp_acl_rule_info *rulei)
+{
+       return mlxsw_sp_acl_ctcam_region_entry_action_replace(mlxsw_sp, cregion,
+                                                             centry,
+                                                             rulei->act_block,
+                                                             rulei->priority);
+}
index 0a4fd3c..1c19fee 100644 (file)
@@ -7,7 +7,7 @@
 #include <linux/gfp.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
-#include <linux/rhashtable.h>
+#include <linux/objagg.h>
 #include <linux/rtnetlink.h>
 #include <linux/slab.h>
 
@@ -24,11 +24,14 @@ struct mlxsw_sp_acl_erp_core {
        unsigned int erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_MAX + 1];
        struct gen_pool *erp_tables;
        struct mlxsw_sp *mlxsw_sp;
+       struct mlxsw_sp_acl_bf *bf;
        unsigned int num_erp_banks;
 };
 
 struct mlxsw_sp_acl_erp_key {
        char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN];
+#define __MASK_LEN 0x38
+#define __MASK_IDX(i) (__MASK_LEN - (i) - 1)
        bool ctcam;
 };
 
@@ -36,10 +39,8 @@ struct mlxsw_sp_acl_erp {
        struct mlxsw_sp_acl_erp_key key;
        u8 id;
        u8 index;
-       refcount_t refcnt;
        DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
        struct list_head list;
-       struct rhash_head ht_node;
        struct mlxsw_sp_acl_erp_table *erp_table;
 };
 
@@ -53,7 +54,6 @@ struct mlxsw_sp_acl_erp_table {
        DECLARE_BITMAP(erp_id_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION);
        DECLARE_BITMAP(erp_index_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION);
        struct list_head atcam_erps_list;
-       struct rhashtable erp_ht;
        struct mlxsw_sp_acl_erp_core *erp_core;
        struct mlxsw_sp_acl_atcam_region *aregion;
        const struct mlxsw_sp_acl_erp_table_ops *ops;
@@ -61,12 +61,8 @@ struct mlxsw_sp_acl_erp_table {
        unsigned int num_atcam_erps;
        unsigned int num_max_atcam_erps;
        unsigned int num_ctcam_erps;
-};
-
-static const struct rhashtable_params mlxsw_sp_acl_erp_ht_params = {
-       .key_len = sizeof(struct mlxsw_sp_acl_erp_key),
-       .key_offset = offsetof(struct mlxsw_sp_acl_erp, key),
-       .head_offset = offsetof(struct mlxsw_sp_acl_erp, ht_node),
+       unsigned int num_deltas;
+       struct objagg *objagg;
 };
 
 struct mlxsw_sp_acl_erp_table_ops {
@@ -119,14 +115,17 @@ static const struct mlxsw_sp_acl_erp_table_ops erp_no_mask_ops = {
        .erp_destroy = mlxsw_sp_acl_erp_no_mask_destroy,
 };
 
-bool mlxsw_sp_acl_erp_is_ctcam_erp(const struct mlxsw_sp_acl_erp *erp)
+static bool
+mlxsw_sp_acl_erp_table_is_used(const struct mlxsw_sp_acl_erp_table *erp_table)
 {
-       return erp->key.ctcam;
+       return erp_table->ops != &erp_single_mask_ops &&
+              erp_table->ops != &erp_no_mask_ops;
 }
 
-u8 mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp *erp)
+static unsigned int
+mlxsw_sp_acl_erp_bank_get(const struct mlxsw_sp_acl_erp *erp)
 {
-       return erp->id;
+       return erp->index % erp->erp_table->erp_core->num_erp_banks;
 }
 
 static unsigned int
@@ -194,12 +193,15 @@ mlxsw_sp_acl_erp_master_mask_update(struct mlxsw_sp_acl_erp_table *erp_table)
 
 static int
 mlxsw_sp_acl_erp_master_mask_set(struct mlxsw_sp_acl_erp_table *erp_table,
-                                const struct mlxsw_sp_acl_erp *erp)
+                                struct mlxsw_sp_acl_erp_key *key)
 {
+       DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
        unsigned long bit;
        int err;
 
-       for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
+       bitmap_from_arr32(mask_bitmap, (u32 *) key->mask,
+                         MLXSW_SP_ACL_TCAM_MASK_LEN);
+       for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
                mlxsw_sp_acl_erp_master_mask_bit_set(bit,
                                                     &erp_table->master_mask);
 
@@ -210,7 +212,7 @@ mlxsw_sp_acl_erp_master_mask_set(struct mlxsw_sp_acl_erp_table *erp_table,
        return 0;
 
 err_master_mask_update:
-       for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
+       for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
                mlxsw_sp_acl_erp_master_mask_bit_clear(bit,
                                                       &erp_table->master_mask);
        return err;
@@ -218,12 +220,15 @@ err_master_mask_update:
 
 static int
 mlxsw_sp_acl_erp_master_mask_clear(struct mlxsw_sp_acl_erp_table *erp_table,
-                                  const struct mlxsw_sp_acl_erp *erp)
+                                  struct mlxsw_sp_acl_erp_key *key)
 {
+       DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
        unsigned long bit;
        int err;
 
-       for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
+       bitmap_from_arr32(mask_bitmap, (u32 *) key->mask,
+                         MLXSW_SP_ACL_TCAM_MASK_LEN);
+       for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
                mlxsw_sp_acl_erp_master_mask_bit_clear(bit,
                                                       &erp_table->master_mask);
 
@@ -234,7 +239,7 @@ mlxsw_sp_acl_erp_master_mask_clear(struct mlxsw_sp_acl_erp_table *erp_table,
        return 0;
 
 err_master_mask_update:
-       for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
+       for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
                mlxsw_sp_acl_erp_master_mask_bit_set(bit,
                                                     &erp_table->master_mask);
        return err;
@@ -256,26 +261,16 @@ mlxsw_sp_acl_erp_generic_create(struct mlxsw_sp_acl_erp_table *erp_table,
                goto err_erp_id_get;
 
        memcpy(&erp->key, key, sizeof(*key));
-       bitmap_from_arr32(erp->mask_bitmap, (u32 *) key->mask,
-                         MLXSW_SP_ACL_TCAM_MASK_LEN);
        list_add(&erp->list, &erp_table->atcam_erps_list);
-       refcount_set(&erp->refcnt, 1);
        erp_table->num_atcam_erps++;
        erp->erp_table = erp_table;
 
-       err = mlxsw_sp_acl_erp_master_mask_set(erp_table, erp);
+       err = mlxsw_sp_acl_erp_master_mask_set(erp_table, &erp->key);
        if (err)
                goto err_master_mask_set;
 
-       err = rhashtable_insert_fast(&erp_table->erp_ht, &erp->ht_node,
-                                    mlxsw_sp_acl_erp_ht_params);
-       if (err)
-               goto err_rhashtable_insert;
-
        return erp;
 
-err_rhashtable_insert:
-       mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
 err_master_mask_set:
        erp_table->num_atcam_erps--;
        list_del(&erp->list);
@@ -290,9 +285,7 @@ mlxsw_sp_acl_erp_generic_destroy(struct mlxsw_sp_acl_erp *erp)
 {
        struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
 
-       rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
-                              mlxsw_sp_acl_erp_ht_params);
-       mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
+       mlxsw_sp_acl_erp_master_mask_clear(erp_table, &erp->key);
        erp_table->num_atcam_erps--;
        list_del(&erp->list);
        mlxsw_sp_acl_erp_id_put(erp_table, erp->id);
@@ -524,6 +517,48 @@ err_table_relocate:
        return err;
 }
 
+static int
+mlxsw_acl_erp_table_bf_add(struct mlxsw_sp_acl_erp_table *erp_table,
+                          struct mlxsw_sp_acl_erp *erp)
+{
+       struct mlxsw_sp_acl_atcam_region *aregion = erp_table->aregion;
+       unsigned int erp_bank = mlxsw_sp_acl_erp_bank_get(erp);
+       struct mlxsw_sp_acl_atcam_entry *aentry;
+       int err;
+
+       list_for_each_entry(aentry, &aregion->entries_list, list) {
+               err = mlxsw_sp_acl_bf_entry_add(aregion->region->mlxsw_sp,
+                                               erp_table->erp_core->bf,
+                                               aregion, erp_bank, aentry);
+               if (err)
+                       goto bf_entry_add_err;
+       }
+
+       return 0;
+
+bf_entry_add_err:
+       list_for_each_entry_continue_reverse(aentry, &aregion->entries_list,
+                                            list)
+               mlxsw_sp_acl_bf_entry_del(aregion->region->mlxsw_sp,
+                                         erp_table->erp_core->bf,
+                                         aregion, erp_bank, aentry);
+       return err;
+}
+
+static void
+mlxsw_acl_erp_table_bf_del(struct mlxsw_sp_acl_erp_table *erp_table,
+                          struct mlxsw_sp_acl_erp *erp)
+{
+       struct mlxsw_sp_acl_atcam_region *aregion = erp_table->aregion;
+       unsigned int erp_bank = mlxsw_sp_acl_erp_bank_get(erp);
+       struct mlxsw_sp_acl_atcam_entry *aentry;
+
+       list_for_each_entry_reverse(aentry, &aregion->entries_list, list)
+               mlxsw_sp_acl_bf_entry_del(aregion->region->mlxsw_sp,
+                                         erp_table->erp_core->bf,
+                                         aregion, erp_bank, aentry);
+}
+
 static int
 mlxsw_sp_acl_erp_region_table_trans(struct mlxsw_sp_acl_erp_table *erp_table)
 {
@@ -548,16 +583,24 @@ mlxsw_sp_acl_erp_region_table_trans(struct mlxsw_sp_acl_erp_table *erp_table)
                goto err_table_master_rp;
        }
 
-       /* Maintain the same eRP bank for the master RP, so that we
-        * wouldn't need to update the bloom filter
+       /* Make sure the master RP is using a valid index, as
+        * only a single eRP row is currently allocated.
         */
-       master_rp->index = master_rp->index % erp_core->num_erp_banks;
+       master_rp->index = 0;
        __set_bit(master_rp->index, erp_table->erp_index_bitmap);
 
        err = mlxsw_sp_acl_erp_table_erp_add(erp_table, master_rp);
        if (err)
                goto err_table_master_rp_add;
 
+       /* Update Bloom filter before enabling eRP table, as rules
+        * on the master RP were not set to Bloom filter up to this
+        * point.
+        */
+       err = mlxsw_acl_erp_table_bf_add(erp_table, master_rp);
+       if (err)
+               goto err_table_bf_add;
+
        err = mlxsw_sp_acl_erp_table_enable(erp_table, false);
        if (err)
                goto err_table_enable;
@@ -565,6 +608,8 @@ mlxsw_sp_acl_erp_region_table_trans(struct mlxsw_sp_acl_erp_table *erp_table)
        return 0;
 
 err_table_enable:
+       mlxsw_acl_erp_table_bf_del(erp_table, master_rp);
+err_table_bf_add:
        mlxsw_sp_acl_erp_table_erp_del(master_rp);
 err_table_master_rp_add:
        __clear_bit(master_rp->index, erp_table->erp_index_bitmap);
@@ -585,6 +630,7 @@ mlxsw_sp_acl_erp_region_master_mask_trans(struct mlxsw_sp_acl_erp_table *erp_tab
        master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table);
        if (!master_rp)
                return;
+       mlxsw_acl_erp_table_bf_del(erp_table, master_rp);
        mlxsw_sp_acl_erp_table_erp_del(master_rp);
        __clear_bit(master_rp->index, erp_table->erp_index_bitmap);
        mlxsw_sp_acl_erp_table_free(erp_core, erp_table->num_max_atcam_erps,
@@ -647,9 +693,55 @@ mlxsw_sp_acl_erp_region_ctcam_disable(struct mlxsw_sp_acl_erp_table *erp_table)
        mlxsw_sp_acl_erp_table_enable(erp_table, false);
 }
 
+static int
+__mlxsw_sp_acl_erp_table_other_inc(struct mlxsw_sp_acl_erp_table *erp_table,
+                                  unsigned int *inc_num)
+{
+       int err;
+
+       /* If there are C-TCAM eRP or deltas in use we need to transition
+        * the region to use eRP table, if it is not already done
+        */
+       if (!mlxsw_sp_acl_erp_table_is_used(erp_table)) {
+               err = mlxsw_sp_acl_erp_region_table_trans(erp_table);
+               if (err)
+                       return err;
+       }
+
+       /* When C-TCAM or deltas are used, the eRP table must be used */
+       if (erp_table->ops != &erp_multiple_masks_ops)
+               erp_table->ops = &erp_multiple_masks_ops;
+
+       (*inc_num)++;
+
+       return 0;
+}
+
+static int mlxsw_sp_acl_erp_ctcam_inc(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+       return __mlxsw_sp_acl_erp_table_other_inc(erp_table,
+                                                 &erp_table->num_ctcam_erps);
+}
+
+static int mlxsw_sp_acl_erp_delta_inc(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+       return __mlxsw_sp_acl_erp_table_other_inc(erp_table,
+                                                 &erp_table->num_deltas);
+}
+
 static void
-mlxsw_sp_acl_erp_ctcam_table_ops_set(struct mlxsw_sp_acl_erp_table *erp_table)
+__mlxsw_sp_acl_erp_table_other_dec(struct mlxsw_sp_acl_erp_table *erp_table,
+                                  unsigned int *dec_num)
 {
+       (*dec_num)--;
+
+       /* If there are no C-TCAM eRP or deltas in use, the state we
+        * transition to depends on the number of A-TCAM eRPs currently
+        * in use.
+        */
+       if (erp_table->num_ctcam_erps > 0 || erp_table->num_deltas > 0)
+               return;
+
        switch (erp_table->num_atcam_erps) {
        case 2:
                /* Keep using the eRP table, but correctly set the
@@ -683,9 +775,21 @@ mlxsw_sp_acl_erp_ctcam_table_ops_set(struct mlxsw_sp_acl_erp_table *erp_table)
        }
 }
 
+static void mlxsw_sp_acl_erp_ctcam_dec(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+       __mlxsw_sp_acl_erp_table_other_dec(erp_table,
+                                          &erp_table->num_ctcam_erps);
+}
+
+static void mlxsw_sp_acl_erp_delta_dec(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+       __mlxsw_sp_acl_erp_table_other_dec(erp_table,
+                                          &erp_table->num_deltas);
+}
+
 static struct mlxsw_sp_acl_erp *
-__mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
-                                    struct mlxsw_sp_acl_erp_key *key)
+mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
+                                  struct mlxsw_sp_acl_erp_key *key)
 {
        struct mlxsw_sp_acl_erp *erp;
        int err;
@@ -697,89 +801,41 @@ __mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
        memcpy(&erp->key, key, sizeof(*key));
        bitmap_from_arr32(erp->mask_bitmap, (u32 *) key->mask,
                          MLXSW_SP_ACL_TCAM_MASK_LEN);
-       refcount_set(&erp->refcnt, 1);
-       erp_table->num_ctcam_erps++;
-       erp->erp_table = erp_table;
 
-       err = mlxsw_sp_acl_erp_master_mask_set(erp_table, erp);
+       err = mlxsw_sp_acl_erp_ctcam_inc(erp_table);
        if (err)
-               goto err_master_mask_set;
+               goto err_erp_ctcam_inc;
+
+       erp->erp_table = erp_table;
 
-       err = rhashtable_insert_fast(&erp_table->erp_ht, &erp->ht_node,
-                                    mlxsw_sp_acl_erp_ht_params);
+       err = mlxsw_sp_acl_erp_master_mask_set(erp_table, &erp->key);
        if (err)
-               goto err_rhashtable_insert;
+               goto err_master_mask_set;
 
        err = mlxsw_sp_acl_erp_region_ctcam_enable(erp_table);
        if (err)
                goto err_erp_region_ctcam_enable;
 
-       /* When C-TCAM is used, the eRP table must be used */
-       erp_table->ops = &erp_multiple_masks_ops;
-
        return erp;
 
 err_erp_region_ctcam_enable:
-       rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
-                              mlxsw_sp_acl_erp_ht_params);
-err_rhashtable_insert:
-       mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
+       mlxsw_sp_acl_erp_master_mask_clear(erp_table, &erp->key);
 err_master_mask_set:
-       erp_table->num_ctcam_erps--;
+       mlxsw_sp_acl_erp_ctcam_dec(erp_table);
+err_erp_ctcam_inc:
        kfree(erp);
        return ERR_PTR(err);
 }
 
-static struct mlxsw_sp_acl_erp *
-mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
-                                  struct mlxsw_sp_acl_erp_key *key)
-{
-       struct mlxsw_sp_acl_erp *erp;
-       int err;
-
-       /* There is a special situation where we need to spill rules
-        * into the C-TCAM, yet the region is still using a master
-        * mask and thus not performing a lookup in the C-TCAM. This
-        * can happen when two rules that only differ in priority - and
-        * thus sharing the same key - are programmed. In this case
-        * we transition the region to use an eRP table
-        */
-       err = mlxsw_sp_acl_erp_region_table_trans(erp_table);
-       if (err)
-               return ERR_PTR(err);
-
-       erp = __mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
-       if (IS_ERR(erp)) {
-               err = PTR_ERR(erp);
-               goto err_erp_create;
-       }
-
-       return erp;
-
-err_erp_create:
-       mlxsw_sp_acl_erp_region_master_mask_trans(erp_table);
-       return ERR_PTR(err);
-}
-
 static void
 mlxsw_sp_acl_erp_ctcam_mask_destroy(struct mlxsw_sp_acl_erp *erp)
 {
        struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
 
        mlxsw_sp_acl_erp_region_ctcam_disable(erp_table);
-       rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
-                              mlxsw_sp_acl_erp_ht_params);
-       mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
-       erp_table->num_ctcam_erps--;
+       mlxsw_sp_acl_erp_master_mask_clear(erp_table, &erp->key);
+       mlxsw_sp_acl_erp_ctcam_dec(erp_table);
        kfree(erp);
-
-       /* Once the last C-TCAM eRP was destroyed, the state we
-        * transition to depends on the number of A-TCAM eRPs currently
-        * in use
-        */
-       if (erp_table->num_ctcam_erps > 0)
-               return;
-       mlxsw_sp_acl_erp_ctcam_table_ops_set(erp_table);
 }
 
 static struct mlxsw_sp_acl_erp *
@@ -790,7 +846,7 @@ mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
        int err;
 
        if (key->ctcam)
-               return __mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
+               return mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
 
        /* Expand the eRP table for the new eRP, if needed */
        err = mlxsw_sp_acl_erp_table_expand(erp_table);
@@ -838,7 +894,8 @@ mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
        mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
        mlxsw_sp_acl_erp_generic_destroy(erp);
 
-       if (erp_table->num_atcam_erps == 2 && erp_table->num_ctcam_erps == 0)
+       if (erp_table->num_atcam_erps == 2 && erp_table->num_ctcam_erps == 0 &&
+           erp_table->num_deltas == 0)
                erp_table->ops = &erp_two_masks_ops;
 }
 
@@ -940,13 +997,12 @@ mlxsw_sp_acl_erp_no_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
        WARN_ON(1);
 }
 
-struct mlxsw_sp_acl_erp *
-mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
-                    const char *mask, bool ctcam)
+struct mlxsw_sp_acl_erp_mask *
+mlxsw_sp_acl_erp_mask_get(struct mlxsw_sp_acl_atcam_region *aregion,
+                         const char *mask, bool ctcam)
 {
-       struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
        struct mlxsw_sp_acl_erp_key key;
-       struct mlxsw_sp_acl_erp *erp;
+       struct objagg_obj *objagg_obj;
 
        /* eRPs are allocated from a shared resource, but currently all
         * allocations are done under RTNL.
@@ -955,29 +1011,276 @@ mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
 
        memcpy(key.mask, mask, MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN);
        key.ctcam = ctcam;
-       erp = rhashtable_lookup_fast(&erp_table->erp_ht, &key,
-                                    mlxsw_sp_acl_erp_ht_params);
-       if (erp) {
-               refcount_inc(&erp->refcnt);
-               return erp;
-       }
+       objagg_obj = objagg_obj_get(aregion->erp_table->objagg, &key);
+       if (IS_ERR(objagg_obj))
+               return ERR_CAST(objagg_obj);
+       return (struct mlxsw_sp_acl_erp_mask *) objagg_obj;
+}
+
+void mlxsw_sp_acl_erp_mask_put(struct mlxsw_sp_acl_atcam_region *aregion,
+                              struct mlxsw_sp_acl_erp_mask *erp_mask)
+{
+       struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
 
-       return erp_table->ops->erp_create(erp_table, &key);
+       ASSERT_RTNL();
+       objagg_obj_put(aregion->erp_table->objagg, objagg_obj);
 }
 
-void mlxsw_sp_acl_erp_put(struct mlxsw_sp_acl_atcam_region *aregion,
-                         struct mlxsw_sp_acl_erp *erp)
+int mlxsw_sp_acl_erp_bf_insert(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)
 {
-       struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
+       struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
+       const struct mlxsw_sp_acl_erp *erp = objagg_obj_root_priv(objagg_obj);
+       unsigned int erp_bank;
 
        ASSERT_RTNL();
+       if (!mlxsw_sp_acl_erp_table_is_used(erp->erp_table))
+               return 0;
+
+       erp_bank = mlxsw_sp_acl_erp_bank_get(erp);
+       return mlxsw_sp_acl_bf_entry_add(mlxsw_sp,
+                                       erp->erp_table->erp_core->bf,
+                                       aregion, erp_bank, aentry);
+}
+
+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)
+{
+       struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
+       const struct mlxsw_sp_acl_erp *erp = objagg_obj_root_priv(objagg_obj);
+       unsigned int erp_bank;
 
-       if (!refcount_dec_and_test(&erp->refcnt))
+       ASSERT_RTNL();
+       if (!mlxsw_sp_acl_erp_table_is_used(erp->erp_table))
                return;
 
-       erp_table->ops->erp_destroy(erp_table, erp);
+       erp_bank = mlxsw_sp_acl_erp_bank_get(erp);
+       mlxsw_sp_acl_bf_entry_del(mlxsw_sp,
+                                 erp->erp_table->erp_core->bf,
+                                 aregion, erp_bank, aentry);
+}
+
+bool
+mlxsw_sp_acl_erp_mask_is_ctcam(const struct mlxsw_sp_acl_erp_mask *erp_mask)
+{
+       struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
+       const struct mlxsw_sp_acl_erp_key *key = objagg_obj_raw(objagg_obj);
+
+       return key->ctcam;
+}
+
+u8 mlxsw_sp_acl_erp_mask_erp_id(const struct mlxsw_sp_acl_erp_mask *erp_mask)
+{
+       struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
+       const struct mlxsw_sp_acl_erp *erp = objagg_obj_root_priv(objagg_obj);
+
+       return erp->id;
+}
+
+struct mlxsw_sp_acl_erp_delta {
+       struct mlxsw_sp_acl_erp_key key;
+       u16 start;
+       u8 mask;
+};
+
+u16 mlxsw_sp_acl_erp_delta_start(const struct mlxsw_sp_acl_erp_delta *delta)
+{
+       return delta->start;
+}
+
+u8 mlxsw_sp_acl_erp_delta_mask(const struct mlxsw_sp_acl_erp_delta *delta)
+{
+       return delta->mask;
+}
+
+u8 mlxsw_sp_acl_erp_delta_value(const struct mlxsw_sp_acl_erp_delta *delta,
+                               const char *enc_key)
+{
+       u16 start = delta->start;
+       u8 mask = delta->mask;
+       u16 tmp;
+
+       if (!mask)
+               return 0;
+
+       tmp = (unsigned char) enc_key[__MASK_IDX(start / 8)];
+       if (start / 8 + 1 < __MASK_LEN)
+               tmp |= (unsigned char) enc_key[__MASK_IDX(start / 8 + 1)] << 8;
+       tmp >>= start % 8;
+       tmp &= mask;
+       return tmp;
+}
+
+void mlxsw_sp_acl_erp_delta_clear(const struct mlxsw_sp_acl_erp_delta *delta,
+                                 const char *enc_key)
+{
+       u16 start = delta->start;
+       u8 mask = delta->mask;
+       unsigned char *byte;
+       u16 tmp;
+
+       tmp = mask;
+       tmp <<= start % 8;
+       tmp = ~tmp;
+
+       byte = (unsigned char *) &enc_key[__MASK_IDX(start / 8)];
+       *byte &= tmp & 0xff;
+       if (start / 8 + 1 < __MASK_LEN) {
+               byte = (unsigned char *) &enc_key[__MASK_IDX(start / 8 + 1)];
+               *byte &= (tmp >> 8) & 0xff;
+       }
+}
+
+static const struct mlxsw_sp_acl_erp_delta
+mlxsw_sp_acl_erp_delta_default = {};
+
+const struct mlxsw_sp_acl_erp_delta *
+mlxsw_sp_acl_erp_delta(const struct mlxsw_sp_acl_erp_mask *erp_mask)
+{
+       struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
+       const struct mlxsw_sp_acl_erp_delta *delta;
+
+       delta = objagg_obj_delta_priv(objagg_obj);
+       if (!delta)
+               delta = &mlxsw_sp_acl_erp_delta_default;
+       return delta;
+}
+
+static int
+mlxsw_sp_acl_erp_delta_fill(const struct mlxsw_sp_acl_erp_key *parent_key,
+                           const struct mlxsw_sp_acl_erp_key *key,
+                           u16 *delta_start, u8 *delta_mask)
+{
+       int offset = 0;
+       int si = -1;
+       u16 pmask;
+       u16 mask;
+       int i;
+
+       /* The difference between 2 masks can be up to 8 consecutive bits. */
+       for (i = 0; i < __MASK_LEN; i++) {
+               if (parent_key->mask[__MASK_IDX(i)] == key->mask[__MASK_IDX(i)])
+                       continue;
+               if (si == -1)
+                       si = i;
+               else if (si != i - 1)
+                       return -EINVAL;
+       }
+       if (si == -1) {
+               /* The masks are the same, this cannot happen.
+                * That means the caller is broken.
+                */
+               WARN_ON(1);
+               *delta_start = 0;
+               *delta_mask = 0;
+               return 0;
+       }
+       pmask = (unsigned char) parent_key->mask[__MASK_IDX(si)];
+       mask = (unsigned char) key->mask[__MASK_IDX(si)];
+       if (si + 1 < __MASK_LEN) {
+               pmask |= (unsigned char) parent_key->mask[__MASK_IDX(si + 1)] << 8;
+               mask |= (unsigned char) key->mask[__MASK_IDX(si + 1)] << 8;
+       }
+
+       if ((pmask ^ mask) & pmask)
+               return -EINVAL;
+       mask &= ~pmask;
+       while (!(mask & (1 << offset)))
+               offset++;
+       while (!(mask & 1))
+               mask >>= 1;
+       if (mask & 0xff00)
+               return -EINVAL;
+
+       *delta_start = si * 8 + offset;
+       *delta_mask = mask;
+
+       return 0;
+}
+
+static void *mlxsw_sp_acl_erp_delta_create(void *priv, void *parent_obj,
+                                          void *obj)
+{
+       struct mlxsw_sp_acl_erp_key *parent_key = parent_obj;
+       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;
+       struct mlxsw_sp_acl_erp_delta *delta;
+       u16 delta_start;
+       u8 delta_mask;
+       int err;
+
+       if (parent_key->ctcam || key->ctcam)
+               return ERR_PTR(-EINVAL);
+       err = mlxsw_sp_acl_erp_delta_fill(parent_key, key,
+                                         &delta_start, &delta_mask);
+       if (err)
+               return ERR_PTR(-EINVAL);
+
+       delta = kzalloc(sizeof(*delta), GFP_KERNEL);
+       if (!delta)
+               return ERR_PTR(-ENOMEM);
+       delta->start = delta_start;
+       delta->mask = delta_mask;
+
+       err = mlxsw_sp_acl_erp_delta_inc(erp_table);
+       if (err)
+               goto err_erp_delta_inc;
+
+       memcpy(&delta->key, key, sizeof(*key));
+       err = mlxsw_sp_acl_erp_master_mask_set(erp_table, &delta->key);
+       if (err)
+               goto err_master_mask_set;
+
+       return delta;
+
+err_master_mask_set:
+       mlxsw_sp_acl_erp_delta_dec(erp_table);
+err_erp_delta_inc:
+       kfree(delta);
+       return ERR_PTR(err);
+}
+
+static void mlxsw_sp_acl_erp_delta_destroy(void *priv, void *delta_priv)
+{
+       struct mlxsw_sp_acl_erp_delta *delta = delta_priv;
+       struct mlxsw_sp_acl_atcam_region *aregion = priv;
+       struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
+
+       mlxsw_sp_acl_erp_master_mask_clear(erp_table, &delta->key);
+       mlxsw_sp_acl_erp_delta_dec(erp_table);
+       kfree(delta);
+}
+
+static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj)
+{
+       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;
+
+       return erp_table->ops->erp_create(erp_table, key);
+}
+
+static void mlxsw_sp_acl_erp_root_destroy(void *priv, void *root_priv)
+{
+       struct mlxsw_sp_acl_atcam_region *aregion = priv;
+       struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
+
+       erp_table->ops->erp_destroy(erp_table, root_priv);
 }
 
+static const struct objagg_ops mlxsw_sp_acl_erp_objagg_ops = {
+       .obj_size = sizeof(struct mlxsw_sp_acl_erp_key),
+       .delta_create = mlxsw_sp_acl_erp_delta_create,
+       .delta_destroy = mlxsw_sp_acl_erp_delta_destroy,
+       .root_create = mlxsw_sp_acl_erp_root_create,
+       .root_destroy = mlxsw_sp_acl_erp_root_destroy,
+};
+
 static struct mlxsw_sp_acl_erp_table *
 mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
 {
@@ -988,9 +1291,12 @@ mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
        if (!erp_table)
                return ERR_PTR(-ENOMEM);
 
-       err = rhashtable_init(&erp_table->erp_ht, &mlxsw_sp_acl_erp_ht_params);
-       if (err)
-               goto err_rhashtable_init;
+       erp_table->objagg = objagg_create(&mlxsw_sp_acl_erp_objagg_ops,
+                                         aregion);
+       if (IS_ERR(erp_table->objagg)) {
+               err = PTR_ERR(erp_table->objagg);
+               goto err_objagg_create;
+       }
 
        erp_table->erp_core = aregion->atcam->erp_core;
        erp_table->ops = &erp_no_mask_ops;
@@ -999,7 +1305,7 @@ mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
 
        return erp_table;
 
-err_rhashtable_init:
+err_objagg_create:
        kfree(erp_table);
        return ERR_PTR(err);
 }
@@ -1008,7 +1314,7 @@ static void
 mlxsw_sp_acl_erp_table_destroy(struct mlxsw_sp_acl_erp_table *erp_table)
 {
        WARN_ON(!list_empty(&erp_table->atcam_erps_list));
-       rhashtable_destroy(&erp_table->erp_ht);
+       objagg_destroy(erp_table->objagg);
        kfree(erp_table);
 }
 
@@ -1118,6 +1424,12 @@ static int mlxsw_sp_acl_erp_tables_init(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_gen_pool_add;
 
+       erp_core->bf = mlxsw_sp_acl_bf_init(mlxsw_sp, erp_core->num_erp_banks);
+       if (IS_ERR(erp_core->bf)) {
+               err = PTR_ERR(erp_core->bf);
+               goto err_bf_init;
+       }
+
        /* Different regions require masks of different sizes */
        err = mlxsw_sp_acl_erp_tables_sizes_query(mlxsw_sp, erp_core);
        if (err)
@@ -1126,6 +1438,8 @@ static int mlxsw_sp_acl_erp_tables_init(struct mlxsw_sp *mlxsw_sp,
        return 0;
 
 err_erp_tables_sizes_query:
+       mlxsw_sp_acl_bf_fini(erp_core->bf);
+err_bf_init:
 err_gen_pool_add:
        gen_pool_destroy(erp_core->erp_tables);
        return err;
@@ -1134,6 +1448,7 @@ err_gen_pool_add:
 static void mlxsw_sp_acl_erp_tables_fini(struct mlxsw_sp *mlxsw_sp,
                                         struct mlxsw_sp_acl_erp_core *erp_core)
 {
+       mlxsw_sp_acl_bf_fini(erp_core->bf);
        gen_pool_destroy(erp_core->erp_tables);
 }
 
index d409b09..2a998de 100644 (file)
@@ -98,8 +98,8 @@ static const struct mlxsw_afk_block mlxsw_sp1_afk_blocks[] = {
 
 #define MLXSW_SP1_AFK_KEY_BLOCK_SIZE 16
 
-static void mlxsw_sp1_afk_encode_block(char *block, int block_index,
-                                      char *output)
+static void mlxsw_sp1_afk_encode_block(char *output, int block_index,
+                                      char *block)
 {
        unsigned int offset = block_index * MLXSW_SP1_AFK_KEY_BLOCK_SIZE;
        char *output_indexed = output + offset;
@@ -107,10 +107,19 @@ static void mlxsw_sp1_afk_encode_block(char *block, int block_index,
        memcpy(output_indexed, block, MLXSW_SP1_AFK_KEY_BLOCK_SIZE);
 }
 
+static void mlxsw_sp1_afk_clear_block(char *output, int block_index)
+{
+       unsigned int offset = block_index * MLXSW_SP1_AFK_KEY_BLOCK_SIZE;
+       char *output_indexed = output + offset;
+
+       memset(output_indexed, 0, MLXSW_SP1_AFK_KEY_BLOCK_SIZE);
+}
+
 const struct mlxsw_afk_ops mlxsw_sp1_afk_ops = {
        .blocks         = mlxsw_sp1_afk_blocks,
        .blocks_count   = ARRAY_SIZE(mlxsw_sp1_afk_blocks),
        .encode_block   = mlxsw_sp1_afk_encode_block,
+       .clear_block    = mlxsw_sp1_afk_clear_block,
 };
 
 static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_mac_0[] = {
@@ -158,6 +167,11 @@ static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv4_2[] = {
        MLXSW_AFK_ELEMENT_INST_U32(IP_PROTO, 0x04, 16, 8),
 };
 
+static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv4_4[] = {
+       MLXSW_AFK_ELEMENT_INST_U32(VIRT_ROUTER_0_7, 0x04, 24, 8),
+       MLXSW_AFK_ELEMENT_INST_U32(VIRT_ROUTER_8_10, 0x00, 0, 3),
+};
+
 static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv6_0[] = {
        MLXSW_AFK_ELEMENT_INST_BUF(DST_IP_32_63, 0x04, 4),
 };
@@ -201,6 +215,7 @@ static const struct mlxsw_afk_block mlxsw_sp2_afk_blocks[] = {
        MLXSW_AFK_BLOCK(0x38, mlxsw_sp_afk_element_info_ipv4_0),
        MLXSW_AFK_BLOCK(0x39, mlxsw_sp_afk_element_info_ipv4_1),
        MLXSW_AFK_BLOCK(0x3A, mlxsw_sp_afk_element_info_ipv4_2),
+       MLXSW_AFK_BLOCK(0x3C, mlxsw_sp_afk_element_info_ipv4_4),
        MLXSW_AFK_BLOCK(0x40, mlxsw_sp_afk_element_info_ipv6_0),
        MLXSW_AFK_BLOCK(0x41, mlxsw_sp_afk_element_info_ipv6_1),
        MLXSW_AFK_BLOCK(0x42, mlxsw_sp_afk_element_info_ipv6_2),
@@ -263,10 +278,9 @@ static const struct mlxsw_sp2_afk_block_layout mlxsw_sp2_afk_blocks_layout[] = {
        MLXSW_SP2_AFK_BLOCK_LAYOUT(block11, 0x00, 12),
 };
 
-static void mlxsw_sp2_afk_encode_block(char *block, int block_index,
-                                      char *output)
+static void __mlxsw_sp2_afk_block_value_set(char *output, int block_index,
+                                           u64 block_value)
 {
-       u64 block_value = mlxsw_sp2_afk_block_value_get(block);
        const struct mlxsw_sp2_afk_block_layout *block_layout;
 
        if (WARN_ON(block_index < 0 ||
@@ -278,8 +292,22 @@ static void mlxsw_sp2_afk_encode_block(char *block, int block_index,
                           &block_layout->item, 0, block_value);
 }
 
+static void mlxsw_sp2_afk_encode_block(char *output, int block_index,
+                                      char *block)
+{
+       u64 block_value = mlxsw_sp2_afk_block_value_get(block);
+
+       __mlxsw_sp2_afk_block_value_set(output, block_index, block_value);
+}
+
+static void mlxsw_sp2_afk_clear_block(char *output, int block_index)
+{
+       __mlxsw_sp2_afk_block_value_set(output, block_index, 0);
+}
+
 const struct mlxsw_afk_ops mlxsw_sp2_afk_ops = {
        .blocks         = mlxsw_sp2_afk_blocks,
        .blocks_count   = ARRAY_SIZE(mlxsw_sp2_afk_blocks),
        .encode_block   = mlxsw_sp2_afk_encode_block,
+       .clear_block    = mlxsw_sp2_afk_clear_block,
 };
index e171513..fe230ac 100644 (file)
@@ -95,8 +95,9 @@ int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,
        if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, KVD_SIZE))
                return -EIO;
 
-       max_priority = MLXSW_CORE_RES_GET(mlxsw_sp->core, KVD_SIZE);
-       if (rulei->priority > max_priority)
+       /* Priority range is 1..cap_kvd_size-1. */
+       max_priority = MLXSW_CORE_RES_GET(mlxsw_sp->core, KVD_SIZE) - 1;
+       if (rulei->priority >= max_priority)
                return -EINVAL;
 
        /* Unlike in TC, in HW, higher number means higher priority. */
@@ -778,6 +779,20 @@ static void mlxsw_sp_acl_tcam_entry_del(struct mlxsw_sp *mlxsw_sp,
        mlxsw_sp_acl_tcam_chunk_put(mlxsw_sp, chunk);
 }
 
+static int
+mlxsw_sp_acl_tcam_entry_action_replace(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)
+{
+       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, chunk->priv,
+                                        entry->priv, rulei);
+}
+
 static int
 mlxsw_sp_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp,
                                     struct mlxsw_sp_acl_tcam_entry *entry,
@@ -848,6 +863,15 @@ struct mlxsw_sp_acl_tcam_flower_rule {
        struct mlxsw_sp_acl_tcam_entry entry;
 };
 
+struct mlxsw_sp_acl_tcam_mr_ruleset {
+       struct mlxsw_sp_acl_tcam_chunk *chunk;
+       struct mlxsw_sp_acl_tcam_group group;
+};
+
+struct mlxsw_sp_acl_tcam_mr_rule {
+       struct mlxsw_sp_acl_tcam_entry entry;
+};
+
 static int
 mlxsw_sp_acl_tcam_flower_ruleset_add(struct mlxsw_sp *mlxsw_sp,
                                     struct mlxsw_sp_acl_tcam *tcam,
@@ -929,6 +953,15 @@ mlxsw_sp_acl_tcam_flower_rule_del(struct mlxsw_sp *mlxsw_sp, void *rule_priv)
        mlxsw_sp_acl_tcam_entry_del(mlxsw_sp, &rule->entry);
 }
 
+static int
+mlxsw_sp_acl_tcam_flower_rule_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                            void *ruleset_priv,
+                                            void *rule_priv,
+                                            struct mlxsw_sp_acl_rule_info *rulei)
+{
+       return -EOPNOTSUPP;
+}
+
 static int
 mlxsw_sp_acl_tcam_flower_rule_activity_get(struct mlxsw_sp *mlxsw_sp,
                                           void *rule_priv, bool *activity)
@@ -949,12 +982,146 @@ static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = {
        .rule_priv_size         = mlxsw_sp_acl_tcam_flower_rule_priv_size,
        .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,
        .rule_activity_get      = mlxsw_sp_acl_tcam_flower_rule_activity_get,
 };
 
+static int
+mlxsw_sp_acl_tcam_mr_ruleset_add(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_acl_tcam *tcam,
+                                void *ruleset_priv,
+                                struct mlxsw_afk_element_usage *tmplt_elusage)
+{
+       struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;
+       int err;
+
+       err = mlxsw_sp_acl_tcam_group_add(mlxsw_sp, tcam, &ruleset->group,
+                                         mlxsw_sp_acl_tcam_patterns,
+                                         MLXSW_SP_ACL_TCAM_PATTERNS_COUNT,
+                                         tmplt_elusage);
+       if (err)
+               return err;
+
+       /* For most of the TCAM clients it would make sense to take a tcam chunk
+        * only when the first rule is written. This is not the case for
+        * multicast router as it is required to bind the multicast router to a
+        * 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);
+               goto err_chunk_get;
+       }
+
+       return 0;
+
+err_chunk_get:
+       mlxsw_sp_acl_tcam_group_del(mlxsw_sp, &ruleset->group);
+       return err;
+}
+
+static void
+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_group_del(mlxsw_sp, &ruleset->group);
+}
+
+static int
+mlxsw_sp_acl_tcam_mr_ruleset_bind(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv,
+                                 struct mlxsw_sp_port *mlxsw_sp_port,
+                                 bool ingress)
+{
+       /* Binding is done when initializing multicast router */
+       return 0;
+}
+
+static void
+mlxsw_sp_acl_tcam_mr_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
+                                   void *ruleset_priv,
+                                   struct mlxsw_sp_port *mlxsw_sp_port,
+                                   bool ingress)
+{
+}
+
+static u16
+mlxsw_sp_acl_tcam_mr_ruleset_group_id(void *ruleset_priv)
+{
+       struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = 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,
+                             struct mlxsw_sp_acl_rule_info *rulei)
+{
+       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);
+}
+
+static void
+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);
+}
+
+static int
+mlxsw_sp_acl_tcam_mr_rule_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                        void *ruleset_priv, void *rule_priv,
+                                        struct mlxsw_sp_acl_rule_info *rulei)
+{
+       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_action_replace(mlxsw_sp, &ruleset->group,
+                                                     &rule->entry, rulei);
+}
+
+static int
+mlxsw_sp_acl_tcam_mr_rule_activity_get(struct mlxsw_sp *mlxsw_sp,
+                                      void *rule_priv, bool *activity)
+{
+       struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;
+
+       return mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp, &rule->entry,
+                                                   activity);
+}
+
+static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_mr_ops = {
+       .ruleset_priv_size      = sizeof(struct mlxsw_sp_acl_tcam_mr_ruleset),
+       .ruleset_add            = mlxsw_sp_acl_tcam_mr_ruleset_add,
+       .ruleset_del            = mlxsw_sp_acl_tcam_mr_ruleset_del,
+       .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_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,
+       .rule_activity_get      = mlxsw_sp_acl_tcam_mr_rule_activity_get,
+};
+
 static const struct mlxsw_sp_acl_profile_ops *
 mlxsw_sp_acl_tcam_profile_ops_arr[] = {
        [MLXSW_SP_ACL_PROFILE_FLOWER] = &mlxsw_sp_acl_tcam_flower_ops,
+       [MLXSW_SP_ACL_PROFILE_MR] = &mlxsw_sp_acl_tcam_mr_ops,
 };
 
 const struct mlxsw_sp_acl_profile_ops *
index 219a4e2..0f1a9de 100644 (file)
@@ -48,6 +48,9 @@ struct mlxsw_sp_acl_profile_ops {
                        void *ruleset_priv, void *rule_priv,
                        struct mlxsw_sp_acl_rule_info *rulei);
        void (*rule_del)(struct mlxsw_sp *mlxsw_sp, void *rule_priv);
+       int (*rule_action_replace)(struct mlxsw_sp *mlxsw_sp,
+                                  void *ruleset_priv, void *rule_priv,
+                                  struct mlxsw_sp_acl_rule_info *rulei);
        int (*rule_activity_get)(struct mlxsw_sp *mlxsw_sp, void *rule_priv,
                                 bool *activity);
 };
@@ -121,6 +124,11 @@ void mlxsw_sp_acl_ctcam_entry_del(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_acl_ctcam_region *cregion,
                                  struct mlxsw_sp_acl_ctcam_chunk *cchunk,
                                  struct mlxsw_sp_acl_ctcam_entry *centry);
+int mlxsw_sp_acl_ctcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                           struct mlxsw_sp_acl_ctcam_region *cregion,
+                                           struct mlxsw_sp_acl_ctcam_chunk *cchunk,
+                                           struct mlxsw_sp_acl_ctcam_entry *centry,
+                                           struct mlxsw_sp_acl_rule_info *rulei);
 static inline unsigned int
 mlxsw_sp_acl_ctcam_entry_offset(struct mlxsw_sp_acl_ctcam_entry *centry)
 {
@@ -144,6 +152,7 @@ struct mlxsw_sp_acl_atcam {
 
 struct mlxsw_sp_acl_atcam_region {
        struct rhashtable entries_ht; /* A-TCAM only */
+       struct list_head entries_list; /* A-TCAM only */
        struct mlxsw_sp_acl_ctcam_region cregion;
        const struct mlxsw_sp_acl_atcam_region_ops *ops;
        struct mlxsw_sp_acl_tcam_region *region;
@@ -154,7 +163,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 */
+       char enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key,
+                                                           * minus delta bits.
+                                                           */
        u8 erp_id;
 };
 
@@ -164,10 +175,17 @@ struct mlxsw_sp_acl_atcam_chunk {
 
 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 */
+       struct {
+               u16 start;
+               u8 mask;
+               u8 value;
+       } delta_info;
        struct mlxsw_sp_acl_ctcam_entry centry;
        struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
-       struct mlxsw_sp_acl_erp *erp;
+       struct mlxsw_sp_acl_erp_mask *erp_mask;
 };
 
 static inline struct mlxsw_sp_acl_atcam_region *
@@ -204,20 +222,45 @@ void mlxsw_sp_acl_atcam_entry_del(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_acl_atcam_region *aregion,
                                  struct mlxsw_sp_acl_atcam_chunk *achunk,
                                  struct mlxsw_sp_acl_atcam_entry *aentry);
+int mlxsw_sp_acl_atcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                           struct mlxsw_sp_acl_atcam_region *aregion,
+                                           struct mlxsw_sp_acl_atcam_chunk *achunk,
+                                           struct mlxsw_sp_acl_atcam_entry *aentry,
+                                           struct mlxsw_sp_acl_rule_info *rulei);
 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);
 
-struct mlxsw_sp_acl_erp;
+struct mlxsw_sp_acl_erp_delta;
 
-bool mlxsw_sp_acl_erp_is_ctcam_erp(const struct mlxsw_sp_acl_erp *erp);
-u8 mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp *erp);
-struct mlxsw_sp_acl_erp *
-mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
-                    const char *mask, bool ctcam);
-void mlxsw_sp_acl_erp_put(struct mlxsw_sp_acl_atcam_region *aregion,
-                         struct mlxsw_sp_acl_erp *erp);
+u16 mlxsw_sp_acl_erp_delta_start(const struct mlxsw_sp_acl_erp_delta *delta);
+u8 mlxsw_sp_acl_erp_delta_mask(const struct mlxsw_sp_acl_erp_delta *delta);
+u8 mlxsw_sp_acl_erp_delta_value(const struct mlxsw_sp_acl_erp_delta *delta,
+                               const char *enc_key);
+void mlxsw_sp_acl_erp_delta_clear(const struct mlxsw_sp_acl_erp_delta *delta,
+                                 const char *enc_key);
+
+struct mlxsw_sp_acl_erp_mask;
+
+bool
+mlxsw_sp_acl_erp_mask_is_ctcam(const struct mlxsw_sp_acl_erp_mask *erp_mask);
+u8 mlxsw_sp_acl_erp_mask_erp_id(const struct mlxsw_sp_acl_erp_mask *erp_mask);
+const struct mlxsw_sp_acl_erp_delta *
+mlxsw_sp_acl_erp_delta(const struct mlxsw_sp_acl_erp_mask *erp_mask);
+struct mlxsw_sp_acl_erp_mask *
+mlxsw_sp_acl_erp_mask_get(struct mlxsw_sp_acl_atcam_region *aregion,
+                         const char *mask, bool ctcam);
+void mlxsw_sp_acl_erp_mask_put(struct mlxsw_sp_acl_atcam_region *aregion,
+                              struct mlxsw_sp_acl_erp_mask *erp_mask);
+int mlxsw_sp_acl_erp_bf_insert(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);
+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_region_fini(struct mlxsw_sp_acl_atcam_region *aregion);
 int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp,
@@ -225,4 +268,22 @@ int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp,
 void mlxsw_sp_acl_erps_fini(struct mlxsw_sp *mlxsw_sp,
                            struct mlxsw_sp_acl_atcam *atcam);
 
+struct mlxsw_sp_acl_bf;
+
+int
+mlxsw_sp_acl_bf_entry_add(struct mlxsw_sp *mlxsw_sp,
+                         struct mlxsw_sp_acl_bf *bf,
+                         struct mlxsw_sp_acl_atcam_region *aregion,
+                         unsigned int erp_bank,
+                         struct mlxsw_sp_acl_atcam_entry *aentry);
+void
+mlxsw_sp_acl_bf_entry_del(struct mlxsw_sp *mlxsw_sp,
+                         struct mlxsw_sp_acl_bf *bf,
+                         struct mlxsw_sp_acl_atcam_region *aregion,
+                         unsigned int erp_bank,
+                         struct mlxsw_sp_acl_atcam_entry *aentry);
+struct mlxsw_sp_acl_bf *
+mlxsw_sp_acl_bf_init(struct mlxsw_sp *mlxsw_sp, unsigned int num_erp_banks);
+void mlxsw_sp_acl_bf_fini(struct mlxsw_sp_acl_bf *bf);
+
 #endif
index a3db033..055cc69 100644 (file)
@@ -15,6 +15,7 @@
 struct mlxsw_sp_fid_family;
 
 struct mlxsw_sp_fid_core {
+       struct rhashtable fid_ht;
        struct rhashtable vni_ht;
        struct mlxsw_sp_fid_family *fid_family_arr[MLXSW_SP_FID_TYPE_MAX];
        unsigned int *port_fid_mappings;
@@ -26,10 +27,13 @@ struct mlxsw_sp_fid {
        unsigned int ref_count;
        u16 fid_index;
        struct mlxsw_sp_fid_family *fid_family;
+       struct rhash_head ht_node;
 
        struct rhash_head vni_ht_node;
+       enum mlxsw_sp_nve_type nve_type;
        __be32 vni;
        u32 nve_flood_index;
+       int nve_ifindex;
        u8 vni_valid:1,
           nve_flood_index_valid:1;
 };
@@ -44,6 +48,12 @@ struct mlxsw_sp_fid_8021d {
        int br_ifindex;
 };
 
+static const struct rhashtable_params mlxsw_sp_fid_ht_params = {
+       .key_len = sizeof_field(struct mlxsw_sp_fid, fid_index),
+       .key_offset = offsetof(struct mlxsw_sp_fid, fid_index),
+       .head_offset = offsetof(struct mlxsw_sp_fid, ht_node),
+};
+
 static const struct rhashtable_params mlxsw_sp_fid_vni_ht_params = {
        .key_len = sizeof_field(struct mlxsw_sp_fid, vni),
        .key_offset = offsetof(struct mlxsw_sp_fid, vni),
@@ -75,6 +85,8 @@ struct mlxsw_sp_fid_ops {
        int (*nve_flood_index_set)(struct mlxsw_sp_fid *fid,
                                   u32 nve_flood_index);
        void (*nve_flood_index_clear)(struct mlxsw_sp_fid *fid);
+       void (*fdb_clear_offload)(const struct mlxsw_sp_fid *fid,
+                                 const struct net_device *nve_dev);
 };
 
 struct mlxsw_sp_fid_family {
@@ -89,6 +101,7 @@ struct mlxsw_sp_fid_family {
        enum mlxsw_sp_rif_type rif_type;
        const struct mlxsw_sp_fid_ops *ops;
        struct mlxsw_sp *mlxsw_sp;
+       u8 lag_vid_valid:1;
 };
 
 static const int mlxsw_sp_sfgc_uc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = {
@@ -113,6 +126,45 @@ static const int *mlxsw_sp_packet_type_sfgc_types[] = {
        [MLXSW_SP_FLOOD_TYPE_MC]        = mlxsw_sp_sfgc_mc_packet_types,
 };
 
+bool mlxsw_sp_fid_lag_vid_valid(const struct mlxsw_sp_fid *fid)
+{
+       return fid->fid_family->lag_vid_valid;
+}
+
+struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_index(struct mlxsw_sp *mlxsw_sp,
+                                                 u16 fid_index)
+{
+       struct mlxsw_sp_fid *fid;
+
+       fid = rhashtable_lookup_fast(&mlxsw_sp->fid_core->fid_ht, &fid_index,
+                                    mlxsw_sp_fid_ht_params);
+       if (fid)
+               fid->ref_count++;
+
+       return fid;
+}
+
+int mlxsw_sp_fid_nve_ifindex(const struct mlxsw_sp_fid *fid, int *nve_ifindex)
+{
+       if (!fid->vni_valid)
+               return -EINVAL;
+
+       *nve_ifindex = fid->nve_ifindex;
+
+       return 0;
+}
+
+int mlxsw_sp_fid_nve_type(const struct mlxsw_sp_fid *fid,
+                         enum mlxsw_sp_nve_type *p_type)
+{
+       if (!fid->vni_valid)
+               return -EINVAL;
+
+       *p_type = fid->nve_type;
+
+       return 0;
+}
+
 struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp,
                                                __be32 vni)
 {
@@ -173,7 +225,8 @@ bool mlxsw_sp_fid_nve_flood_index_is_set(const struct mlxsw_sp_fid *fid)
        return fid->nve_flood_index_valid;
 }
 
-int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, __be32 vni)
+int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, enum mlxsw_sp_nve_type type,
+                        __be32 vni, int nve_ifindex)
 {
        struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
        const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
@@ -183,6 +236,8 @@ int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, __be32 vni)
        if (WARN_ON(!ops->vni_set || fid->vni_valid))
                return -EINVAL;
 
+       fid->nve_type = type;
+       fid->nve_ifindex = nve_ifindex;
        fid->vni = vni;
        err = rhashtable_lookup_insert_fast(&mlxsw_sp->fid_core->vni_ht,
                                            &fid->vni_ht_node,
@@ -224,6 +279,16 @@ bool mlxsw_sp_fid_vni_is_set(const struct mlxsw_sp_fid *fid)
        return fid->vni_valid;
 }
 
+void mlxsw_sp_fid_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
+                                   const struct net_device *nve_dev)
+{
+       struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
+       const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
+
+       if (ops->fdb_clear_offload)
+               ops->fdb_clear_offload(fid, nve_dev);
+}
+
 static const struct mlxsw_sp_flood_table *
 mlxsw_sp_fid_flood_table_lookup(const struct mlxsw_sp_fid *fid,
                                enum mlxsw_sp_flood_type packet_type)
@@ -284,11 +349,6 @@ void mlxsw_sp_fid_port_vid_unmap(struct mlxsw_sp_fid *fid,
        fid->fid_family->ops->port_vid_unmap(fid, mlxsw_sp_port, vid);
 }
 
-enum mlxsw_sp_rif_type mlxsw_sp_fid_rif_type(const struct mlxsw_sp_fid *fid)
-{
-       return fid->fid_family->rif_type;
-}
-
 u16 mlxsw_sp_fid_index(const struct mlxsw_sp_fid *fid)
 {
        return fid->fid_index;
@@ -304,6 +364,11 @@ void mlxsw_sp_fid_rif_set(struct mlxsw_sp_fid *fid, struct mlxsw_sp_rif *rif)
        fid->rif = rif;
 }
 
+struct mlxsw_sp_rif *mlxsw_sp_fid_rif(const struct mlxsw_sp_fid *fid)
+{
+       return fid->rif;
+}
+
 enum mlxsw_sp_rif_type
 mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp,
                           enum mlxsw_sp_fid_type type)
@@ -568,7 +633,7 @@ mlxsw_sp_fid_8021d_compare(const struct mlxsw_sp_fid *fid, const void *arg)
 
 static u16 mlxsw_sp_fid_8021d_flood_index(const struct mlxsw_sp_fid *fid)
 {
-       return fid->fid_index - fid->fid_family->start_index;
+       return fid->fid_index - VLAN_N_VID;
 }
 
 static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
@@ -713,6 +778,13 @@ static void mlxsw_sp_fid_8021d_nve_flood_index_clear(struct mlxsw_sp_fid *fid)
                            fid->vni_valid, 0, false);
 }
 
+static void
+mlxsw_sp_fid_8021d_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
+                                    const struct net_device *nve_dev)
+{
+       br_fdb_clear_offload(nve_dev, 0);
+}
+
 static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = {
        .setup                  = mlxsw_sp_fid_8021d_setup,
        .configure              = mlxsw_sp_fid_8021d_configure,
@@ -726,6 +798,7 @@ static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = {
        .vni_clear              = mlxsw_sp_fid_8021d_vni_clear,
        .nve_flood_index_set    = mlxsw_sp_fid_8021d_nve_flood_index_set,
        .nve_flood_index_clear  = mlxsw_sp_fid_8021d_nve_flood_index_clear,
+       .fdb_clear_offload      = mlxsw_sp_fid_8021d_fdb_clear_offload,
 };
 
 static const struct mlxsw_sp_flood_table mlxsw_sp_fid_8021d_flood_tables[] = {
@@ -759,6 +832,48 @@ static const struct mlxsw_sp_fid_family mlxsw_sp_fid_8021d_family = {
        .nr_flood_tables        = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables),
        .rif_type               = MLXSW_SP_RIF_TYPE_FID,
        .ops                    = &mlxsw_sp_fid_8021d_ops,
+       .lag_vid_valid          = 1,
+};
+
+static void
+mlxsw_sp_fid_8021q_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
+                                    const struct net_device *nve_dev)
+{
+       br_fdb_clear_offload(nve_dev, mlxsw_sp_fid_8021q_vid(fid));
+}
+
+static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_emu_ops = {
+       .setup                  = mlxsw_sp_fid_8021q_setup,
+       .configure              = mlxsw_sp_fid_8021d_configure,
+       .deconfigure            = mlxsw_sp_fid_8021d_deconfigure,
+       .index_alloc            = mlxsw_sp_fid_8021d_index_alloc,
+       .compare                = mlxsw_sp_fid_8021q_compare,
+       .flood_index            = mlxsw_sp_fid_8021d_flood_index,
+       .port_vid_map           = mlxsw_sp_fid_8021d_port_vid_map,
+       .port_vid_unmap         = mlxsw_sp_fid_8021d_port_vid_unmap,
+       .vni_set                = mlxsw_sp_fid_8021d_vni_set,
+       .vni_clear              = mlxsw_sp_fid_8021d_vni_clear,
+       .nve_flood_index_set    = mlxsw_sp_fid_8021d_nve_flood_index_set,
+       .nve_flood_index_clear  = mlxsw_sp_fid_8021d_nve_flood_index_clear,
+       .fdb_clear_offload      = mlxsw_sp_fid_8021q_fdb_clear_offload,
+};
+
+/* There are 4K-2 emulated 802.1Q FIDs, starting right after the 802.1D FIDs */
+#define MLXSW_SP_FID_8021Q_EMU_START   (VLAN_N_VID + MLXSW_SP_FID_8021D_MAX)
+#define MLXSW_SP_FID_8021Q_EMU_END     (MLXSW_SP_FID_8021Q_EMU_START + \
+                                        VLAN_VID_MASK - 2)
+
+/* Range and flood configuration must match mlxsw_config_profile */
+static const struct mlxsw_sp_fid_family mlxsw_sp_fid_8021q_emu_family = {
+       .type                   = MLXSW_SP_FID_TYPE_8021Q,
+       .fid_size               = sizeof(struct mlxsw_sp_fid_8021q),
+       .start_index            = MLXSW_SP_FID_8021Q_EMU_START,
+       .end_index              = MLXSW_SP_FID_8021Q_EMU_END,
+       .flood_tables           = mlxsw_sp_fid_8021d_flood_tables,
+       .nr_flood_tables        = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables),
+       .rif_type               = MLXSW_SP_RIF_TYPE_VLAN,
+       .ops                    = &mlxsw_sp_fid_8021q_emu_ops,
+       .lag_vid_valid          = 1,
 };
 
 static int mlxsw_sp_fid_rfid_configure(struct mlxsw_sp_fid *fid)
@@ -888,7 +1003,7 @@ static const struct mlxsw_sp_fid_family mlxsw_sp_fid_dummy_family = {
 };
 
 static const struct mlxsw_sp_fid_family *mlxsw_sp_fid_family_arr[] = {
-       [MLXSW_SP_FID_TYPE_8021Q]       = &mlxsw_sp_fid_8021q_family,
+       [MLXSW_SP_FID_TYPE_8021Q]       = &mlxsw_sp_fid_8021q_emu_family,
        [MLXSW_SP_FID_TYPE_8021D]       = &mlxsw_sp_fid_8021d_family,
        [MLXSW_SP_FID_TYPE_RFID]        = &mlxsw_sp_fid_rfid_family,
        [MLXSW_SP_FID_TYPE_DUMMY]       = &mlxsw_sp_fid_dummy_family,
@@ -944,10 +1059,17 @@ static struct mlxsw_sp_fid *mlxsw_sp_fid_get(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_configure;
 
+       err = rhashtable_insert_fast(&mlxsw_sp->fid_core->fid_ht, &fid->ht_node,
+                                    mlxsw_sp_fid_ht_params);
+       if (err)
+               goto err_rhashtable_insert;
+
        list_add(&fid->list, &fid_family->fids_list);
        fid->ref_count++;
        return fid;
 
+err_rhashtable_insert:
+       fid->fid_family->ops->deconfigure(fid);
 err_configure:
        __clear_bit(fid_index - fid_family->start_index,
                    fid_family->fids_bitmap);
@@ -959,19 +1081,18 @@ err_index_alloc:
 void mlxsw_sp_fid_put(struct mlxsw_sp_fid *fid)
 {
        struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
+       struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
 
-       if (--fid->ref_count == 1 && fid->rif) {
-               /* Destroy the associated RIF and let it drop the last
-                * reference on the FID.
-                */
-               return mlxsw_sp_rif_destroy(fid->rif);
-       } else if (fid->ref_count == 0) {
-               list_del(&fid->list);
-               fid->fid_family->ops->deconfigure(fid);
-               __clear_bit(fid->fid_index - fid_family->start_index,
-                           fid_family->fids_bitmap);
-               kfree(fid);
-       }
+       if (--fid->ref_count != 0)
+               return;
+
+       list_del(&fid->list);
+       rhashtable_remove_fast(&mlxsw_sp->fid_core->fid_ht,
+                              &fid->ht_node, mlxsw_sp_fid_ht_params);
+       fid->fid_family->ops->deconfigure(fid);
+       __clear_bit(fid->fid_index - fid_family->start_index,
+                   fid_family->fids_bitmap);
+       kfree(fid);
 }
 
 struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_get(struct mlxsw_sp *mlxsw_sp, u16 vid)
@@ -985,6 +1106,12 @@ struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_get(struct mlxsw_sp *mlxsw_sp,
        return mlxsw_sp_fid_get(mlxsw_sp, MLXSW_SP_FID_TYPE_8021D, &br_ifindex);
 }
 
+struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_lookup(struct mlxsw_sp *mlxsw_sp,
+                                              u16 vid)
+{
+       return mlxsw_sp_fid_lookup(mlxsw_sp, MLXSW_SP_FID_TYPE_8021Q, &vid);
+}
+
 struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_lookup(struct mlxsw_sp *mlxsw_sp,
                                               int br_ifindex)
 {
@@ -1126,9 +1253,13 @@ int mlxsw_sp_fids_init(struct mlxsw_sp *mlxsw_sp)
                return -ENOMEM;
        mlxsw_sp->fid_core = fid_core;
 
+       err = rhashtable_init(&fid_core->fid_ht, &mlxsw_sp_fid_ht_params);
+       if (err)
+               goto err_rhashtable_fid_init;
+
        err = rhashtable_init(&fid_core->vni_ht, &mlxsw_sp_fid_vni_ht_params);
        if (err)
-               goto err_rhashtable_init;
+               goto err_rhashtable_vni_init;
 
        fid_core->port_fid_mappings = kcalloc(max_ports, sizeof(unsigned int),
                                              GFP_KERNEL);
@@ -1157,7 +1288,9 @@ err_fid_ops_register:
        kfree(fid_core->port_fid_mappings);
 err_alloc_port_fid_mappings:
        rhashtable_destroy(&fid_core->vni_ht);
-err_rhashtable_init:
+err_rhashtable_vni_init:
+       rhashtable_destroy(&fid_core->fid_ht);
+err_rhashtable_fid_init:
        kfree(fid_core);
        return err;
 }
@@ -1172,5 +1305,6 @@ void mlxsw_sp_fids_fini(struct mlxsw_sp *mlxsw_sp)
                                               fid_core->fid_family_arr[i]);
        kfree(fid_core->port_fid_mappings);
        rhashtable_destroy(&fid_core->vni_ht);
+       rhashtable_destroy(&fid_core->fid_ht);
        kfree(fid_core);
 }
index 8d21197..ff07235 100644 (file)
@@ -406,7 +406,7 @@ int mlxsw_sp_flower_replace(struct mlxsw_sp *mlxsw_sp,
        if (IS_ERR(ruleset))
                return PTR_ERR(ruleset);
 
-       rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset, f->cookie,
+       rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset, f->cookie, NULL,
                                        f->common.extack);
        if (IS_ERR(rule)) {
                err = PTR_ERR(rule);
index ad06d99..0a31fff 100644 (file)
@@ -174,6 +174,20 @@ mlxsw_sp_nve_mc_record_ops_arr[] = {
        [MLXSW_SP_L3_PROTO_IPV6] = &mlxsw_sp_nve_mc_record_ipv6_ops,
 };
 
+int mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip,
+                                   enum mlxsw_sp_l3proto proto,
+                                   union mlxsw_sp_l3addr *addr)
+{
+       switch (proto) {
+       case MLXSW_SP_L3_PROTO_IPV4:
+               addr->addr4 = cpu_to_be32(uip);
+               return 0;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+       }
+}
+
 static struct mlxsw_sp_nve_mc_list *
 mlxsw_sp_nve_mc_list_find(struct mlxsw_sp *mlxsw_sp,
                          const struct mlxsw_sp_nve_mc_list_key *key)
@@ -560,7 +574,7 @@ static void mlxsw_sp_nve_mc_list_ip_del(struct mlxsw_sp *mlxsw_sp,
 
        mc_record = mlxsw_sp_nve_mc_record_find(mc_list, proto, addr,
                                                &mc_entry);
-       if (WARN_ON(!mc_record))
+       if (!mc_record)
                return;
 
        mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
@@ -647,7 +661,7 @@ void mlxsw_sp_nve_flood_ip_del(struct mlxsw_sp *mlxsw_sp,
 
        key.fid_index = mlxsw_sp_fid_index(fid);
        mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
-       if (WARN_ON(!mc_list))
+       if (!mc_list)
                return;
 
        mlxsw_sp_nve_fid_flood_index_clear(fid, mc_list);
@@ -775,6 +789,21 @@ static void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
        mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
 }
 
+static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
+                                          const struct mlxsw_sp_fid *fid,
+                                          const struct net_device *nve_dev,
+                                          __be32 vni)
+{
+       const struct mlxsw_sp_nve_ops *ops;
+       enum mlxsw_sp_nve_type type;
+
+       if (WARN_ON(mlxsw_sp_fid_nve_type(fid, &type)))
+               return;
+
+       ops = mlxsw_sp->nve->nve_ops_arr[type];
+       ops->fdb_clear_offload(nve_dev, vni);
+}
+
 int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
                            struct mlxsw_sp_nve_params *params,
                            struct netlink_ext_ack *extack)
@@ -803,7 +832,8 @@ int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
                return err;
        }
 
-       err = mlxsw_sp_fid_vni_set(fid, params->vni);
+       err = mlxsw_sp_fid_vni_set(fid, params->type, params->vni,
+                                  params->dev->ifindex);
        if (err) {
                NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID");
                goto err_fid_vni_set;
@@ -811,8 +841,16 @@ int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
 
        nve->config = config;
 
+       err = ops->fdb_replay(params->dev, params->vni);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Failed to offload the FDB");
+               goto err_fdb_replay;
+       }
+
        return 0;
 
+err_fdb_replay:
+       mlxsw_sp_fid_vni_clear(fid);
 err_fid_vni_set:
        mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
        return err;
@@ -822,9 +860,27 @@ void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
                              struct mlxsw_sp_fid *fid)
 {
        u16 fid_index = mlxsw_sp_fid_index(fid);
+       struct net_device *nve_dev;
+       int nve_ifindex;
+       __be32 vni;
 
        mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
        mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
+
+       if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
+                   mlxsw_sp_fid_vni(fid, &vni)))
+               goto out;
+
+       nve_dev = dev_get_by_index(&init_net, nve_ifindex);
+       if (!nve_dev)
+               goto out;
+
+       mlxsw_sp_nve_fdb_clear_offload(mlxsw_sp, fid, nve_dev, vni);
+       mlxsw_sp_fid_fdb_clear_offload(fid, nve_dev);
+
+       dev_put(nve_dev);
+
+out:
        mlxsw_sp_fid_vni_clear(fid);
        mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
 }
@@ -977,6 +1033,6 @@ void mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp)
 {
        WARN_ON(mlxsw_sp->nve->num_nve_tunnels);
        rhashtable_destroy(&mlxsw_sp->nve->mc_list_ht);
-       mlxsw_sp->nve = NULL;
        kfree(mlxsw_sp->nve);
+       mlxsw_sp->nve = NULL;
 }
index 4cc3297..02937ea 100644 (file)
@@ -41,6 +41,8 @@ struct mlxsw_sp_nve_ops {
        int (*init)(struct mlxsw_sp_nve *nve,
                    const struct mlxsw_sp_nve_config *config);
        void (*fini)(struct mlxsw_sp_nve *nve);
+       int (*fdb_replay)(const struct net_device *nve_dev, __be32 vni);
+       void (*fdb_clear_offload)(const struct net_device *nve_dev, __be32 vni);
 };
 
 extern const struct mlxsw_sp_nve_ops mlxsw_sp1_nve_vxlan_ops;
index d21c7be..74e564c 100644 (file)
@@ -17,7 +17,8 @@
 #define MLXSW_SP_NVE_VXLAN_PARSING_DEPTH 128
 #define MLXSW_SP_NVE_DEFAULT_PARSING_DEPTH 96
 
-#define MLXSW_SP_NVE_VXLAN_SUPPORTED_FLAGS     VXLAN_F_UDP_ZERO_CSUM_TX
+#define MLXSW_SP_NVE_VXLAN_SUPPORTED_FLAGS     (VXLAN_F_UDP_ZERO_CSUM_TX | \
+                                                VXLAN_F_LEARN)
 
 static bool mlxsw_sp1_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
                                            const struct net_device *dev,
@@ -61,11 +62,6 @@ static bool mlxsw_sp1_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
                return false;
        }
 
-       if (cfg->flags & VXLAN_F_LEARN) {
-               NL_SET_ERR_MSG_MOD(extack, "VxLAN: Learning is not supported");
-               return false;
-       }
-
        if (!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM_TX)) {
                NL_SET_ERR_MSG_MOD(extack, "VxLAN: UDP checksum is not supported");
                return false;
@@ -215,12 +211,30 @@ static void mlxsw_sp1_nve_vxlan_fini(struct mlxsw_sp_nve *nve)
                                 config->udp_dport);
 }
 
+static int
+mlxsw_sp_nve_vxlan_fdb_replay(const struct net_device *nve_dev, __be32 vni)
+{
+       if (WARN_ON(!netif_is_vxlan(nve_dev)))
+               return -EINVAL;
+       return vxlan_fdb_replay(nve_dev, vni, &mlxsw_sp_switchdev_notifier);
+}
+
+static void
+mlxsw_sp_nve_vxlan_clear_offload(const struct net_device *nve_dev, __be32 vni)
+{
+       if (WARN_ON(!netif_is_vxlan(nve_dev)))
+               return;
+       vxlan_fdb_clear_offload(nve_dev, vni);
+}
+
 const struct mlxsw_sp_nve_ops mlxsw_sp1_nve_vxlan_ops = {
        .type           = MLXSW_SP_NVE_TYPE_VXLAN,
        .can_offload    = mlxsw_sp1_nve_vxlan_can_offload,
        .nve_config     = mlxsw_sp_nve_vxlan_config,
        .init           = mlxsw_sp1_nve_vxlan_init,
        .fini           = mlxsw_sp1_nve_vxlan_fini,
+       .fdb_replay     = mlxsw_sp_nve_vxlan_fdb_replay,
+       .fdb_clear_offload = mlxsw_sp_nve_vxlan_clear_offload,
 };
 
 static bool mlxsw_sp2_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
@@ -246,4 +260,6 @@ const struct mlxsw_sp_nve_ops mlxsw_sp2_nve_vxlan_ops = {
        .nve_config     = mlxsw_sp_nve_vxlan_config,
        .init           = mlxsw_sp2_nve_vxlan_init,
        .fini           = mlxsw_sp2_nve_vxlan_fini,
+       .fdb_replay     = mlxsw_sp_nve_vxlan_fdb_replay,
+       .fdb_clear_offload = mlxsw_sp_nve_vxlan_clear_offload,
 };
index 9e9bb57..98e5ffd 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/gcd.h>
 #include <linux/random.h>
 #include <linux/if_macvlan.h>
+#include <linux/refcount.h>
 #include <net/netevent.h>
 #include <net/neighbour.h>
 #include <net/arp.h>
@@ -70,6 +71,8 @@ struct mlxsw_sp_router {
        bool aborted;
        struct notifier_block fib_nb;
        struct notifier_block netevent_nb;
+       struct notifier_block inetaddr_nb;
+       struct notifier_block inet6addr_nb;
        const struct mlxsw_sp_rif_ops **rif_ops_arr;
        const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
 };
@@ -104,6 +107,7 @@ struct mlxsw_sp_rif_params {
 
 struct mlxsw_sp_rif_subport {
        struct mlxsw_sp_rif common;
+       refcount_t ref_count;
        union {
                u16 system_port;
                u16 lag_id;
@@ -136,6 +140,7 @@ struct mlxsw_sp_rif_ops {
        void (*fdb_del)(struct mlxsw_sp_rif *rif, const char *mac);
 };
 
+static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif);
 static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree);
 static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_lpm_tree *lpm_tree);
@@ -1275,15 +1280,12 @@ mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,
 {
        u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;
        enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
-       struct net_device *ipip_ul_dev;
 
        if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
                return false;
 
-       ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev);
        return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip,
-                                                ul_tb_id, ipip_entry) &&
-              (!ipip_ul_dev || ipip_ul_dev == ul_dev);
+                                                ul_tb_id, ipip_entry);
 }
 
 /* Given decap parameters, find the corresponding IPIP entry. */
@@ -6300,6 +6302,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
                err = -ENOMEM;
                goto err_rif_alloc;
        }
+       dev_hold(rif->dev);
        rif->mlxsw_sp = mlxsw_sp;
        rif->ops = ops;
 
@@ -6338,6 +6341,7 @@ err_configure:
        if (fid)
                mlxsw_sp_fid_put(fid);
 err_fid_get:
+       dev_put(rif->dev);
        kfree(rif);
 err_rif_alloc:
 err_rif_index_alloc:
@@ -6346,7 +6350,7 @@ err_rif_index_alloc:
        return ERR_PTR(err);
 }
 
-void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
+static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
 {
        const struct mlxsw_sp_rif_ops *ops = rif->ops;
        struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
@@ -6365,6 +6369,7 @@ void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
        if (fid)
                /* Loopback RIFs are not associated with a FID. */
                mlxsw_sp_fid_put(fid);
+       dev_put(rif->dev);
        kfree(rif);
        vr->rif_count--;
        mlxsw_sp_vr_put(mlxsw_sp, vr);
@@ -6395,6 +6400,40 @@ mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
                params->system_port = mlxsw_sp_port->local_port;
 }
 
+static struct mlxsw_sp_rif_subport *
+mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
+{
+       return container_of(rif, struct mlxsw_sp_rif_subport, common);
+}
+
+static struct mlxsw_sp_rif *
+mlxsw_sp_rif_subport_get(struct mlxsw_sp *mlxsw_sp,
+                        const struct mlxsw_sp_rif_params *params,
+                        struct netlink_ext_ack *extack)
+{
+       struct mlxsw_sp_rif_subport *rif_subport;
+       struct mlxsw_sp_rif *rif;
+
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, params->dev);
+       if (!rif)
+               return mlxsw_sp_rif_create(mlxsw_sp, params, extack);
+
+       rif_subport = mlxsw_sp_rif_subport_rif(rif);
+       refcount_inc(&rif_subport->ref_count);
+       return rif;
+}
+
+static void mlxsw_sp_rif_subport_put(struct mlxsw_sp_rif *rif)
+{
+       struct mlxsw_sp_rif_subport *rif_subport;
+
+       rif_subport = mlxsw_sp_rif_subport_rif(rif);
+       if (!refcount_dec_and_test(&rif_subport->ref_count))
+               return;
+
+       mlxsw_sp_rif_destroy(rif);
+}
+
 static int
 mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
                               struct net_device *l3_dev,
@@ -6402,22 +6441,18 @@ mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
 {
        struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       struct mlxsw_sp_rif_params params = {
+               .dev = l3_dev,
+       };
        u16 vid = mlxsw_sp_port_vlan->vid;
        struct mlxsw_sp_rif *rif;
        struct mlxsw_sp_fid *fid;
        int err;
 
-       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
-       if (!rif) {
-               struct mlxsw_sp_rif_params params = {
-                       .dev = l3_dev,
-               };
-
-               mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
-               rif = mlxsw_sp_rif_create(mlxsw_sp, &params, extack);
-               if (IS_ERR(rif))
-                       return PTR_ERR(rif);
-       }
+       mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
+       rif = mlxsw_sp_rif_subport_get(mlxsw_sp, &params, extack);
+       if (IS_ERR(rif))
+               return PTR_ERR(rif);
 
        /* FID was already created, just take a reference */
        fid = rif->ops->fid_get(rif, extack);
@@ -6444,6 +6479,7 @@ err_port_vid_learning_set:
        mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
 err_fid_port_vid_map:
        mlxsw_sp_fid_put(fid);
+       mlxsw_sp_rif_subport_put(rif);
        return err;
 }
 
@@ -6452,6 +6488,7 @@ mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
        struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
+       struct mlxsw_sp_rif *rif = mlxsw_sp_fid_rif(fid);
        u16 vid = mlxsw_sp_port_vlan->vid;
 
        if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
@@ -6461,10 +6498,8 @@ mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
        mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
        mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
        mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
-       /* If router port holds the last reference on the rFID, then the
-        * associated Sub-port RIF will be destroyed.
-        */
        mlxsw_sp_fid_put(fid);
+       mlxsw_sp_rif_subport_put(rif);
 }
 
 static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
@@ -6500,8 +6535,8 @@ static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
            netif_is_ovs_port(port_dev))
                return 0;
 
-       return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1,
-                                                extack);
+       return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event,
+                                                MLXSW_SP_DEFAULT_VID, extack);
 }
 
 static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
@@ -6534,15 +6569,15 @@ static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
        if (netif_is_bridge_port(lag_dev))
                return 0;
 
-       return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1,
-                                            extack);
+       return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event,
+                                            MLXSW_SP_DEFAULT_VID, extack);
 }
 
-static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
+static int mlxsw_sp_inetaddr_bridge_event(struct mlxsw_sp *mlxsw_sp,
+                                         struct net_device *l3_dev,
                                          unsigned long event,
                                          struct netlink_ext_ack *extack)
 {
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
        struct mlxsw_sp_rif_params params = {
                .dev = l3_dev,
        };
@@ -6563,7 +6598,8 @@ static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
        return 0;
 }
 
-static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
+static int mlxsw_sp_inetaddr_vlan_event(struct mlxsw_sp *mlxsw_sp,
+                                       struct net_device *vlan_dev,
                                        unsigned long event,
                                        struct netlink_ext_ack *extack)
 {
@@ -6580,7 +6616,8 @@ static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
                return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
                                                     vid, extack);
        else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
-               return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event, extack);
+               return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, vlan_dev, event,
+                                                     extack);
 
        return 0;
 }
@@ -6681,16 +6718,11 @@ void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
                            mlxsw_sp_fid_index(rif->fid), false);
 }
 
-static int mlxsw_sp_inetaddr_macvlan_event(struct net_device *macvlan_dev,
+static int mlxsw_sp_inetaddr_macvlan_event(struct mlxsw_sp *mlxsw_sp,
+                                          struct net_device *macvlan_dev,
                                           unsigned long event,
                                           struct netlink_ext_ack *extack)
 {
-       struct mlxsw_sp *mlxsw_sp;
-
-       mlxsw_sp = mlxsw_sp_lower_get(macvlan_dev);
-       if (!mlxsw_sp)
-               return 0;
-
        switch (event) {
        case NETDEV_UP:
                return mlxsw_sp_rif_macvlan_add(mlxsw_sp, macvlan_dev, extack);
@@ -6702,7 +6734,35 @@ static int mlxsw_sp_inetaddr_macvlan_event(struct net_device *macvlan_dev,
        return 0;
 }
 
-static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
+static int mlxsw_sp_router_port_check_rif_addr(struct mlxsw_sp *mlxsw_sp,
+                                              struct net_device *dev,
+                                              const unsigned char *dev_addr,
+                                              struct netlink_ext_ack *extack)
+{
+       struct mlxsw_sp_rif *rif;
+       int i;
+
+       /* A RIF is not created for macvlan netdevs. Their MAC is used to
+        * populate the FDB
+        */
+       if (netif_is_macvlan(dev))
+               return 0;
+
+       for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
+               rif = mlxsw_sp->router->rifs[i];
+               if (rif && rif->dev != dev &&
+                   !ether_addr_equal_masked(rif->dev->dev_addr, dev_addr,
+                                            mlxsw_sp->mac_mask)) {
+                       NL_SET_ERR_MSG_MOD(extack, "All router interface MAC addresses must have the same prefix");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int __mlxsw_sp_inetaddr_event(struct mlxsw_sp *mlxsw_sp,
+                                    struct net_device *dev,
                                     unsigned long event,
                                     struct netlink_ext_ack *extack)
 {
@@ -6711,21 +6771,24 @@ static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
        else if (netif_is_lag_master(dev))
                return mlxsw_sp_inetaddr_lag_event(dev, event, extack);
        else if (netif_is_bridge_master(dev))
-               return mlxsw_sp_inetaddr_bridge_event(dev, event, extack);
+               return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, dev, event,
+                                                     extack);
        else if (is_vlan_dev(dev))
-               return mlxsw_sp_inetaddr_vlan_event(dev, event, extack);
+               return mlxsw_sp_inetaddr_vlan_event(mlxsw_sp, dev, event,
+                                                   extack);
        else if (netif_is_macvlan(dev))
-               return mlxsw_sp_inetaddr_macvlan_event(dev, event, extack);
+               return mlxsw_sp_inetaddr_macvlan_event(mlxsw_sp, dev, event,
+                                                      extack);
        else
                return 0;
 }
 
-int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
-                           unsigned long event, void *ptr)
+static int mlxsw_sp_inetaddr_event(struct notifier_block *nb,
+                                  unsigned long event, void *ptr)
 {
        struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
        struct net_device *dev = ifa->ifa_dev->dev;
-       struct mlxsw_sp *mlxsw_sp;
+       struct mlxsw_sp_router *router;
        struct mlxsw_sp_rif *rif;
        int err = 0;
 
@@ -6733,15 +6796,12 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
        if (event == NETDEV_UP)
                goto out;
 
-       mlxsw_sp = mlxsw_sp_lower_get(dev);
-       if (!mlxsw_sp)
-               goto out;
-
-       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
+       router = container_of(nb, struct mlxsw_sp_router, inetaddr_nb);
+       rif = mlxsw_sp_rif_find_by_dev(router->mlxsw_sp, dev);
        if (!mlxsw_sp_rif_should_config(rif, dev, event))
                goto out;
 
-       err = __mlxsw_sp_inetaddr_event(dev, event, NULL);
+       err = __mlxsw_sp_inetaddr_event(router->mlxsw_sp, dev, event, NULL);
 out:
        return notifier_from_errno(err);
 }
@@ -6763,13 +6823,19 @@ int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused,
        if (!mlxsw_sp_rif_should_config(rif, dev, event))
                goto out;
 
-       err = __mlxsw_sp_inetaddr_event(dev, event, ivi->extack);
+       err = mlxsw_sp_router_port_check_rif_addr(mlxsw_sp, dev, dev->dev_addr,
+                                                 ivi->extack);
+       if (err)
+               goto out;
+
+       err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, ivi->extack);
 out:
        return notifier_from_errno(err);
 }
 
 struct mlxsw_sp_inet6addr_event_work {
        struct work_struct work;
+       struct mlxsw_sp *mlxsw_sp;
        struct net_device *dev;
        unsigned long event;
 };
@@ -6778,21 +6844,18 @@ static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
 {
        struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
                container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
+       struct mlxsw_sp *mlxsw_sp = inet6addr_work->mlxsw_sp;
        struct net_device *dev = inet6addr_work->dev;
        unsigned long event = inet6addr_work->event;
-       struct mlxsw_sp *mlxsw_sp;
        struct mlxsw_sp_rif *rif;
 
        rtnl_lock();
-       mlxsw_sp = mlxsw_sp_lower_get(dev);
-       if (!mlxsw_sp)
-               goto out;
 
        rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
        if (!mlxsw_sp_rif_should_config(rif, dev, event))
                goto out;
 
-       __mlxsw_sp_inetaddr_event(dev, event, NULL);
+       __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, NULL);
 out:
        rtnl_unlock();
        dev_put(dev);
@@ -6800,25 +6863,25 @@ out:
 }
 
 /* Called with rcu_read_lock() */
-int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
-                            unsigned long event, void *ptr)
+static int mlxsw_sp_inet6addr_event(struct notifier_block *nb,
+                                   unsigned long event, void *ptr)
 {
        struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
        struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
        struct net_device *dev = if6->idev->dev;
+       struct mlxsw_sp_router *router;
 
        /* NETDEV_UP event is handled by mlxsw_sp_inet6addr_valid_event */
        if (event == NETDEV_UP)
                return NOTIFY_DONE;
 
-       if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
-               return NOTIFY_DONE;
-
        inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
        if (!inet6addr_work)
                return NOTIFY_BAD;
 
+       router = container_of(nb, struct mlxsw_sp_router, inet6addr_nb);
        INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
+       inet6addr_work->mlxsw_sp = router->mlxsw_sp;
        inet6addr_work->dev = dev;
        inet6addr_work->event = event;
        dev_hold(dev);
@@ -6844,7 +6907,12 @@ int mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused,
        if (!mlxsw_sp_rif_should_config(rif, dev, event))
                goto out;
 
-       err = __mlxsw_sp_inetaddr_event(dev, event, i6vi->extack);
+       err = mlxsw_sp_router_port_check_rif_addr(mlxsw_sp, dev, dev->dev_addr,
+                                                 i6vi->extack);
+       if (err)
+               goto out;
+
+       err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, i6vi->extack);
 out:
        return notifier_from_errno(err);
 }
@@ -6866,20 +6934,14 @@ static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
 }
 
-int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
+static int
+mlxsw_sp_router_port_change_event(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_rif *rif)
 {
-       struct mlxsw_sp *mlxsw_sp;
-       struct mlxsw_sp_rif *rif;
+       struct net_device *dev = rif->dev;
        u16 fid_index;
        int err;
 
-       mlxsw_sp = mlxsw_sp_lower_get(dev);
-       if (!mlxsw_sp)
-               return 0;
-
-       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
-       if (!rif)
-               return 0;
        fid_index = mlxsw_sp_fid_index(rif->fid);
 
        err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
@@ -6923,6 +6985,41 @@ err_rif_edit:
        return err;
 }
 
+static int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif,
+                           struct netdev_notifier_pre_changeaddr_info *info)
+{
+       struct netlink_ext_ack *extack;
+
+       extack = netdev_notifier_info_to_extack(&info->info);
+       return mlxsw_sp_router_port_check_rif_addr(rif->mlxsw_sp, rif->dev,
+                                                  info->dev_addr, extack);
+}
+
+int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
+                                        unsigned long event, void *ptr)
+{
+       struct mlxsw_sp *mlxsw_sp;
+       struct mlxsw_sp_rif *rif;
+
+       mlxsw_sp = mlxsw_sp_lower_get(dev);
+       if (!mlxsw_sp)
+               return 0;
+
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
+       if (!rif)
+               return 0;
+
+       switch (event) {
+       case NETDEV_CHANGEMTU: /* fall through */
+       case NETDEV_CHANGEADDR:
+               return mlxsw_sp_router_port_change_event(mlxsw_sp, rif);
+       case NETDEV_PRE_CHANGEADDR:
+               return mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr);
+       }
+
+       return 0;
+}
+
 static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
                                  struct net_device *l3_dev,
                                  struct netlink_ext_ack *extack)
@@ -6934,9 +7031,10 @@ static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
         */
        rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
        if (rif)
-               __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN, extack);
+               __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN,
+                                         extack);
 
-       return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP, extack);
+       return __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_UP, extack);
 }
 
 static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
@@ -6947,7 +7045,7 @@ static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
        rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
        if (!rif)
                return;
-       __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN, NULL);
+       __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN, NULL);
 }
 
 int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
@@ -7001,18 +7099,13 @@ static int mlxsw_sp_rif_macvlan_flush(struct mlxsw_sp_rif *rif)
                                             __mlxsw_sp_rif_macvlan_flush, rif);
 }
 
-static struct mlxsw_sp_rif_subport *
-mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
-{
-       return container_of(rif, struct mlxsw_sp_rif_subport, common);
-}
-
 static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
                                       const struct mlxsw_sp_rif_params *params)
 {
        struct mlxsw_sp_rif_subport *rif_subport;
 
        rif_subport = mlxsw_sp_rif_subport_rif(rif);
+       refcount_set(&rif_subport->ref_count, 1);
        rif_subport->vid = params->vid;
        rif_subport->lag = params->lag;
        if (params->lag)
@@ -7167,11 +7260,15 @@ static struct mlxsw_sp_fid *
 mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif,
                          struct netlink_ext_ack *extack)
 {
+       struct net_device *br_dev = rif->dev;
        u16 vid;
        int err;
 
        if (is_vlan_dev(rif->dev)) {
                vid = vlan_dev_vlan_id(rif->dev);
+               br_dev = vlan_dev_real_dev(rif->dev);
+               if (WARN_ON(!netif_is_bridge_master(br_dev)))
+                       return ERR_PTR(-EINVAL);
        } else {
                err = br_vlan_get_pvid(rif->dev, &vid);
                if (err < 0 || !vid) {
@@ -7180,7 +7277,7 @@ mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif,
                }
        }
 
-       return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
+       return mlxsw_sp_bridge_fid_get(rif->mlxsw_sp, br_dev, vid, extack);
 }
 
 static void mlxsw_sp_rif_vlan_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)
@@ -7270,7 +7367,7 @@ static struct mlxsw_sp_fid *
 mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif,
                         struct netlink_ext_ack *extack)
 {
-       return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
+       return mlxsw_sp_bridge_fid_get(rif->mlxsw_sp, rif->dev, 0, extack);
 }
 
 static void mlxsw_sp_rif_fid_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)
@@ -7296,6 +7393,15 @@ static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
        .fdb_del                = mlxsw_sp_rif_fid_fdb_del,
 };
 
+static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_emu_ops = {
+       .type                   = MLXSW_SP_RIF_TYPE_VLAN,
+       .rif_size               = sizeof(struct mlxsw_sp_rif),
+       .configure              = mlxsw_sp_rif_fid_configure,
+       .deconfigure            = mlxsw_sp_rif_fid_deconfigure,
+       .fid_get                = mlxsw_sp_rif_vlan_fid_get,
+       .fdb_del                = mlxsw_sp_rif_vlan_fdb_del,
+};
+
 static struct mlxsw_sp_rif_ipip_lb *
 mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
 {
@@ -7364,7 +7470,7 @@ static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
 
 static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
        [MLXSW_SP_RIF_TYPE_SUBPORT]     = &mlxsw_sp_rif_subport_ops,
-       [MLXSW_SP_RIF_TYPE_VLAN]        = &mlxsw_sp_rif_vlan_ops,
+       [MLXSW_SP_RIF_TYPE_VLAN]        = &mlxsw_sp_rif_vlan_emu_ops,
        [MLXSW_SP_RIF_TYPE_FID]         = &mlxsw_sp_rif_fid_ops,
        [MLXSW_SP_RIF_TYPE_IPIP_LB]     = &mlxsw_sp_rif_ipip_lb_ops,
 };
@@ -7555,6 +7661,16 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
        mlxsw_sp->router = router;
        router->mlxsw_sp = mlxsw_sp;
 
+       router->inetaddr_nb.notifier_call = mlxsw_sp_inetaddr_event;
+       err = register_inetaddr_notifier(&router->inetaddr_nb);
+       if (err)
+               goto err_register_inetaddr_notifier;
+
+       router->inet6addr_nb.notifier_call = mlxsw_sp_inet6addr_event;
+       err = register_inet6addr_notifier(&router->inet6addr_nb);
+       if (err)
+               goto err_register_inet6addr_notifier;
+
        INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
        err = __mlxsw_sp_router_init(mlxsw_sp);
        if (err)
@@ -7640,6 +7756,10 @@ err_ipips_init:
 err_rifs_init:
        __mlxsw_sp_router_fini(mlxsw_sp);
 err_router_init:
+       unregister_inet6addr_notifier(&router->inet6addr_nb);
+err_register_inet6addr_notifier:
+       unregister_inetaddr_notifier(&router->inetaddr_nb);
+err_register_inetaddr_notifier:
        kfree(mlxsw_sp->router);
        return err;
 }
@@ -7657,5 +7777,7 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
        mlxsw_sp_ipips_fini(mlxsw_sp);
        mlxsw_sp_rifs_fini(mlxsw_sp);
        __mlxsw_sp_router_fini(mlxsw_sp);
+       unregister_inet6addr_notifier(&mlxsw_sp->router->inet6addr_nb);
+       unregister_inetaddr_notifier(&mlxsw_sp->router->inetaddr_nb);
        kfree(mlxsw_sp->router);
 }
index d965fd2..ad5a9b9 100644 (file)
@@ -383,7 +383,7 @@ mlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry *span_entry)
 }
 
 static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap4 = {
-       .can_handle = is_gretap_dev,
+       .can_handle = netif_is_gretap,
        .parms = mlxsw_sp_span_entry_gretap4_parms,
        .configure = mlxsw_sp_span_entry_gretap4_configure,
        .deconfigure = mlxsw_sp_span_entry_gretap4_deconfigure,
@@ -484,7 +484,7 @@ mlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry *span_entry)
 
 static const
 struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap6 = {
-       .can_handle = is_ip6gretap_dev,
+       .can_handle = netif_is_ip6gretap,
        .parms = mlxsw_sp_span_entry_gretap6_parms,
        .configure = mlxsw_sp_span_entry_gretap6_configure,
        .deconfigure = mlxsw_sp_span_entry_gretap6_deconfigure,
index 739a51f..1bd2c6e 100644 (file)
@@ -85,13 +85,11 @@ struct mlxsw_sp_bridge_ops {
                           struct mlxsw_sp_bridge_port *bridge_port,
                           struct mlxsw_sp_port *mlxsw_sp_port);
        int (*vxlan_join)(struct mlxsw_sp_bridge_device *bridge_device,
-                         const struct net_device *vxlan_dev,
+                         const struct net_device *vxlan_dev, u16 vid,
                          struct netlink_ext_ack *extack);
-       void (*vxlan_leave)(struct mlxsw_sp_bridge_device *bridge_device,
-                           const struct net_device *vxlan_dev);
        struct mlxsw_sp_fid *
                (*fid_get)(struct mlxsw_sp_bridge_device *bridge_device,
-                          u16 vid);
+                          u16 vid, struct netlink_ext_ack *extack);
        struct mlxsw_sp_fid *
                (*fid_lookup)(struct mlxsw_sp_bridge_device *bridge_device,
                              u16 vid);
@@ -292,24 +290,6 @@ mlxsw_sp_bridge_port_destroy(struct mlxsw_sp_bridge_port *bridge_port)
        kfree(bridge_port);
 }
 
-static bool
-mlxsw_sp_bridge_port_should_destroy(const struct mlxsw_sp_bridge_port *
-                                   bridge_port)
-{
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_port->dev);
-
-       /* In case ports were pulled from out of a bridged LAG, then
-        * it's possible the reference count isn't zero, yet the bridge
-        * port should be destroyed, as it's no longer an upper of ours.
-        */
-       if (!mlxsw_sp && list_empty(&bridge_port->vlans_list))
-               return true;
-       else if (bridge_port->ref_count == 0)
-               return true;
-       else
-               return false;
-}
-
 static struct mlxsw_sp_bridge_port *
 mlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge,
                         struct net_device *brport_dev)
@@ -347,8 +327,7 @@ static void mlxsw_sp_bridge_port_put(struct mlxsw_sp_bridge *bridge,
 {
        struct mlxsw_sp_bridge_device *bridge_device;
 
-       bridge_port->ref_count--;
-       if (!mlxsw_sp_bridge_port_should_destroy(bridge_port))
+       if (--bridge_port->ref_count != 0)
                return;
        bridge_device = bridge_port->bridge_device;
        mlxsw_sp_bridge_port_destroy(bridge_port);
@@ -929,7 +908,8 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
 
 static int
 mlxsw_sp_port_vlan_fid_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
-                           struct mlxsw_sp_bridge_port *bridge_port)
+                           struct mlxsw_sp_bridge_port *bridge_port,
+                           struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
        struct mlxsw_sp_bridge_device *bridge_device;
@@ -939,7 +919,7 @@ mlxsw_sp_port_vlan_fid_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
        int err;
 
        bridge_device = bridge_port->bridge_device;
-       fid = bridge_device->ops->fid_get(bridge_device, vid);
+       fid = bridge_device->ops->fid_get(bridge_device, vid, extack);
        if (IS_ERR(fid))
                return PTR_ERR(fid);
 
@@ -1007,7 +987,8 @@ mlxsw_sp_port_pvid_determine(const struct mlxsw_sp_port *mlxsw_sp_port,
 
 static int
 mlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
-                              struct mlxsw_sp_bridge_port *bridge_port)
+                              struct mlxsw_sp_bridge_port *bridge_port,
+                              struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
        struct mlxsw_sp_bridge_vlan *bridge_vlan;
@@ -1015,12 +996,11 @@ mlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
        int err;
 
        /* No need to continue if only VLAN flags were changed */
-       if (mlxsw_sp_port_vlan->bridge_port) {
-               mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
+       if (mlxsw_sp_port_vlan->bridge_port)
                return 0;
-       }
 
-       err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port);
+       err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port,
+                                         extack);
        if (err)
                return err;
 
@@ -1097,16 +1077,33 @@ mlxsw_sp_port_vlan_bridge_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
 static int
 mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
                              struct mlxsw_sp_bridge_port *bridge_port,
-                             u16 vid, bool is_untagged, bool is_pvid)
+                             u16 vid, bool is_untagged, bool is_pvid,
+                             struct netlink_ext_ack *extack,
+                             struct switchdev_trans *trans)
 {
        u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid);
        struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
        u16 old_pvid = mlxsw_sp_port->pvid;
        int err;
 
-       mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_get(mlxsw_sp_port, vid);
-       if (IS_ERR(mlxsw_sp_port_vlan))
-               return PTR_ERR(mlxsw_sp_port_vlan);
+       /* The only valid scenario in which a port-vlan already exists, is if
+        * the VLAN flags were changed and the port-vlan is associated with the
+        * correct bridge port
+        */
+       mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
+       if (mlxsw_sp_port_vlan &&
+           mlxsw_sp_port_vlan->bridge_port != bridge_port)
+               return -EEXIST;
+
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
+       if (!mlxsw_sp_port_vlan) {
+               mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port,
+                                                              vid);
+               if (IS_ERR(mlxsw_sp_port_vlan))
+                       return PTR_ERR(mlxsw_sp_port_vlan);
+       }
 
        err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true,
                                     is_untagged);
@@ -1117,7 +1114,8 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
        if (err)
                goto err_port_pvid_set;
 
-       err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port);
+       err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port,
+                                            extack);
        if (err)
                goto err_port_vlan_bridge_join;
 
@@ -1128,7 +1126,7 @@ err_port_vlan_bridge_join:
 err_port_pvid_set:
        mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
 err_port_vlan_set:
-       mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
+       mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
        return err;
 }
 
@@ -1167,7 +1165,8 @@ mlxsw_sp_br_ban_rif_pvid_change(struct mlxsw_sp *mlxsw_sp,
 
 static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
                                   const struct switchdev_obj_port_vlan *vlan,
-                                  struct switchdev_trans *trans)
+                                  struct switchdev_trans *trans,
+                                  struct netlink_ext_ack *extack)
 {
        bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
@@ -1189,9 +1188,6 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
                return err;
        }
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
        if (WARN_ON(!bridge_port))
                return -EINVAL;
@@ -1204,7 +1200,7 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
 
                err = mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port,
                                                    vid, flag_untagged,
-                                                   flag_pvid);
+                                                   flag_pvid, extack, trans);
                if (err)
                        return err;
        }
@@ -1773,7 +1769,8 @@ static void mlxsw_sp_span_respin_schedule(struct mlxsw_sp *mlxsw_sp)
 
 static int mlxsw_sp_port_obj_add(struct net_device *dev,
                                 const struct switchdev_obj *obj,
-                                struct switchdev_trans *trans)
+                                struct switchdev_trans *trans,
+                                struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
        const struct switchdev_obj_port_vlan *vlan;
@@ -1782,7 +1779,8 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
                vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
-               err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans);
+               err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans,
+                                             extack);
 
                if (switchdev_trans_ph_prepare(trans)) {
                        /* The event is emitted before the changes are actually
@@ -1820,7 +1818,7 @@ mlxsw_sp_bridge_port_vlan_del(struct mlxsw_sp_port *mlxsw_sp_port,
        mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
        mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid);
        mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
-       mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
+       mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
 }
 
 static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
@@ -1968,8 +1966,6 @@ static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
 static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
        .switchdev_port_attr_get        = mlxsw_sp_port_attr_get,
        .switchdev_port_attr_set        = mlxsw_sp_port_attr_set,
-       .switchdev_port_obj_add         = mlxsw_sp_port_obj_add,
-       .switchdev_port_obj_del         = mlxsw_sp_port_obj_del,
 };
 
 static int
@@ -1978,19 +1974,14 @@ mlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device,
                                struct mlxsw_sp_port *mlxsw_sp_port,
                                struct netlink_ext_ack *extack)
 {
-       struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
-
        if (is_vlan_dev(bridge_port->dev)) {
                NL_SET_ERR_MSG_MOD(extack, "Can not enslave a VLAN device to a VLAN-aware bridge");
                return -EINVAL;
        }
 
-       mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, 1);
-       if (WARN_ON(!mlxsw_sp_port_vlan))
-               return -EINVAL;
-
-       /* Let VLAN-aware bridge take care of its own VLANs */
-       mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
+       /* Port is no longer usable as a router interface */
+       if (mlxsw_sp_port->default_vlan->fid)
+               mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port->default_vlan);
 
        return 0;
 }
@@ -2000,41 +1991,133 @@ mlxsw_sp_bridge_8021q_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
                                 struct mlxsw_sp_bridge_port *bridge_port,
                                 struct mlxsw_sp_port *mlxsw_sp_port)
 {
-       mlxsw_sp_port_vlan_get(mlxsw_sp_port, 1);
        /* Make sure untagged frames are allowed to ingress */
-       mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1);
+       mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID);
 }
 
 static int
 mlxsw_sp_bridge_8021q_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
-                                const struct net_device *vxlan_dev,
+                                const struct net_device *vxlan_dev, u16 vid,
                                 struct netlink_ext_ack *extack)
 {
-       WARN_ON(1);
-       return -EINVAL;
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
+       struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
+       struct mlxsw_sp_nve_params params = {
+               .type = MLXSW_SP_NVE_TYPE_VXLAN,
+               .vni = vxlan->cfg.vni,
+               .dev = vxlan_dev,
+       };
+       struct mlxsw_sp_fid *fid;
+       int err;
+
+       /* If the VLAN is 0, we need to find the VLAN that is configured as
+        * PVID and egress untagged on the bridge port of the VxLAN device.
+        * It is possible no such VLAN exists
+        */
+       if (!vid) {
+               err = mlxsw_sp_vxlan_mapped_vid(vxlan_dev, &vid);
+               if (err || !vid)
+                       return err;
+       }
+
+       /* If no other port is member in the VLAN, then the FID does not exist.
+        * NVE will be enabled on the FID once a port joins the VLAN
+        */
+       fid = mlxsw_sp_fid_8021q_lookup(mlxsw_sp, vid);
+       if (!fid)
+               return 0;
+
+       if (mlxsw_sp_fid_vni_is_set(fid)) {
+               err = -EINVAL;
+               goto err_vni_exists;
+       }
+
+       err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, &params, extack);
+       if (err)
+               goto err_nve_fid_enable;
+
+       /* The tunnel port does not hold a reference on the FID. Only
+        * local ports and the router port
+        */
+       mlxsw_sp_fid_put(fid);
+
+       return 0;
+
+err_nve_fid_enable:
+err_vni_exists:
+       mlxsw_sp_fid_put(fid);
+       return err;
 }
 
-static void
-mlxsw_sp_bridge_8021q_vxlan_leave(struct mlxsw_sp_bridge_device *bridge_device,
-                                 const struct net_device *vxlan_dev)
+static struct net_device *
+mlxsw_sp_bridge_8021q_vxlan_dev_find(struct net_device *br_dev, u16 vid)
 {
+       struct net_device *dev;
+       struct list_head *iter;
+
+       netdev_for_each_lower_dev(br_dev, dev, iter) {
+               u16 pvid;
+               int err;
+
+               if (!netif_is_vxlan(dev))
+                       continue;
+
+               err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid);
+               if (err || pvid != vid)
+                       continue;
+
+               return dev;
+       }
+
+       return NULL;
 }
 
 static struct mlxsw_sp_fid *
 mlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
-                             u16 vid)
+                             u16 vid, struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
+       struct net_device *vxlan_dev;
+       struct mlxsw_sp_fid *fid;
+       int err;
+
+       fid = mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
+       if (IS_ERR(fid))
+               return fid;
+
+       if (mlxsw_sp_fid_vni_is_set(fid))
+               return fid;
+
+       /* Find the VxLAN device that has the specified VLAN configured as
+        * PVID and egress untagged. There can be at most one such device
+        */
+       vxlan_dev = mlxsw_sp_bridge_8021q_vxlan_dev_find(bridge_device->dev,
+                                                        vid);
+       if (!vxlan_dev)
+               return fid;
+
+       if (!netif_running(vxlan_dev))
+               return fid;
+
+       err = mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, vid,
+                                              extack);
+       if (err)
+               goto err_vxlan_join;
+
+       return fid;
 
-       return mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
+err_vxlan_join:
+       mlxsw_sp_fid_put(fid);
+       return ERR_PTR(err);
 }
 
 static struct mlxsw_sp_fid *
 mlxsw_sp_bridge_8021q_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device,
                                 u16 vid)
 {
-       WARN_ON(1);
-       return NULL;
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
+
+       return mlxsw_sp_fid_8021q_lookup(mlxsw_sp, vid);
 }
 
 static u16
@@ -2048,7 +2131,6 @@ static const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021q_ops = {
        .port_join      = mlxsw_sp_bridge_8021q_port_join,
        .port_leave     = mlxsw_sp_bridge_8021q_port_leave,
        .vxlan_join     = mlxsw_sp_bridge_8021q_vxlan_join,
-       .vxlan_leave    = mlxsw_sp_bridge_8021q_vxlan_leave,
        .fid_get        = mlxsw_sp_bridge_8021q_fid_get,
        .fid_lookup     = mlxsw_sp_bridge_8021q_fid_lookup,
        .fid_vid        = mlxsw_sp_bridge_8021q_fid_vid,
@@ -2081,7 +2163,7 @@ mlxsw_sp_bridge_8021d_port_join(struct mlxsw_sp_bridge_device *bridge_device,
        struct net_device *dev = bridge_port->dev;
        u16 vid;
 
-       vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : 1;
+       vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID;
        mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
        if (WARN_ON(!mlxsw_sp_port_vlan))
                return -EINVAL;
@@ -2095,7 +2177,8 @@ mlxsw_sp_bridge_8021d_port_join(struct mlxsw_sp_bridge_device *bridge_device,
        if (mlxsw_sp_port_vlan->fid)
                mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
 
-       return mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port);
+       return mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port,
+                                             extack);
 }
 
 static void
@@ -2107,9 +2190,9 @@ mlxsw_sp_bridge_8021d_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
        struct net_device *dev = bridge_port->dev;
        u16 vid;
 
-       vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : 1;
+       vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID;
        mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
-       if (WARN_ON(!mlxsw_sp_port_vlan))
+       if (!mlxsw_sp_port_vlan || !mlxsw_sp_port_vlan->bridge_port)
                return;
 
        mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
@@ -2117,7 +2200,7 @@ mlxsw_sp_bridge_8021d_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
 
 static int
 mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
-                                const struct net_device *vxlan_dev,
+                                const struct net_device *vxlan_dev, u16 vid,
                                 struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
@@ -2134,8 +2217,10 @@ mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
        if (!fid)
                return -EINVAL;
 
-       if (mlxsw_sp_fid_vni_is_set(fid))
-               return -EINVAL;
+       if (mlxsw_sp_fid_vni_is_set(fid)) {
+               err = -EINVAL;
+               goto err_vni_exists;
+       }
 
        err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, &params, extack);
        if (err)
@@ -2149,33 +2234,14 @@ mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
        return 0;
 
 err_nve_fid_enable:
+err_vni_exists:
        mlxsw_sp_fid_put(fid);
        return err;
 }
 
-static void
-mlxsw_sp_bridge_8021d_vxlan_leave(struct mlxsw_sp_bridge_device *bridge_device,
-                                 const struct net_device *vxlan_dev)
-{
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
-       struct mlxsw_sp_fid *fid;
-
-       fid = mlxsw_sp_fid_8021d_lookup(mlxsw_sp, bridge_device->dev->ifindex);
-       if (WARN_ON(!fid))
-               return;
-
-       /* If the VxLAN device is down, then the FID does not have a VNI */
-       if (!mlxsw_sp_fid_vni_is_set(fid))
-               goto out;
-
-       mlxsw_sp_nve_fid_disable(mlxsw_sp, fid);
-out:
-       mlxsw_sp_fid_put(fid);
-}
-
 static struct mlxsw_sp_fid *
 mlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
-                             u16 vid)
+                             u16 vid, struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
        struct net_device *vxlan_dev;
@@ -2196,7 +2262,8 @@ mlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
        if (!netif_running(vxlan_dev))
                return fid;
 
-       err = mlxsw_sp_bridge_8021d_vxlan_join(bridge_device, vxlan_dev, NULL);
+       err = mlxsw_sp_bridge_8021d_vxlan_join(bridge_device, vxlan_dev, 0,
+                                              extack);
        if (err)
                goto err_vxlan_join;
 
@@ -2231,7 +2298,6 @@ static const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021d_ops = {
        .port_join      = mlxsw_sp_bridge_8021d_port_join,
        .port_leave     = mlxsw_sp_bridge_8021d_port_leave,
        .vxlan_join     = mlxsw_sp_bridge_8021d_vxlan_join,
-       .vxlan_leave    = mlxsw_sp_bridge_8021d_vxlan_leave,
        .fid_get        = mlxsw_sp_bridge_8021d_fid_get,
        .fid_lookup     = mlxsw_sp_bridge_8021d_fid_lookup,
        .fid_vid        = mlxsw_sp_bridge_8021d_fid_vid,
@@ -2286,7 +2352,7 @@ void mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port,
 
 int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
                               const struct net_device *br_dev,
-                              const struct net_device *vxlan_dev,
+                              const struct net_device *vxlan_dev, u16 vid,
                               struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp_bridge_device *bridge_device;
@@ -2295,20 +2361,102 @@ int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
        if (WARN_ON(!bridge_device))
                return -EINVAL;
 
-       return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, extack);
+       return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, vid,
+                                             extack);
 }
 
 void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
-                                const struct net_device *br_dev,
                                 const struct net_device *vxlan_dev)
+{
+       struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
+       struct mlxsw_sp_fid *fid;
+
+       /* If the VxLAN device is down, then the FID does not have a VNI */
+       fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg.vni);
+       if (!fid)
+               return;
+
+       mlxsw_sp_nve_fid_disable(mlxsw_sp, fid);
+       mlxsw_sp_fid_put(fid);
+}
+
+struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp,
+                                            const struct net_device *br_dev,
+                                            u16 vid,
+                                            struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp_bridge_device *bridge_device;
 
        bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
        if (WARN_ON(!bridge_device))
-               return;
+               return ERR_PTR(-EINVAL);
 
-       bridge_device->ops->vxlan_leave(bridge_device, vxlan_dev);
+       return bridge_device->ops->fid_get(bridge_device, vid, extack);
+}
+
+static void
+mlxsw_sp_switchdev_vxlan_addr_convert(const union vxlan_addr *vxlan_addr,
+                                     enum mlxsw_sp_l3proto *proto,
+                                     union mlxsw_sp_l3addr *addr)
+{
+       if (vxlan_addr->sa.sa_family == AF_INET) {
+               addr->addr4 = vxlan_addr->sin.sin_addr.s_addr;
+               *proto = MLXSW_SP_L3_PROTO_IPV4;
+       } else {
+               addr->addr6 = vxlan_addr->sin6.sin6_addr;
+               *proto = MLXSW_SP_L3_PROTO_IPV6;
+       }
+}
+
+static void
+mlxsw_sp_switchdev_addr_vxlan_convert(enum mlxsw_sp_l3proto proto,
+                                     const union mlxsw_sp_l3addr *addr,
+                                     union vxlan_addr *vxlan_addr)
+{
+       switch (proto) {
+       case MLXSW_SP_L3_PROTO_IPV4:
+               vxlan_addr->sa.sa_family = AF_INET;
+               vxlan_addr->sin.sin_addr.s_addr = addr->addr4;
+               break;
+       case MLXSW_SP_L3_PROTO_IPV6:
+               vxlan_addr->sa.sa_family = AF_INET6;
+               vxlan_addr->sin6.sin6_addr = addr->addr6;
+               break;
+       }
+}
+
+static void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev,
+                                             const char *mac,
+                                             enum mlxsw_sp_l3proto proto,
+                                             union mlxsw_sp_l3addr *addr,
+                                             __be32 vni, bool adding)
+{
+       struct switchdev_notifier_vxlan_fdb_info info;
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       enum switchdev_notifier_type type;
+
+       type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE :
+                       SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE;
+       mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip);
+       info.remote_port = vxlan->cfg.dst_port;
+       info.remote_vni = vni;
+       info.remote_ifindex = 0;
+       ether_addr_copy(info.eth_addr, mac);
+       info.vni = vni;
+       info.offloaded = adding;
+       call_switchdev_notifiers(type, dev, &info.info);
+}
+
+static void mlxsw_sp_fdb_nve_call_notifiers(struct net_device *dev,
+                                           const char *mac,
+                                           enum mlxsw_sp_l3proto proto,
+                                           union mlxsw_sp_l3addr *addr,
+                                           __be32 vni,
+                                           bool adding)
+{
+       if (netif_is_vxlan(dev))
+               mlxsw_sp_fdb_vxlan_call_notifiers(dev, mac, proto, addr, vni,
+                                                 adding);
 }
 
 static void
@@ -2419,7 +2567,8 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
 
        bridge_device = bridge_port->bridge_device;
        vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0;
-       lag_vid = mlxsw_sp_port_vlan->vid;
+       lag_vid = mlxsw_sp_fid_lag_vid_valid(mlxsw_sp_port_vlan->fid) ?
+                 mlxsw_sp_port_vlan->vid : 0;
 
 do_fdb_op:
        err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
@@ -2442,6 +2591,122 @@ just_remove:
        goto do_fdb_op;
 }
 
+static int
+__mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
+                                           const struct mlxsw_sp_fid *fid,
+                                           bool adding,
+                                           struct net_device **nve_dev,
+                                           u16 *p_vid, __be32 *p_vni)
+{
+       struct mlxsw_sp_bridge_device *bridge_device;
+       struct net_device *br_dev, *dev;
+       int nve_ifindex;
+       int err;
+
+       err = mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex);
+       if (err)
+               return err;
+
+       err = mlxsw_sp_fid_vni(fid, p_vni);
+       if (err)
+               return err;
+
+       dev = __dev_get_by_index(&init_net, nve_ifindex);
+       if (!dev)
+               return -EINVAL;
+       *nve_dev = dev;
+
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       if (adding && !br_port_flag_is_set(dev, BR_LEARNING))
+               return -EINVAL;
+
+       if (adding && netif_is_vxlan(dev)) {
+               struct vxlan_dev *vxlan = netdev_priv(dev);
+
+               if (!(vxlan->cfg.flags & VXLAN_F_LEARN))
+                       return -EINVAL;
+       }
+
+       br_dev = netdev_master_upper_dev_get(dev);
+       if (!br_dev)
+               return -EINVAL;
+
+       bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
+       if (!bridge_device)
+               return -EINVAL;
+
+       *p_vid = bridge_device->ops->fid_vid(bridge_device, fid);
+
+       return 0;
+}
+
+static void mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
+                                                     char *sfn_pl,
+                                                     int rec_index,
+                                                     bool adding)
+{
+       enum mlxsw_reg_sfn_uc_tunnel_protocol sfn_proto;
+       enum switchdev_notifier_type type;
+       struct net_device *nve_dev;
+       union mlxsw_sp_l3addr addr;
+       struct mlxsw_sp_fid *fid;
+       char mac[ETH_ALEN];
+       u16 fid_index, vid;
+       __be32 vni;
+       u32 uip;
+       int err;
+
+       mlxsw_reg_sfn_uc_tunnel_unpack(sfn_pl, rec_index, mac, &fid_index,
+                                      &uip, &sfn_proto);
+
+       fid = mlxsw_sp_fid_lookup_by_index(mlxsw_sp, fid_index);
+       if (!fid)
+               goto err_fid_lookup;
+
+       err = mlxsw_sp_nve_learned_ip_resolve(mlxsw_sp, uip,
+                                             (enum mlxsw_sp_l3proto) sfn_proto,
+                                             &addr);
+       if (err)
+               goto err_ip_resolve;
+
+       err = __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, fid, adding,
+                                                         &nve_dev, &vid, &vni);
+       if (err)
+               goto err_fdb_process;
+
+       err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index,
+                                            (enum mlxsw_sp_l3proto) sfn_proto,
+                                            &addr, adding, true);
+       if (err)
+               goto err_fdb_op;
+
+       mlxsw_sp_fdb_nve_call_notifiers(nve_dev, mac,
+                                       (enum mlxsw_sp_l3proto) sfn_proto,
+                                       &addr, vni, adding);
+
+       type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE :
+                       SWITCHDEV_FDB_DEL_TO_BRIDGE;
+       mlxsw_sp_fdb_call_notifiers(type, mac, vid, nve_dev, adding);
+
+       mlxsw_sp_fid_put(fid);
+
+       return;
+
+err_fdb_op:
+err_fdb_process:
+err_ip_resolve:
+       mlxsw_sp_fid_put(fid);
+err_fid_lookup:
+       /* Remove an FDB entry in case we cannot process it. Otherwise the
+        * device will keep sending the same notification over and over again.
+        */
+       mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index,
+                                      (enum mlxsw_sp_l3proto) sfn_proto, &addr,
+                                      false, true);
+}
+
 static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
                                            char *sfn_pl, int rec_index)
 {
@@ -2462,6 +2727,14 @@ static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
                mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
                                                    rec_index, false);
                break;
+       case MLXSW_REG_SFN_REC_TYPE_LEARNED_UNICAST_TUNNEL:
+               mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl,
+                                                         rec_index, true);
+               break;
+       case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_UNICAST_TUNNEL:
+               mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl,
+                                                         rec_index, false);
+               break;
        }
 }
 
@@ -2516,20 +2789,6 @@ struct mlxsw_sp_switchdev_event_work {
        unsigned long event;
 };
 
-static void
-mlxsw_sp_switchdev_vxlan_addr_convert(const union vxlan_addr *vxlan_addr,
-                                     enum mlxsw_sp_l3proto *proto,
-                                     union mlxsw_sp_l3addr *addr)
-{
-       if (vxlan_addr->sa.sa_family == AF_INET) {
-               addr->addr4 = vxlan_addr->sin.sin_addr.s_addr;
-               *proto = MLXSW_SP_L3_PROTO_IPV4;
-       } else {
-               addr->addr6 = vxlan_addr->sin6.sin6_addr;
-               *proto = MLXSW_SP_L3_PROTO_IPV6;
-       }
-}
-
 static void
 mlxsw_sp_switchdev_bridge_vxlan_fdb_event(struct mlxsw_sp *mlxsw_sp,
                                          struct mlxsw_sp_switchdev_event_work *
@@ -2595,7 +2854,8 @@ mlxsw_sp_switchdev_bridge_nve_fdb_event(struct mlxsw_sp_switchdev_event_work *
            switchdev_work->event != SWITCHDEV_FDB_DEL_TO_DEVICE)
                return;
 
-       if (!switchdev_work->fdb_info.added_by_user)
+       if (switchdev_work->event == SWITCHDEV_FDB_ADD_TO_DEVICE &&
+           !switchdev_work->fdb_info.added_by_user)
                return;
 
        if (!netif_running(dev))
@@ -2938,10 +3198,274 @@ err_addr_alloc:
        return NOTIFY_BAD;
 }
 
-static struct notifier_block mlxsw_sp_switchdev_notifier = {
+struct notifier_block mlxsw_sp_switchdev_notifier = {
        .notifier_call = mlxsw_sp_switchdev_event,
 };
 
+static int
+mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_bridge_device *bridge_device,
+                                 const struct net_device *vxlan_dev, u16 vid,
+                                 bool flag_untagged, bool flag_pvid,
+                                 struct switchdev_trans *trans,
+                                 struct netlink_ext_ack *extack)
+{
+       struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
+       __be32 vni = vxlan->cfg.vni;
+       struct mlxsw_sp_fid *fid;
+       u16 old_vid;
+       int err;
+
+       /* We cannot have the same VLAN as PVID and egress untagged on multiple
+        * VxLAN devices. Note that we get this notification before the VLAN is
+        * actually added to the bridge's database, so it is not possible for
+        * the lookup function to return 'vxlan_dev'
+        */
+       if (flag_untagged && flag_pvid &&
+           mlxsw_sp_bridge_8021q_vxlan_dev_find(bridge_device->dev, vid))
+               return -EINVAL;
+
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
+       if (!netif_running(vxlan_dev))
+               return 0;
+
+       /* First case: FID is not associated with this VNI, but the new VLAN
+        * is both PVID and egress untagged. Need to enable NVE on the FID, if
+        * it exists
+        */
+       fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);
+       if (!fid) {
+               if (!flag_untagged || !flag_pvid)
+                       return 0;
+               return mlxsw_sp_bridge_8021q_vxlan_join(bridge_device,
+                                                       vxlan_dev, vid, extack);
+       }
+
+       /* Second case: FID is associated with the VNI and the VLAN associated
+        * with the FID is the same as the notified VLAN. This means the flags
+        * (PVID / egress untagged) were toggled and that NVE should be
+        * disabled on the FID
+        */
+       old_vid = mlxsw_sp_fid_8021q_vid(fid);
+       if (vid == old_vid) {
+               if (WARN_ON(flag_untagged && flag_pvid)) {
+                       mlxsw_sp_fid_put(fid);
+                       return -EINVAL;
+               }
+               mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
+               mlxsw_sp_fid_put(fid);
+               return 0;
+       }
+
+       /* Third case: A new VLAN was configured on the VxLAN device, but this
+        * VLAN is not PVID, so there is nothing to do.
+        */
+       if (!flag_pvid) {
+               mlxsw_sp_fid_put(fid);
+               return 0;
+       }
+
+       /* Fourth case: Thew new VLAN is PVID, which means the VLAN currently
+        * mapped to the VNI should be unmapped
+        */
+       mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
+       mlxsw_sp_fid_put(fid);
+
+       /* Fifth case: The new VLAN is also egress untagged, which means the
+        * VLAN needs to be mapped to the VNI
+        */
+       if (!flag_untagged)
+               return 0;
+
+       err = mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, vid,
+                                              extack);
+       if (err)
+               goto err_vxlan_join;
+
+       return 0;
+
+err_vxlan_join:
+       mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, old_vid,
+                                        NULL);
+       return err;
+}
+
+static void
+mlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_bridge_device *bridge_device,
+                                 const struct net_device *vxlan_dev, u16 vid)
+{
+       struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
+       __be32 vni = vxlan->cfg.vni;
+       struct mlxsw_sp_fid *fid;
+
+       if (!netif_running(vxlan_dev))
+               return;
+
+       fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);
+       if (!fid)
+               return;
+
+       /* A different VLAN than the one mapped to the VNI is deleted */
+       if (mlxsw_sp_fid_8021q_vid(fid) != vid)
+               goto out;
+
+       mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
+
+out:
+       mlxsw_sp_fid_put(fid);
+}
+
+static int
+mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev,
+                                  struct switchdev_notifier_port_obj_info *
+                                  port_obj_info)
+{
+       struct switchdev_obj_port_vlan *vlan =
+               SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj);
+       bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+       struct switchdev_trans *trans = port_obj_info->trans;
+       struct mlxsw_sp_bridge_device *bridge_device;
+       struct netlink_ext_ack *extack;
+       struct mlxsw_sp *mlxsw_sp;
+       struct net_device *br_dev;
+       u16 vid;
+
+       extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
+       br_dev = netdev_master_upper_dev_get(vxlan_dev);
+       if (!br_dev)
+               return 0;
+
+       mlxsw_sp = mlxsw_sp_lower_get(br_dev);
+       if (!mlxsw_sp)
+               return 0;
+
+       port_obj_info->handled = true;
+
+       bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
+       if (!bridge_device)
+               return -EINVAL;
+
+       if (!bridge_device->vlan_enabled)
+               return 0;
+
+       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+               int err;
+
+               err = mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device,
+                                                       vxlan_dev, vid,
+                                                       flag_untagged,
+                                                       flag_pvid, trans,
+                                                       extack);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void
+mlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev,
+                                  struct switchdev_notifier_port_obj_info *
+                                  port_obj_info)
+{
+       struct switchdev_obj_port_vlan *vlan =
+               SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj);
+       struct mlxsw_sp_bridge_device *bridge_device;
+       struct mlxsw_sp *mlxsw_sp;
+       struct net_device *br_dev;
+       u16 vid;
+
+       br_dev = netdev_master_upper_dev_get(vxlan_dev);
+       if (!br_dev)
+               return;
+
+       mlxsw_sp = mlxsw_sp_lower_get(br_dev);
+       if (!mlxsw_sp)
+               return;
+
+       port_obj_info->handled = true;
+
+       bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
+       if (!bridge_device)
+               return;
+
+       if (!bridge_device->vlan_enabled)
+               return;
+
+       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
+               mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device,
+                                                 vxlan_dev, vid);
+}
+
+static int
+mlxsw_sp_switchdev_handle_vxlan_obj_add(struct net_device *vxlan_dev,
+                                       struct switchdev_notifier_port_obj_info *
+                                       port_obj_info)
+{
+       int err = 0;
+
+       switch (port_obj_info->obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = mlxsw_sp_switchdev_vxlan_vlans_add(vxlan_dev,
+                                                        port_obj_info);
+               break;
+       default:
+               break;
+       }
+
+       return err;
+}
+
+static void
+mlxsw_sp_switchdev_handle_vxlan_obj_del(struct net_device *vxlan_dev,
+                                       struct switchdev_notifier_port_obj_info *
+                                       port_obj_info)
+{
+       switch (port_obj_info->obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               mlxsw_sp_switchdev_vxlan_vlans_del(vxlan_dev, port_obj_info);
+               break;
+       default:
+               break;
+       }
+}
+
+static int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused,
+                                            unsigned long event, void *ptr)
+{
+       struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+       int err = 0;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD:
+               if (netif_is_vxlan(dev))
+                       err = mlxsw_sp_switchdev_handle_vxlan_obj_add(dev, ptr);
+               else
+                       err = switchdev_handle_port_obj_add(dev, ptr,
+                                                       mlxsw_sp_port_dev_check,
+                                                       mlxsw_sp_port_obj_add);
+               return notifier_from_errno(err);
+       case SWITCHDEV_PORT_OBJ_DEL:
+               if (netif_is_vxlan(dev))
+                       mlxsw_sp_switchdev_handle_vxlan_obj_del(dev, ptr);
+               else
+                       err = switchdev_handle_port_obj_del(dev, ptr,
+                                                       mlxsw_sp_port_dev_check,
+                                                       mlxsw_sp_port_obj_del);
+               return notifier_from_errno(err);
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block mlxsw_sp_switchdev_blocking_notifier = {
+       .notifier_call = mlxsw_sp_switchdev_blocking_event,
+};
+
 u8
 mlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port)
 {
@@ -2951,6 +3475,7 @@ mlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port)
 static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
 {
        struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge;
+       struct notifier_block *nb;
        int err;
 
        err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME);
@@ -2965,17 +3490,33 @@ static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
                return err;
        }
 
+       nb = &mlxsw_sp_switchdev_blocking_notifier;
+       err = register_switchdev_blocking_notifier(nb);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev blocking notifier\n");
+               goto err_register_switchdev_blocking_notifier;
+       }
+
        INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work);
        bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL;
        mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
        return 0;
+
+err_register_switchdev_blocking_notifier:
+       unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
+       return err;
 }
 
 static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp)
 {
+       struct notifier_block *nb;
+
        cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw);
-       unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
 
+       nb = &mlxsw_sp_switchdev_blocking_notifier;
+       unregister_switchdev_blocking_notifier(nb);
+
+       unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
 }
 
 int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp)
index 6f18f4d..451216d 100644 (file)
@@ -60,6 +60,7 @@ enum {
        MLXSW_TRAP_ID_IPV6_MC_LINK_LOCAL_DEST = 0x91,
        MLXSW_TRAP_ID_HOST_MISS_IPV6 = 0x92,
        MLXSW_TRAP_ID_IPIP_DECAP_ERROR = 0xB1,
+       MLXSW_TRAP_ID_NVE_DECAP_ARP = 0xB8,
        MLXSW_TRAP_ID_NVE_ENCAP_ARP = 0xBD,
        MLXSW_TRAP_ID_ROUTER_ALERT_IPV4 = 0xD6,
        MLXSW_TRAP_ID_ROUTER_ALERT_IPV6 = 0xD7,
index 867cddb..20c9377 100644 (file)
@@ -802,14 +802,8 @@ static int lan743x_mac_init(struct lan743x_adapter *adapter)
        u32 mac_addr_hi = 0;
        u32 mac_addr_lo = 0;
        u32 data;
-       int ret;
 
        netdev = adapter->netdev;
-       lan743x_csr_write(adapter, MAC_CR, MAC_CR_RST_);
-       ret = lan743x_csr_wait_for_bit(adapter, MAC_CR, MAC_CR_RST_,
-                                      0, 1000, 20000, 100);
-       if (ret)
-               return ret;
 
        /* setup auto duplex, and speed detection */
        data = lan743x_csr_read(adapter, MAC_CR);
@@ -1672,7 +1666,7 @@ static int lan743x_tx_napi_poll(struct napi_struct *napi, int weight)
                netif_wake_queue(adapter->netdev);
        }
 
-       if (!napi_complete_done(napi, weight))
+       if (!napi_complete(napi))
                goto done;
 
        /* enable isr */
@@ -1681,7 +1675,7 @@ static int lan743x_tx_napi_poll(struct napi_struct *napi, int weight)
        lan743x_csr_read(adapter, INT_STS);
 
 done:
-       return weight;
+       return 0;
 }
 
 static void lan743x_tx_ring_cleanup(struct lan743x_tx *tx)
@@ -1870,9 +1864,9 @@ static int lan743x_tx_open(struct lan743x_tx *tx)
        tx->vector_flags = lan743x_intr_get_vector_flags(adapter,
                                                         INT_BIT_DMA_TX_
                                                         (tx->channel_number));
-       netif_napi_add(adapter->netdev,
-                      &tx->napi, lan743x_tx_napi_poll,
-                      tx->ring_size - 1);
+       netif_tx_napi_add(adapter->netdev,
+                         &tx->napi, lan743x_tx_napi_poll,
+                         tx->ring_size - 1);
        napi_enable(&tx->napi);
 
        data = 0;
@@ -2719,8 +2713,9 @@ static int lan743x_mdiobus_init(struct lan743x_adapter *adapter)
        snprintf(adapter->mdiobus->id, MII_BUS_ID_SIZE,
                 "pci-%s", pci_name(adapter->pdev));
 
-       /* set to internal PHY id */
-       adapter->mdiobus->phy_mask = ~(u32)BIT(1);
+       if ((adapter->csr.id_rev & ID_REV_ID_MASK_) == ID_REV_ID_LAN7430_)
+               /* LAN7430 uses internal phy at address 1 */
+               adapter->mdiobus->phy_mask = ~(u32)BIT(1);
 
        /* register mdiobus */
        ret = mdiobus_register(adapter->mdiobus);
@@ -3017,6 +3012,7 @@ static const struct dev_pm_ops lan743x_pm_ops = {
 
 static const struct pci_device_id lan743x_pcidev_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_SMSC, PCI_DEVICE_ID_SMSC_LAN7430) },
+       { PCI_DEVICE(PCI_VENDOR_ID_SMSC, PCI_DEVICE_ID_SMSC_LAN7431) },
        { 0, }
 };
 
index 0e82b63..2d6eea1 100644 (file)
@@ -548,6 +548,7 @@ struct lan743x_adapter;
 /* SMSC acquired EFAR late 1990's, MCHP acquired SMSC 2012 */
 #define PCI_VENDOR_ID_SMSC             PCI_VENDOR_ID_EFAR
 #define PCI_DEVICE_ID_SMSC_LAN7430     (0x7430)
+#define PCI_DEVICE_ID_SMSC_LAN7431     (0x7431)
 
 #define PCI_CONFIG_LENGTH              (0x1000)
 
index 3238b9e..feb9ffc 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/netdevice.h>
 #include <linux/phy.h>
 #include <linux/skbuff.h>
+#include <linux/iopoll.h>
 #include <net/arp.h>
 #include <net/netevent.h>
 #include <net/rtnetlink.h>
@@ -22,6 +23,9 @@
 
 #include "ocelot.h"
 
+#define TABLE_UPDATE_SLEEP_US 10
+#define TABLE_UPDATE_TIMEOUT_US 100000
+
 /* MAC table entry types.
  * ENTRYTYPE_NORMAL is subject to aging.
  * ENTRYTYPE_LOCKED is not subject to aging.
@@ -41,23 +45,20 @@ struct ocelot_mact_entry {
        enum macaccess_entry_type type;
 };
 
-static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot)
+static inline u32 ocelot_mact_read_macaccess(struct ocelot *ocelot)
 {
-       unsigned int val, timeout = 10;
-
-       /* Wait for the issued mac table command to be completed, or timeout.
-        * When the command read from  ANA_TABLES_MACACCESS is
-        * MACACCESS_CMD_IDLE, the issued command completed successfully.
-        */
-       do {
-               val = ocelot_read(ocelot, ANA_TABLES_MACACCESS);
-               val &= ANA_TABLES_MACACCESS_MAC_TABLE_CMD_M;
-       } while (val != MACACCESS_CMD_IDLE && timeout--);
+       return ocelot_read(ocelot, ANA_TABLES_MACACCESS);
+}
 
-       if (!timeout)
-               return -ETIMEDOUT;
+static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot)
+{
+       u32 val;
 
-       return 0;
+       return readx_poll_timeout(ocelot_mact_read_macaccess,
+               ocelot, val,
+               (val & ANA_TABLES_MACACCESS_MAC_TABLE_CMD_M) ==
+               MACACCESS_CMD_IDLE,
+               TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
 }
 
 static void ocelot_mact_select(struct ocelot *ocelot,
@@ -129,23 +130,21 @@ static void ocelot_mact_init(struct ocelot *ocelot)
        ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS);
 }
 
-static inline int ocelot_vlant_wait_for_completion(struct ocelot *ocelot)
+static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot)
 {
-       unsigned int val, timeout = 10;
-
-       /* Wait for the issued vlan table command to be completed, or timeout.
-        * When the command read from ANA_TABLES_VLANACCESS is
-        * VLANACCESS_CMD_IDLE, the issued command completed successfully.
-        */
-       do {
-               val = ocelot_read(ocelot, ANA_TABLES_VLANACCESS);
-               val &= ANA_TABLES_VLANACCESS_VLAN_TBL_CMD_M;
-       } while (val != ANA_TABLES_VLANACCESS_CMD_IDLE && timeout--);
+       return ocelot_read(ocelot, ANA_TABLES_VLANACCESS);
+}
 
-       if (!timeout)
-               return -ETIMEDOUT;
+static inline int ocelot_vlant_wait_for_completion(struct ocelot *ocelot)
+{
+       u32 val;
 
-       return 0;
+       return readx_poll_timeout(ocelot_vlant_read_vlanaccess,
+               ocelot,
+               val,
+               (val & ANA_TABLES_VLANACCESS_VLAN_TBL_CMD_M) ==
+               ANA_TABLES_VLANACCESS_CMD_IDLE,
+               TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
 }
 
 static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask)
@@ -1293,7 +1292,8 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
 
 static int ocelot_port_obj_add(struct net_device *dev,
                               const struct switchdev_obj *obj,
-                              struct switchdev_trans *trans)
+                              struct switchdev_trans *trans,
+                              struct netlink_ext_ack *extack)
 {
        int ret = 0;
 
@@ -1337,8 +1337,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,
-       .switchdev_port_obj_add         = ocelot_port_obj_add,
-       .switchdev_port_obj_del         = ocelot_port_obj_del,
 };
 
 static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port,
@@ -1595,6 +1593,34 @@ struct notifier_block ocelot_netdevice_nb __read_mostly = {
 };
 EXPORT_SYMBOL(ocelot_netdevice_nb);
 
+static int ocelot_switchdev_blocking_event(struct notifier_block *unused,
+                                          unsigned long event, void *ptr)
+{
+       struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+       int err;
+
+       switch (event) {
+               /* Blocking events. */
+       case SWITCHDEV_PORT_OBJ_ADD:
+               err = switchdev_handle_port_obj_add(dev, ptr,
+                                                   ocelot_netdevice_dev_check,
+                                                   ocelot_port_obj_add);
+               return notifier_from_errno(err);
+       case SWITCHDEV_PORT_OBJ_DEL:
+               err = switchdev_handle_port_obj_del(dev, ptr,
+                                                   ocelot_netdevice_dev_check,
+                                                   ocelot_port_obj_del);
+               return notifier_from_errno(err);
+       }
+
+       return NOTIFY_DONE;
+}
+
+struct notifier_block ocelot_switchdev_blocking_nb __read_mostly = {
+       .notifier_call = ocelot_switchdev_blocking_event,
+};
+EXPORT_SYMBOL(ocelot_switchdev_blocking_nb);
+
 int ocelot_probe_port(struct ocelot *ocelot, u8 port,
                      void __iomem *regs,
                      struct phy_device *phy)
index 62c7c8e..086775f 100644 (file)
@@ -499,5 +499,6 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
                      struct phy_device *phy);
 
 extern struct notifier_block ocelot_netdevice_nb;
+extern struct notifier_block ocelot_switchdev_blocking_nb;
 
 #endif
index 4c23d18..ca3ea2f 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/of_platform.h>
 #include <linux/mfd/syscon.h>
 #include <linux/skbuff.h>
+#include <net/switchdev.h>
 
 #include "ocelot.h"
 
@@ -328,6 +329,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
        }
 
        register_netdevice_notifier(&ocelot_netdevice_nb);
+       register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
 
        dev_info(&pdev->dev, "Ocelot switch probed\n");
 
@@ -342,6 +344,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
        struct ocelot *ocelot = platform_get_drvdata(pdev);
 
        ocelot_deinit(ocelot);
+       unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
        unregister_netdevice_notifier(&ocelot_netdevice_nb);
 
        return 0;
index c26e0f7..7df2056 100644 (file)
@@ -26,7 +26,7 @@ config S2IO
          on its age.
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/s2io.txt>.
+         <file:Documentation/networking/device_drivers/neterion/s2io.txt>.
 
          To compile this driver as a module, choose M here. The module
          will be called s2io.
@@ -41,7 +41,7 @@ config VXGE
          labeled as either one, depending on its age.
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/vxge.txt>.
+         <file:Documentation/networking/device_drivers/neterion/vxge.txt>.
 
          To compile this driver as a module, choose M here. The module
          will be called vxge.
index 4c1fb7e..7cde387 100644 (file)
@@ -808,7 +808,7 @@ __vxge_hw_vpath_fw_ver_get(struct __vxge_hw_virtualpath *vpath,
        struct vxge_hw_device_date *fw_date = &hw_info->fw_date;
        struct vxge_hw_device_version *flash_version = &hw_info->flash_version;
        struct vxge_hw_device_date *flash_date = &hw_info->flash_date;
-       u64 data0, data1 = 0, steer_ctrl = 0;
+       u64 data0 = 0, data1 = 0, steer_ctrl = 0;
        enum vxge_hw_status status;
 
        status = vxge_hw_vpath_fw_api(vpath,
index f7a0d1d..59e77e3 100644 (file)
@@ -1695,17 +1695,10 @@ exit:
  */
 void vxge_hw_fifo_txdl_free(struct __vxge_hw_fifo *fifo, void *txdlh)
 {
-       struct __vxge_hw_fifo_txdl_priv *txdl_priv;
-       u32 max_frags;
        struct __vxge_hw_channel *channel;
 
        channel = &fifo->channel;
 
-       txdl_priv = __vxge_hw_fifo_txdl_priv(fifo,
-                       (struct vxge_hw_fifo_txd *)txdlh);
-
-       max_frags = fifo->config->max_frags;
-
        vxge_hw_channel_dtr_free(channel, txdlh);
 }
 
index 190e8b5..47c708f 100644 (file)
@@ -56,6 +56,7 @@ endif
 
 ifeq ($(CONFIG_NFP_APP_ABM_NIC),y)
 nfp-objs += \
+           abm/cls.o \
            abm/ctrl.o \
            abm/qdisc.o \
            abm/main.o
diff --git a/drivers/net/ethernet/netronome/nfp/abm/cls.c b/drivers/net/ethernet/netronome/nfp/abm/cls.c
new file mode 100644 (file)
index 0000000..9852080
--- /dev/null
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2018 Netronome Systems, Inc. */
+
+#include <linux/bitfield.h>
+#include <net/pkt_cls.h>
+
+#include "../nfpcore/nfp_cpp.h"
+#include "../nfp_app.h"
+#include "../nfp_net_repr.h"
+#include "main.h"
+
+struct nfp_abm_u32_match {
+       u32 handle;
+       u32 band;
+       u8 mask;
+       u8 val;
+       struct list_head list;
+};
+
+static bool
+nfp_abm_u32_check_knode(struct nfp_abm *abm, struct tc_cls_u32_knode *knode,
+                       __be16 proto, struct netlink_ext_ack *extack)
+{
+       struct tc_u32_key *k;
+       unsigned int tos_off;
+
+       if (knode->exts && tcf_exts_has_actions(knode->exts)) {
+               NL_SET_ERR_MSG_MOD(extack, "action offload not supported");
+               return false;
+       }
+       if (knode->link_handle) {
+               NL_SET_ERR_MSG_MOD(extack, "linking not supported");
+               return false;
+       }
+       if (knode->sel->flags != TC_U32_TERMINAL) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "flags must be equal to TC_U32_TERMINAL");
+               return false;
+       }
+       if (knode->sel->off || knode->sel->offshift || knode->sel->offmask ||
+           knode->sel->offoff || knode->fshift) {
+               NL_SET_ERR_MSG_MOD(extack, "variable offseting not supported");
+               return false;
+       }
+       if (knode->sel->hoff || knode->sel->hmask) {
+               NL_SET_ERR_MSG_MOD(extack, "hashing not supported");
+               return false;
+       }
+       if (knode->val || knode->mask) {
+               NL_SET_ERR_MSG_MOD(extack, "matching on mark not supported");
+               return false;
+       }
+       if (knode->res && knode->res->class) {
+               NL_SET_ERR_MSG_MOD(extack, "setting non-0 class not supported");
+               return false;
+       }
+       if (knode->res && knode->res->classid >= abm->num_bands) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "classid higher than number of bands");
+               return false;
+       }
+       if (knode->sel->nkeys != 1) {
+               NL_SET_ERR_MSG_MOD(extack, "exactly one key required");
+               return false;
+       }
+
+       switch (proto) {
+       case htons(ETH_P_IP):
+               tos_off = 16;
+               break;
+       case htons(ETH_P_IPV6):
+               tos_off = 20;
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(extack, "only IP and IPv6 supported as filter protocol");
+               return false;
+       }
+
+       k = &knode->sel->keys[0];
+       if (k->offmask) {
+               NL_SET_ERR_MSG_MOD(extack, "offset mask - variable offseting not supported");
+               return false;
+       }
+       if (k->off) {
+               NL_SET_ERR_MSG_MOD(extack, "only DSCP fields can be matched");
+               return false;
+       }
+       if (k->val & ~k->mask) {
+               NL_SET_ERR_MSG_MOD(extack, "mask does not cover the key");
+               return false;
+       }
+       if (be32_to_cpu(k->mask) >> tos_off & ~abm->dscp_mask) {
+               NL_SET_ERR_MSG_MOD(extack, "only high DSCP class selector bits can be used");
+               nfp_err(abm->app->cpp,
+                       "u32 offload: requested mask %x FW can support only %x\n",
+                       be32_to_cpu(k->mask) >> tos_off, abm->dscp_mask);
+               return false;
+       }
+
+       return true;
+}
+
+/* This filter list -> map conversion is O(n * m), we expect single digit or
+ * low double digit number of prios and likewise for the filters.  Also u32
+ * doesn't report stats, so it's really only setup time cost.
+ */
+static unsigned int
+nfp_abm_find_band_for_prio(struct nfp_abm_link *alink, unsigned int prio)
+{
+       struct nfp_abm_u32_match *iter;
+
+       list_for_each_entry(iter, &alink->dscp_map, list)
+               if ((prio & iter->mask) == iter->val)
+                       return iter->band;
+
+       return alink->def_band;
+}
+
+static int nfp_abm_update_band_map(struct nfp_abm_link *alink)
+{
+       unsigned int i, bits_per_prio, prios_per_word, base_shift;
+       struct nfp_abm *abm = alink->abm;
+       u32 field_mask;
+
+       alink->has_prio = !list_empty(&alink->dscp_map);
+
+       bits_per_prio = roundup_pow_of_two(order_base_2(abm->num_bands));
+       field_mask = (1 << bits_per_prio) - 1;
+       prios_per_word = sizeof(u32) * BITS_PER_BYTE / bits_per_prio;
+
+       /* FW mask applies from top bits */
+       base_shift = 8 - order_base_2(abm->num_prios);
+
+       for (i = 0; i < abm->num_prios; i++) {
+               unsigned int offset;
+               u32 *word;
+               u8 band;
+
+               word = &alink->prio_map[i / prios_per_word];
+               offset = (i % prios_per_word) * bits_per_prio;
+
+               band = nfp_abm_find_band_for_prio(alink, i << base_shift);
+
+               *word &= ~(field_mask << offset);
+               *word |= band << offset;
+       }
+
+       /* Qdisc offload status may change if has_prio changed */
+       nfp_abm_qdisc_offload_update(alink);
+
+       return nfp_abm_ctrl_prio_map_update(alink, alink->prio_map);
+}
+
+static void
+nfp_abm_u32_knode_delete(struct nfp_abm_link *alink,
+                        struct tc_cls_u32_knode *knode)
+{
+       struct nfp_abm_u32_match *iter;
+
+       list_for_each_entry(iter, &alink->dscp_map, list)
+               if (iter->handle == knode->handle) {
+                       list_del(&iter->list);
+                       kfree(iter);
+                       nfp_abm_update_band_map(alink);
+                       return;
+               }
+}
+
+static int
+nfp_abm_u32_knode_replace(struct nfp_abm_link *alink,
+                         struct tc_cls_u32_knode *knode,
+                         __be16 proto, struct netlink_ext_ack *extack)
+{
+       struct nfp_abm_u32_match *match = NULL, *iter;
+       unsigned int tos_off;
+       u8 mask, val;
+       int err;
+
+       if (!nfp_abm_u32_check_knode(alink->abm, knode, proto, extack))
+               goto err_delete;
+
+       tos_off = proto == htons(ETH_P_IP) ? 16 : 20;
+
+       /* Extract the DSCP Class Selector bits */
+       val = be32_to_cpu(knode->sel->keys[0].val) >> tos_off & 0xff;
+       mask = be32_to_cpu(knode->sel->keys[0].mask) >> tos_off & 0xff;
+
+       /* Check if there is no conflicting mapping and find match by handle */
+       list_for_each_entry(iter, &alink->dscp_map, list) {
+               u32 cmask;
+
+               if (iter->handle == knode->handle) {
+                       match = iter;
+                       continue;
+               }
+
+               cmask = iter->mask & mask;
+               if ((iter->val & cmask) == (val & cmask) &&
+                   iter->band != knode->res->classid) {
+                       NL_SET_ERR_MSG_MOD(extack, "conflict with already offloaded filter");
+                       goto err_delete;
+               }
+       }
+
+       if (!match) {
+               match = kzalloc(sizeof(*match), GFP_KERNEL);
+               if (!match)
+                       return -ENOMEM;
+               list_add(&match->list, &alink->dscp_map);
+       }
+       match->handle = knode->handle;
+       match->band = knode->res->classid;
+       match->mask = mask;
+       match->val = val;
+
+       err = nfp_abm_update_band_map(alink);
+       if (err)
+               goto err_delete;
+
+       return 0;
+
+err_delete:
+       nfp_abm_u32_knode_delete(alink, knode);
+       return -EOPNOTSUPP;
+}
+
+static int nfp_abm_setup_tc_block_cb(enum tc_setup_type type,
+                                    void *type_data, void *cb_priv)
+{
+       struct tc_cls_u32_offload *cls_u32 = type_data;
+       struct nfp_repr *repr = cb_priv;
+       struct nfp_abm_link *alink;
+
+       alink = repr->app_priv;
+
+       if (type != TC_SETUP_CLSU32) {
+               NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
+                                  "only offload of u32 classifier supported");
+               return -EOPNOTSUPP;
+       }
+       if (!tc_cls_can_offload_and_chain0(repr->netdev, &cls_u32->common))
+               return -EOPNOTSUPP;
+
+       if (cls_u32->common.protocol != htons(ETH_P_IP) &&
+           cls_u32->common.protocol != htons(ETH_P_IPV6)) {
+               NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
+                                  "only IP and IPv6 supported as filter protocol");
+               return -EOPNOTSUPP;
+       }
+
+       switch (cls_u32->command) {
+       case TC_CLSU32_NEW_KNODE:
+       case TC_CLSU32_REPLACE_KNODE:
+               return nfp_abm_u32_knode_replace(alink, &cls_u32->knode,
+                                                cls_u32->common.protocol,
+                                                cls_u32->common.extack);
+       case TC_CLSU32_DELETE_KNODE:
+               nfp_abm_u32_knode_delete(alink, &cls_u32->knode);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+int nfp_abm_setup_cls_block(struct net_device *netdev, struct nfp_repr *repr,
+                           struct tc_block_offload *f)
+{
+       if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
+               return -EOPNOTSUPP;
+
+       switch (f->command) {
+       case TC_BLOCK_BIND:
+               return tcf_block_cb_register(f->block,
+                                            nfp_abm_setup_tc_block_cb,
+                                            repr, repr, f->extack);
+       case TC_BLOCK_UNBIND:
+               tcf_block_cb_unregister(f->block, nfp_abm_setup_tc_block_cb,
+                                       repr);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
index 3c661f4..9584f03 100644 (file)
@@ -1,7 +1,9 @@
 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 /* Copyright (C) 2018 Netronome Systems, Inc. */
 
+#include <linux/bitops.h>
 #include <linux/kernel.h>
+#include <linux/log2.h>
 
 #include "../nfpcore/nfp_cpp.h"
 #include "../nfpcore/nfp_nffw.h"
 #include "../nfp_net.h"
 #include "main.h"
 
-#define NFP_QLVL_SYM_NAME      "_abi_nfd_out_q_lvls_%u"
+#define NFP_NUM_PRIOS_SYM_NAME "_abi_pci_dscp_num_prio_%u"
+#define NFP_NUM_BANDS_SYM_NAME "_abi_pci_dscp_num_band_%u"
+#define NFP_ACT_MASK_SYM_NAME  "_abi_nfd_out_q_actions_%u"
+
+#define NFP_RED_SUPPORT_SYM_NAME       "_abi_nfd_out_red_offload_%u"
+
+#define NFP_QLVL_SYM_NAME      "_abi_nfd_out_q_lvls_%u%s"
 #define NFP_QLVL_STRIDE                16
 #define NFP_QLVL_BLOG_BYTES    0
 #define NFP_QLVL_BLOG_PKTS     4
 #define NFP_QLVL_THRS          8
+#define NFP_QLVL_ACT           12
 
-#define NFP_QMSTAT_SYM_NAME    "_abi_nfdqm%u_stats"
+#define NFP_QMSTAT_SYM_NAME    "_abi_nfdqm%u_stats%s"
 #define NFP_QMSTAT_STRIDE      32
 #define NFP_QMSTAT_NON_STO     0
 #define NFP_QMSTAT_STO         8
 #define NFP_QMSTAT_DROP                16
 #define NFP_QMSTAT_ECN         24
 
+#define NFP_Q_STAT_SYM_NAME    "_abi_nfd_rxq_stats%u%s"
+#define NFP_Q_STAT_STRIDE      16
+#define NFP_Q_STAT_PKTS                0
+#define NFP_Q_STAT_BYTES       8
+
+#define NFP_NET_ABM_MBOX_CMD           NFP_NET_CFG_MBOX_SIMPLE_CMD
+#define NFP_NET_ABM_MBOX_RET           NFP_NET_CFG_MBOX_SIMPLE_RET
+#define NFP_NET_ABM_MBOX_DATALEN       NFP_NET_CFG_MBOX_SIMPLE_VAL
+#define NFP_NET_ABM_MBOX_RESERVED      (NFP_NET_CFG_MBOX_SIMPLE_VAL + 4)
+#define NFP_NET_ABM_MBOX_DATA          (NFP_NET_CFG_MBOX_SIMPLE_VAL + 8)
+
 static int
 nfp_abm_ctrl_stat(struct nfp_abm_link *alink, const struct nfp_rtsym *sym,
-                 unsigned int stride, unsigned int offset, unsigned int i,
-                 bool is_u64, u64 *res)
+                 unsigned int stride, unsigned int offset, unsigned int band,
+                 unsigned int queue, bool is_u64, u64 *res)
 {
        struct nfp_cpp *cpp = alink->abm->app->cpp;
        u64 val, sym_offset;
+       unsigned int qid;
        u32 val32;
        int err;
 
-       sym_offset = (alink->queue_base + i) * stride + offset;
+       qid = band * NFP_NET_MAX_RX_RINGS + alink->queue_base + queue;
+
+       sym_offset = qid * stride + offset;
        if (is_u64)
                err = __nfp_rtsym_readq(cpp, sym, 3, 0, sym_offset, &val);
        else
                err = __nfp_rtsym_readl(cpp, sym, 3, 0, sym_offset, &val32);
        if (err) {
-               nfp_err(cpp,
-                       "RED offload reading stat failed on vNIC %d queue %d\n",
-                       alink->id, i);
+               nfp_err(cpp, "RED offload reading stat failed on vNIC %d band %d queue %d (+ %d)\n",
+                       alink->id, band, queue, alink->queue_base);
                return err;
        }
 
@@ -50,175 +72,179 @@ nfp_abm_ctrl_stat(struct nfp_abm_link *alink, const struct nfp_rtsym *sym,
        return 0;
 }
 
-static int
-nfp_abm_ctrl_stat_all(struct nfp_abm_link *alink, const struct nfp_rtsym *sym,
-                     unsigned int stride, unsigned int offset, bool is_u64,
-                     u64 *res)
+int __nfp_abm_ctrl_set_q_lvl(struct nfp_abm *abm, unsigned int id, u32 val)
 {
-       u64 val, sum = 0;
-       unsigned int i;
+       struct nfp_cpp *cpp = abm->app->cpp;
+       u64 sym_offset;
        int err;
 
-       for (i = 0; i < alink->vnic->max_rx_rings; i++) {
-               err = nfp_abm_ctrl_stat(alink, sym, stride, offset, i,
-                                       is_u64, &val);
-               if (err)
-                       return err;
-               sum += val;
+       __clear_bit(id, abm->threshold_undef);
+       if (abm->thresholds[id] == val)
+               return 0;
+
+       sym_offset = id * NFP_QLVL_STRIDE + NFP_QLVL_THRS;
+       err = __nfp_rtsym_writel(cpp, abm->q_lvls, 4, 0, sym_offset, val);
+       if (err) {
+               nfp_err(cpp,
+                       "RED offload setting level failed on subqueue %d\n",
+                       id);
+               return err;
        }
 
-       *res = sum;
+       abm->thresholds[id] = val;
        return 0;
 }
 
-int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int i, u32 val)
+int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int band,
+                          unsigned int queue, u32 val)
 {
-       struct nfp_cpp *cpp = alink->abm->app->cpp;
+       unsigned int threshold;
+
+       threshold = band * NFP_NET_MAX_RX_RINGS + alink->queue_base + queue;
+
+       return __nfp_abm_ctrl_set_q_lvl(alink->abm, threshold, val);
+}
+
+int __nfp_abm_ctrl_set_q_act(struct nfp_abm *abm, unsigned int id,
+                            enum nfp_abm_q_action act)
+{
+       struct nfp_cpp *cpp = abm->app->cpp;
        u64 sym_offset;
        int err;
 
-       sym_offset = (alink->queue_base + i) * NFP_QLVL_STRIDE + NFP_QLVL_THRS;
-       err = __nfp_rtsym_writel(cpp, alink->abm->q_lvls, 4, 0,
-                                sym_offset, val);
+       if (abm->actions[id] == act)
+               return 0;
+
+       sym_offset = id * NFP_QLVL_STRIDE + NFP_QLVL_ACT;
+       err = __nfp_rtsym_writel(cpp, abm->q_lvls, 4, 0, sym_offset, act);
        if (err) {
-               nfp_err(cpp, "RED offload setting level failed on vNIC %d queue %d\n",
-                       alink->id, i);
+               nfp_err(cpp,
+                       "RED offload setting action failed on subqueue %d\n",
+                       id);
                return err;
        }
 
+       abm->actions[id] = act;
        return 0;
 }
 
-int nfp_abm_ctrl_set_all_q_lvls(struct nfp_abm_link *alink, u32 val)
+int nfp_abm_ctrl_set_q_act(struct nfp_abm_link *alink, unsigned int band,
+                          unsigned int queue, enum nfp_abm_q_action act)
+{
+       unsigned int qid;
+
+       qid = band * NFP_NET_MAX_RX_RINGS + alink->queue_base + queue;
+
+       return __nfp_abm_ctrl_set_q_act(alink->abm, qid, act);
+}
+
+u64 nfp_abm_ctrl_stat_non_sto(struct nfp_abm_link *alink, unsigned int queue)
 {
-       int i, err;
+       unsigned int band;
+       u64 val, sum = 0;
 
-       for (i = 0; i < alink->vnic->max_rx_rings; i++) {
-               err = nfp_abm_ctrl_set_q_lvl(alink, i, val);
-               if (err)
-                       return err;
+       for (band = 0; band < alink->abm->num_bands; band++) {
+               if (nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
+                                     NFP_QMSTAT_STRIDE, NFP_QMSTAT_NON_STO,
+                                     band, queue, true, &val))
+                       return 0;
+               sum += val;
        }
 
-       return 0;
+       return sum;
 }
 
-u64 nfp_abm_ctrl_stat_non_sto(struct nfp_abm_link *alink, unsigned int i)
+u64 nfp_abm_ctrl_stat_sto(struct nfp_abm_link *alink, unsigned int queue)
 {
-       u64 val;
+       unsigned int band;
+       u64 val, sum = 0;
 
-       if (nfp_abm_ctrl_stat(alink, alink->abm->qm_stats, NFP_QMSTAT_STRIDE,
-                             NFP_QMSTAT_NON_STO, i, true, &val))
-               return 0;
-       return val;
+       for (band = 0; band < alink->abm->num_bands; band++) {
+               if (nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
+                                     NFP_QMSTAT_STRIDE, NFP_QMSTAT_STO,
+                                     band, queue, true, &val))
+                       return 0;
+               sum += val;
+       }
+
+       return sum;
 }
 
-u64 nfp_abm_ctrl_stat_sto(struct nfp_abm_link *alink, unsigned int i)
+static int
+nfp_abm_ctrl_stat_basic(struct nfp_abm_link *alink, unsigned int band,
+                       unsigned int queue, unsigned int off, u64 *val)
 {
-       u64 val;
+       if (!nfp_abm_has_prio(alink->abm)) {
+               if (!band) {
+                       unsigned int id = alink->queue_base + queue;
+
+                       *val = nn_readq(alink->vnic,
+                                       NFP_NET_CFG_RXR_STATS(id) + off);
+               } else {
+                       *val = 0;
+               }
 
-       if (nfp_abm_ctrl_stat(alink, alink->abm->qm_stats, NFP_QMSTAT_STRIDE,
-                             NFP_QMSTAT_STO, i, true, &val))
                return 0;
-       return val;
+       } else {
+               return nfp_abm_ctrl_stat(alink, alink->abm->q_stats,
+                                        NFP_Q_STAT_STRIDE, off, band, queue,
+                                        true, val);
+       }
 }
 
-int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink, unsigned int i,
-                             struct nfp_alink_stats *stats)
+int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink, unsigned int band,
+                             unsigned int queue, struct nfp_alink_stats *stats)
 {
        int err;
 
-       stats->tx_pkts = nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i));
-       stats->tx_bytes = nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i) + 8);
+       err = nfp_abm_ctrl_stat_basic(alink, band, queue, NFP_Q_STAT_PKTS,
+                                     &stats->tx_pkts);
+       if (err)
+               return err;
 
-       err = nfp_abm_ctrl_stat(alink, alink->abm->q_lvls,
-                               NFP_QLVL_STRIDE, NFP_QLVL_BLOG_BYTES,
-                               i, false, &stats->backlog_bytes);
+       err = nfp_abm_ctrl_stat_basic(alink, band, queue, NFP_Q_STAT_BYTES,
+                                     &stats->tx_bytes);
+       if (err)
+               return err;
+
+       err = nfp_abm_ctrl_stat(alink, alink->abm->q_lvls, NFP_QLVL_STRIDE,
+                               NFP_QLVL_BLOG_BYTES, band, queue, false,
+                               &stats->backlog_bytes);
        if (err)
                return err;
 
        err = nfp_abm_ctrl_stat(alink, alink->abm->q_lvls,
                                NFP_QLVL_STRIDE, NFP_QLVL_BLOG_PKTS,
-                               i, false, &stats->backlog_pkts);
+                               band, queue, false, &stats->backlog_pkts);
        if (err)
                return err;
 
        err = nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
                                NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
-                               i, true, &stats->drops);
+                               band, queue, true, &stats->drops);
        if (err)
                return err;
 
        return nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
                                 NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
-                                i, true, &stats->overlimits);
+                                band, queue, true, &stats->overlimits);
 }
 
-int nfp_abm_ctrl_read_stats(struct nfp_abm_link *alink,
-                           struct nfp_alink_stats *stats)
-{
-       u64 pkts = 0, bytes = 0;
-       int i, err;
-
-       for (i = 0; i < alink->vnic->max_rx_rings; i++) {
-               pkts += nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i));
-               bytes += nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i) + 8);
-       }
-       stats->tx_pkts = pkts;
-       stats->tx_bytes = bytes;
-
-       err = nfp_abm_ctrl_stat_all(alink, alink->abm->q_lvls,
-                                   NFP_QLVL_STRIDE, NFP_QLVL_BLOG_BYTES,
-                                   false, &stats->backlog_bytes);
-       if (err)
-               return err;
-
-       err = nfp_abm_ctrl_stat_all(alink, alink->abm->q_lvls,
-                                   NFP_QLVL_STRIDE, NFP_QLVL_BLOG_PKTS,
-                                   false, &stats->backlog_pkts);
-       if (err)
-               return err;
-
-       err = nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
-                                   NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
-                                   true, &stats->drops);
-       if (err)
-               return err;
-
-       return nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
-                                    NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
-                                    true, &stats->overlimits);
-}
-
-int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink, unsigned int i,
+int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink,
+                              unsigned int band, unsigned int queue,
                               struct nfp_alink_xstats *xstats)
 {
        int err;
 
        err = nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
                                NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
-                               i, true, &xstats->pdrop);
+                               band, queue, true, &xstats->pdrop);
        if (err)
                return err;
 
        return nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
                                 NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
-                                i, true, &xstats->ecn_marked);
-}
-
-int nfp_abm_ctrl_read_xstats(struct nfp_abm_link *alink,
-                            struct nfp_alink_xstats *xstats)
-{
-       int err;
-
-       err = nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
-                                   NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
-                                   true, &xstats->pdrop);
-       if (err)
-               return err;
-
-       return nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
-                                    NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
-                                    true, &xstats->ecn_marked);
+                                band, queue, true, &xstats->ecn_marked);
 }
 
 int nfp_abm_ctrl_qm_enable(struct nfp_abm *abm)
@@ -233,10 +259,64 @@ int nfp_abm_ctrl_qm_disable(struct nfp_abm *abm)
                            NULL, 0, NULL, 0);
 }
 
-void nfp_abm_ctrl_read_params(struct nfp_abm_link *alink)
+int nfp_abm_ctrl_prio_map_update(struct nfp_abm_link *alink, u32 *packed)
+{
+       struct nfp_net *nn = alink->vnic;
+       unsigned int i;
+       int err;
+
+       /* Write data_len and wipe reserved */
+       nn_writeq(nn, nn->tlv_caps.mbox_off + NFP_NET_ABM_MBOX_DATALEN,
+                 alink->abm->prio_map_len);
+
+       for (i = 0; i < alink->abm->prio_map_len; i += sizeof(u32))
+               nn_writel(nn, nn->tlv_caps.mbox_off + NFP_NET_ABM_MBOX_DATA + i,
+                         packed[i / sizeof(u32)]);
+
+       err = nfp_net_reconfig_mbox(nn,
+                                   NFP_NET_CFG_MBOX_CMD_PCI_DSCP_PRIOMAP_SET);
+       if (err)
+               nfp_err(alink->abm->app->cpp,
+                       "setting DSCP -> VQ map failed with error %d\n", err);
+       return err;
+}
+
+static int nfp_abm_ctrl_prio_check_params(struct nfp_abm_link *alink)
+{
+       struct nfp_abm *abm = alink->abm;
+       struct nfp_net *nn = alink->vnic;
+       unsigned int min_mbox_sz;
+
+       if (!nfp_abm_has_prio(alink->abm))
+               return 0;
+
+       min_mbox_sz = NFP_NET_ABM_MBOX_DATA + alink->abm->prio_map_len;
+       if (nn->tlv_caps.mbox_len < min_mbox_sz) {
+               nfp_err(abm->app->pf->cpp, "vNIC mailbox too small for prio offload: %u, need: %u\n",
+                       nn->tlv_caps.mbox_len,  min_mbox_sz);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int nfp_abm_ctrl_read_params(struct nfp_abm_link *alink)
 {
        alink->queue_base = nn_readl(alink->vnic, NFP_NET_CFG_START_RXQ);
        alink->queue_base /= alink->vnic->stride_rx;
+
+       return nfp_abm_ctrl_prio_check_params(alink);
+}
+
+static unsigned int nfp_abm_ctrl_prio_map_size(struct nfp_abm *abm)
+{
+       unsigned int size;
+
+       size = roundup_pow_of_two(order_base_2(abm->num_bands));
+       size = DIV_ROUND_UP(size * abm->num_prios, BITS_PER_BYTE);
+       size = round_up(size, sizeof(u32));
+
+       return size;
 }
 
 static const struct nfp_rtsym *
@@ -260,33 +340,86 @@ nfp_abm_ctrl_find_rtsym(struct nfp_pf *pf, const char *name, unsigned int size)
 }
 
 static const struct nfp_rtsym *
-nfp_abm_ctrl_find_q_rtsym(struct nfp_pf *pf, const char *name,
-                         unsigned int size)
+nfp_abm_ctrl_find_q_rtsym(struct nfp_abm *abm, const char *name_fmt,
+                         size_t size)
 {
-       return nfp_abm_ctrl_find_rtsym(pf, name, size * NFP_NET_MAX_RX_RINGS);
+       char pf_symbol[64];
+
+       size = array3_size(size, abm->num_bands, NFP_NET_MAX_RX_RINGS);
+       snprintf(pf_symbol, sizeof(pf_symbol), name_fmt,
+                abm->pf_id, nfp_abm_has_prio(abm) ? "_per_band" : "");
+
+       return nfp_abm_ctrl_find_rtsym(abm->app->pf, pf_symbol, size);
 }
 
 int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm)
 {
        struct nfp_pf *pf = abm->app->pf;
        const struct nfp_rtsym *sym;
-       unsigned int pf_id;
-       char pf_symbol[64];
+       int res;
+
+       abm->pf_id = nfp_cppcore_pcie_unit(pf->cpp);
+
+       /* Check if Qdisc offloads are supported */
+       res = nfp_pf_rtsym_read_optional(pf, NFP_RED_SUPPORT_SYM_NAME, 1);
+       if (res < 0)
+               return res;
+       abm->red_support = res;
+
+       /* Read count of prios and prio bands */
+       res = nfp_pf_rtsym_read_optional(pf, NFP_NUM_BANDS_SYM_NAME, 1);
+       if (res < 0)
+               return res;
+       abm->num_bands = res;
+
+       res = nfp_pf_rtsym_read_optional(pf, NFP_NUM_PRIOS_SYM_NAME, 1);
+       if (res < 0)
+               return res;
+       abm->num_prios = res;
+
+       /* Read available actions */
+       res = nfp_pf_rtsym_read_optional(pf, NFP_ACT_MASK_SYM_NAME,
+                                        BIT(NFP_ABM_ACT_MARK_DROP));
+       if (res < 0)
+               return res;
+       abm->action_mask = res;
+
+       abm->prio_map_len = nfp_abm_ctrl_prio_map_size(abm);
+       abm->dscp_mask = GENMASK(7, 8 - order_base_2(abm->num_prios));
+
+       /* Check values are sane, U16_MAX is arbitrarily chosen as max */
+       if (!is_power_of_2(abm->num_bands) || !is_power_of_2(abm->num_prios) ||
+           abm->num_bands > U16_MAX || abm->num_prios > U16_MAX ||
+           (abm->num_bands == 1) != (abm->num_prios == 1)) {
+               nfp_err(pf->cpp,
+                       "invalid priomap description num bands: %u and num prios: %u\n",
+                       abm->num_bands, abm->num_prios);
+               return -EINVAL;
+       }
 
-       pf_id = nfp_cppcore_pcie_unit(pf->cpp);
-       abm->pf_id = pf_id;
+       /* Find level and stat symbols */
+       if (!abm->red_support)
+               return 0;
 
-       snprintf(pf_symbol, sizeof(pf_symbol), NFP_QLVL_SYM_NAME, pf_id);
-       sym = nfp_abm_ctrl_find_q_rtsym(pf, pf_symbol, NFP_QLVL_STRIDE);
+       sym = nfp_abm_ctrl_find_q_rtsym(abm, NFP_QLVL_SYM_NAME,
+                                       NFP_QLVL_STRIDE);
        if (IS_ERR(sym))
                return PTR_ERR(sym);
        abm->q_lvls = sym;
 
-       snprintf(pf_symbol, sizeof(pf_symbol), NFP_QMSTAT_SYM_NAME, pf_id);
-       sym = nfp_abm_ctrl_find_q_rtsym(pf, pf_symbol, NFP_QMSTAT_STRIDE);
+       sym = nfp_abm_ctrl_find_q_rtsym(abm, NFP_QMSTAT_SYM_NAME,
+                                       NFP_QMSTAT_STRIDE);
        if (IS_ERR(sym))
                return PTR_ERR(sym);
        abm->qm_stats = sym;
 
+       if (nfp_abm_has_prio(abm)) {
+               sym = nfp_abm_ctrl_find_q_rtsym(abm, NFP_Q_STAT_SYM_NAME,
+                                               NFP_Q_STAT_STRIDE);
+               if (IS_ERR(sym))
+                       return PTR_ERR(sym);
+               abm->q_stats = sym;
+       }
+
        return 0;
 }
index 3d15de0..4d4ff58 100644 (file)
@@ -2,10 +2,12 @@
 /* Copyright (C) 2018 Netronome Systems, Inc. */
 
 #include <linux/bitfield.h>
+#include <linux/bitmap.h>
 #include <linux/etherdevice.h>
 #include <linux/lockdep.h>
 #include <linux/netdevice.h>
 #include <linux/rcupdate.h>
+#include <linux/rtnetlink.h>
 #include <linux/slab.h>
 
 #include "../nfpcore/nfp.h"
@@ -36,10 +38,16 @@ nfp_abm_setup_tc(struct nfp_app *app, struct net_device *netdev,
                return -EOPNOTSUPP;
 
        switch (type) {
+       case TC_SETUP_ROOT_QDISC:
+               return nfp_abm_setup_root(netdev, repr->app_priv, type_data);
        case TC_SETUP_QDISC_MQ:
                return nfp_abm_setup_tc_mq(netdev, repr->app_priv, type_data);
        case TC_SETUP_QDISC_RED:
                return nfp_abm_setup_tc_red(netdev, repr->app_priv, type_data);
+       case TC_SETUP_QDISC_GRED:
+               return nfp_abm_setup_tc_gred(netdev, repr->app_priv, type_data);
+       case TC_SETUP_BLOCK:
+               return nfp_abm_setup_cls_block(netdev, repr, type_data);
        default:
                return -EOPNOTSUPP;
        }
@@ -118,7 +126,9 @@ nfp_abm_spawn_repr(struct nfp_app *app, struct nfp_abm_link *alink,
 
        reprs = nfp_reprs_get_locked(app, rtype);
        WARN(nfp_repr_get_locked(app, reprs, alink->id), "duplicate repr");
+       rtnl_lock();
        rcu_assign_pointer(reprs->reprs[alink->id], netdev);
+       rtnl_unlock();
 
        nfp_info(app->cpp, "%s Port %d Representor(%s) created\n",
                 ptype == NFP_PORT_PF_PORT ? "PCIe" : "Phys",
@@ -144,7 +154,9 @@ nfp_abm_kill_repr(struct nfp_app *app, struct nfp_abm_link *alink,
        netdev = nfp_repr_get_locked(app, reprs, alink->id);
        if (!netdev)
                return;
+       rtnl_lock();
        rcu_assign_pointer(reprs->reprs[alink->id], NULL);
+       rtnl_unlock();
        synchronize_rcu();
        /* Cast to make sure nfp_repr_clean_and_free() takes a nfp_repr */
        nfp_repr_clean_and_free((struct nfp_repr *)netdev_priv(netdev));
@@ -195,6 +207,9 @@ static int nfp_abm_eswitch_set_switchdev(struct nfp_abm *abm)
        struct nfp_net *nn;
        int err;
 
+       if (!abm->red_support)
+               return -EOPNOTSUPP;
+
        err = nfp_abm_ctrl_qm_enable(abm);
        if (err)
                return err;
@@ -307,31 +322,34 @@ nfp_abm_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id)
        alink->abm = abm;
        alink->vnic = nn;
        alink->id = id;
-       alink->parent = TC_H_ROOT;
        alink->total_queues = alink->vnic->max_rx_rings;
-       alink->qdiscs = kvcalloc(alink->total_queues, sizeof(*alink->qdiscs),
-                                GFP_KERNEL);
-       if (!alink->qdiscs) {
-               err = -ENOMEM;
+
+       INIT_LIST_HEAD(&alink->dscp_map);
+
+       err = nfp_abm_ctrl_read_params(alink);
+       if (err)
+               goto err_free_alink;
+
+       alink->prio_map = kzalloc(abm->prio_map_len, GFP_KERNEL);
+       if (!alink->prio_map)
                goto err_free_alink;
-       }
 
        /* This is a multi-host app, make sure MAC/PHY is up, but don't
         * make the MAC/PHY state follow the state of any of the ports.
         */
        err = nfp_eth_set_configured(app->cpp, eth_port->index, true);
        if (err < 0)
-               goto err_free_qdiscs;
+               goto err_free_priomap;
 
        netif_keep_dst(nn->dp.netdev);
 
        nfp_abm_vnic_set_mac(app->pf, abm, nn, id);
-       nfp_abm_ctrl_read_params(alink);
+       INIT_RADIX_TREE(&alink->qdiscs, GFP_KERNEL);
 
        return 0;
 
-err_free_qdiscs:
-       kvfree(alink->qdiscs);
+err_free_priomap:
+       kfree(alink->prio_map);
 err_free_alink:
        kfree(alink);
        return err;
@@ -342,10 +360,20 @@ static void nfp_abm_vnic_free(struct nfp_app *app, struct nfp_net *nn)
        struct nfp_abm_link *alink = nn->app_priv;
 
        nfp_abm_kill_reprs(alink->abm, alink);
-       kvfree(alink->qdiscs);
+       WARN(!radix_tree_empty(&alink->qdiscs), "left over qdiscs\n");
+       kfree(alink->prio_map);
        kfree(alink);
 }
 
+static int nfp_abm_vnic_init(struct nfp_app *app, struct nfp_net *nn)
+{
+       struct nfp_abm_link *alink = nn->app_priv;
+
+       if (nfp_abm_has_prio(alink->abm))
+               return nfp_abm_ctrl_prio_map_update(alink, alink->prio_map);
+       return 0;
+}
+
 static u64 *
 nfp_abm_port_get_stats(struct nfp_app *app, struct nfp_port *port, u64 *data)
 {
@@ -393,6 +421,21 @@ nfp_abm_port_get_stats_strings(struct nfp_app *app, struct nfp_port *port,
        return data;
 }
 
+static int nfp_abm_fw_init_reset(struct nfp_abm *abm)
+{
+       unsigned int i;
+
+       if (!abm->red_support)
+               return 0;
+
+       for (i = 0; i < abm->num_bands * NFP_NET_MAX_RX_RINGS; i++) {
+               __nfp_abm_ctrl_set_q_lvl(abm, i, NFP_ABM_LVL_INFINITY);
+               __nfp_abm_ctrl_set_q_act(abm, i, NFP_ABM_ACT_DROP);
+       }
+
+       return nfp_abm_ctrl_qm_disable(abm);
+}
+
 static int nfp_abm_init(struct nfp_app *app)
 {
        struct nfp_pf *pf = app->pf;
@@ -424,15 +467,31 @@ static int nfp_abm_init(struct nfp_app *app)
        if (err)
                goto err_free_abm;
 
+       err = -ENOMEM;
+       abm->num_thresholds = array_size(abm->num_bands, NFP_NET_MAX_RX_RINGS);
+       abm->threshold_undef = bitmap_zalloc(abm->num_thresholds, GFP_KERNEL);
+       if (!abm->threshold_undef)
+               goto err_free_abm;
+
+       abm->thresholds = kvcalloc(abm->num_thresholds,
+                                  sizeof(*abm->thresholds), GFP_KERNEL);
+       if (!abm->thresholds)
+               goto err_free_thresh_umap;
+
+       abm->actions = kvcalloc(abm->num_thresholds, sizeof(*abm->actions),
+                               GFP_KERNEL);
+       if (!abm->actions)
+               goto err_free_thresh;
+
        /* We start in legacy mode, make sure advanced queuing is disabled */
-       err = nfp_abm_ctrl_qm_disable(abm);
+       err = nfp_abm_fw_init_reset(abm);
        if (err)
-               goto err_free_abm;
+               goto err_free_act;
 
        err = -ENOMEM;
        reprs = nfp_reprs_alloc(pf->max_data_vnics);
        if (!reprs)
-               goto err_free_abm;
+               goto err_free_act;
        RCU_INIT_POINTER(app->reprs[NFP_REPR_TYPE_PHYS_PORT], reprs);
 
        reprs = nfp_reprs_alloc(pf->max_data_vnics);
@@ -444,6 +503,12 @@ static int nfp_abm_init(struct nfp_app *app)
 
 err_free_phys:
        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT);
+err_free_act:
+       kvfree(abm->actions);
+err_free_thresh:
+       kvfree(abm->thresholds);
+err_free_thresh_umap:
+       bitmap_free(abm->threshold_undef);
 err_free_abm:
        kfree(abm);
        app->priv = NULL;
@@ -457,6 +522,9 @@ static void nfp_abm_clean(struct nfp_app *app)
        nfp_abm_eswitch_clean_up(abm);
        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF);
        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT);
+       bitmap_free(abm->threshold_undef);
+       kvfree(abm->actions);
+       kvfree(abm->thresholds);
        kfree(abm);
        app->priv = NULL;
 }
@@ -470,6 +538,7 @@ const struct nfp_app_type app_abm = {
 
        .vnic_alloc     = nfp_abm_vnic_alloc,
        .vnic_free      = nfp_abm_vnic_free,
+       .vnic_init      = nfp_abm_vnic_init,
 
        .port_get_stats         = nfp_abm_port_get_stats,
        .port_get_stats_count   = nfp_abm_port_get_stats_count,
index 3774c06..49749c6 100644 (file)
@@ -5,8 +5,16 @@
 #define __NFP_ABM_H__ 1
 
 #include <linux/bits.h>
+#include <linux/list.h>
+#include <linux/radix-tree.h>
 #include <net/devlink.h>
 #include <net/pkt_cls.h>
+#include <net/pkt_sched.h>
+
+/* Dump of 64 PRIOs and 256 REDs seems to take 850us on Xeon v4 @ 2.20GHz;
+ * 2.5ms / 400Hz seems more than sufficient for stats resolution.
+ */
+#define NFP_ABM_STATS_REFRESH_IVAL     (2500 * 1000) /* ns */
 
 #define NFP_ABM_LVL_INFINITY           S32_MAX
 
@@ -16,21 +24,64 @@ struct nfp_net;
 #define NFP_ABM_PORTID_TYPE    GENMASK(23, 16)
 #define NFP_ABM_PORTID_ID      GENMASK(7, 0)
 
+/* The possible actions if thresholds are exceeded */
+enum nfp_abm_q_action {
+       /* mark if ECN capable, otherwise drop */
+       NFP_ABM_ACT_MARK_DROP           = 0,
+       /* mark if ECN capable, otherwise goto QM */
+       NFP_ABM_ACT_MARK_QUEUE          = 1,
+       NFP_ABM_ACT_DROP                = 2,
+       NFP_ABM_ACT_QUEUE               = 3,
+       NFP_ABM_ACT_NOQUEUE             = 4,
+};
+
 /**
  * struct nfp_abm - ABM NIC app structure
  * @app:       back pointer to nfp_app
  * @pf_id:     ID of our PF link
+ *
+ * @red_support:       is RED offload supported
+ * @num_prios: number of supported DSCP priorities
+ * @num_bands: number of supported DSCP priority bands
+ * @action_mask:       bitmask of supported actions
+ *
+ * @thresholds:                current threshold configuration
+ * @threshold_undef:   bitmap of thresholds which have not been set
+ * @actions:           current FW action configuration
+ * @num_thresholds:    number of @thresholds and bits in @threshold_undef
+ *
+ * @prio_map_len:      computed length of FW priority map (in bytes)
+ * @dscp_mask:         mask FW will apply on DSCP field
+ *
  * @eswitch_mode:      devlink eswitch mode, advanced functions only visible
  *                     in switchdev mode
+ *
  * @q_lvls:    queue level control area
  * @qm_stats:  queue statistics symbol
+ * @q_stats:   basic queue statistics (only in per-band case)
  */
 struct nfp_abm {
        struct nfp_app *app;
        unsigned int pf_id;
+
+       unsigned int red_support;
+       unsigned int num_prios;
+       unsigned int num_bands;
+       unsigned int action_mask;
+
+       u32 *thresholds;
+       unsigned long *threshold_undef;
+       u8 *actions;
+       size_t num_thresholds;
+
+       unsigned int prio_map_len;
+       u8 dscp_mask;
+
        enum devlink_eswitch_mode eswitch_mode;
+
        const struct nfp_rtsym *q_lvls;
        const struct nfp_rtsym *qm_stats;
+       const struct nfp_rtsym *q_stats;
 };
 
 /**
@@ -61,16 +112,76 @@ struct nfp_alink_xstats {
        u64 pdrop;
 };
 
+enum nfp_qdisc_type {
+       NFP_QDISC_NONE = 0,
+       NFP_QDISC_MQ,
+       NFP_QDISC_RED,
+       NFP_QDISC_GRED,
+};
+
+#define NFP_QDISC_UNTRACKED    ((struct nfp_qdisc *)1UL)
+
 /**
- * struct nfp_red_qdisc - representation of single RED Qdisc
- * @handle:    handle of currently offloaded RED Qdisc
- * @stats:     statistics from last refresh
- * @xstats:    base of extended statistics
+ * struct nfp_qdisc - tracked TC Qdisc
+ * @netdev:            netdev on which Qdisc was created
+ * @type:              Qdisc type
+ * @handle:            handle of this Qdisc
+ * @parent_handle:     handle of the parent (unreliable if Qdisc was grafted)
+ * @use_cnt:           number of attachment points in the hierarchy
+ * @num_children:      current size of the @children array
+ * @children:          pointers to children
+ *
+ * @params_ok:         parameters of this Qdisc are OK for offload
+ * @offload_mark:      offload refresh state - selected for offload
+ * @offloaded:         Qdisc is currently offloaded to the HW
+ *
+ * @mq:                        MQ Qdisc specific parameters and state
+ * @mq.stats:          current stats of the MQ Qdisc
+ * @mq.prev_stats:     previously reported @mq.stats
+ *
+ * @red:               RED Qdisc specific parameters and state
+ * @red.num_bands:     Number of valid entries in the @red.band table
+ * @red.band:          Per-band array of RED instances
+ * @red.band.ecn:              ECN marking is enabled (rather than drop)
+ * @red.band.threshold:                ECN marking threshold
+ * @red.band.stats:            current stats of the RED Qdisc
+ * @red.band.prev_stats:       previously reported @red.stats
+ * @red.band.xstats:           extended stats for RED - current
+ * @red.band.prev_xstats:      extended stats for RED - previously reported
  */
-struct nfp_red_qdisc {
+struct nfp_qdisc {
+       struct net_device *netdev;
+       enum nfp_qdisc_type type;
        u32 handle;
-       struct nfp_alink_stats stats;
-       struct nfp_alink_xstats xstats;
+       u32 parent_handle;
+       unsigned int use_cnt;
+       unsigned int num_children;
+       struct nfp_qdisc **children;
+
+       bool params_ok;
+       bool offload_mark;
+       bool offloaded;
+
+       union {
+               /* NFP_QDISC_MQ */
+               struct {
+                       struct nfp_alink_stats stats;
+                       struct nfp_alink_stats prev_stats;
+               } mq;
+               /* TC_SETUP_QDISC_RED, TC_SETUP_QDISC_GRED */
+               struct {
+                       unsigned int num_bands;
+
+                       struct {
+                               bool ecn;
+                               u32 threshold;
+                               struct nfp_alink_stats stats;
+                               struct nfp_alink_stats prev_stats;
+                               struct nfp_alink_xstats xstats;
+                               struct nfp_alink_xstats prev_xstats;
+                       } band[MAX_DPs];
+               } red;
+       };
 };
 
 /**
@@ -80,9 +191,17 @@ struct nfp_red_qdisc {
  * @id:                id of the data vNIC
  * @queue_base:        id of base to host queue within PCIe (not QC idx)
  * @total_queues:      number of PF queues
- * @parent:    handle of expected parent, i.e. handle of MQ, or TC_H_ROOT
- * @num_qdiscs:        number of currently used qdiscs
- * @qdiscs:    array of qdiscs
+ *
+ * @last_stats_update: ktime of last stats update
+ *
+ * @prio_map:          current map of priorities
+ * @has_prio:          @prio_map is valid
+ *
+ * @def_band:          default band to use
+ * @dscp_map:          list of DSCP to band mappings
+ *
+ * @root_qdisc:        pointer to the current root of the Qdisc hierarchy
+ * @qdiscs:    all qdiscs recorded by major part of the handle
  */
 struct nfp_abm_link {
        struct nfp_abm *abm;
@@ -90,31 +209,65 @@ struct nfp_abm_link {
        unsigned int id;
        unsigned int queue_base;
        unsigned int total_queues;
-       u32 parent;
-       unsigned int num_qdiscs;
-       struct nfp_red_qdisc *qdiscs;
+
+       u64 last_stats_update;
+
+       u32 *prio_map;
+       bool has_prio;
+
+       u8 def_band;
+       struct list_head dscp_map;
+
+       struct nfp_qdisc *root_qdisc;
+       struct radix_tree_root qdiscs;
 };
 
+static inline bool nfp_abm_has_prio(struct nfp_abm *abm)
+{
+       return abm->num_bands > 1;
+}
+
+static inline bool nfp_abm_has_drop(struct nfp_abm *abm)
+{
+       return abm->action_mask & BIT(NFP_ABM_ACT_DROP);
+}
+
+static inline bool nfp_abm_has_mark(struct nfp_abm *abm)
+{
+       return abm->action_mask & BIT(NFP_ABM_ACT_MARK_DROP);
+}
+
+void nfp_abm_qdisc_offload_update(struct nfp_abm_link *alink);
+int nfp_abm_setup_root(struct net_device *netdev, struct nfp_abm_link *alink,
+                      struct tc_root_qopt_offload *opt);
 int nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
                         struct tc_red_qopt_offload *opt);
 int nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
                        struct tc_mq_qopt_offload *opt);
+int nfp_abm_setup_tc_gred(struct net_device *netdev, struct nfp_abm_link *alink,
+                         struct tc_gred_qopt_offload *opt);
+int nfp_abm_setup_cls_block(struct net_device *netdev, struct nfp_repr *repr,
+                           struct tc_block_offload *opt);
 
-void nfp_abm_ctrl_read_params(struct nfp_abm_link *alink);
+int nfp_abm_ctrl_read_params(struct nfp_abm_link *alink);
 int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm);
-int nfp_abm_ctrl_set_all_q_lvls(struct nfp_abm_link *alink, u32 val);
-int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int i,
-                          u32 val);
-int nfp_abm_ctrl_read_stats(struct nfp_abm_link *alink,
-                           struct nfp_alink_stats *stats);
-int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink, unsigned int i,
+int __nfp_abm_ctrl_set_q_lvl(struct nfp_abm *abm, unsigned int id, u32 val);
+int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int band,
+                          unsigned int queue, u32 val);
+int __nfp_abm_ctrl_set_q_act(struct nfp_abm *abm, unsigned int id,
+                            enum nfp_abm_q_action act);
+int nfp_abm_ctrl_set_q_act(struct nfp_abm_link *alink, unsigned int band,
+                          unsigned int queue, enum nfp_abm_q_action act);
+int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink,
+                             unsigned int band, unsigned int queue,
                              struct nfp_alink_stats *stats);
-int nfp_abm_ctrl_read_xstats(struct nfp_abm_link *alink,
-                            struct nfp_alink_xstats *xstats);
-int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink, unsigned int i,
+int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink,
+                              unsigned int band, unsigned int queue,
                               struct nfp_alink_xstats *xstats);
 u64 nfp_abm_ctrl_stat_non_sto(struct nfp_abm_link *alink, unsigned int i);
 u64 nfp_abm_ctrl_stat_sto(struct nfp_abm_link *alink, unsigned int i);
 int nfp_abm_ctrl_qm_enable(struct nfp_abm *abm);
 int nfp_abm_ctrl_qm_disable(struct nfp_abm *abm);
+void nfp_abm_prio_map_update(struct nfp_abm *abm);
+int nfp_abm_ctrl_prio_map_update(struct nfp_abm_link *alink, u32 *packed);
 #endif
index bb05f9e..2473fb5 100644 (file)
 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 /* Copyright (C) 2018 Netronome Systems, Inc. */
 
+#include <linux/rtnetlink.h>
 #include <net/pkt_cls.h>
 #include <net/pkt_sched.h>
 #include <net/red.h>
 
 #include "../nfpcore/nfp_cpp.h"
 #include "../nfp_app.h"
+#include "../nfp_main.h"
+#include "../nfp_net.h"
 #include "../nfp_port.h"
 #include "main.h"
 
-static int
-__nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink,
-                    u32 handle, unsigned int qs, u32 init_val)
+static bool nfp_abm_qdisc_is_red(struct nfp_qdisc *qdisc)
 {
-       struct nfp_port *port = nfp_port_from_netdev(netdev);
-       int ret;
+       return qdisc->type == NFP_QDISC_RED || qdisc->type == NFP_QDISC_GRED;
+}
+
+static bool nfp_abm_qdisc_child_valid(struct nfp_qdisc *qdisc, unsigned int id)
+{
+       return qdisc->children[id] &&
+              qdisc->children[id] != NFP_QDISC_UNTRACKED;
+}
+
+static void *nfp_abm_qdisc_tree_deref_slot(void __rcu **slot)
+{
+       return rtnl_dereference(*slot);
+}
+
+static void
+nfp_abm_stats_propagate(struct nfp_alink_stats *parent,
+                       struct nfp_alink_stats *child)
+{
+       parent->tx_pkts         += child->tx_pkts;
+       parent->tx_bytes        += child->tx_bytes;
+       parent->backlog_pkts    += child->backlog_pkts;
+       parent->backlog_bytes   += child->backlog_bytes;
+       parent->overlimits      += child->overlimits;
+       parent->drops           += child->drops;
+}
+
+static void
+nfp_abm_stats_update_red(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc,
+                        unsigned int queue)
+{
+       struct nfp_cpp *cpp = alink->abm->app->cpp;
+       unsigned int i;
+       int err;
+
+       if (!qdisc->offloaded)
+               return;
+
+       for (i = 0; i < qdisc->red.num_bands; i++) {
+               err = nfp_abm_ctrl_read_q_stats(alink, i, queue,
+                                               &qdisc->red.band[i].stats);
+               if (err)
+                       nfp_err(cpp, "RED stats (%d, %d) read failed with error %d\n",
+                               i, queue, err);
+
+               err = nfp_abm_ctrl_read_q_xstats(alink, i, queue,
+                                                &qdisc->red.band[i].xstats);
+               if (err)
+                       nfp_err(cpp, "RED xstats (%d, %d) read failed with error %d\n",
+                               i, queue, err);
+       }
+}
+
+static void
+nfp_abm_stats_update_mq(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc)
+{
+       unsigned int i;
+
+       if (qdisc->type != NFP_QDISC_MQ)
+               return;
+
+       for (i = 0; i < alink->total_queues; i++)
+               if (nfp_abm_qdisc_child_valid(qdisc, i))
+                       nfp_abm_stats_update_red(alink, qdisc->children[i], i);
+}
+
+static void __nfp_abm_stats_update(struct nfp_abm_link *alink, u64 time_now)
+{
+       alink->last_stats_update = time_now;
+       if (alink->root_qdisc)
+               nfp_abm_stats_update_mq(alink, alink->root_qdisc);
+}
 
-       ret = nfp_abm_ctrl_set_all_q_lvls(alink, init_val);
-       memset(alink->qdiscs, 0, sizeof(*alink->qdiscs) * alink->num_qdiscs);
+static void nfp_abm_stats_update(struct nfp_abm_link *alink)
+{
+       u64 now;
+
+       /* Limit the frequency of updates - stats of non-leaf qdiscs are a sum
+        * of all their leafs, so we would read the same stat multiple times
+        * for every dump.
+        */
+       now = ktime_get();
+       if (now - alink->last_stats_update < NFP_ABM_STATS_REFRESH_IVAL)
+               return;
+
+       __nfp_abm_stats_update(alink, now);
+}
 
-       alink->parent = handle;
-       alink->num_qdiscs = qs;
-       port->tc_offload_cnt = qs;
+static void
+nfp_abm_qdisc_unlink_children(struct nfp_qdisc *qdisc,
+                             unsigned int start, unsigned int end)
+{
+       unsigned int i;
 
-       return ret;
+       for (i = start; i < end; i++)
+               if (nfp_abm_qdisc_child_valid(qdisc, i)) {
+                       qdisc->children[i]->use_cnt--;
+                       qdisc->children[i] = NULL;
+               }
 }
 
 static void
-nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink,
-                  u32 handle, unsigned int qs)
+nfp_abm_qdisc_offload_stop(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc)
 {
-       __nfp_abm_reset_root(netdev, alink, handle, qs, NFP_ABM_LVL_INFINITY);
+       unsigned int i;
+
+       /* Don't complain when qdisc is getting unlinked */
+       if (qdisc->use_cnt)
+               nfp_warn(alink->abm->app->cpp, "Offload of '%08x' stopped\n",
+                        qdisc->handle);
+
+       if (!nfp_abm_qdisc_is_red(qdisc))
+               return;
+
+       for (i = 0; i < qdisc->red.num_bands; i++) {
+               qdisc->red.band[i].stats.backlog_pkts = 0;
+               qdisc->red.band[i].stats.backlog_bytes = 0;
+       }
 }
 
 static int
-nfp_abm_red_find(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
+__nfp_abm_stats_init(struct nfp_abm_link *alink, unsigned int band,
+                    unsigned int queue, struct nfp_alink_stats *prev_stats,
+                    struct nfp_alink_xstats *prev_xstats)
 {
-       unsigned int i = TC_H_MIN(opt->parent) - 1;
+       u64 backlog_pkts, backlog_bytes;
+       int err;
 
-       if (opt->parent == TC_H_ROOT)
-               i = 0;
-       else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent))
-               i = TC_H_MIN(opt->parent) - 1;
-       else
-               return -EOPNOTSUPP;
+       /* Don't touch the backlog, backlog can only be reset after it has
+        * been reported back to the tc qdisc stats.
+        */
+       backlog_pkts = prev_stats->backlog_pkts;
+       backlog_bytes = prev_stats->backlog_bytes;
+
+       err = nfp_abm_ctrl_read_q_stats(alink, band, queue, prev_stats);
+       if (err) {
+               nfp_err(alink->abm->app->cpp,
+                       "RED stats init (%d, %d) failed with error %d\n",
+                       band, queue, err);
+               return err;
+       }
 
-       if (i >= alink->num_qdiscs || opt->handle != alink->qdiscs[i].handle)
-               return -EOPNOTSUPP;
+       err = nfp_abm_ctrl_read_q_xstats(alink, band, queue, prev_xstats);
+       if (err) {
+               nfp_err(alink->abm->app->cpp,
+                       "RED xstats init (%d, %d) failed with error %d\n",
+                       band, queue, err);
+               return err;
+       }
 
-       return i;
+       prev_stats->backlog_pkts = backlog_pkts;
+       prev_stats->backlog_bytes = backlog_bytes;
+       return 0;
+}
+
+static int
+nfp_abm_stats_init(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc,
+                  unsigned int queue)
+{
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < qdisc->red.num_bands; i++) {
+               err = __nfp_abm_stats_init(alink, i, queue,
+                                          &qdisc->red.band[i].prev_stats,
+                                          &qdisc->red.band[i].prev_xstats);
+               if (err)
+                       return err;
+       }
+
+       return 0;
 }
 
 static void
-nfp_abm_red_destroy(struct net_device *netdev, struct nfp_abm_link *alink,
-                   u32 handle)
+nfp_abm_offload_compile_red(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc,
+                           unsigned int queue)
 {
+       bool good_red, good_gred;
        unsigned int i;
 
-       for (i = 0; i < alink->num_qdiscs; i++)
-               if (handle == alink->qdiscs[i].handle)
-                       break;
-       if (i == alink->num_qdiscs)
+       good_red = qdisc->type == NFP_QDISC_RED &&
+                  qdisc->params_ok &&
+                  qdisc->use_cnt == 1 &&
+                  !alink->has_prio &&
+                  !qdisc->children[0];
+       good_gred = qdisc->type == NFP_QDISC_GRED &&
+                   qdisc->params_ok &&
+                   qdisc->use_cnt == 1;
+       qdisc->offload_mark = good_red || good_gred;
+
+       /* If we are starting offload init prev_stats */
+       if (qdisc->offload_mark && !qdisc->offloaded)
+               if (nfp_abm_stats_init(alink, qdisc, queue))
+                       qdisc->offload_mark = false;
+
+       if (!qdisc->offload_mark)
                return;
 
-       if (alink->parent == TC_H_ROOT) {
-               nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0);
-       } else {
-               nfp_abm_ctrl_set_q_lvl(alink, i, NFP_ABM_LVL_INFINITY);
-               memset(&alink->qdiscs[i], 0, sizeof(*alink->qdiscs));
+       for (i = 0; i < alink->abm->num_bands; i++) {
+               enum nfp_abm_q_action act;
+
+               nfp_abm_ctrl_set_q_lvl(alink, i, queue,
+                                      qdisc->red.band[i].threshold);
+               act = qdisc->red.band[i].ecn ?
+                       NFP_ABM_ACT_MARK_DROP : NFP_ABM_ACT_DROP;
+               nfp_abm_ctrl_set_q_act(alink, i, queue, act);
        }
 }
 
-static bool
-nfp_abm_red_check_params(struct nfp_abm_link *alink,
-                        struct tc_red_qopt_offload *opt)
+static void
+nfp_abm_offload_compile_mq(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc)
 {
-       struct nfp_cpp *cpp = alink->abm->app->cpp;
+       unsigned int i;
 
-       if (!opt->set.is_ecn) {
-               nfp_warn(cpp, "RED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x)\n",
-                        opt->parent, opt->handle);
-               return false;
+       qdisc->offload_mark = qdisc->type == NFP_QDISC_MQ;
+       if (!qdisc->offload_mark)
+               return;
+
+       for (i = 0; i < alink->total_queues; i++) {
+               struct nfp_qdisc *child = qdisc->children[i];
+
+               if (!nfp_abm_qdisc_child_valid(qdisc, i))
+                       continue;
+
+               nfp_abm_offload_compile_red(alink, child, i);
        }
-       if (opt->set.is_harddrop) {
-               nfp_warn(cpp, "RED offload failed - harddrop is not supported (p:%08x h:%08x)\n",
-                        opt->parent, opt->handle);
-               return false;
+}
+
+void nfp_abm_qdisc_offload_update(struct nfp_abm_link *alink)
+{
+       struct nfp_abm *abm = alink->abm;
+       struct radix_tree_iter iter;
+       struct nfp_qdisc *qdisc;
+       void __rcu **slot;
+       size_t i;
+
+       /* Mark all thresholds as unconfigured */
+       for (i = 0; i < abm->num_bands; i++)
+               __bitmap_set(abm->threshold_undef,
+                            i * NFP_NET_MAX_RX_RINGS + alink->queue_base,
+                            alink->total_queues);
+
+       /* Clear offload marks */
+       radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) {
+               qdisc = nfp_abm_qdisc_tree_deref_slot(slot);
+               qdisc->offload_mark = false;
        }
-       if (opt->set.min != opt->set.max) {
-               nfp_warn(cpp, "RED offload failed - unsupported min/max parameters (p:%08x h:%08x)\n",
-                        opt->parent, opt->handle);
-               return false;
+
+       if (alink->root_qdisc)
+               nfp_abm_offload_compile_mq(alink, alink->root_qdisc);
+
+       /* Refresh offload status */
+       radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) {
+               qdisc = nfp_abm_qdisc_tree_deref_slot(slot);
+               if (!qdisc->offload_mark && qdisc->offloaded)
+                       nfp_abm_qdisc_offload_stop(alink, qdisc);
+               qdisc->offloaded = qdisc->offload_mark;
        }
-       if (opt->set.min > NFP_ABM_LVL_INFINITY) {
-               nfp_warn(cpp, "RED offload failed - threshold too large %d > %d (p:%08x h:%08x)\n",
-                        opt->set.min, NFP_ABM_LVL_INFINITY, opt->parent,
-                        opt->handle);
-               return false;
+
+       /* Reset the unconfigured thresholds */
+       for (i = 0; i < abm->num_thresholds; i++)
+               if (test_bit(i, abm->threshold_undef))
+                       __nfp_abm_ctrl_set_q_lvl(abm, i, NFP_ABM_LVL_INFINITY);
+
+       __nfp_abm_stats_update(alink, ktime_get());
+}
+
+static void
+nfp_abm_qdisc_clear_mq(struct net_device *netdev, struct nfp_abm_link *alink,
+                      struct nfp_qdisc *qdisc)
+{
+       struct radix_tree_iter iter;
+       unsigned int mq_refs = 0;
+       void __rcu **slot;
+
+       if (!qdisc->use_cnt)
+               return;
+       /* MQ doesn't notify well on destruction, we need special handling of
+        * MQ's children.
+        */
+       if (qdisc->type == NFP_QDISC_MQ &&
+           qdisc == alink->root_qdisc &&
+           netdev->reg_state == NETREG_UNREGISTERING)
+               return;
+
+       /* Count refs held by MQ instances and clear pointers */
+       radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) {
+               struct nfp_qdisc *mq = nfp_abm_qdisc_tree_deref_slot(slot);
+               unsigned int i;
+
+               if (mq->type != NFP_QDISC_MQ || mq->netdev != netdev)
+                       continue;
+               for (i = 0; i < mq->num_children; i++)
+                       if (mq->children[i] == qdisc) {
+                               mq->children[i] = NULL;
+                               mq_refs++;
+                       }
        }
 
-       return true;
+       WARN(qdisc->use_cnt != mq_refs, "non-zero qdisc use count: %d (- %d)\n",
+            qdisc->use_cnt, mq_refs);
+}
+
+static void
+nfp_abm_qdisc_free(struct net_device *netdev, struct nfp_abm_link *alink,
+                  struct nfp_qdisc *qdisc)
+{
+       struct nfp_port *port = nfp_port_from_netdev(netdev);
+
+       if (!qdisc)
+               return;
+       nfp_abm_qdisc_clear_mq(netdev, alink, qdisc);
+       WARN_ON(radix_tree_delete(&alink->qdiscs,
+                                 TC_H_MAJ(qdisc->handle)) != qdisc);
+
+       kfree(qdisc->children);
+       kfree(qdisc);
+
+       port->tc_offload_cnt--;
+}
+
+static struct nfp_qdisc *
+nfp_abm_qdisc_alloc(struct net_device *netdev, struct nfp_abm_link *alink,
+                   enum nfp_qdisc_type type, u32 parent_handle, u32 handle,
+                   unsigned int children)
+{
+       struct nfp_port *port = nfp_port_from_netdev(netdev);
+       struct nfp_qdisc *qdisc;
+       int err;
+
+       qdisc = kzalloc(sizeof(*qdisc), GFP_KERNEL);
+       if (!qdisc)
+               return NULL;
+
+       if (children) {
+               qdisc->children = kcalloc(children, sizeof(void *), GFP_KERNEL);
+               if (!qdisc->children)
+                       goto err_free_qdisc;
+       }
+
+       qdisc->netdev = netdev;
+       qdisc->type = type;
+       qdisc->parent_handle = parent_handle;
+       qdisc->handle = handle;
+       qdisc->num_children = children;
+
+       err = radix_tree_insert(&alink->qdiscs, TC_H_MAJ(qdisc->handle), qdisc);
+       if (err) {
+               nfp_err(alink->abm->app->cpp,
+                       "Qdisc insertion into radix tree failed: %d\n", err);
+               goto err_free_child_tbl;
+       }
+
+       port->tc_offload_cnt++;
+       return qdisc;
+
+err_free_child_tbl:
+       kfree(qdisc->children);
+err_free_qdisc:
+       kfree(qdisc);
+       return NULL;
+}
+
+static struct nfp_qdisc *
+nfp_abm_qdisc_find(struct nfp_abm_link *alink, u32 handle)
+{
+       return radix_tree_lookup(&alink->qdiscs, TC_H_MAJ(handle));
 }
 
 static int
-nfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink,
-                   struct tc_red_qopt_offload *opt)
+nfp_abm_qdisc_replace(struct net_device *netdev, struct nfp_abm_link *alink,
+                     enum nfp_qdisc_type type, u32 parent_handle, u32 handle,
+                     unsigned int children, struct nfp_qdisc **qdisc)
 {
-       bool existing;
-       int i, err;
+       *qdisc = nfp_abm_qdisc_find(alink, handle);
+       if (*qdisc) {
+               if (WARN_ON((*qdisc)->type != type))
+                       return -EINVAL;
+               return 1;
+       }
 
-       i = nfp_abm_red_find(alink, opt);
-       existing = i >= 0;
+       *qdisc = nfp_abm_qdisc_alloc(netdev, alink, type, parent_handle, handle,
+                                    children);
+       return *qdisc ? 0 : -ENOMEM;
+}
+
+static void
+nfp_abm_qdisc_destroy(struct net_device *netdev, struct nfp_abm_link *alink,
+                     u32 handle)
+{
+       struct nfp_qdisc *qdisc;
 
-       if (!nfp_abm_red_check_params(alink, opt)) {
-               err = -EINVAL;
-               goto err_destroy;
+       qdisc = nfp_abm_qdisc_find(alink, handle);
+       if (!qdisc)
+               return;
+
+       /* We don't get TC_SETUP_ROOT_QDISC w/ MQ when netdev is unregistered */
+       if (alink->root_qdisc == qdisc)
+               qdisc->use_cnt--;
+
+       nfp_abm_qdisc_unlink_children(qdisc, 0, qdisc->num_children);
+       nfp_abm_qdisc_free(netdev, alink, qdisc);
+
+       if (alink->root_qdisc == qdisc) {
+               alink->root_qdisc = NULL;
+               /* Only root change matters, other changes are acted upon on
+                * the graft notification.
+                */
+               nfp_abm_qdisc_offload_update(alink);
        }
+}
 
-       if (existing) {
-               if (alink->parent == TC_H_ROOT)
-                       err = nfp_abm_ctrl_set_all_q_lvls(alink, opt->set.min);
-               else
-                       err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min);
-               if (err)
-                       goto err_destroy;
+static int
+nfp_abm_qdisc_graft(struct nfp_abm_link *alink, u32 handle, u32 child_handle,
+                   unsigned int id)
+{
+       struct nfp_qdisc *parent, *child;
+
+       parent = nfp_abm_qdisc_find(alink, handle);
+       if (!parent)
                return 0;
-       }
 
-       if (opt->parent == TC_H_ROOT) {
-               i = 0;
-               err = __nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 1,
-                                          opt->set.min);
-       } else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent)) {
-               i = TC_H_MIN(opt->parent) - 1;
-               err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min);
-       } else {
+       if (WARN(id >= parent->num_children,
+                "graft child out of bound %d >= %d\n",
+                id, parent->num_children))
                return -EINVAL;
-       }
-       /* Set the handle to try full clean up, in case IO failed */
-       alink->qdiscs[i].handle = opt->handle;
-       if (err)
-               goto err_destroy;
 
-       if (opt->parent == TC_H_ROOT)
-               err = nfp_abm_ctrl_read_stats(alink, &alink->qdiscs[i].stats);
-       else
-               err = nfp_abm_ctrl_read_q_stats(alink, i,
-                                               &alink->qdiscs[i].stats);
-       if (err)
-               goto err_destroy;
-
-       if (opt->parent == TC_H_ROOT)
-               err = nfp_abm_ctrl_read_xstats(alink,
-                                              &alink->qdiscs[i].xstats);
+       nfp_abm_qdisc_unlink_children(parent, id, id + 1);
+
+       child = nfp_abm_qdisc_find(alink, child_handle);
+       if (child)
+               child->use_cnt++;
        else
-               err = nfp_abm_ctrl_read_q_xstats(alink, i,
-                                                &alink->qdiscs[i].xstats);
-       if (err)
-               goto err_destroy;
+               child = NFP_QDISC_UNTRACKED;
+       parent->children[id] = child;
 
-       alink->qdiscs[i].stats.backlog_pkts = 0;
-       alink->qdiscs[i].stats.backlog_bytes = 0;
+       nfp_abm_qdisc_offload_update(alink);
 
        return 0;
-err_destroy:
-       /* If the qdisc keeps on living, but we can't offload undo changes */
-       if (existing) {
-               opt->set.qstats->qlen -= alink->qdiscs[i].stats.backlog_pkts;
-               opt->set.qstats->backlog -=
-                       alink->qdiscs[i].stats.backlog_bytes;
-       }
-       nfp_abm_red_destroy(netdev, alink, opt->handle);
-
-       return err;
 }
 
 static void
-nfp_abm_update_stats(struct nfp_alink_stats *new, struct nfp_alink_stats *old,
-                    struct tc_qopt_offload_stats *stats)
+nfp_abm_stats_calculate(struct nfp_alink_stats *new,
+                       struct nfp_alink_stats *old,
+                       struct gnet_stats_basic_packed *bstats,
+                       struct gnet_stats_queue *qstats)
 {
-       _bstats_update(stats->bstats, new->tx_bytes - old->tx_bytes,
+       _bstats_update(bstats, new->tx_bytes - old->tx_bytes,
                       new->tx_pkts - old->tx_pkts);
-       stats->qstats->qlen += new->backlog_pkts - old->backlog_pkts;
-       stats->qstats->backlog += new->backlog_bytes - old->backlog_bytes;
-       stats->qstats->overlimits += new->overlimits - old->overlimits;
-       stats->qstats->drops += new->drops - old->drops;
+       qstats->qlen += new->backlog_pkts - old->backlog_pkts;
+       qstats->backlog += new->backlog_bytes - old->backlog_bytes;
+       qstats->overlimits += new->overlimits - old->overlimits;
+       qstats->drops += new->drops - old->drops;
+}
+
+static void
+nfp_abm_stats_red_calculate(struct nfp_alink_xstats *new,
+                           struct nfp_alink_xstats *old,
+                           struct red_stats *stats)
+{
+       stats->forced_mark += new->ecn_marked - old->ecn_marked;
+       stats->pdrop += new->pdrop - old->pdrop;
 }
 
 static int
-nfp_abm_red_stats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
+nfp_abm_gred_stats(struct nfp_abm_link *alink, u32 handle,
+                  struct tc_gred_qopt_offload_stats *stats)
 {
-       struct nfp_alink_stats *prev_stats;
-       struct nfp_alink_stats stats;
-       int i, err;
+       struct nfp_qdisc *qdisc;
+       unsigned int i;
 
-       i = nfp_abm_red_find(alink, opt);
-       if (i < 0)
-               return i;
-       prev_stats = &alink->qdiscs[i].stats;
+       nfp_abm_stats_update(alink);
 
-       if (alink->parent == TC_H_ROOT)
-               err = nfp_abm_ctrl_read_stats(alink, &stats);
-       else
-               err = nfp_abm_ctrl_read_q_stats(alink, i, &stats);
-       if (err)
-               return err;
+       qdisc = nfp_abm_qdisc_find(alink, handle);
+       if (!qdisc)
+               return -EOPNOTSUPP;
+       /* If the qdisc offload has stopped we may need to adjust the backlog
+        * counters back so carry on even if qdisc is not currently offloaded.
+        */
 
-       nfp_abm_update_stats(&stats, prev_stats, &opt->stats);
+       for (i = 0; i < qdisc->red.num_bands; i++) {
+               if (!stats->xstats[i])
+                       continue;
+
+               nfp_abm_stats_calculate(&qdisc->red.band[i].stats,
+                                       &qdisc->red.band[i].prev_stats,
+                                       &stats->bstats[i], &stats->qstats[i]);
+               qdisc->red.band[i].prev_stats = qdisc->red.band[i].stats;
 
-       *prev_stats = stats;
+               nfp_abm_stats_red_calculate(&qdisc->red.band[i].xstats,
+                                           &qdisc->red.band[i].prev_xstats,
+                                           stats->xstats[i]);
+               qdisc->red.band[i].prev_xstats = qdisc->red.band[i].xstats;
+       }
+
+       return qdisc->offloaded ? 0 : -EOPNOTSUPP;
+}
+
+static bool
+nfp_abm_gred_check_params(struct nfp_abm_link *alink,
+                         struct tc_gred_qopt_offload *opt)
+{
+       struct nfp_cpp *cpp = alink->abm->app->cpp;
+       struct nfp_abm *abm = alink->abm;
+       unsigned int i;
+
+       if (opt->set.grio_on || opt->set.wred_on) {
+               nfp_warn(cpp, "GRED offload failed - GRIO and WRED not supported (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.dp_def != alink->def_band) {
+               nfp_warn(cpp, "GRED offload failed - default band must be %d (p:%08x h:%08x)\n",
+                        alink->def_band, opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.dp_cnt != abm->num_bands) {
+               nfp_warn(cpp, "GRED offload failed - band count must be %d (p:%08x h:%08x)\n",
+                        abm->num_bands, opt->parent, opt->handle);
+               return false;
+       }
+
+       for (i = 0; i < abm->num_bands; i++) {
+               struct tc_gred_vq_qopt_offload_params *band = &opt->set.tab[i];
+
+               if (!band->present)
+                       return false;
+               if (!band->is_ecn && !nfp_abm_has_drop(abm)) {
+                       nfp_warn(cpp, "GRED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x vq:%d)\n",
+                                opt->parent, opt->handle, i);
+                       return false;
+               }
+               if (band->is_ecn && !nfp_abm_has_mark(abm)) {
+                       nfp_warn(cpp, "GRED offload failed - ECN marking not supported (p:%08x h:%08x vq:%d)\n",
+                                opt->parent, opt->handle, i);
+                       return false;
+               }
+               if (band->is_harddrop) {
+                       nfp_warn(cpp, "GRED offload failed - harddrop is not supported (p:%08x h:%08x vq:%d)\n",
+                                opt->parent, opt->handle, i);
+                       return false;
+               }
+               if (band->min != band->max) {
+                       nfp_warn(cpp, "GRED offload failed - threshold mismatch (p:%08x h:%08x vq:%d)\n",
+                                opt->parent, opt->handle, i);
+                       return false;
+               }
+               if (band->min > S32_MAX) {
+                       nfp_warn(cpp, "GRED offload failed - threshold too large %d > %d (p:%08x h:%08x vq:%d)\n",
+                                band->min, S32_MAX, opt->parent, opt->handle,
+                                i);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static int
+nfp_abm_gred_replace(struct net_device *netdev, struct nfp_abm_link *alink,
+                    struct tc_gred_qopt_offload *opt)
+{
+       struct nfp_qdisc *qdisc;
+       unsigned int i;
+       int ret;
+
+       ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_GRED, opt->parent,
+                                   opt->handle, 0, &qdisc);
+       if (ret < 0)
+               return ret;
+
+       qdisc->params_ok = nfp_abm_gred_check_params(alink, opt);
+       if (qdisc->params_ok) {
+               qdisc->red.num_bands = opt->set.dp_cnt;
+               for (i = 0; i < qdisc->red.num_bands; i++) {
+                       qdisc->red.band[i].ecn = opt->set.tab[i].is_ecn;
+                       qdisc->red.band[i].threshold = opt->set.tab[i].min;
+               }
+       }
+
+       if (qdisc->use_cnt)
+               nfp_abm_qdisc_offload_update(alink);
 
        return 0;
 }
 
+int nfp_abm_setup_tc_gred(struct net_device *netdev, struct nfp_abm_link *alink,
+                         struct tc_gred_qopt_offload *opt)
+{
+       switch (opt->command) {
+       case TC_GRED_REPLACE:
+               return nfp_abm_gred_replace(netdev, alink, opt);
+       case TC_GRED_DESTROY:
+               nfp_abm_qdisc_destroy(netdev, alink, opt->handle);
+               return 0;
+       case TC_GRED_STATS:
+               return nfp_abm_gred_stats(alink, opt->handle, &opt->stats);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static int
 nfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
 {
-       struct nfp_alink_xstats *prev_xstats;
-       struct nfp_alink_xstats xstats;
-       int i, err;
+       struct nfp_qdisc *qdisc;
 
-       i = nfp_abm_red_find(alink, opt);
-       if (i < 0)
-               return i;
-       prev_xstats = &alink->qdiscs[i].xstats;
+       nfp_abm_stats_update(alink);
 
-       if (alink->parent == TC_H_ROOT)
-               err = nfp_abm_ctrl_read_xstats(alink, &xstats);
-       else
-               err = nfp_abm_ctrl_read_q_xstats(alink, i, &xstats);
-       if (err)
-               return err;
+       qdisc = nfp_abm_qdisc_find(alink, opt->handle);
+       if (!qdisc || !qdisc->offloaded)
+               return -EOPNOTSUPP;
+
+       nfp_abm_stats_red_calculate(&qdisc->red.band[0].xstats,
+                                   &qdisc->red.band[0].prev_xstats,
+                                   opt->xstats);
+       qdisc->red.band[0].prev_xstats = qdisc->red.band[0].xstats;
+       return 0;
+}
+
+static int
+nfp_abm_red_stats(struct nfp_abm_link *alink, u32 handle,
+                 struct tc_qopt_offload_stats *stats)
+{
+       struct nfp_qdisc *qdisc;
+
+       nfp_abm_stats_update(alink);
+
+       qdisc = nfp_abm_qdisc_find(alink, handle);
+       if (!qdisc)
+               return -EOPNOTSUPP;
+       /* If the qdisc offload has stopped we may need to adjust the backlog
+        * counters back so carry on even if qdisc is not currently offloaded.
+        */
 
-       opt->xstats->forced_mark += xstats.ecn_marked - prev_xstats->ecn_marked;
-       opt->xstats->pdrop += xstats.pdrop - prev_xstats->pdrop;
+       nfp_abm_stats_calculate(&qdisc->red.band[0].stats,
+                               &qdisc->red.band[0].prev_stats,
+                               stats->bstats, stats->qstats);
+       qdisc->red.band[0].prev_stats = qdisc->red.band[0].stats;
 
-       *prev_xstats = xstats;
+       return qdisc->offloaded ? 0 : -EOPNOTSUPP;
+}
+
+static bool
+nfp_abm_red_check_params(struct nfp_abm_link *alink,
+                        struct tc_red_qopt_offload *opt)
+{
+       struct nfp_cpp *cpp = alink->abm->app->cpp;
+       struct nfp_abm *abm = alink->abm;
+
+       if (!opt->set.is_ecn && !nfp_abm_has_drop(abm)) {
+               nfp_warn(cpp, "RED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.is_ecn && !nfp_abm_has_mark(abm)) {
+               nfp_warn(cpp, "RED offload failed - ECN marking not supported (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.is_harddrop) {
+               nfp_warn(cpp, "RED offload failed - harddrop is not supported (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.min != opt->set.max) {
+               nfp_warn(cpp, "RED offload failed - unsupported min/max parameters (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.min > NFP_ABM_LVL_INFINITY) {
+               nfp_warn(cpp, "RED offload failed - threshold too large %d > %d (p:%08x h:%08x)\n",
+                        opt->set.min, NFP_ABM_LVL_INFINITY, opt->parent,
+                        opt->handle);
+               return false;
+       }
+
+       return true;
+}
+
+static int
+nfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink,
+                   struct tc_red_qopt_offload *opt)
+{
+       struct nfp_qdisc *qdisc;
+       int ret;
+
+       ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_RED, opt->parent,
+                                   opt->handle, 1, &qdisc);
+       if (ret < 0)
+               return ret;
+
+       /* If limit != 0 child gets reset */
+       if (opt->set.limit) {
+               if (nfp_abm_qdisc_child_valid(qdisc, 0))
+                       qdisc->children[0]->use_cnt--;
+               qdisc->children[0] = NULL;
+       } else {
+               /* Qdisc was just allocated without a limit will use noop_qdisc,
+                * i.e. a block hole.
+                */
+               if (!ret)
+                       qdisc->children[0] = NFP_QDISC_UNTRACKED;
+       }
+
+       qdisc->params_ok = nfp_abm_red_check_params(alink, opt);
+       if (qdisc->params_ok) {
+               qdisc->red.num_bands = 1;
+               qdisc->red.band[0].ecn = opt->set.is_ecn;
+               qdisc->red.band[0].threshold = opt->set.min;
+       }
+
+       if (qdisc->use_cnt == 1)
+               nfp_abm_qdisc_offload_update(alink);
 
        return 0;
 }
@@ -248,37 +739,78 @@ int nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
        case TC_RED_REPLACE:
                return nfp_abm_red_replace(netdev, alink, opt);
        case TC_RED_DESTROY:
-               nfp_abm_red_destroy(netdev, alink, opt->handle);
+               nfp_abm_qdisc_destroy(netdev, alink, opt->handle);
                return 0;
        case TC_RED_STATS:
-               return nfp_abm_red_stats(alink, opt);
+               return nfp_abm_red_stats(alink, opt->handle, &opt->stats);
        case TC_RED_XSTATS:
                return nfp_abm_red_xstats(alink, opt);
+       case TC_RED_GRAFT:
+               return nfp_abm_qdisc_graft(alink, opt->handle,
+                                          opt->child_handle, 0);
        default:
                return -EOPNOTSUPP;
        }
 }
 
 static int
-nfp_abm_mq_stats(struct nfp_abm_link *alink, struct tc_mq_qopt_offload *opt)
+nfp_abm_mq_create(struct net_device *netdev, struct nfp_abm_link *alink,
+                 struct tc_mq_qopt_offload *opt)
 {
-       struct nfp_alink_stats stats;
-       unsigned int i;
-       int err;
+       struct nfp_qdisc *qdisc;
+       int ret;
 
-       for (i = 0; i < alink->num_qdiscs; i++) {
-               if (alink->qdiscs[i].handle == TC_H_UNSPEC)
-                       continue;
+       ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_MQ,
+                                   TC_H_ROOT, opt->handle, alink->total_queues,
+                                   &qdisc);
+       if (ret < 0)
+               return ret;
 
-               err = nfp_abm_ctrl_read_q_stats(alink, i, &stats);
-               if (err)
-                       return err;
+       qdisc->params_ok = true;
+       qdisc->offloaded = true;
+       nfp_abm_qdisc_offload_update(alink);
+       return 0;
+}
 
-               nfp_abm_update_stats(&stats, &alink->qdiscs[i].stats,
-                                    &opt->stats);
+static int
+nfp_abm_mq_stats(struct nfp_abm_link *alink, u32 handle,
+                struct tc_qopt_offload_stats *stats)
+{
+       struct nfp_qdisc *qdisc, *red;
+       unsigned int i, j;
+
+       qdisc = nfp_abm_qdisc_find(alink, handle);
+       if (!qdisc)
+               return -EOPNOTSUPP;
+
+       nfp_abm_stats_update(alink);
+
+       /* MQ stats are summed over the children in the core, so we need
+        * to add up the unreported child values.
+        */
+       memset(&qdisc->mq.stats, 0, sizeof(qdisc->mq.stats));
+       memset(&qdisc->mq.prev_stats, 0, sizeof(qdisc->mq.prev_stats));
+
+       for (i = 0; i < qdisc->num_children; i++) {
+               if (!nfp_abm_qdisc_child_valid(qdisc, i))
+                       continue;
+
+               if (!nfp_abm_qdisc_is_red(qdisc->children[i]))
+                       continue;
+               red = qdisc->children[i];
+
+               for (j = 0; j < red->red.num_bands; j++) {
+                       nfp_abm_stats_propagate(&qdisc->mq.stats,
+                                               &red->red.band[j].stats);
+                       nfp_abm_stats_propagate(&qdisc->mq.prev_stats,
+                                               &red->red.band[j].prev_stats);
+               }
        }
 
-       return 0;
+       nfp_abm_stats_calculate(&qdisc->mq.stats, &qdisc->mq.prev_stats,
+                               stats->bstats, stats->qstats);
+
+       return qdisc->offloaded ? 0 : -EOPNOTSUPP;
 }
 
 int nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
@@ -286,16 +818,33 @@ int nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
 {
        switch (opt->command) {
        case TC_MQ_CREATE:
-               nfp_abm_reset_root(netdev, alink, opt->handle,
-                                  alink->total_queues);
-               return 0;
+               return nfp_abm_mq_create(netdev, alink, opt);
        case TC_MQ_DESTROY:
-               if (opt->handle == alink->parent)
-                       nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0);
+               nfp_abm_qdisc_destroy(netdev, alink, opt->handle);
                return 0;
        case TC_MQ_STATS:
-               return nfp_abm_mq_stats(alink, opt);
+               return nfp_abm_mq_stats(alink, opt->handle, &opt->stats);
+       case TC_MQ_GRAFT:
+               return nfp_abm_qdisc_graft(alink, opt->handle,
+                                          opt->graft_params.child_handle,
+                                          opt->graft_params.queue);
        default:
                return -EOPNOTSUPP;
        }
 }
+
+int nfp_abm_setup_root(struct net_device *netdev, struct nfp_abm_link *alink,
+                      struct tc_root_qopt_offload *opt)
+{
+       if (opt->ingress)
+               return -EOPNOTSUPP;
+       if (alink->root_qdisc)
+               alink->root_qdisc->use_cnt--;
+       alink->root_qdisc = nfp_abm_qdisc_find(alink, opt->handle);
+       if (alink->root_qdisc)
+               alink->root_qdisc->use_cnt++;
+
+       nfp_abm_qdisc_offload_update(alink);
+
+       return 0;
+}
index 97d33bb..e23ca90 100644 (file)
@@ -2382,6 +2382,49 @@ static int neg_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
        return 0;
 }
 
+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);
+       wrp_immed(nfp_prog, reg_both(dst + 1), 0);
+
+       return 0;
+}
+
+static int ashr_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 __ashr_imm(nfp_prog, dst, umin);
+
+       src = insn->src_reg * 2;
+       /* NOTE: the first insn will set both indirect shift amount (source A)
+        * and signedness bit (MSB of result).
+        */
+       emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_b(dst));
+       emit_shf_indir(nfp_prog, reg_both(dst), reg_none(), SHF_OP_ASHR,
+                      reg_b(dst), SHF_SC_R_SHF);
+       wrp_immed(nfp_prog, reg_both(dst + 1), 0);
+
+       return 0;
+}
+
+static int ashr_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 __ashr_imm(nfp_prog, dst, insn->imm);
+}
+
 static int shl_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
        const struct bpf_insn *insn = &meta->insn;
@@ -3009,26 +3052,19 @@ static int jset_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
        const struct bpf_insn *insn = &meta->insn;
        u64 imm = insn->imm; /* sign extend */
+       u8 dst_gpr = insn->dst_reg * 2;
        swreg tmp_reg;
 
-       if (!imm) {
-               meta->skip = true;
-               return 0;
-       }
-
-       if (imm & ~0U) {
-               tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog));
-               emit_alu(nfp_prog, reg_none(),
-                        reg_a(insn->dst_reg * 2), ALU_OP_AND, tmp_reg);
-               emit_br(nfp_prog, BR_BNE, insn->off, 0);
-       }
-
-       if (imm >> 32) {
-               tmp_reg = ur_load_imm_any(nfp_prog, imm >> 32, imm_b(nfp_prog));
+       tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog));
+       emit_alu(nfp_prog, imm_b(nfp_prog),
+                reg_a(dst_gpr), ALU_OP_AND, tmp_reg);
+       /* Upper word of the mask can only be 0 or ~0 from sign extension,
+        * so either ignore it or OR the whole thing in.
+        */
+       if (imm >> 32)
                emit_alu(nfp_prog, reg_none(),
-                        reg_a(insn->dst_reg * 2 + 1), ALU_OP_AND, tmp_reg);
-               emit_br(nfp_prog, BR_BNE, insn->off, 0);
-       }
+                        reg_a(dst_gpr + 1), ALU_OP_OR, imm_b(nfp_prog));
+       emit_br(nfp_prog, BR_BNE, insn->off, 0);
 
        return 0;
 }
@@ -3286,6 +3322,8 @@ static const instr_cb_t instr_cb[256] = {
        [BPF_ALU | BPF_DIV | BPF_K] =   div_imm,
        [BPF_ALU | BPF_NEG] =           neg_reg,
        [BPF_ALU | BPF_LSH | BPF_K] =   shl_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,
        [BPF_LD | BPF_IMM | BPF_DW] =   imm_ld8,
        [BPF_LD | BPF_ABS | BPF_B] =    data_ld1,
index 6243af0..dccae03 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();
+       bpf->bpf_dev = bpf_offload_dev_create(&nfp_bpf_dev_ops);
        err = PTR_ERR_OR_ZERO(bpf->bpf_dev);
        if (err)
                goto err_free_neutral_maps;
index 7f591d7..9412779 100644 (file)
@@ -509,7 +509,11 @@ void nfp_bpf_jit_prepare(struct nfp_prog *nfp_prog, unsigned int cnt);
 int nfp_bpf_jit(struct nfp_prog *prog);
 bool nfp_bpf_supported_opcode(u8 code);
 
-extern const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops;
+int nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx,
+                   int prev_insn_idx);
+int nfp_bpf_finalize(struct bpf_verifier_env *env);
+
+extern const struct bpf_prog_offload_ops nfp_bpf_dev_ops;
 
 struct netdev_bpf;
 struct nfp_app;
index ba8ceed..f028385 100644 (file)
@@ -33,9 +33,6 @@ nfp_map_ptr_record(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog,
        struct nfp_bpf_neutral_map *record;
        int err;
 
-       /* Map record paths are entered via ndo, update side is protected. */
-       ASSERT_RTNL();
-
        /* Reuse path - other offloaded program is already tracking this map. */
        record = rhashtable_lookup_fast(&bpf->maps_neutral, &map->id,
                                        nfp_bpf_maps_neutral_params);
@@ -84,8 +81,6 @@ nfp_map_ptrs_forget(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog)
        bool freed = false;
        int i;
 
-       ASSERT_RTNL();
-
        for (i = 0; i < nfp_prog->map_records_cnt; i++) {
                if (--nfp_prog->map_records[i]->count) {
                        nfp_prog->map_records[i] = NULL;
@@ -187,11 +182,10 @@ static void nfp_prog_free(struct nfp_prog *nfp_prog)
        kfree(nfp_prog);
 }
 
-static int
-nfp_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn,
-                     struct netdev_bpf *bpf)
+static int nfp_bpf_verifier_prep(struct bpf_prog *prog)
 {
-       struct bpf_prog *prog = bpf->verifier.prog;
+       struct nfp_net *nn = netdev_priv(prog->aux->offload->netdev);
+       struct nfp_app *app = nn->app;
        struct nfp_prog *nfp_prog;
        int ret;
 
@@ -209,7 +203,6 @@ nfp_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn,
                goto err_free;
 
        nfp_prog->verifier_meta = nfp_prog_first_meta(nfp_prog);
-       bpf->verifier.ops = &nfp_bpf_analyzer_ops;
 
        return 0;
 
@@ -219,8 +212,9 @@ err_free:
        return ret;
 }
 
-static int nfp_bpf_translate(struct nfp_net *nn, struct bpf_prog *prog)
+static int nfp_bpf_translate(struct bpf_prog *prog)
 {
+       struct nfp_net *nn = netdev_priv(prog->aux->offload->netdev);
        struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv;
        unsigned int max_instr;
        int err;
@@ -242,15 +236,13 @@ static int nfp_bpf_translate(struct nfp_net *nn, struct bpf_prog *prog)
        return nfp_map_ptrs_record(nfp_prog->bpf, nfp_prog, prog);
 }
 
-static int nfp_bpf_destroy(struct nfp_net *nn, struct bpf_prog *prog)
+static void nfp_bpf_destroy(struct bpf_prog *prog)
 {
        struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv;
 
        kvfree(nfp_prog->prog);
        nfp_map_ptrs_forget(nfp_prog->bpf, nfp_prog);
        nfp_prog_free(nfp_prog);
-
-       return 0;
 }
 
 /* Atomic engine requires values to be in big endian, we need to byte swap
@@ -422,12 +414,6 @@ nfp_bpf_map_free(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap)
 int nfp_ndo_bpf(struct nfp_app *app, struct nfp_net *nn, struct netdev_bpf *bpf)
 {
        switch (bpf->command) {
-       case BPF_OFFLOAD_VERIFIER_PREP:
-               return nfp_bpf_verifier_prep(app, nn, bpf);
-       case BPF_OFFLOAD_TRANSLATE:
-               return nfp_bpf_translate(nn, bpf->offload.prog);
-       case BPF_OFFLOAD_DESTROY:
-               return nfp_bpf_destroy(nn, bpf->offload.prog);
        case BPF_OFFLOAD_MAP_ALLOC:
                return nfp_bpf_map_alloc(app->priv, bpf->offmap);
        case BPF_OFFLOAD_MAP_FREE:
@@ -489,14 +475,15 @@ nfp_net_bpf_load(struct nfp_net *nn, struct bpf_prog *prog,
                 struct netlink_ext_ack *extack)
 {
        struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv;
-       unsigned int max_mtu, max_stack, max_prog_len;
+       unsigned int fw_mtu, pkt_off, max_stack, max_prog_len;
        dma_addr_t dma_addr;
        void *img;
        int err;
 
-       max_mtu = nn_readb(nn, NFP_NET_CFG_BPF_INL_MTU) * 64 - 32;
-       if (max_mtu < nn->dp.netdev->mtu) {
-               NL_SET_ERR_MSG_MOD(extack, "BPF offload not supported with MTU larger than HW packet split boundary");
+       fw_mtu = nn_readb(nn, NFP_NET_CFG_BPF_INL_MTU) * 64 - 32;
+       pkt_off = min(prog->aux->max_pkt_offset, nn->dp.netdev->mtu);
+       if (fw_mtu < pkt_off) {
+               NL_SET_ERR_MSG_MOD(extack, "BPF offload not supported with potential packet access beyond HW packet split boundary");
                return -EOPNOTSUPP;
        }
 
@@ -600,3 +587,11 @@ int nfp_net_bpf_offload(struct nfp_net *nn, struct bpf_prog *prog,
 
        return 0;
 }
+
+const struct bpf_prog_offload_ops nfp_bpf_dev_ops = {
+       .insn_hook      = nfp_verify_insn,
+       .finalize       = nfp_bpf_finalize,
+       .prepare        = nfp_bpf_verifier_prep,
+       .translate      = nfp_bpf_translate,
+       .destroy        = nfp_bpf_destroy,
+};
index 99f977b..337bb86 100644 (file)
@@ -623,8 +623,8 @@ nfp_bpf_check_alu(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
        return 0;
 }
 
-static int
-nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
+int nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx,
+                   int prev_insn_idx)
 {
        struct nfp_prog *nfp_prog = env->prog->aux->offload->dev_priv;
        struct nfp_insn_meta *meta = nfp_prog->verifier_meta;
@@ -745,7 +745,7 @@ continue_subprog:
        goto continue_subprog;
 }
 
-static int nfp_bpf_finalize(struct bpf_verifier_env *env)
+int nfp_bpf_finalize(struct bpf_verifier_env *env)
 {
        struct bpf_subprog_info *info;
        struct nfp_prog *nfp_prog;
@@ -788,8 +788,3 @@ static int nfp_bpf_finalize(struct bpf_verifier_env *env)
 
        return 0;
 }
-
-const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops = {
-       .insn_hook      = nfp_verify_insn,
-       .finalize       = nfp_bpf_finalize,
-};
index 545d941..2cdbf29 100644 (file)
@@ -343,13 +343,29 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
                    !(tcp_flags & (TCPHDR_FIN | TCPHDR_SYN | TCPHDR_RST)))
                        return -EOPNOTSUPP;
 
-               /* We need to store TCP flags in the IPv4 key space, thus
-                * we need to ensure we include a IPv4 key layer if we have
-                * not done so already.
+               /* We need to store TCP flags in the either the IPv4 or IPv6 key
+                * space, thus we need to ensure we include a IPv4/IPv6 key
+                * layer if we have not done so already.
                 */
-               if (!(key_layer & NFP_FLOWER_LAYER_IPV4)) {
-                       key_layer |= NFP_FLOWER_LAYER_IPV4;
-                       key_size += sizeof(struct nfp_flower_ipv4);
+               if (!key_basic)
+                       return -EOPNOTSUPP;
+
+               if (!(key_layer & NFP_FLOWER_LAYER_IPV4) &&
+                   !(key_layer & NFP_FLOWER_LAYER_IPV6)) {
+                       switch (key_basic->n_proto) {
+                       case cpu_to_be16(ETH_P_IP):
+                               key_layer |= NFP_FLOWER_LAYER_IPV4;
+                               key_size += sizeof(struct nfp_flower_ipv4);
+                               break;
+
+                       case cpu_to_be16(ETH_P_IPV6):
+                               key_layer |= NFP_FLOWER_LAYER_IPV6;
+                               key_size += sizeof(struct nfp_flower_ipv6);
+                               break;
+
+                       default:
+                               return -EOPNOTSUPP;
+                       }
                }
        }
 
@@ -460,16 +476,16 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,
        if (err)
                goto err_destroy_flow;
 
-       err = nfp_flower_xmit_flow(app, flow_pay,
-                                  NFP_FLOWER_CMSG_TYPE_FLOW_ADD);
-       if (err)
-               goto err_destroy_flow;
-
        flow_pay->tc_flower_cookie = flow->cookie;
        err = rhashtable_insert_fast(&priv->flow_table, &flow_pay->fl_node,
                                     nfp_flower_table_params);
        if (err)
-               goto err_destroy_flow;
+               goto err_release_metadata;
+
+       err = nfp_flower_xmit_flow(app, flow_pay,
+                                  NFP_FLOWER_CMSG_TYPE_FLOW_ADD);
+       if (err)
+               goto err_remove_rhash;
 
        if (port)
                port->tc_offload_cnt++;
@@ -479,6 +495,12 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,
 
        return 0;
 
+err_remove_rhash:
+       WARN_ON_ONCE(rhashtable_remove_fast(&priv->flow_table,
+                                           &flow_pay->fl_node,
+                                           nfp_flower_table_params));
+err_release_metadata:
+       nfp_modify_flow_metadata(app, flow_pay);
 err_destroy_flow:
        kfree(flow_pay->action_data);
        kfree(flow_pay->mask_data);
@@ -712,7 +734,7 @@ nfp_flower_setup_indr_tc_block(struct net_device *netdev, struct nfp_app *app,
 
                err = tcf_block_cb_register(f->block,
                                            nfp_flower_setup_indr_block_cb,
-                                           netdev, cb_priv, f->extack);
+                                           cb_priv, cb_priv, f->extack);
                if (err) {
                        list_del(&cb_priv->list);
                        kfree(cb_priv);
@@ -720,13 +742,15 @@ nfp_flower_setup_indr_tc_block(struct net_device *netdev, struct nfp_app *app,
 
                return err;
        case TC_BLOCK_UNBIND:
-               tcf_block_cb_unregister(f->block,
-                                       nfp_flower_setup_indr_block_cb, netdev);
                cb_priv = nfp_flower_indr_block_cb_priv_lookup(app, netdev);
-               if (cb_priv) {
-                       list_del(&cb_priv->list);
-                       kfree(cb_priv);
-               }
+               if (!cb_priv)
+                       return -ENOENT;
+
+               tcf_block_cb_unregister(f->block,
+                                       nfp_flower_setup_indr_block_cb,
+                                       cb_priv);
+               list_del(&cb_priv->list);
+               kfree(cb_priv);
 
                return 0;
        default:
@@ -760,15 +784,14 @@ int nfp_flower_reg_indir_block_handler(struct nfp_app *app,
        if (event == NETDEV_REGISTER) {
                err = __tc_indr_block_cb_register(netdev, app,
                                                  nfp_flower_indr_setup_tc_cb,
-                                                 netdev);
+                                                 app);
                if (err)
                        nfp_flower_cmsg_warn(app,
                                             "Indirect block reg failed - %s\n",
                                             netdev->name);
        } else if (event == NETDEV_UNREGISTER) {
                __tc_indr_block_cb_unregister(netdev,
-                                             nfp_flower_indr_setup_tc_cb,
-                                             netdev);
+                                             nfp_flower_indr_setup_tc_cb, app);
        }
 
        return NOTIFY_OK;
index 4a1b8f7..3a97328 100644 (file)
@@ -131,11 +131,45 @@ nfp_app_reprs_set(struct nfp_app *app, enum nfp_repr_type type,
        struct nfp_reprs *old;
 
        old = nfp_reprs_get_locked(app, type);
+       rtnl_lock();
        rcu_assign_pointer(app->reprs[type], reprs);
+       rtnl_unlock();
 
        return old;
 }
 
+static void
+nfp_app_netdev_feat_change(struct nfp_app *app, struct net_device *netdev)
+{
+       struct nfp_net *nn;
+       unsigned int type;
+
+       if (!nfp_netdev_is_nfp_net(netdev))
+               return;
+       nn = netdev_priv(netdev);
+       if (nn->app != app)
+               return;
+
+       for (type = 0; type < __NFP_REPR_TYPE_MAX; type++) {
+               struct nfp_reprs *reprs;
+               unsigned int i;
+
+               reprs = rtnl_dereference(app->reprs[type]);
+               if (!reprs)
+                       continue;
+
+               for (i = 0; i < reprs->num_reprs; i++) {
+                       struct net_device *repr;
+
+                       repr = rtnl_dereference(reprs->reprs[i]);
+                       if (!repr)
+                               continue;
+
+                       nfp_repr_transfer_features(repr, netdev);
+               }
+       }
+}
+
 static int
 nfp_app_netdev_event(struct notifier_block *nb, unsigned long event, void *ptr)
 {
@@ -145,6 +179,14 @@ nfp_app_netdev_event(struct notifier_block *nb, unsigned long event, void *ptr)
        netdev = netdev_notifier_info_to_dev(ptr);
        app = container_of(nb, struct nfp_app, netdev_nb);
 
+       /* Handle events common code is interested in */
+       switch (event) {
+       case NETDEV_FEAT_CHANGE:
+               nfp_app_netdev_feat_change(app, netdev);
+               break;
+       }
+
+       /* Call offload specific handlers */
        if (app->type->netdev_event)
                return app->type->netdev_event(app, netdev, event, ptr);
        return NOTIFY_DONE;
index dda02fe..be37c2d 100644 (file)
@@ -158,6 +158,7 @@ struct nfp_net_tx_desc {
                        __le16 data_len; /* Length of frame + meta data */
                } __packed;
                __le32 vals[4];
+               __le64 vals8[2];
        };
 };
 
@@ -543,6 +544,7 @@ struct nfp_net_dp {
  * @reconfig_timer_active:  Timer for reading reconfiguration results is pending
  * @reconfig_sync_present:  Some thread is performing synchronous reconfig
  * @reconfig_timer:    Timer for async reading of reconfig results
+ * @reconfig_in_progress_update:       Update FW is processing now (debug only)
  * @link_up:            Is the link up?
  * @link_status_lock:  Protects @link_* and ensures atomicity with BAR reading
  * @rx_coalesce_usecs:      RX interrupt moderation usecs delay parameter
@@ -611,6 +613,7 @@ struct nfp_net {
        bool reconfig_timer_active;
        bool reconfig_sync_present;
        struct timer_list reconfig_timer;
+       u32 reconfig_in_progress_update;
 
        u32 rx_coalesce_usecs;
        u32 rx_coalesce_max_frames;
@@ -868,6 +871,7 @@ unsigned int nfp_net_rss_key_sz(struct nfp_net *nn);
 void nfp_net_rss_write_itbl(struct nfp_net *nn);
 void nfp_net_rss_write_key(struct nfp_net *nn);
 void nfp_net_coalesce_write_cfg(struct nfp_net *nn);
+int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd);
 
 unsigned int
 nfp_net_irqs_alloc(struct pci_dev *pdev, struct msix_entry *irq_entries,
index a0343f2..e97636d 100644 (file)
@@ -101,6 +101,7 @@ static void nfp_net_reconfig_start(struct nfp_net *nn, u32 update)
        /* ensure update is written before pinging HW */
        nn_pci_flush(nn);
        nfp_qcp_wr_ptr_add(nn->qcp_cfg, 1);
+       nn->reconfig_in_progress_update = update;
 }
 
 /* Pass 0 as update to run posted reconfigs. */
@@ -123,10 +124,14 @@ static bool nfp_net_reconfig_check_done(struct nfp_net *nn, bool last_check)
        if (reg == 0)
                return true;
        if (reg & NFP_NET_CFG_UPDATE_ERR) {
-               nn_err(nn, "Reconfig error: 0x%08x\n", reg);
+               nn_err(nn, "Reconfig error (status: 0x%08x update: 0x%08x ctrl: 0x%08x)\n",
+                      reg, nn->reconfig_in_progress_update,
+                      nn_readl(nn, NFP_NET_CFG_CTRL));
                return true;
        } else if (last_check) {
-               nn_err(nn, "Reconfig timeout: 0x%08x\n", reg);
+               nn_err(nn, "Reconfig timeout (status: 0x%08x update: 0x%08x ctrl: 0x%08x)\n",
+                      reg, nn->reconfig_in_progress_update,
+                      nn_readl(nn, NFP_NET_CFG_CTRL));
                return true;
        }
 
@@ -279,7 +284,7 @@ int nfp_net_reconfig(struct nfp_net *nn, u32 update)
  *
  * Return: Negative errno on error, 0 on success
  */
-static int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd)
+int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd)
 {
        u32 mbox = nn->tlv_caps.mbox_off;
        int ret;
@@ -647,27 +652,29 @@ static void nfp_net_tx_ring_stop(struct netdev_queue *nd_q,
  * @txbuf: Pointer to driver soft TX descriptor
  * @txd: Pointer to HW TX descriptor
  * @skb: Pointer to SKB
+ * @md_bytes: Prepend length
  *
  * Set up Tx descriptor for LSO, do nothing for non-LSO skbs.
  * Return error on packet header greater than maximum supported LSO header size.
  */
 static void nfp_net_tx_tso(struct nfp_net_r_vector *r_vec,
                           struct nfp_net_tx_buf *txbuf,
-                          struct nfp_net_tx_desc *txd, struct sk_buff *skb)
+                          struct nfp_net_tx_desc *txd, struct sk_buff *skb,
+                          u32 md_bytes)
 {
-       u32 hdrlen;
+       u32 l3_offset, l4_offset, hdrlen;
        u16 mss;
 
        if (!skb_is_gso(skb))
                return;
 
        if (!skb->encapsulation) {
-               txd->l3_offset = skb_network_offset(skb);
-               txd->l4_offset = skb_transport_offset(skb);
+               l3_offset = skb_network_offset(skb);
+               l4_offset = skb_transport_offset(skb);
                hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb);
        } else {
-               txd->l3_offset = skb_inner_network_offset(skb);
-               txd->l4_offset = skb_inner_transport_offset(skb);
+               l3_offset = skb_inner_network_offset(skb);
+               l4_offset = skb_inner_transport_offset(skb);
                hdrlen = skb_inner_transport_header(skb) - skb->data +
                        inner_tcp_hdrlen(skb);
        }
@@ -676,7 +683,9 @@ static void nfp_net_tx_tso(struct nfp_net_r_vector *r_vec,
        txbuf->real_len += hdrlen * (txbuf->pkt_cnt - 1);
 
        mss = skb_shinfo(skb)->gso_size & PCIE_DESC_TX_MSS_MASK;
-       txd->lso_hdrlen = hdrlen;
+       txd->l3_offset = l3_offset - md_bytes;
+       txd->l4_offset = l4_offset - md_bytes;
+       txd->lso_hdrlen = hdrlen - md_bytes;
        txd->mss = cpu_to_le16(mss);
        txd->flags |= PCIE_DESC_TX_LSO;
 
@@ -786,11 +795,11 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
 {
        struct nfp_net *nn = netdev_priv(netdev);
        const struct skb_frag_struct *frag;
-       struct nfp_net_tx_desc *txd, txdg;
        int f, nr_frags, wr_idx, md_bytes;
        struct nfp_net_tx_ring *tx_ring;
        struct nfp_net_r_vector *r_vec;
        struct nfp_net_tx_buf *txbuf;
+       struct nfp_net_tx_desc *txd;
        struct netdev_queue *nd_q;
        struct nfp_net_dp *dp;
        dma_addr_t dma_addr;
@@ -801,13 +810,13 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
        qidx = skb_get_queue_mapping(skb);
        tx_ring = &dp->tx_rings[qidx];
        r_vec = tx_ring->r_vec;
-       nd_q = netdev_get_tx_queue(dp->netdev, qidx);
 
        nr_frags = skb_shinfo(skb)->nr_frags;
 
        if (unlikely(nfp_net_tx_full(tx_ring, nr_frags + 1))) {
                nn_dp_warn(dp, "TX ring %d busy. wrp=%u rdp=%u\n",
                           qidx, tx_ring->wr_p, tx_ring->rd_p);
+               nd_q = netdev_get_tx_queue(dp->netdev, qidx);
                netif_tx_stop_queue(nd_q);
                nfp_net_tx_xmit_more_flush(tx_ring);
                u64_stats_update_begin(&r_vec->tx_sync);
@@ -851,7 +860,7 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
        txd->lso_hdrlen = 0;
 
        /* Do not reorder - tso may adjust pkt cnt, vlan may override fields */
-       nfp_net_tx_tso(r_vec, txbuf, txd, skb);
+       nfp_net_tx_tso(r_vec, txbuf, txd, skb, md_bytes);
        nfp_net_tx_csum(dp, r_vec, txbuf, txd, skb);
        if (skb_vlan_tag_present(skb) && dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN) {
                txd->flags |= PCIE_DESC_TX_VLAN;
@@ -860,8 +869,10 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
 
        /* Gather DMA */
        if (nr_frags > 0) {
+               __le64 second_half;
+
                /* all descs must match except for in addr, length and eop */
-               txdg = *txd;
+               second_half = txd->vals8[1];
 
                for (f = 0; f < nr_frags; f++) {
                        frag = &skb_shinfo(skb)->frags[f];
@@ -878,11 +889,11 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
                        tx_ring->txbufs[wr_idx].fidx = f;
 
                        txd = &tx_ring->txds[wr_idx];
-                       *txd = txdg;
                        txd->dma_len = cpu_to_le16(fsize);
                        nfp_desc_set_dma_addr(txd, dma_addr);
-                       txd->offset_eop |=
-                               (f == nr_frags - 1) ? PCIE_DESC_TX_EOP : 0;
+                       txd->offset_eop = md_bytes |
+                               ((f == nr_frags - 1) ? PCIE_DESC_TX_EOP : 0);
+                       txd->vals8[1] = second_half;
                }
 
                u64_stats_update_begin(&r_vec->tx_sync);
@@ -892,6 +903,8 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
 
        skb_tx_timestamp(skb);
 
+       nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx);
+
        tx_ring->wr_p += nr_frags + 1;
        if (nfp_net_tx_ring_should_stop(tx_ring))
                nfp_net_tx_ring_stop(nd_q, tx_ring);
@@ -938,14 +951,10 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget)
 {
        struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
        struct nfp_net_dp *dp = &r_vec->nfp_net->dp;
-       const struct skb_frag_struct *frag;
        struct netdev_queue *nd_q;
        u32 done_pkts = 0, done_bytes = 0;
-       struct sk_buff *skb;
-       int todo, nr_frags;
        u32 qcp_rd_p;
-       int fidx;
-       int idx;
+       int todo;
 
        if (tx_ring->wr_p == tx_ring->rd_p)
                return;
@@ -959,26 +968,33 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget)
        todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p);
 
        while (todo--) {
+               const struct skb_frag_struct *frag;
+               struct nfp_net_tx_buf *tx_buf;
+               struct sk_buff *skb;
+               int fidx, nr_frags;
+               int idx;
+
                idx = D_IDX(tx_ring, tx_ring->rd_p++);
+               tx_buf = &tx_ring->txbufs[idx];
 
-               skb = tx_ring->txbufs[idx].skb;
+               skb = tx_buf->skb;
                if (!skb)
                        continue;
 
                nr_frags = skb_shinfo(skb)->nr_frags;
-               fidx = tx_ring->txbufs[idx].fidx;
+               fidx = tx_buf->fidx;
 
                if (fidx == -1) {
                        /* unmap head */
-                       dma_unmap_single(dp->dev, tx_ring->txbufs[idx].dma_addr,
+                       dma_unmap_single(dp->dev, tx_buf->dma_addr,
                                         skb_headlen(skb), DMA_TO_DEVICE);
 
-                       done_pkts += tx_ring->txbufs[idx].pkt_cnt;
-                       done_bytes += tx_ring->txbufs[idx].real_len;
+                       done_pkts += tx_buf->pkt_cnt;
+                       done_bytes += tx_buf->real_len;
                } else {
                        /* unmap fragment */
                        frag = &skb_shinfo(skb)->frags[fidx];
-                       dma_unmap_page(dp->dev, tx_ring->txbufs[idx].dma_addr,
+                       dma_unmap_page(dp->dev, tx_buf->dma_addr,
                                       skb_frag_size(frag), DMA_TO_DEVICE);
                }
 
@@ -986,9 +1002,9 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget)
                if (fidx == nr_frags - 1)
                        napi_consume_skb(skb, budget);
 
-               tx_ring->txbufs[idx].dma_addr = 0;
-               tx_ring->txbufs[idx].skb = NULL;
-               tx_ring->txbufs[idx].fidx = -2;
+               tx_buf->dma_addr = 0;
+               tx_buf->skb = NULL;
+               tx_buf->fidx = -2;
        }
 
        tx_ring->qcp_rd_p = qcp_rd_p;
@@ -3273,7 +3289,10 @@ nfp_net_features_check(struct sk_buff *skb, struct net_device *dev,
                hdrlen = skb_inner_transport_header(skb) - skb->data +
                        inner_tcp_hdrlen(skb);
 
-               if (unlikely(hdrlen > NFP_NET_LSO_MAX_HDR_SZ))
+               /* Assume worst case scenario of having longest possible
+                * metadata prepend - 8B
+                */
+               if (unlikely(hdrlen > NFP_NET_LSO_MAX_HDR_SZ - 8))
                        features &= ~NETIF_F_GSO_MASK;
        }
 
index f2aaef9..6d5213b 100644 (file)
@@ -41,8 +41,8 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
                data += 4;
 
                if (length % NFP_NET_CFG_TLV_LENGTH_INC) {
-                       dev_err(dev, "TLV size not multiple of %u len:%u\n",
-                               NFP_NET_CFG_TLV_LENGTH_INC, length);
+                       dev_err(dev, "TLV size not multiple of %u offset:%u len:%u\n",
+                               NFP_NET_CFG_TLV_LENGTH_INC, offset, length);
                        return -EINVAL;
                }
                if (data + length > end) {
@@ -61,14 +61,14 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
                        if (!length)
                                return 0;
 
-                       dev_err(dev, "END TLV should be empty, has len:%d\n",
-                               length);
+                       dev_err(dev, "END TLV should be empty, has offset:%u len:%d\n",
+                               offset, length);
                        return -EINVAL;
                case NFP_NET_CFG_TLV_TYPE_ME_FREQ:
                        if (length != 4) {
                                dev_err(dev,
-                                       "ME FREQ TLV should be 4B, is %dB\n",
-                                       length);
+                                       "ME FREQ TLV should be 4B, is %dB offset:%u\n",
+                                       length, offset);
                                return -EINVAL;
                        }
 
@@ -90,6 +90,15 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
                                 FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr),
                                 offset, length);
                        break;
+               case NFP_NET_CFG_TLV_TYPE_REPR_CAP:
+                       if (length < 4) {
+                               dev_err(dev, "REPR CAP TLV short %dB < 4B offset:%u\n",
+                                       length, offset);
+                               return -EINVAL;
+                       }
+
+                       caps->repr_cap = readl(data);
+                       break;
                default:
                        if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr))
                                break;
index d7c8518..166d7f7 100644 (file)
 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD 1
 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL 2
 
+#define NFP_NET_CFG_MBOX_CMD_PCI_DSCP_PRIOMAP_SET      5
+
 /**
  * VLAN filtering using general use mailbox
  * %NFP_NET_CFG_VLAN_FILTER:           Base address of VLAN filter mailbox
  * Variable, experimental IDs.  IDs designated for internal development and
  * experiments before a stable TLV ID has been allocated to a feature.  Should
  * never be present in production firmware.
+ *
+ * %NFP_NET_CFG_TLV_TYPE_REPR_CAP:
+ * Single word, equivalent of %NFP_NET_CFG_CAP for representors, features which
+ * can be used on representors.
  */
 #define NFP_NET_CFG_TLV_TYPE_UNKNOWN           0
 #define NFP_NET_CFG_TLV_TYPE_RESERVED          1
 #define NFP_NET_CFG_TLV_TYPE_MBOX              4
 #define NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL0     5
 #define NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL1     6
+#define NFP_NET_CFG_TLV_TYPE_REPR_CAP          7
 
 struct device;
 
@@ -480,11 +487,13 @@ struct device;
  * @me_freq_mhz:       ME clock_freq (MHz)
  * @mbox_off:          vNIC mailbox area offset
  * @mbox_len:          vNIC mailbox area length
+ * @repr_cap:          capabilities for representors
  */
 struct nfp_net_tlv_caps {
        u32 me_freq_mhz;
        unsigned int mbox_off;
        unsigned int mbox_len;
+       u32 repr_cap;
 };
 
 int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
index 69b1c9b..ab7f249 100644 (file)
@@ -8,7 +8,7 @@
 
 static struct dentry *nfp_dir;
 
-static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data)
+static int nfp_rx_q_show(struct seq_file *file, void *data)
 {
        struct nfp_net_r_vector *r_vec = file->private;
        struct nfp_net_rx_ring *rx_ring;
@@ -65,31 +65,12 @@ out:
        rtnl_unlock();
        return 0;
 }
+DEFINE_SHOW_ATTRIBUTE(nfp_rx_q);
 
-static int nfp_net_debugfs_rx_q_open(struct inode *inode, struct file *f)
-{
-       return single_open(f, nfp_net_debugfs_rx_q_read, inode->i_private);
-}
+static int nfp_tx_q_show(struct seq_file *file, void *data);
+DEFINE_SHOW_ATTRIBUTE(nfp_tx_q);
 
-static const struct file_operations nfp_rx_q_fops = {
-       .owner = THIS_MODULE,
-       .open = nfp_net_debugfs_rx_q_open,
-       .release = single_release,
-       .read = seq_read,
-       .llseek = seq_lseek
-};
-
-static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f);
-
-static const struct file_operations nfp_tx_q_fops = {
-       .owner = THIS_MODULE,
-       .open = nfp_net_debugfs_tx_q_open,
-       .release = single_release,
-       .read = seq_read,
-       .llseek = seq_lseek
-};
-
-static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data)
+static int nfp_tx_q_show(struct seq_file *file, void *data)
 {
        struct nfp_net_r_vector *r_vec = file->private;
        struct nfp_net_tx_ring *tx_ring;
@@ -158,18 +139,11 @@ out:
        return 0;
 }
 
-static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f)
+static int nfp_xdp_q_show(struct seq_file *file, void *data)
 {
-       return single_open(f, nfp_net_debugfs_tx_q_read, inode->i_private);
+       return nfp_tx_q_show(file, data);
 }
-
-static const struct file_operations nfp_xdp_q_fops = {
-       .owner = THIS_MODULE,
-       .open = nfp_net_debugfs_tx_q_open,
-       .release = single_release,
-       .read = seq_read,
-       .llseek = seq_lseek
-};
+DEFINE_SHOW_ATTRIBUTE(nfp_xdp_q);
 
 void nfp_net_debugfs_vnic_add(struct nfp_net *nn, struct dentry *ddir)
 {
index c09b893..69d7aeb 100644 (file)
@@ -11,6 +11,7 @@
 #include "nfpcore/nfp_nsp.h"
 #include "nfp_app.h"
 #include "nfp_main.h"
+#include "nfp_net.h"
 #include "nfp_net_ctrl.h"
 #include "nfp_net_repr.h"
 #include "nfp_net_sriov.h"
@@ -231,6 +232,27 @@ err_port_disable:
        return err;
 }
 
+static netdev_features_t
+nfp_repr_fix_features(struct net_device *netdev, netdev_features_t features)
+{
+       struct nfp_repr *repr = netdev_priv(netdev);
+       netdev_features_t old_features = features;
+       netdev_features_t lower_features;
+       struct net_device *lower_dev;
+
+       lower_dev = repr->dst->u.port_info.lower_dev;
+
+       lower_features = lower_dev->features;
+       if (lower_features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
+               lower_features |= NETIF_F_HW_CSUM;
+
+       features = netdev_intersect_features(features, lower_features);
+       features |= old_features & (NETIF_F_SOFT_FEATURES | NETIF_F_HW_TC);
+       features |= NETIF_F_LLTX;
+
+       return features;
+}
+
 const struct net_device_ops nfp_repr_netdev_ops = {
        .ndo_init               = nfp_app_ndo_init,
        .ndo_uninit             = nfp_app_ndo_uninit,
@@ -248,10 +270,25 @@ const struct net_device_ops nfp_repr_netdev_ops = {
        .ndo_set_vf_spoofchk    = nfp_app_set_vf_spoofchk,
        .ndo_get_vf_config      = nfp_app_get_vf_config,
        .ndo_set_vf_link_state  = nfp_app_set_vf_link_state,
+       .ndo_fix_features       = nfp_repr_fix_features,
        .ndo_set_features       = nfp_port_set_features,
        .ndo_set_mac_address    = eth_mac_addr,
 };
 
+void
+nfp_repr_transfer_features(struct net_device *netdev, struct net_device *lower)
+{
+       struct nfp_repr *repr = netdev_priv(netdev);
+
+       if (repr->dst->u.port_info.lower_dev != lower)
+               return;
+
+       netdev->gso_max_size = lower->gso_max_size;
+       netdev->gso_max_segs = lower->gso_max_segs;
+
+       netdev_update_features(netdev);
+}
+
 static void nfp_repr_clean(struct nfp_repr *repr)
 {
        unregister_netdev(repr->netdev);
@@ -281,6 +318,8 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
                  struct net_device *pf_netdev)
 {
        struct nfp_repr *repr = netdev_priv(netdev);
+       struct nfp_net *nn = netdev_priv(pf_netdev);
+       u32 repr_cap = nn->tlv_caps.repr_cap;
        int err;
 
        nfp_repr_set_lockdep_class(netdev);
@@ -299,6 +338,55 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
 
        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;
+
+       netdev->hw_features = NETIF_F_HIGHDMA;
+       if (repr_cap & NFP_NET_CFG_CTRL_RXCSUM_ANY)
+               netdev->hw_features |= NETIF_F_RXCSUM;
+       if (repr_cap & NFP_NET_CFG_CTRL_TXCSUM)
+               netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+       if (repr_cap & NFP_NET_CFG_CTRL_GATHER)
+               netdev->hw_features |= NETIF_F_SG;
+       if ((repr_cap & NFP_NET_CFG_CTRL_LSO && nn->fw_ver.major > 2) ||
+           repr_cap & NFP_NET_CFG_CTRL_LSO2)
+               netdev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
+       if (repr_cap & NFP_NET_CFG_CTRL_RSS_ANY)
+               netdev->hw_features |= NETIF_F_RXHASH;
+       if (repr_cap & NFP_NET_CFG_CTRL_VXLAN) {
+               if (repr_cap & NFP_NET_CFG_CTRL_LSO)
+                       netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL;
+       }
+       if (repr_cap & NFP_NET_CFG_CTRL_NVGRE) {
+               if (repr_cap & NFP_NET_CFG_CTRL_LSO)
+                       netdev->hw_features |= NETIF_F_GSO_GRE;
+       }
+       if (repr_cap & (NFP_NET_CFG_CTRL_VXLAN | NFP_NET_CFG_CTRL_NVGRE))
+               netdev->hw_enc_features = netdev->hw_features;
+
+       netdev->vlan_features = netdev->hw_features;
+
+       if (repr_cap & NFP_NET_CFG_CTRL_RXVLAN)
+               netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
+       if (repr_cap & NFP_NET_CFG_CTRL_TXVLAN) {
+               if (repr_cap & NFP_NET_CFG_CTRL_LSO2)
+                       netdev_warn(netdev, "Device advertises both TSO2 and TXVLAN. Refusing to enable TXVLAN.\n");
+               else
+                       netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
+       }
+       if (repr_cap & NFP_NET_CFG_CTRL_CTAG_FILTER)
+               netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
+       netdev->features = netdev->hw_features;
+
+       /* Advertise but disable TSO by default. */
+       netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
+       netdev->gso_max_segs = NFP_NET_LSO_MAX_SEGS;
+
+       netdev->priv_flags |= IFF_NO_QUEUE;
+       netdev->features |= NETIF_F_LLTX;
+
        if (nfp_app_has_tc(app)) {
                netdev->features |= NETIF_F_HW_TC;
                netdev->hw_features |= NETIF_F_HW_TC;
@@ -442,7 +530,9 @@ int nfp_reprs_resync_phys_ports(struct nfp_app *app)
                        continue;
 
                nfp_app_repr_preclean(app, netdev);
+               rtnl_lock();
                rcu_assign_pointer(reprs->reprs[i], NULL);
+               rtnl_unlock();
                synchronize_rcu();
                nfp_repr_clean(repr);
        }
index c412b94..e0f13df 100644 (file)
@@ -92,6 +92,8 @@ nfp_repr_get_locked(struct nfp_app *app, struct nfp_reprs *set,
                    unsigned int id);
 
 void nfp_repr_inc_rx_stats(struct net_device *netdev, unsigned int len);
+void
+nfp_repr_transfer_features(struct net_device *netdev, struct net_device *lower);
 int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
                  u32 cmsg_port_id, struct nfp_port *port,
                  struct net_device *pf_netdev);
index 052b3d2..c662c6f 100644 (file)
@@ -912,7 +912,7 @@ static const struct net_device_ops w90p910_ether_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
 };
 
-static void __init get_mac_address(struct net_device *dev)
+static void get_mac_address(struct net_device *dev)
 {
        struct w90p910_ether *ether = netdev_priv(dev);
        struct platform_device *pdev;
index bd8695a..89d1739 100644 (file)
 #define LPC_FCCR_MIRRORCOUNTERCURRENT(n)       ((n) & 0xFFFF)
 
 /*
- * rxfliterctrl, rxfilterwolstatus, and rxfilterwolclear shared
+ * rxfilterctrl, rxfilterwolstatus, and rxfilterwolclear shared
  * register definitions
  */
 #define LPC_RXFLTRW_ACCEPTUNICAST              (1 << 0)
 #define LPC_RXFLTRW_ACCEPTPERFECT              (1 << 5)
 
 /*
- * rxfliterctrl register definitions
+ * rxfilterctrl register definitions
  */
 #define LPC_RXFLTRWSTS_MAGICPACKETENWOL                (1 << 12)
 #define LPC_RXFLTRWSTS_RXFILTERENWOL           (1 << 13)
index d9a03ab..24a9016 100644 (file)
@@ -296,6 +296,12 @@ enum qed_wol_support {
        QED_WOL_SUPPORT_PME,
 };
 
+enum qed_db_rec_exec {
+       DB_REC_DRY_RUN,
+       DB_REC_REAL_DEAL,
+       DB_REC_ONCE,
+};
+
 struct qed_hw_info {
        /* PCI personality */
        enum qed_pci_personality personality;
@@ -425,6 +431,14 @@ struct qed_qm_info {
        u8 num_pf_rls;
 };
 
+struct qed_db_recovery_info {
+       struct list_head list;
+
+       /* Lock to protect the doorbell recovery mechanism list */
+       spinlock_t lock;
+       u32 db_recovery_counter;
+};
+
 struct storm_stats {
        u32     address;
        u32     len;
@@ -522,6 +536,7 @@ struct qed_simd_fp_handler {
 
 enum qed_slowpath_wq_flag {
        QED_SLOWPATH_MFW_TLV_REQ,
+       QED_SLOWPATH_PERIODIC_DB_REC,
 };
 
 struct qed_hwfn {
@@ -640,6 +655,9 @@ struct qed_hwfn {
        /* L2-related */
        struct qed_l2_info *p_l2_info;
 
+       /* Mechanism for recovering from doorbell drop */
+       struct qed_db_recovery_info db_recovery_info;
+
        /* Nvm images number and attributes */
        struct qed_nvm_image_info nvm_info;
 
@@ -652,11 +670,12 @@ struct qed_hwfn {
        struct delayed_work iov_task;
        unsigned long iov_task_flags;
 #endif
-
-       struct z_stream_s               *stream;
+       struct z_stream_s *stream;
+       bool slowpath_wq_active;
        struct workqueue_struct *slowpath_wq;
        struct delayed_work slowpath_task;
        unsigned long slowpath_task_flags;
+       u32 periodic_db_rec_count;
 };
 
 struct pci_params {
@@ -897,6 +916,12 @@ u16 qed_get_cm_pq_idx_llt_mtc(struct qed_hwfn *p_hwfn, u8 tc);
 
 #define QED_LEADING_HWFN(dev)   (&dev->hwfns[0])
 
+/* doorbell recovery mechanism */
+void qed_db_recovery_dp(struct qed_hwfn *p_hwfn);
+void qed_db_recovery_execute(struct qed_hwfn *p_hwfn,
+                            enum qed_db_rec_exec db_exec);
+bool qed_edpm_enabled(struct qed_hwfn *p_hwfn);
+
 /* Other Linux specific common definitions */
 #define DP_NAME(cdev) ((cdev)->name)
 
@@ -931,4 +956,6 @@ int qed_mfw_fill_tlv_data(struct qed_hwfn *hwfn,
                          union qed_mfw_tlv_data *tlv_data);
 
 void qed_hw_info_set_offload_tc(struct qed_hw_info *p_info, u8 tc);
+
+void qed_periodic_db_rec_start(struct qed_hwfn *p_hwfn);
 #endif /* _QED_H */
index 8e8fa82..69966df 100644 (file)
@@ -191,7 +191,7 @@ qed_dcbx_dp_protocol(struct qed_hwfn *p_hwfn, struct qed_dcbx_results *p_data)
 static void
 qed_dcbx_set_params(struct qed_dcbx_results *p_data,
                    struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
-                   bool enable, u8 prio, u8 tc,
+                   bool app_tlv, bool enable, u8 prio, u8 tc,
                    enum dcbx_protocol_type type,
                    enum qed_pci_personality personality)
 {
@@ -210,7 +210,7 @@ qed_dcbx_set_params(struct qed_dcbx_results *p_data,
                p_data->arr[type].dont_add_vlan0 = true;
 
        /* QM reconf data */
-       if (p_hwfn->hw_info.personality == personality)
+       if (app_tlv && p_hwfn->hw_info.personality == personality)
                qed_hw_info_set_offload_tc(&p_hwfn->hw_info, tc);
 
        /* Configure dcbx vlan priority in doorbell block for roce EDPM */
@@ -225,7 +225,7 @@ qed_dcbx_set_params(struct qed_dcbx_results *p_data,
 static void
 qed_dcbx_update_app_info(struct qed_dcbx_results *p_data,
                         struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
-                        bool enable, u8 prio, u8 tc,
+                        bool app_tlv, bool enable, u8 prio, u8 tc,
                         enum dcbx_protocol_type type)
 {
        enum qed_pci_personality personality;
@@ -240,7 +240,7 @@ qed_dcbx_update_app_info(struct qed_dcbx_results *p_data,
 
                personality = qed_dcbx_app_update[i].personality;
 
-               qed_dcbx_set_params(p_data, p_hwfn, p_ptt, enable,
+               qed_dcbx_set_params(p_data, p_hwfn, p_ptt, app_tlv, enable,
                                    prio, tc, type, personality);
        }
 }
@@ -319,8 +319,8 @@ qed_dcbx_process_tlv(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
                                enable = true;
                        }
 
-                       qed_dcbx_update_app_info(p_data, p_hwfn, p_ptt, enable,
-                                                priority, tc, type);
+                       qed_dcbx_update_app_info(p_data, p_hwfn, p_ptt, true,
+                                                enable, priority, tc, type);
                }
        }
 
@@ -341,7 +341,7 @@ qed_dcbx_process_tlv(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
                        continue;
 
                enable = (type == DCBX_PROTOCOL_ETH) ? false : !!dcbx_version;
-               qed_dcbx_update_app_info(p_data, p_hwfn, p_ptt, enable,
+               qed_dcbx_update_app_info(p_data, p_hwfn, p_ptt, false, enable,
                                         priority, tc, type);
        }
 
index 78a638e..979f1e4 100644 (file)
@@ -6071,7 +6071,7 @@ static const char * const s_igu_fifo_error_strs[] = {
        "no error",
        "length error",
        "function disabled",
-       "VF sent command to attnetion address",
+       "VF sent command to attention address",
        "host sent prod update command",
        "read of during interrupt register while in MIMD mode",
        "access to PXP BAR reserved address",
index 7ceb2b9..8f65514 100644 (file)
 
 static DEFINE_SPINLOCK(qm_lock);
 
+/******************** Doorbell Recovery *******************/
+/* The doorbell recovery mechanism consists of a list of entries which represent
+ * doorbelling entities (l2 queues, roce sq/rq/cqs, the slowpath spq, etc). Each
+ * entity needs to register with the mechanism and provide the parameters
+ * describing it's doorbell, including a location where last used doorbell data
+ * can be found. The doorbell execute function will traverse the list and
+ * doorbell all of the registered entries.
+ */
+struct qed_db_recovery_entry {
+       struct list_head list_entry;
+       void __iomem *db_addr;
+       void *db_data;
+       enum qed_db_rec_width db_width;
+       enum qed_db_rec_space db_space;
+       u8 hwfn_idx;
+};
+
+/* Display a single doorbell recovery entry */
+static void qed_db_recovery_dp_entry(struct qed_hwfn *p_hwfn,
+                                    struct qed_db_recovery_entry *db_entry,
+                                    char *action)
+{
+       DP_VERBOSE(p_hwfn,
+                  QED_MSG_SPQ,
+                  "(%s: db_entry %p, addr %p, data %p, width %s, %s space, hwfn %d)\n",
+                  action,
+                  db_entry,
+                  db_entry->db_addr,
+                  db_entry->db_data,
+                  db_entry->db_width == DB_REC_WIDTH_32B ? "32b" : "64b",
+                  db_entry->db_space == DB_REC_USER ? "user" : "kernel",
+                  db_entry->hwfn_idx);
+}
+
+/* Doorbell address sanity (address within doorbell bar range) */
+static bool qed_db_rec_sanity(struct qed_dev *cdev,
+                             void __iomem *db_addr, void *db_data)
+{
+       /* Make sure doorbell address is within the doorbell bar */
+       if (db_addr < cdev->doorbells ||
+           (u8 __iomem *)db_addr >
+           (u8 __iomem *)cdev->doorbells + cdev->db_size) {
+               WARN(true,
+                    "Illegal doorbell address: %p. Legal range for doorbell addresses is [%p..%p]\n",
+                    db_addr,
+                    cdev->doorbells,
+                    (u8 __iomem *)cdev->doorbells + cdev->db_size);
+               return false;
+       }
+
+       /* ake sure doorbell data pointer is not null */
+       if (!db_data) {
+               WARN(true, "Illegal doorbell data pointer: %p", db_data);
+               return false;
+       }
+
+       return true;
+}
+
+/* Find hwfn according to the doorbell address */
+static struct qed_hwfn *qed_db_rec_find_hwfn(struct qed_dev *cdev,
+                                            void __iomem *db_addr)
+{
+       struct qed_hwfn *p_hwfn;
+
+       /* In CMT doorbell bar is split down the middle between engine 0 and enigne 1 */
+       if (cdev->num_hwfns > 1)
+               p_hwfn = db_addr < cdev->hwfns[1].doorbells ?
+                   &cdev->hwfns[0] : &cdev->hwfns[1];
+       else
+               p_hwfn = QED_LEADING_HWFN(cdev);
+
+       return p_hwfn;
+}
+
+/* Add a new entry to the doorbell recovery mechanism */
+int qed_db_recovery_add(struct qed_dev *cdev,
+                       void __iomem *db_addr,
+                       void *db_data,
+                       enum qed_db_rec_width db_width,
+                       enum qed_db_rec_space db_space)
+{
+       struct qed_db_recovery_entry *db_entry;
+       struct qed_hwfn *p_hwfn;
+
+       /* Shortcircuit VFs, for now */
+       if (IS_VF(cdev)) {
+               DP_VERBOSE(cdev,
+                          QED_MSG_IOV, "db recovery - skipping VF doorbell\n");
+               return 0;
+       }
+
+       /* Sanitize doorbell address */
+       if (!qed_db_rec_sanity(cdev, db_addr, db_data))
+               return -EINVAL;
+
+       /* Obtain hwfn from doorbell address */
+       p_hwfn = qed_db_rec_find_hwfn(cdev, db_addr);
+
+       /* Create entry */
+       db_entry = kzalloc(sizeof(*db_entry), GFP_KERNEL);
+       if (!db_entry) {
+               DP_NOTICE(cdev, "Failed to allocate a db recovery entry\n");
+               return -ENOMEM;
+       }
+
+       /* Populate entry */
+       db_entry->db_addr = db_addr;
+       db_entry->db_data = db_data;
+       db_entry->db_width = db_width;
+       db_entry->db_space = db_space;
+       db_entry->hwfn_idx = p_hwfn->my_id;
+
+       /* Display */
+       qed_db_recovery_dp_entry(p_hwfn, db_entry, "Adding");
+
+       /* Protect the list */
+       spin_lock_bh(&p_hwfn->db_recovery_info.lock);
+       list_add_tail(&db_entry->list_entry, &p_hwfn->db_recovery_info.list);
+       spin_unlock_bh(&p_hwfn->db_recovery_info.lock);
+
+       return 0;
+}
+
+/* Remove an entry from the doorbell recovery mechanism */
+int qed_db_recovery_del(struct qed_dev *cdev,
+                       void __iomem *db_addr, void *db_data)
+{
+       struct qed_db_recovery_entry *db_entry = NULL;
+       struct qed_hwfn *p_hwfn;
+       int rc = -EINVAL;
+
+       /* Shortcircuit VFs, for now */
+       if (IS_VF(cdev)) {
+               DP_VERBOSE(cdev,
+                          QED_MSG_IOV, "db recovery - skipping VF doorbell\n");
+               return 0;
+       }
+
+       /* Sanitize doorbell address */
+       if (!qed_db_rec_sanity(cdev, db_addr, db_data))
+               return -EINVAL;
+
+       /* Obtain hwfn from doorbell address */
+       p_hwfn = qed_db_rec_find_hwfn(cdev, db_addr);
+
+       /* Protect the list */
+       spin_lock_bh(&p_hwfn->db_recovery_info.lock);
+       list_for_each_entry(db_entry,
+                           &p_hwfn->db_recovery_info.list, list_entry) {
+               /* search according to db_data addr since db_addr is not unique (roce) */
+               if (db_entry->db_data == db_data) {
+                       qed_db_recovery_dp_entry(p_hwfn, db_entry, "Deleting");
+                       list_del(&db_entry->list_entry);
+                       rc = 0;
+                       break;
+               }
+       }
+
+       spin_unlock_bh(&p_hwfn->db_recovery_info.lock);
+
+       if (rc == -EINVAL)
+
+               DP_NOTICE(p_hwfn,
+                         "Failed to find element in list. Key (db_data addr) was %p. db_addr was %p\n",
+                         db_data, db_addr);
+       else
+               kfree(db_entry);
+
+       return rc;
+}
+
+/* Initialize the doorbell recovery mechanism */
+static int qed_db_recovery_setup(struct qed_hwfn *p_hwfn)
+{
+       DP_VERBOSE(p_hwfn, QED_MSG_SPQ, "Setting up db recovery\n");
+
+       /* Make sure db_size was set in cdev */
+       if (!p_hwfn->cdev->db_size) {
+               DP_ERR(p_hwfn->cdev, "db_size not set\n");
+               return -EINVAL;
+       }
+
+       INIT_LIST_HEAD(&p_hwfn->db_recovery_info.list);
+       spin_lock_init(&p_hwfn->db_recovery_info.lock);
+       p_hwfn->db_recovery_info.db_recovery_counter = 0;
+
+       return 0;
+}
+
+/* Destroy the doorbell recovery mechanism */
+static void qed_db_recovery_teardown(struct qed_hwfn *p_hwfn)
+{
+       struct qed_db_recovery_entry *db_entry = NULL;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SPQ, "Tearing down db recovery\n");
+       if (!list_empty(&p_hwfn->db_recovery_info.list)) {
+               DP_VERBOSE(p_hwfn,
+                          QED_MSG_SPQ,
+                          "Doorbell Recovery teardown found the doorbell recovery list was not empty (Expected in disorderly driver unload (e.g. recovery) otherwise this probably means some flow forgot to db_recovery_del). Prepare to purge doorbell recovery list...\n");
+               while (!list_empty(&p_hwfn->db_recovery_info.list)) {
+                       db_entry =
+                           list_first_entry(&p_hwfn->db_recovery_info.list,
+                                            struct qed_db_recovery_entry,
+                                            list_entry);
+                       qed_db_recovery_dp_entry(p_hwfn, db_entry, "Purging");
+                       list_del(&db_entry->list_entry);
+                       kfree(db_entry);
+               }
+       }
+       p_hwfn->db_recovery_info.db_recovery_counter = 0;
+}
+
+/* Print the content of the doorbell recovery mechanism */
+void qed_db_recovery_dp(struct qed_hwfn *p_hwfn)
+{
+       struct qed_db_recovery_entry *db_entry = NULL;
+
+       DP_NOTICE(p_hwfn,
+                 "Displaying doorbell recovery database. Counter was %d\n",
+                 p_hwfn->db_recovery_info.db_recovery_counter);
+
+       /* Protect the list */
+       spin_lock_bh(&p_hwfn->db_recovery_info.lock);
+       list_for_each_entry(db_entry,
+                           &p_hwfn->db_recovery_info.list, list_entry) {
+               qed_db_recovery_dp_entry(p_hwfn, db_entry, "Printing");
+       }
+
+       spin_unlock_bh(&p_hwfn->db_recovery_info.lock);
+}
+
+/* Ring the doorbell of a single doorbell recovery entry */
+static void qed_db_recovery_ring(struct qed_hwfn *p_hwfn,
+                                struct qed_db_recovery_entry *db_entry,
+                                enum qed_db_rec_exec db_exec)
+{
+       if (db_exec != DB_REC_ONCE) {
+               /* Print according to width */
+               if (db_entry->db_width == DB_REC_WIDTH_32B) {
+                       DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
+                                  "%s doorbell address %p data %x\n",
+                                  db_exec == DB_REC_DRY_RUN ?
+                                  "would have rung" : "ringing",
+                                  db_entry->db_addr,
+                                  *(u32 *)db_entry->db_data);
+               } else {
+                       DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
+                                  "%s doorbell address %p data %llx\n",
+                                  db_exec == DB_REC_DRY_RUN ?
+                                  "would have rung" : "ringing",
+                                  db_entry->db_addr,
+                                  *(u64 *)(db_entry->db_data));
+               }
+       }
+
+       /* Sanity */
+       if (!qed_db_rec_sanity(p_hwfn->cdev, db_entry->db_addr,
+                              db_entry->db_data))
+               return;
+
+       /* Flush the write combined buffer. Since there are multiple doorbelling
+        * entities using the same address, if we don't flush, a transaction
+        * could be lost.
+        */
+       wmb();
+
+       /* Ring the doorbell */
+       if (db_exec == DB_REC_REAL_DEAL || db_exec == DB_REC_ONCE) {
+               if (db_entry->db_width == DB_REC_WIDTH_32B)
+                       DIRECT_REG_WR(db_entry->db_addr,
+                                     *(u32 *)(db_entry->db_data));
+               else
+                       DIRECT_REG_WR64(db_entry->db_addr,
+                                       *(u64 *)(db_entry->db_data));
+       }
+
+       /* Flush the write combined buffer. Next doorbell may come from a
+        * different entity to the same address...
+        */
+       wmb();
+}
+
+/* Traverse the doorbell recovery entry list and ring all the doorbells */
+void qed_db_recovery_execute(struct qed_hwfn *p_hwfn,
+                            enum qed_db_rec_exec db_exec)
+{
+       struct qed_db_recovery_entry *db_entry = NULL;
+
+       if (db_exec != DB_REC_ONCE) {
+               DP_NOTICE(p_hwfn,
+                         "Executing doorbell recovery. Counter was %d\n",
+                         p_hwfn->db_recovery_info.db_recovery_counter);
+
+               /* Track amount of times recovery was executed */
+               p_hwfn->db_recovery_info.db_recovery_counter++;
+       }
+
+       /* Protect the list */
+       spin_lock_bh(&p_hwfn->db_recovery_info.lock);
+       list_for_each_entry(db_entry,
+                           &p_hwfn->db_recovery_info.list, list_entry) {
+               qed_db_recovery_ring(p_hwfn, db_entry, db_exec);
+               if (db_exec == DB_REC_ONCE)
+                       break;
+       }
+
+       spin_unlock_bh(&p_hwfn->db_recovery_info.lock);
+}
+
+/******************** Doorbell Recovery end ****************/
+
 #define QED_MIN_DPIS            (4)
 #define QED_MIN_PWM_REGION      (QED_WID_SIZE * QED_MIN_DPIS)
 
@@ -185,11 +497,18 @@ void qed_resc_free(struct qed_dev *cdev)
                        qed_iscsi_free(p_hwfn);
                        qed_ooo_free(p_hwfn);
                }
+
+               if (QED_IS_RDMA_PERSONALITY(p_hwfn))
+                       qed_rdma_info_free(p_hwfn);
+
                qed_iov_free(p_hwfn);
                qed_l2_free(p_hwfn);
                qed_dmae_info_free(p_hwfn);
                qed_dcbx_info_free(p_hwfn);
                qed_dbg_user_data_free(p_hwfn);
+
+               /* Destroy doorbell recovery mechanism */
+               qed_db_recovery_teardown(p_hwfn);
        }
 }
 
@@ -481,8 +800,16 @@ static u16 *qed_init_qm_get_idx_from_flags(struct qed_hwfn *p_hwfn,
        struct qed_qm_info *qm_info = &p_hwfn->qm_info;
 
        /* Can't have multiple flags set here */
-       if (bitmap_weight((unsigned long *)&pq_flags, sizeof(pq_flags)) > 1)
+       if (bitmap_weight((unsigned long *)&pq_flags,
+                         sizeof(pq_flags) * BITS_PER_BYTE) > 1) {
+               DP_ERR(p_hwfn, "requested multiple pq flags 0x%x\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);
+               goto err;
+       }
 
        switch (pq_flags) {
        case PQ_FLAGS_RLS:
@@ -506,8 +833,7 @@ static u16 *qed_init_qm_get_idx_from_flags(struct qed_hwfn *p_hwfn,
        }
 
 err:
-       DP_ERR(p_hwfn, "BAD pq flags %d\n", pq_flags);
-       return NULL;
+       return &qm_info->start_pq;
 }
 
 /* save pq index in qm info */
@@ -531,20 +857,32 @@ u16 qed_get_cm_pq_idx_mcos(struct qed_hwfn *p_hwfn, u8 tc)
 {
        u8 max_tc = qed_init_qm_get_num_tcs(p_hwfn);
 
+       if (max_tc == 0) {
+               DP_ERR(p_hwfn, "pq with flag 0x%lx do not exist\n",
+                      PQ_FLAGS_MCOS);
+               return p_hwfn->qm_info.start_pq;
+       }
+
        if (tc > max_tc)
                DP_ERR(p_hwfn, "tc %d must be smaller than %d\n", tc, max_tc);
 
-       return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_MCOS) + tc;
+       return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_MCOS) + (tc % max_tc);
 }
 
 u16 qed_get_cm_pq_idx_vf(struct qed_hwfn *p_hwfn, u16 vf)
 {
        u16 max_vf = qed_init_qm_get_num_vfs(p_hwfn);
 
+       if (max_vf == 0) {
+               DP_ERR(p_hwfn, "pq with flag 0x%lx do not exist\n",
+                      PQ_FLAGS_VFS);
+               return p_hwfn->qm_info.start_pq;
+       }
+
        if (vf > max_vf)
                DP_ERR(p_hwfn, "vf %d must be smaller than %d\n", vf, max_vf);
 
-       return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_VFS) + vf;
+       return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_VFS) + (vf % max_vf);
 }
 
 u16 qed_get_cm_pq_idx_ofld_mtc(struct qed_hwfn *p_hwfn, u8 tc)
@@ -946,6 +1284,11 @@ int qed_resc_alloc(struct qed_dev *cdev)
                struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
                u32 n_eqes, num_cons;
 
+               /* Initialize the doorbell recovery mechanism */
+               rc = qed_db_recovery_setup(p_hwfn);
+               if (rc)
+                       goto alloc_err;
+
                /* First allocate the context manager structure */
                rc = qed_cxt_mngr_alloc(p_hwfn);
                if (rc)
@@ -1081,6 +1424,12 @@ int qed_resc_alloc(struct qed_dev *cdev)
                                goto alloc_err;
                }
 
+               if (QED_IS_RDMA_PERSONALITY(p_hwfn)) {
+                       rc = qed_rdma_info_alloc(p_hwfn);
+                       if (rc)
+                               goto alloc_err;
+               }
+
                /* DMA info initialization */
                rc = qed_dmae_info_alloc(p_hwfn);
                if (rc)
@@ -1439,6 +1788,14 @@ enum QED_ROCE_EDPM_MODE {
        QED_ROCE_EDPM_MODE_DISABLE = 2,
 };
 
+bool qed_edpm_enabled(struct qed_hwfn *p_hwfn)
+{
+       if (p_hwfn->dcbx_no_edpm || p_hwfn->db_bar_no_edpm)
+               return false;
+
+       return true;
+}
+
 static int
 qed_hw_init_pf_doorbell_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
 {
@@ -1508,13 +1865,13 @@ qed_hw_init_pf_doorbell_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
        p_hwfn->wid_count = (u16) n_cpus;
 
        DP_INFO(p_hwfn,
-               "doorbell bar: normal_region_size=%d, pwm_region_size=%d, dpi_size=%d, dpi_count=%d, roce_edpm=%s\n",
+               "doorbell bar: normal_region_size=%d, pwm_region_size=%d, dpi_size=%d, dpi_count=%d, roce_edpm=%s, page_size=%lu\n",
                norm_regsize,
                pwm_regsize,
                p_hwfn->dpi_size,
                p_hwfn->dpi_count,
-               ((p_hwfn->dcbx_no_edpm) || (p_hwfn->db_bar_no_edpm)) ?
-               "disabled" : "enabled");
+               (!qed_edpm_enabled(p_hwfn)) ?
+               "disabled" : "enabled", PAGE_SIZE);
 
        if (rc) {
                DP_ERR(p_hwfn,
@@ -2102,11 +2459,8 @@ int qed_hw_start_fastpath(struct qed_hwfn *p_hwfn)
        if (!p_ptt)
                return -EAGAIN;
 
-       /* If roce info is allocated it means roce is initialized and should
-        * be enabled in searcher.
-        */
        if (p_hwfn->p_rdma_info &&
-           p_hwfn->b_rdma_enabled_in_prs)
+           p_hwfn->p_rdma_info->active && p_hwfn->b_rdma_enabled_in_prs)
                qed_wr(p_hwfn, p_ptt, p_hwfn->rdma_prs_search_reg, 0x1);
 
        /* Re-open incoming traffic */
index defdda1..acccd85 100644 (file)
@@ -472,6 +472,34 @@ int qed_get_queue_coalesce(struct qed_hwfn *p_hwfn, u16 *coal, void *handle);
 int
 qed_set_queue_coalesce(u16 rx_coal, u16 tx_coal, void *p_handle);
 
+/**
+ * @brief db_recovery_add - add doorbell information to the doorbell
+ * recovery mechanism.
+ *
+ * @param cdev
+ * @param db_addr - doorbell address
+ * @param db_data - address of where db_data is stored
+ * @param db_width - doorbell is 32b pr 64b
+ * @param db_space - doorbell recovery addresses are user or kernel space
+ */
+int qed_db_recovery_add(struct qed_dev *cdev,
+                       void __iomem *db_addr,
+                       void *db_data,
+                       enum qed_db_rec_width db_width,
+                       enum qed_db_rec_space db_space);
+
+/**
+ * @brief db_recovery_del - remove doorbell information from the doorbell
+ * recovery mechanism. db_data serves as key (db_addr is not unique).
+ *
+ * @param cdev
+ * @param db_addr - doorbell address
+ * @param db_data - address where db_data is stored. Serves as key for the
+ *                  entry to delete.
+ */
+int qed_db_recovery_del(struct qed_dev *cdev,
+                       void __iomem *db_addr, void *db_data);
+
 
 const char *qed_hw_get_resc_name(enum qed_resources res_id);
 #endif
index 5c221eb..b13cfb4 100644 (file)
@@ -12655,6 +12655,7 @@ struct public_drv_mb {
 #define DRV_MB_PARAM_DCBX_NOTIFY_MASK          0x000000FF
 #define DRV_MB_PARAM_DCBX_NOTIFY_SHIFT         3
 
+#define DRV_MB_PARAM_NVM_PUT_FILE_BEGIN_MBI     0x3
 #define DRV_MB_PARAM_NVM_LEN_OFFSET            24
 
 #define DRV_MB_PARAM_CFG_VF_MSIX_VF_ID_SHIFT   0
@@ -12814,6 +12815,11 @@ struct public_drv_mb {
        union drv_union_data union_data;
 };
 
+#define FW_MB_PARAM_NVM_PUT_FILE_REQ_OFFSET_MASK       0x00ffffff
+#define FW_MB_PARAM_NVM_PUT_FILE_REQ_OFFSET_SHIFT      0
+#define FW_MB_PARAM_NVM_PUT_FILE_REQ_SIZE_MASK         0xff000000
+#define FW_MB_PARAM_NVM_PUT_FILE_REQ_SIZE_SHIFT                24
+
 enum MFW_DRV_MSG_TYPE {
        MFW_DRV_MSG_LINK_CHANGE,
        MFW_DRV_MSG_FLR_FW_ACK_FAILED,
@@ -12831,8 +12837,9 @@ enum MFW_DRV_MSG_TYPE {
        MFW_DRV_MSG_BW_UPDATE10,
        MFW_DRV_MSG_TRANSCEIVER_STATE_CHANGE,
        MFW_DRV_MSG_BW_UPDATE11,
-       MFW_DRV_MSG_OEM_CFG_UPDATE,
+       MFW_DRV_MSG_RESERVED,
        MFW_DRV_MSG_GET_TLV_REQ,
+       MFW_DRV_MSG_OEM_CFG_UPDATE,
        MFW_DRV_MSG_MAX
 };
 
index 0f0aba7..9234091 100644 (file)
@@ -361,29 +361,147 @@ static int qed_pglub_rbc_attn_cb(struct qed_hwfn *p_hwfn)
        return 0;
 }
 
-#define QED_DORQ_ATTENTION_REASON_MASK (0xfffff)
-#define QED_DORQ_ATTENTION_OPAQUE_MASK (0xffff)
-#define QED_DORQ_ATTENTION_SIZE_MASK   (0x7f)
-#define QED_DORQ_ATTENTION_SIZE_SHIFT  (16)
+#define QED_DORQ_ATTENTION_REASON_MASK  (0xfffff)
+#define QED_DORQ_ATTENTION_OPAQUE_MASK  (0xffff)
+#define QED_DORQ_ATTENTION_OPAQUE_SHIFT (0x0)
+#define QED_DORQ_ATTENTION_SIZE_MASK            (0x7f)
+#define QED_DORQ_ATTENTION_SIZE_SHIFT           (16)
+
+#define QED_DB_REC_COUNT                        1000
+#define QED_DB_REC_INTERVAL                     100
+
+static int qed_db_rec_flush_queue(struct qed_hwfn *p_hwfn,
+                                 struct qed_ptt *p_ptt)
+{
+       u32 count = QED_DB_REC_COUNT;
+       u32 usage = 1;
+
+       /* wait for usage to zero or count to run out. This is necessary since
+        * EDPM doorbell transactions can take multiple 64b cycles, and as such
+        * can "split" over the pci. Possibly, the doorbell drop can happen with
+        * half an EDPM in the queue and other half dropped. Another EDPM
+        * doorbell to the same address (from doorbell recovery mechanism or
+        * from the doorbelling entity) could have first half dropped and second
+        * half interpreted as continuation of the first. To prevent such
+        * malformed doorbells from reaching the device, flush the queue before
+        * releasing the overflow sticky indication.
+        */
+       while (count-- && usage) {
+               usage = qed_rd(p_hwfn, p_ptt, DORQ_REG_PF_USAGE_CNT);
+               udelay(QED_DB_REC_INTERVAL);
+       }
+
+       /* should have been depleted by now */
+       if (usage) {
+               DP_NOTICE(p_hwfn->cdev,
+                         "DB recovery: doorbell usage failed to zero after %d usec. usage was %x\n",
+                         QED_DB_REC_INTERVAL * QED_DB_REC_COUNT, usage);
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+int qed_db_rec_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
+{
+       u32 overflow;
+       int rc;
+
+       overflow = qed_rd(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY);
+       DP_NOTICE(p_hwfn, "PF Overflow sticky 0x%x\n", overflow);
+       if (!overflow) {
+               qed_db_recovery_execute(p_hwfn, DB_REC_ONCE);
+               return 0;
+       }
+
+       if (qed_edpm_enabled(p_hwfn)) {
+               rc = qed_db_rec_flush_queue(p_hwfn, p_ptt);
+               if (rc)
+                       return rc;
+       }
+
+       /* Flush any pending (e)dpm as they may never arrive */
+       qed_wr(p_hwfn, p_ptt, DORQ_REG_DPM_FORCE_ABORT, 0x1);
+
+       /* Release overflow sticky indication (stop silently dropping everything) */
+       qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY, 0x0);
+
+       /* Repeat all last doorbells (doorbell drop recovery) */
+       qed_db_recovery_execute(p_hwfn, DB_REC_REAL_DEAL);
+
+       return 0;
+}
+
 static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn)
 {
-       u32 reason;
+       u32 int_sts, first_drop_reason, details, address, all_drops_reason;
+       struct qed_ptt *p_ptt = p_hwfn->p_dpc_ptt;
+       int rc;
 
-       reason = qed_rd(p_hwfn, p_hwfn->p_dpc_ptt, DORQ_REG_DB_DROP_REASON) &
-                       QED_DORQ_ATTENTION_REASON_MASK;
-       if (reason) {
-               u32 details = qed_rd(p_hwfn, p_hwfn->p_dpc_ptt,
-                                    DORQ_REG_DB_DROP_DETAILS);
+       int_sts = qed_rd(p_hwfn, p_ptt, DORQ_REG_INT_STS);
+       DP_NOTICE(p_hwfn->cdev, "DORQ attention. int_sts was %x\n", int_sts);
 
-               DP_INFO(p_hwfn->cdev,
-                       "DORQ db_drop: address 0x%08x Opaque FID 0x%04x Size [bytes] 0x%08x Reason: 0x%08x\n",
-                       qed_rd(p_hwfn, p_hwfn->p_dpc_ptt,
-                              DORQ_REG_DB_DROP_DETAILS_ADDRESS),
-                       (u16)(details & QED_DORQ_ATTENTION_OPAQUE_MASK),
-                       GET_FIELD(details, QED_DORQ_ATTENTION_SIZE) * 4,
-                       reason);
+       /* int_sts may be zero since all PFs were interrupted for doorbell
+        * overflow but another one already handled it. Can abort here. If
+        * This PF also requires overflow recovery we will be interrupted again.
+        * The masked almost full indication may also be set. Ignoring.
+        */
+       if (!(int_sts & ~DORQ_REG_INT_STS_DORQ_FIFO_AFULL))
+               return 0;
+
+       /* check if db_drop or overflow happened */
+       if (int_sts & (DORQ_REG_INT_STS_DB_DROP |
+                      DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR)) {
+               /* Obtain data about db drop/overflow */
+               first_drop_reason = qed_rd(p_hwfn, p_ptt,
+                                          DORQ_REG_DB_DROP_REASON) &
+                   QED_DORQ_ATTENTION_REASON_MASK;
+               details = qed_rd(p_hwfn, p_ptt, DORQ_REG_DB_DROP_DETAILS);
+               address = qed_rd(p_hwfn, p_ptt,
+                                DORQ_REG_DB_DROP_DETAILS_ADDRESS);
+               all_drops_reason = qed_rd(p_hwfn, p_ptt,
+                                         DORQ_REG_DB_DROP_DETAILS_REASON);
+
+               /* Log info */
+               DP_NOTICE(p_hwfn->cdev,
+                         "Doorbell drop occurred\n"
+                         "Address\t\t0x%08x\t(second BAR address)\n"
+                         "FID\t\t0x%04x\t\t(Opaque FID)\n"
+                         "Size\t\t0x%04x\t\t(in bytes)\n"
+                         "1st drop reason\t0x%08x\t(details on first drop since last handling)\n"
+                         "Sticky reasons\t0x%08x\t(all drop reasons since last handling)\n",
+                         address,
+                         GET_FIELD(details, QED_DORQ_ATTENTION_OPAQUE),
+                         GET_FIELD(details, QED_DORQ_ATTENTION_SIZE) * 4,
+                         first_drop_reason, all_drops_reason);
+
+               rc = qed_db_rec_handler(p_hwfn, p_ptt);
+               qed_periodic_db_rec_start(p_hwfn);
+               if (rc)
+                       return rc;
+
+               /* Clear the doorbell drop details and prepare for next drop */
+               qed_wr(p_hwfn, p_ptt, DORQ_REG_DB_DROP_DETAILS_REL, 0);
+
+               /* Mark interrupt as handled (note: even if drop was due to a different
+                * reason than overflow we mark as handled)
+                */
+               qed_wr(p_hwfn,
+                      p_ptt,
+                      DORQ_REG_INT_STS_WR,
+                      DORQ_REG_INT_STS_DB_DROP |
+                      DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR);
+
+               /* If there are no indications other than drop indications, success */
+               if ((int_sts & ~(DORQ_REG_INT_STS_DB_DROP |
+                                DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR |
+                                DORQ_REG_INT_STS_DORQ_FIFO_AFULL)) == 0)
+                       return 0;
        }
 
+       /* Some other indication was present - non recoverable */
+       DP_INFO(p_hwfn, "DORQ fatal attention\n");
+
        return -EINVAL;
 }
 
@@ -992,6 +1110,8 @@ static int qed_int_attentions(struct qed_hwfn *p_hwfn)
         */
        do {
                index = p_sb_attn->sb_index;
+               /* finish reading index before the loop condition */
+               dma_rmb();
                attn_bits = le32_to_cpu(p_sb_attn->atten_bits);
                attn_acks = le32_to_cpu(p_sb_attn->atten_ack);
        } while (index != p_sb_attn->sb_index);
index 54b4ee0..d81a62e 100644 (file)
@@ -190,6 +190,16 @@ void qed_int_get_num_sbs(struct qed_hwfn   *p_hwfn,
  */
 void qed_int_disable_post_isr_release(struct qed_dev *cdev);
 
+/**
+ * @brief - Doorbell Recovery handler.
+ *          Run DB_REAL_DEAL doorbell recovery in case of PF overflow
+ *          (and flush DORQ if needed), otherwise run DB_REC_ONCE.
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ */
+int qed_db_rec_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
+
 #define QED_CAU_DEF_RX_TIMER_RES 0
 #define QED_CAU_DEF_TX_TIMER_RES 0
 
index aa63338..90afd51 100644 (file)
@@ -1085,7 +1085,14 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn,
 
        p_ramrod->gsi_offload_flag = p_ll2_conn->input.gsi_enable;
 
-       return qed_spq_post(p_hwfn, p_ent, NULL);
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc)
+               return rc;
+
+       rc = qed_db_recovery_add(p_hwfn->cdev, p_tx->doorbell_addr,
+                                &p_tx->db_msg, DB_REC_WIDTH_32B,
+                                DB_REC_KERNEL);
+       return rc;
 }
 
 static int qed_sp_ll2_rx_queue_stop(struct qed_hwfn *p_hwfn,
@@ -1119,9 +1126,11 @@ static int qed_sp_ll2_rx_queue_stop(struct qed_hwfn *p_hwfn,
 static int qed_sp_ll2_tx_queue_stop(struct qed_hwfn *p_hwfn,
                                    struct qed_ll2_info *p_ll2_conn)
 {
+       struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
        struct qed_spq_entry *p_ent = NULL;
        struct qed_sp_init_data init_data;
        int rc = -EINVAL;
+       qed_db_recovery_del(p_hwfn->cdev, p_tx->doorbell_addr, &p_tx->db_msg);
 
        /* Get SPQ entry */
        memset(&init_data, 0, sizeof(init_data));
@@ -1542,6 +1551,13 @@ int qed_ll2_establish_connection(void *cxt, u8 connection_handle)
        p_tx->doorbell_addr = (u8 __iomem *)p_hwfn->doorbells +
                                            qed_db_addr(p_ll2_conn->cid,
                                                        DQ_DEMS_LEGACY);
+       /* prepare db data */
+       SET_FIELD(p_tx->db_msg.params, CORE_DB_DATA_DEST, DB_DEST_XCM);
+       SET_FIELD(p_tx->db_msg.params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_SET);
+       SET_FIELD(p_tx->db_msg.params, CORE_DB_DATA_AGG_VAL_SEL,
+                 DQ_XCM_CORE_TX_BD_PROD_CMD);
+       p_tx->db_msg.agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
+
 
        rc = qed_ll2_establish_connection_rx(p_hwfn, p_ll2_conn);
        if (rc)
@@ -1780,7 +1796,6 @@ static void qed_ll2_tx_packet_notify(struct qed_hwfn *p_hwfn,
        bool b_notify = p_ll2_conn->tx_queue.cur_send_packet->notify_fw;
        struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
        struct qed_ll2_tx_packet *p_pkt = NULL;
-       struct core_db_data db_msg = { 0, 0, 0 };
        u16 bd_prod;
 
        /* If there are missing BDs, don't do anything now */
@@ -1809,24 +1824,19 @@ static void qed_ll2_tx_packet_notify(struct qed_hwfn *p_hwfn,
                list_move_tail(&p_pkt->list_entry, &p_tx->active_descq);
        }
 
-       SET_FIELD(db_msg.params, CORE_DB_DATA_DEST, DB_DEST_XCM);
-       SET_FIELD(db_msg.params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_SET);
-       SET_FIELD(db_msg.params, CORE_DB_DATA_AGG_VAL_SEL,
-                 DQ_XCM_CORE_TX_BD_PROD_CMD);
-       db_msg.agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
-       db_msg.spq_prod = cpu_to_le16(bd_prod);
+       p_tx->db_msg.spq_prod = cpu_to_le16(bd_prod);
 
        /* Make sure the BDs data is updated before ringing the doorbell */
        wmb();
 
-       DIRECT_REG_WR(p_tx->doorbell_addr, *((u32 *)&db_msg));
+       DIRECT_REG_WR(p_tx->doorbell_addr, *((u32 *)&p_tx->db_msg));
 
        DP_VERBOSE(p_hwfn,
                   (NETIF_MSG_TX_QUEUED | QED_MSG_LL2),
                   "LL2 [q 0x%02x cid 0x%08x type 0x%08x] Doorbelled [producer 0x%04x]\n",
                   p_ll2_conn->queue_id,
                   p_ll2_conn->cid,
-                  p_ll2_conn->input.conn_type, db_msg.spq_prod);
+                  p_ll2_conn->input.conn_type, p_tx->db_msg.spq_prod);
 }
 
 int qed_ll2_prepare_tx_packet(void *cxt,
@@ -2496,6 +2506,7 @@ static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb,
                if (unlikely(dma_mapping_error(&cdev->pdev->dev, mapping))) {
                        DP_NOTICE(cdev,
                                  "Unable to map frag - dropping packet\n");
+                       rc = -ENOMEM;
                        goto err;
                }
 
index 1a5c1ae..5f01fbd 100644 (file)
@@ -103,6 +103,7 @@ struct qed_ll2_tx_queue {
        struct qed_ll2_tx_packet cur_completing_packet;
        u16 cur_completing_bd_idx;
        void __iomem *doorbell_addr;
+       struct core_db_data db_msg;
        u16 bds_idx;
        u16 cur_send_frag_num;
        u16 cur_completing_frag_num;
index 35fd0db..6adf5bd 100644 (file)
@@ -966,9 +966,47 @@ static void qed_update_pf_params(struct qed_dev *cdev,
        }
 }
 
+#define QED_PERIODIC_DB_REC_COUNT              100
+#define QED_PERIODIC_DB_REC_INTERVAL_MS                100
+#define QED_PERIODIC_DB_REC_INTERVAL \
+       msecs_to_jiffies(QED_PERIODIC_DB_REC_INTERVAL_MS)
+#define QED_PERIODIC_DB_REC_WAIT_COUNT         10
+#define QED_PERIODIC_DB_REC_WAIT_INTERVAL \
+       (QED_PERIODIC_DB_REC_INTERVAL_MS / QED_PERIODIC_DB_REC_WAIT_COUNT)
+
+static int qed_slowpath_delayed_work(struct qed_hwfn *hwfn,
+                                    enum qed_slowpath_wq_flag wq_flag,
+                                    unsigned long delay)
+{
+       if (!hwfn->slowpath_wq_active)
+               return -EINVAL;
+
+       /* Memory barrier for setting atomic bit */
+       smp_mb__before_atomic();
+       set_bit(wq_flag, &hwfn->slowpath_task_flags);
+       smp_mb__after_atomic();
+       queue_delayed_work(hwfn->slowpath_wq, &hwfn->slowpath_task, delay);
+
+       return 0;
+}
+
+void qed_periodic_db_rec_start(struct qed_hwfn *p_hwfn)
+{
+       /* Reset periodic Doorbell Recovery counter */
+       p_hwfn->periodic_db_rec_count = QED_PERIODIC_DB_REC_COUNT;
+
+       /* Don't schedule periodic Doorbell Recovery if already scheduled */
+       if (test_bit(QED_SLOWPATH_PERIODIC_DB_REC,
+                    &p_hwfn->slowpath_task_flags))
+               return;
+
+       qed_slowpath_delayed_work(p_hwfn, QED_SLOWPATH_PERIODIC_DB_REC,
+                                 QED_PERIODIC_DB_REC_INTERVAL);
+}
+
 static void qed_slowpath_wq_stop(struct qed_dev *cdev)
 {
-       int i;
+       int i, sleep_count = QED_PERIODIC_DB_REC_WAIT_COUNT;
 
        if (IS_VF(cdev))
                return;
@@ -977,6 +1015,15 @@ static void qed_slowpath_wq_stop(struct qed_dev *cdev)
                if (!cdev->hwfns[i].slowpath_wq)
                        continue;
 
+               /* Stop queuing new delayed works */
+               cdev->hwfns[i].slowpath_wq_active = false;
+
+               /* Wait until the last periodic doorbell recovery is executed */
+               while (test_bit(QED_SLOWPATH_PERIODIC_DB_REC,
+                               &cdev->hwfns[i].slowpath_task_flags) &&
+                      sleep_count--)
+                       msleep(QED_PERIODIC_DB_REC_WAIT_INTERVAL);
+
                flush_workqueue(cdev->hwfns[i].slowpath_wq);
                destroy_workqueue(cdev->hwfns[i].slowpath_wq);
        }
@@ -989,7 +1036,10 @@ static void qed_slowpath_task(struct work_struct *work)
        struct qed_ptt *ptt = qed_ptt_acquire(hwfn);
 
        if (!ptt) {
-               queue_delayed_work(hwfn->slowpath_wq, &hwfn->slowpath_task, 0);
+               if (hwfn->slowpath_wq_active)
+                       queue_delayed_work(hwfn->slowpath_wq,
+                                          &hwfn->slowpath_task, 0);
+
                return;
        }
 
@@ -997,6 +1047,15 @@ static void qed_slowpath_task(struct work_struct *work)
                               &hwfn->slowpath_task_flags))
                qed_mfw_process_tlv_req(hwfn, ptt);
 
+       if (test_and_clear_bit(QED_SLOWPATH_PERIODIC_DB_REC,
+                              &hwfn->slowpath_task_flags)) {
+               qed_db_rec_handler(hwfn, ptt);
+               if (hwfn->periodic_db_rec_count--)
+                       qed_slowpath_delayed_work(hwfn,
+                                                 QED_SLOWPATH_PERIODIC_DB_REC,
+                                                 QED_PERIODIC_DB_REC_INTERVAL);
+       }
+
        qed_ptt_release(hwfn, ptt);
 }
 
@@ -1023,6 +1082,7 @@ static int qed_slowpath_wq_start(struct qed_dev *cdev)
                }
 
                INIT_DELAYED_WORK(&hwfn->slowpath_task, qed_slowpath_task);
+               hwfn->slowpath_wq_active = true;
        }
 
        return 0;
@@ -1782,9 +1842,9 @@ static int qed_drain(struct qed_dev *cdev)
                        return -EBUSY;
                }
                rc = qed_mcp_drain(hwfn, ptt);
+               qed_ptt_release(hwfn, ptt);
                if (rc)
                        return rc;
-               qed_ptt_release(hwfn, ptt);
        }
 
        return 0;
@@ -1939,21 +1999,30 @@ exit:
  * 0B  |                       0x3 [command index]                            |
  * 4B  | b'0: check_response?   | b'1-31  reserved                            |
  * 8B  | File-type |                   reserved                               |
+ * 12B |                    Image length in bytes                             |
  *     \----------------------------------------------------------------------/
  *     Start a new file of the provided type
  */
 static int qed_nvm_flash_image_file_start(struct qed_dev *cdev,
                                          const u8 **data, bool *check_resp)
 {
+       u32 file_type, file_size = 0;
        int rc;
 
        *data += 4;
        *check_resp = !!(**data & BIT(0));
        *data += 4;
+       file_type = **data;
 
        DP_VERBOSE(cdev, NETIF_MSG_DRV,
-                  "About to start a new file of type %02x\n", **data);
-       rc = qed_mcp_nvm_put_file_begin(cdev, **data);
+                  "About to start a new file of type %02x\n", file_type);
+       if (file_type == DRV_MB_PARAM_NVM_PUT_FILE_BEGIN_MBI) {
+               *data += 4;
+               file_size = *((u32 *)(*data));
+       }
+
+       rc = qed_mcp_nvm_write(cdev, QED_PUT_FILE_BEGIN, file_type,
+                              (u8 *)(&file_size), 4);
        *data += 4;
 
        return rc;
@@ -2315,6 +2384,8 @@ const struct qed_common_ops qed_common_ops_pass = {
        .update_mac = &qed_update_mac,
        .update_mtu = &qed_update_mtu,
        .update_wol = &qed_update_wol,
+       .db_recovery_add = &qed_db_recovery_add,
+       .db_recovery_del = &qed_db_recovery_del,
        .read_module_eeprom = &qed_read_module_eeprom,
 };
 
index a96364d..e7f18e3 100644 (file)
@@ -1619,7 +1619,7 @@ static void qed_mcp_update_stag(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
                qed_sp_pf_update_stag(p_hwfn);
        }
 
-       DP_VERBOSE(p_hwfn, QED_MSG_SP, "ovlan  = %d hw_mode = 0x%x\n",
+       DP_VERBOSE(p_hwfn, QED_MSG_SP, "ovlan = %d hw_mode = 0x%x\n",
                   p_hwfn->mcp_info->func_info.ovlan, p_hwfn->hw_info.hw_mode);
 
        /* Acknowledge the MFW */
@@ -1641,7 +1641,9 @@ void qed_mcp_read_ufp_config(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
        val = (port_cfg & OEM_CFG_CHANNEL_TYPE_MASK) >>
                OEM_CFG_CHANNEL_TYPE_OFFSET;
        if (val != OEM_CFG_CHANNEL_TYPE_STAGGED)
-               DP_NOTICE(p_hwfn, "Incorrect UFP Channel type  %d\n", val);
+               DP_NOTICE(p_hwfn,
+                         "Incorrect UFP Channel type  %d port_id 0x%02x\n",
+                         val, MFW_PORT(p_hwfn));
 
        val = (port_cfg & OEM_CFG_SCHED_TYPE_MASK) >> OEM_CFG_SCHED_TYPE_OFFSET;
        if (val == OEM_CFG_SCHED_TYPE_ETS) {
@@ -1650,7 +1652,9 @@ void qed_mcp_read_ufp_config(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
                p_hwfn->ufp_info.mode = QED_UFP_MODE_VNIC_BW;
        } else {
                p_hwfn->ufp_info.mode = QED_UFP_MODE_UNKNOWN;
-               DP_NOTICE(p_hwfn, "Unknown UFP scheduling mode %d\n", val);
+               DP_NOTICE(p_hwfn,
+                         "Unknown UFP scheduling mode %d port_id 0x%02x\n",
+                         val, MFW_PORT(p_hwfn));
        }
 
        qed_mcp_get_shmem_func(p_hwfn, p_ptt, &shmem_info, MCP_PF_ID(p_hwfn));
@@ -1665,13 +1669,15 @@ void qed_mcp_read_ufp_config(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
                p_hwfn->ufp_info.pri_type = QED_UFP_PRI_OS;
        } else {
                p_hwfn->ufp_info.pri_type = QED_UFP_PRI_UNKNOWN;
-               DP_NOTICE(p_hwfn, "Unknown Host priority control %d\n", val);
+               DP_NOTICE(p_hwfn,
+                         "Unknown Host priority control %d port_id 0x%02x\n",
+                         val, MFW_PORT(p_hwfn));
        }
 
        DP_NOTICE(p_hwfn,
-                 "UFP shmem config: mode = %d tc = %d pri_type = %d\n",
-                 p_hwfn->ufp_info.mode,
-                 p_hwfn->ufp_info.tc, p_hwfn->ufp_info.pri_type);
+                 "UFP shmem config: mode = %d tc = %d pri_type = %d port_id 0x%02x\n",
+                 p_hwfn->ufp_info.mode, p_hwfn->ufp_info.tc,
+                 p_hwfn->ufp_info.pri_type, MFW_PORT(p_hwfn));
 }
 
 static int
@@ -2739,24 +2745,6 @@ int qed_mcp_nvm_resp(struct qed_dev *cdev, u8 *p_buf)
        return 0;
 }
 
-int qed_mcp_nvm_put_file_begin(struct qed_dev *cdev, u32 addr)
-{
-       struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
-       struct qed_ptt *p_ptt;
-       u32 resp, param;
-       int rc;
-
-       p_ptt = qed_ptt_acquire(p_hwfn);
-       if (!p_ptt)
-               return -EBUSY;
-       rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_NVM_PUT_FILE_BEGIN, addr,
-                        &resp, &param);
-       cdev->mcp_nvm_resp = resp;
-       qed_ptt_release(p_hwfn, p_ptt);
-
-       return rc;
-}
-
 int qed_mcp_nvm_write(struct qed_dev *cdev,
                      u32 cmd, u32 addr, u8 *p_buf, u32 len)
 {
@@ -2770,6 +2758,9 @@ int qed_mcp_nvm_write(struct qed_dev *cdev,
                return -EBUSY;
 
        switch (cmd) {
+       case QED_PUT_FILE_BEGIN:
+               nvm_cmd = DRV_MSG_CODE_NVM_PUT_FILE_BEGIN;
+               break;
        case QED_PUT_FILE_DATA:
                nvm_cmd = DRV_MSG_CODE_NVM_PUT_FILE_DATA;
                break;
@@ -2782,10 +2773,14 @@ int qed_mcp_nvm_write(struct qed_dev *cdev,
                goto out;
        }
 
+       buf_size = min_t(u32, (len - buf_idx), MCP_DRV_NVM_BUF_LEN);
        while (buf_idx < len) {
-               buf_size = min_t(u32, (len - buf_idx), MCP_DRV_NVM_BUF_LEN);
-               nvm_offset = ((buf_size << DRV_MB_PARAM_NVM_LEN_OFFSET) |
-                             addr) + buf_idx;
+               if (cmd == QED_PUT_FILE_BEGIN)
+                       nvm_offset = addr;
+               else
+                       nvm_offset = ((buf_size <<
+                                      DRV_MB_PARAM_NVM_LEN_OFFSET) | addr) +
+                                      buf_idx;
                rc = qed_mcp_nvm_wr_cmd(p_hwfn, p_ptt, nvm_cmd, nvm_offset,
                                        &resp, &param, buf_size,
                                        (u32 *)&p_buf[buf_idx]);
@@ -2810,7 +2805,19 @@ int qed_mcp_nvm_write(struct qed_dev *cdev,
                if (buf_idx % 0x1000 > (buf_idx + buf_size) % 0x1000)
                        usleep_range(1000, 2000);
 
-               buf_idx += buf_size;
+               /* For MBI upgrade, MFW response includes the next buffer offset
+                * to be delivered to MFW.
+                */
+               if (param && cmd == QED_PUT_FILE_DATA) {
+                       buf_idx = QED_MFW_GET_FIELD(param,
+                                       FW_MB_PARAM_NVM_PUT_FILE_REQ_OFFSET);
+                       buf_size = QED_MFW_GET_FIELD(param,
+                                        FW_MB_PARAM_NVM_PUT_FILE_REQ_SIZE);
+               } else {
+                       buf_idx += buf_size;
+                       buf_size = min_t(u32, (len - buf_idx),
+                                        MCP_DRV_NVM_BUF_LEN);
+               }
        }
 
        cdev->mcp_nvm_resp = resp;
index 1adfe52..eddf677 100644 (file)
@@ -542,16 +542,6 @@ int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len);
 int qed_mcp_nvm_write(struct qed_dev *cdev,
                      u32 cmd, u32 addr, u8 *p_buf, u32 len);
 
-/**
- * @brief Put file begin
- *
- *  @param cdev
- *  @param addr - nvm offset
- *
- * @return int - 0 - operation was successful.
- */
-int qed_mcp_nvm_put_file_begin(struct qed_dev *cdev, u32 addr);
-
 /**
  * @brief Check latest response
  *
index 6211343..7873d6d 100644 (file)
@@ -140,22 +140,34 @@ static u32 qed_rdma_get_sb_id(void *p_hwfn, u32 rel_sb_id)
        return FEAT_NUM((struct qed_hwfn *)p_hwfn, QED_PF_L2_QUE) + rel_sb_id;
 }
 
-static int qed_rdma_alloc(struct qed_hwfn *p_hwfn,
-                         struct qed_ptt *p_ptt,
-                         struct qed_rdma_start_in_params *params)
+int qed_rdma_info_alloc(struct qed_hwfn *p_hwfn)
 {
        struct qed_rdma_info *p_rdma_info;
-       u32 num_cons, num_tasks;
-       int rc = -ENOMEM;
 
-       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocating RDMA\n");
-
-       /* Allocate a struct with current pf rdma info */
        p_rdma_info = kzalloc(sizeof(*p_rdma_info), GFP_KERNEL);
        if (!p_rdma_info)
-               return rc;
+               return -ENOMEM;
+
+       spin_lock_init(&p_rdma_info->lock);
 
        p_hwfn->p_rdma_info = p_rdma_info;
+       return 0;
+}
+
+void qed_rdma_info_free(struct qed_hwfn *p_hwfn)
+{
+       kfree(p_hwfn->p_rdma_info);
+       p_hwfn->p_rdma_info = NULL;
+}
+
+static int qed_rdma_alloc(struct qed_hwfn *p_hwfn)
+{
+       struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info;
+       u32 num_cons, num_tasks;
+       int rc = -ENOMEM;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocating RDMA\n");
+
        if (QED_IS_IWARP_PERSONALITY(p_hwfn))
                p_rdma_info->proto = PROTOCOLID_IWARP;
        else
@@ -183,7 +195,7 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn,
        /* Allocate a struct with device params and fill it */
        p_rdma_info->dev = kzalloc(sizeof(*p_rdma_info->dev), GFP_KERNEL);
        if (!p_rdma_info->dev)
-               goto free_rdma_info;
+               return rc;
 
        /* Allocate a struct with port params and fill it */
        p_rdma_info->port = kzalloc(sizeof(*p_rdma_info->port), GFP_KERNEL);
@@ -298,8 +310,6 @@ free_rdma_port:
        kfree(p_rdma_info->port);
 free_rdma_dev:
        kfree(p_rdma_info->dev);
-free_rdma_info:
-       kfree(p_rdma_info);
 
        return rc;
 }
@@ -370,8 +380,6 @@ static void qed_rdma_resc_free(struct qed_hwfn *p_hwfn)
 
        kfree(p_rdma_info->port);
        kfree(p_rdma_info->dev);
-
-       kfree(p_rdma_info);
 }
 
 static void qed_rdma_free_tid(void *rdma_cxt, u32 itid)
@@ -679,8 +687,6 @@ static int qed_rdma_setup(struct qed_hwfn *p_hwfn,
 
        DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "RDMA setup\n");
 
-       spin_lock_init(&p_hwfn->p_rdma_info->lock);
-
        qed_rdma_init_devinfo(p_hwfn, params);
        qed_rdma_init_port(p_hwfn);
        qed_rdma_init_events(p_hwfn, params);
@@ -727,7 +733,7 @@ static int qed_rdma_stop(void *rdma_cxt)
        /* Disable RoCE search */
        qed_wr(p_hwfn, p_ptt, p_hwfn->rdma_prs_search_reg, 0);
        p_hwfn->b_rdma_enabled_in_prs = false;
-
+       p_hwfn->p_rdma_info->active = 0;
        qed_wr(p_hwfn, p_ptt, PRS_REG_ROCE_DEST_QP_MAX_PF, 0);
 
        ll2_ethertype_en = qed_rd(p_hwfn, p_ptt, PRS_REG_LIGHT_L2_ETHERTYPE_EN);
@@ -1236,7 +1242,8 @@ qed_rdma_create_qp(void *rdma_cxt,
        u8 max_stats_queues;
        int rc;
 
-       if (!rdma_cxt || !in_params || !out_params || !p_hwfn->p_rdma_info) {
+       if (!rdma_cxt || !in_params || !out_params ||
+           !p_hwfn->p_rdma_info->active) {
                DP_ERR(p_hwfn->cdev,
                       "qed roce create qp failed due to NULL entry (rdma_cxt=%p, in=%p, out=%p, roce_info=?\n",
                       rdma_cxt, in_params, out_params);
@@ -1802,8 +1809,8 @@ bool qed_rdma_allocated_qps(struct qed_hwfn *p_hwfn)
 {
        bool result;
 
-       /* if rdma info has not been allocated, naturally there are no qps */
-       if (!p_hwfn->p_rdma_info)
+       /* if rdma wasn't activated yet, naturally there are no qps */
+       if (!p_hwfn->p_rdma_info->active)
                return false;
 
        spin_lock_bh(&p_hwfn->p_rdma_info->lock);
@@ -1849,7 +1856,7 @@ static int qed_rdma_start(void *rdma_cxt,
        if (!p_ptt)
                goto err;
 
-       rc = qed_rdma_alloc(p_hwfn, p_ptt, params);
+       rc = qed_rdma_alloc(p_hwfn);
        if (rc)
                goto err1;
 
@@ -1858,6 +1865,7 @@ static int qed_rdma_start(void *rdma_cxt,
                goto err2;
 
        qed_ptt_release(p_hwfn, p_ptt);
+       p_hwfn->p_rdma_info->active = 1;
 
        return rc;
 
index 6f722ee..3689fe3 100644 (file)
@@ -102,6 +102,7 @@ struct qed_rdma_info {
        u16 max_queue_zones;
        enum protocol_type proto;
        struct qed_iwarp_info iwarp;
+       u8 active:1;
 };
 
 struct qed_rdma_qp {
@@ -176,10 +177,14 @@ struct qed_rdma_qp {
 #if IS_ENABLED(CONFIG_QED_RDMA)
 void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
 void qed_rdma_dpm_conf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
+int qed_rdma_info_alloc(struct qed_hwfn *p_hwfn);
+void qed_rdma_info_free(struct qed_hwfn *p_hwfn);
 #else
 static inline void qed_rdma_dpm_conf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) {}
 static inline void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn,
                                    struct qed_ptt *p_ptt) {}
+static inline int qed_rdma_info_alloc(struct qed_hwfn *p_hwfn) {return -EINVAL;}
+static inline void qed_rdma_info_free(struct qed_hwfn *p_hwfn) {}
 #endif
 
 int
index 2440970..8939ed6 100644 (file)
        0x1701534UL
 #define TSEM_REG_DBG_FORCE_FRAME \
        0x1701538UL
+#define DORQ_REG_PF_USAGE_CNT \
+       0x1009c0UL
+#define DORQ_REG_PF_OVFL_STICKY        \
+       0x1009d0UL
+#define DORQ_REG_DPM_FORCE_ABORT \
+       0x1009d8UL
+#define DORQ_REG_INT_STS \
+       0x100180UL
+#define DORQ_REG_INT_STS_ADDRESS_ERROR \
+       (0x1UL << 0)
+#define DORQ_REG_INT_STS_WR \
+       0x100188UL
+#define DORQ_REG_DB_DROP_DETAILS_REL \
+       0x100a28UL
+#define DORQ_REG_INT_STS_ADDRESS_ERROR_SHIFT \
+       0
+#define DORQ_REG_INT_STS_DB_DROP \
+               (0x1UL << 1)
+#define DORQ_REG_INT_STS_DB_DROP_SHIFT \
+       1
+#define DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR \
+               (0x1UL << 2)
+#define DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR_SHIFT \
+       2
+#define DORQ_REG_INT_STS_DORQ_FIFO_AFULL\
+               (0x1UL << 3)
+#define DORQ_REG_INT_STS_DORQ_FIFO_AFULL_SHIFT \
+       3
+#define DORQ_REG_INT_STS_CFC_BYP_VALIDATION_ERR \
+               (0x1UL << 4)
+#define DORQ_REG_INT_STS_CFC_BYP_VALIDATION_ERR_SHIFT \
+       4
+#define DORQ_REG_INT_STS_CFC_LD_RESP_ERR \
+               (0x1UL << 5)
+#define DORQ_REG_INT_STS_CFC_LD_RESP_ERR_SHIFT \
+       5
+#define DORQ_REG_INT_STS_XCM_DONE_CNT_ERR \
+               (0x1UL << 6)
+#define DORQ_REG_INT_STS_XCM_DONE_CNT_ERR_SHIFT        \
+       6
+#define DORQ_REG_INT_STS_CFC_LD_REQ_FIFO_OVFL_ERR \
+               (0x1UL << 7)
+#define DORQ_REG_INT_STS_CFC_LD_REQ_FIFO_OVFL_ERR_SHIFT        \
+       7
+#define DORQ_REG_INT_STS_CFC_LD_REQ_FIFO_UNDER_ERR \
+               (0x1UL << 8)
+#define DORQ_REG_INT_STS_CFC_LD_REQ_FIFO_UNDER_ERR_SHIFT \
+       8
+#define DORQ_REG_DB_DROP_DETAILS_REASON        \
+       0x100a20UL
 #define MSEM_REG_DBG_SELECT \
        0x1801528UL
 #define MSEM_REG_DBG_DWORD_ENABLE \
index 3157c0d..4179c90 100644 (file)
@@ -227,7 +227,9 @@ struct qed_spq {
        u32                     comp_count;
 
        u32                     cid;
-       qed_spq_async_comp_cb async_comp_cb[MAX_PROTOCOL_TYPE];
+       u32                     db_addr_offset;
+       struct core_db_data     db_data;
+       qed_spq_async_comp_cb   async_comp_cb[MAX_PROTOCOL_TYPE];
 };
 
 /**
index 0a9c5bb..eb88bbc 100644 (file)
@@ -252,9 +252,9 @@ static int qed_spq_hw_post(struct qed_hwfn *p_hwfn,
                           struct qed_spq *p_spq, struct qed_spq_entry *p_ent)
 {
        struct qed_chain *p_chain = &p_hwfn->p_spq->chain;
+       struct core_db_data *p_db_data = &p_spq->db_data;
        u16 echo = qed_chain_get_prod_idx(p_chain);
        struct slow_path_element        *elem;
-       struct core_db_data             db;
 
        p_ent->elem.hdr.echo    = cpu_to_le16(echo);
        elem = qed_chain_produce(p_chain);
@@ -266,27 +266,22 @@ static int qed_spq_hw_post(struct qed_hwfn *p_hwfn,
        *elem = p_ent->elem; /* struct assignment */
 
        /* send a doorbell on the slow hwfn session */
-       memset(&db, 0, sizeof(db));
-       SET_FIELD(db.params, CORE_DB_DATA_DEST, DB_DEST_XCM);
-       SET_FIELD(db.params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_SET);
-       SET_FIELD(db.params, CORE_DB_DATA_AGG_VAL_SEL,
-                 DQ_XCM_CORE_SPQ_PROD_CMD);
-       db.agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
-       db.spq_prod = cpu_to_le16(qed_chain_get_prod_idx(p_chain));
+       p_db_data->spq_prod = cpu_to_le16(qed_chain_get_prod_idx(p_chain));
 
        /* make sure the SPQE is updated before the doorbell */
        wmb();
 
-       DOORBELL(p_hwfn, qed_db_addr(p_spq->cid, DQ_DEMS_LEGACY), *(u32 *)&db);
+       DOORBELL(p_hwfn, p_spq->db_addr_offset, *(u32 *)p_db_data);
 
        /* make sure doorbell is rang */
        wmb();
 
        DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
                   "Doorbelled [0x%08x, CID 0x%08x] with Flags: %02x agg_params: %02x, prod: %04x\n",
-                  qed_db_addr(p_spq->cid, DQ_DEMS_LEGACY),
-                  p_spq->cid, db.params, db.agg_flags,
-                  qed_chain_get_prod_idx(p_chain));
+                  p_spq->db_addr_offset,
+                  p_spq->cid,
+                  p_db_data->params,
+                  p_db_data->agg_flags, qed_chain_get_prod_idx(p_chain));
 
        return 0;
 }
@@ -490,8 +485,11 @@ void qed_spq_setup(struct qed_hwfn *p_hwfn)
 {
        struct qed_spq *p_spq = p_hwfn->p_spq;
        struct qed_spq_entry *p_virt = NULL;
+       struct core_db_data *p_db_data;
+       void __iomem *db_addr;
        dma_addr_t p_phys = 0;
        u32 i, capacity;
+       int rc;
 
        INIT_LIST_HEAD(&p_spq->pending);
        INIT_LIST_HEAD(&p_spq->completion_pending);
@@ -528,6 +526,25 @@ void qed_spq_setup(struct qed_hwfn *p_hwfn)
 
        /* reset the chain itself */
        qed_chain_reset(&p_spq->chain);
+
+       /* Initialize the address/data of the SPQ doorbell */
+       p_spq->db_addr_offset = qed_db_addr(p_spq->cid, DQ_DEMS_LEGACY);
+       p_db_data = &p_spq->db_data;
+       memset(p_db_data, 0, sizeof(*p_db_data));
+       SET_FIELD(p_db_data->params, CORE_DB_DATA_DEST, DB_DEST_XCM);
+       SET_FIELD(p_db_data->params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_MAX);
+       SET_FIELD(p_db_data->params, CORE_DB_DATA_AGG_VAL_SEL,
+                 DQ_XCM_CORE_SPQ_PROD_CMD);
+       p_db_data->agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
+
+       /* Register the SPQ doorbell with the doorbell recovery mechanism */
+       db_addr = (void __iomem *)((u8 __iomem *)p_hwfn->doorbells +
+                                  p_spq->db_addr_offset);
+       rc = qed_db_recovery_add(p_hwfn->cdev, db_addr, &p_spq->db_data,
+                                DB_REC_WIDTH_32B, DB_REC_KERNEL);
+       if (rc)
+               DP_INFO(p_hwfn,
+                       "Failed to register the SPQ doorbell with the doorbell recovery mechanism\n");
 }
 
 int qed_spq_alloc(struct qed_hwfn *p_hwfn)
@@ -575,11 +592,17 @@ spq_allocate_fail:
 void qed_spq_free(struct qed_hwfn *p_hwfn)
 {
        struct qed_spq *p_spq = p_hwfn->p_spq;
+       void __iomem *db_addr;
        u32 capacity;
 
        if (!p_spq)
                return;
 
+       /* Delete the SPQ doorbell from the doorbell recovery mechanism */
+       db_addr = (void __iomem *)((u8 __iomem *)p_hwfn->doorbells +
+                                  p_spq->db_addr_offset);
+       qed_db_recovery_del(p_hwfn->cdev, db_addr, &p_spq->db_data);
+
        if (p_spq->p_virt) {
                capacity = qed_chain_get_capacity(&p_spq->chain);
                dma_free_coherent(&p_hwfn->cdev->pdev->dev,
index de98a97..613249d 100644 (file)
@@ -168,6 +168,13 @@ struct qede_ptp;
 
 #define QEDE_RFS_MAX_FLTR      256
 
+enum qede_flags_bit {
+       QEDE_FLAGS_IS_VF = 0,
+       QEDE_FLAGS_LINK_REQUESTED,
+       QEDE_FLAGS_PTP_TX_IN_PRORGESS,
+       QEDE_FLAGS_TX_TIMESTAMPING_EN
+};
+
 struct qede_dev {
        struct qed_dev                  *cdev;
        struct net_device               *ndev;
@@ -177,10 +184,7 @@ struct qede_dev {
        u8                              dp_level;
 
        unsigned long flags;
-#define QEDE_FLAG_IS_VF                        BIT(0)
-#define IS_VF(edev)    (!!((edev)->flags & QEDE_FLAG_IS_VF))
-#define QEDE_TX_TIMESTAMPING_EN                BIT(1)
-#define QEDE_FLAGS_PTP_TX_IN_PRORGESS  BIT(2)
+#define IS_VF(edev)    (test_bit(QEDE_FLAGS_IS_VF, &(edev)->flags))
 
        const struct qed_eth_ops        *ops;
        struct qede_ptp                 *ptp;
@@ -377,6 +381,7 @@ struct qede_tx_queue {
 
        u64 xmit_pkts;
        u64 stopped_cnt;
+       u64 tx_mem_alloc_err;
 
        __le16 *hw_cons_ptr;
 
index 8cbbd62..16331c6 100644 (file)
@@ -73,6 +73,7 @@ static const struct {
 } qede_tqstats_arr[] = {
        QEDE_TQSTAT(xmit_pkts),
        QEDE_TQSTAT(stopped_cnt),
+       QEDE_TQSTAT(tx_mem_alloc_err),
 };
 
 #define QEDE_STAT_OFFSET(stat_name, type, base) \
index 1a78027..bdf816f 100644 (file)
@@ -1466,8 +1466,8 @@ netdev_tx_t qede_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 #if ((MAX_SKB_FRAGS + 2) > ETH_TX_MAX_BDS_PER_NON_LSO_PACKET)
        if (qede_pkt_req_lin(skb, xmit_type)) {
                if (skb_linearize(skb)) {
-                       DP_NOTICE(edev,
-                                 "SKB linearization failed - silently dropping this SKB\n");
+                       txq->tx_mem_alloc_err++;
+
                        dev_kfree_skb_any(skb);
                        return NETDEV_TX_OK;
                }
index 46d0f2e..5a74fcb 100644 (file)
@@ -1086,7 +1086,7 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level,
        }
 
        if (is_vf)
-               edev->flags |= QEDE_FLAG_IS_VF;
+               set_bit(QEDE_FLAGS_IS_VF, &edev->flags);
 
        qede_init_ndev(edev);
 
@@ -1774,6 +1774,10 @@ static int qede_drain_txq(struct qede_dev *edev,
 static int qede_stop_txq(struct qede_dev *edev,
                         struct qede_tx_queue *txq, int rss_id)
 {
+       /* delete doorbell from doorbell recovery mechanism */
+       edev->ops->common->db_recovery_del(edev->cdev, txq->doorbell_addr,
+                                          &txq->tx_db);
+
        return edev->ops->q_tx_stop(edev->cdev, rss_id, txq->handle);
 }
 
@@ -1910,6 +1914,11 @@ static int qede_start_txq(struct qede_dev *edev,
                  DQ_XCM_ETH_TX_BD_PROD_CMD);
        txq->tx_db.data.agg_flags = DQ_XCM_ETH_DQ_CF_CMD;
 
+       /* register doorbell with doorbell recovery mechanism */
+       rc = edev->ops->common->db_recovery_add(edev->cdev, txq->doorbell_addr,
+                                               &txq->tx_db, DB_REC_WIDTH_32B,
+                                               DB_REC_KERNEL);
+
        return rc;
 }
 
@@ -2057,6 +2066,8 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode,
        if (!is_locked)
                __qede_lock(edev);
 
+       clear_bit(QEDE_FLAGS_LINK_REQUESTED, &edev->flags);
+
        edev->state = QEDE_STATE_CLOSED;
 
        qede_rdma_dev_event_close(edev);
@@ -2163,6 +2174,8 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode,
        /* Program un-configured VLANs */
        qede_configure_vlan_filters(edev);
 
+       set_bit(QEDE_FLAGS_LINK_REQUESTED, &edev->flags);
+
        /* Ask for link-up using current configuration */
        memset(&link_params, 0, sizeof(link_params));
        link_params.link_up = true;
@@ -2258,8 +2271,8 @@ static void qede_link_update(void *dev, struct qed_link_output *link)
 {
        struct qede_dev *edev = dev;
 
-       if (!netif_running(edev->ndev)) {
-               DP_VERBOSE(edev, NETIF_MSG_LINK, "Interface is not running\n");
+       if (!test_bit(QEDE_FLAGS_LINK_REQUESTED, &edev->flags)) {
+               DP_VERBOSE(edev, NETIF_MSG_LINK, "Interface is not ready\n");
                return;
        }
 
index 013ff56..5f3f42a 100644 (file)
@@ -223,12 +223,12 @@ static int qede_ptp_cfg_filters(struct qede_dev *edev)
 
        switch (ptp->tx_type) {
        case HWTSTAMP_TX_ON:
-               edev->flags |= QEDE_TX_TIMESTAMPING_EN;
+               set_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags);
                tx_type = QED_PTP_HWTSTAMP_TX_ON;
                break;
 
        case HWTSTAMP_TX_OFF:
-               edev->flags &= ~QEDE_TX_TIMESTAMPING_EN;
+               clear_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags);
                tx_type = QED_PTP_HWTSTAMP_TX_OFF;
                break;
 
@@ -518,7 +518,7 @@ void qede_ptp_tx_ts(struct qede_dev *edev, struct sk_buff *skb)
        if (test_and_set_bit_lock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags))
                return;
 
-       if (unlikely(!(edev->flags & QEDE_TX_TIMESTAMPING_EN))) {
+       if (unlikely(!test_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags))) {
                DP_NOTICE(edev,
                          "Tx timestamping was not enabled, this packet will not be timestamped\n");
        } else if (unlikely(ptp->tx_skb)) {
index d42ba22..16d0479 100644 (file)
@@ -2993,10 +2993,8 @@ int qlcnic_check_temp(struct qlcnic_adapter *adapter)
 static inline void dump_tx_ring_desc(struct qlcnic_host_tx_ring *tx_ring)
 {
        int i;
-       struct cmd_desc_type0 *tx_desc_info;
 
        for (i = 0; i < tx_ring->num_desc; i++) {
-               tx_desc_info = &tx_ring->desc_head[i];
                pr_info("TX Desc: %d\n", i);
                print_hex_dump(KERN_INFO, "TX: ", DUMP_PREFIX_OFFSET, 16, 1,
                               &tx_ring->desc_head[i],
@@ -4008,19 +4006,12 @@ int qlcnic_validate_rings(struct qlcnic_adapter *adapter, __u32 ring_cnt,
                          int queue_type)
 {
        struct net_device *netdev = adapter->netdev;
-       u8 max_hw_rings = 0;
        char buf[8];
-       int cur_rings;
 
-       if (queue_type == QLCNIC_RX_QUEUE) {
-               max_hw_rings = adapter->max_sds_rings;
-               cur_rings = adapter->drv_sds_rings;
+       if (queue_type == QLCNIC_RX_QUEUE)
                strcpy(buf, "SDS");
-       } else if (queue_type == QLCNIC_TX_QUEUE) {
-               max_hw_rings = adapter->max_tx_rings;
-               cur_rings = adapter->drv_tx_rings;
+       else
                strcpy(buf, "Tx");
-       }
 
        if (!is_power_of_2(ring_cnt)) {
                netdev_err(netdev, "%s rings value should be a power of 2\n",
index 50eaafa..af3b037 100644 (file)
@@ -1067,9 +1067,6 @@ static int qlcnic_sriov_pf_cfg_ip_cmd(struct qlcnic_bc_trans *trans,
        struct qlcnic_vf_info *vf = trans->vf;
        struct qlcnic_adapter *adapter = vf->adapter;
        int err = -EIO;
-       u8 op;
-
-       op =  cmd->req.arg[1] & 0xff;
 
        cmd->req.arg[1] |= vf->vp->handle << 16;
        cmd->req.arg[1] |= BIT_31;
@@ -1339,14 +1336,13 @@ static int qlcnic_sriov_pf_get_acl_cmd(struct qlcnic_bc_trans *trans,
 {
        struct qlcnic_vf_info *vf = trans->vf;
        struct qlcnic_vport *vp = vf->vp;
-       u8 cmd_op, mode = vp->vlan_mode;
+       u8 mode = vp->vlan_mode;
        struct qlcnic_adapter *adapter;
        struct qlcnic_sriov *sriov;
 
        adapter = vf->adapter;
        sriov = adapter->ahw->sriov;
 
-       cmd_op = trans->req_hdr->cmd_op;
        cmd->rsp.arg[0] |= 1 << 25;
 
        /* For 84xx adapter in case of PVID , PFD should send vlan mode as
index 1450f38..bcb890b 100644 (file)
@@ -126,19 +126,7 @@ qcaspi_info_show(struct seq_file *s, void *what)
 
        return 0;
 }
-
-static int
-qcaspi_info_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, qcaspi_info_show, inode->i_private);
-}
-
-static const struct file_operations qcaspi_info_ops = {
-       .open = qcaspi_info_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(qcaspi_info);
 
 void
 qcaspi_init_device_debugfs(struct qcaspi *qca)
@@ -154,7 +142,7 @@ qcaspi_init_device_debugfs(struct qcaspi *qca)
                return;
        }
        debugfs_create_file("info", S_IFREG | 0444, device_root, qca,
-                           &qcaspi_info_ops);
+                           &qcaspi_info_fops);
 }
 
 void
index 5f4e447..b8bbee6 100644 (file)
@@ -301,10 +301,13 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
        struct rmnet_port *port;
        u16 mux_id;
 
+       if (!dev)
+               return -ENODEV;
+
        real_dev = __dev_get_by_index(dev_net(dev),
                                      nla_get_u32(tb[IFLA_LINK]));
 
-       if (!real_dev || !dev || !rmnet_is_real_dev_registered(real_dev))
+       if (!real_dev || !rmnet_is_real_dev_registered(real_dev))
                return -ENODEV;
 
        port = rmnet_get_port_rtnl(real_dev);
index 3ee8ae9..f6cf59a 100644 (file)
@@ -20,17 +20,12 @@ static u8 rmnet_map_do_flow_control(struct sk_buff *skb,
                                    struct rmnet_port *port,
                                    int enable)
 {
-       struct rmnet_map_control_command *cmd;
        struct rmnet_endpoint *ep;
        struct net_device *vnd;
-       u16 ip_family;
-       u16 fc_seq;
-       u32 qos_id;
        u8 mux_id;
        int r;
 
        mux_id = RMNET_MAP_GET_MUX_ID(skb);
-       cmd = RMNET_MAP_GET_CMD_START(skb);
 
        if (mux_id >= RMNET_MAX_LOGICAL_EP) {
                kfree_skb(skb);
@@ -45,10 +40,6 @@ static u8 rmnet_map_do_flow_control(struct sk_buff *skb,
 
        vnd = ep->egress_dev;
 
-       ip_family = cmd->flow_control.ip_family;
-       fc_seq = ntohs(cmd->flow_control.flow_control_seq_num);
-       qos_id = ntohl(cmd->flow_control.qos_id);
-
        /* Ignore the ip family and pass the sequence number for both v4 and v6
         * sequence. User space does not support creating dedicated flows for
         * the 2 protocols
index 81045df..44f6e48 100644 (file)
@@ -571,6 +571,7 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance)
        struct cp_private *cp;
        int handled = 0;
        u16 status;
+       u16 mask;
 
        if (unlikely(dev == NULL))
                return IRQ_NONE;
@@ -578,6 +579,10 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance)
 
        spin_lock(&cp->lock);
 
+       mask = cpr16(IntrMask);
+       if (!mask)
+               goto out_unlock;
+
        status = cpr16(IntrStatus);
        if (!status || (status == 0xFFFF))
                goto out_unlock;
index b3010cc..5b0c32b 100644 (file)
 #define R8169_MSG_DEFAULT \
        (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN)
 
-#define TX_SLOTS_AVAIL(tp) \
-       (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx)
-
-/* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */
-#define TX_FRAGS_READY_FOR(tp,nr_frags) \
-       (TX_SLOTS_AVAIL(tp) >= (nr_frags + 1))
-
 /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
    The RTL chips use a 64 element hash table based on the Ethernet CRC. */
 static const int multicast_filter_limit = 32;
@@ -212,24 +205,24 @@ enum cfg_version {
 };
 
 static const struct pci_device_id rtl8169_pci_tbl[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8129), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8136), 0, 0, RTL_CFG_2 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8161), 0, 0, RTL_CFG_1 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8167), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8168), 0, 0, RTL_CFG_1 },
-       { PCI_DEVICE(PCI_VENDOR_ID_NCUBE,       0x8168), 0, 0, RTL_CFG_1 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8169), 0, 0, RTL_CFG_0 },
-       { PCI_VENDOR_ID_DLINK,                  0x4300,
-               PCI_VENDOR_ID_DLINK, 0x4b10,             0, 0, RTL_CFG_1 },
-       { PCI_DEVICE(PCI_VENDOR_ID_DLINK,       0x4300), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_DLINK,       0x4302), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_AT,          0xc107), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_USR,         0x0116), 0, 0, RTL_CFG_0 },
+       { PCI_VDEVICE(REALTEK,  0x8129), RTL_CFG_0 },
+       { PCI_VDEVICE(REALTEK,  0x8136), RTL_CFG_2 },
+       { PCI_VDEVICE(REALTEK,  0x8161), RTL_CFG_1 },
+       { PCI_VDEVICE(REALTEK,  0x8167), RTL_CFG_0 },
+       { PCI_VDEVICE(REALTEK,  0x8168), RTL_CFG_1 },
+       { PCI_VDEVICE(NCUBE,    0x8168), RTL_CFG_1 },
+       { PCI_VDEVICE(REALTEK,  0x8169), RTL_CFG_0 },
+       { PCI_VENDOR_ID_DLINK,  0x4300,
+               PCI_VENDOR_ID_DLINK, 0x4b10, 0, 0, RTL_CFG_1 },
+       { PCI_VDEVICE(DLINK,    0x4300), RTL_CFG_0 },
+       { PCI_VDEVICE(DLINK,    0x4302), RTL_CFG_0 },
+       { PCI_VDEVICE(AT,       0xc107), RTL_CFG_0 },
+       { PCI_VDEVICE(USR,      0x0116), RTL_CFG_0 },
        { PCI_VENDOR_ID_LINKSYS,                0x1032,
                PCI_ANY_ID, 0x0024, 0, 0, RTL_CFG_0 },
        { 0x0001,                               0x8168,
                PCI_ANY_ID, 0x2410, 0, 0, RTL_CFG_2 },
-       {0,},
+       {}
 };
 
 MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl);
@@ -603,7 +596,6 @@ struct RxDesc {
 struct ring_info {
        struct sk_buff  *skb;
        u32             len;
-       u8              __pad[sizeof(void *) - sizeof(u32)];
 };
 
 struct rtl8169_counters {
@@ -661,7 +653,7 @@ struct rtl8169_private {
        struct ring_info tx_skb[NUM_TX_DESC];   /* Tx data buffers */
        u16 cp_cmd;
 
-       u16 event_slow;
+       u16 irq_mask;
        const struct rtl_coalesce_info *coalesce_info;
        struct clk *clk;
 
@@ -1102,23 +1094,6 @@ static u32 r8168ep_ocp_read(struct rtl8169_private *tp, u8 mask, u16 reg)
        return rtl_eri_read(tp, reg, ERIAR_OOB);
 }
 
-static u32 ocp_read(struct rtl8169_private *tp, u8 mask, u16 reg)
-{
-       switch (tp->mac_version) {
-       case RTL_GIGA_MAC_VER_27:
-       case RTL_GIGA_MAC_VER_28:
-       case RTL_GIGA_MAC_VER_31:
-               return r8168dp_ocp_read(tp, mask, reg);
-       case RTL_GIGA_MAC_VER_49:
-       case RTL_GIGA_MAC_VER_50:
-       case RTL_GIGA_MAC_VER_51:
-               return r8168ep_ocp_read(tp, mask, reg);
-       default:
-               BUG();
-               return ~0;
-       }
-}
-
 static void r8168dp_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg,
                              u32 data)
 {
@@ -1134,30 +1109,11 @@ static void r8168ep_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg,
                      data, ERIAR_OOB);
 }
 
-static void ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg, u32 data)
-{
-       switch (tp->mac_version) {
-       case RTL_GIGA_MAC_VER_27:
-       case RTL_GIGA_MAC_VER_28:
-       case RTL_GIGA_MAC_VER_31:
-               r8168dp_ocp_write(tp, mask, reg, data);
-               break;
-       case RTL_GIGA_MAC_VER_49:
-       case RTL_GIGA_MAC_VER_50:
-       case RTL_GIGA_MAC_VER_51:
-               r8168ep_ocp_write(tp, mask, reg, data);
-               break;
-       default:
-               BUG();
-               break;
-       }
-}
-
-static void rtl8168_oob_notify(struct rtl8169_private *tp, u8 cmd)
+static void r8168dp_oob_notify(struct rtl8169_private *tp, u8 cmd)
 {
        rtl_eri_write(tp, 0xe8, ERIAR_MASK_0001, cmd, ERIAR_EXGMAC);
 
-       ocp_write(tp, 0x1, 0x30, 0x00000001);
+       r8168dp_ocp_write(tp, 0x1, 0x30, 0x00000001);
 }
 
 #define OOB_CMD_RESET          0x00
@@ -1169,18 +1125,18 @@ static u16 rtl8168_get_ocp_reg(struct rtl8169_private *tp)
        return (tp->mac_version == RTL_GIGA_MAC_VER_31) ? 0xb8 : 0x10;
 }
 
-DECLARE_RTL_COND(rtl_ocp_read_cond)
+DECLARE_RTL_COND(rtl_dp_ocp_read_cond)
 {
        u16 reg;
 
        reg = rtl8168_get_ocp_reg(tp);
 
-       return ocp_read(tp, 0x0f, reg) & 0x00000800;
+       return r8168dp_ocp_read(tp, 0x0f, reg) & 0x00000800;
 }
 
 DECLARE_RTL_COND(rtl_ep_ocp_read_cond)
 {
-       return ocp_read(tp, 0x0f, 0x124) & 0x00000001;
+       return r8168ep_ocp_read(tp, 0x0f, 0x124) & 0x00000001;
 }
 
 DECLARE_RTL_COND(rtl_ocp_tx_cond)
@@ -1198,14 +1154,15 @@ static void rtl8168ep_stop_cmac(struct rtl8169_private *tp)
 
 static void rtl8168dp_driver_start(struct rtl8169_private *tp)
 {
-       rtl8168_oob_notify(tp, OOB_CMD_DRIVER_START);
-       rtl_msleep_loop_wait_high(tp, &rtl_ocp_read_cond, 10, 10);
+       r8168dp_oob_notify(tp, OOB_CMD_DRIVER_START);
+       rtl_msleep_loop_wait_high(tp, &rtl_dp_ocp_read_cond, 10, 10);
 }
 
 static void rtl8168ep_driver_start(struct rtl8169_private *tp)
 {
-       ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START);
-       ocp_write(tp, 0x01, 0x30, ocp_read(tp, 0x01, 0x30) | 0x01);
+       r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START);
+       r8168ep_ocp_write(tp, 0x01, 0x30,
+                         r8168ep_ocp_read(tp, 0x01, 0x30) | 0x01);
        rtl_msleep_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10, 10);
 }
 
@@ -1230,15 +1187,16 @@ static void rtl8168_driver_start(struct rtl8169_private *tp)
 
 static void rtl8168dp_driver_stop(struct rtl8169_private *tp)
 {
-       rtl8168_oob_notify(tp, OOB_CMD_DRIVER_STOP);
-       rtl_msleep_loop_wait_low(tp, &rtl_ocp_read_cond, 10, 10);
+       r8168dp_oob_notify(tp, OOB_CMD_DRIVER_STOP);
+       rtl_msleep_loop_wait_low(tp, &rtl_dp_ocp_read_cond, 10, 10);
 }
 
 static void rtl8168ep_driver_stop(struct rtl8169_private *tp)
 {
        rtl8168ep_stop_cmac(tp);
-       ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_STOP);
-       ocp_write(tp, 0x01, 0x30, ocp_read(tp, 0x01, 0x30) | 0x01);
+       r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_STOP);
+       r8168ep_ocp_write(tp, 0x01, 0x30,
+                         r8168ep_ocp_read(tp, 0x01, 0x30) | 0x01);
        rtl_msleep_loop_wait_low(tp, &rtl_ep_ocp_read_cond, 10, 10);
 }
 
@@ -1265,12 +1223,12 @@ static bool r8168dp_check_dash(struct rtl8169_private *tp)
 {
        u16 reg = rtl8168_get_ocp_reg(tp);
 
-       return !!(ocp_read(tp, 0x0f, reg) & 0x00008000);
+       return !!(r8168dp_ocp_read(tp, 0x0f, reg) & 0x00008000);
 }
 
 static bool r8168ep_check_dash(struct rtl8169_private *tp)
 {
-       return !!(ocp_read(tp, 0x0f, 0x128) & 0x00000001);
+       return !!(r8168ep_ocp_read(tp, 0x0f, 0x128) & 0x00000001);
 }
 
 static bool r8168_check_dash(struct rtl8169_private *tp)
@@ -1325,27 +1283,20 @@ static u16 rtl_get_events(struct rtl8169_private *tp)
 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();
-}
-
-static void rtl_irq_enable(struct rtl8169_private *tp, u16 bits)
-{
-       RTL_W16(tp, IntrMask, bits);
 }
 
 #define RTL_EVENT_NAPI_RX      (RxOK | RxErr)
 #define RTL_EVENT_NAPI_TX      (TxOK | TxErr)
 #define RTL_EVENT_NAPI         (RTL_EVENT_NAPI_RX | RTL_EVENT_NAPI_TX)
 
-static void rtl_irq_enable_all(struct rtl8169_private *tp)
+static void rtl_irq_enable(struct rtl8169_private *tp)
 {
-       rtl_irq_enable(tp, RTL_EVENT_NAPI | tp->event_slow);
+       RTL_W16(tp, IntrMask, tp->irq_mask);
 }
 
 static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
@@ -2051,8 +2002,7 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
        .set_link_ksettings     = phy_ethtool_set_link_ksettings,
 };
 
-static void rtl8169_get_mac_version(struct rtl8169_private *tp,
-                                   u8 default_version)
+static void rtl8169_get_mac_version(struct rtl8169_private *tp)
 {
        /*
         * The driver currently handles the 8168Bf and the 8168Be identically
@@ -2066,120 +2016,107 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp,
         * (RTL_R32(tp, TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec
         */
        static const struct rtl_mac_info {
-               u32 mask;
-               u32 val;
-               int mac_version;
+               u16 mask;
+               u16 val;
+               u16 mac_version;
        } mac_info[] = {
                /* 8168EP family. */
-               { 0x7cf00000, 0x50200000,       RTL_GIGA_MAC_VER_51 },
-               { 0x7cf00000, 0x50100000,       RTL_GIGA_MAC_VER_50 },
-               { 0x7cf00000, 0x50000000,       RTL_GIGA_MAC_VER_49 },
+               { 0x7cf, 0x502, RTL_GIGA_MAC_VER_51 },
+               { 0x7cf, 0x501, RTL_GIGA_MAC_VER_50 },
+               { 0x7cf, 0x500, RTL_GIGA_MAC_VER_49 },
 
                /* 8168H family. */
-               { 0x7cf00000, 0x54100000,       RTL_GIGA_MAC_VER_46 },
-               { 0x7cf00000, 0x54000000,       RTL_GIGA_MAC_VER_45 },
+               { 0x7cf, 0x541, RTL_GIGA_MAC_VER_46 },
+               { 0x7cf, 0x540, RTL_GIGA_MAC_VER_45 },
 
                /* 8168G family. */
-               { 0x7cf00000, 0x5c800000,       RTL_GIGA_MAC_VER_44 },
-               { 0x7cf00000, 0x50900000,       RTL_GIGA_MAC_VER_42 },
-               { 0x7cf00000, 0x4c100000,       RTL_GIGA_MAC_VER_41 },
-               { 0x7cf00000, 0x4c000000,       RTL_GIGA_MAC_VER_40 },
+               { 0x7cf, 0x5c8, RTL_GIGA_MAC_VER_44 },
+               { 0x7cf, 0x509, RTL_GIGA_MAC_VER_42 },
+               { 0x7cf, 0x4c1, RTL_GIGA_MAC_VER_41 },
+               { 0x7cf, 0x4c0, RTL_GIGA_MAC_VER_40 },
 
                /* 8168F family. */
-               { 0x7c800000, 0x48800000,       RTL_GIGA_MAC_VER_38 },
-               { 0x7cf00000, 0x48100000,       RTL_GIGA_MAC_VER_36 },
-               { 0x7cf00000, 0x48000000,       RTL_GIGA_MAC_VER_35 },
+               { 0x7c8, 0x488, RTL_GIGA_MAC_VER_38 },
+               { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36 },
+               { 0x7cf, 0x480, RTL_GIGA_MAC_VER_35 },
 
                /* 8168E family. */
-               { 0x7c800000, 0x2c800000,       RTL_GIGA_MAC_VER_34 },
-               { 0x7cf00000, 0x2c100000,       RTL_GIGA_MAC_VER_32 },
-               { 0x7c800000, 0x2c000000,       RTL_GIGA_MAC_VER_33 },
+               { 0x7c8, 0x2c8, RTL_GIGA_MAC_VER_34 },
+               { 0x7cf, 0x2c1, RTL_GIGA_MAC_VER_32 },
+               { 0x7c8, 0x2c0, RTL_GIGA_MAC_VER_33 },
 
                /* 8168D family. */
-               { 0x7cf00000, 0x28100000,       RTL_GIGA_MAC_VER_25 },
-               { 0x7c800000, 0x28000000,       RTL_GIGA_MAC_VER_26 },
+               { 0x7cf, 0x281, RTL_GIGA_MAC_VER_25 },
+               { 0x7c8, 0x280, RTL_GIGA_MAC_VER_26 },
 
                /* 8168DP family. */
-               { 0x7cf00000, 0x28800000,       RTL_GIGA_MAC_VER_27 },
-               { 0x7cf00000, 0x28a00000,       RTL_GIGA_MAC_VER_28 },
-               { 0x7cf00000, 0x28b00000,       RTL_GIGA_MAC_VER_31 },
+               { 0x7cf, 0x288, RTL_GIGA_MAC_VER_27 },
+               { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28 },
+               { 0x7cf, 0x28b, RTL_GIGA_MAC_VER_31 },
 
                /* 8168C family. */
-               { 0x7cf00000, 0x3c900000,       RTL_GIGA_MAC_VER_23 },
-               { 0x7cf00000, 0x3c800000,       RTL_GIGA_MAC_VER_18 },
-               { 0x7c800000, 0x3c800000,       RTL_GIGA_MAC_VER_24 },
-               { 0x7cf00000, 0x3c000000,       RTL_GIGA_MAC_VER_19 },
-               { 0x7cf00000, 0x3c200000,       RTL_GIGA_MAC_VER_20 },
-               { 0x7cf00000, 0x3c300000,       RTL_GIGA_MAC_VER_21 },
-               { 0x7c800000, 0x3c000000,       RTL_GIGA_MAC_VER_22 },
+               { 0x7cf, 0x3c9, RTL_GIGA_MAC_VER_23 },
+               { 0x7cf, 0x3c8, RTL_GIGA_MAC_VER_18 },
+               { 0x7c8, 0x3c8, RTL_GIGA_MAC_VER_24 },
+               { 0x7cf, 0x3c0, RTL_GIGA_MAC_VER_19 },
+               { 0x7cf, 0x3c2, RTL_GIGA_MAC_VER_20 },
+               { 0x7cf, 0x3c3, RTL_GIGA_MAC_VER_21 },
+               { 0x7c8, 0x3c0, RTL_GIGA_MAC_VER_22 },
 
                /* 8168B family. */
-               { 0x7cf00000, 0x38000000,       RTL_GIGA_MAC_VER_12 },
-               { 0x7c800000, 0x38000000,       RTL_GIGA_MAC_VER_17 },
-               { 0x7c800000, 0x30000000,       RTL_GIGA_MAC_VER_11 },
+               { 0x7cf, 0x380, RTL_GIGA_MAC_VER_12 },
+               { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17 },
+               { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 },
 
                /* 8101 family. */
-               { 0x7c800000, 0x44800000,       RTL_GIGA_MAC_VER_39 },
-               { 0x7c800000, 0x44000000,       RTL_GIGA_MAC_VER_37 },
-               { 0x7cf00000, 0x40900000,       RTL_GIGA_MAC_VER_29 },
-               { 0x7c800000, 0x40800000,       RTL_GIGA_MAC_VER_30 },
-               { 0x7cf00000, 0x34900000,       RTL_GIGA_MAC_VER_08 },
-               { 0x7cf00000, 0x24900000,       RTL_GIGA_MAC_VER_08 },
-               { 0x7cf00000, 0x34800000,       RTL_GIGA_MAC_VER_07 },
-               { 0x7cf00000, 0x24800000,       RTL_GIGA_MAC_VER_07 },
-               { 0x7cf00000, 0x34000000,       RTL_GIGA_MAC_VER_13 },
-               { 0x7cf00000, 0x34300000,       RTL_GIGA_MAC_VER_10 },
-               { 0x7cf00000, 0x34200000,       RTL_GIGA_MAC_VER_16 },
-               { 0x7c800000, 0x34800000,       RTL_GIGA_MAC_VER_09 },
-               { 0x7c800000, 0x24800000,       RTL_GIGA_MAC_VER_09 },
-               { 0x7c800000, 0x34000000,       RTL_GIGA_MAC_VER_16 },
+               { 0x7c8, 0x448, RTL_GIGA_MAC_VER_39 },
+               { 0x7c8, 0x440, RTL_GIGA_MAC_VER_37 },
+               { 0x7cf, 0x409, RTL_GIGA_MAC_VER_29 },
+               { 0x7c8, 0x408, RTL_GIGA_MAC_VER_30 },
+               { 0x7cf, 0x349, RTL_GIGA_MAC_VER_08 },
+               { 0x7cf, 0x249, RTL_GIGA_MAC_VER_08 },
+               { 0x7cf, 0x348, RTL_GIGA_MAC_VER_07 },
+               { 0x7cf, 0x248, RTL_GIGA_MAC_VER_07 },
+               { 0x7cf, 0x340, RTL_GIGA_MAC_VER_13 },
+               { 0x7cf, 0x343, RTL_GIGA_MAC_VER_10 },
+               { 0x7cf, 0x342, RTL_GIGA_MAC_VER_16 },
+               { 0x7c8, 0x348, RTL_GIGA_MAC_VER_09 },
+               { 0x7c8, 0x248, RTL_GIGA_MAC_VER_09 },
+               { 0x7c8, 0x340, RTL_GIGA_MAC_VER_16 },
                /* FIXME: where did these entries come from ? -- FR */
-               { 0xfc800000, 0x38800000,       RTL_GIGA_MAC_VER_15 },
-               { 0xfc800000, 0x30800000,       RTL_GIGA_MAC_VER_14 },
+               { 0xfc8, 0x388, RTL_GIGA_MAC_VER_15 },
+               { 0xfc8, 0x308, RTL_GIGA_MAC_VER_14 },
 
                /* 8110 family. */
-               { 0xfc800000, 0x98000000,       RTL_GIGA_MAC_VER_06 },
-               { 0xfc800000, 0x18000000,       RTL_GIGA_MAC_VER_05 },
-               { 0xfc800000, 0x10000000,       RTL_GIGA_MAC_VER_04 },
-               { 0xfc800000, 0x04000000,       RTL_GIGA_MAC_VER_03 },
-               { 0xfc800000, 0x00800000,       RTL_GIGA_MAC_VER_02 },
-               { 0xfc800000, 0x00000000,       RTL_GIGA_MAC_VER_01 },
+               { 0xfc8, 0x980, RTL_GIGA_MAC_VER_06 },
+               { 0xfc8, 0x180, RTL_GIGA_MAC_VER_05 },
+               { 0xfc8, 0x100, RTL_GIGA_MAC_VER_04 },
+               { 0xfc8, 0x040, RTL_GIGA_MAC_VER_03 },
+               { 0xfc8, 0x008, RTL_GIGA_MAC_VER_02 },
+               { 0xfc8, 0x000, RTL_GIGA_MAC_VER_01 },
 
                /* Catch-all */
-               { 0x00000000, 0x00000000,       RTL_GIGA_MAC_NONE   }
+               { 0x000, 0x000, RTL_GIGA_MAC_NONE   }
        };
        const struct rtl_mac_info *p = mac_info;
-       u32 reg;
+       u16 reg = RTL_R32(tp, TxConfig) >> 20;
 
-       reg = RTL_R32(tp, TxConfig);
        while ((reg & p->mask) != p->val)
                p++;
        tp->mac_version = p->mac_version;
 
        if (tp->mac_version == RTL_GIGA_MAC_NONE) {
-               dev_notice(tp_to_dev(tp),
-                          "unknown MAC, using family default\n");
-               tp->mac_version = default_version;
-       } else if (tp->mac_version == RTL_GIGA_MAC_VER_42) {
-               tp->mac_version = tp->supports_gmii ?
-                                 RTL_GIGA_MAC_VER_42 :
-                                 RTL_GIGA_MAC_VER_43;
-       } else if (tp->mac_version == RTL_GIGA_MAC_VER_45) {
-               tp->mac_version = tp->supports_gmii ?
-                                 RTL_GIGA_MAC_VER_45 :
-                                 RTL_GIGA_MAC_VER_47;
-       } else if (tp->mac_version == RTL_GIGA_MAC_VER_46) {
-               tp->mac_version = tp->supports_gmii ?
-                                 RTL_GIGA_MAC_VER_46 :
-                                 RTL_GIGA_MAC_VER_48;
+               dev_err(tp_to_dev(tp), "unknown chip XID %03x\n", reg & 0xfcf);
+       } else if (!tp->supports_gmii) {
+               if (tp->mac_version == RTL_GIGA_MAC_VER_42)
+                       tp->mac_version = RTL_GIGA_MAC_VER_43;
+               else if (tp->mac_version == RTL_GIGA_MAC_VER_45)
+                       tp->mac_version = RTL_GIGA_MAC_VER_47;
+               else if (tp->mac_version == RTL_GIGA_MAC_VER_46)
+                       tp->mac_version = RTL_GIGA_MAC_VER_48;
        }
 }
 
-static void rtl8169_print_mac_version(struct rtl8169_private *tp)
-{
-       netif_dbg(tp, drv, tp->dev, "mac_version = 0x%02x\n", tp->mac_version);
-}
-
 struct phy_reg {
        u16 reg;
        u16 val;
@@ -3902,8 +3839,6 @@ static void rtl_hw_phy_config(struct net_device *dev)
 {
        struct rtl8169_private *tp = netdev_priv(dev);
 
-       rtl8169_print_mac_version(tp);
-
        switch (tp->mac_version) {
        case RTL_GIGA_MAC_VER_01:
                break;
@@ -4643,7 +4578,7 @@ static void rtl_hw_start(struct  rtl8169_private *tp)
        rtl_set_rx_mode(tp->dev);
        /* no early-rx interrupts */
        RTL_W16(tp, MultiIntr, RTL_R16(tp, MultiIntr) & 0xf000);
-       rtl_irq_enable_all(tp);
+       rtl_irq_enable(tp);
 }
 
 static void rtl_hw_start_8169(struct rtl8169_private *tp)
@@ -5394,8 +5329,8 @@ static void rtl_hw_start_8168(struct rtl8169_private *tp)
 
        /* Work around for RxFIFO overflow. */
        if (tp->mac_version == RTL_GIGA_MAC_VER_11) {
-               tp->event_slow |= RxFIFOOver | PCSTimeout;
-               tp->event_slow &= ~RxOverflow;
+               tp->irq_mask |= RxFIFOOver;
+               tp->irq_mask &= ~RxOverflow;
        }
 
        switch (tp->mac_version) {
@@ -5632,7 +5567,7 @@ static void rtl_hw_start_8106(struct rtl8169_private *tp)
 static void rtl_hw_start_8101(struct rtl8169_private *tp)
 {
        if (tp->mac_version >= RTL_GIGA_MAC_VER_30)
-               tp->event_slow &= ~RxFIFOOver;
+               tp->irq_mask &= ~RxFIFOOver;
 
        if (tp->mac_version == RTL_GIGA_MAC_VER_13 ||
            tp->mac_version == RTL_GIGA_MAC_VER_16)
@@ -5888,6 +5823,16 @@ static void rtl8169_tx_timeout(struct net_device *dev)
        rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
 }
 
+static __le32 rtl8169_get_txd_opts1(u32 opts0, u32 len, unsigned int entry)
+{
+       u32 status = opts0 | len;
+
+       if (entry == NUM_TX_DESC - 1)
+               status |= RingEnd;
+
+       return cpu_to_le32(status);
+}
+
 static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
                              u32 *opts)
 {
@@ -5900,7 +5845,7 @@ static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
        for (cur_frag = 0; cur_frag < info->nr_frags; cur_frag++) {
                const skb_frag_t *frag = info->frags + cur_frag;
                dma_addr_t mapping;
-               u32 status, len;
+               u32 len;
                void *addr;
 
                entry = (entry + 1) % NUM_TX_DESC;
@@ -5916,11 +5861,7 @@ static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
                        goto err_out;
                }
 
-               /* Anti gcc 2.95.3 bugware (sic) */
-               status = opts[0] | len |
-                       (RingEnd * !((entry + 1) % NUM_TX_DESC));
-
-               txd->opts1 = cpu_to_le32(status);
+               txd->opts1 = rtl8169_get_txd_opts1(opts[0], len, entry);
                txd->opts2 = cpu_to_le32(opts[1]);
                txd->addr = cpu_to_le64(mapping);
 
@@ -6108,6 +6049,15 @@ static bool rtl8169_tso_csum_v2(struct rtl8169_private *tp,
        return true;
 }
 
+static bool rtl_tx_slots_avail(struct rtl8169_private *tp,
+                              unsigned int nr_frags)
+{
+       unsigned int slots_avail = tp->dirty_tx + NUM_TX_DESC - tp->cur_tx;
+
+       /* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */
+       return slots_avail > nr_frags;
+}
+
 static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
                                      struct net_device *dev)
 {
@@ -6116,11 +6066,11 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
        struct TxDesc *txd = tp->TxDescArray + entry;
        struct device *d = tp_to_dev(tp);
        dma_addr_t mapping;
-       u32 status, len;
-       u32 opts[2];
+       u32 opts[2], len;
+       bool stop_queue;
        int frags;
 
-       if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) {
+       if (unlikely(!rtl_tx_slots_avail(tp, skb_shinfo(skb)->nr_frags))) {
                netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n");
                goto err_stop_0;
        }
@@ -6159,32 +6109,26 @@ 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 */
        dma_wmb();
 
-       /* Anti gcc 2.95.3 bugware (sic) */
-       status = opts[0] | len | (RingEnd * !((entry + 1) % NUM_TX_DESC));
-       txd->opts1 = cpu_to_le32(status);
+       txd->opts1 = rtl8169_get_txd_opts1(opts[0], len, entry);
 
        /* Force all memory writes to complete before notifying device */
        wmb();
 
        tp->cur_tx += frags + 1;
 
-       RTL_W8(tp, TxPoll, NPQ);
+       stop_queue = !rtl_tx_slots_avail(tp, MAX_SKB_FRAGS);
+       if (unlikely(stop_queue))
+               netif_stop_queue(dev);
 
-       mmiowb();
+       if (__netdev_sent_queue(dev, skb->len, skb->xmit_more))
+               RTL_W8(tp, TxPoll, NPQ);
 
-       if (!TX_FRAGS_READY_FOR(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);
+       if (unlikely(stop_queue)) {
                /* Sync with rtl_tx:
                 * - publish queue status and cur_tx ring index (write barrier)
                 * - refresh dirty_tx ring index (read barrier).
@@ -6193,7 +6137,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
                 * can't.
                 */
                smp_mb();
-               if (TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS))
+               if (rtl_tx_slots_avail(tp, MAX_SKB_FRAGS))
                        netif_wake_queue(dev);
        }
 
@@ -6257,7 +6201,8 @@ static void rtl8169_pcierr_interrupt(struct net_device *dev)
        rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
 }
 
-static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
+static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
+                  int budget)
 {
        unsigned int dirty_tx, tx_left, bytes_compl = 0, pkts_compl = 0;
 
@@ -6285,7 +6230,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
                if (status & LastFrag) {
                        pkts_compl++;
                        bytes_compl += tx_skb->skb->len;
-                       dev_consume_skb_any(tx_skb->skb);
+                       napi_consume_skb(tx_skb->skb, budget);
                        tx_skb->skb = NULL;
                }
                dirty_tx++;
@@ -6310,7 +6255,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
                 */
                smp_mb();
                if (netif_queue_stopped(dev) &&
-                   TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) {
+                   rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) {
                        netif_wake_queue(dev);
                }
                /*
@@ -6460,8 +6405,9 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
 {
        struct rtl8169_private *tp = dev_instance;
        u16 status = rtl_get_events(tp);
+       u16 irq_mask = RTL_R16(tp, IntrMask);
 
-       if (status == 0xffff || !(status & (RTL_EVENT_NAPI | tp->event_slow)))
+       if (status == 0xffff || !(status & irq_mask))
                return IRQ_NONE;
 
        if (unlikely(status & SYSErr)) {
@@ -6469,7 +6415,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
                goto out;
        }
 
-       if (status & LinkChg)
+       if (status & LinkChg && tp->dev->phydev)
                phy_mac_interrupt(tp->dev->phydev);
 
        if (unlikely(status & RxFIFOOver &&
@@ -6528,13 +6474,11 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
 
        work_done = rtl_rx(dev, tp, (u32) budget);
 
-       rtl_tx(dev, tp);
+       rtl_tx(dev, tp, budget);
 
        if (work_done < budget) {
                napi_complete_done(napi, work_done);
-
-               rtl_irq_enable_all(tp);
-               mmiowb();
+               rtl_irq_enable(tp);
        }
 
        return work_done;
@@ -6824,8 +6768,7 @@ static void rtl8169_net_suspend(struct net_device *dev)
 
 static int rtl8169_suspend(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rtl8169_private *tp = netdev_priv(dev);
 
        rtl8169_net_suspend(dev);
@@ -6855,8 +6798,7 @@ static void __rtl8169_resume(struct net_device *dev)
 
 static int rtl8169_resume(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rtl8169_private *tp = netdev_priv(dev);
 
        clk_prepare_enable(tp->clk);
@@ -6869,8 +6811,7 @@ static int rtl8169_resume(struct device *device)
 
 static int rtl8169_runtime_suspend(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rtl8169_private *tp = netdev_priv(dev);
 
        if (!tp->TxDescArray)
@@ -6891,8 +6832,7 @@ static int rtl8169_runtime_suspend(struct device *device)
 
 static int rtl8169_runtime_resume(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rtl8169_private *tp = netdev_priv(dev);
        rtl_rar_set(tp, dev->dev_addr);
 
@@ -6910,8 +6850,7 @@ static int rtl8169_runtime_resume(struct device *device)
 
 static int rtl8169_runtime_idle(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
 
        if (!netif_running(dev) || !netif_carrier_ok(dev))
                pm_schedule_suspend(device, 10000);
@@ -7023,31 +6962,26 @@ static const struct net_device_ops rtl_netdev_ops = {
 
 static const struct rtl_cfg_info {
        void (*hw_start)(struct rtl8169_private *tp);
-       u16 event_slow;
+       u16 irq_mask;
        unsigned int has_gmii:1;
        const struct rtl_coalesce_info *coalesce_info;
-       u8 default_ver;
 } rtl_cfg_infos [] = {
        [RTL_CFG_0] = {
                .hw_start       = rtl_hw_start_8169,
-               .event_slow     = SYSErr | LinkChg | RxOverflow | RxFIFOOver,
+               .irq_mask       = SYSErr | LinkChg | RxOverflow | RxFIFOOver,
                .has_gmii       = 1,
                .coalesce_info  = rtl_coalesce_info_8169,
-               .default_ver    = RTL_GIGA_MAC_VER_01,
        },
        [RTL_CFG_1] = {
                .hw_start       = rtl_hw_start_8168,
-               .event_slow     = SYSErr | LinkChg | RxOverflow,
+               .irq_mask       = LinkChg | RxOverflow,
                .has_gmii       = 1,
                .coalesce_info  = rtl_coalesce_info_8168_8136,
-               .default_ver    = RTL_GIGA_MAC_VER_11,
        },
        [RTL_CFG_2] = {
                .hw_start       = rtl_hw_start_8101,
-               .event_slow     = SYSErr | LinkChg | RxOverflow | RxFIFOOver |
-                                 PCSTimeout,
+               .irq_mask       = LinkChg | RxOverflow | RxFIFOOver,
                .coalesce_info  = rtl_coalesce_info_8168_8136,
-               .default_ver    = RTL_GIGA_MAC_VER_13,
        }
 };
 
@@ -7309,11 +7243,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        tp->mmio_addr = pcim_iomap_table(pdev)[region];
 
-       if (!pci_is_pcie(pdev))
-               dev_info(&pdev->dev, "not PCI Express\n");
-
        /* Identify chip attached to board */
-       rtl8169_get_mac_version(tp, cfg->default_ver);
+       rtl8169_get_mac_version(tp);
+       if (tp->mac_version == RTL_GIGA_MAC_NONE)
+               return -ENODEV;
 
        if (rtl_tbi_enabled(tp)) {
                dev_err(&pdev->dev, "TBI fiber mode not supported\n");
@@ -7351,8 +7284,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        rtl_init_mdio_ops(tp);
        rtl_init_jumbo_ops(tp);
 
-       rtl8169_print_mac_version(tp);
-
        chipset = tp->mac_version;
 
        rc = rtl_alloc_irq(tp);
@@ -7426,7 +7357,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->max_mtu = jumbo_max;
 
        tp->hw_start = cfg->hw_start;
-       tp->event_slow = cfg->event_slow;
+       tp->irq_mask = RTL_EVENT_NAPI | cfg->irq_mask;
        tp->coalesce_info = cfg->coalesce_info;
 
        tp->rtl_fw = RTL_FIRMWARE_UNKNOWN;
@@ -7450,9 +7381,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (rc)
                goto err_mdio_unregister;
 
-       netif_info(tp, probe, dev, "%s, %pM, XID %08x, IRQ %d\n",
+       netif_info(tp, probe, dev, "%s, %pM, XID %03x, IRQ %d\n",
                   rtl_chip_infos[chipset].name, dev->dev_addr,
-                  (u32)(RTL_R32(tp, TxConfig) & 0xfcf0f8ff),
+                  (RTL_R32(tp, TxConfig) >> 20) & 0xfcf,
                   pci_irq_vector(pdev, 0));
 
        if (jumbo_max > JUMBO_1K)
index 1c6e4df..ac9195a 100644 (file)
@@ -1032,7 +1032,6 @@ struct ravb_private {
        phy_interface_t phy_interface;
        int msg_enable;
        int speed;
-       int duplex;
        int emac_irq;
        enum ravb_chip_id chip_id;
        int rx_irqs[NUM_RX_QUEUE];
index defed0d..ffc1ada 100644 (file)
@@ -82,13 +82,6 @@ static int ravb_config(struct net_device *ndev)
        return error;
 }
 
-static void ravb_set_duplex(struct net_device *ndev)
-{
-       struct ravb_private *priv = netdev_priv(ndev);
-
-       ravb_modify(ndev, ECMR, ECMR_DM, priv->duplex ? ECMR_DM : 0);
-}
-
 static void ravb_set_rate(struct net_device *ndev)
 {
        struct ravb_private *priv = netdev_priv(ndev);
@@ -406,13 +399,11 @@ error:
 /* E-MAC init function */
 static void ravb_emac_init(struct net_device *ndev)
 {
-       struct ravb_private *priv = netdev_priv(ndev);
-
        /* Receive frame limit set register */
        ravb_write(ndev, ndev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, RFLR);
 
        /* EMAC Mode: PAUSE prohibition; Duplex; RX Checksum; TX; RX */
-       ravb_write(ndev, ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) |
+       ravb_write(ndev, ECMR_ZPF | ECMR_DM |
                   (ndev->features & NETIF_F_RXCSUM ? ECMR_RCSC : 0) |
                   ECMR_TE | ECMR_RE, ECMR);
 
@@ -995,12 +986,6 @@ static void ravb_adjust_link(struct net_device *ndev)
                ravb_rcv_snd_disable(ndev);
 
        if (phydev->link) {
-               if (phydev->duplex != priv->duplex) {
-                       new_state = true;
-                       priv->duplex = phydev->duplex;
-                       ravb_set_duplex(ndev);
-               }
-
                if (phydev->speed != priv->speed) {
                        new_state = true;
                        priv->speed = phydev->speed;
@@ -1015,7 +1000,6 @@ static void ravb_adjust_link(struct net_device *ndev)
                new_state = true;
                priv->link = 0;
                priv->speed = 0;
-               priv->duplex = -1;
        }
 
        /* Enable TX and RX right over here, if E-MAC change is ignored */
@@ -1045,7 +1029,6 @@ static int ravb_phy_init(struct net_device *ndev)
 
        priv->link = 0;
        priv->speed = 0;
-       priv->duplex = -1;
 
        /* Try connecting to PHY */
        pn = of_parse_phandle(np, "phy-handle", 0);
@@ -1088,6 +1071,10 @@ static int ravb_phy_init(struct net_device *ndev)
        phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_Pause_BIT);
        phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT);
 
+       /* Half Duplex is not supported */
+       phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
+       phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT);
+
        phy_attached_info(phydev);
 
        return 0;
index beb0662..6213827 100644 (file)
@@ -1632,9 +1632,6 @@ rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port,
 {
        struct rocker_world_ops *wops = rocker_port->rocker->wops;
 
-       if (netif_is_bridge_master(vlan->obj.orig_dev))
-               return -EOPNOTSUPP;
-
        if (!wops->port_obj_vlan_add)
                return -EOPNOTSUPP;
 
@@ -2145,8 +2142,6 @@ static int rocker_port_obj_del(struct net_device *dev,
 static const struct switchdev_ops rocker_port_switchdev_ops = {
        .switchdev_port_attr_get        = rocker_port_attr_get,
        .switchdev_port_attr_set        = rocker_port_attr_set,
-       .switchdev_port_obj_add         = rocker_port_obj_add,
-       .switchdev_port_obj_del         = rocker_port_obj_del,
 };
 
 struct rocker_fib_event_work {
@@ -2812,12 +2807,54 @@ static int rocker_switchdev_event(struct notifier_block *unused,
        return NOTIFY_DONE;
 }
 
+static int
+rocker_switchdev_port_obj_event(unsigned long event, struct net_device *netdev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info)
+{
+       int err = -EOPNOTSUPP;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD:
+               err = rocker_port_obj_add(netdev, port_obj_info->obj,
+                                         port_obj_info->trans);
+               break;
+       case SWITCHDEV_PORT_OBJ_DEL:
+               err = rocker_port_obj_del(netdev, port_obj_info->obj);
+               break;
+       }
+
+       port_obj_info->handled = true;
+       return notifier_from_errno(err);
+}
+
+static int rocker_switchdev_blocking_event(struct notifier_block *unused,
+                                          unsigned long event, void *ptr)
+{
+       struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+
+       if (!rocker_port_dev_check(dev))
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD:
+       case SWITCHDEV_PORT_OBJ_DEL:
+               return rocker_switchdev_port_obj_event(event, dev, ptr);
+       }
+
+       return NOTIFY_DONE;
+}
+
 static struct notifier_block rocker_switchdev_notifier = {
        .notifier_call = rocker_switchdev_event,
 };
 
+static struct notifier_block rocker_switchdev_blocking_notifier = {
+       .notifier_call = rocker_switchdev_blocking_event,
+};
+
 static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
+       struct notifier_block *nb;
        struct rocker *rocker;
        int err;
 
@@ -2933,6 +2970,13 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_register_switchdev_notifier;
        }
 
+       nb = &rocker_switchdev_blocking_notifier;
+       err = register_switchdev_blocking_notifier(nb);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to register switchdev blocking notifier\n");
+               goto err_register_switchdev_blocking_notifier;
+       }
+
        rocker->hw.id = rocker_read64(rocker, SWITCH_ID);
 
        dev_info(&pdev->dev, "Rocker switch with id %*phN\n",
@@ -2940,6 +2984,8 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        return 0;
 
+err_register_switchdev_blocking_notifier:
+       unregister_switchdev_notifier(&rocker_switchdev_notifier);
 err_register_switchdev_notifier:
        unregister_fib_notifier(&rocker->fib_nb);
 err_register_fib_notifier:
@@ -2971,6 +3017,10 @@ err_pci_enable_device:
 static void rocker_remove(struct pci_dev *pdev)
 {
        struct rocker *rocker = pci_get_drvdata(pdev);
+       struct notifier_block *nb;
+
+       nb = &rocker_switchdev_blocking_notifier;
+       unregister_switchdev_blocking_notifier(nb);
 
        unregister_switchdev_notifier(&rocker_switchdev_notifier);
        unregister_fib_notifier(&rocker->fib_nb);
index 3143588..600d7b8 100644 (file)
@@ -539,7 +539,7 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
        /* We need rx buffers and interrupts. */
        already_up = (efx->net_dev->flags & IFF_UP);
        if (!already_up) {
-               rc = dev_open(efx->net_dev);
+               rc = dev_open(efx->net_dev, NULL);
                if (rc) {
                        netif_err(efx, drv, efx->net_dev,
                                  "failed opening device.\n");
index 1ccdb7a..72cedec 100644 (file)
@@ -517,7 +517,7 @@ static void ef4_ethtool_self_test(struct net_device *net_dev,
        /* We need rx buffers and interrupts. */
        already_up = (efx->net_dev->flags & IFF_UP);
        if (!already_up) {
-               rc = dev_open(efx->net_dev);
+               rc = dev_open(efx->net_dev, NULL);
                if (rc) {
                        netif_err(efx, drv, efx->net_dev,
                                  "failed opening device.\n");
index 3588202..7961206 100644 (file)
@@ -27,7 +27,7 @@ config SMC9194
          option if you have a DELL laptop with the docking station, or
          another SMC9192/9194 based chipset.  Say Y if you want it compiled
          into the kernel, and read the file
-         <file:Documentation/networking/smc9.txt>.
+         <file:Documentation/networking/device_drivers/smsc/smc9.txt>.
 
          To compile this driver as a module, choose M here. The module
          will be called smc9194.
@@ -43,7 +43,7 @@ config SMC91X
          This is a driver for SMC's 91x series of Ethernet chipsets,
          including the SMC91C94 and the SMC91C111. Say Y if you want it
          compiled into the kernel, and read the file
-         <file:Documentation/networking/smc9.txt>.
+         <file:Documentation/networking/device_drivers/smsc/smc9.txt>.
 
          This driver is also available as a module ( = code which can be
          inserted in and removed from the running kernel whenever you want).
index bba9733..05a0948 100644 (file)
@@ -257,7 +257,6 @@ struct netsec_desc_ring {
        dma_addr_t desc_dma;
        struct netsec_desc *desc;
        void *vaddr;
-       u16 pkt_cnt;
        u16 head, tail;
 };
 
@@ -598,33 +597,26 @@ static void netsec_set_rx_de(struct netsec_priv *priv,
        dring->desc[idx].len = desc->len;
 }
 
-static int netsec_clean_tx_dring(struct netsec_priv *priv, int budget)
+static bool netsec_clean_tx_dring(struct netsec_priv *priv)
 {
        struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_TX];
        unsigned int pkts, bytes;
-
-       dring->pkt_cnt += netsec_read(priv, NETSEC_REG_NRM_TX_DONE_PKTCNT);
-
-       if (dring->pkt_cnt < budget)
-               budget = dring->pkt_cnt;
+       struct netsec_de *entry;
+       int tail = dring->tail;
+       int cnt = 0;
 
        pkts = 0;
        bytes = 0;
+       entry = dring->vaddr + DESC_SZ * tail;
 
-       while (pkts < budget) {
+       while (!(entry->attr & (1U << NETSEC_TX_SHIFT_OWN_FIELD)) &&
+              cnt < DESC_NUM) {
                struct netsec_desc *desc;
-               struct netsec_de *entry;
-               int tail, eop;
-
-               tail = dring->tail;
-
-               /* move tail ahead */
-               dring->tail = (tail + 1) % DESC_NUM;
+               int eop;
 
                desc = &dring->desc[tail];
-               entry = dring->vaddr + DESC_SZ * tail;
-
                eop = (entry->attr >> NETSEC_TX_LAST) & 1;
+               dma_rmb();
 
                dma_unmap_single(priv->dev, desc->dma_addr, desc->len,
                                 DMA_TO_DEVICE);
@@ -633,33 +625,51 @@ static int netsec_clean_tx_dring(struct netsec_priv *priv, int budget)
                        bytes += desc->skb->len;
                        dev_kfree_skb(desc->skb);
                }
+               /* clean up so netsec_uninit_pkt_dring() won't free the skb
+                * again
+                */
                *desc = (struct netsec_desc){};
+
+               /* entry->attr is not going to be accessed by the NIC until
+                * netsec_set_tx_de() is called. No need for a dma_wmb() here
+                */
+               entry->attr = 1U << NETSEC_TX_SHIFT_OWN_FIELD;
+               /* move tail ahead */
+               dring->tail = (tail + 1) % DESC_NUM;
+
+               tail = dring->tail;
+               entry = dring->vaddr + DESC_SZ * tail;
+               cnt++;
        }
-       dring->pkt_cnt -= budget;
 
-       priv->ndev->stats.tx_packets += budget;
+       if (!cnt)
+               return false;
+
+       /* reading the register clears the irq */
+       netsec_read(priv, NETSEC_REG_NRM_TX_DONE_PKTCNT);
+
+       priv->ndev->stats.tx_packets += cnt;
        priv->ndev->stats.tx_bytes += bytes;
 
-       netdev_completed_queue(priv->ndev, budget, bytes);
+       netdev_completed_queue(priv->ndev, cnt, bytes);
 
-       return budget;
+       return true;
 }
 
-static int netsec_process_tx(struct netsec_priv *priv, int budget)
+static void netsec_process_tx(struct netsec_priv *priv)
 {
        struct net_device *ndev = priv->ndev;
-       int new, done = 0;
+       bool cleaned;
 
-       do {
-               new = netsec_clean_tx_dring(priv, budget);
-               done += new;
-               budget -= new;
-       } while (new);
+       cleaned = netsec_clean_tx_dring(priv);
 
-       if (done && netif_queue_stopped(ndev))
+       if (cleaned && netif_queue_stopped(ndev)) {
+               /* Make sure we update the value, anyone stopping the queue
+                * after this will read the proper consumer idx
+                */
+               smp_wmb();
                netif_wake_queue(ndev);
-
-       return done;
+       }
 }
 
 static void *netsec_alloc_rx_data(struct netsec_priv *priv,
@@ -808,24 +818,17 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget)
 static int netsec_napi_poll(struct napi_struct *napi, int budget)
 {
        struct netsec_priv *priv;
-       int tx, rx, done, todo;
+       int rx, done, todo;
 
        priv = container_of(napi, struct netsec_priv, napi);
 
+       netsec_process_tx(priv);
+
        todo = budget;
        do {
-               if (!todo)
-                       break;
-
-               tx = netsec_process_tx(priv, todo);
-               todo -= tx;
-
-               if (!todo)
-                       break;
-
                rx = netsec_process_rx(priv, todo);
                todo -= rx;
-       } while (rx || tx);
+       } while (rx);
 
        done = budget - todo;
 
@@ -877,6 +880,41 @@ static void netsec_set_tx_de(struct netsec_priv *priv,
        dring->head = (dring->head + 1) % DESC_NUM;
 }
 
+static int netsec_desc_used(struct netsec_desc_ring *dring)
+{
+       int used;
+
+       if (dring->head >= dring->tail)
+               used = dring->head - dring->tail;
+       else
+               used = dring->head + DESC_NUM - dring->tail;
+
+       return used;
+}
+
+static int netsec_check_stop_tx(struct netsec_priv *priv, int used)
+{
+       struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_TX];
+
+       /* keep tail from touching the queue */
+       if (DESC_NUM - used < 2) {
+               netif_stop_queue(priv->ndev);
+
+               /* Make sure we read the updated value in case
+                * descriptors got freed
+                */
+               smp_rmb();
+
+               used = netsec_desc_used(dring);
+               if (DESC_NUM - used < 2)
+                       return NETDEV_TX_BUSY;
+
+               netif_wake_queue(priv->ndev);
+       }
+
+       return 0;
+}
+
 static netdev_tx_t netsec_netdev_start_xmit(struct sk_buff *skb,
                                            struct net_device *ndev)
 {
@@ -887,16 +925,10 @@ static netdev_tx_t netsec_netdev_start_xmit(struct sk_buff *skb,
        u16 tso_seg_len = 0;
        int filled;
 
-       /* differentiate between full/emtpy ring */
-       if (dring->head >= dring->tail)
-               filled = dring->head - dring->tail;
-       else
-               filled = dring->head + DESC_NUM - dring->tail;
-
-       if (DESC_NUM - filled < 2) { /* if less than 2 available */
-               netif_err(priv, drv, priv->ndev, "%s: TxQFull!\n", __func__);
-               netif_stop_queue(priv->ndev);
-               dma_wmb();
+       filled = netsec_desc_used(dring);
+       if (netsec_check_stop_tx(priv, filled)) {
+               net_warn_ratelimited("%s %s Tx queue full\n",
+                                    dev_name(priv->dev), ndev->name);
                return NETDEV_TX_BUSY;
        }
 
@@ -973,7 +1005,6 @@ static void netsec_uninit_pkt_dring(struct netsec_priv *priv, int id)
 
        dring->head = 0;
        dring->tail = 0;
-       dring->pkt_cnt = 0;
 
        if (id == NETSEC_RING_TX)
                netdev_reset_queue(priv->ndev);
@@ -996,6 +1027,7 @@ static void netsec_free_dring(struct netsec_priv *priv, int id)
 static int netsec_alloc_dring(struct netsec_priv *priv, enum ring_id id)
 {
        struct netsec_desc_ring *dring = &priv->desc_ring[id];
+       int i;
 
        dring->vaddr = dma_zalloc_coherent(priv->dev, DESC_SZ * DESC_NUM,
                                           &dring->desc_dma, GFP_KERNEL);
@@ -1006,6 +1038,19 @@ static int netsec_alloc_dring(struct netsec_priv *priv, enum ring_id id)
        if (!dring->desc)
                goto err;
 
+       if (id == NETSEC_RING_TX) {
+               for (i = 0; i < DESC_NUM; i++) {
+                       struct netsec_de *de;
+
+                       de = dring->vaddr + (DESC_SZ * i);
+                       /* de->attr is not going to be accessed by the NIC
+                        * until netsec_set_tx_de() is called.
+                        * No need for a dma_wmb() here
+                        */
+                       de->attr = 1U << NETSEC_TX_SHIFT_OWN_FIELD;
+               }
+       }
+
        return 0;
 err:
        netsec_free_dring(priv, id);
index 9e7391f..bb6d5fb 100644 (file)
                                 NETIF_MSG_TX_ERR)
 
 /* Parameter for descriptor */
-#define AVE_NR_TXDESC          32      /* Tx descriptor */
-#define AVE_NR_RXDESC          64      /* Rx descriptor */
+#define AVE_NR_TXDESC          64      /* Tx descriptor */
+#define AVE_NR_RXDESC          256     /* Rx descriptor */
 
 #define AVE_DESC_OFS_CMDSTS    0
 #define AVE_DESC_OFS_ADDRL     4
 
 /* Parameter for ethernet frame */
 #define AVE_MAX_ETHFRAME       1518
+#define AVE_FRAME_HEADROOM     2
 
 /* Parameter for interrupt */
 #define AVE_INTM_COUNT         20
@@ -261,6 +262,7 @@ struct ave_private {
        struct regmap           *regmap;
        unsigned int            pinmode_mask;
        unsigned int            pinmode_val;
+       u32                     wolopts;
 
        /* stats */
        struct ave_stats        stats_rx;
@@ -576,12 +578,13 @@ static int ave_rxdesc_prepare(struct net_device *ndev, int entry)
 
        skb = priv->rx.desc[entry].skbs;
        if (!skb) {
-               skb = netdev_alloc_skb_ip_align(ndev,
-                                               AVE_MAX_ETHFRAME);
+               skb = netdev_alloc_skb(ndev, AVE_MAX_ETHFRAME);
                if (!skb) {
                        netdev_err(ndev, "can't allocate skb for Rx\n");
                        return -ENOMEM;
                }
+               skb->data += AVE_FRAME_HEADROOM;
+               skb->tail += AVE_FRAME_HEADROOM;
        }
 
        /* set disable to cmdsts */
@@ -594,12 +597,12 @@ static int ave_rxdesc_prepare(struct net_device *ndev, int entry)
         * - Rx buffer begins with 2 byte headroom, and data will be put from
         *   (buffer + 2).
         * To satisfy this, specify the address to put back the buffer
-        * pointer advanced by NET_IP_ALIGN by netdev_alloc_skb_ip_align(),
-        * and expand the map size by NET_IP_ALIGN.
+        * pointer advanced by AVE_FRAME_HEADROOM, and expand the map size
+        * by AVE_FRAME_HEADROOM.
         */
        ret = ave_dma_map(ndev, &priv->rx.desc[entry],
-                         skb->data - NET_IP_ALIGN,
-                         AVE_MAX_ETHFRAME + NET_IP_ALIGN,
+                         skb->data - AVE_FRAME_HEADROOM,
+                         AVE_MAX_ETHFRAME + AVE_FRAME_HEADROOM,
                          DMA_FROM_DEVICE, &paddr);
        if (ret) {
                netdev_err(ndev, "can't map skb for Rx\n");
@@ -1208,9 +1211,13 @@ static int ave_init(struct net_device *ndev)
 
        priv->phydev = phydev;
 
-       phy_ethtool_get_wol(phydev, &wol);
+       ave_ethtool_get_wol(ndev, &wol);
        device_set_wakeup_capable(&ndev->dev, !!wol.supported);
 
+       /* set wol initial state disabled */
+       wol.wolopts = 0;
+       ave_ethtool_set_wol(ndev, &wol);
+
        if (!phy_interface_is_rgmii(phydev))
                phy_set_max_speed(phydev, SPEED_100);
 
@@ -1689,9 +1696,10 @@ static int ave_probe(struct platform_device *pdev)
                 pdev->name, pdev->id);
 
        /* Register as a NAPI supported driver */
-       netif_napi_add(ndev, &priv->napi_rx, ave_napi_poll_rx, priv->rx.ndesc);
+       netif_napi_add(ndev, &priv->napi_rx, ave_napi_poll_rx,
+                      NAPI_POLL_WEIGHT);
        netif_tx_napi_add(ndev, &priv->napi_tx, ave_napi_poll_tx,
-                         priv->tx.ndesc);
+                         NAPI_POLL_WEIGHT);
 
        platform_set_drvdata(pdev, ndev);
 
@@ -1734,6 +1742,58 @@ static int ave_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int ave_suspend(struct device *dev)
+{
+       struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct ave_private *priv = netdev_priv(ndev);
+       int ret = 0;
+
+       if (netif_running(ndev)) {
+               ret = ave_stop(ndev);
+               netif_device_detach(ndev);
+       }
+
+       ave_ethtool_get_wol(ndev, &wol);
+       priv->wolopts = wol.wolopts;
+
+       return ret;
+}
+
+static int ave_resume(struct device *dev)
+{
+       struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct ave_private *priv = netdev_priv(ndev);
+       int ret = 0;
+
+       ave_global_reset(ndev);
+
+       ave_ethtool_get_wol(ndev, &wol);
+       wol.wolopts = priv->wolopts;
+       ave_ethtool_set_wol(ndev, &wol);
+
+       if (ndev->phydev) {
+               ret = phy_resume(ndev->phydev);
+               if (ret)
+                       return ret;
+       }
+
+       if (netif_running(ndev)) {
+               ret = ave_open(ndev);
+               netif_device_attach(ndev);
+       }
+
+       return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(ave_pm_ops, ave_suspend, ave_resume);
+#define AVE_PM_OPS     (&ave_pm_ops)
+#else
+#define AVE_PM_OPS     NULL
+#endif
+
 static int ave_pro4_get_pinmode(struct ave_private *priv,
                                phy_interface_t phy_mode, u32 arg)
 {
@@ -1908,10 +1968,12 @@ static struct platform_driver ave_driver = {
        .remove = ave_remove,
        .driver = {
                .name = "ave",
+               .pm   = AVE_PM_OPS,
                .of_match_table = of_ave_match,
        },
 };
 module_platform_driver(ave_driver);
 
+MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
 MODULE_DESCRIPTION("Socionext UniPhier AVE ethernet driver");
 MODULE_LICENSE("GPL v2");
index 324049e..6209cc1 100644 (file)
@@ -75,6 +75,14 @@ config DWMAC_LPC18XX
        ---help---
          Support for NXP LPC18xx/43xx DWMAC Ethernet.
 
+config DWMAC_MEDIATEK
+       tristate "MediaTek MT27xx GMAC support"
+       depends on OF && (ARCH_MEDIATEK || COMPILE_TEST)
+       help
+         Support for MediaTek GMAC Ethernet controller.
+
+         This selects the MT2712 SoC support for the stmmac driver.
+
 config DWMAC_MESON
        tristate "Amlogic Meson dwmac support"
        default ARCH_MESON
index 99967a8..bf09701 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
 obj-$(CONFIG_DWMAC_ANARION)    += dwmac-anarion.o
 obj-$(CONFIG_DWMAC_IPQ806X)    += dwmac-ipq806x.o
 obj-$(CONFIG_DWMAC_LPC18XX)    += dwmac-lpc18xx.o
+obj-$(CONFIG_DWMAC_MEDIATEK)   += dwmac-mediatek.o
 obj-$(CONFIG_DWMAC_MESON)      += dwmac-meson.o dwmac-meson8b.o
 obj-$(CONFIG_DWMAC_OXNAS)      += dwmac-oxnas.o
 obj-$(CONFIG_DWMAC_ROCKCHIP)   += dwmac-rk.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
new file mode 100644 (file)
index 0000000..bf25629
--- /dev/null
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_net.h>
+#include <linux/regmap.h>
+#include <linux/stmmac.h>
+
+#include "stmmac.h"
+#include "stmmac_platform.h"
+
+/* Peri Configuration register for mt2712 */
+#define PERI_ETH_PHY_INTF_SEL  0x418
+#define PHY_INTF_MII           0
+#define PHY_INTF_RGMII         1
+#define PHY_INTF_RMII          4
+#define RMII_CLK_SRC_RXC       BIT(4)
+#define RMII_CLK_SRC_INTERNAL  BIT(5)
+
+#define PERI_ETH_DLY   0x428
+#define ETH_DLY_GTXC_INV       BIT(6)
+#define ETH_DLY_GTXC_ENABLE    BIT(5)
+#define ETH_DLY_GTXC_STAGES    GENMASK(4, 0)
+#define ETH_DLY_TXC_INV                BIT(20)
+#define ETH_DLY_TXC_ENABLE     BIT(19)
+#define ETH_DLY_TXC_STAGES     GENMASK(18, 14)
+#define ETH_DLY_RXC_INV                BIT(13)
+#define ETH_DLY_RXC_ENABLE     BIT(12)
+#define ETH_DLY_RXC_STAGES     GENMASK(11, 7)
+
+#define PERI_ETH_DLY_FINE      0x800
+#define ETH_RMII_DLY_TX_INV    BIT(2)
+#define ETH_FINE_DLY_GTXC      BIT(1)
+#define ETH_FINE_DLY_RXC       BIT(0)
+
+struct mac_delay_struct {
+       u32 tx_delay;
+       u32 rx_delay;
+       bool tx_inv;
+       bool rx_inv;
+};
+
+struct mediatek_dwmac_plat_data {
+       const struct mediatek_dwmac_variant *variant;
+       struct mac_delay_struct mac_delay;
+       struct clk_bulk_data *clks;
+       struct device_node *np;
+       struct regmap *peri_regmap;
+       struct device *dev;
+       int phy_mode;
+       bool rmii_rxc;
+};
+
+struct mediatek_dwmac_variant {
+       int (*dwmac_set_phy_interface)(struct mediatek_dwmac_plat_data *plat);
+       int (*dwmac_set_delay)(struct mediatek_dwmac_plat_data *plat);
+
+       /* clock ids to be requested */
+       const char * const *clk_list;
+       int num_clks;
+
+       u32 dma_bit_mask;
+       u32 rx_delay_max;
+       u32 tx_delay_max;
+};
+
+/* list of clocks required for mac */
+static const char * const mt2712_dwmac_clk_l[] = {
+       "axi", "apb", "mac_main", "ptp_ref"
+};
+
+static int mt2712_set_interface(struct mediatek_dwmac_plat_data *plat)
+{
+       int rmii_rxc = plat->rmii_rxc ? RMII_CLK_SRC_RXC : 0;
+       u32 intf_val = 0;
+
+       /* select phy interface in top control domain */
+       switch (plat->phy_mode) {
+       case PHY_INTERFACE_MODE_MII:
+               intf_val |= PHY_INTF_MII;
+               break;
+       case PHY_INTERFACE_MODE_RMII:
+               intf_val |= (PHY_INTF_RMII | rmii_rxc);
+               break;
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+               intf_val |= PHY_INTF_RGMII;
+               break;
+       default:
+               dev_err(plat->dev, "phy interface not supported\n");
+               return -EINVAL;
+       }
+
+       regmap_write(plat->peri_regmap, PERI_ETH_PHY_INTF_SEL, intf_val);
+
+       return 0;
+}
+
+static void mt2712_delay_ps2stage(struct mediatek_dwmac_plat_data *plat)
+{
+       struct mac_delay_struct *mac_delay = &plat->mac_delay;
+
+       switch (plat->phy_mode) {
+       case PHY_INTERFACE_MODE_MII:
+       case PHY_INTERFACE_MODE_RMII:
+               /* 550ps per stage for MII/RMII */
+               mac_delay->tx_delay /= 550;
+               mac_delay->rx_delay /= 550;
+               break;
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+               /* 170ps per stage for RGMII */
+               mac_delay->tx_delay /= 170;
+               mac_delay->rx_delay /= 170;
+               break;
+       default:
+               dev_err(plat->dev, "phy interface not supported\n");
+               break;
+       }
+}
+
+static int mt2712_set_delay(struct mediatek_dwmac_plat_data *plat)
+{
+       struct mac_delay_struct *mac_delay = &plat->mac_delay;
+       u32 delay_val = 0, fine_val = 0;
+
+       mt2712_delay_ps2stage(plat);
+
+       switch (plat->phy_mode) {
+       case PHY_INTERFACE_MODE_MII:
+               delay_val |= FIELD_PREP(ETH_DLY_TXC_ENABLE, !!mac_delay->tx_delay);
+               delay_val |= FIELD_PREP(ETH_DLY_TXC_STAGES, mac_delay->tx_delay);
+               delay_val |= FIELD_PREP(ETH_DLY_TXC_INV, mac_delay->tx_inv);
+
+               delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay);
+               delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay);
+               delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv);
+               break;
+       case PHY_INTERFACE_MODE_RMII:
+               /* the rmii reference clock is from external phy,
+                * and the property "rmii_rxc" indicates which pin(TXC/RXC)
+                * the reference clk is connected to. The reference clock is a
+                * received signal, so rx_delay/rx_inv are used to indicate
+                * the reference clock timing adjustment
+                */
+               if (plat->rmii_rxc) {
+                       /* the rmii reference clock from outside is connected
+                        * to RXC pin, the reference clock will be adjusted
+                        * by RXC delay macro circuit.
+                        */
+                       delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay);
+                       delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay);
+                       delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv);
+               } else {
+                       /* the rmii reference clock from outside is connected
+                        * to TXC pin, the reference clock will be adjusted
+                        * by TXC delay macro circuit.
+                        */
+                       delay_val |= FIELD_PREP(ETH_DLY_TXC_ENABLE, !!mac_delay->rx_delay);
+                       delay_val |= FIELD_PREP(ETH_DLY_TXC_STAGES, mac_delay->rx_delay);
+                       delay_val |= FIELD_PREP(ETH_DLY_TXC_INV, mac_delay->rx_inv);
+               }
+               /* tx_inv will inverse the tx clock inside mac relateive to
+                * reference clock from external phy,
+                * and this bit is located in the same register with fine-tune
+                */
+               if (mac_delay->tx_inv)
+                       fine_val = ETH_RMII_DLY_TX_INV;
+               break;
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+               fine_val = ETH_FINE_DLY_GTXC | ETH_FINE_DLY_RXC;
+
+               delay_val |= FIELD_PREP(ETH_DLY_GTXC_ENABLE, !!mac_delay->tx_delay);
+               delay_val |= FIELD_PREP(ETH_DLY_GTXC_STAGES, mac_delay->tx_delay);
+               delay_val |= FIELD_PREP(ETH_DLY_GTXC_INV, mac_delay->tx_inv);
+
+               delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay);
+               delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay);
+               delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv);
+               break;
+       default:
+               dev_err(plat->dev, "phy interface not supported\n");
+               return -EINVAL;
+       }
+       regmap_write(plat->peri_regmap, PERI_ETH_DLY, delay_val);
+       regmap_write(plat->peri_regmap, PERI_ETH_DLY_FINE, fine_val);
+
+       return 0;
+}
+
+static const struct mediatek_dwmac_variant mt2712_gmac_variant = {
+               .dwmac_set_phy_interface = mt2712_set_interface,
+               .dwmac_set_delay = mt2712_set_delay,
+               .clk_list = mt2712_dwmac_clk_l,
+               .num_clks = ARRAY_SIZE(mt2712_dwmac_clk_l),
+               .dma_bit_mask = 33,
+               .rx_delay_max = 17600,
+               .tx_delay_max = 17600,
+};
+
+static int mediatek_dwmac_config_dt(struct mediatek_dwmac_plat_data *plat)
+{
+       struct mac_delay_struct *mac_delay = &plat->mac_delay;
+       u32 tx_delay_ps, rx_delay_ps;
+
+       plat->peri_regmap = syscon_regmap_lookup_by_phandle(plat->np, "mediatek,pericfg");
+       if (IS_ERR(plat->peri_regmap)) {
+               dev_err(plat->dev, "Failed to get pericfg syscon\n");
+               return PTR_ERR(plat->peri_regmap);
+       }
+
+       plat->phy_mode = of_get_phy_mode(plat->np);
+       if (plat->phy_mode < 0) {
+               dev_err(plat->dev, "not find phy-mode\n");
+               return -EINVAL;
+       }
+
+       if (!of_property_read_u32(plat->np, "mediatek,tx-delay-ps", &tx_delay_ps)) {
+               if (tx_delay_ps < plat->variant->tx_delay_max) {
+                       mac_delay->tx_delay = tx_delay_ps;
+               } else {
+                       dev_err(plat->dev, "Invalid TX clock delay: %dps\n", tx_delay_ps);
+                       return -EINVAL;
+               }
+       }
+
+       if (!of_property_read_u32(plat->np, "mediatek,rx-delay-ps", &rx_delay_ps)) {
+               if (rx_delay_ps < plat->variant->rx_delay_max) {
+                       mac_delay->rx_delay = rx_delay_ps;
+               } else {
+                       dev_err(plat->dev, "Invalid RX clock delay: %dps\n", rx_delay_ps);
+                       return -EINVAL;
+               }
+       }
+
+       mac_delay->tx_inv = of_property_read_bool(plat->np, "mediatek,txc-inverse");
+       mac_delay->rx_inv = of_property_read_bool(plat->np, "mediatek,rxc-inverse");
+       plat->rmii_rxc = of_property_read_bool(plat->np, "mediatek,rmii-rxc");
+
+       return 0;
+}
+
+static int mediatek_dwmac_clk_init(struct mediatek_dwmac_plat_data *plat)
+{
+       const struct mediatek_dwmac_variant *variant = plat->variant;
+       int i, num = variant->num_clks;
+
+       plat->clks = devm_kcalloc(plat->dev, num, sizeof(*plat->clks), GFP_KERNEL);
+       if (!plat->clks)
+               return -ENOMEM;
+
+       for (i = 0; i < num; i++)
+               plat->clks[i].id = variant->clk_list[i];
+
+       return devm_clk_bulk_get(plat->dev, num, plat->clks);
+}
+
+static int mediatek_dwmac_init(struct platform_device *pdev, void *priv)
+{
+       struct mediatek_dwmac_plat_data *plat = priv;
+       const struct mediatek_dwmac_variant *variant = plat->variant;
+       int ret;
+
+       ret = dma_set_mask_and_coherent(plat->dev, DMA_BIT_MASK(variant->dma_bit_mask));
+       if (ret) {
+               dev_err(plat->dev, "No suitable DMA available, err = %d\n", ret);
+               return ret;
+       }
+
+       ret = variant->dwmac_set_phy_interface(plat);
+       if (ret) {
+               dev_err(plat->dev, "failed to set phy interface, err = %d\n", ret);
+               return ret;
+       }
+
+       ret = variant->dwmac_set_delay(plat);
+       if (ret) {
+               dev_err(plat->dev, "failed to set delay value, err = %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_bulk_prepare_enable(variant->num_clks, plat->clks);
+       if (ret) {
+               dev_err(plat->dev, "failed to enable clks, err = %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void mediatek_dwmac_exit(struct platform_device *pdev, void *priv)
+{
+       struct mediatek_dwmac_plat_data *plat = priv;
+       const struct mediatek_dwmac_variant *variant = plat->variant;
+
+       clk_bulk_disable_unprepare(variant->num_clks, plat->clks);
+}
+
+static int mediatek_dwmac_probe(struct platform_device *pdev)
+{
+       struct mediatek_dwmac_plat_data *priv_plat;
+       struct plat_stmmacenet_data *plat_dat;
+       struct stmmac_resources stmmac_res;
+       int ret;
+
+       priv_plat = devm_kzalloc(&pdev->dev, sizeof(*priv_plat), GFP_KERNEL);
+       if (!priv_plat)
+               return -ENOMEM;
+
+       priv_plat->variant = of_device_get_match_data(&pdev->dev);
+       if (!priv_plat->variant) {
+               dev_err(&pdev->dev, "Missing dwmac-mediatek variant\n");
+               return -EINVAL;
+       }
+
+       priv_plat->dev = &pdev->dev;
+       priv_plat->np = pdev->dev.of_node;
+
+       ret = mediatek_dwmac_config_dt(priv_plat);
+       if (ret)
+               return ret;
+
+       ret = mediatek_dwmac_clk_init(priv_plat);
+       if (ret)
+               return ret;
+
+       ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+       if (ret)
+               return ret;
+
+       plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
+       if (IS_ERR(plat_dat))
+               return PTR_ERR(plat_dat);
+
+       plat_dat->interface = priv_plat->phy_mode;
+       /* clk_csr_i = 250-300MHz & MDC = clk_csr_i/124 */
+       plat_dat->clk_csr = 5;
+       plat_dat->has_gmac4 = 1;
+       plat_dat->has_gmac = 0;
+       plat_dat->pmt = 0;
+       plat_dat->maxmtu = ETH_DATA_LEN;
+       plat_dat->bsp_priv = priv_plat;
+       plat_dat->init = mediatek_dwmac_init;
+       plat_dat->exit = mediatek_dwmac_exit;
+       mediatek_dwmac_init(pdev, priv_plat);
+
+       ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+       if (ret) {
+               stmmac_remove_config_dt(pdev, plat_dat);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id mediatek_dwmac_match[] = {
+       { .compatible = "mediatek,mt2712-gmac",
+         .data = &mt2712_gmac_variant },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, mediatek_dwmac_match);
+
+static struct platform_driver mediatek_dwmac_driver = {
+       .probe  = mediatek_dwmac_probe,
+       .remove = stmmac_pltfr_remove,
+       .driver = {
+               .name           = "dwmac-mediatek",
+               .pm             = &stmmac_pltfr_pm_ops,
+               .of_match_table = mediatek_dwmac_match,
+       },
+};
+module_platform_driver(mediatek_dwmac_driver);
+
+MODULE_AUTHOR("Biao Huang <biao.huang@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek DWMAC specific glue layer");
+MODULE_LICENSE("GPL v2");
index 076a8be..0e0a078 100644 (file)
@@ -2550,12 +2550,6 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
                        netdev_warn(priv->dev, "PTP init failed\n");
        }
 
-#ifdef CONFIG_DEBUG_FS
-       ret = stmmac_init_fs(dev);
-       if (ret < 0)
-               netdev_warn(priv->dev, "%s: failed debugFS registration\n",
-                           __func__);
-#endif
        priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS;
 
        if (priv->use_riwt) {
@@ -2756,10 +2750,6 @@ static int stmmac_release(struct net_device *dev)
 
        netif_carrier_off(dev);
 
-#ifdef CONFIG_DEBUG_FS
-       stmmac_exit_fs(dev);
-#endif
-
        stmmac_release_ptp(priv);
 
        return 0;
@@ -3891,7 +3881,7 @@ static void sysfs_display_ring(void *head, int size, int extend_desc,
        }
 }
 
-static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v)
+static int stmmac_rings_status_show(struct seq_file *seq, void *v)
 {
        struct net_device *dev = seq->private;
        struct stmmac_priv *priv = netdev_priv(dev);
@@ -3899,6 +3889,9 @@ static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v)
        u32 tx_count = priv->plat->tx_queues_to_use;
        u32 queue;
 
+       if ((dev->flags & IFF_UP) == 0)
+               return 0;
+
        for (queue = 0; queue < rx_count; queue++) {
                struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
 
@@ -3933,23 +3926,9 @@ static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v)
 
        return 0;
 }
+DEFINE_SHOW_ATTRIBUTE(stmmac_rings_status);
 
-static int stmmac_sysfs_ring_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, stmmac_sysfs_ring_read, inode->i_private);
-}
-
-/* Debugfs files, should appear in /sys/kernel/debug/stmmaceth/eth0 */
-
-static const struct file_operations stmmac_rings_status_fops = {
-       .owner = THIS_MODULE,
-       .open = stmmac_sysfs_ring_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
-
-static int stmmac_sysfs_dma_cap_read(struct seq_file *seq, void *v)
+static int stmmac_dma_cap_show(struct seq_file *seq, void *v)
 {
        struct net_device *dev = seq->private;
        struct stmmac_priv *priv = netdev_priv(dev);
@@ -4012,19 +3991,7 @@ static int stmmac_sysfs_dma_cap_read(struct seq_file *seq, void *v)
 
        return 0;
 }
-
-static int stmmac_sysfs_dma_cap_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, stmmac_sysfs_dma_cap_read, inode->i_private);
-}
-
-static const struct file_operations stmmac_dma_cap_fops = {
-       .owner = THIS_MODULE,
-       .open = stmmac_sysfs_dma_cap_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(stmmac_dma_cap);
 
 static int stmmac_init_fs(struct net_device *dev)
 {
@@ -4108,7 +4075,7 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv)
 
        set_bit(STMMAC_DOWN, &priv->state);
        dev_close(priv->dev);
-       dev_open(priv->dev);
+       dev_open(priv->dev, NULL);
        clear_bit(STMMAC_DOWN, &priv->state);
        clear_bit(STMMAC_RESETING, &priv->state);
        rtnl_unlock();
@@ -4257,6 +4224,7 @@ int stmmac_dvr_probe(struct device *device,
        priv->wq = create_singlethread_workqueue("stmmac_wq");
        if (!priv->wq) {
                dev_err(priv->device, "failed to create workqueue\n");
+               ret = -ENOMEM;
                goto error_wq;
        }
 
@@ -4397,6 +4365,13 @@ int stmmac_dvr_probe(struct device *device,
                goto error_netdev_register;
        }
 
+#ifdef CONFIG_DEBUG_FS
+       ret = stmmac_init_fs(ndev);
+       if (ret < 0)
+               netdev_warn(priv->dev, "%s: failed debugFS registration\n",
+                           __func__);
+#endif
+
        return ret;
 
 error_netdev_register:
@@ -4432,6 +4407,9 @@ int stmmac_dvr_remove(struct device *dev)
 
        netdev_info(priv->dev, "%s: removing driver", __func__);
 
+#ifdef CONFIG_DEBUG_FS
+       stmmac_exit_fs(ndev);
+#endif
        stmmac_stop_all_dma(priv);
 
        stmmac_mac_set(priv, priv->ioaddr, false);
index 863fd60..ff641cf 100644 (file)
@@ -2691,7 +2691,7 @@ static int happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe)
        sbus_dp = op->dev.parent->of_node;
 
        /* We can match PCI devices too, do not accept those here. */
-       if (strcmp(sbus_dp->name, "sbus") && strcmp(sbus_dp->name, "sbi"))
+       if (!of_node_name_eq(sbus_dp, "sbus") && !of_node_name_eq(sbus_dp, "sbi"))
                return err;
 
        if (is_qfe) {
index f932923..bb126be 100644 (file)
@@ -121,7 +121,8 @@ config TLAN
 
          Devices currently supported by this driver are Compaq Netelligent,
          Compaq NetFlex and Olicom cards.  Please read the file
-         <file:Documentation/networking/tlan.txt> for more details.
+         <file:Documentation/networking/device_drivers/ti/tlan.txt>
+         for more details.
 
          To compile this driver as a module, choose M here. The module
          will be called tlan.
index 9b8a30b..810dfc7 100644 (file)
@@ -991,7 +991,6 @@ static int cpmac_open(struct net_device *dev)
        cpmac_hw_start(dev);
 
        napi_enable(&priv->napi);
-       dev->phydev->state = PHY_CHANGELINK;
        phy_start(dev->phydev);
 
        return 0;
index 9434fd5..0e8f61a 100644 (file)
@@ -283,7 +283,7 @@ struct cpsw_ss_regs {
 
 #define CTRL_V2_TS_BITS \
        (TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 |\
-        TS_TTL_NONZERO  | TS_ANNEX_D_EN | TS_LTYPE1_EN)
+        TS_TTL_NONZERO  | TS_ANNEX_D_EN | TS_LTYPE1_EN | VLAN_LTYPE1_EN)
 
 #define CTRL_V2_ALL_TS_MASK (CTRL_V2_TS_BITS | TS_TX_EN | TS_RX_EN)
 #define CTRL_V2_TX_TS_BITS  (CTRL_V2_TS_BITS | TS_TX_EN)
@@ -293,7 +293,7 @@ struct cpsw_ss_regs {
 #define CTRL_V3_TS_BITS \
        (TS_107 | TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 |\
         TS_TTL_NONZERO | TS_ANNEX_F_EN | TS_ANNEX_D_EN |\
-        TS_LTYPE1_EN)
+        TS_LTYPE1_EN | VLAN_LTYPE1_EN)
 
 #define CTRL_V3_ALL_TS_MASK (CTRL_V3_TS_BITS | TS_TX_EN | TS_RX_EN)
 #define CTRL_V3_TX_TS_BITS  (CTRL_V3_TS_BITS | TS_TX_EN)
@@ -466,6 +466,8 @@ struct cpsw_priv {
        bool                            mqprio_hw;
        int                             fifo_bw[CPSW_TC_NUM];
        int                             shp_cfg_speed;
+       int                             tx_ts_enabled;
+       int                             rx_ts_enabled;
        u32 emac_port;
        struct cpsw_common *cpsw;
 };
@@ -905,6 +907,7 @@ static void cpsw_rx_handler(void *token, int len, int status)
        struct net_device       *ndev = skb->dev;
        int                     ret = 0, port;
        struct cpsw_common      *cpsw = ndev_to_cpsw(ndev);
+       struct cpsw_priv        *priv;
 
        if (cpsw->data.dual_emac) {
                port = CPDMA_RX_SOURCE_PORT(status);
@@ -939,7 +942,9 @@ static void cpsw_rx_handler(void *token, int len, int status)
                skb_put(skb, len);
                if (status & CPDMA_RX_VLAN_ENCAP)
                        cpsw_rx_vlan_encap(skb);
-               cpts_rx_timestamp(cpsw->cpts, skb);
+               priv = netdev_priv(ndev);
+               if (priv->rx_ts_enabled)
+                       cpts_rx_timestamp(cpsw->cpts, skb);
                skb->protocol = eth_type_trans(skb, ndev);
                netif_receive_skb(skb);
                ndev->stats.rx_bytes += len;
@@ -2126,7 +2131,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
        }
 
        if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
-           cpts_is_tx_enabled(cpts) && cpts_can_timestamp(cpts, skb))
+           priv->tx_ts_enabled && cpts_can_timestamp(cpts, skb))
                skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
 
        q_idx = skb_get_queue_mapping(skb);
@@ -2170,13 +2175,13 @@ fail:
 
 #if IS_ENABLED(CONFIG_TI_CPTS)
 
-static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw)
+static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
 {
+       struct cpsw_common *cpsw = priv->cpsw;
        struct cpsw_slave *slave = &cpsw->slaves[cpsw->data.active_slave];
        u32 ts_en, seq_id;
 
-       if (!cpts_is_tx_enabled(cpsw->cpts) &&
-           !cpts_is_rx_enabled(cpsw->cpts)) {
+       if (!priv->tx_ts_enabled && !priv->rx_ts_enabled) {
                slave_write(slave, 0, CPSW1_TS_CTL);
                return;
        }
@@ -2184,10 +2189,10 @@ static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw)
        seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588;
        ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS;
 
-       if (cpts_is_tx_enabled(cpsw->cpts))
+       if (priv->tx_ts_enabled)
                ts_en |= CPSW_V1_TS_TX_EN;
 
-       if (cpts_is_rx_enabled(cpsw->cpts))
+       if (priv->rx_ts_enabled)
                ts_en |= CPSW_V1_TS_RX_EN;
 
        slave_write(slave, ts_en, CPSW1_TS_CTL);
@@ -2207,20 +2212,20 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
        case CPSW_VERSION_2:
                ctrl &= ~CTRL_V2_ALL_TS_MASK;
 
-               if (cpts_is_tx_enabled(cpsw->cpts))
+               if (priv->tx_ts_enabled)
                        ctrl |= CTRL_V2_TX_TS_BITS;
 
-               if (cpts_is_rx_enabled(cpsw->cpts))
+               if (priv->rx_ts_enabled)
                        ctrl |= CTRL_V2_RX_TS_BITS;
                break;
        case CPSW_VERSION_3:
        default:
                ctrl &= ~CTRL_V3_ALL_TS_MASK;
 
-               if (cpts_is_tx_enabled(cpsw->cpts))
+               if (priv->tx_ts_enabled)
                        ctrl |= CTRL_V3_TX_TS_BITS;
 
-               if (cpts_is_rx_enabled(cpsw->cpts))
+               if (priv->rx_ts_enabled)
                        ctrl |= CTRL_V3_RX_TS_BITS;
                break;
        }
@@ -2230,6 +2235,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
        slave_write(slave, mtype, CPSW2_TS_SEQ_MTYPE);
        slave_write(slave, ctrl, CPSW2_CONTROL);
        writel_relaxed(ETH_P_1588, &cpsw->regs->ts_ltype);
+       writel_relaxed(ETH_P_8021Q, &cpsw->regs->vlan_ltype);
 }
 
 static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
@@ -2237,7 +2243,6 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
        struct cpsw_priv *priv = netdev_priv(dev);
        struct hwtstamp_config cfg;
        struct cpsw_common *cpsw = priv->cpsw;
-       struct cpts *cpts = cpsw->cpts;
 
        if (cpsw->version != CPSW_VERSION_1 &&
            cpsw->version != CPSW_VERSION_2 &&
@@ -2256,7 +2261,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 
        switch (cfg.rx_filter) {
        case HWTSTAMP_FILTER_NONE:
-               cpts_rx_enable(cpts, 0);
+               priv->rx_ts_enabled = 0;
                break;
        case HWTSTAMP_FILTER_ALL:
        case HWTSTAMP_FILTER_NTP_ALL:
@@ -2264,7 +2269,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
        case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
        case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
-               cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V1_L4_EVENT);
+               priv->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
                cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
                break;
        case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
@@ -2276,18 +2281,18 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V2_EVENT:
        case HWTSTAMP_FILTER_PTP_V2_SYNC:
        case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
-               cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V2_EVENT);
+               priv->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V2_EVENT;
                cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
                break;
        default:
                return -ERANGE;
        }
 
-       cpts_tx_enable(cpts, cfg.tx_type == HWTSTAMP_TX_ON);
+       priv->tx_ts_enabled = cfg.tx_type == HWTSTAMP_TX_ON;
 
        switch (cpsw->version) {
        case CPSW_VERSION_1:
-               cpsw_hwtstamp_v1(cpsw);
+               cpsw_hwtstamp_v1(priv);
                break;
        case CPSW_VERSION_2:
        case CPSW_VERSION_3:
@@ -2303,7 +2308,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
 {
        struct cpsw_common *cpsw = ndev_to_cpsw(dev);
-       struct cpts *cpts = cpsw->cpts;
+       struct cpsw_priv *priv = netdev_priv(dev);
        struct hwtstamp_config cfg;
 
        if (cpsw->version != CPSW_VERSION_1 &&
@@ -2312,10 +2317,8 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
                return -EOPNOTSUPP;
 
        cfg.flags = 0;
-       cfg.tx_type = cpts_is_tx_enabled(cpts) ?
-                     HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-       cfg.rx_filter = (cpts_is_rx_enabled(cpts) ?
-                        cpts->rx_enable : HWTSTAMP_FILTER_NONE);
+       cfg.tx_type = priv->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+       cfg.rx_filter = priv->rx_ts_enabled;
 
        return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
 }
@@ -3268,7 +3271,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                const __be32 *parp;
 
                /* This is no slave child node, continue */
-               if (strcmp(slave_node->name, "slave"))
+               if (!of_node_name_eq(slave_node, "slave"))
                        continue;
 
                slave_data->phy_node = of_parse_phandle(slave_node,
@@ -3364,7 +3367,7 @@ static void cpsw_remove_dt(struct platform_device *pdev)
        for_each_available_child_of_node(node, slave_node) {
                struct cpsw_slave_data *slave_data = &data->slave_data[i];
 
-               if (strcmp(slave_node->name, "slave"))
+               if (!of_node_name_eq(slave_node, "slave"))
                        continue;
 
                if (of_phy_is_fixed_link(slave_node))
index b96b93c..054f782 100644 (file)
@@ -86,6 +86,25 @@ static int cpts_purge_events(struct cpts *cpts)
        return removed ? 0 : -1;
 }
 
+static void cpts_purge_txq(struct cpts *cpts)
+{
+       struct cpts_skb_cb_data *skb_cb;
+       struct sk_buff *skb, *tmp;
+       int removed = 0;
+
+       skb_queue_walk_safe(&cpts->txq, skb, tmp) {
+               skb_cb = (struct cpts_skb_cb_data *)skb->cb;
+               if (time_after(jiffies, skb_cb->tmo)) {
+                       __skb_unlink(skb, &cpts->txq);
+                       dev_consume_skb_any(skb);
+                       ++removed;
+               }
+       }
+
+       if (removed)
+               dev_dbg(cpts->dev, "txq cleaned up %d\n", removed);
+}
+
 static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
 {
        struct sk_buff *skb, *tmp;
@@ -119,9 +138,7 @@ static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
 
                if (time_after(jiffies, skb_cb->tmo)) {
                        /* timeout any expired skbs over 1s */
-                       dev_dbg(cpts->dev,
-                               "expiring tx timestamp mtype %u seqid %04x\n",
-                               mtype, seqid);
+                       dev_dbg(cpts->dev, "expiring tx timestamp from txq\n");
                        __skb_unlink(skb, &cpts->txq);
                        dev_consume_skb_any(skb);
                }
@@ -294,8 +311,11 @@ static long cpts_overflow_check(struct ptp_clock_info *ptp)
        spin_lock_irqsave(&cpts->lock, flags);
        ts = ns_to_timespec64(timecounter_read(&cpts->tc));
 
-       if (!skb_queue_empty(&cpts->txq))
-               delay = CPTS_SKB_TX_WORK_TIMEOUT;
+       if (!skb_queue_empty(&cpts->txq)) {
+               cpts_purge_txq(cpts);
+               if (!skb_queue_empty(&cpts->txq))
+                       delay = CPTS_SKB_TX_WORK_TIMEOUT;
+       }
        spin_unlock_irqrestore(&cpts->lock, flags);
 
        pr_debug("cpts overflow check at %lld.%09ld\n",
@@ -410,8 +430,6 @@ void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb)
        u64 ns;
        struct skb_shared_hwtstamps *ssh;
 
-       if (!cpts->rx_enable)
-               return;
        ns = cpts_find_ts(cpts, skb, CPTS_EV_RX);
        if (!ns)
                return;
index 73d73fa..d2c7dec 100644 (file)
@@ -136,26 +136,6 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
                         struct device_node *node);
 void cpts_release(struct cpts *cpts);
 
-static inline void cpts_rx_enable(struct cpts *cpts, int enable)
-{
-       cpts->rx_enable = enable;
-}
-
-static inline bool cpts_is_rx_enabled(struct cpts *cpts)
-{
-       return !!cpts->rx_enable;
-}
-
-static inline void cpts_tx_enable(struct cpts *cpts, int enable)
-{
-       cpts->tx_enable = enable;
-}
-
-static inline bool cpts_is_tx_enabled(struct cpts *cpts)
-{
-       return !!cpts->tx_enable;
-}
-
 static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
        unsigned int class = ptp_classify_raw(skb);
@@ -197,24 +177,6 @@ static inline void cpts_unregister(struct cpts *cpts)
 {
 }
 
-static inline void cpts_rx_enable(struct cpts *cpts, int enable)
-{
-}
-
-static inline bool cpts_is_rx_enabled(struct cpts *cpts)
-{
-       return false;
-}
-
-static inline void cpts_tx_enable(struct cpts *cpts, int enable)
-{
-}
-
-static inline bool cpts_is_tx_enabled(struct cpts *cpts)
-{
-       return false;
-}
-
 static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
        return false;
index 9153db1..8408204 100644 (file)
@@ -1912,11 +1912,15 @@ static int davinci_emac_probe(struct platform_device *pdev)
                ether_addr_copy(ndev->dev_addr, priv->mac_addr);
 
        if (!is_valid_ether_addr(priv->mac_addr)) {
-               /* Use random MAC if none passed */
-               eth_hw_addr_random(ndev);
-               memcpy(priv->mac_addr, ndev->dev_addr, ndev->addr_len);
-               dev_warn(&pdev->dev, "using random MAC addr: %pM\n",
-                                                       priv->mac_addr);
+               /* Try nvmem if MAC wasn't passed over pdata or DT. */
+               rc = nvmem_get_mac_address(&pdev->dev, priv->mac_addr);
+               if (rc) {
+                       /* Use random MAC if still none obtained. */
+                       eth_hw_addr_random(ndev);
+                       memcpy(priv->mac_addr, ndev->dev_addr, ndev->addr_len);
+                       dev_warn(&pdev->dev, "using random MAC addr: %pM\n",
+                                priv->mac_addr);
+               }
        }
 
        ndev->netdev_ops = &emac_netdev_ops;
index 0397ccb..5174d31 100644 (file)
@@ -763,6 +763,8 @@ struct gbe_priv {
 
        int                             cpts_registered;
        struct cpts                     *cpts;
+       int                             rx_ts_enabled;
+       int                             tx_ts_enabled;
 };
 
 struct gbe_intf {
@@ -2564,7 +2566,7 @@ static int gbe_txtstamp_mark_pkt(struct gbe_intf *gbe_intf,
        struct gbe_priv *gbe_dev = gbe_intf->gbe_dev;
 
        if (!(skb_shinfo(p_info->skb)->tx_flags & SKBTX_HW_TSTAMP) ||
-           !cpts_is_tx_enabled(gbe_dev->cpts))
+           !gbe_dev->tx_ts_enabled)
                return 0;
 
        /* If phy has the txtstamp api, assume it will do it.
@@ -2598,7 +2600,9 @@ static int gbe_rxtstamp(struct gbe_intf *gbe_intf, struct netcp_packet *p_info)
                return 0;
        }
 
-       cpts_rx_timestamp(gbe_dev->cpts, p_info->skb);
+       if (gbe_dev->rx_ts_enabled)
+               cpts_rx_timestamp(gbe_dev->cpts, p_info->skb);
+
        p_info->rxtstamp_complete = true;
 
        return 0;
@@ -2614,10 +2618,8 @@ static int gbe_hwtstamp_get(struct gbe_intf *gbe_intf, struct ifreq *ifr)
                return -EOPNOTSUPP;
 
        cfg.flags = 0;
-       cfg.tx_type = cpts_is_tx_enabled(cpts) ?
-                     HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-       cfg.rx_filter = (cpts_is_rx_enabled(cpts) ?
-                        cpts->rx_enable : HWTSTAMP_FILTER_NONE);
+       cfg.tx_type = gbe_dev->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+       cfg.rx_filter = gbe_dev->rx_ts_enabled;
 
        return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
 }
@@ -2628,8 +2630,8 @@ static void gbe_hwtstamp(struct gbe_intf *gbe_intf)
        struct gbe_slave *slave = gbe_intf->slave;
        u32 ts_en, seq_id, ctl;
 
-       if (!cpts_is_rx_enabled(gbe_dev->cpts) &&
-           !cpts_is_tx_enabled(gbe_dev->cpts)) {
+       if (!gbe_dev->rx_ts_enabled &&
+           !gbe_dev->tx_ts_enabled) {
                writel(0, GBE_REG_ADDR(slave, port_regs, ts_ctl));
                return;
        }
@@ -2641,10 +2643,10 @@ static void gbe_hwtstamp(struct gbe_intf *gbe_intf)
                (slave->ts_ctl.uni ?  TS_UNI_EN :
                        slave->ts_ctl.maddr_map << TS_CTL_MADDR_SHIFT);
 
-       if (cpts_is_tx_enabled(gbe_dev->cpts))
+       if (gbe_dev->tx_ts_enabled)
                ts_en |= (TS_TX_ANX_ALL_EN | TS_TX_VLAN_LT1_EN);
 
-       if (cpts_is_rx_enabled(gbe_dev->cpts))
+       if (gbe_dev->rx_ts_enabled)
                ts_en |= (TS_RX_ANX_ALL_EN | TS_RX_VLAN_LT1_EN);
 
        writel(ts_en,  GBE_REG_ADDR(slave, port_regs, ts_ctl));
@@ -2670,10 +2672,10 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
 
        switch (cfg.tx_type) {
        case HWTSTAMP_TX_OFF:
-               cpts_tx_enable(cpts, 0);
+               gbe_dev->tx_ts_enabled = 0;
                break;
        case HWTSTAMP_TX_ON:
-               cpts_tx_enable(cpts, 1);
+               gbe_dev->tx_ts_enabled = 1;
                break;
        default:
                return -ERANGE;
@@ -2681,12 +2683,12 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
 
        switch (cfg.rx_filter) {
        case HWTSTAMP_FILTER_NONE:
-               cpts_rx_enable(cpts, 0);
+               gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_NONE;
                break;
        case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
        case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
        case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
-               cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V1_L4_EVENT);
+               gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
                cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
                break;
        case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
@@ -2698,7 +2700,7 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V2_EVENT:
        case HWTSTAMP_FILTER_PTP_V2_SYNC:
        case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
-               cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V2_EVENT);
+               gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V2_EVENT;
                cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
                break;
        default:
@@ -3621,7 +3623,7 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
                return -EINVAL;
        }
 
-       if (!strcmp(node->name, "gbe")) {
+       if (of_node_name_eq(node, "gbe")) {
                ret = get_gbe_resource_version(gbe_dev, node);
                if (ret)
                        return ret;
@@ -3635,7 +3637,7 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
                else
                        ret = -ENODEV;
 
-       } else if (!strcmp(node->name, "xgbe")) {
+       } else if (of_node_name_eq(node, "xgbe")) {
                ret = set_xgbe_ethss10_priv(gbe_dev, node);
                if (ret)
                        return ret;
index 93d1428..b4ab1a5 100644 (file)
@@ -69,7 +69,9 @@ MODULE_AUTHOR("Maintainer: Samuel Chessman <chessman@tux.org>");
 MODULE_DESCRIPTION("Driver for TI ThunderLAN based ethernet PCI adapters");
 MODULE_LICENSE("GPL");
 
-/* Turn on debugging. See Documentation/networking/tlan.txt for details */
+/* Turn on debugging.
+ * See Documentation/networking/device_drivers/ti/tlan.txt for details
+ */
 static  int            debug;
 module_param(debug, int, 0);
 MODULE_PARM_DESC(debug, "ThunderLAN debug mask");
index ef9538e..8241269 100644 (file)
@@ -3605,7 +3605,7 @@ static const char velocity_gstrings[][ETH_GSTRING_LEN] = {
        "tx_jumbo",
        "rx_mac_control_frames",
        "tx_mac_control_frames",
-       "rx_frame_alignement_errors",
+       "rx_frame_alignment_errors",
        "rx_long_ok",
        "rx_long_err",
        "tx_sqe_errors",
index 30052eb..7fed88e 100644 (file)
@@ -62,19 +62,7 @@ static int fjes_dbg_status_show(struct seq_file *m, void *v)
 
        return 0;
 }
-
-static int fjes_dbg_status_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, fjes_dbg_status_show, inode->i_private);
-}
-
-static const struct file_operations fjes_dbg_status_fops = {
-       .owner          = THIS_MODULE,
-       .open           = fjes_dbg_status_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(fjes_dbg_status);
 
 void fjes_dbg_adapter_init(struct fjes_adapter *adapter)
 {
index 7c53e06..58bbba8 100644 (file)
@@ -425,7 +425,9 @@ static int geneve_udp_encap_err_lookup(struct sock *sk, struct sk_buff *skb)
 #if IS_ENABLED(CONFIG_IPV6)
        if (geneve_get_sk_family(gs) == AF_INET6) {
                struct ipv6hdr *ip6h = ipv6_hdr(skb);
-               struct in6_addr addr6 = { 0 };
+               struct in6_addr addr6;
+
+               memset(&addr6, 0, sizeof(struct in6_addr));
 
                if (!gs->collect_md) {
                        vni = geneve_hdr(skb)->vni;
index 17e6dcd..28c7499 100644 (file)
@@ -120,7 +120,7 @@ struct sixpack {
        struct timer_list       tx_t;
        struct timer_list       resync_t;
        refcount_t              refcnt;
-       struct semaphore        dead_sem;
+       struct completion       dead;
        spinlock_t              lock;
 };
 
@@ -389,7 +389,7 @@ static struct sixpack *sp_get(struct tty_struct *tty)
 static void sp_put(struct sixpack *sp)
 {
        if (refcount_dec_and_test(&sp->refcnt))
-               up(&sp->dead_sem);
+               complete(&sp->dead);
 }
 
 /*
@@ -576,7 +576,7 @@ static int sixpack_open(struct tty_struct *tty)
 
        spin_lock_init(&sp->lock);
        refcount_set(&sp->refcnt, 1);
-       sema_init(&sp->dead_sem, 0);
+       init_completion(&sp->dead);
 
        /* !!! length of the buffers. MTU is IP MTU, not PACLEN!  */
 
@@ -670,10 +670,10 @@ static void sixpack_close(struct tty_struct *tty)
         * we have to wait for all existing users to finish.
         */
        if (!refcount_dec_and_test(&sp->refcnt))
-               down(&sp->dead_sem);
+               wait_for_completion(&sp->dead);
 
        /* We must stop the queue to avoid potentially scribbling
-        * on the free buffers. The sp->dead_sem is not sufficient
+        * on the free buffers. The sp->dead completion is not sufficient
         * to protect us from sp->xbuff access.
         */
        netif_stop_queue(sp->dev);
index 802233d..4938cf4 100644 (file)
@@ -81,7 +81,7 @@ struct mkiss {
 #define CRC_MODE_SMACK_TEST    4
 
        atomic_t                refcnt;
-       struct semaphore        dead_sem;
+       struct completion       dead;
 };
 
 /*---------------------------------------------------------------------------*/
@@ -687,7 +687,7 @@ static struct mkiss *mkiss_get(struct tty_struct *tty)
 static void mkiss_put(struct mkiss *ax)
 {
        if (atomic_dec_and_test(&ax->refcnt))
-               up(&ax->dead_sem);
+               complete(&ax->dead);
 }
 
 static int crc_force = 0;      /* Can be overridden with insmod */
@@ -715,7 +715,7 @@ static int mkiss_open(struct tty_struct *tty)
 
        spin_lock_init(&ax->buflock);
        atomic_set(&ax->refcnt, 1);
-       sema_init(&ax->dead_sem, 0);
+       init_completion(&ax->dead);
 
        ax->tty = tty;
        tty->disc_data = ax;
@@ -795,7 +795,7 @@ static void mkiss_close(struct tty_struct *tty)
         * we have to wait for all existing users to finish.
         */
        if (!atomic_dec_and_test(&ax->refcnt))
-               down(&ax->dead_sem);
+               wait_for_completion(&ax->dead);
        /*
         * Halt the transmit queue so that a new transmit cannot scribble
         * on our buffers
index cf36e7f..91ed15e 100644 (file)
@@ -137,7 +137,7 @@ static int netvsc_open(struct net_device *net)
                 * slave as up. If open fails, then slave will be
                 * still be offline (and not used).
                 */
-               ret = dev_open(vf_netdev);
+               ret = dev_open(vf_netdev, NULL);
                if (ret)
                        netdev_warn(net,
                                    "unable to open slave: %s: %d\n",
@@ -605,9 +605,9 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                                     IEEE_8021Q_INFO);
 
                vlan->value = 0;
-               vlan->vlanid = skb->vlan_tci & VLAN_VID_MASK;
-               vlan->pri = (skb->vlan_tci & VLAN_PRIO_MASK) >>
-                               VLAN_PRIO_SHIFT;
+               vlan->vlanid = skb_vlan_tag_get_id(skb);
+               vlan->cfi = skb_vlan_tag_get_cfi(skb);
+               vlan->pri = skb_vlan_tag_get_prio(skb);
        }
 
        if (skb_is_gso(skb)) {
@@ -781,7 +781,8 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
        }
 
        if (vlan) {
-               u16 vlan_tci = vlan->vlanid | (vlan->pri << VLAN_PRIO_SHIFT);
+               u16 vlan_tci = vlan->vlanid | (vlan->pri << VLAN_PRIO_SHIFT) |
+                       (vlan->cfi ? VLAN_CFI_MASK : 0);
 
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
                                       vlan_tci);
@@ -1246,7 +1247,7 @@ static int netvsc_set_mac_addr(struct net_device *ndev, void *p)
                return -ENODEV;
 
        if (vf_netdev) {
-               err = dev_set_mac_address(vf_netdev, addr);
+               err = dev_set_mac_address(vf_netdev, addr, NULL);
                if (err)
                        return err;
        }
@@ -1257,7 +1258,7 @@ static int netvsc_set_mac_addr(struct net_device *ndev, void *p)
        } else if (vf_netdev) {
                /* rollback change on VF */
                memcpy(addr->sa_data, ndev->dev_addr, ETH_ALEN);
-               dev_set_mac_address(vf_netdev, addr);
+               dev_set_mac_address(vf_netdev, addr, NULL);
        }
 
        return err;
@@ -1992,7 +1993,7 @@ static void __netvsc_vf_setup(struct net_device *ndev,
                            "unable to change mtu to %u\n", ndev->mtu);
 
        /* set multicast etc flags on VF */
-       dev_change_flags(vf_netdev, ndev->flags | IFF_SLAVE);
+       dev_change_flags(vf_netdev, ndev->flags | IFF_SLAVE, NULL);
 
        /* sync address list from ndev to VF */
        netif_addr_lock_bh(ndev);
@@ -2001,7 +2002,7 @@ static void __netvsc_vf_setup(struct net_device *ndev,
        netif_addr_unlock_bh(ndev);
 
        if (netif_running(ndev)) {
-               ret = dev_open(vf_netdev);
+               ret = dev_open(vf_netdev, NULL);
                if (ret)
                        netdev_warn(vf_netdev,
                                    "unable to open: %d\n", ret);
index 3d9e915..0253eb5 100644 (file)
@@ -1632,18 +1632,7 @@ static int at86rf230_stats_show(struct seq_file *file, void *offset)
        seq_printf(file, "INVALID:\t\t%8llu\n", lp->trac.invalid);
        return 0;
 }
-
-static int at86rf230_stats_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, at86rf230_stats_show, inode->i_private);
-}
-
-static const struct file_operations at86rf230_stats_fops = {
-       .open           = at86rf230_stats_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(at86rf230_stats);
 
 static int at86rf230_debugfs_init(struct at86rf230_local *lp)
 {
index 0ff5a40..b2ff903 100644 (file)
@@ -721,7 +721,7 @@ static void ca8210_mlme_reset_worker(struct work_struct *work)
 static void ca8210_rx_done(struct cas_control *cas_ctl)
 {
        u8 *buf;
-       u8 len;
+       unsigned int len;
        struct work_priv_container *mlme_reset_wpc;
        struct ca8210_priv *priv = cas_ctl->priv;
 
@@ -730,7 +730,7 @@ static void ca8210_rx_done(struct cas_control *cas_ctl)
        if (len > CA8210_SPI_BUF_SIZE) {
                dev_crit(
                        &priv->spi->dev,
-                       "Received packet len (%d) erroneously long\n",
+                       "Received packet len (%u) erroneously long\n",
                        len
                );
                goto finish;
index 51b5198..b6743f0 100644 (file)
@@ -492,7 +492,7 @@ static int hwsim_del_edge_nl(struct sk_buff *msg, struct genl_info *info)
            !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
                return -EINVAL;
 
-       if (nla_parse_nested(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX + 1,
+       if (nla_parse_nested(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX,
                             info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE],
                             hwsim_edge_policy, NULL))
                return -EINVAL;
@@ -542,7 +542,7 @@ static int hwsim_set_edge_lqi(struct sk_buff *msg, struct genl_info *info)
            !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
                return -EINVAL;
 
-       if (nla_parse_nested(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX + 1,
+       if (nla_parse_nested(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX,
                             info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE],
                             hwsim_edge_policy, NULL))
                return -EINVAL;
index 4a94956..19bdde6 100644 (file)
@@ -71,7 +71,8 @@ static void ipvlan_unregister_nf_hook(struct net *net)
                                        ARRAY_SIZE(ipvl_nfops));
 }
 
-static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval)
+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;
@@ -84,10 +85,12 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval)
                        flags = ipvlan->dev->flags;
                        if (nval == IPVLAN_MODE_L3 || nval == IPVLAN_MODE_L3S) {
                                err = dev_change_flags(ipvlan->dev,
-                                                      flags | IFF_NOARP);
+                                                      flags | IFF_NOARP,
+                                                      extack);
                        } else {
                                err = dev_change_flags(ipvlan->dev,
-                                                      flags & ~IFF_NOARP);
+                                                      flags & ~IFF_NOARP,
+                                                      extack);
                        }
                        if (unlikely(err))
                                goto fail;
@@ -116,9 +119,11 @@ fail:
                flags = ipvlan->dev->flags;
                if (port->mode == IPVLAN_MODE_L3 ||
                    port->mode == IPVLAN_MODE_L3S)
-                       dev_change_flags(ipvlan->dev, flags | IFF_NOARP);
+                       dev_change_flags(ipvlan->dev, flags | IFF_NOARP,
+                                        NULL);
                else
-                       dev_change_flags(ipvlan->dev, flags & ~IFF_NOARP);
+                       dev_change_flags(ipvlan->dev, flags & ~IFF_NOARP,
+                                        NULL);
        }
 
        return err;
@@ -498,7 +503,7 @@ static int ipvlan_nl_changelink(struct net_device *dev,
        if (data[IFLA_IPVLAN_MODE]) {
                u16 nmode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
 
-               err = ipvlan_set_port_mode(port, nmode);
+               err = ipvlan_set_port_mode(port, nmode, extack);
        }
 
        if (!err && data[IFLA_IPVLAN_FLAGS]) {
@@ -535,7 +540,7 @@ static int ipvlan_nl_validate(struct nlattr *tb[], struct nlattr *data[],
        if (data[IFLA_IPVLAN_MODE]) {
                u16 mode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
 
-               if (mode < IPVLAN_MODE_L2 || mode >= IPVLAN_MODE_MAX)
+               if (mode >= IPVLAN_MODE_MAX)
                        return -EINVAL;
        }
        if (data[IFLA_IPVLAN_FLAGS]) {
@@ -672,7 +677,7 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev,
        if (data && data[IFLA_IPVLAN_MODE])
                mode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
 
-       err = ipvlan_set_port_mode(port, mode);
+       err = ipvlan_set_port_mode(port, mode, extack);
        if (err)
                goto unlink_netdev;
 
@@ -754,10 +759,13 @@ EXPORT_SYMBOL_GPL(ipvlan_link_register);
 static int ipvlan_device_event(struct notifier_block *unused,
                               unsigned long event, void *ptr)
 {
+       struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
+       struct netdev_notifier_pre_changeaddr_info *prechaddr_info;
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct ipvl_dev *ipvlan, *next;
        struct ipvl_port *port;
        LIST_HEAD(lst_kill);
+       int err;
 
        if (!netif_is_ipvlan_port(dev))
                return NOTIFY_DONE;
@@ -813,6 +821,17 @@ static int ipvlan_device_event(struct notifier_block *unused,
                        ipvlan_adjust_mtu(ipvlan, dev);
                break;
 
+       case NETDEV_PRE_CHANGEADDR:
+               prechaddr_info = ptr;
+               list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+                       err = dev_pre_changeaddr_notify(ipvlan->dev,
+                                                   prechaddr_info->dev_addr,
+                                                   extack);
+                       if (err)
+                               return notifier_from_errno(err);
+               }
+               break;
+
        case NETDEV_CHANGEADDR:
                list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
                        ether_addr_copy(ipvlan->dev->dev_addr, dev->dev_addr);
index fc8d5f1..fc726ce 100644 (file)
@@ -608,7 +608,7 @@ static int macvlan_open(struct net_device *dev)
                goto hash_add;
        }
 
-       err = -EBUSY;
+       err = -EADDRINUSE;
        if (macvlan_addr_busy(vlan->port, dev->dev_addr))
                goto out;
 
@@ -706,7 +706,7 @@ static int macvlan_sync_address(struct net_device *dev, unsigned char *addr)
        } else {
                /* Rehash and update the device filters */
                if (macvlan_addr_busy(vlan->port, addr))
-                       return -EBUSY;
+                       return -EADDRINUSE;
 
                if (!macvlan_passthru(port)) {
                        err = dev_uc_add(lowerdev, addr);
@@ -744,9 +744,12 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p)
 
        if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
                macvlan_set_addr_change(vlan->port);
-               return dev_set_mac_address(vlan->lowerdev, addr);
+               return dev_set_mac_address(vlan->lowerdev, addr, NULL);
        }
 
+       if (macvlan_addr_busy(vlan->port, addr->sa_data))
+               return -EADDRINUSE;
+
        return macvlan_sync_address(dev, addr->sa_data);
 }
 
@@ -1210,7 +1213,7 @@ static void macvlan_port_destroy(struct net_device *dev)
 
                sa.sa_family = port->dev->type;
                memcpy(&sa.sa_data, port->perm_addr, port->dev->addr_len);
-               dev_set_mac_address(port->dev, &sa);
+               dev_set_mac_address(port->dev, &sa, NULL);
        }
 
        kfree(port);
index e964d31..ed1166a 100644 (file)
@@ -40,14 +40,14 @@ static int net_failover_open(struct net_device *dev)
 
        primary_dev = rtnl_dereference(nfo_info->primary_dev);
        if (primary_dev) {
-               err = dev_open(primary_dev);
+               err = dev_open(primary_dev, NULL);
                if (err)
                        goto err_primary_open;
        }
 
        standby_dev = rtnl_dereference(nfo_info->standby_dev);
        if (standby_dev) {
-               err = dev_open(standby_dev);
+               err = dev_open(standby_dev, NULL);
                if (err)
                        goto err_standby_open;
        }
@@ -517,7 +517,7 @@ static int net_failover_slave_register(struct net_device *slave_dev,
        dev_hold(slave_dev);
 
        if (netif_running(failover_dev)) {
-               err = dev_open(slave_dev);
+               err = dev_open(slave_dev, NULL);
                if (err && (err != -EBUSY)) {
                        netdev_err(failover_dev, "Opening slave %s failed err:%d\n",
                                   slave_dev->name, err);
@@ -680,7 +680,7 @@ static int net_failover_slave_name_change(struct net_device *slave_dev,
        /* We need to bring up the slave after the rename by udev in case
         * open failed with EBUSY when it was registered.
         */
-       dev_open(slave_dev);
+       dev_open(slave_dev, NULL);
 
        return 0;
 }
index cb35184..172b271 100644 (file)
@@ -48,7 +48,7 @@ struct nsim_bpf_bound_map {
        struct list_head l;
 };
 
-static int nsim_debugfs_bpf_string_read(struct seq_file *file, void *data)
+static int nsim_bpf_string_show(struct seq_file *file, void *data)
 {
        const char **str = file->private;
 
@@ -57,19 +57,7 @@ static int nsim_debugfs_bpf_string_read(struct seq_file *file, void *data)
 
        return 0;
 }
-
-static int nsim_debugfs_bpf_string_open(struct inode *inode, struct file *f)
-{
-       return single_open(f, nsim_debugfs_bpf_string_read, inode->i_private);
-}
-
-static const struct file_operations nsim_bpf_string_fops = {
-       .owner = THIS_MODULE,
-       .open = nsim_debugfs_bpf_string_open,
-       .release = single_release,
-       .read = seq_read,
-       .llseek = seq_lseek
-};
+DEFINE_SHOW_ATTRIBUTE(nsim_bpf_string);
 
 static int
 nsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn)
@@ -91,11 +79,6 @@ static int nsim_bpf_finalize(struct bpf_verifier_env *env)
        return 0;
 }
 
-static const struct bpf_prog_offload_ops nsim_bpf_analyzer_ops = {
-       .insn_hook      = nsim_bpf_verify_insn,
-       .finalize       = nsim_bpf_finalize,
-};
-
 static bool nsim_xdp_offload_active(struct netdevsim *ns)
 {
        return ns->xdp_hw.prog;
@@ -263,6 +246,24 @@ static int nsim_bpf_create_prog(struct netdevsim *ns, struct bpf_prog *prog)
        return 0;
 }
 
+static int nsim_bpf_verifier_prep(struct bpf_prog *prog)
+{
+       struct netdevsim *ns = netdev_priv(prog->aux->offload->netdev);
+
+       if (!ns->bpf_bind_accept)
+               return -EOPNOTSUPP;
+
+       return nsim_bpf_create_prog(ns, prog);
+}
+
+static int nsim_bpf_translate(struct bpf_prog *prog)
+{
+       struct nsim_bpf_bound_prog *state = prog->aux->offload->dev_priv;
+
+       state->state = "xlated";
+       return 0;
+}
+
 static void nsim_bpf_destroy_prog(struct bpf_prog *prog)
 {
        struct nsim_bpf_bound_prog *state;
@@ -275,6 +276,14 @@ static void nsim_bpf_destroy_prog(struct bpf_prog *prog)
        kfree(state);
 }
 
+static const struct bpf_prog_offload_ops nsim_bpf_dev_ops = {
+       .insn_hook      = nsim_bpf_verify_insn,
+       .finalize       = nsim_bpf_finalize,
+       .prepare        = nsim_bpf_verifier_prep,
+       .translate      = nsim_bpf_translate,
+       .destroy        = nsim_bpf_destroy_prog,
+};
+
 static int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
 {
        if (bpf->prog && bpf->prog->aux->offload) {
@@ -533,30 +542,11 @@ static void nsim_bpf_map_free(struct bpf_offloaded_map *offmap)
 int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)
 {
        struct netdevsim *ns = netdev_priv(dev);
-       struct nsim_bpf_bound_prog *state;
        int err;
 
        ASSERT_RTNL();
 
        switch (bpf->command) {
-       case BPF_OFFLOAD_VERIFIER_PREP:
-               if (!ns->bpf_bind_accept)
-                       return -EOPNOTSUPP;
-
-               err = nsim_bpf_create_prog(ns, bpf->verifier.prog);
-               if (err)
-                       return err;
-
-               bpf->verifier.ops = &nsim_bpf_analyzer_ops;
-               return 0;
-       case BPF_OFFLOAD_TRANSLATE:
-               state = bpf->offload.prog->aux->offload->dev_priv;
-
-               state->state = "xlated";
-               return 0;
-       case BPF_OFFLOAD_DESTROY:
-               nsim_bpf_destroy_prog(bpf->offload.prog);
-               return 0;
        case XDP_QUERY_PROG:
                return xdp_attachment_query(&ns->xdp, bpf);
        case XDP_QUERY_PROG_HW:
@@ -599,7 +589,7 @@ 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();
+               ns->sdev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops);
                err = PTR_ERR_OR_ZERO(ns->sdev->bpf_dev);
                if (err)
                        return err;
index 2dcf6cc..76e11d8 100644 (file)
@@ -227,18 +227,19 @@ static const struct xfrmdev_ops nsim_xfrmdev_ops = {
 
 bool nsim_ipsec_tx(struct netdevsim *ns, struct sk_buff *skb)
 {
+       struct sec_path *sp = skb_sec_path(skb);
        struct nsim_ipsec *ipsec = &ns->ipsec;
        struct xfrm_state *xs;
        struct nsim_sa *tsa;
        u32 sa_idx;
 
        /* do we even need to check this packet? */
-       if (!skb->sp)
+       if (!sp)
                return true;
 
-       if (unlikely(!skb->sp->len)) {
+       if (unlikely(!sp->len)) {
                netdev_err(ns->netdev, "no xfrm state len = %d\n",
-                          skb->sp->len);
+                          sp->len);
                return false;
        }
 
index f7fb627..72d43c8 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/gpio.h>
 #include <linux/seqlock.h>
 #include <linux/idr.h>
+#include <linux/netdevice.h>
 
 #include "swphy.h"
 
@@ -38,6 +39,7 @@ struct fixed_phy {
        struct phy_device *phydev;
        seqcount_t seqcount;
        struct fixed_phy_status status;
+       bool no_carrier;
        int (*link_update)(struct net_device *, struct fixed_phy_status *);
        struct list_head node;
        int link_gpio;
@@ -48,9 +50,28 @@ static struct fixed_mdio_bus platform_fmb = {
        .phys = LIST_HEAD_INIT(platform_fmb.phys),
 };
 
+int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier)
+{
+       struct fixed_mdio_bus *fmb = &platform_fmb;
+       struct phy_device *phydev = dev->phydev;
+       struct fixed_phy *fp;
+
+       if (!phydev || !phydev->mdio.bus)
+               return -EINVAL;
+
+       list_for_each_entry(fp, &fmb->phys, node) {
+               if (fp->addr == phydev->mdio.addr) {
+                       fp->no_carrier = !new_carrier;
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(fixed_phy_change_carrier);
+
 static void fixed_phy_update(struct fixed_phy *fp)
 {
-       if (gpio_is_valid(fp->link_gpio))
+       if (!fp->no_carrier && gpio_is_valid(fp->link_gpio))
                fp->status.link = !!gpio_get_value_cansleep(fp->link_gpio);
 }
 
@@ -66,6 +87,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
 
                        do {
                                s = read_seqcount_begin(&fp->seqcount);
+                               fp->status.link = !fp->no_carrier;
                                /* Issue callback if user registered it. */
                                if (fp->link_update) {
                                        fp->link_update(fp->phydev->attached_dev,
index 21ce689..7d5938b 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/mii.h>
 #include <linux/ethtool.h>
 #include <linux/phy.h>
+#include <linux/property.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -36,14 +37,34 @@ MODULE_LICENSE("GPL");
 
 /* IP101A/G - IP1001 */
 #define IP10XX_SPEC_CTRL_STATUS                16      /* Spec. Control Register */
-#define IP1001_RXPHASE_SEL             (1<<0)  /* Add delay on RX_CLK */
-#define IP1001_TXPHASE_SEL             (1<<1)  /* Add delay on TX_CLK */
+#define IP1001_RXPHASE_SEL             BIT(0)  /* Add delay on RX_CLK */
+#define IP1001_TXPHASE_SEL             BIT(1)  /* Add delay on TX_CLK */
 #define IP1001_SPEC_CTRL_STATUS_2      20      /* IP1001 Spec. Control Reg 2 */
 #define IP1001_APS_ON                  11      /* IP1001 APS Mode  bit */
-#define IP101A_G_APS_ON                        2       /* IP101A/G APS Mode bit */
+#define IP101A_G_APS_ON                        BIT(1)  /* IP101A/G APS Mode bit */
 #define IP101A_G_IRQ_CONF_STATUS       0x11    /* Conf Info IRQ & Status Reg */
-#define        IP101A_G_IRQ_PIN_USED           (1<<15) /* INTR pin used */
-#define        IP101A_G_IRQ_DEFAULT            IP101A_G_IRQ_PIN_USED
+#define        IP101A_G_IRQ_PIN_USED           BIT(15) /* INTR pin used */
+#define IP101A_G_IRQ_ALL_MASK          BIT(11) /* IRQ's inactive */
+#define IP101A_G_IRQ_SPEED_CHANGE      BIT(2)
+#define IP101A_G_IRQ_DUPLEX_CHANGE     BIT(1)
+#define IP101A_G_IRQ_LINK_CHANGE       BIT(0)
+
+#define IP101G_DIGITAL_IO_SPEC_CTRL                    0x1d
+#define IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32         BIT(2)
+
+/* The 32-pin IP101GR package can re-configure the mode of the RXER/INTR_32 pin
+ * (pin number 21). The hardware default is RXER (receive error) mode. But it
+ * can be configured to interrupt mode manually.
+ */
+enum ip101gr_sel_intr32 {
+       IP101GR_SEL_INTR32_KEEP,
+       IP101GR_SEL_INTR32_INTR,
+       IP101GR_SEL_INTR32_RXER,
+};
+
+struct ip101a_g_phy_priv {
+       enum ip101gr_sel_intr32 sel_intr32;
+};
 
 static int ip175c_config_init(struct phy_device *phydev)
 {
@@ -162,18 +183,92 @@ static int ip1001_config_init(struct phy_device *phydev)
        return 0;
 }
 
+static int ip175c_read_status(struct phy_device *phydev)
+{
+       if (phydev->mdio.addr == 4) /* WAN port */
+               genphy_read_status(phydev);
+       else
+               /* Don't need to read status for switch ports */
+               phydev->irq = PHY_IGNORE_INTERRUPT;
+
+       return 0;
+}
+
+static int ip175c_config_aneg(struct phy_device *phydev)
+{
+       if (phydev->mdio.addr == 4) /* WAN port */
+               genphy_config_aneg(phydev);
+
+       return 0;
+}
+
+static int ip101a_g_probe(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->mdio.dev;
+       struct ip101a_g_phy_priv *priv;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       /* Both functions (RX error and interrupt status) are sharing the same
+        * pin on the 32-pin IP101GR, so this is an exclusive choice.
+        */
+       if (device_property_read_bool(dev, "icplus,select-rx-error") &&
+           device_property_read_bool(dev, "icplus,select-interrupt")) {
+               dev_err(dev,
+                       "RXER and INTR mode cannot be selected together\n");
+               return -EINVAL;
+       }
+
+       if (device_property_read_bool(dev, "icplus,select-rx-error"))
+               priv->sel_intr32 = IP101GR_SEL_INTR32_RXER;
+       else if (device_property_read_bool(dev, "icplus,select-interrupt"))
+               priv->sel_intr32 = IP101GR_SEL_INTR32_INTR;
+       else
+               priv->sel_intr32 = IP101GR_SEL_INTR32_KEEP;
+
+       phydev->priv = priv;
+
+       return 0;
+}
+
 static int ip101a_g_config_init(struct phy_device *phydev)
 {
-       int c;
+       struct ip101a_g_phy_priv *priv = phydev->priv;
+       int err, c;
 
        c = ip1xx_reset(phydev);
        if (c < 0)
                return c;
 
-       /* INTR pin used: speed/link/duplex will cause an interrupt */
-       c = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, IP101A_G_IRQ_DEFAULT);
-       if (c < 0)
-               return c;
+       /* configure the RXER/INTR_32 pin of the 32-pin IP101GR if needed: */
+       switch (priv->sel_intr32) {
+       case IP101GR_SEL_INTR32_RXER:
+               err = phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL,
+                                IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32, 0);
+               if (err < 0)
+                       return err;
+               break;
+
+       case IP101GR_SEL_INTR32_INTR:
+               err = phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL,
+                                IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32,
+                                IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32);
+               if (err < 0)
+                       return err;
+               break;
+
+       default:
+               /* Don't touch IP101G_DIGITAL_IO_SPEC_CTRL because it's not
+                * documented on IP101A and it's not clear whether this would
+                * cause problems.
+                * For the 32-pin IP101GR we simply keep the SEL_INTR32
+                * configuration as set by the bootloader when not configured
+                * to one of the special functions.
+                */
+               break;
+       }
 
        /* Enable Auto Power Saving mode */
        c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS);
@@ -182,23 +277,29 @@ static int ip101a_g_config_init(struct phy_device *phydev)
        return phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c);
 }
 
-static int ip175c_read_status(struct phy_device *phydev)
+static int ip101a_g_config_intr(struct phy_device *phydev)
 {
-       if (phydev->mdio.addr == 4) /* WAN port */
-               genphy_read_status(phydev);
+       u16 val;
+
+       if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+               /* INTR pin used: Speed/link/duplex will cause an interrupt */
+               val = IP101A_G_IRQ_PIN_USED;
        else
-               /* Don't need to read status for switch ports */
-               phydev->irq = PHY_IGNORE_INTERRUPT;
+               val = IP101A_G_IRQ_ALL_MASK;
 
-       return 0;
+       return phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, val);
 }
 
-static int ip175c_config_aneg(struct phy_device *phydev)
+static int ip101a_g_did_interrupt(struct phy_device *phydev)
 {
-       if (phydev->mdio.addr == 4) /* WAN port */
-               genphy_config_aneg(phydev);
+       int val = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS);
 
-       return 0;
+       if (val < 0)
+               return 0;
+
+       return val & (IP101A_G_IRQ_SPEED_CHANGE |
+                     IP101A_G_IRQ_DUPLEX_CHANGE |
+                     IP101A_G_IRQ_LINK_CHANGE);
 }
 
 static int ip101a_g_ack_interrupt(struct phy_device *phydev)
@@ -234,6 +335,9 @@ static struct phy_driver icplus_driver[] = {
        .name           = "ICPlus IP101A/G",
        .phy_id_mask    = 0x0ffffff0,
        .features       = PHY_BASIC_FEATURES,
+       .probe          = ip101a_g_probe,
+       .config_intr    = ip101a_g_config_intr,
+       .did_interrupt  = ip101a_g_did_interrupt,
        .ack_interrupt  = ip101a_g_ack_interrupt,
        .config_init    = &ip101a_g_config_init,
        .suspend        = genphy_suspend,
index 6a98819..a9c7c7f 100644 (file)
@@ -1047,21 +1047,21 @@ static int m88e1145_config_init(struct phy_device *phydev)
 }
 
 /**
- * fiber_lpa_to_linkmode_lpa_t
+ * fiber_lpa_mod_linkmode_lpa_t
  * @advertising: the linkmode advertisement settings
  * @lpa: value of the MII_LPA register for fiber link
  *
- * A small helper function that translates MII_LPA
- * bits to linkmode LP advertisement settings.
+ * A small helper function that translates MII_LPA bits to linkmode LP
+ * advertisement settings. Other bits in advertising are left
+ * unchanged.
  */
-static void fiber_lpa_to_linkmode_lpa_t(unsigned long *advertising, u32 lpa)
+static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa)
 {
-       if (lpa & LPA_FIBER_1000HALF)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-                                advertising);
-       if (lpa & LPA_FIBER_1000FULL)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-                                advertising);
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                        advertising, lpa & LPA_FIBER_1000HALF);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                        advertising, lpa & LPA_FIBER_1000FULL);
 }
 
 /**
@@ -1138,7 +1138,7 @@ static int marvell_read_status_page_an(struct phy_device *phydev,
 
        if (!fiber) {
                mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa);
-               mii_stat1000_to_linkmode_lpa_t(phydev->lp_advertising, lpagb);
+               mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, lpagb);
 
                if (phydev->duplex == DUPLEX_FULL) {
                        phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
@@ -1146,7 +1146,7 @@ static int marvell_read_status_page_an(struct phy_device *phydev,
                }
        } else {
                /* The fiber link is only 1000M capable */
-               fiber_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa);
+               fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
 
                if (phydev->duplex == DUPLEX_FULL) {
                        if (!(lpa & LPA_PAUSE_FIBER)) {
index 6f6e886..82ab6ed 100644 (file)
@@ -490,7 +490,7 @@ static int mv3310_read_status(struct phy_device *phydev)
                if (val < 0)
                        return val;
 
-               mii_stat1000_to_linkmode_lpa_t(phydev->lp_advertising, val);
+               mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
 
                if (phydev->autoneg == AUTONEG_ENABLE)
                        phy_resolve_aneg_linkmode(phydev);
index 3326574..ea9a0e3 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/mdio-gpio.h>
 #include <linux/mdio-bitbang.h>
 #include <linux/mdio-gpio.h>
 #include <linux/gpio/consumer.h>
@@ -63,7 +64,7 @@ static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
                 * assume the pin serves as pull-up. If direction is
                 * output, the default value is high.
                 */
-               gpiod_set_value(bitbang->mdo, 1);
+               gpiod_set_value_cansleep(bitbang->mdo, 1);
                return;
        }
 
@@ -78,7 +79,7 @@ static int mdio_get(struct mdiobb_ctrl *ctrl)
        struct mdio_gpio_info *bitbang =
                container_of(ctrl, struct mdio_gpio_info, ctrl);
 
-       return gpiod_get_value(bitbang->mdio);
+       return gpiod_get_value_cansleep(bitbang->mdio);
 }
 
 static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
@@ -87,9 +88,9 @@ static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
                container_of(ctrl, struct mdio_gpio_info, ctrl);
 
        if (bitbang->mdo)
-               gpiod_set_value(bitbang->mdo, what);
+               gpiod_set_value_cansleep(bitbang->mdo, what);
        else
-               gpiod_set_value(bitbang->mdio, what);
+               gpiod_set_value_cansleep(bitbang->mdio, what);
 }
 
 static void mdc_set(struct mdiobb_ctrl *ctrl, int what)
@@ -97,7 +98,7 @@ static void mdc_set(struct mdiobb_ctrl *ctrl, int what)
        struct mdio_gpio_info *bitbang =
                container_of(ctrl, struct mdio_gpio_info, ctrl);
 
-       gpiod_set_value(bitbang->mdc, what);
+       gpiod_set_value_cansleep(bitbang->mdc, what);
 }
 
 static const struct mdiobb_ops mdio_gpio_ops = {
@@ -112,6 +113,7 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
                                          struct mdio_gpio_info *bitbang,
                                          int bus_id)
 {
+       struct mdio_gpio_platform_data *pdata = dev_get_platdata(dev);
        struct mii_bus *new_bus;
 
        bitbang->ctrl.ops = &mdio_gpio_ops;
@@ -128,6 +130,11 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
        else
                strncpy(new_bus->id, "gpio", MII_BUS_ID_SIZE);
 
+       if (pdata) {
+               new_bus->phy_mask = pdata->phy_mask;
+               new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask;
+       }
+
        dev_set_drvdata(dev, new_bus);
 
        return new_bus;
index 62269e5..3949fe2 100644 (file)
@@ -810,17 +810,13 @@ static int vsc85xx_default_config(struct phy_device *phydev)
 
        phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
        mutex_lock(&phydev->lock);
-       rc = phy_select_page(phydev, MSCC_PHY_PAGE_EXTENDED_2);
-       if (rc < 0)
-               goto out_unlock;
 
-       reg_val = phy_read(phydev, MSCC_PHY_RGMII_CNTL);
-       reg_val &= ~(RGMII_RX_CLK_DELAY_MASK);
-       reg_val |= (RGMII_RX_CLK_DELAY_1_1_NS << RGMII_RX_CLK_DELAY_POS);
-       phy_write(phydev, MSCC_PHY_RGMII_CNTL, reg_val);
+       reg_val = RGMII_RX_CLK_DELAY_1_1_NS << RGMII_RX_CLK_DELAY_POS;
+
+       rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_EXTENDED_2,
+                             MSCC_PHY_RGMII_CNTL, RGMII_RX_CLK_DELAY_MASK,
+                             reg_val);
 
-out_unlock:
-       rc = phy_restore_page(phydev, rc, rc > 0 ? 0 : rc);
        mutex_unlock(&phydev->lock);
 
        return rc;
@@ -857,6 +853,51 @@ static void vsc85xx_tr_write(struct phy_device *phydev, u16 addr, u32 val)
        __phy_write(phydev, MSCC_PHY_TR_CNTL, TR_WRITE | TR_ADDR(addr));
 }
 
+static int vsc8531_pre_init_seq_set(struct phy_device *phydev)
+{
+       int rc;
+       const struct reg_val init_seq[] = {
+               {0x0f90, 0x00688980},
+               {0x0696, 0x00000003},
+               {0x07fa, 0x0050100f},
+               {0x1686, 0x00000004},
+       };
+       unsigned int i;
+       int oldpage;
+
+       rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_STANDARD,
+                             MSCC_PHY_EXT_CNTL_STATUS, SMI_BROADCAST_WR_EN,
+                             SMI_BROADCAST_WR_EN);
+       if (rc < 0)
+               return rc;
+       rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_TEST,
+                             MSCC_PHY_TEST_PAGE_24, 0, 0x0400);
+       if (rc < 0)
+               return rc;
+       rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_TEST,
+                             MSCC_PHY_TEST_PAGE_5, 0x0a00, 0x0e00);
+       if (rc < 0)
+               return rc;
+       rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_TEST,
+                             MSCC_PHY_TEST_PAGE_8, 0x8000, 0x8000);
+       if (rc < 0)
+               return rc;
+
+       mutex_lock(&phydev->lock);
+       oldpage = phy_select_page(phydev, MSCC_PHY_PAGE_TR);
+       if (oldpage < 0)
+               goto out_unlock;
+
+       for (i = 0; i < ARRAY_SIZE(init_seq); i++)
+               vsc85xx_tr_write(phydev, init_seq[i].reg, init_seq[i].val);
+
+out_unlock:
+       oldpage = phy_restore_page(phydev, oldpage, oldpage);
+       mutex_unlock(&phydev->lock);
+
+       return oldpage;
+}
+
 static int vsc85xx_eee_init_seq_set(struct phy_device *phydev)
 {
        const struct reg_val init_eee[] = {
@@ -1654,7 +1695,7 @@ err:
 
 static int vsc85xx_config_init(struct phy_device *phydev)
 {
-       int rc, i;
+       int rc, i, phy_id;
        struct vsc8531_private *vsc8531 = phydev->priv;
 
        rc = vsc85xx_default_config(phydev);
@@ -1669,6 +1710,14 @@ static int vsc85xx_config_init(struct phy_device *phydev)
        if (rc)
                return rc;
 
+       phy_id = phydev->drv->phy_id & phydev->drv->phy_id_mask;
+       if (PHY_ID_VSC8531 == phy_id || PHY_ID_VSC8541 == phy_id ||
+           PHY_ID_VSC8530 == phy_id || PHY_ID_VSC8540 == phy_id) {
+               rc = vsc8531_pre_init_seq_set(phydev);
+               if (rc)
+                       return rc;
+       }
+
        rc = vsc85xx_eee_init_seq_set(phydev);
        if (rc)
                return rc;
index d738733..d33e7b3 100644 (file)
@@ -328,7 +328,7 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,
        if (autoneg != AUTONEG_ENABLE && autoneg != AUTONEG_DISABLE)
                return -EINVAL;
 
-       if (autoneg == AUTONEG_ENABLE && advertising == 0)
+       if (autoneg == AUTONEG_ENABLE && linkmode_empty(advertising))
                return -EINVAL;
 
        if (autoneg == AUTONEG_DISABLE &&
@@ -437,8 +437,8 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
                                }
                                break;
                        case MII_ADVERTISE:
-                               mii_adv_to_linkmode_adv_t(phydev->advertising,
-                                                         val);
+                               mii_adv_mod_linkmode_adv_t(phydev->advertising,
+                                                          val);
                                change_autoneg = true;
                                break;
                        default:
@@ -543,6 +543,13 @@ int phy_start_aneg(struct phy_device *phydev)
 
        mutex_lock(&phydev->lock);
 
+       if (!__phy_is_started(phydev)) {
+               WARN(1, "called from state %s\n",
+                    phy_state_to_str(phydev->state));
+               err = -EBUSY;
+               goto out_unlock;
+       }
+
        if (AUTONEG_DISABLE == phydev->autoneg)
                phy_sanitize_settings(phydev);
 
@@ -553,13 +560,11 @@ int phy_start_aneg(struct phy_device *phydev)
        if (err < 0)
                goto out_unlock;
 
-       if (phydev->state != PHY_HALTED) {
-               if (AUTONEG_ENABLE == phydev->autoneg) {
-                       err = phy_check_link_status(phydev);
-               } else {
-                       phydev->state = PHY_FORCING;
-                       phydev->link_timeout = PHY_FORCE_TIMEOUT;
-               }
+       if (phydev->autoneg == AUTONEG_ENABLE) {
+               err = phy_check_link_status(phydev);
+       } else {
+               phydev->state = PHY_FORCING;
+               phydev->link_timeout = PHY_FORCE_TIMEOUT;
        }
 
 out_unlock:
@@ -709,7 +714,7 @@ void phy_stop_machine(struct phy_device *phydev)
        cancel_delayed_work_sync(&phydev->state_queue);
 
        mutex_lock(&phydev->lock);
-       if (phydev->state > PHY_UP && phydev->state != PHY_HALTED)
+       if (__phy_is_started(phydev))
                phydev->state = PHY_UP;
        mutex_unlock(&phydev->lock);
 }
@@ -725,6 +730,8 @@ void phy_stop_machine(struct phy_device *phydev)
  */
 static void phy_error(struct phy_device *phydev)
 {
+       WARN_ON(1);
+
        mutex_lock(&phydev->lock);
        phydev->state = PHY_HALTED;
        mutex_unlock(&phydev->lock);
@@ -760,7 +767,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
 {
        struct phy_device *phydev = phy_dat;
 
-       if (PHY_HALTED == phydev->state)
+       if (!phy_is_started(phydev))
                return IRQ_NONE;                /* It can't be ours.  */
 
        if (phydev->drv->did_interrupt && !phydev->drv->did_interrupt(phydev))
@@ -842,15 +849,18 @@ void phy_stop(struct phy_device *phydev)
 {
        mutex_lock(&phydev->lock);
 
-       if (PHY_HALTED == phydev->state)
-               goto out_unlock;
+       if (!__phy_is_started(phydev)) {
+               WARN(1, "called from state %s\n",
+                    phy_state_to_str(phydev->state));
+               mutex_unlock(&phydev->lock);
+               return;
+       }
 
        if (phy_interrupt_is_valid(phydev))
                phy_disable_interrupts(phydev);
 
        phydev->state = PHY_HALTED;
 
-out_unlock:
        mutex_unlock(&phydev->lock);
 
        phy_state_machine(&phydev->state_queue.work);
@@ -984,7 +994,7 @@ void phy_state_machine(struct work_struct *work)
         * state machine would be pointless and possibly error prone when
         * called from phy_disconnect() synchronously.
         */
-       if (phy_polling_mode(phydev) && old_state != PHY_HALTED)
+       if (phy_polling_mode(phydev) && phy_is_started(phydev))
                phy_queue_state_machine(phydev, PHY_STATE_TIME);
 }
 
@@ -1144,6 +1154,7 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
        if (val < 0)
                return val;
        data->advertised = mmd_eee_adv_to_ethtool_adv_t(val);
+       data->eee_enabled = !!data->advertised;
 
        /* Get LP advertisement EEE */
        val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
@@ -1151,6 +1162,8 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
                return val;
        data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val);
 
+       data->eee_active = !!(data->advertised & data->lp_advertised);
+
        return 0;
 }
 EXPORT_SYMBOL(phy_ethtool_get_eee);
@@ -1164,7 +1177,7 @@ EXPORT_SYMBOL(phy_ethtool_get_eee);
  */
 int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
 {
-       int cap, old_adv, adv, ret;
+       int cap, old_adv, adv = 0, ret;
 
        if (!phydev->drv)
                return -EIO;
@@ -1178,10 +1191,12 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
        if (old_adv < 0)
                return old_adv;
 
-       adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap;
-
-       /* Mask prohibited EEE modes */
-       adv &= ~phydev->eee_broken_modes;
+       if (data->eee_enabled) {
+               adv = !data->advertised ? cap :
+                     ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap;
+               /* Mask prohibited EEE modes */
+               adv &= ~phydev->eee_broken_modes;
+       }
 
        if (old_adv != adv) {
                ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv);
index 55202a0..5199000 100644 (file)
@@ -315,11 +315,8 @@ static int mdio_bus_phy_restore(struct device *dev)
        if (ret < 0)
                return ret;
 
-       /* The PHY needs to renegotiate. */
-       phydev->link = 0;
-       phydev->state = PHY_UP;
-
-       phy_start_machine(phydev);
+       if (phydev->attached_dev && phydev->adjust_link)
+               phy_start_machine(phydev);
 
        return 0;
 }
@@ -605,7 +602,21 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
         * driver will get bored and give up as soon as it finds that
         * there's no driver _already_ loaded.
         */
-       request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));
+       if (is_c45 && c45_ids) {
+               const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
+               int i;
+
+               for (i = 1; i < num_ids; i++) {
+                       if (!(c45_ids->devices_in_package & (1 << i)))
+                               continue;
+
+                       request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
+                                      MDIO_ID_ARGS(c45_ids->device_ids[i]));
+               }
+       } else {
+               request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
+                              MDIO_ID_ARGS(phy_id));
+       }
 
        device_initialize(&mdiodev->dev);
 
@@ -1725,8 +1736,8 @@ int genphy_read_status(struct phy_device *phydev)
                                return -ENOLINK;
                        }
 
-                       mii_stat1000_to_linkmode_lpa_t(phydev->lp_advertising,
-                                                      lpagb);
+                       mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
+                                                       lpagb);
                        common_adv_gb = lpagb & adv << 2;
                }
 
@@ -1734,7 +1745,7 @@ int genphy_read_status(struct phy_device *phydev)
                if (lpa < 0)
                        return lpa;
 
-               mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa);
+               mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
 
                adv = phy_read(phydev, MII_ADVERTISE);
                if (adv < 0)
@@ -1898,37 +1909,23 @@ EXPORT_SYMBOL(genphy_loopback);
 
 static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
 {
-       __ETHTOOL_DECLARE_LINK_MODE_MASK(speeds) = { 0, };
-
-       linkmode_set_bit_array(phy_10_100_features_array,
-                              ARRAY_SIZE(phy_10_100_features_array),
-                              speeds);
-       linkmode_set_bit_array(phy_gbit_features_array,
-                              ARRAY_SIZE(phy_gbit_features_array),
-                              speeds);
-
-       linkmode_andnot(phydev->supported, phydev->supported, speeds);
-
        switch (max_speed) {
-       default:
-               return -ENOTSUPP;
-       case SPEED_1000:
-               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-                                phydev->supported);
-               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-                                phydev->supported);
+       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_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
-                                phydev->supported);
-               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
-                                phydev->supported);
-               /* fall through */
-       case SPEED_10:
-               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
-                                phydev->supported);
-               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
-                                phydev->supported);
+               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;
@@ -2122,7 +2119,7 @@ static void of_set_phy_eee_broken(struct phy_device *phydev)
 
 static bool phy_drv_supports_irq(struct phy_driver *phydrv)
 {
-       return phydrv->config_intr || phydrv->ack_interrupt;
+       return phydrv->config_intr && phydrv->ack_interrupt;
 }
 
 /**
@@ -2138,7 +2135,6 @@ static int phy_probe(struct device *dev)
        struct phy_device *phydev = to_phy_device(dev);
        struct device_driver *drv = phydev->mdio.dev.driver;
        struct phy_driver *phydrv = to_phy_driver(drv);
-       u32 features;
        int err = 0;
 
        phydev->drv = phydrv;
@@ -2158,7 +2154,6 @@ static int phy_probe(struct device *dev)
         * a controller will attach, and may modify one
         * or both of these values
         */
-       ethtool_convert_link_mode_to_legacy_u32(&features, phydrv->features);
        linkmode_copy(phydev->supported, phydrv->features);
        of_set_phy_supported(phydev);
        linkmode_copy(phydev->advertising, phydev->supported);
@@ -2255,6 +2250,14 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
        new_driver->mdiodrv.driver.remove = phy_remove;
        new_driver->mdiodrv.driver.owner = owner;
 
+       /* The following works around an issue where the PHY driver doesn't bind
+        * to the device, resulting in the genphy driver being used instead of
+        * the dedicated driver. The root cause of the issue isn't known yet
+        * and seems to be in the base driver core. Once this is fixed we may
+        * remove this workaround.
+        */
+       new_driver->mdiodrv.driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
+
        retval = driver_register(&new_driver->mdiodrv.driver);
        if (retval) {
                pr_err("%s: Error %d in registering driver\n",
index 83060fb..ad9db65 100644 (file)
@@ -162,7 +162,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
        /* 1000Base-PX or 1000Base-BX10 */
        if ((id->base.e_base_px || id->base.e_base_bx10) &&
            br_min <= 1300 && br_max >= 1200)
-               phylink_set(support, 1000baseX_Full);
+               phylink_set(modes, 1000baseX_Full);
 
        /* For active or passive cables, select the link modes
         * based on the bit rates and the cable compliance bytes.
index 4ca513f..0646af4 100644 (file)
@@ -70,7 +70,6 @@
 #define PHY_ID_VSC8244                 0x000fc6c0
 #define PHY_ID_VSC8514                 0x00070670
 #define PHY_ID_VSC8572                 0x000704d0
-#define PHY_ID_VSC8574                 0x000704a0
 #define PHY_ID_VSC8601                 0x00070420
 #define PHY_ID_VSC7385                 0x00070450
 #define PHY_ID_VSC7388                 0x00070480
@@ -303,7 +302,6 @@ static int vsc82xx_config_intr(struct phy_device *phydev)
                         phydev->drv->phy_id == PHY_ID_VSC8244 ||
                         phydev->drv->phy_id == PHY_ID_VSC8514 ||
                         phydev->drv->phy_id == PHY_ID_VSC8572 ||
-                        phydev->drv->phy_id == PHY_ID_VSC8574 ||
                         phydev->drv->phy_id == PHY_ID_VSC8601) ?
                                MII_VSC8244_IMASK_MASK :
                                MII_VSC8221_IMASK_MASK);
@@ -430,15 +428,6 @@ static struct phy_driver vsc82xx_driver[] = {
        .config_aneg    = &vsc82x4_config_aneg,
        .ack_interrupt  = &vsc824x_ack_interrupt,
        .config_intr    = &vsc82xx_config_intr,
-}, {
-       .phy_id         = PHY_ID_VSC8574,
-       .name           = "Vitesse VSC8574",
-       .phy_id_mask    = 0x000ffff0,
-       .features       = PHY_GBIT_FEATURES,
-       .config_init    = &vsc824x_config_init,
-       .config_aneg    = &vsc82x4_config_aneg,
-       .ack_interrupt  = &vsc824x_ack_interrupt,
-       .config_intr    = &vsc82xx_config_intr,
 }, {
        .phy_id         = PHY_ID_VSC8601,
        .name           = "Vitesse VSC8601",
@@ -519,7 +508,6 @@ static struct mdio_device_id __maybe_unused vitesse_tbl[] = {
        { PHY_ID_VSC8244, 0x000fffc0 },
        { PHY_ID_VSC8514, 0x000ffff0 },
        { PHY_ID_VSC8572, 0x000ffff0 },
-       { PHY_ID_VSC8574, 0x000ffff0 },
        { PHY_ID_VSC7385, 0x000ffff0 },
        { PHY_ID_VSC7388, 0x000ffff0 },
        { PHY_ID_VSC7395, 0x000ffff0 },
index bdc4d23..b287bb8 100644 (file)
@@ -70,7 +70,7 @@ struct asyncppp {
        struct tasklet_struct tsk;
 
        refcount_t      refcnt;
-       struct semaphore dead_sem;
+       struct completion dead;
        struct ppp_channel chan;        /* interface to generic ppp layer */
        unsigned char   obuf[OBUFSIZE];
 };
@@ -148,7 +148,7 @@ static struct asyncppp *ap_get(struct tty_struct *tty)
 static void ap_put(struct asyncppp *ap)
 {
        if (refcount_dec_and_test(&ap->refcnt))
-               up(&ap->dead_sem);
+               complete(&ap->dead);
 }
 
 /*
@@ -186,7 +186,7 @@ ppp_asynctty_open(struct tty_struct *tty)
        tasklet_init(&ap->tsk, ppp_async_process, (unsigned long) ap);
 
        refcount_set(&ap->refcnt, 1);
-       sema_init(&ap->dead_sem, 0);
+       init_completion(&ap->dead);
 
        ap->chan.private = ap;
        ap->chan.ops = &async_ops;
@@ -235,7 +235,7 @@ ppp_asynctty_close(struct tty_struct *tty)
         * by the time it returns.
         */
        if (!refcount_dec_and_test(&ap->refcnt))
-               down(&ap->dead_sem);
+               wait_for_completion(&ap->dead);
        tasklet_kill(&ap->tsk);
 
        ppp_unregister_channel(&ap->chan);
@@ -770,7 +770,7 @@ process_input_packet(struct asyncppp *ap)
 {
        struct sk_buff *skb;
        unsigned char *p;
-       unsigned int len, fcs, proto;
+       unsigned int len, fcs;
 
        skb = ap->rpkt;
        if (ap->state & (SC_TOSS | SC_ESCAPE))
@@ -799,14 +799,14 @@ process_input_packet(struct asyncppp *ap)
                        goto err;
                p = skb_pull(skb, 2);
        }
-       proto = p[0];
-       if (proto & 1) {
-               /* protocol is compressed */
-               *(u8 *)skb_push(skb, 1) = 0;
-       } else {
+
+       /* If protocol field is not compressed, it can be LCP packet */
+       if (!(p[0] & 0x01)) {
+               unsigned int proto;
+
                if (skb->len < 2)
                        goto err;
-               proto = (proto << 8) + p[1];
+               proto = (p[0] << 8) + p[1];
                if (proto == PPP_LCP)
                        async_lcp_peek(ap, p, skb->len, 1);
        }
index 500bc00..c708400 100644 (file)
@@ -1965,6 +1965,46 @@ ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
        ppp_recv_unlock(ppp);
 }
 
+/**
+ * __ppp_decompress_proto - Decompress protocol field, slim version.
+ * @skb: Socket buffer where protocol field should be decompressed. It must have
+ *      at least 1 byte of head room and 1 byte of linear data. First byte of
+ *      data must be a protocol field byte.
+ *
+ * Decompress protocol field in PPP header if it's compressed, e.g. when
+ * Protocol-Field-Compression (PFC) was negotiated. No checks w.r.t. skb data
+ * length are done in this function.
+ */
+static void __ppp_decompress_proto(struct sk_buff *skb)
+{
+       if (skb->data[0] & 0x01)
+               *(u8 *)skb_push(skb, 1) = 0x00;
+}
+
+/**
+ * ppp_decompress_proto - Check skb data room and decompress protocol field.
+ * @skb: Socket buffer where protocol field should be decompressed. First byte
+ *      of data must be a protocol field byte.
+ *
+ * Decompress protocol field in PPP header if it's compressed, e.g. when
+ * Protocol-Field-Compression (PFC) was negotiated. This function also makes
+ * sure that skb data room is sufficient for Protocol field, before and after
+ * decompression.
+ *
+ * Return: true - decompressed successfully, false - not enough room in skb.
+ */
+static bool ppp_decompress_proto(struct sk_buff *skb)
+{
+       /* At least one byte should be present (if protocol is compressed) */
+       if (!pskb_may_pull(skb, 1))
+               return false;
+
+       __ppp_decompress_proto(skb);
+
+       /* Protocol field should occupy 2 bytes when not compressed */
+       return pskb_may_pull(skb, 2);
+}
+
 void
 ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
 {
@@ -1977,7 +2017,7 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
        }
 
        read_lock_bh(&pch->upl);
-       if (!pskb_may_pull(skb, 2)) {
+       if (!ppp_decompress_proto(skb)) {
                kfree_skb(skb);
                if (pch->ppp) {
                        ++pch->ppp->dev->stats.rx_length_errors;
@@ -2074,6 +2114,9 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
        if (ppp->flags & SC_MUST_COMP && ppp->rstate & SC_DC_FERROR)
                goto err;
 
+       /* At this point the "Protocol" field MUST be decompressed, either in
+        * ppp_input(), ppp_decompress_frame() or in ppp_receive_mp_frame().
+        */
        proto = PPP_PROTO(skb);
        switch (proto) {
        case PPP_VJC_COMP:
@@ -2245,6 +2288,9 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
                skb_put(skb, len);
                skb_pull(skb, 2);       /* pull off the A/C bytes */
 
+               /* Don't call __ppp_decompress_proto() here, but instead rely on
+                * corresponding algo (mppe/bsd/deflate) to decompress it.
+                */
        } else {
                /* Uncompressed frame - pass to decompressor so it
                   can update its dictionary if necessary. */
@@ -2290,9 +2336,11 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
 
        /*
         * Do protocol ID decompression on the first fragment of each packet.
+        * We have to do that here, because ppp_receive_nonmp_frame() expects
+        * decompressed protocol field.
         */
-       if ((PPP_MP_CB(skb)->BEbits & B) && (skb->data[0] & 1))
-               *(u8 *)skb_push(skb, 1) = 0;
+       if (PPP_MP_CB(skb)->BEbits & B)
+               __ppp_decompress_proto(skb);
 
        /*
         * Expand sequence number to 32 bits, making it as close
index 047f6c6..d02ba24 100644 (file)
@@ -709,11 +709,10 @@ ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
                p = skb_pull(skb, 2);
        }
 
-       /* decompress protocol field if compressed */
-       if (p[0] & 1) {
-               /* protocol is compressed */
-               *(u8 *)skb_push(skb, 1) = 0;
-       } else if (skb->len < 2)
+       /* PPP packet length should be >= 2 bytes when protocol field is not
+        * compressed.
+        */
+       if (!(p[0] & 0x01) && skb->len < 2)
                goto err;
 
        /* queue the frame to be processed */
index 67ffe74..8f09edd 100644 (file)
@@ -325,11 +325,6 @@ allow_packet:
                        skb_pull(skb, 2);
                }
 
-               if ((*skb->data) & 1) {
-                       /* protocol is compressed */
-                       *(u8 *)skb_push(skb, 1) = 0;
-               }
-
                skb->ip_summed = CHECKSUM_NONE;
                skb_set_network_header(skb, skb->head-skb->data);
                ppp_input(&po->chan, skb);
index e9f101c..bfbb39f 100644 (file)
@@ -216,9 +216,9 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                         * it just report sending a packet to the target
                         * (without actual packet transfer).
                         */
-                       dev_kfree_skb_any(skb);
                        ndev->stats.tx_packets++;
                        ndev->stats.tx_bytes += skb->len;
+                       dev_kfree_skb_any(skb);
                }
        }
 
index f03004f..443b269 100644 (file)
@@ -1113,7 +1113,7 @@ static long tap_ioctl(struct file *file, unsigned int cmd,
                        rtnl_unlock();
                        return -ENOLINK;
                }
-               ret = dev_set_mac_address(tap->dev, &sa);
+               ret = dev_set_mac_address(tap->dev, &sa, NULL);
                tap_put_tap_dev(tap);
                rtnl_unlock();
                return ret;
index db633ae..afd9d25 100644 (file)
@@ -59,7 +59,7 @@ static int __set_port_dev_addr(struct net_device *port_dev,
 
        memcpy(addr.__data, dev_addr, port_dev->addr_len);
        addr.ss_family = port_dev->type;
-       return dev_set_mac_address(port_dev, (struct sockaddr *)&addr);
+       return dev_set_mac_address(port_dev, (struct sockaddr *)&addr, NULL);
 }
 
 static int team_port_set_orig_dev_addr(struct team_port *port)
@@ -985,8 +985,6 @@ static void team_port_disable(struct team *team,
        team->en_port_count--;
        team_queue_override_port_del(team, port);
        team_adjust_ops(team);
-       team_notify_peers(team);
-       team_mcast_rejoin(team);
        team_lower_state_changed(port);
 }
 
@@ -1214,7 +1212,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
                goto err_port_enter;
        }
 
-       err = dev_open(port_dev);
+       err = dev_open(port_dev, extack);
        if (err) {
                netdev_dbg(dev, "Device %s opening failed\n",
                           portname);
index a65779c..a4fdad4 100644 (file)
@@ -188,6 +188,11 @@ struct tun_file {
        struct xdp_rxq_info xdp_rxq;
 };
 
+struct tun_page {
+       struct page *page;
+       int count;
+};
+
 struct tun_flow_entry {
        struct hlist_node hash_link;
        struct rcu_head rcu;
@@ -196,7 +201,7 @@ struct tun_flow_entry {
        u32 rxhash;
        u32 rps_rxhash;
        int queue_index;
-       unsigned long updated;
+       unsigned long updated ____cacheline_aligned_in_smp;
 };
 
 #define TUN_NUM_FLOW_ENTRIES 1024
@@ -524,18 +529,17 @@ static void tun_flow_update(struct tun_struct *tun, u32 rxhash,
        unsigned long delay = tun->ageing_time;
        u16 queue_index = tfile->queue_index;
 
-       if (!rxhash)
-               return;
-       else
-               head = &tun->flows[tun_hashfn(rxhash)];
+       head = &tun->flows[tun_hashfn(rxhash)];
 
        rcu_read_lock();
 
        e = tun_flow_find(head, rxhash);
        if (likely(e)) {
                /* TODO: keep queueing to old queue until it's empty? */
-               e->queue_index = queue_index;
-               e->updated = jiffies;
+               if (e->queue_index != queue_index)
+                       e->queue_index = queue_index;
+               if (e->updated != jiffies)
+                       e->updated = jiffies;
                sock_rps_record_flow_hash(e->rps_rxhash);
        } else {
                spin_lock_bh(&tun->lock);
@@ -1249,6 +1253,21 @@ static int tun_xdp(struct net_device *dev, struct netdev_bpf *xdp)
        }
 }
 
+static int tun_net_change_carrier(struct net_device *dev, bool new_carrier)
+{
+       if (new_carrier) {
+               struct tun_struct *tun = netdev_priv(dev);
+
+               if (!tun->numqueues)
+                       return -EPERM;
+
+               netif_carrier_on(dev);
+       } else {
+               netif_carrier_off(dev);
+       }
+       return 0;
+}
+
 static const struct net_device_ops tun_netdev_ops = {
        .ndo_uninit             = tun_net_uninit,
        .ndo_open               = tun_net_open,
@@ -1258,6 +1277,7 @@ static const struct net_device_ops tun_netdev_ops = {
        .ndo_select_queue       = tun_select_queue,
        .ndo_set_rx_headroom    = tun_set_headroom,
        .ndo_get_stats64        = tun_net_get_stats64,
+       .ndo_change_carrier     = tun_net_change_carrier,
 };
 
 static void __tun_xdp_flush_tfile(struct tun_file *tfile)
@@ -1340,6 +1360,7 @@ static const struct net_device_ops tap_netdev_ops = {
        .ndo_get_stats64        = tun_net_get_stats64,
        .ndo_bpf                = tun_xdp,
        .ndo_xdp_xmit           = tun_xdp_xmit,
+       .ndo_change_carrier     = tun_net_change_carrier,
 };
 
 static void tun_flow_init(struct tun_struct *tun)
@@ -1473,23 +1494,22 @@ static struct sk_buff *tun_napi_alloc_frags(struct tun_file *tfile,
        skb->truesize += skb->data_len;
 
        for (i = 1; i < it->nr_segs; i++) {
-               struct page_frag *pfrag = &current->task_frag;
                size_t fragsz = it->iov[i].iov_len;
+               struct page *page;
+               void *frag;
 
                if (fragsz == 0 || fragsz > PAGE_SIZE) {
                        err = -EINVAL;
                        goto free;
                }
-
-               if (!skb_page_frag_refill(fragsz, pfrag, GFP_KERNEL)) {
+               frag = netdev_alloc_frag(fragsz);
+               if (!frag) {
                        err = -ENOMEM;
                        goto free;
                }
-
-               skb_fill_page_desc(skb, i - 1, pfrag->page,
-                                  pfrag->offset, fragsz);
-               page_ref_inc(pfrag->page);
-               pfrag->offset += fragsz;
+               page = virt_to_head_page(frag);
+               skb_fill_page_desc(skb, i - 1, page,
+                                  frag - page_address(page), fragsz);
        }
 
        return skb;
@@ -1536,6 +1556,7 @@ static void tun_rx_batched(struct tun_struct *tun, struct tun_file *tfile,
 
        if (!rx_batched || (!more && skb_queue_empty(queue))) {
                local_bh_disable();
+               skb_record_rx_queue(skb, tfile->queue_index);
                netif_receive_skb(skb);
                local_bh_enable();
                return;
@@ -1555,8 +1576,11 @@ static void tun_rx_batched(struct tun_struct *tun, struct tun_file *tfile,
                struct sk_buff *nskb;
 
                local_bh_disable();
-               while ((nskb = __skb_dequeue(&process_queue)))
+               while ((nskb = __skb_dequeue(&process_queue))) {
+                       skb_record_rx_queue(nskb, tfile->queue_index);
                        netif_receive_skb(nskb);
+               }
+               skb_record_rx_queue(skb, tfile->queue_index);
                netif_receive_skb(skb);
                local_bh_enable();
        }
@@ -2289,9 +2313,9 @@ static void tun_setup(struct net_device *dev)
 static int tun_validate(struct nlattr *tb[], struct nlattr *data[],
                        struct netlink_ext_ack *extack)
 {
-       if (!data)
-               return 0;
-       return -EINVAL;
+       NL_SET_ERR_MSG(extack,
+                      "tun/tap creation via rtnetlink is not supported.");
+       return -EOPNOTSUPP;
 }
 
 static size_t tun_get_size(const struct net_device *dev)
@@ -2377,10 +2401,18 @@ static void tun_sock_write_space(struct sock *sk)
        kill_fasync(&tfile->fasync, SIGIO, POLL_OUT);
 }
 
+static void tun_put_page(struct tun_page *tpage)
+{
+       if (tpage->page)
+               __page_frag_cache_drain(tpage->page, tpage->count);
+}
+
 static int tun_xdp_one(struct tun_struct *tun,
                       struct tun_file *tfile,
-                      struct xdp_buff *xdp, int *flush)
+                      struct xdp_buff *xdp, int *flush,
+                      struct tun_page *tpage)
 {
+       unsigned int datasize = xdp->data_end - xdp->data;
        struct tun_xdp_hdr *hdr = xdp->data_hard_start;
        struct virtio_net_hdr *gso = &hdr->gso;
        struct tun_pcpu_stats *stats;
@@ -2390,6 +2422,7 @@ static int tun_xdp_one(struct tun_struct *tun,
        int buflen = hdr->buflen;
        int err = 0;
        bool skb_xdp = false;
+       struct page *page;
 
        xdp_prog = rcu_dereference(tun->xdp_prog);
        if (xdp_prog) {
@@ -2416,7 +2449,14 @@ static int tun_xdp_one(struct tun_struct *tun,
                case XDP_PASS:
                        break;
                default:
-                       put_page(virt_to_head_page(xdp->data));
+                       page = virt_to_head_page(xdp->data);
+                       if (tpage->page == page) {
+                               ++tpage->count;
+                       } else {
+                               tun_put_page(tpage);
+                               tpage->page = page;
+                               tpage->count = 1;
+                       }
                        return 0;
                }
        }
@@ -2452,14 +2492,17 @@ build:
            !tfile->detached)
                rxhash = __skb_get_hash_symmetric(skb);
 
+       skb_record_rx_queue(skb, tfile->queue_index);
        netif_receive_skb(skb);
 
-       stats = get_cpu_ptr(tun->pcpu_stats);
+       /* No need for get_cpu_ptr() here since this function is
+        * always called with bh disabled
+        */
+       stats = this_cpu_ptr(tun->pcpu_stats);
        u64_stats_update_begin(&stats->syncp);
        stats->rx_packets++;
-       stats->rx_bytes += skb->len;
+       stats->rx_bytes += datasize;
        u64_stats_update_end(&stats->syncp);
-       put_cpu_ptr(stats);
 
        if (rxhash)
                tun_flow_update(tun, rxhash, tfile);
@@ -2480,15 +2523,18 @@ static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
                return -EBADFD;
 
        if (ctl && (ctl->type == TUN_MSG_PTR)) {
+               struct tun_page tpage;
                int n = ctl->num;
                int flush = 0;
 
+               memset(&tpage, 0, sizeof(tpage));
+
                local_bh_disable();
                rcu_read_lock();
 
                for (i = 0; i < n; i++) {
                        xdp = &((struct xdp_buff *)ctl->ptr)[i];
-                       tun_xdp_one(tun, tfile, xdp, &flush);
+                       tun_xdp_one(tun, tfile, xdp, &flush, &tpage);
                }
 
                if (flush)
@@ -2497,6 +2543,8 @@ static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
                rcu_read_unlock();
                local_bh_enable();
 
+               tun_put_page(&tpage);
+
                ret = total_len;
                goto out;
        }
@@ -2973,12 +3021,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
        struct net *net = sock_net(&tfile->sk);
        struct tun_struct *tun;
        void __user* argp = (void __user*)arg;
+       unsigned int ifindex, carrier;
        struct ifreq ifr;
        kuid_t owner;
        kgid_t group;
        int sndbuf;
        int vnet_hdr_sz;
-       unsigned int ifindex;
        int le;
        int ret;
        bool do_notify = false;
@@ -3156,7 +3204,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
                tun_debug(KERN_DEBUG, tun, "set hw address: %pM\n",
                          ifr.ifr_hwaddr.sa_data);
 
-               ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
+               ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr, NULL);
                break;
 
        case TUNGETSNDBUF:
@@ -3262,6 +3310,14 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
                ret = tun_set_ebpf(tun, &tun->filter_prog, argp);
                break;
 
+       case TUNSETCARRIER:
+               ret = -EFAULT;
+               if (copy_from_user(&carrier, argp, sizeof(carrier)))
+                       goto unlock;
+
+               ret = tun_net_change_carrier(tun->dev, (bool)carrier);
+               break;
+
        default:
                ret = -EINVAL;
                break;
index 418b090..860352a 100644 (file)
@@ -613,4 +613,15 @@ config USB_NET_CH9200
          To compile this driver as a module, choose M here: the
          module will be called ch9200.
 
+config USB_NET_AQC111
+       tristate "Aquantia AQtion USB to 5/2.5GbE Controllers support"
+       depends on USB_USBNET
+       select CRC32
+       help
+         This option adds support for Aquantia AQtion USB
+         Ethernet adapters based on AQC111U/AQC112 chips.
+
+         This driver should work with at least the following devices:
+         * Aquantia AQtion USB to 5GbE
+
 endif # USB_NET_DRIVERS
index 27307a4..99fd12b 100644 (file)
@@ -40,3 +40,4 @@ obj-$(CONFIG_USB_VL600)               += lg-vl600.o
 obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o
 obj-$(CONFIG_USB_NET_CDC_MBIM) += cdc_mbim.o
 obj-$(CONFIG_USB_NET_CH9200)   += ch9200.o
+obj-$(CONFIG_USB_NET_AQC111)   += aqc111.o
diff --git a/drivers/net/usb/aqc111.c b/drivers/net/usb/aqc111.c
new file mode 100644 (file)
index 0000000..57f1c94
--- /dev/null
@@ -0,0 +1,1459 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Aquantia Corp. Aquantia AQtion USB to 5GbE Controller
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
+ * Copyright (C) 2002-2003 TiVo Inc.
+ * Copyright (C) 2017-2018 ASIX
+ * Copyright (C) 2018 Aquantia Corp.
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/if_vlan.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+#include <linux/linkmode.h>
+
+#include "aqc111.h"
+
+#define DRIVER_NAME "aqc111"
+
+static int aqc111_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+                               u16 index, u16 size, void *data)
+{
+       int ret;
+
+       ret = usbnet_read_cmd_nopm(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR |
+                                  USB_RECIP_DEVICE, value, index, data, size);
+
+       if (unlikely(ret < 0))
+               netdev_warn(dev->net,
+                           "Failed to read(0x%x) reg index 0x%04x: %d\n",
+                           cmd, index, ret);
+
+       return ret;
+}
+
+static int aqc111_read_cmd(struct usbnet *dev, u8 cmd, u16 value,
+                          u16 index, u16 size, void *data)
+{
+       int ret;
+
+       ret = usbnet_read_cmd(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR |
+                             USB_RECIP_DEVICE, value, index, data, size);
+
+       if (unlikely(ret < 0))
+               netdev_warn(dev->net,
+                           "Failed to read(0x%x) reg index 0x%04x: %d\n",
+                           cmd, index, ret);
+
+       return ret;
+}
+
+static int aqc111_read16_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+                                 u16 index, u16 *data)
+{
+       int ret = 0;
+
+       ret = aqc111_read_cmd_nopm(dev, cmd, value, index, sizeof(*data), data);
+       le16_to_cpus(data);
+
+       return ret;
+}
+
+static int aqc111_read16_cmd(struct usbnet *dev, u8 cmd, u16 value,
+                            u16 index, u16 *data)
+{
+       int ret = 0;
+
+       ret = aqc111_read_cmd(dev, cmd, value, index, sizeof(*data), data);
+       le16_to_cpus(data);
+
+       return ret;
+}
+
+static int __aqc111_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
+                             u16 value, u16 index, u16 size, const void *data)
+{
+       int err = -ENOMEM;
+       void *buf = NULL;
+
+       netdev_dbg(dev->net,
+                  "%s cmd=%#x reqtype=%#x value=%#x index=%#x size=%d\n",
+                  __func__, cmd, reqtype, value, index, size);
+
+       if (data) {
+               buf = kmemdup(data, size, GFP_KERNEL);
+               if (!buf)
+                       goto out;
+       }
+
+       err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+                             cmd, reqtype, value, index, buf, size,
+                             (cmd == AQ_PHY_POWER) ? AQ_USB_PHY_SET_TIMEOUT :
+                             AQ_USB_SET_TIMEOUT);
+
+       if (unlikely(err < 0))
+               netdev_warn(dev->net,
+                           "Failed to write(0x%x) reg index 0x%04x: %d\n",
+                           cmd, index, err);
+       kfree(buf);
+
+out:
+       return err;
+}
+
+static int aqc111_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+                                u16 index, u16 size, void *data)
+{
+       int ret;
+
+       ret = __aqc111_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_DEVICE, value, index, size, data);
+
+       return ret;
+}
+
+static int aqc111_write_cmd(struct usbnet *dev, u8 cmd, u16 value,
+                           u16 index, u16 size, void *data)
+{
+       int ret;
+
+       if (usb_autopm_get_interface(dev->intf) < 0)
+               return -ENODEV;
+
+       ret = __aqc111_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_DEVICE, value, index, size, data);
+
+       usb_autopm_put_interface(dev->intf);
+
+       return ret;
+}
+
+static int aqc111_write16_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+                                  u16 index, u16 *data)
+{
+       u16 tmp = *data;
+
+       cpu_to_le16s(&tmp);
+
+       return aqc111_write_cmd_nopm(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
+static int aqc111_write16_cmd(struct usbnet *dev, u8 cmd, u16 value,
+                             u16 index, u16 *data)
+{
+       u16 tmp = *data;
+
+       cpu_to_le16s(&tmp);
+
+       return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
+static int aqc111_write32_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+                                  u16 index, u32 *data)
+{
+       u32 tmp = *data;
+
+       cpu_to_le32s(&tmp);
+
+       return aqc111_write_cmd_nopm(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
+static int aqc111_write32_cmd(struct usbnet *dev, u8 cmd, u16 value,
+                             u16 index, u32 *data)
+{
+       u32 tmp = *data;
+
+       cpu_to_le32s(&tmp);
+
+       return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
+static int aqc111_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+                                 u16 index, u16 size, void *data)
+{
+       return usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+                                     USB_RECIP_DEVICE, value, index, data,
+                                     size);
+}
+
+static int aqc111_write16_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+                                   u16 index, u16 *data)
+{
+       u16 tmp = *data;
+
+       cpu_to_le16s(&tmp);
+
+       return aqc111_write_cmd_async(dev, cmd, value, index,
+                                     sizeof(tmp), &tmp);
+}
+
+static void aqc111_get_drvinfo(struct net_device *net,
+                              struct ethtool_drvinfo *info)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+
+       /* Inherit standard device info */
+       usbnet_get_drvinfo(net, info);
+       strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
+       snprintf(info->fw_version, sizeof(info->fw_version), "%u.%u.%u",
+                aqc111_data->fw_ver.major,
+                aqc111_data->fw_ver.minor,
+                aqc111_data->fw_ver.rev);
+       info->eedump_len = 0x00;
+       info->regdump_len = 0x00;
+}
+
+static void aqc111_get_wol(struct net_device *net,
+                          struct ethtool_wolinfo *wolinfo)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+
+       wolinfo->supported = WAKE_MAGIC;
+       wolinfo->wolopts = 0;
+
+       if (aqc111_data->wol_flags & AQ_WOL_FLAG_MP)
+               wolinfo->wolopts |= WAKE_MAGIC;
+}
+
+static int aqc111_set_wol(struct net_device *net,
+                         struct ethtool_wolinfo *wolinfo)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+
+       if (wolinfo->wolopts & ~WAKE_MAGIC)
+               return -EINVAL;
+
+       aqc111_data->wol_flags = 0;
+       if (wolinfo->wolopts & WAKE_MAGIC)
+               aqc111_data->wol_flags |= AQ_WOL_FLAG_MP;
+
+       return 0;
+}
+
+static void aqc111_speed_to_link_mode(u32 speed,
+                                     struct ethtool_link_ksettings *elk)
+{
+       switch (speed) {
+       case SPEED_5000:
+               ethtool_link_ksettings_add_link_mode(elk, advertising,
+                                                    5000baseT_Full);
+               break;
+       case SPEED_2500:
+               ethtool_link_ksettings_add_link_mode(elk, advertising,
+                                                    2500baseT_Full);
+               break;
+       case SPEED_1000:
+               ethtool_link_ksettings_add_link_mode(elk, advertising,
+                                                    1000baseT_Full);
+               break;
+       case SPEED_100:
+               ethtool_link_ksettings_add_link_mode(elk, advertising,
+                                                    100baseT_Full);
+               break;
+       }
+}
+
+static int aqc111_get_link_ksettings(struct net_device *net,
+                                    struct ethtool_link_ksettings *elk)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       enum usb_device_speed usb_speed = dev->udev->speed;
+       u32 speed = SPEED_UNKNOWN;
+
+       ethtool_link_ksettings_zero_link_mode(elk, supported);
+       ethtool_link_ksettings_add_link_mode(elk, supported,
+                                            100baseT_Full);
+       ethtool_link_ksettings_add_link_mode(elk, supported,
+                                            1000baseT_Full);
+       if (usb_speed == USB_SPEED_SUPER) {
+               ethtool_link_ksettings_add_link_mode(elk, supported,
+                                                    2500baseT_Full);
+               ethtool_link_ksettings_add_link_mode(elk, supported,
+                                                    5000baseT_Full);
+       }
+       ethtool_link_ksettings_add_link_mode(elk, supported, TP);
+       ethtool_link_ksettings_add_link_mode(elk, supported, Autoneg);
+
+       elk->base.port = PORT_TP;
+       elk->base.transceiver = XCVR_INTERNAL;
+
+       elk->base.mdio_support = 0x00; /*Not supported*/
+
+       if (aqc111_data->autoneg)
+               linkmode_copy(elk->link_modes.advertising,
+                             elk->link_modes.supported);
+       else
+               aqc111_speed_to_link_mode(aqc111_data->advertised_speed, elk);
+
+       elk->base.autoneg = aqc111_data->autoneg;
+
+       switch (aqc111_data->link_speed) {
+       case AQ_INT_SPEED_5G:
+               speed = SPEED_5000;
+               break;
+       case AQ_INT_SPEED_2_5G:
+               speed = SPEED_2500;
+               break;
+       case AQ_INT_SPEED_1G:
+               speed = SPEED_1000;
+               break;
+       case AQ_INT_SPEED_100M:
+               speed = SPEED_100;
+               break;
+       }
+       elk->base.duplex = DUPLEX_FULL;
+       elk->base.speed = speed;
+
+       return 0;
+}
+
+static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+
+       aqc111_data->phy_cfg &= ~AQ_ADV_MASK;
+       aqc111_data->phy_cfg |= AQ_PAUSE;
+       aqc111_data->phy_cfg |= AQ_ASYM_PAUSE;
+       aqc111_data->phy_cfg |= AQ_DOWNSHIFT;
+       aqc111_data->phy_cfg &= ~AQ_DSH_RETRIES_MASK;
+       aqc111_data->phy_cfg |= (3 << AQ_DSH_RETRIES_SHIFT) &
+                               AQ_DSH_RETRIES_MASK;
+
+       if (autoneg == AUTONEG_ENABLE) {
+               switch (speed) {
+               case SPEED_5000:
+                       aqc111_data->phy_cfg |= AQ_ADV_5G;
+                       /* fall-through */
+               case SPEED_2500:
+                       aqc111_data->phy_cfg |= AQ_ADV_2G5;
+                       /* fall-through */
+               case SPEED_1000:
+                       aqc111_data->phy_cfg |= AQ_ADV_1G;
+                       /* fall-through */
+               case SPEED_100:
+                       aqc111_data->phy_cfg |= AQ_ADV_100M;
+                       /* fall-through */
+               }
+       } else {
+               switch (speed) {
+               case SPEED_5000:
+                       aqc111_data->phy_cfg |= AQ_ADV_5G;
+                       break;
+               case SPEED_2500:
+                       aqc111_data->phy_cfg |= AQ_ADV_2G5;
+                       break;
+               case SPEED_1000:
+                       aqc111_data->phy_cfg |= AQ_ADV_1G;
+                       break;
+               case SPEED_100:
+                       aqc111_data->phy_cfg |= AQ_ADV_100M;
+                       break;
+               }
+       }
+
+       aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, &aqc111_data->phy_cfg);
+}
+
+static int aqc111_set_link_ksettings(struct net_device *net,
+                                    const struct ethtool_link_ksettings *elk)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       enum usb_device_speed usb_speed = dev->udev->speed;
+       u8 autoneg = elk->base.autoneg;
+       u32 speed = elk->base.speed;
+
+       if (autoneg == AUTONEG_ENABLE) {
+               if (aqc111_data->autoneg != AUTONEG_ENABLE) {
+                       aqc111_data->autoneg = AUTONEG_ENABLE;
+                       aqc111_data->advertised_speed =
+                                       (usb_speed == USB_SPEED_SUPER) ?
+                                        SPEED_5000 : SPEED_1000;
+                       aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+                                            aqc111_data->advertised_speed);
+               }
+       } else {
+               if (speed != SPEED_100 &&
+                   speed != SPEED_1000 &&
+                   speed != SPEED_2500 &&
+                   speed != SPEED_5000 &&
+                   speed != SPEED_UNKNOWN)
+                       return -EINVAL;
+
+               if (elk->base.duplex != DUPLEX_FULL)
+                       return -EINVAL;
+
+               if (usb_speed != USB_SPEED_SUPER && speed > SPEED_1000)
+                       return -EINVAL;
+
+               aqc111_data->autoneg = AUTONEG_DISABLE;
+               if (speed != SPEED_UNKNOWN)
+                       aqc111_data->advertised_speed = speed;
+
+               aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+                                    aqc111_data->advertised_speed);
+       }
+
+       return 0;
+}
+
+static const struct ethtool_ops aqc111_ethtool_ops = {
+       .get_drvinfo = aqc111_get_drvinfo,
+       .get_wol = aqc111_get_wol,
+       .set_wol = aqc111_set_wol,
+       .get_msglevel = usbnet_get_msglevel,
+       .set_msglevel = usbnet_set_msglevel,
+       .get_link = ethtool_op_get_link,
+       .get_link_ksettings = aqc111_get_link_ksettings,
+       .set_link_ksettings = aqc111_set_link_ksettings
+};
+
+static int aqc111_change_mtu(struct net_device *net, int new_mtu)
+{
+       struct usbnet *dev = netdev_priv(net);
+       u16 reg16 = 0;
+       u8 buf[5];
+
+       net->mtu = new_mtu;
+       dev->hard_mtu = net->mtu + net->hard_header_len;
+
+       aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                         2, &reg16);
+       if (net->mtu > 1500)
+               reg16 |= SFR_MEDIUM_JUMBO_EN;
+       else
+               reg16 &= ~SFR_MEDIUM_JUMBO_EN;
+
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                          2, &reg16);
+
+       if (dev->net->mtu > 12500 && dev->net->mtu <= 16334) {
+               memcpy(buf, &AQC111_BULKIN_SIZE[2], 5);
+               /* RX bulk configuration */
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL,
+                                5, 5, buf);
+       }
+
+       /* Set high low water level */
+       if (dev->net->mtu <= 4500)
+               reg16 = 0x0810;
+       else if (dev->net->mtu <= 9500)
+               reg16 = 0x1020;
+       else if (dev->net->mtu <= 12500)
+               reg16 = 0x1420;
+       else if (dev->net->mtu <= 16334)
+               reg16 = 0x1A20;
+
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_PAUSE_WATERLVL_LOW,
+                          2, &reg16);
+
+       return 0;
+}
+
+static int aqc111_set_mac_addr(struct net_device *net, void *p)
+{
+       struct usbnet *dev = netdev_priv(net);
+       int ret = 0;
+
+       ret = eth_mac_addr(net, p);
+       if (ret < 0)
+               return ret;
+
+       /* Set the MAC address */
+       return aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_NODE_ID, ETH_ALEN,
+                               ETH_ALEN, net->dev_addr);
+}
+
+static int aqc111_vlan_rx_kill_vid(struct net_device *net,
+                                  __be16 proto, u16 vid)
+{
+       struct usbnet *dev = netdev_priv(net);
+       u8 vlan_ctrl = 0;
+       u16 reg16 = 0;
+       u8 reg8 = 0;
+
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+       vlan_ctrl = reg8;
+
+       /* Address */
+       reg8 = (vid / 16);
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_ADDRESS, 1, 1, &reg8);
+       /* Data */
+       reg8 = vlan_ctrl | SFR_VLAN_CONTROL_RD;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+       aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+       reg16 &= ~(1 << (vid % 16));
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+       reg8 = vlan_ctrl | SFR_VLAN_CONTROL_WE;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+
+       return 0;
+}
+
+static int aqc111_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid)
+{
+       struct usbnet *dev = netdev_priv(net);
+       u8 vlan_ctrl = 0;
+       u16 reg16 = 0;
+       u8 reg8 = 0;
+
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+       vlan_ctrl = reg8;
+
+       /* Address */
+       reg8 = (vid / 16);
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_ADDRESS, 1, 1, &reg8);
+       /* Data */
+       reg8 = vlan_ctrl | SFR_VLAN_CONTROL_RD;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+       aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+       reg16 |= (1 << (vid % 16));
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+       reg8 = vlan_ctrl | SFR_VLAN_CONTROL_WE;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+
+       return 0;
+}
+
+static void aqc111_set_rx_mode(struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       int mc_count = 0;
+
+       mc_count = netdev_mc_count(net);
+
+       aqc111_data->rxctl &= ~(SFR_RX_CTL_PRO | SFR_RX_CTL_AMALL |
+                               SFR_RX_CTL_AM);
+
+       if (net->flags & IFF_PROMISC) {
+               aqc111_data->rxctl |= SFR_RX_CTL_PRO;
+       } else if ((net->flags & IFF_ALLMULTI) || mc_count > AQ_MAX_MCAST) {
+               aqc111_data->rxctl |= SFR_RX_CTL_AMALL;
+       } else if (!netdev_mc_empty(net)) {
+               u8 m_filter[AQ_MCAST_FILTER_SIZE] = { 0 };
+               struct netdev_hw_addr *ha = NULL;
+               u32 crc_bits = 0;
+
+               netdev_for_each_mc_addr(ha, net) {
+                       crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+                       m_filter[crc_bits >> 3] |= BIT(crc_bits & 7);
+               }
+
+               aqc111_write_cmd_async(dev, AQ_ACCESS_MAC,
+                                      SFR_MULTI_FILTER_ARRY,
+                                      AQ_MCAST_FILTER_SIZE,
+                                      AQ_MCAST_FILTER_SIZE, m_filter);
+
+               aqc111_data->rxctl |= SFR_RX_CTL_AM;
+       }
+
+       aqc111_write16_cmd_async(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+                                2, &aqc111_data->rxctl);
+}
+
+static int aqc111_set_features(struct net_device *net,
+                              netdev_features_t features)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       netdev_features_t changed = net->features ^ features;
+       u16 reg16 = 0;
+       u8 reg8 = 0;
+
+       if (changed & NETIF_F_IP_CSUM) {
+               aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, &reg8);
+               reg8 ^= SFR_TXCOE_TCP | SFR_TXCOE_UDP;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL,
+                                1, 1, &reg8);
+       }
+
+       if (changed & NETIF_F_IPV6_CSUM) {
+               aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, &reg8);
+               reg8 ^= SFR_TXCOE_TCPV6 | SFR_TXCOE_UDPV6;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL,
+                                1, 1, &reg8);
+       }
+
+       if (changed & NETIF_F_RXCSUM) {
+               aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL, 1, 1, &reg8);
+               if (features & NETIF_F_RXCSUM) {
+                       aqc111_data->rx_checksum = 1;
+                       reg8 &= ~(SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP |
+                                 SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6);
+               } else {
+                       aqc111_data->rx_checksum = 0;
+                       reg8 |= SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP |
+                               SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6;
+               }
+
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL,
+                                1, 1, &reg8);
+       }
+       if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
+               if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+                       u16 i = 0;
+
+                       for (i = 0; i < 256; i++) {
+                               /* Address */
+                               reg8 = i;
+                               aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+                                                SFR_VLAN_ID_ADDRESS,
+                                                1, 1, &reg8);
+                               /* Data */
+                               aqc111_write16_cmd(dev, AQ_ACCESS_MAC,
+                                                  SFR_VLAN_ID_DATA0,
+                                                  2, &reg16);
+                               reg8 = SFR_VLAN_CONTROL_WE;
+                               aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+                                                SFR_VLAN_ID_CONTROL,
+                                                1, 1, &reg8);
+                       }
+                       aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
+                                       1, 1, &reg8);
+                       reg8 |= SFR_VLAN_CONTROL_VFE;
+                       aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+                                        SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+               } else {
+                       aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
+                                       1, 1, &reg8);
+                       reg8 &= ~SFR_VLAN_CONTROL_VFE;
+                       aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+                                        SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+               }
+       }
+
+       return 0;
+}
+
+static const struct net_device_ops aqc111_netdev_ops = {
+       .ndo_open               = usbnet_open,
+       .ndo_stop               = usbnet_stop,
+       .ndo_start_xmit         = usbnet_start_xmit,
+       .ndo_tx_timeout         = usbnet_tx_timeout,
+       .ndo_get_stats64        = usbnet_get_stats64,
+       .ndo_change_mtu         = aqc111_change_mtu,
+       .ndo_set_mac_address    = aqc111_set_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_vlan_rx_add_vid    = aqc111_vlan_rx_add_vid,
+       .ndo_vlan_rx_kill_vid   = aqc111_vlan_rx_kill_vid,
+       .ndo_set_rx_mode        = aqc111_set_rx_mode,
+       .ndo_set_features       = aqc111_set_features,
+};
+
+static int aqc111_read_perm_mac(struct usbnet *dev)
+{
+       u8 buf[ETH_ALEN];
+       int ret;
+
+       ret = aqc111_read_cmd(dev, AQ_FLASH_PARAMETERS, 0, 0, ETH_ALEN, buf);
+       if (ret < 0)
+               goto out;
+
+       ether_addr_copy(dev->net->perm_addr, buf);
+
+       return 0;
+out:
+       return ret;
+}
+
+static void aqc111_read_fw_version(struct usbnet *dev,
+                                  struct aqc111_data *aqc111_data)
+{
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_MAJOR,
+                       1, 1, &aqc111_data->fw_ver.major);
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_MINOR,
+                       1, 1, &aqc111_data->fw_ver.minor);
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_REV,
+                       1, 1, &aqc111_data->fw_ver.rev);
+
+       if (aqc111_data->fw_ver.major & 0x80)
+               aqc111_data->fw_ver.major &= ~0x80;
+}
+
+static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       enum usb_device_speed usb_speed = udev->speed;
+       struct aqc111_data *aqc111_data;
+       int ret;
+
+       /* Check if vendor configuration */
+       if (udev->actconfig->desc.bConfigurationValue != 1) {
+               usb_driver_set_configuration(udev, 1);
+               return -ENODEV;
+       }
+
+       usb_reset_configuration(dev->udev);
+
+       ret = usbnet_get_endpoints(dev, intf);
+       if (ret < 0) {
+               netdev_dbg(dev->net, "usbnet_get_endpoints failed");
+               return ret;
+       }
+
+       aqc111_data = kzalloc(sizeof(*aqc111_data), GFP_KERNEL);
+       if (!aqc111_data)
+               return -ENOMEM;
+
+       /* store aqc111_data pointer in device data field */
+       dev->driver_priv = aqc111_data;
+
+       /* Init the MAC address */
+       ret = aqc111_read_perm_mac(dev);
+       if (ret)
+               goto out;
+
+       ether_addr_copy(dev->net->dev_addr, dev->net->perm_addr);
+
+       /* Set Rx urb size */
+       dev->rx_urb_size = URB_SIZE;
+
+       /* Set TX needed headroom & tailroom */
+       dev->net->needed_headroom += sizeof(u64);
+       dev->net->needed_tailroom += sizeof(u64);
+
+       dev->net->max_mtu = 16334;
+
+       dev->net->netdev_ops = &aqc111_netdev_ops;
+       dev->net->ethtool_ops = &aqc111_ethtool_ops;
+
+       if (usb_device_no_sg_constraint(dev->udev))
+               dev->can_dma_sg = 1;
+
+       dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
+       dev->net->features |= AQ_SUPPORT_FEATURE;
+       dev->net->vlan_features |= AQ_SUPPORT_VLAN_FEATURE;
+
+       netif_set_gso_max_size(dev->net, 65535);
+
+       aqc111_read_fw_version(dev, aqc111_data);
+       aqc111_data->autoneg = AUTONEG_ENABLE;
+       aqc111_data->advertised_speed = (usb_speed == USB_SPEED_SUPER) ?
+                                        SPEED_5000 : SPEED_1000;
+
+       return 0;
+
+out:
+       kfree(aqc111_data);
+       return ret;
+}
+
+static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u16 reg16;
+
+       /* Force bz */
+       reg16 = SFR_PHYPWR_RSTCTL_BZ;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+                               2, &reg16);
+       reg16 = 0;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+                               2, &reg16);
+
+       /* Power down ethernet PHY */
+       aqc111_data->phy_cfg &= ~AQ_ADV_MASK;
+       aqc111_data->phy_cfg |= AQ_LOW_POWER;
+       aqc111_data->phy_cfg &= ~AQ_PHY_POWER_EN;
+       aqc111_write32_cmd_nopm(dev, AQ_PHY_OPS, 0, 0,
+                               &aqc111_data->phy_cfg);
+
+       kfree(aqc111_data);
+}
+
+static void aqc111_status(struct usbnet *dev, struct urb *urb)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u64 *event_data = NULL;
+       int link = 0;
+
+       if (urb->actual_length < sizeof(*event_data))
+               return;
+
+       event_data = urb->transfer_buffer;
+       le64_to_cpus(event_data);
+
+       if (*event_data & AQ_LS_MASK)
+               link = 1;
+       else
+               link = 0;
+
+       aqc111_data->link_speed = (*event_data & AQ_SPEED_MASK) >>
+                                 AQ_SPEED_SHIFT;
+       aqc111_data->link = link;
+
+       if (netif_carrier_ok(dev->net) != link)
+               usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+}
+
+static void aqc111_configure_rx(struct usbnet *dev,
+                               struct aqc111_data *aqc111_data)
+{
+       enum usb_device_speed usb_speed = dev->udev->speed;
+       u16 link_speed = 0, usb_host = 0;
+       u8 buf[5] = { 0 };
+       u8 queue_num = 0;
+       u16 reg16 = 0;
+       u8 reg8 = 0;
+
+       buf[0] = 0x00;
+       buf[1] = 0xF8;
+       buf[2] = 0x07;
+       switch (aqc111_data->link_speed) {
+       case AQ_INT_SPEED_5G:
+               link_speed = 5000;
+               reg8 = 0x05;
+               reg16 = 0x001F;
+               break;
+       case AQ_INT_SPEED_2_5G:
+               link_speed = 2500;
+               reg16 = 0x003F;
+               break;
+       case AQ_INT_SPEED_1G:
+               link_speed = 1000;
+               reg16 = 0x009F;
+               break;
+       case AQ_INT_SPEED_100M:
+               link_speed = 100;
+               queue_num = 1;
+               reg16 = 0x063F;
+               buf[1] = 0xFB;
+               buf[2] = 0x4;
+               break;
+       }
+
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_INTER_PACKET_GAP_0,
+                        1, 1, &reg8);
+
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TX_PAUSE_RESEND_T, 3, 3, buf);
+
+       switch (usb_speed) {
+       case USB_SPEED_SUPER:
+               usb_host = 3;
+               break;
+       case USB_SPEED_HIGH:
+               usb_host = 2;
+               break;
+       case USB_SPEED_FULL:
+       case USB_SPEED_LOW:
+               usb_host = 1;
+               queue_num = 0;
+               break;
+       default:
+               usb_host = 0;
+               break;
+       }
+
+       if (dev->net->mtu > 12500 && dev->net->mtu <= 16334)
+               queue_num = 2; /* For Jumbo packet 16KB */
+
+       memcpy(buf, &AQC111_BULKIN_SIZE[queue_num], 5);
+       /* RX bulk configuration */
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL, 5, 5, buf);
+
+       /* Set high low water level */
+       if (dev->net->mtu <= 4500)
+               reg16 = 0x0810;
+       else if (dev->net->mtu <= 9500)
+               reg16 = 0x1020;
+       else if (dev->net->mtu <= 12500)
+               reg16 = 0x1420;
+       else if (dev->net->mtu <= 16334)
+               reg16 = 0x1A20;
+
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_PAUSE_WATERLVL_LOW,
+                          2, &reg16);
+       netdev_info(dev->net, "Link Speed %d, USB %d", link_speed, usb_host);
+}
+
+static void aqc111_configure_csum_offload(struct usbnet *dev)
+{
+       u8 reg8 = 0;
+
+       if (dev->net->features & NETIF_F_RXCSUM) {
+               reg8 |= SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP |
+                       SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6;
+       }
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL, 1, 1, &reg8);
+
+       reg8 = 0;
+       if (dev->net->features & NETIF_F_IP_CSUM)
+               reg8 |= SFR_TXCOE_IP | SFR_TXCOE_TCP | SFR_TXCOE_UDP;
+
+       if (dev->net->features & NETIF_F_IPV6_CSUM)
+               reg8 |= SFR_TXCOE_TCPV6 | SFR_TXCOE_UDPV6;
+
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, &reg8);
+}
+
+static int aqc111_link_reset(struct usbnet *dev)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u16 reg16 = 0;
+       u8 reg8 = 0;
+
+       if (aqc111_data->link == 1) { /* Link up */
+               aqc111_configure_rx(dev, aqc111_data);
+
+               /* Vlan Tag Filter */
+               reg8 = SFR_VLAN_CONTROL_VSO;
+               if (dev->net->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+                       reg8 |= SFR_VLAN_CONTROL_VFE;
+
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
+                                1, 1, &reg8);
+
+               reg8 = 0x0;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL,
+                                1, 1, &reg8);
+
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMTX_DMA_CONTROL,
+                                1, 1, &reg8);
+
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ARC_CTRL, 1, 1, &reg8);
+
+               reg16 = SFR_RX_CTL_IPE | SFR_RX_CTL_AB;
+               aqc111_data->rxctl = reg16;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+               reg8 = SFR_RX_PATH_READY;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+                                1, 1, &reg8);
+
+               reg8 = SFR_BULK_OUT_EFF_EN;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+                                1, 1, &reg8);
+
+               reg16 = 0;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                  2, &reg16);
+
+               reg16 = SFR_MEDIUM_XGMIIMODE | SFR_MEDIUM_FULL_DUPLEX;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                  2, &reg16);
+
+               aqc111_configure_csum_offload(dev);
+
+               aqc111_set_rx_mode(dev->net);
+
+               aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                 2, &reg16);
+
+               if (dev->net->mtu > 1500)
+                       reg16 |= SFR_MEDIUM_JUMBO_EN;
+
+               reg16 |= SFR_MEDIUM_RECEIVE_EN | SFR_MEDIUM_RXFLOW_CTRLEN |
+                        SFR_MEDIUM_TXFLOW_CTRLEN;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                  2, &reg16);
+
+               aqc111_data->rxctl |= SFR_RX_CTL_START;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+                                  2, &aqc111_data->rxctl);
+
+               netif_carrier_on(dev->net);
+       } else {
+               aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                 2, &reg16);
+               reg16 &= ~SFR_MEDIUM_RECEIVE_EN;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                  2, &reg16);
+
+               aqc111_data->rxctl &= ~SFR_RX_CTL_START;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+                                  2, &aqc111_data->rxctl);
+
+               reg8 = SFR_BULK_OUT_FLUSH_EN | SFR_BULK_OUT_EFF_EN;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+                                1, 1, &reg8);
+               reg8 = SFR_BULK_OUT_EFF_EN;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+                                1, 1, &reg8);
+
+               netif_carrier_off(dev->net);
+       }
+       return 0;
+}
+
+static int aqc111_reset(struct usbnet *dev)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u8 reg8 = 0;
+
+       dev->rx_urb_size = URB_SIZE;
+
+       if (usb_device_no_sg_constraint(dev->udev))
+               dev->can_dma_sg = 1;
+
+       dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
+       dev->net->features |= AQ_SUPPORT_FEATURE;
+       dev->net->vlan_features |= AQ_SUPPORT_VLAN_FEATURE;
+
+       /* Power up ethernet PHY */
+       aqc111_data->phy_cfg = AQ_PHY_POWER_EN;
+       aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+                          &aqc111_data->phy_cfg);
+
+       /* Set the MAC address */
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_NODE_ID, ETH_ALEN,
+                        ETH_ALEN, dev->net->dev_addr);
+
+       reg8 = 0xFF;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK, 1, 1, &reg8);
+
+       reg8 = 0x0;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_SWP_CTRL, 1, 1, &reg8);
+
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, &reg8);
+       reg8 &= ~(SFR_MONITOR_MODE_EPHYRW | SFR_MONITOR_MODE_RWLC |
+                 SFR_MONITOR_MODE_RWMP | SFR_MONITOR_MODE_RWWF |
+                 SFR_MONITOR_MODE_RW_FLAG);
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, &reg8);
+
+       netif_carrier_off(dev->net);
+
+       /* Phy advertise */
+       aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+                            aqc111_data->advertised_speed);
+
+       return 0;
+}
+
+static int aqc111_stop(struct usbnet *dev)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u16 reg16 = 0;
+
+       aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                         2, &reg16);
+       reg16 &= ~SFR_MEDIUM_RECEIVE_EN;
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                          2, &reg16);
+       reg16 = 0;
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+       /* Put PHY to low power*/
+       aqc111_data->phy_cfg |= AQ_LOW_POWER;
+       aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+                          &aqc111_data->phy_cfg);
+
+       netif_carrier_off(dev->net);
+
+       return 0;
+}
+
+static void aqc111_rx_checksum(struct sk_buff *skb, u64 pkt_desc)
+{
+       u32 pkt_type = 0;
+
+       skb->ip_summed = CHECKSUM_NONE;
+       /* checksum error bit is set */
+       if (pkt_desc & AQ_RX_PD_L4_ERR || pkt_desc & AQ_RX_PD_L3_ERR)
+               return;
+
+       pkt_type = pkt_desc & AQ_RX_PD_L4_TYPE_MASK;
+       /* It must be a TCP or UDP packet with a valid checksum */
+       if (pkt_type == AQ_RX_PD_L4_TCP || pkt_type == AQ_RX_PD_L4_UDP)
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       struct sk_buff *new_skb = NULL;
+       u32 pkt_total_offset = 0;
+       u64 *pkt_desc_ptr = NULL;
+       u32 start_of_descs = 0;
+       u32 desc_offset = 0; /*RX Header Offset*/
+       u16 pkt_count = 0;
+       u64 desc_hdr = 0;
+       u16 vlan_tag = 0;
+       u32 skb_len = 0;
+
+       if (!skb)
+               goto err;
+
+       if (skb->len == 0)
+               goto err;
+
+       skb_len = skb->len;
+       /* RX Descriptor Header */
+       skb_trim(skb, skb->len - sizeof(desc_hdr));
+       desc_hdr = le64_to_cpup((u64 *)skb_tail_pointer(skb));
+
+       /* Check these packets */
+       desc_offset = (desc_hdr & AQ_RX_DH_DESC_OFFSET_MASK) >>
+                     AQ_RX_DH_DESC_OFFSET_SHIFT;
+       pkt_count = desc_hdr & AQ_RX_DH_PKT_CNT_MASK;
+       start_of_descs = skb_len - ((pkt_count + 1) *  sizeof(desc_hdr));
+
+       /* self check descs position */
+       if (start_of_descs != desc_offset)
+               goto err;
+
+       /* self check desc_offset from header*/
+       if (desc_offset >= skb_len)
+               goto err;
+
+       if (pkt_count == 0)
+               goto err;
+
+       /* Get the first RX packet descriptor */
+       pkt_desc_ptr = (u64 *)(skb->data + desc_offset);
+
+       while (pkt_count--) {
+               u64 pkt_desc = le64_to_cpup(pkt_desc_ptr);
+               u32 pkt_len_with_padd = 0;
+               u32 pkt_len = 0;
+
+               pkt_len = (u32)((pkt_desc & AQ_RX_PD_LEN_MASK) >>
+                         AQ_RX_PD_LEN_SHIFT);
+               pkt_len_with_padd = ((pkt_len + 7) & 0x7FFF8);
+
+               pkt_total_offset += pkt_len_with_padd;
+               if (pkt_total_offset > desc_offset ||
+                   (pkt_count == 0 && pkt_total_offset != desc_offset)) {
+                       goto err;
+               }
+
+               if (pkt_desc & AQ_RX_PD_DROP ||
+                   !(pkt_desc & AQ_RX_PD_RX_OK) ||
+                   pkt_len > (dev->hard_mtu + AQ_RX_HW_PAD)) {
+                       skb_pull(skb, pkt_len_with_padd);
+                       /* Next RX Packet Descriptor */
+                       pkt_desc_ptr++;
+                       continue;
+               }
+
+               /* Clone SKB */
+               new_skb = skb_clone(skb, GFP_ATOMIC);
+
+               if (!new_skb)
+                       goto err;
+
+               new_skb->len = pkt_len;
+               skb_pull(new_skb, AQ_RX_HW_PAD);
+               skb_set_tail_pointer(new_skb, new_skb->len);
+
+               new_skb->truesize = SKB_TRUESIZE(new_skb->len);
+               if (aqc111_data->rx_checksum)
+                       aqc111_rx_checksum(new_skb, pkt_desc);
+
+               if (pkt_desc & AQ_RX_PD_VLAN) {
+                       vlan_tag = pkt_desc >> AQ_RX_PD_VLAN_SHIFT;
+                       __vlan_hwaccel_put_tag(new_skb, htons(ETH_P_8021Q),
+                                              vlan_tag & VLAN_VID_MASK);
+               }
+
+               usbnet_skb_return(dev, new_skb);
+               if (pkt_count == 0)
+                       break;
+
+               skb_pull(skb, pkt_len_with_padd);
+
+               /* Next RX Packet Header */
+               pkt_desc_ptr++;
+
+               new_skb = NULL;
+       }
+
+       return 1;
+
+err:
+       return 0;
+}
+
+static struct sk_buff *aqc111_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+                                      gfp_t flags)
+{
+       int frame_size = dev->maxpacket;
+       struct sk_buff *new_skb = NULL;
+       u64 *tx_desc_ptr = NULL;
+       int padding_size = 0;
+       int headroom = 0;
+       int tailroom = 0;
+       u64 tx_desc = 0;
+       u16 tci = 0;
+
+       /*Length of actual data*/
+       tx_desc |= skb->len & AQ_TX_DESC_LEN_MASK;
+
+       /* TSO MSS */
+       tx_desc |= ((u64)(skb_shinfo(skb)->gso_size & AQ_TX_DESC_MSS_MASK)) <<
+                  AQ_TX_DESC_MSS_SHIFT;
+
+       headroom = (skb->len + sizeof(tx_desc)) % 8;
+       if (headroom != 0)
+               padding_size = 8 - headroom;
+
+       if (((skb->len + sizeof(tx_desc) + padding_size) % frame_size) == 0) {
+               padding_size += 8;
+               tx_desc |= AQ_TX_DESC_DROP_PADD;
+       }
+
+       /* Vlan Tag */
+       if (vlan_get_tag(skb, &tci) >= 0) {
+               tx_desc |= AQ_TX_DESC_VLAN;
+               tx_desc |= ((u64)tci & AQ_TX_DESC_VLAN_MASK) <<
+                          AQ_TX_DESC_VLAN_SHIFT;
+       }
+
+       if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) &&
+           skb_linearize(skb))
+               return NULL;
+
+       headroom = skb_headroom(skb);
+       tailroom = skb_tailroom(skb);
+
+       if (!(headroom >= sizeof(tx_desc) && tailroom >= padding_size)) {
+               new_skb = skb_copy_expand(skb, sizeof(tx_desc),
+                                         padding_size, flags);
+               dev_kfree_skb_any(skb);
+               skb = new_skb;
+               if (!skb)
+                       return NULL;
+       }
+       if (padding_size != 0)
+               skb_put_zero(skb, padding_size);
+       /* Copy TX header */
+       tx_desc_ptr = skb_push(skb, sizeof(tx_desc));
+       *tx_desc_ptr = cpu_to_le64(tx_desc);
+
+       usbnet_set_skb_tx_stats(skb, 1, 0);
+
+       return skb;
+}
+
+static const struct driver_info aqc111_info = {
+       .description    = "Aquantia AQtion USB to 5GbE Controller",
+       .bind           = aqc111_bind,
+       .unbind         = aqc111_unbind,
+       .status         = aqc111_status,
+       .link_reset     = aqc111_link_reset,
+       .reset          = aqc111_reset,
+       .stop           = aqc111_stop,
+       .flags          = FLAG_ETHER | FLAG_FRAMING_AX |
+                         FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+       .rx_fixup       = aqc111_rx_fixup,
+       .tx_fixup       = aqc111_tx_fixup,
+};
+
+#define ASIX111_DESC \
+"ASIX USB 3.1 Gen1 to 5G Multi-Gigabit Ethernet Adapter"
+
+static const struct driver_info asix111_info = {
+       .description    = ASIX111_DESC,
+       .bind           = aqc111_bind,
+       .unbind         = aqc111_unbind,
+       .status         = aqc111_status,
+       .link_reset     = aqc111_link_reset,
+       .reset          = aqc111_reset,
+       .stop           = aqc111_stop,
+       .flags          = FLAG_ETHER | FLAG_FRAMING_AX |
+                         FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+       .rx_fixup       = aqc111_rx_fixup,
+       .tx_fixup       = aqc111_tx_fixup,
+};
+
+#undef ASIX111_DESC
+
+#define ASIX112_DESC \
+"ASIX USB 3.1 Gen1 to 2.5G Multi-Gigabit Ethernet Adapter"
+
+static const struct driver_info asix112_info = {
+       .description    = ASIX112_DESC,
+       .bind           = aqc111_bind,
+       .unbind         = aqc111_unbind,
+       .status         = aqc111_status,
+       .link_reset     = aqc111_link_reset,
+       .reset          = aqc111_reset,
+       .stop           = aqc111_stop,
+       .flags          = FLAG_ETHER | FLAG_FRAMING_AX |
+                         FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+       .rx_fixup       = aqc111_rx_fixup,
+       .tx_fixup       = aqc111_tx_fixup,
+};
+
+#undef ASIX112_DESC
+
+static int aqc111_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct usbnet *dev = usb_get_intfdata(intf);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u16 temp_rx_ctrl = 0x00;
+       u16 reg16;
+       u8 reg8;
+
+       usbnet_suspend(intf, message);
+
+       aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+       temp_rx_ctrl = reg16;
+       /* Stop RX operations*/
+       reg16 &= ~SFR_RX_CTL_START;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+       /* Force bz */
+       aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+                              2, &reg16);
+       reg16 |= SFR_PHYPWR_RSTCTL_BZ;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+                               2, &reg16);
+
+       reg8 = SFR_BULK_OUT_EFF_EN;
+       aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+                             1, 1, &reg8);
+
+       temp_rx_ctrl &= ~(SFR_RX_CTL_START | SFR_RX_CTL_RF_WAK |
+                         SFR_RX_CTL_AP | SFR_RX_CTL_AM);
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+                               2, &temp_rx_ctrl);
+
+       reg8 = 0x00;
+       aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+                             1, 1, &reg8);
+
+       if (aqc111_data->wol_flags) {
+               struct aqc111_wol_cfg wol_cfg;
+
+               memset(&wol_cfg, 0, sizeof(struct aqc111_wol_cfg));
+
+               aqc111_data->phy_cfg |= AQ_WOL;
+               ether_addr_copy(wol_cfg.hw_addr, dev->net->dev_addr);
+               wol_cfg.flags = aqc111_data->wol_flags;
+
+               temp_rx_ctrl |= (SFR_RX_CTL_AB | SFR_RX_CTL_START);
+               aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+                                       2, &temp_rx_ctrl);
+               reg8 = 0x00;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK,
+                                     1, 1, &reg8);
+               reg8 = SFR_BMRX_DMA_EN;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL,
+                                     1, 1, &reg8);
+               reg8 = SFR_RX_PATH_READY;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+                                     1, 1, &reg8);
+               reg8 = 0x07;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL,
+                                     1, 1, &reg8);
+               reg8 = 0x00;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                     SFR_RX_BULKIN_QTIMR_LOW, 1, 1, &reg8);
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                     SFR_RX_BULKIN_QTIMR_HIGH, 1, 1, &reg8);
+               reg8 = 0xFF;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QSIZE,
+                                     1, 1, &reg8);
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QIFG,
+                                     1, 1, &reg8);
+
+               aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                      SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+               reg16 |= SFR_MEDIUM_RECEIVE_EN;
+               aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                       SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+
+               aqc111_write_cmd(dev, AQ_WOL_CFG, 0, 0,
+                                WOL_CFG_SIZE, &wol_cfg);
+               aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+                                  &aqc111_data->phy_cfg);
+       } else {
+               aqc111_data->phy_cfg |= AQ_LOW_POWER;
+               aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+                                  &aqc111_data->phy_cfg);
+
+               /* Disable RX path */
+               aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                      SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+               reg16 &= ~SFR_MEDIUM_RECEIVE_EN;
+               aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                       SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+       }
+
+       return 0;
+}
+
+static int aqc111_resume(struct usb_interface *intf)
+{
+       struct usbnet *dev = usb_get_intfdata(intf);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u16 reg16;
+       u8 reg8;
+
+       netif_carrier_off(dev->net);
+
+       /* Power up ethernet PHY */
+       aqc111_data->phy_cfg |= AQ_PHY_POWER_EN;
+       aqc111_data->phy_cfg &= ~AQ_LOW_POWER;
+       aqc111_data->phy_cfg &= ~AQ_WOL;
+
+       reg8 = 0xFF;
+       aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK,
+                             1, 1, &reg8);
+       /* Configure RX control register => start operation */
+       reg16 = aqc111_data->rxctl;
+       reg16 &= ~SFR_RX_CTL_START;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+       reg16 |= SFR_RX_CTL_START;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+       aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+                            aqc111_data->advertised_speed);
+
+       aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                              2, &reg16);
+       reg16 |= SFR_MEDIUM_RECEIVE_EN;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                               2, &reg16);
+       reg8 = SFR_RX_PATH_READY;
+       aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+                             1, 1, &reg8);
+       reg8 = 0x0;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL, 1, 1, &reg8);
+
+       return usbnet_resume(intf);
+}
+
+#define AQC111_USB_ETH_DEV(vid, pid, table) \
+       USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_VENDOR_SPEC), \
+       .driver_info = (unsigned long)&(table) \
+}, \
+{ \
+       USB_DEVICE_AND_INTERFACE_INFO((vid), (pid), \
+                                     USB_CLASS_COMM, \
+                                     USB_CDC_SUBCLASS_ETHERNET, \
+                                     USB_CDC_PROTO_NONE), \
+       .driver_info = (unsigned long)&(table),
+
+static const struct usb_device_id products[] = {
+       {AQC111_USB_ETH_DEV(0x2eca, 0xc101, aqc111_info)},
+       {AQC111_USB_ETH_DEV(0x0b95, 0x2790, asix111_info)},
+       {AQC111_USB_ETH_DEV(0x0b95, 0x2791, asix112_info)},
+       { },/* END */
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver aq_driver = {
+       .name           = "aqc111",
+       .id_table       = products,
+       .probe          = usbnet_probe,
+       .suspend        = aqc111_suspend,
+       .resume         = aqc111_resume,
+       .disconnect     = usbnet_disconnect,
+};
+
+module_usb_driver(aq_driver);
+
+MODULE_DESCRIPTION("Aquantia AQtion USB to 5/2.5GbE Controllers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/aqc111.h b/drivers/net/usb/aqc111.h
new file mode 100644 (file)
index 0000000..4d68b3a
--- /dev/null
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Aquantia Corp. Aquantia AQtion USB to 5GbE Controller
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
+ * Copyright (C) 2002-2003 TiVo Inc.
+ * Copyright (C) 2017-2018 ASIX
+ * Copyright (C) 2018 Aquantia Corp.
+ */
+
+#ifndef __LINUX_USBNET_AQC111_H
+#define __LINUX_USBNET_AQC111_H
+
+#define URB_SIZE       (1024 * 62)
+
+#define AQ_MCAST_FILTER_SIZE           8
+#define AQ_MAX_MCAST                   64
+
+#define AQ_ACCESS_MAC                  0x01
+#define AQ_FLASH_PARAMETERS            0x20
+#define AQ_PHY_POWER                   0x31
+#define AQ_WOL_CFG                     0x60
+#define AQ_PHY_OPS                     0x61
+
+#define AQ_USB_PHY_SET_TIMEOUT         10000
+#define AQ_USB_SET_TIMEOUT             4000
+
+/* Feature. ********************************************/
+#define AQ_SUPPORT_FEATURE     (NETIF_F_SG | NETIF_F_IP_CSUM |\
+                                NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
+                                NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_TX |\
+                                NETIF_F_HW_VLAN_CTAG_RX)
+
+#define AQ_SUPPORT_HW_FEATURE  (NETIF_F_SG | NETIF_F_IP_CSUM |\
+                                NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
+                                NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_FILTER)
+
+#define AQ_SUPPORT_VLAN_FEATURE (NETIF_F_SG | NETIF_F_IP_CSUM |\
+                                NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
+                                NETIF_F_TSO)
+
+/* SFR Reg. ********************************************/
+
+#define SFR_GENERAL_STATUS             0x03
+#define SFR_CHIP_STATUS                        0x05
+#define SFR_RX_CTL                     0x0B
+       #define SFR_RX_CTL_TXPADCRC             0x0400
+       #define SFR_RX_CTL_IPE                  0x0200
+       #define SFR_RX_CTL_DROPCRCERR           0x0100
+       #define SFR_RX_CTL_START                0x0080
+       #define SFR_RX_CTL_RF_WAK               0x0040
+       #define SFR_RX_CTL_AP                   0x0020
+       #define SFR_RX_CTL_AM                   0x0010
+       #define SFR_RX_CTL_AB                   0x0008
+       #define SFR_RX_CTL_AMALL                0x0002
+       #define SFR_RX_CTL_PRO                  0x0001
+       #define SFR_RX_CTL_STOP                 0x0000
+#define SFR_INTER_PACKET_GAP_0         0x0D
+#define SFR_NODE_ID                    0x10
+#define SFR_MULTI_FILTER_ARRY          0x16
+#define SFR_MEDIUM_STATUS_MODE         0x22
+       #define SFR_MEDIUM_XGMIIMODE            0x0001
+       #define SFR_MEDIUM_FULL_DUPLEX          0x0002
+       #define SFR_MEDIUM_RXFLOW_CTRLEN        0x0010
+       #define SFR_MEDIUM_TXFLOW_CTRLEN        0x0020
+       #define SFR_MEDIUM_JUMBO_EN             0x0040
+       #define SFR_MEDIUM_RECEIVE_EN           0x0100
+#define SFR_MONITOR_MODE               0x24
+       #define SFR_MONITOR_MODE_EPHYRW         0x01
+       #define SFR_MONITOR_MODE_RWLC           0x02
+       #define SFR_MONITOR_MODE_RWMP           0x04
+       #define SFR_MONITOR_MODE_RWWF           0x08
+       #define SFR_MONITOR_MODE_RW_FLAG        0x10
+       #define SFR_MONITOR_MODE_PMEPOL         0x20
+       #define SFR_MONITOR_MODE_PMETYPE        0x40
+#define SFR_PHYPWR_RSTCTL              0x26
+       #define SFR_PHYPWR_RSTCTL_BZ            0x0010
+       #define SFR_PHYPWR_RSTCTL_IPRL          0x0020
+#define SFR_VLAN_ID_ADDRESS            0x2A
+#define SFR_VLAN_ID_CONTROL            0x2B
+       #define SFR_VLAN_CONTROL_WE             0x0001
+       #define SFR_VLAN_CONTROL_RD             0x0002
+       #define SFR_VLAN_CONTROL_VSO            0x0010
+       #define SFR_VLAN_CONTROL_VFE            0x0020
+#define SFR_VLAN_ID_DATA0              0x2C
+#define SFR_VLAN_ID_DATA1              0x2D
+#define SFR_RX_BULKIN_QCTRL            0x2E
+       #define SFR_RX_BULKIN_QCTRL_TIME        0x01
+       #define SFR_RX_BULKIN_QCTRL_IFG         0x02
+       #define SFR_RX_BULKIN_QCTRL_SIZE        0x04
+#define SFR_RX_BULKIN_QTIMR_LOW                0x2F
+#define SFR_RX_BULKIN_QTIMR_HIGH       0x30
+#define SFR_RX_BULKIN_QSIZE            0x31
+#define SFR_RX_BULKIN_QIFG             0x32
+#define SFR_RXCOE_CTL                  0x34
+       #define SFR_RXCOE_IP                    0x01
+       #define SFR_RXCOE_TCP                   0x02
+       #define SFR_RXCOE_UDP                   0x04
+       #define SFR_RXCOE_ICMP                  0x08
+       #define SFR_RXCOE_IGMP                  0x10
+       #define SFR_RXCOE_TCPV6                 0x20
+       #define SFR_RXCOE_UDPV6                 0x40
+       #define SFR_RXCOE_ICMV6                 0x80
+#define SFR_TXCOE_CTL                  0x35
+       #define SFR_TXCOE_IP                    0x01
+       #define SFR_TXCOE_TCP                   0x02
+       #define SFR_TXCOE_UDP                   0x04
+       #define SFR_TXCOE_ICMP                  0x08
+       #define SFR_TXCOE_IGMP                  0x10
+       #define SFR_TXCOE_TCPV6                 0x20
+       #define SFR_TXCOE_UDPV6                 0x40
+       #define SFR_TXCOE_ICMV6                 0x80
+#define SFR_BM_INT_MASK                        0x41
+#define SFR_BMRX_DMA_CONTROL           0x43
+       #define SFR_BMRX_DMA_EN                 0x80
+#define SFR_BMTX_DMA_CONTROL           0x46
+#define SFR_PAUSE_WATERLVL_LOW         0x54
+#define SFR_PAUSE_WATERLVL_HIGH                0x55
+#define SFR_ARC_CTRL                   0x9E
+#define SFR_SWP_CTRL                   0xB1
+#define SFR_TX_PAUSE_RESEND_T          0xB2
+#define SFR_ETH_MAC_PATH               0xB7
+       #define SFR_RX_PATH_READY               0x01
+#define SFR_BULK_OUT_CTRL              0xB9
+       #define SFR_BULK_OUT_FLUSH_EN           0x01
+       #define SFR_BULK_OUT_EFF_EN             0x02
+
+#define AQ_FW_VER_MAJOR                        0xDA
+#define AQ_FW_VER_MINOR                        0xDB
+#define AQ_FW_VER_REV                  0xDC
+
+/*PHY_OPS**********************************************************************/
+
+#define AQ_ADV_100M    BIT(0)
+#define AQ_ADV_1G      BIT(1)
+#define AQ_ADV_2G5     BIT(2)
+#define AQ_ADV_5G      BIT(3)
+#define AQ_ADV_MASK    0x0F
+
+#define AQ_PAUSE       BIT(16)
+#define AQ_ASYM_PAUSE  BIT(17)
+#define AQ_LOW_POWER   BIT(18)
+#define AQ_PHY_POWER_EN        BIT(19)
+#define AQ_WOL         BIT(20)
+#define AQ_DOWNSHIFT   BIT(21)
+
+#define AQ_DSH_RETRIES_SHIFT   0x18
+#define AQ_DSH_RETRIES_MASK    0xF000000
+
+#define AQ_WOL_FLAG_MP                 0x2
+
+/******************************************************************************/
+
+struct aqc111_wol_cfg {
+       u8 hw_addr[6];
+       u8 flags;
+       u8 rsvd[283];
+} __packed;
+
+#define WOL_CFG_SIZE sizeof(struct aqc111_wol_cfg)
+
+struct aqc111_data {
+       u16 rxctl;
+       u8 rx_checksum;
+       u8 link_speed;
+       u8 link;
+       u8 autoneg;
+       u32 advertised_speed;
+       struct {
+               u8 major;
+               u8 minor;
+               u8 rev;
+       } fw_ver;
+       u32 phy_cfg;
+       u8 wol_flags;
+};
+
+#define AQ_LS_MASK             0x8000
+#define AQ_SPEED_MASK          0x7F00
+#define AQ_SPEED_SHIFT         0x0008
+#define AQ_INT_SPEED_5G                0x000F
+#define AQ_INT_SPEED_2_5G      0x0010
+#define AQ_INT_SPEED_1G                0x0011
+#define AQ_INT_SPEED_100M      0x0013
+
+/* TX Descriptor */
+#define AQ_TX_DESC_LEN_MASK    0x1FFFFF
+#define AQ_TX_DESC_DROP_PADD   BIT(28)
+#define AQ_TX_DESC_VLAN                BIT(29)
+#define AQ_TX_DESC_MSS_MASK    0x7FFF
+#define AQ_TX_DESC_MSS_SHIFT   0x20
+#define AQ_TX_DESC_VLAN_MASK   0xFFFF
+#define AQ_TX_DESC_VLAN_SHIFT  0x30
+
+#define AQ_RX_HW_PAD                   0x02
+
+/* RX Packet Descriptor */
+#define AQ_RX_PD_L4_ERR                BIT(0)
+#define AQ_RX_PD_L3_ERR                BIT(1)
+#define AQ_RX_PD_L4_TYPE_MASK  0x1C
+#define AQ_RX_PD_L4_UDP                0x04
+#define AQ_RX_PD_L4_TCP                0x10
+#define AQ_RX_PD_L3_TYPE_MASK  0x60
+#define AQ_RX_PD_L3_IP         0x20
+#define AQ_RX_PD_L3_IP6                0x40
+
+#define AQ_RX_PD_VLAN          BIT(10)
+#define AQ_RX_PD_RX_OK         BIT(11)
+#define AQ_RX_PD_DROP          BIT(31)
+#define AQ_RX_PD_LEN_MASK      0x7FFF0000
+#define AQ_RX_PD_LEN_SHIFT     0x10
+#define AQ_RX_PD_VLAN_SHIFT    0x20
+
+/* RX Descriptor header */
+#define AQ_RX_DH_PKT_CNT_MASK          0x1FFF
+#define AQ_RX_DH_DESC_OFFSET_MASK      0xFFFFE000
+#define AQ_RX_DH_DESC_OFFSET_SHIFT     0x0D
+
+static struct {
+       unsigned char ctrl;
+       unsigned char timer_l;
+       unsigned char timer_h;
+       unsigned char size;
+       unsigned char ifg;
+} AQC111_BULKIN_SIZE[] = {
+       /* xHCI & EHCI & OHCI */
+       {7, 0x00, 0x01, 0x1E, 0xFF},/* 10G, 5G, 2.5G, 1G */
+       {7, 0xA0, 0x00, 0x14, 0x00},/* 100M */
+       /* Jumbo packet */
+       {7, 0x00, 0x01, 0x18, 0xFF},
+};
+
+#endif /* __LINUX_USBNET_AQC111_H */
index 5c42cf8..b3b3c05 100644 (file)
@@ -562,6 +562,8 @@ static const struct driver_info wwan_info = {
 #define MICROSOFT_VENDOR_ID    0x045e
 #define UBLOX_VENDOR_ID                0x1546
 #define TPLINK_VENDOR_ID       0x2357
+#define AQUANTIA_VENDOR_ID     0x2eca
+#define ASIX_VENDOR_ID         0x0b95
 
 static const struct usb_device_id      products[] = {
 /* BLACKLIST !!
@@ -821,6 +823,30 @@ static const struct usb_device_id  products[] = {
        .driver_info = 0,
 },
 
+/* Aquantia AQtion USB to 5GbE Controller (based on AQC111U) */
+{
+       USB_DEVICE_AND_INTERFACE_INFO(AQUANTIA_VENDOR_ID, 0xc101,
+                                     USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
+                                     USB_CDC_PROTO_NONE),
+       .driver_info = 0,
+},
+
+/* ASIX USB 3.1 Gen1 to 5G Multi-Gigabit Ethernet Adapter(based on AQC111U) */
+{
+       USB_DEVICE_AND_INTERFACE_INFO(ASIX_VENDOR_ID, 0x2790, USB_CLASS_COMM,
+                                     USB_CDC_SUBCLASS_ETHERNET,
+                                     USB_CDC_PROTO_NONE),
+       .driver_info = 0,
+},
+
+/* ASIX USB 3.1 Gen1 to 2.5G Multi-Gigabit Ethernet Adapter(based on AQC112U) */
+{
+       USB_DEVICE_AND_INTERFACE_INFO(ASIX_VENDOR_ID, 0x2791, USB_CLASS_COMM,
+                                     USB_CDC_SUBCLASS_ETHERNET,
+                                     USB_CDC_PROTO_NONE),
+       .driver_info = 0,
+},
+
 /* WHITELIST!!!
  *
  * CDC Ether uses two interfaces, not necessarily consecutive.
index 184c24b..d6916f7 100644 (file)
@@ -2807,6 +2807,12 @@ static int hso_get_config_data(struct usb_interface *interface)
                return -EIO;
        }
 
+       /* check if we have a valid interface */
+       if (if_num > 16) {
+               kfree(config_data);
+               return -EINVAL;
+       }
+
        switch (config_data[if_num]) {
        case 0x0:
                result = 0;
@@ -2877,10 +2883,18 @@ static int hso_probe(struct usb_interface *interface,
 
        /* Get the interface/port specification from either driver_info or from
         * the device itself */
-       if (id->driver_info)
+       if (id->driver_info) {
+               /* if_num is controlled by the device, driver_info is a 0 terminated
+                * array. Make sure, the access is in bounds! */
+               for (i = 0; i <= if_num; ++i)
+                       if (((u32 *)(id->driver_info))[i] == 0)
+                               goto exit;
                port_spec = ((u32 *)(id->driver_info))[if_num];
-       else
+       } else {
                port_spec = hso_get_config_data(interface);
+               if (port_spec < 0)
+                       goto exit;
+       }
 
        /* Check if we need to switch to alt interfaces prior to port
         * configuration */
index 7275761..3d8a70d 100644 (file)
@@ -140,7 +140,6 @@ struct ipheth_device {
        struct usb_device *udev;
        struct usb_interface *intf;
        struct net_device *net;
-       struct sk_buff *tx_skb;
        struct urb *tx_urb;
        struct urb *rx_urb;
        unsigned char *tx_buf;
@@ -230,6 +229,7 @@ static void ipheth_rcvbulk_callback(struct urb *urb)
        case -ENOENT:
        case -ECONNRESET:
        case -ESHUTDOWN:
+       case -EPROTO:
                return;
        case 0:
                break;
@@ -281,7 +281,6 @@ static void ipheth_sndbulk_callback(struct urb *urb)
                dev_err(&dev->intf->dev, "%s: urb status: %d\n",
                __func__, status);
 
-       dev_kfree_skb_irq(dev->tx_skb);
        if (status == 0)
                netif_wake_queue(dev->net);
        else
@@ -423,7 +422,7 @@ static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
        if (skb->len > IPHETH_BUF_SIZE) {
                WARN(1, "%s: skb too large: %d bytes\n", __func__, skb->len);
                dev->net->stats.tx_dropped++;
-               dev_kfree_skb_irq(skb);
+               dev_kfree_skb_any(skb);
                return NETDEV_TX_OK;
        }
 
@@ -443,12 +442,11 @@ static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
                dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n",
                        __func__, retval);
                dev->net->stats.tx_errors++;
-               dev_kfree_skb_irq(skb);
+               dev_kfree_skb_any(skb);
        } else {
-               dev->tx_skb = skb;
-
                dev->net->stats.tx_packets++;
                dev->net->stats.tx_bytes += skb->len;
+               dev_consume_skb_any(skb);
                netif_stop_queue(net);
        }
 
index 3c8bdac..e96bc0c 100644 (file)
@@ -2325,6 +2325,10 @@ static int lan78xx_set_mac_addr(struct net_device *netdev, void *p)
        ret = lan78xx_write_reg(dev, RX_ADDRL, addr_lo);
        ret = lan78xx_write_reg(dev, RX_ADDRH, addr_hi);
 
+       /* Added to support MAC address changes */
+       ret = lan78xx_write_reg(dev, MAF_LO(0), addr_lo);
+       ret = lan78xx_write_reg(dev, MAF_HI(0), addr_hi | MAF_HI_VALID_);
+
        return 0;
 }
 
index 72a55b6..c8872dd 100644 (file)
@@ -1117,6 +1117,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x1435, 0xd181, 4)},    /* Wistron NeWeb D18Q1 */
        {QMI_FIXED_INTF(0x1435, 0xd181, 5)},    /* Wistron NeWeb D18Q1 */
        {QMI_FIXED_INTF(0x1435, 0xd191, 4)},    /* Wistron NeWeb D19Q1 */
+       {QMI_QUIRK_SET_DTR(0x1508, 0x1001, 4)}, /* Fibocom NL668 series */
        {QMI_FIXED_INTF(0x16d8, 0x6003, 0)},    /* CMOTech 6003 */
        {QMI_FIXED_INTF(0x16d8, 0x6007, 0)},    /* CMOTech CHE-628S */
        {QMI_FIXED_INTF(0x16d8, 0x6008, 0)},    /* CMOTech CMU-301 */
@@ -1229,6 +1230,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x1bc7, 0x1101, 3)},    /* Telit ME910 dual modem */
        {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)},    /* Telit LE920 */
        {QMI_QUIRK_SET_DTR(0x1bc7, 0x1201, 2)}, /* Telit LE920, LE920A4 */
+       {QMI_QUIRK_SET_DTR(0x1bc7, 0x1900, 1)}, /* Telit LN940 series */
        {QMI_FIXED_INTF(0x1c9e, 0x9801, 3)},    /* Telewell TW-3G HSPA+ */
        {QMI_FIXED_INTF(0x1c9e, 0x9803, 4)},    /* Telewell TW-3G HSPA+ */
        {QMI_FIXED_INTF(0x1c9e, 0x9b01, 3)},    /* XS Stick W100-2 from 4G Systems */
index f1b5201..60dd1ec 100644 (file)
 #define USB_UPS_CTRL           0xd800
 #define USB_POWER_CUT          0xd80a
 #define USB_MISC_0             0xd81a
+#define USB_MISC_1             0xd81f
 #define USB_AFE_CTRL2          0xd824
 #define USB_UPS_CFG            0xd842
 #define USB_UPS_FLAGS          0xd848
@@ -555,6 +556,7 @@ enum spd_duplex {
 
 /* MAC PASSTHRU */
 #define AD_MASK                        0xfee0
+#define BND_MASK               0x0004
 #define EFUSE                  0xcfdb
 #define PASS_THRU_MASK         0x1
 
@@ -1150,7 +1152,7 @@ out1:
        return ret;
 }
 
-/* Devices containing RTL8153-AD can support a persistent
+/* Devices containing proper chips can support a persistent
  * host system provided MAC address.
  * Examples of this are Dell TB15 and Dell WD15 docks
  */
@@ -1165,13 +1167,23 @@ static int vendor_mac_passthru_addr_read(struct r8152 *tp, struct sockaddr *sa)
 
        /* test for -AD variant of RTL8153 */
        ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
-       if ((ocp_data & AD_MASK) != 0x1000)
-               return -ENODEV;
-
-       /* test for MAC address pass-through bit */
-       ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, EFUSE);
-       if ((ocp_data & PASS_THRU_MASK) != 1)
-               return -ENODEV;
+       if ((ocp_data & AD_MASK) == 0x1000) {
+               /* test for MAC address pass-through bit */
+               ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, EFUSE);
+               if ((ocp_data & PASS_THRU_MASK) != 1) {
+                       netif_dbg(tp, probe, tp->netdev,
+                                 "No efuse for RTL8153-AD MAC pass through\n");
+                       return -ENODEV;
+               }
+       } else {
+               /* test for RTL8153-BND */
+               ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_1);
+               if ((ocp_data & BND_MASK) == 0) {
+                       netif_dbg(tp, probe, tp->netdev,
+                                 "Invalid variant for MAC pass through\n");
+                       return -ENODEV;
+               }
+       }
 
        /* returns _AUXMAC_#AABBCCDDEEFF# */
        status = acpi_evaluate_object(NULL, "\\_SB.AMAC", NULL, &buffer);
@@ -1217,9 +1229,8 @@ static int set_ethernet_addr(struct r8152 *tp)
        if (tp->version == RTL_VER_01) {
                ret = pla_ocp_read(tp, PLA_IDR, 8, sa.sa_data);
        } else {
-               /* if this is not an RTL8153-AD, no eFuse mac pass thru set,
-                * or system doesn't provide valid _SB.AMAC this will be
-                * be expected to non-zero
+               /* if device doesn't support MAC pass through this will
+                * be expected to be non-zero
                 */
                ret = vendor_mac_passthru_addr_read(tp, &sa);
                if (ret < 0)
index f2d01cb..e3d0862 100644 (file)
@@ -618,9 +618,7 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
                return;
        }
 
-       memcpy(&intdata, urb->transfer_buffer, 4);
-       le32_to_cpus(&intdata);
-
+       intdata = get_unaligned_le32(urb->transfer_buffer);
        netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata);
 
        if (intdata & INT_ENP_PHY_INT_)
@@ -1295,6 +1293,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
                dev->net->features |= NETIF_F_RXCSUM;
 
        dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+       set_bit(EVENT_NO_IP_ALIGN, &dev->flags);
 
        smsc95xx_init_mac_address(dev);
 
@@ -1933,8 +1932,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                unsigned char *packet;
                u16 size;
 
-               memcpy(&header, skb->data, sizeof(header));
-               le32_to_cpus(&header);
+               header = get_unaligned_le32(skb->data);
                skb_pull(skb, 4 + NET_IP_ALIGN);
                packet = skb->data;
 
@@ -2011,12 +2009,30 @@ static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb)
        return (high_16 << 16) | low_16;
 }
 
+/* The TX CSUM won't work if the checksum lies in the last 4 bytes of the
+ * transmission. This is fairly unlikely, only seems to trigger with some
+ * short TCP ACK packets sent.
+ *
+ * Note, this calculation should probably check for the alignment of the
+ * data as well, but a straight check for csum being in the last four bytes
+ * of the packet should be ok for now.
+ */
+static bool smsc95xx_can_tx_checksum(struct sk_buff *skb)
+{
+       unsigned int len = skb->len - skb_checksum_start_offset(skb);
+
+       if (skb->len <= 45)
+              return false;
+       return skb->csum_offset < (len - (4 + 1));
+}
+
 static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
                                         struct sk_buff *skb, gfp_t flags)
 {
        bool csum = skb->ip_summed == CHECKSUM_PARTIAL;
        int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD;
        u32 tx_cmd_a, tx_cmd_b;
+       void *ptr;
 
        /* We do not advertise SG, so skbs should be already linearized */
        BUG_ON(skb_shinfo(skb)->nr_frags);
@@ -2030,8 +2046,11 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
                return NULL;
        }
 
+       tx_cmd_b = (u32)skb->len;
+       tx_cmd_a = tx_cmd_b | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+
        if (csum) {
-               if (skb->len <= 45) {
+               if (!smsc95xx_can_tx_checksum(skb)) {
                        /* workaround - hardware tx checksum does not work
                         * properly with extremely small packets */
                        long csstart = skb_checksum_start_offset(skb);
@@ -2043,24 +2062,18 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
                        csum = false;
                } else {
                        u32 csum_preamble = smsc95xx_calc_csum_preamble(skb);
-                       skb_push(skb, 4);
-                       cpu_to_le32s(&csum_preamble);
-                       memcpy(skb->data, &csum_preamble, 4);
+                       ptr = skb_push(skb, 4);
+                       put_unaligned_le32(csum_preamble, ptr);
+
+                       tx_cmd_a += 4;
+                       tx_cmd_b += 4;
+                       tx_cmd_b |= TX_CMD_B_CSUM_ENABLE;
                }
        }
 
-       skb_push(skb, 4);
-       tx_cmd_b = (u32)(skb->len - 4);
-       if (csum)
-               tx_cmd_b |= TX_CMD_B_CSUM_ENABLE;
-       cpu_to_le32s(&tx_cmd_b);
-       memcpy(skb->data, &tx_cmd_b, 4);
-
-       skb_push(skb, 4);
-       tx_cmd_a = (u32)(skb->len - 8) | TX_CMD_A_FIRST_SEG_ |
-               TX_CMD_A_LAST_SEG_;
-       cpu_to_le32s(&tx_cmd_a);
-       memcpy(skb->data, &tx_cmd_a, 4);
+       ptr = skb_push(skb, 8);
+       put_unaligned_le32(tx_cmd_a, ptr);
+       put_unaligned_le32(tx_cmd_b, ptr+4);
 
        return skb;
 }
index 3e2c041..ea67214 100644 (file)
@@ -70,7 +70,8 @@ static const unsigned long guest_offloads[] = {
        VIRTIO_NET_F_GUEST_TSO4,
        VIRTIO_NET_F_GUEST_TSO6,
        VIRTIO_NET_F_GUEST_ECN,
-       VIRTIO_NET_F_GUEST_UFO
+       VIRTIO_NET_F_GUEST_UFO,
+       VIRTIO_NET_F_GUEST_CSUM
 };
 
 struct virtnet_stat_desc {
@@ -364,7 +365,8 @@ static unsigned int mergeable_ctx_to_truesize(void *mrg_ctx)
 static struct sk_buff *page_to_skb(struct virtnet_info *vi,
                                   struct receive_queue *rq,
                                   struct page *page, unsigned int offset,
-                                  unsigned int len, unsigned int truesize)
+                                  unsigned int len, unsigned int truesize,
+                                  bool hdr_valid)
 {
        struct sk_buff *skb;
        struct virtio_net_hdr_mrg_rxbuf *hdr;
@@ -386,7 +388,8 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi,
        else
                hdr_padded_len = sizeof(struct padded_vnet_hdr);
 
-       memcpy(hdr, p, hdr_len);
+       if (hdr_valid)
+               memcpy(hdr, p, hdr_len);
 
        len -= hdr_len;
        offset += hdr_padded_len;
@@ -738,7 +741,8 @@ static struct sk_buff *receive_big(struct net_device *dev,
                                   struct virtnet_rq_stats *stats)
 {
        struct page *page = buf;
-       struct sk_buff *skb = page_to_skb(vi, rq, page, 0, len, PAGE_SIZE);
+       struct sk_buff *skb = page_to_skb(vi, rq, page, 0, len,
+                                         PAGE_SIZE, true);
 
        stats->bytes += len - vi->hdr_len;
        if (unlikely(!skb))
@@ -841,7 +845,8 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
                                rcu_read_unlock();
                                put_page(page);
                                head_skb = page_to_skb(vi, rq, xdp_page,
-                                                      offset, len, PAGE_SIZE);
+                                                      offset, len,
+                                                      PAGE_SIZE, false);
                                return head_skb;
                        }
                        break;
@@ -897,7 +902,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
                goto err_skb;
        }
 
-       head_skb = page_to_skb(vi, rq, page, offset, len, truesize);
+       head_skb = page_to_skb(vi, rq, page, offset, len, truesize, !xdp_prog);
        curr_skb = head_skb;
 
        if (unlikely(!curr_skb))
@@ -2334,9 +2339,6 @@ static int virtnet_clear_guest_offloads(struct virtnet_info *vi)
        if (!vi->guest_offloads)
                return 0;
 
-       if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM))
-               offloads = 1ULL << VIRTIO_NET_F_GUEST_CSUM;
-
        return virtnet_set_guest_offloads(vi, offloads);
 }
 
@@ -2346,8 +2348,6 @@ static int virtnet_restore_guest_offloads(struct virtnet_info *vi)
 
        if (!vi->guest_offloads)
                return 0;
-       if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM))
-               offloads |= 1ULL << VIRTIO_NET_F_GUEST_CSUM;
 
        return virtnet_set_guest_offloads(vi, offloads);
 }
@@ -2365,8 +2365,9 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog,
            && (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO4) ||
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) ||
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) ||
-               virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO))) {
-               NL_SET_ERR_MSG_MOD(extack, "Can't set XDP while host is implementing LRO, disable LRO first");
+               virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO) ||
+               virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM))) {
+               NL_SET_ERR_MSG_MOD(extack, "Can't set XDP while host is implementing LRO/CSUM, disable LRO/CSUM first");
                return -EOPNOTSUPP;
        }
 
index 21ad4b1..95909e2 100644 (file)
@@ -747,7 +747,8 @@ static int vrf_rtable_create(struct net_device *dev)
 /**************************** device handling ********************/
 
 /* cycle interface to flush neighbor cache and move routes across tables */
-static void cycle_netdev(struct net_device *dev)
+static void cycle_netdev(struct net_device *dev,
+                        struct netlink_ext_ack *extack)
 {
        unsigned int flags = dev->flags;
        int ret;
@@ -755,9 +756,9 @@ static void cycle_netdev(struct net_device *dev)
        if (!netif_running(dev))
                return;
 
-       ret = dev_change_flags(dev, flags & ~IFF_UP);
+       ret = dev_change_flags(dev, flags & ~IFF_UP, extack);
        if (ret >= 0)
-               ret = dev_change_flags(dev, flags);
+               ret = dev_change_flags(dev, flags, extack);
 
        if (ret < 0) {
                netdev_err(dev,
@@ -785,7 +786,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev,
        if (ret < 0)
                goto err;
 
-       cycle_netdev(port_dev);
+       cycle_netdev(port_dev, extack);
 
        return 0;
 
@@ -815,7 +816,7 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
        netdev_upper_dev_unlink(port_dev, dev);
        port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
 
-       cycle_netdev(port_dev);
+       cycle_netdev(port_dev, NULL);
 
        return 0;
 }
index c3e65e7..5209ee9 100644 (file)
@@ -79,9 +79,11 @@ struct vxlan_fdb {
        u8                eth_addr[ETH_ALEN];
        u16               state;        /* see ndm_state */
        __be32            vni;
-       u8                flags;        /* see ndm_flags */
+       u16               flags;        /* see ndm_flags and below */
 };
 
+#define NTF_VXLAN_ADDED_BY_USER 0x100
+
 /* salt for hash table */
 static u32 vxlan_salt __read_mostly;
 
@@ -186,7 +188,7 @@ static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb)
  * and enabled unshareable flags.
  */
 static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family,
-                                         __be16 port, u32 flags)
+                                         __be16 port, u32 flags, int ifindex)
 {
        struct vxlan_sock *vs;
 
@@ -195,7 +197,8 @@ static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family,
        hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {
                if (inet_sk(vs->sock->sk)->inet_sport == port &&
                    vxlan_get_sk_family(vs) == family &&
-                   vs->flags == flags)
+                   vs->flags == flags &&
+                   vs->sock->sk->sk_bound_dev_if == ifindex)
                        return vs;
        }
        return NULL;
@@ -235,7 +238,7 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, int ifindex,
 {
        struct vxlan_sock *vs;
 
-       vs = vxlan_find_sock(net, family, port, flags);
+       vs = vxlan_find_sock(net, family, port, flags, ifindex);
        if (!vs)
                return NULL;
 
@@ -355,6 +358,23 @@ errout:
                rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
 }
 
+static void vxlan_fdb_switchdev_notifier_info(const struct vxlan_dev *vxlan,
+                           const struct vxlan_fdb *fdb,
+                           const struct vxlan_rdst *rd,
+                           struct switchdev_notifier_vxlan_fdb_info *fdb_info)
+{
+       fdb_info->info.dev = vxlan->dev;
+       fdb_info->info.extack = NULL;
+       fdb_info->remote_ip = rd->remote_ip;
+       fdb_info->remote_port = rd->remote_port;
+       fdb_info->remote_vni = rd->remote_vni;
+       fdb_info->remote_ifindex = rd->remote_ifindex;
+       memcpy(fdb_info->eth_addr, fdb->eth_addr, ETH_ALEN);
+       fdb_info->vni = fdb->vni;
+       fdb_info->offloaded = rd->offloaded;
+       fdb_info->added_by_user = fdb->flags & NTF_VXLAN_ADDED_BY_USER;
+}
+
 static void vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan,
                                               struct vxlan_fdb *fdb,
                                               struct vxlan_rdst *rd,
@@ -368,31 +388,25 @@ static void vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan,
 
        notifier_type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE
                               : SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE;
-
-       info = (struct switchdev_notifier_vxlan_fdb_info){
-               .remote_ip = rd->remote_ip,
-               .remote_port = rd->remote_port,
-               .remote_vni = rd->remote_vni,
-               .remote_ifindex = rd->remote_ifindex,
-               .vni = fdb->vni,
-               .offloaded = rd->offloaded,
-       };
-       memcpy(info.eth_addr, fdb->eth_addr, ETH_ALEN);
-
+       vxlan_fdb_switchdev_notifier_info(vxlan, fdb, rd, &info);
        call_switchdev_notifiers(notifier_type, vxlan->dev,
                                 &info.info);
 }
 
 static void vxlan_fdb_notify(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb,
-                            struct vxlan_rdst *rd, int type)
+                            struct vxlan_rdst *rd, int type, bool swdev_notify)
 {
-       switch (type) {
-       case RTM_NEWNEIGH:
-               vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd, true);
-               break;
-       case RTM_DELNEIGH:
-               vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd, false);
-               break;
+       if (swdev_notify) {
+               switch (type) {
+               case RTM_NEWNEIGH:
+                       vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd,
+                                                          true);
+                       break;
+               case RTM_DELNEIGH:
+                       vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd,
+                                                          false);
+                       break;
+               }
        }
 
        __vxlan_fdb_notify(vxlan, fdb, rd, type);
@@ -409,7 +423,7 @@ static void vxlan_ip_miss(struct net_device *dev, union vxlan_addr *ipa)
                .remote_vni = cpu_to_be32(VXLAN_N_VID),
        };
 
-       vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH);
+       vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH, true);
 }
 
 static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
@@ -421,7 +435,7 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
 
        memcpy(f.eth_addr, eth_addr, ETH_ALEN);
 
-       vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH);
+       vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH, true);
 }
 
 /* Hash Ethernet address */
@@ -531,16 +545,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
        }
 
        rdst = first_remote_rcu(f);
-
-       memset(fdb_info, 0, sizeof(*fdb_info));
-       fdb_info->info.dev = dev;
-       fdb_info->remote_ip = rdst->remote_ip;
-       fdb_info->remote_port = rdst->remote_port;
-       fdb_info->remote_vni = rdst->remote_vni;
-       fdb_info->remote_ifindex = rdst->remote_ifindex;
-       fdb_info->vni = vni;
-       fdb_info->offloaded = rdst->offloaded;
-       ether_addr_copy(fdb_info->eth_addr, mac);
+       vxlan_fdb_switchdev_notifier_info(vxlan, f, rdst, fdb_info);
 
 out:
        rcu_read_unlock();
@@ -548,6 +553,75 @@ out:
 }
 EXPORT_SYMBOL_GPL(vxlan_fdb_find_uc);
 
+static int vxlan_fdb_notify_one(struct notifier_block *nb,
+                               const struct vxlan_dev *vxlan,
+                               const struct vxlan_fdb *f,
+                               const struct vxlan_rdst *rdst)
+{
+       struct switchdev_notifier_vxlan_fdb_info fdb_info;
+       int rc;
+
+       vxlan_fdb_switchdev_notifier_info(vxlan, f, rdst, &fdb_info);
+       rc = nb->notifier_call(nb, SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE,
+                              &fdb_info);
+       return notifier_to_errno(rc);
+}
+
+int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
+                    struct notifier_block *nb)
+{
+       struct vxlan_dev *vxlan;
+       struct vxlan_rdst *rdst;
+       struct vxlan_fdb *f;
+       unsigned int h;
+       int rc = 0;
+
+       if (!netif_is_vxlan(dev))
+               return -EINVAL;
+       vxlan = netdev_priv(dev);
+
+       spin_lock_bh(&vxlan->hash_lock);
+       for (h = 0; h < FDB_HASH_SIZE; ++h) {
+               hlist_for_each_entry(f, &vxlan->fdb_head[h], hlist) {
+                       if (f->vni == vni) {
+                               list_for_each_entry(rdst, &f->remotes, list) {
+                                       rc = vxlan_fdb_notify_one(nb, vxlan,
+                                                                 f, rdst);
+                                       if (rc)
+                                               goto out;
+                               }
+                       }
+               }
+       }
+
+out:
+       spin_unlock_bh(&vxlan->hash_lock);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(vxlan_fdb_replay);
+
+void vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni)
+{
+       struct vxlan_dev *vxlan;
+       struct vxlan_rdst *rdst;
+       struct vxlan_fdb *f;
+       unsigned int h;
+
+       if (!netif_is_vxlan(dev))
+               return;
+       vxlan = netdev_priv(dev);
+
+       spin_lock_bh(&vxlan->hash_lock);
+       for (h = 0; h < FDB_HASH_SIZE; ++h) {
+               hlist_for_each_entry(f, &vxlan->fdb_head[h], hlist)
+                       if (f->vni == vni)
+                               list_for_each_entry(rdst, &f->remotes, list)
+                                       rdst->offloaded = false;
+       }
+       spin_unlock_bh(&vxlan->hash_lock);
+}
+EXPORT_SYMBOL_GPL(vxlan_fdb_clear_offload);
+
 /* Replace destination of unicast mac */
 static int vxlan_fdb_replace(struct vxlan_fdb *f,
                             union vxlan_addr *ip, __be16 port, __be32 vni,
@@ -568,6 +642,7 @@ static int vxlan_fdb_replace(struct vxlan_fdb *f,
        rd->remote_port = port;
        rd->remote_vni = vni;
        rd->remote_ifindex = ifindex;
+       rd->offloaded = false;
        return 1;
 }
 
@@ -700,7 +775,7 @@ static int vxlan_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
 
 static struct vxlan_fdb *vxlan_fdb_alloc(struct vxlan_dev *vxlan,
                                         const u8 *mac, __u16 state,
-                                        __be32 src_vni, __u8 ndm_flags)
+                                        __be32 src_vni, __u16 ndm_flags)
 {
        struct vxlan_fdb *f;
 
@@ -720,7 +795,7 @@ static struct vxlan_fdb *vxlan_fdb_alloc(struct vxlan_dev *vxlan,
 static int vxlan_fdb_create(struct vxlan_dev *vxlan,
                            const u8 *mac, union vxlan_addr *ip,
                            __u16 state, __be16 port, __be32 src_vni,
-                           __be32 vni, __u32 ifindex, __u8 ndm_flags,
+                           __be32 vni, __u32 ifindex, __u16 ndm_flags,
                            struct vxlan_fdb **fdb)
 {
        struct vxlan_rdst *rd = NULL;
@@ -756,9 +831,10 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
                            const u8 *mac, union vxlan_addr *ip,
                            __u16 state, __u16 flags,
                            __be16 port, __be32 src_vni, __be32 vni,
-                           __u32 ifindex, __u8 ndm_flags)
+                           __u32 ifindex, __u16 ndm_flags,
+                           bool swdev_notify)
 {
-       __u8 fdb_flags = (ndm_flags & ~NTF_USE);
+       __u16 fdb_flags = (ndm_flags & ~NTF_USE);
        struct vxlan_rdst *rd = NULL;
        struct vxlan_fdb *f;
        int notify = 0;
@@ -771,16 +847,24 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
                                   "lost race to create %pM\n", mac);
                        return -EEXIST;
                }
-               if (f->state != state) {
-                       f->state = state;
-                       f->updated = jiffies;
-                       notify = 1;
-               }
-               if (f->flags != fdb_flags) {
-                       f->flags = fdb_flags;
-                       f->updated = jiffies;
-                       notify = 1;
+
+               /* Do not allow an externally learned entry to take over an
+                * entry added by the user.
+                */
+               if (!(fdb_flags & NTF_EXT_LEARNED) ||
+                   !(f->flags & NTF_VXLAN_ADDED_BY_USER)) {
+                       if (f->state != state) {
+                               f->state = state;
+                               f->updated = jiffies;
+                               notify = 1;
+                       }
+                       if (f->flags != fdb_flags) {
+                               f->flags = fdb_flags;
+                               f->updated = jiffies;
+                               notify = 1;
+                       }
                }
+
                if ((flags & NLM_F_REPLACE)) {
                        /* Only change unicasts */
                        if (!(is_multicast_ether_addr(f->eth_addr) ||
@@ -822,7 +906,7 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
        if (notify) {
                if (rd == NULL)
                        rd = first_remote_rtnl(f);
-               vxlan_fdb_notify(vxlan, f, rd, RTM_NEWNEIGH);
+               vxlan_fdb_notify(vxlan, f, rd, RTM_NEWNEIGH, swdev_notify);
        }
 
        return 0;
@@ -841,7 +925,7 @@ static void vxlan_fdb_free(struct rcu_head *head)
 }
 
 static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
-                             bool do_notify)
+                             bool do_notify, bool swdev_notify)
 {
        struct vxlan_rdst *rd;
 
@@ -851,7 +935,8 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
        --vxlan->addrcnt;
        if (do_notify)
                list_for_each_entry(rd, &f->remotes, list)
-                       vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH);
+                       vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH,
+                                        swdev_notify);
 
        hlist_del_rcu(&f->hlist);
        call_rcu(&f->rcu, vxlan_fdb_free);
@@ -866,10 +951,10 @@ static void vxlan_dst_free(struct rcu_head *head)
 }
 
 static void vxlan_fdb_dst_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
-                                 struct vxlan_rdst *rd)
+                                 struct vxlan_rdst *rd, bool swdev_notify)
 {
        list_del_rcu(&rd->list);
-       vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH);
+       vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH, swdev_notify);
        call_rcu(&rd->rcu, vxlan_dst_free);
 }
 
@@ -968,7 +1053,9 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 
        spin_lock_bh(&vxlan->hash_lock);
        err = vxlan_fdb_update(vxlan, addr, &ip, ndm->ndm_state, flags,
-                              port, src_vni, vni, ifindex, ndm->ndm_flags);
+                              port, src_vni, vni, ifindex,
+                              ndm->ndm_flags | NTF_VXLAN_ADDED_BY_USER,
+                              true);
        spin_unlock_bh(&vxlan->hash_lock);
 
        return err;
@@ -977,7 +1064,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 static int __vxlan_fdb_delete(struct vxlan_dev *vxlan,
                              const unsigned char *addr, union vxlan_addr ip,
                              __be16 port, __be32 src_vni, __be32 vni,
-                             u32 ifindex, u16 vid)
+                             u32 ifindex, bool swdev_notify)
 {
        struct vxlan_fdb *f;
        struct vxlan_rdst *rd = NULL;
@@ -997,11 +1084,11 @@ static int __vxlan_fdb_delete(struct vxlan_dev *vxlan,
         * otherwise destroy the fdb entry
         */
        if (rd && !list_is_singular(&f->remotes)) {
-               vxlan_fdb_dst_destroy(vxlan, f, rd);
+               vxlan_fdb_dst_destroy(vxlan, f, rd, swdev_notify);
                goto out;
        }
 
-       vxlan_fdb_destroy(vxlan, f, true);
+       vxlan_fdb_destroy(vxlan, f, true, swdev_notify);
 
 out:
        return 0;
@@ -1025,7 +1112,7 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 
        spin_lock_bh(&vxlan->hash_lock);
        err = __vxlan_fdb_delete(vxlan, addr, ip, port, src_vni, vni, ifindex,
-                                vid);
+                                true);
        spin_unlock_bh(&vxlan->hash_lock);
 
        return err;
@@ -1066,6 +1153,39 @@ out:
        return err;
 }
 
+static int vxlan_fdb_get(struct sk_buff *skb,
+                        struct nlattr *tb[],
+                        struct net_device *dev,
+                        const unsigned char *addr,
+                        u16 vid, u32 portid, u32 seq,
+                        struct netlink_ext_ack *extack)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_fdb *f;
+       __be32 vni;
+       int err;
+
+       if (tb[NDA_VNI])
+               vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));
+       else
+               vni = vxlan->default_dst.remote_vni;
+
+       rcu_read_lock();
+
+       f = __vxlan_find_mac(vxlan, addr, vni);
+       if (!f) {
+               NL_SET_ERR_MSG(extack, "Fdb entry not found");
+               err = -ENOENT;
+               goto errout;
+       }
+
+       err = vxlan_fdb_info(skb, vxlan, f, portid, seq,
+                            RTM_NEWNEIGH, 0, first_remote_rcu(f));
+errout:
+       rcu_read_unlock();
+       return err;
+}
+
 /* Watch incoming packets to learn mapping between Ethernet address
  * and Tunnel endpoint.
  * Return true if packet is bogus and should be dropped.
@@ -1103,7 +1223,7 @@ static bool vxlan_snoop(struct net_device *dev,
 
                rdst->remote_ip = *src_ip;
                f->updated = jiffies;
-               vxlan_fdb_notify(vxlan, f, rdst, RTM_NEWNEIGH);
+               vxlan_fdb_notify(vxlan, f, rdst, RTM_NEWNEIGH, true);
        } else {
                /* learned new entry */
                spin_lock(&vxlan->hash_lock);
@@ -1116,7 +1236,7 @@ static bool vxlan_snoop(struct net_device *dev,
                                         vxlan->cfg.dst_port,
                                         vni,
                                         vxlan->default_dst.remote_vni,
-                                        ifindex, NTF_SELF);
+                                        ifindex, NTF_SELF, true);
                spin_unlock(&vxlan->hash_lock);
        }
 
@@ -2268,6 +2388,9 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                struct rtable *rt;
                __be16 df = 0;
 
+               if (!ifindex)
+                       ifindex = sock4->sock->sk->sk_bound_dev_if;
+
                rt = vxlan_get_route(vxlan, dev, sock4, skb, ifindex, tos,
                                     dst->sin.sin_addr.s_addr,
                                     &local_ip.sin.sin_addr.s_addr,
@@ -2317,6 +2440,9 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
        } else {
                struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock);
 
+               if (!ifindex)
+                       ifindex = sock6->sock->sk->sk_bound_dev_if;
+
                ndst = vxlan6_get_route(vxlan, dev, sock6, skb, ifindex, tos,
                                        label, &dst->sin6.sin6_addr,
                                        &local_ip.sin6.sin6_addr,
@@ -2500,7 +2626,7 @@ static void vxlan_cleanup(struct timer_list *t)
                                           "garbage collect %pM\n",
                                           f->eth_addr);
                                f->state = NUD_STALE;
-                               vxlan_fdb_destroy(vxlan, f, true);
+                               vxlan_fdb_destroy(vxlan, f, true, true);
                        } else if (time_before(timeout, next_timer))
                                next_timer = timeout;
                }
@@ -2551,7 +2677,7 @@ static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan, __be32 vni)
        spin_lock_bh(&vxlan->hash_lock);
        f = __vxlan_find_mac(vxlan, all_zeros_mac, vni);
        if (f)
-               vxlan_fdb_destroy(vxlan, f, true);
+               vxlan_fdb_destroy(vxlan, f, true, true);
        spin_unlock_bh(&vxlan->hash_lock);
 }
 
@@ -2605,7 +2731,7 @@ static void vxlan_flush(struct vxlan_dev *vxlan, bool do_all)
                                continue;
                        /* the all_zeros_mac entry is deleted at vxlan_uninit */
                        if (!is_zero_ether_addr(f->eth_addr))
-                               vxlan_fdb_destroy(vxlan, f, true);
+                               vxlan_fdb_destroy(vxlan, f, true, true);
                }
        }
        spin_unlock_bh(&vxlan->hash_lock);
@@ -2713,6 +2839,7 @@ static const struct net_device_ops vxlan_netdev_ether_ops = {
        .ndo_fdb_add            = vxlan_fdb_add,
        .ndo_fdb_del            = vxlan_fdb_delete,
        .ndo_fdb_dump           = vxlan_fdb_dump,
+       .ndo_fdb_get            = vxlan_fdb_get,
        .ndo_fill_metadata_dst  = vxlan_fill_metadata_dst,
 };
 
@@ -2931,7 +3058,7 @@ static const struct ethtool_ops vxlan_ethtool_ops = {
 };
 
 static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
-                                       __be16 port, u32 flags)
+                                       __be16 port, u32 flags, int ifindex)
 {
        struct socket *sock;
        struct udp_port_cfg udp_conf;
@@ -2949,6 +3076,7 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
        }
 
        udp_conf.local_udp_port = port;
+       udp_conf.bind_ifindex = ifindex;
 
        /* Open UDP socket */
        err = udp_sock_create(net, &udp_conf, &sock);
@@ -2960,7 +3088,8 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
 
 /* Create new listen socket if needed */
 static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
-                                             __be16 port, u32 flags)
+                                             __be16 port, u32 flags,
+                                             int ifindex)
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_sock *vs;
@@ -2975,7 +3104,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
        for (h = 0; h < VNI_HASH_SIZE; ++h)
                INIT_HLIST_HEAD(&vs->vni_list[h]);
 
-       sock = vxlan_create_sock(net, ipv6, port, flags);
+       sock = vxlan_create_sock(net, ipv6, port, flags, ifindex);
        if (IS_ERR(sock)) {
                kfree(vs);
                return ERR_CAST(sock);
@@ -3013,11 +3142,17 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
        struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
        struct vxlan_sock *vs = NULL;
        struct vxlan_dev_node *node;
+       int l3mdev_index = 0;
+
+       if (vxlan->cfg.remote_ifindex)
+               l3mdev_index = l3mdev_master_upper_ifindex_by_index(
+                       vxlan->net, vxlan->cfg.remote_ifindex);
 
        if (!vxlan->cfg.no_share) {
                spin_lock(&vn->sock_lock);
                vs = vxlan_find_sock(vxlan->net, ipv6 ? AF_INET6 : AF_INET,
-                                    vxlan->cfg.dst_port, vxlan->cfg.flags);
+                                    vxlan->cfg.dst_port, vxlan->cfg.flags,
+                                    l3mdev_index);
                if (vs && !refcount_inc_not_zero(&vs->refcnt)) {
                        spin_unlock(&vn->sock_lock);
                        return -EBUSY;
@@ -3026,7 +3161,8 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
        }
        if (!vs)
                vs = vxlan_socket_create(vxlan->net, ipv6,
-                                        vxlan->cfg.dst_port, vxlan->cfg.flags);
+                                        vxlan->cfg.dst_port, vxlan->cfg.flags,
+                                        l3mdev_index);
        if (IS_ERR(vs))
                return PTR_ERR(vs);
 #if IS_ENABLED(CONFIG_IPV6)
@@ -3309,6 +3445,7 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_fdb *f = NULL;
+       bool unregister = false;
        int err;
 
        err = vxlan_dev_configure(net, dev, conf, false, extack);
@@ -3334,22 +3471,29 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,
        err = register_netdevice(dev);
        if (err)
                goto errout;
+       unregister = true;
 
        err = rtnl_configure_link(dev, NULL);
-       if (err) {
-               unregister_netdevice(dev);
+       if (err)
                goto errout;
-       }
 
        /* notify default fdb entry */
        if (f)
-               vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_NEWNEIGH);
+               vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_NEWNEIGH,
+                                true);
 
        list_add(&vxlan->next, &vn->vxlan_list);
        return 0;
+
 errout:
+       /* unregister_netdevice() destroys the default FDB entry with deletion
+        * notification. But the addition notification was not sent yet, so
+        * destroy the entry by hand here.
+        */
        if (f)
-               vxlan_fdb_destroy(vxlan, f, false);
+               vxlan_fdb_destroy(vxlan, f, false, false);
+       if (unregister)
+               unregister_netdevice(dev);
        return err;
 }
 
@@ -3437,11 +3581,8 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],
                conf->flags |= VXLAN_F_LEARN;
        }
 
-       if (data[IFLA_VXLAN_AGEING]) {
-               if (changelink)
-                       return -EOPNOTSUPP;
+       if (data[IFLA_VXLAN_AGEING])
                conf->age_interval = nla_get_u32(data[IFLA_VXLAN_AGEING]);
-       }
 
        if (data[IFLA_VXLAN_PROXY]) {
                if (changelink)
@@ -3586,9 +3727,9 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_rdst *dst = &vxlan->default_dst;
+       unsigned long old_age_interval;
        struct vxlan_rdst old_dst;
        struct vxlan_config conf;
-       struct vxlan_fdb *f = NULL;
        int err;
 
        err = vxlan_nl2conf(tb, data,
@@ -3596,12 +3737,16 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
        if (err)
                return err;
 
+       old_age_interval = vxlan->cfg.age_interval;
        memcpy(&old_dst, dst, sizeof(struct vxlan_rdst));
 
        err = vxlan_dev_configure(vxlan->net, dev, &conf, true, extack);
        if (err)
                return err;
 
+       if (old_age_interval != vxlan->cfg.age_interval)
+               mod_timer(&vxlan->age_timer, jiffies);
+
        /* handle default dst entry */
        if (!vxlan_addr_equal(&dst->remote_ip, &old_dst.remote_ip)) {
                spin_lock_bh(&vxlan->hash_lock);
@@ -3611,22 +3756,23 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
                                           vxlan->cfg.dst_port,
                                           old_dst.remote_vni,
                                           old_dst.remote_vni,
-                                          old_dst.remote_ifindex, 0);
+                                          old_dst.remote_ifindex,
+                                          true);
 
                if (!vxlan_addr_any(&dst->remote_ip)) {
-                       err = vxlan_fdb_create(vxlan, all_zeros_mac,
+                       err = vxlan_fdb_update(vxlan, all_zeros_mac,
                                               &dst->remote_ip,
                                               NUD_REACHABLE | NUD_PERMANENT,
+                                              NLM_F_APPEND | NLM_F_CREATE,
                                               vxlan->cfg.dst_port,
                                               dst->remote_vni,
                                               dst->remote_vni,
                                               dst->remote_ifindex,
-                                              NTF_SELF, &f);
+                                              NTF_SELF, true);
                        if (err) {
                                spin_unlock_bh(&vxlan->hash_lock);
                                return err;
                        }
-                       vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_NEWNEIGH);
                }
                spin_unlock_bh(&vxlan->hash_lock);
        }
@@ -3900,18 +4046,89 @@ out:
        spin_unlock_bh(&vxlan->hash_lock);
 }
 
+static int
+vxlan_fdb_external_learn_add(struct net_device *dev,
+                            struct switchdev_notifier_vxlan_fdb_info *fdb_info)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       int err;
+
+       spin_lock_bh(&vxlan->hash_lock);
+       err = vxlan_fdb_update(vxlan, fdb_info->eth_addr, &fdb_info->remote_ip,
+                              NUD_REACHABLE,
+                              NLM_F_CREATE | NLM_F_REPLACE,
+                              fdb_info->remote_port,
+                              fdb_info->vni,
+                              fdb_info->remote_vni,
+                              fdb_info->remote_ifindex,
+                              NTF_USE | NTF_SELF | NTF_EXT_LEARNED,
+                              false);
+       spin_unlock_bh(&vxlan->hash_lock);
+
+       return err;
+}
+
+static int
+vxlan_fdb_external_learn_del(struct net_device *dev,
+                            struct switchdev_notifier_vxlan_fdb_info *fdb_info)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_fdb *f;
+       int err = 0;
+
+       spin_lock_bh(&vxlan->hash_lock);
+
+       f = vxlan_find_mac(vxlan, fdb_info->eth_addr, fdb_info->vni);
+       if (!f)
+               err = -ENOENT;
+       else if (f->flags & NTF_EXT_LEARNED)
+               err = __vxlan_fdb_delete(vxlan, fdb_info->eth_addr,
+                                        fdb_info->remote_ip,
+                                        fdb_info->remote_port,
+                                        fdb_info->vni,
+                                        fdb_info->remote_vni,
+                                        fdb_info->remote_ifindex,
+                                        false);
+
+       spin_unlock_bh(&vxlan->hash_lock);
+
+       return err;
+}
+
 static int vxlan_switchdev_event(struct notifier_block *unused,
                                 unsigned long event, void *ptr)
 {
        struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+       struct switchdev_notifier_vxlan_fdb_info *fdb_info;
+       int err = 0;
 
        switch (event) {
        case SWITCHDEV_VXLAN_FDB_OFFLOADED:
                vxlan_fdb_offloaded_set(dev, ptr);
                break;
+       case SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE:
+               fdb_info = ptr;
+               err = vxlan_fdb_external_learn_add(dev, fdb_info);
+               if (err) {
+                       err = notifier_from_errno(err);
+                       break;
+               }
+               fdb_info->offloaded = true;
+               vxlan_fdb_offloaded_set(dev, fdb_info);
+               break;
+       case SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE:
+               fdb_info = ptr;
+               err = vxlan_fdb_external_learn_del(dev, fdb_info);
+               if (err) {
+                       err = notifier_from_errno(err);
+                       break;
+               }
+               fdb_info->offloaded = false;
+               vxlan_fdb_offloaded_set(dev, fdb_info);
+               break;
        }
 
-       return 0;
+       return err;
 }
 
 static struct notifier_block vxlan_switchdev_notifier_block __read_mostly = {
index 166920a..8c456a6 100644 (file)
@@ -114,4 +114,11 @@ config USB_NET_RNDIS_WLAN
 
          If you choose to build a module, it'll be called rndis_wlan.
 
+config VIRT_WIFI
+       tristate "Wifi wrapper for ethernet drivers"
+       depends on CFG80211
+       ---help---
+         This option adds support for ethernet connections to appear as if they
+         are wifi connections through a special rtnetlink device.
+
 endif # WLAN
index 7fc9630..6cfe745 100644 (file)
@@ -27,3 +27,5 @@ obj-$(CONFIG_PCMCIA_WL3501)   += wl3501_cs.o
 obj-$(CONFIG_USB_NET_RNDIS_WLAN)       += rndis_wlan.o
 
 obj-$(CONFIG_MAC80211_HWSIM)   += mac80211_hwsim.o
+
+obj-$(CONFIG_VIRT_WIFI)        += virt_wifi.o
index e1ad6b9..a7fb544 100644 (file)
@@ -47,8 +47,7 @@ config ATH10K_SNOC
        select QCOM_QMI_HELPERS
        ---help---
          This module adds support for integrated WCN3990 chip connected
-         to system NOC(SNOC). Currently work in progress and will not
-         fully work.
+         to system NOC(SNOC).
 
 config ATH10K_DEBUG
        bool "Atheros ath10k debugging"
index da607fe..399b501 100644 (file)
@@ -561,6 +561,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .hw_ops = &wcn3990_ops,
                .decap_align_bytes = 1,
                .num_peers = TARGET_HL_10_TLV_NUM_PEERS,
+               .n_cipher_suites = 8,
                .ast_skid_limit = TARGET_HL_10_TLV_AST_SKID_LIMIT,
                .num_wds_entries = TARGET_HL_10_TLV_NUM_WDS_ENTRIES,
                .target_64bit = true,
@@ -594,6 +595,7 @@ static const char *const ath10k_core_fw_feature_str[] = {
        [ATH10K_FW_FEATURE_NO_PS] = "no-ps",
        [ATH10K_FW_FEATURE_MGMT_TX_BY_REF] = "mgmt-tx-by-reference",
        [ATH10K_FW_FEATURE_NON_BMI] = "non-bmi",
+       [ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL] = "single-chan-info-per-channel",
 };
 
 static unsigned int ath10k_core_get_fw_feature_str(char *buf,
@@ -2183,6 +2185,8 @@ static void ath10k_core_restart(struct work_struct *work)
        if (ret)
                ath10k_warn(ar, "failed to send firmware crash dump via devcoredump: %d",
                            ret);
+
+       complete(&ar->driver_recovery);
 }
 
 static void ath10k_core_set_coverage_class_work(struct work_struct *work)
@@ -2418,6 +2422,28 @@ static int ath10k_core_reset_rx_filter(struct ath10k *ar)
        return 0;
 }
 
+static int ath10k_core_compat_services(struct ath10k *ar)
+{
+       struct ath10k_fw_file *fw_file = &ar->normal_mode_fw.fw_file;
+
+       /* all 10.x firmware versions support thermal throttling but don't
+        * advertise the support via service flags so we have to hardcode
+        * it here
+        */
+       switch (fw_file->wmi_op_version) {
+       case ATH10K_FW_WMI_OP_VERSION_10_1:
+       case ATH10K_FW_WMI_OP_VERSION_10_2:
+       case ATH10K_FW_WMI_OP_VERSION_10_2_4:
+       case ATH10K_FW_WMI_OP_VERSION_10_4:
+               set_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
                      const struct ath10k_fw_components *fw)
 {
@@ -2617,6 +2643,12 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
                goto err_hif_stop;
        }
 
+       status = ath10k_core_compat_services(ar);
+       if (status) {
+               ath10k_err(ar, "compat services failed: %d\n", status);
+               goto err_hif_stop;
+       }
+
        /* Some firmware revisions do not properly set up hardware rx filter
         * registers.
         *
@@ -3046,6 +3078,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
        init_completion(&ar->scan.completed);
        init_completion(&ar->scan.on_channel);
        init_completion(&ar->target_suspend);
+       init_completion(&ar->driver_recovery);
        init_completion(&ar->wow.wakeup_completed);
 
        init_completion(&ar->install_key_done);
index 0424180..46e9c8c 100644 (file)
@@ -474,6 +474,7 @@ struct ath10k_htt_data_stats {
        u64 bw[ATH10K_COUNTER_TYPE_MAX][ATH10K_BW_NUM];
        u64 nss[ATH10K_COUNTER_TYPE_MAX][ATH10K_NSS_NUM];
        u64 gi[ATH10K_COUNTER_TYPE_MAX][ATH10K_GI_NUM];
+       u64 rate_table[ATH10K_COUNTER_TYPE_MAX][ATH10K_RATE_TABLE_NUM];
 };
 
 struct ath10k_htt_tx_stats {
@@ -493,6 +494,7 @@ struct ath10k_sta {
        u32 smps;
        u16 peer_id;
        struct rate_info txrate;
+       struct ieee80211_tx_info tx_info;
 
        struct work_struct update_wk;
        u64 rx_duration;
@@ -760,6 +762,9 @@ enum ath10k_fw_features {
        /* Firmware load is done externally, not by bmi */
        ATH10K_FW_FEATURE_NON_BMI = 19,
 
+       /* Firmware sends only one chan_info event per channel */
+       ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL = 20,
+
        /* keep last */
        ATH10K_FW_FEATURE_COUNT,
 };
@@ -960,6 +965,7 @@ struct ath10k {
        } hif;
 
        struct completion target_suspend;
+       struct completion driver_recovery;
 
        const struct ath10k_hw_regs *regs;
        const struct ath10k_hw_ce_regs *hw_ce_regs;
index 4d28063..eadae2f 100644 (file)
@@ -867,9 +867,105 @@ static const struct ath10k_mem_region qca9984_hw10_mem_regions[] = {
        },
 };
 
+static const struct ath10k_mem_section ipq4019_soc_reg_range[] = {
+       {0x080000, 0x080004},
+       {0x080020, 0x080024},
+       {0x080028, 0x080050},
+       {0x0800d4, 0x0800ec},
+       {0x08010c, 0x080118},
+       {0x080284, 0x080290},
+       {0x0802a8, 0x0802b8},
+       {0x0802dc, 0x08030c},
+       {0x082000, 0x083fff}
+};
+
+static const struct ath10k_mem_region qca4019_hw10_mem_regions[] = {
+       {
+               .type = ATH10K_MEM_REGION_TYPE_DRAM,
+               .start = 0x400000,
+               .len = 0x68000,
+               .name = "DRAM",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_REG,
+               .start = 0xC0000,
+               .len = 0x40000,
+               .name = "SRAM",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_REG,
+               .start = 0x98000,
+               .len = 0x50000,
+               .name = "IRAM",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_IOREG,
+               .start = 0x30000,
+               .len = 0x7000,
+               .name = "APB REG 1",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_IOREG,
+               .start = 0x3f000,
+               .len = 0x3000,
+               .name = "APB REG 2",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_IOREG,
+               .start = 0x43000,
+               .len = 0x3000,
+               .name = "WIFI REG",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_IOREG,
+               .start = 0x4A000,
+               .len = 0x5000,
+               .name = "CE REG",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_REG,
+               .start = 0x080000,
+               .len = 0x083fff - 0x080000,
+               .name = "REG_TOTAL",
+               .section_table = {
+                       .sections = ipq4019_soc_reg_range,
+                       .size = ARRAY_SIZE(ipq4019_soc_reg_range),
+               },
+       },
+};
+
 static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        {
                .hw_id = QCA6174_HW_1_0_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw10_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw10_mem_regions),
@@ -877,6 +973,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA6174_HW_1_1_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw10_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw10_mem_regions),
@@ -884,6 +981,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA6174_HW_1_3_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw10_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw10_mem_regions),
@@ -891,6 +989,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA6174_HW_2_1_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw21_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw21_mem_regions),
@@ -898,6 +997,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA6174_HW_3_0_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw30_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw30_mem_regions),
@@ -905,6 +1005,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA6174_HW_3_2_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw30_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw30_mem_regions),
@@ -912,6 +1013,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA9377_HW_1_1_DEV_VERSION,
+               .hw_rev = ATH10K_HW_QCA9377,
                .region_table = {
                        .regions = qca6174_hw30_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw30_mem_regions),
@@ -919,6 +1021,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA988X_HW_2_0_VERSION,
+               .hw_rev = ATH10K_HW_QCA988X,
                .region_table = {
                        .regions = qca988x_hw20_mem_regions,
                        .size = ARRAY_SIZE(qca988x_hw20_mem_regions),
@@ -926,6 +1029,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA9984_HW_1_0_DEV_VERSION,
+               .hw_rev = ATH10K_HW_QCA9984,
                .region_table = {
                        .regions = qca9984_hw10_mem_regions,
                        .size = ARRAY_SIZE(qca9984_hw10_mem_regions),
@@ -933,6 +1037,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA9888_HW_2_0_DEV_VERSION,
+               .hw_rev = ATH10K_HW_QCA9888,
                .region_table = {
                        .regions = qca9984_hw10_mem_regions,
                        .size = ARRAY_SIZE(qca9984_hw10_mem_regions),
@@ -940,12 +1045,20 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA99X0_HW_2_0_DEV_VERSION,
+               .hw_rev = ATH10K_HW_QCA99X0,
                .region_table = {
                        .regions = qca99x0_hw20_mem_regions,
                        .size = ARRAY_SIZE(qca99x0_hw20_mem_regions),
                },
        },
-
+       {
+               .hw_id = QCA4019_HW_1_0_DEV_VERSION,
+               .hw_rev = ATH10K_HW_QCA4019,
+               .region_table = {
+                       .regions = qca4019_hw10_mem_regions,
+                       .size = ARRAY_SIZE(qca4019_hw10_mem_regions),
+               },
+       },
 };
 
 static u32 ath10k_coredump_get_ramdump_size(struct ath10k *ar)
@@ -987,7 +1100,8 @@ const struct ath10k_hw_mem_layout *ath10k_coredump_get_mem_layout(struct ath10k
                return NULL;
 
        for (i = 0; i < ARRAY_SIZE(hw_mem_layouts); i++) {
-               if (ar->target_version == hw_mem_layouts[i].hw_id)
+               if (ar->target_version == hw_mem_layouts[i].hw_id &&
+                   ar->hw_rev == hw_mem_layouts[i].hw_rev)
                        return &hw_mem_layouts[i];
        }
 
index 3baaf9d..5dac653 100644 (file)
@@ -165,6 +165,7 @@ struct ath10k_mem_region {
  */
 struct ath10k_hw_mem_layout {
        u32 hw_id;
+       u32 hw_rev;
 
        struct {
                const struct ath10k_mem_region *regions;
index 15964b3..02988fc 100644 (file)
@@ -2578,8 +2578,9 @@ int ath10k_debug_register(struct ath10k *ar)
        debugfs_create_file("pktlog_filter", 0644, ar->debug.debugfs_phy, ar,
                            &fops_pktlog_filter);
 
-       debugfs_create_file("quiet_period", 0644, ar->debug.debugfs_phy, ar,
-                           &fops_quiet_period);
+       if (test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
+               debugfs_create_file("quiet_period", 0644, ar->debug.debugfs_phy, ar,
+                                   &fops_quiet_period);
 
        debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_phy, ar,
                            &fops_tpc_stats);
index b09cdc6..4778a45 100644 (file)
@@ -71,7 +71,7 @@ void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, u16 peer_id, u8 tid
        spin_lock_bh(&ar->data_lock);
 
        peer = ath10k_peer_find_by_id(ar, peer_id);
-       if (!peer)
+       if (!peer || !peer->sta)
                goto out;
 
        arsta = (struct ath10k_sta *)peer->sta->drv_priv;
@@ -665,7 +665,7 @@ static ssize_t ath10k_dbg_sta_dump_tx_stats(struct file *file,
                                                       "retry", "ampdu"};
        const char *str[ATH10K_COUNTER_TYPE_MAX] = {"bytes", "packets"};
        int len = 0, i, j, k, retval = 0;
-       const int size = 2 * 4096;
+       const int size = 16 * 4096;
        char *buf;
 
        buf = kzalloc(size, GFP_KERNEL);
@@ -719,6 +719,16 @@ static ssize_t ath10k_dbg_sta_dump_tx_stats(struct file *file,
                                len += scnprintf(buf + len, size - len, "%llu ",
                                                 stats->legacy[j][i]);
                        len += scnprintf(buf + len, size - len, "\n");
+                       len += scnprintf(buf + len, size - len,
+                                        " Rate table %s (1,2 ... Mbps)\n  ",
+                                        str[j]);
+                       for (i = 0; i < ATH10K_RATE_TABLE_NUM; i++) {
+                               len += scnprintf(buf + len, size - len, "%llu ",
+                                                stats->rate_table[j][i]);
+                               if (!((i + 1) % 8))
+                                       len +=
+                                       scnprintf(buf + len, size - len, "\n  ");
+                       }
                }
        }
 
index ffec98f..f42bac2 100644 (file)
@@ -469,6 +469,166 @@ static struct sk_buff *ath10k_htt_rx_pop_paddr(struct ath10k_htt *htt,
        return msdu;
 }
 
+static inline void ath10k_htt_append_frag_list(struct sk_buff *skb_head,
+                                              struct sk_buff *frag_list,
+                                              unsigned int frag_len)
+{
+       skb_shinfo(skb_head)->frag_list = frag_list;
+       skb_head->data_len = frag_len;
+       skb_head->len += skb_head->data_len;
+}
+
+static int ath10k_htt_rx_handle_amsdu_mon_32(struct ath10k_htt *htt,
+                                            struct sk_buff *msdu,
+                                            struct htt_rx_in_ord_msdu_desc **msdu_desc)
+{
+       struct ath10k *ar = htt->ar;
+       u32 paddr;
+       struct sk_buff *frag_buf;
+       struct sk_buff *prev_frag_buf;
+       u8 last_frag;
+       struct htt_rx_in_ord_msdu_desc *ind_desc = *msdu_desc;
+       struct htt_rx_desc *rxd;
+       int amsdu_len = __le16_to_cpu(ind_desc->msdu_len);
+
+       rxd = (void *)msdu->data;
+       trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd));
+
+       skb_put(msdu, sizeof(struct htt_rx_desc));
+       skb_pull(msdu, sizeof(struct htt_rx_desc));
+       skb_put(msdu, min(amsdu_len, HTT_RX_MSDU_SIZE));
+       amsdu_len -= msdu->len;
+
+       last_frag = ind_desc->reserved;
+       if (last_frag) {
+               if (amsdu_len) {
+                       ath10k_warn(ar, "invalid amsdu len %u, left %d",
+                                   __le16_to_cpu(ind_desc->msdu_len),
+                                   amsdu_len);
+               }
+               return 0;
+       }
+
+       ind_desc++;
+       paddr = __le32_to_cpu(ind_desc->msdu_paddr);
+       frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
+       if (!frag_buf) {
+               ath10k_warn(ar, "failed to pop frag-1 paddr: 0x%x", paddr);
+               return -ENOENT;
+       }
+
+       skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
+       ath10k_htt_append_frag_list(msdu, frag_buf, amsdu_len);
+
+       amsdu_len -= frag_buf->len;
+       prev_frag_buf = frag_buf;
+       last_frag = ind_desc->reserved;
+       while (!last_frag) {
+               ind_desc++;
+               paddr = __le32_to_cpu(ind_desc->msdu_paddr);
+               frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
+               if (!frag_buf) {
+                       ath10k_warn(ar, "failed to pop frag-n paddr: 0x%x",
+                                   paddr);
+                       prev_frag_buf->next = NULL;
+                       return -ENOENT;
+               }
+
+               skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
+               last_frag = ind_desc->reserved;
+               amsdu_len -= frag_buf->len;
+
+               prev_frag_buf->next = frag_buf;
+               prev_frag_buf = frag_buf;
+       }
+
+       if (amsdu_len) {
+               ath10k_warn(ar, "invalid amsdu len %u, left %d",
+                           __le16_to_cpu(ind_desc->msdu_len), amsdu_len);
+       }
+
+       *msdu_desc = ind_desc;
+
+       prev_frag_buf->next = NULL;
+       return 0;
+}
+
+static int
+ath10k_htt_rx_handle_amsdu_mon_64(struct ath10k_htt *htt,
+                                 struct sk_buff *msdu,
+                                 struct htt_rx_in_ord_msdu_desc_ext **msdu_desc)
+{
+       struct ath10k *ar = htt->ar;
+       u64 paddr;
+       struct sk_buff *frag_buf;
+       struct sk_buff *prev_frag_buf;
+       u8 last_frag;
+       struct htt_rx_in_ord_msdu_desc_ext *ind_desc = *msdu_desc;
+       struct htt_rx_desc *rxd;
+       int amsdu_len = __le16_to_cpu(ind_desc->msdu_len);
+
+       rxd = (void *)msdu->data;
+       trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd));
+
+       skb_put(msdu, sizeof(struct htt_rx_desc));
+       skb_pull(msdu, sizeof(struct htt_rx_desc));
+       skb_put(msdu, min(amsdu_len, HTT_RX_MSDU_SIZE));
+       amsdu_len -= msdu->len;
+
+       last_frag = ind_desc->reserved;
+       if (last_frag) {
+               if (amsdu_len) {
+                       ath10k_warn(ar, "invalid amsdu len %u, left %d",
+                                   __le16_to_cpu(ind_desc->msdu_len),
+                                   amsdu_len);
+               }
+               return 0;
+       }
+
+       ind_desc++;
+       paddr = __le64_to_cpu(ind_desc->msdu_paddr);
+       frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
+       if (!frag_buf) {
+               ath10k_warn(ar, "failed to pop frag-1 paddr: 0x%llx", paddr);
+               return -ENOENT;
+       }
+
+       skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
+       ath10k_htt_append_frag_list(msdu, frag_buf, amsdu_len);
+
+       amsdu_len -= frag_buf->len;
+       prev_frag_buf = frag_buf;
+       last_frag = ind_desc->reserved;
+       while (!last_frag) {
+               ind_desc++;
+               paddr = __le64_to_cpu(ind_desc->msdu_paddr);
+               frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
+               if (!frag_buf) {
+                       ath10k_warn(ar, "failed to pop frag-n paddr: 0x%llx",
+                                   paddr);
+                       prev_frag_buf->next = NULL;
+                       return -ENOENT;
+               }
+
+               skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
+               last_frag = ind_desc->reserved;
+               amsdu_len -= frag_buf->len;
+
+               prev_frag_buf->next = frag_buf;
+               prev_frag_buf = frag_buf;
+       }
+
+       if (amsdu_len) {
+               ath10k_warn(ar, "invalid amsdu len %u, left %d",
+                           __le16_to_cpu(ind_desc->msdu_len), amsdu_len);
+       }
+
+       *msdu_desc = ind_desc;
+
+       prev_frag_buf->next = NULL;
+       return 0;
+}
+
 static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt,
                                          struct htt_rx_in_ord_ind *ev,
                                          struct sk_buff_head *list)
@@ -477,7 +637,7 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt,
        struct htt_rx_in_ord_msdu_desc *msdu_desc = ev->msdu_descs32;
        struct htt_rx_desc *rxd;
        struct sk_buff *msdu;
-       int msdu_count;
+       int msdu_count, ret;
        bool is_offload;
        u32 paddr;
 
@@ -495,6 +655,18 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt,
                        return -ENOENT;
                }
 
+               if (!is_offload && ar->monitor_arvif) {
+                       ret = ath10k_htt_rx_handle_amsdu_mon_32(htt, msdu,
+                                                               &msdu_desc);
+                       if (ret) {
+                               __skb_queue_purge(list);
+                               return ret;
+                       }
+                       __skb_queue_tail(list, msdu);
+                       msdu_desc++;
+                       continue;
+               }
+
                __skb_queue_tail(list, msdu);
 
                if (!is_offload) {
@@ -527,7 +699,7 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt,
        struct htt_rx_in_ord_msdu_desc_ext *msdu_desc = ev->msdu_descs64;
        struct htt_rx_desc *rxd;
        struct sk_buff *msdu;
-       int msdu_count;
+       int msdu_count, ret;
        bool is_offload;
        u64 paddr;
 
@@ -544,6 +716,18 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt,
                        return -ENOENT;
                }
 
+               if (!is_offload && ar->monitor_arvif) {
+                       ret = ath10k_htt_rx_handle_amsdu_mon_64(htt, msdu,
+                                                               &msdu_desc);
+                       if (ret) {
+                               __skb_queue_purge(list);
+                               return ret;
+                       }
+                       __skb_queue_tail(list, msdu);
+                       msdu_desc++;
+                       continue;
+               }
+
                __skb_queue_tail(list, msdu);
 
                if (!is_offload) {
@@ -1159,7 +1343,8 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
                                        struct sk_buff *msdu,
                                        struct ieee80211_rx_status *status,
                                        enum htt_rx_mpdu_encrypt_type enctype,
-                                       bool is_decrypted)
+                                       bool is_decrypted,
+                                       const u8 first_hdr[64])
 {
        struct ieee80211_hdr *hdr;
        struct htt_rx_desc *rxd;
@@ -1167,6 +1352,9 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
        size_t crypto_len;
        bool is_first;
        bool is_last;
+       bool msdu_limit_err;
+       int bytes_aligned = ar->hw_params.decap_align_bytes;
+       u8 *qos;
 
        rxd = (void *)msdu->data - sizeof(*rxd);
        is_first = !!(rxd->msdu_end.common.info0 &
@@ -1184,16 +1372,45 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
         * [FCS] <-- at end, needs to be trimmed
         */
 
+       /* Some hardwares(QCA99x0 variants) limit number of msdus in a-msdu when
+        * deaggregate, so that unwanted MSDU-deaggregation is avoided for
+        * error packets. If limit exceeds, hw sends all remaining MSDUs as
+        * a single last MSDU with this msdu limit error set.
+        */
+       msdu_limit_err = ath10k_rx_desc_msdu_limit_error(&ar->hw_params, rxd);
+
+       /* If MSDU limit error happens, then don't warn on, the partial raw MSDU
+        * without first MSDU is expected in that case, and handled later here.
+        */
        /* This probably shouldn't happen but warn just in case */
-       if (WARN_ON_ONCE(!is_first))
+       if (WARN_ON_ONCE(!is_first && !msdu_limit_err))
                return;
 
        /* This probably shouldn't happen but warn just in case */
-       if (WARN_ON_ONCE(!(is_first && is_last)))
+       if (WARN_ON_ONCE(!(is_first && is_last) && !msdu_limit_err))
                return;
 
        skb_trim(msdu, msdu->len - FCS_LEN);
 
+       /* Push original 80211 header */
+       if (unlikely(msdu_limit_err)) {
+               hdr = (struct ieee80211_hdr *)first_hdr;
+               hdr_len = ieee80211_hdrlen(hdr->frame_control);
+               crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
+
+               if (ieee80211_is_data_qos(hdr->frame_control)) {
+                       qos = ieee80211_get_qos_ctl(hdr);
+                       qos[0] |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+               }
+
+               if (crypto_len)
+                       memcpy(skb_push(msdu, crypto_len),
+                              (void *)hdr + round_up(hdr_len, bytes_aligned),
+                              crypto_len);
+
+               memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
+       }
+
        /* In most cases this will be true for sniffed frames. It makes sense
         * to deliver them as-is without stripping the crypto param. This is
         * necessary for software based decryption.
@@ -1467,7 +1684,7 @@ static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
        switch (decap) {
        case RX_MSDU_DECAP_RAW:
                ath10k_htt_rx_h_undecap_raw(ar, msdu, status, enctype,
-                                           is_decrypted);
+                                           is_decrypted, first_hdr);
                break;
        case RX_MSDU_DECAP_NATIVE_WIFI:
                ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr,
@@ -2627,7 +2844,7 @@ void ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
                dev_kfree_skb_any(skb);
 }
 
-static inline int ath10k_get_legacy_rate_idx(struct ath10k *ar, u8 rate)
+static inline s8 ath10k_get_legacy_rate_idx(struct ath10k *ar, u8 rate)
 {
        static const u8 legacy_rates[] = {1, 2, 5, 11, 6, 9, 12,
                                          18, 24, 36, 48, 54};
@@ -2646,11 +2863,11 @@ static void
 ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
                                    struct ath10k_sta *arsta,
                                    struct ath10k_per_peer_tx_stats *pstats,
-                                   u8 legacy_rate_idx)
+                                   s8 legacy_rate_idx)
 {
        struct rate_info *txrate = &arsta->txrate;
        struct ath10k_htt_tx_stats *tx_stats;
-       int ht_idx, gi, mcs, bw, nss;
+       int idx, ht_idx, gi, mcs, bw, nss;
 
        if (!arsta->tx_stats)
                return;
@@ -2661,6 +2878,8 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
        mcs = txrate->mcs;
        bw = txrate->bw;
        nss = txrate->nss;
+       idx = mcs * 8 + 8 * 10 * nss;
+       idx += bw * 2 + gi;
 
 #define STATS_OP_FMT(name) tx_stats->stats[ATH10K_STATS_TYPE_##name]
 
@@ -2709,12 +2928,16 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
                        pstats->succ_bytes + pstats->retry_bytes;
                STATS_OP_FMT(AMPDU).gi[0][gi] +=
                        pstats->succ_bytes + pstats->retry_bytes;
+               STATS_OP_FMT(AMPDU).rate_table[0][idx] +=
+                       pstats->succ_bytes + pstats->retry_bytes;
                STATS_OP_FMT(AMPDU).bw[1][bw] +=
                        pstats->succ_pkts + pstats->retry_pkts;
                STATS_OP_FMT(AMPDU).nss[1][nss] +=
                        pstats->succ_pkts + pstats->retry_pkts;
                STATS_OP_FMT(AMPDU).gi[1][gi] +=
                        pstats->succ_pkts + pstats->retry_pkts;
+               STATS_OP_FMT(AMPDU).rate_table[1][idx] +=
+                       pstats->succ_pkts + pstats->retry_pkts;
        } else {
                tx_stats->ack_fails +=
                                ATH10K_HW_BA_FAIL(pstats->flags);
@@ -2743,6 +2966,15 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
        STATS_OP_FMT(RETRY).bw[1][bw] += pstats->retry_pkts;
        STATS_OP_FMT(RETRY).nss[1][nss] += pstats->retry_pkts;
        STATS_OP_FMT(RETRY).gi[1][gi] += pstats->retry_pkts;
+
+       if (txrate->flags >= RATE_INFO_FLAGS_MCS) {
+               STATS_OP_FMT(SUCC).rate_table[0][idx] += pstats->succ_bytes;
+               STATS_OP_FMT(SUCC).rate_table[1][idx] += pstats->succ_pkts;
+               STATS_OP_FMT(FAIL).rate_table[0][idx] += pstats->failed_bytes;
+               STATS_OP_FMT(FAIL).rate_table[1][idx] += pstats->failed_pkts;
+               STATS_OP_FMT(RETRY).rate_table[0][idx] += pstats->retry_bytes;
+               STATS_OP_FMT(RETRY).rate_table[1][idx] += pstats->retry_pkts;
+       }
 }
 
 static void
@@ -2751,8 +2983,10 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
                                struct ath10k_per_peer_tx_stats *peer_stats)
 {
        struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+       struct ieee80211_chanctx_conf *conf = NULL;
        u8 rate = 0, sgi;
        s8 rate_idx = 0;
+       bool skip_auto_rate;
        struct rate_info txrate;
 
        lockdep_assert_held(&ar->data_lock);
@@ -2762,6 +2996,13 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
        txrate.nss = ATH10K_HW_NSS(peer_stats->ratecode);
        txrate.mcs = ATH10K_HW_MCS_RATE(peer_stats->ratecode);
        sgi = ATH10K_HW_GI(peer_stats->flags);
+       skip_auto_rate = ATH10K_FW_SKIPPED_RATE_CTRL(peer_stats->flags);
+
+       /* Firmware's rate control skips broadcast/management frames,
+        * if host has configure fixed rates and in some other special cases.
+        */
+       if (skip_auto_rate)
+               return;
 
        if (txrate.flags == WMI_RATE_PREAMBLE_VHT && txrate.mcs > 9) {
                ath10k_warn(ar, "Invalid VHT mcs %hhd peer stats",  txrate.mcs);
@@ -2776,7 +3017,7 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
        }
 
        memset(&arsta->txrate, 0, sizeof(arsta->txrate));
-
+       memset(&arsta->tx_info.status, 0, sizeof(arsta->tx_info.status));
        if (txrate.flags == WMI_RATE_PREAMBLE_CCK ||
            txrate.flags == WMI_RATE_PREAMBLE_OFDM) {
                rate = ATH10K_HW_LEGACY_RATE(peer_stats->ratecode);
@@ -2795,11 +3036,59 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
                arsta->txrate.mcs = txrate.mcs;
        }
 
-       if (sgi)
-               arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+       switch (txrate.flags) {
+       case WMI_RATE_PREAMBLE_OFDM:
+               if (arsta->arvif && arsta->arvif->vif)
+                       conf = rcu_dereference(arsta->arvif->vif->chanctx_conf);
+               if (conf && conf->def.chan->band == NL80211_BAND_5GHZ)
+                       arsta->tx_info.status.rates[0].idx = rate_idx - 4;
+               break;
+       case WMI_RATE_PREAMBLE_CCK:
+               arsta->tx_info.status.rates[0].idx = rate_idx;
+               if (sgi)
+                       arsta->tx_info.status.rates[0].flags |=
+                               (IEEE80211_TX_RC_USE_SHORT_PREAMBLE |
+                                IEEE80211_TX_RC_SHORT_GI);
+               break;
+       case WMI_RATE_PREAMBLE_HT:
+               arsta->tx_info.status.rates[0].idx =
+                               txrate.mcs + ((txrate.nss - 1) * 8);
+               if (sgi)
+                       arsta->tx_info.status.rates[0].flags |=
+                                       IEEE80211_TX_RC_SHORT_GI;
+               arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_MCS;
+               break;
+       case WMI_RATE_PREAMBLE_VHT:
+               ieee80211_rate_set_vht(&arsta->tx_info.status.rates[0],
+                                      txrate.mcs, txrate.nss);
+               if (sgi)
+                       arsta->tx_info.status.rates[0].flags |=
+                                               IEEE80211_TX_RC_SHORT_GI;
+               arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_VHT_MCS;
+               break;
+       }
 
        arsta->txrate.nss = txrate.nss;
        arsta->txrate.bw = ath10k_bw_to_mac80211_bw(txrate.bw);
+       if (sgi)
+               arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+
+       switch (arsta->txrate.bw) {
+       case RATE_INFO_BW_40:
+               arsta->tx_info.status.rates[0].flags |=
+                               IEEE80211_TX_RC_40_MHZ_WIDTH;
+               break;
+       case RATE_INFO_BW_80:
+               arsta->tx_info.status.rates[0].flags |=
+                               IEEE80211_TX_RC_80_MHZ_WIDTH;
+               break;
+       }
+
+       if (peer_stats->succ_pkts) {
+               arsta->tx_info.flags = IEEE80211_TX_STAT_ACK;
+               arsta->tx_info.status.rates[0].count = 1;
+               ieee80211_tx_rate_update(ar->hw, sta, &arsta->tx_info);
+       }
 
        if (ath10k_debug_is_extd_tx_stats_enabled(ar))
                ath10k_accumulate_per_peer_tx_stats(ar, arsta, peer_stats,
@@ -2832,7 +3121,7 @@ static void ath10k_htt_fetch_peer_stats(struct ath10k *ar,
        rcu_read_lock();
        spin_lock_bh(&ar->data_lock);
        peer = ath10k_peer_find_by_id(ar, peer_id);
-       if (!peer) {
+       if (!peer || !peer->sta) {
                ath10k_warn(ar, "Invalid peer id %d peer stats buffer\n",
                            peer_id);
                goto out;
@@ -2885,7 +3174,7 @@ static void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data)
        rcu_read_lock();
        spin_lock_bh(&ar->data_lock);
        peer = ath10k_peer_find_by_id(ar, peer_id);
-       if (!peer) {
+       if (!peer || !peer->sta) {
                ath10k_warn(ar, "Invalid peer id %d in peer stats buffer\n",
                            peer_id);
                goto out;
index af8ae81..61ecf93 100644 (file)
@@ -1119,8 +1119,15 @@ static int ath10k_qca99x0_rx_desc_get_l3_pad_bytes(struct htt_rx_desc *rxd)
                  RX_MSDU_END_INFO1_L3_HDR_PAD);
 }
 
+static bool ath10k_qca99x0_rx_desc_msdu_limit_error(struct htt_rx_desc *rxd)
+{
+       return !!(rxd->msdu_end.common.info0 &
+                 __cpu_to_le32(RX_MSDU_END_INFO0_MSDU_LIMIT_ERR));
+}
+
 const struct ath10k_hw_ops qca99x0_ops = {
        .rx_desc_get_l3_pad_bytes = ath10k_qca99x0_rx_desc_get_l3_pad_bytes,
+       .rx_desc_get_msdu_limit_error = ath10k_qca99x0_rx_desc_msdu_limit_error,
 };
 
 const struct ath10k_hw_ops qca6174_ops = {
index 1b5da27..e50a8dc 100644 (file)
@@ -624,6 +624,7 @@ struct ath10k_hw_ops {
        int (*rx_desc_get_l3_pad_bytes)(struct htt_rx_desc *rxd);
        void (*set_coverage_class)(struct ath10k *ar, s16 value);
        int (*enable_pll_clk)(struct ath10k *ar);
+       bool (*rx_desc_get_msdu_limit_error)(struct htt_rx_desc *rxd);
 };
 
 extern const struct ath10k_hw_ops qca988x_ops;
@@ -642,6 +643,15 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw,
        return 0;
 }
 
+static inline bool
+ath10k_rx_desc_msdu_limit_error(struct ath10k_hw_params *hw,
+                               struct htt_rx_desc *rxd)
+{
+       if (hw->hw_ops->rx_desc_get_msdu_limit_error)
+               return hw->hw_ops->rx_desc_get_msdu_limit_error(rxd);
+       return false;
+}
+
 /* Target specific defines for MAIN firmware */
 #define TARGET_NUM_VDEVS                       8
 #define TARGET_NUM_PEER_AST                    2
index a1c2801..e49b367 100644 (file)
@@ -22,6 +22,7 @@
 #include <net/mac80211.h>
 #include <linux/etherdevice.h>
 #include <linux/acpi.h>
+#include <linux/of.h>
 
 #include "hif.h"
 #include "core.h"
@@ -4637,11 +4638,44 @@ static int ath10k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
        return ret;
 }
 
+static int __ath10k_fetch_bb_timing_dt(struct ath10k *ar,
+                                      struct wmi_bb_timing_cfg_arg *bb_timing)
+{
+       struct device_node *node;
+       const char *fem_name;
+       int ret;
+
+       node = ar->dev->of_node;
+       if (!node)
+               return -ENOENT;
+
+       ret = of_property_read_string_index(node, "ext-fem-name", 0, &fem_name);
+       if (ret)
+               return -ENOENT;
+
+       /*
+        * If external Front End module used in hardware, then default base band timing
+        * parameter cannot be used since they were fine tuned for reference hardware,
+        * so choosing different value suitable for that external FEM.
+        */
+       if (!strcmp("microsemi-lx5586", fem_name)) {
+               bb_timing->bb_tx_timing = 0x00;
+               bb_timing->bb_xpa_timing = 0x0101;
+       } else {
+               return -ENOENT;
+       }
+
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot bb_tx_timing 0x%x bb_xpa_timing 0x%x\n",
+                  bb_timing->bb_tx_timing, bb_timing->bb_xpa_timing);
+       return 0;
+}
+
 static int ath10k_start(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
        u32 param;
        int ret = 0;
+       struct wmi_bb_timing_cfg_arg bb_timing = {0};
 
        /*
         * This makes sense only when restarting hw. It is harmless to call
@@ -4796,6 +4830,19 @@ static int ath10k_start(struct ieee80211_hw *hw)
                clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);
        }
 
+       if (test_bit(WMI_SERVICE_BB_TIMING_CONFIG_SUPPORT, ar->wmi.svc_map)) {
+               ret = __ath10k_fetch_bb_timing_dt(ar, &bb_timing);
+               if (!ret) {
+                       ret = ath10k_wmi_pdev_bb_timing(ar, &bb_timing);
+                       if (ret) {
+                               ath10k_warn(ar,
+                                           "failed to set bb timings: %d\n",
+                                           ret);
+                               goto err_core_stop;
+                       }
+               }
+       }
+
        ar->num_started_vdevs = 0;
        ath10k_regd_update(ar);
 
@@ -5154,6 +5201,17 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                goto err;
        }
 
+       if (test_bit(WMI_SERVICE_VDEV_DISABLE_4_ADDR_SRC_LRN_SUPPORT,
+                    ar->wmi.svc_map)) {
+               vdev_param = ar->wmi.vdev_param->disable_4addr_src_lrn;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+                                               WMI_VDEV_DISABLE_4_ADDR_SRC_LRN);
+               if (ret && ret != -EOPNOTSUPP) {
+                       ath10k_warn(ar, "failed to disable 4addr src lrn vdev %i: %d\n",
+                                   arvif->vdev_id, ret);
+               }
+       }
+
        ar->free_vdev_map &= ~(1LL << arvif->vdev_id);
        spin_lock_bh(&ar->data_lock);
        list_add(&arvif->list, &ar->arvifs);
@@ -5754,30 +5812,6 @@ static int ath10k_mac_tdls_vif_stations_count(struct ieee80211_hw *hw,
        return data.num_tdls_stations;
 }
 
-static void ath10k_mac_tdls_vifs_count_iter(void *data, u8 *mac,
-                                           struct ieee80211_vif *vif)
-{
-       struct ath10k_vif *arvif = (void *)vif->drv_priv;
-       int *num_tdls_vifs = data;
-
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return;
-
-       if (ath10k_mac_tdls_vif_stations_count(arvif->ar->hw, vif) > 0)
-               (*num_tdls_vifs)++;
-}
-
-static int ath10k_mac_tdls_vifs_count(struct ieee80211_hw *hw)
-{
-       int num_tdls_vifs = 0;
-
-       ieee80211_iterate_active_interfaces_atomic(hw,
-                                                  IEEE80211_IFACE_ITER_NORMAL,
-                                                  ath10k_mac_tdls_vifs_count_iter,
-                                                  &num_tdls_vifs);
-       return num_tdls_vifs;
-}
-
 static int ath10k_hw_scan(struct ieee80211_hw *hw,
                          struct ieee80211_vif *vif,
                          struct ieee80211_scan_request *hw_req)
@@ -6285,7 +6319,6 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                 */
                enum wmi_peer_type peer_type = WMI_PEER_TYPE_DEFAULT;
                u32 num_tdls_stations;
-               u32 num_tdls_vifs;
 
                ath10k_dbg(ar, ATH10K_DBG_MAC,
                           "mac vdev %d peer create %pM (new sta) sta %d / %d peer %d / %d\n",
@@ -6293,15 +6326,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                           ar->num_stations + 1, ar->max_num_stations,
                           ar->num_peers + 1, ar->max_num_peers);
 
-               if (ath10k_debug_is_extd_tx_stats_enabled(ar)) {
-                       arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats),
-                                                 GFP_KERNEL);
-                       if (!arsta->tx_stats)
-                               goto exit;
-               }
-
                num_tdls_stations = ath10k_mac_tdls_vif_stations_count(hw, vif);
-               num_tdls_vifs = ath10k_mac_tdls_vifs_count(hw);
 
                if (sta->tdls) {
                        if (num_tdls_stations >= ar->max_num_tdls_vdevs) {
@@ -6321,12 +6346,22 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                        goto exit;
                }
 
+               if (ath10k_debug_is_extd_tx_stats_enabled(ar)) {
+                       arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats),
+                                                 GFP_KERNEL);
+                       if (!arsta->tx_stats) {
+                               ret = -ENOMEM;
+                               goto exit;
+                       }
+               }
+
                ret = ath10k_peer_create(ar, vif, sta, arvif->vdev_id,
                                         sta->addr, peer_type);
                if (ret) {
                        ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
                        ath10k_mac_dec_num_stations(arvif, sta);
+                       kfree(arsta->tx_stats);
                        goto exit;
                }
 
@@ -6339,6 +6374,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                        spin_unlock_bh(&ar->data_lock);
                        ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
                        ath10k_mac_dec_num_stations(arvif, sta);
+                       kfree(arsta->tx_stats);
                        ret = -ENOENT;
                        goto exit;
                }
@@ -6359,6 +6395,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                        ath10k_peer_delete(ar, arvif->vdev_id,
                                           sta->addr);
                        ath10k_mac_dec_num_stations(arvif, sta);
+                       kfree(arsta->tx_stats);
                        goto exit;
                }
 
@@ -6370,6 +6407,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                                    sta->addr, arvif->vdev_id, ret);
                        ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
                        ath10k_mac_dec_num_stations(arvif, sta);
+                       kfree(arsta->tx_stats);
 
                        if (num_tdls_stations != 0)
                                goto exit;
@@ -6385,9 +6423,6 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                           "mac vdev %d peer delete %pM sta %pK (sta gone)\n",
                           arvif->vdev_id, sta->addr, sta);
 
-               if (ath10k_debug_is_extd_tx_stats_enabled(ar))
-                       kfree(arsta->tx_stats);
-
                if (sta->tdls) {
                        ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id,
                                                          sta,
@@ -6427,6 +6462,11 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                }
                spin_unlock_bh(&ar->data_lock);
 
+               if (ath10k_debug_is_extd_tx_stats_enabled(ar)) {
+                       kfree(arsta->tx_stats);
+                       arsta->tx_stats = NULL;
+               }
+
                for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
                        ath10k_mac_txq_unref(ar, sta->txq[i]);
 
@@ -6867,7 +6907,7 @@ static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        u32 bitmap;
 
        if (drop) {
-               if (vif->type == NL80211_IFTYPE_STATION) {
+               if (vif && vif->type == NL80211_IFTYPE_STATION) {
                        bitmap = ~(1 << WMI_MGMT_TID);
                        list_for_each_entry(arvif, &ar->arvifs, list) {
                                if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
@@ -8313,7 +8353,6 @@ static u32 ath10k_mac_wrdd_get_mcc(struct ath10k *ar, union acpi_object *wrdd)
 
 static int ath10k_mac_get_wrdd_regulatory(struct ath10k *ar, u16 *rd)
 {
-       struct pci_dev __maybe_unused *pdev = to_pci_dev(ar->dev);
        acpi_handle root_handle;
        acpi_handle handle;
        struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL};
@@ -8321,7 +8360,7 @@ static int ath10k_mac_get_wrdd_regulatory(struct ath10k *ar, u16 *rd)
        u32 alpha2_code;
        char alpha2[3];
 
-       root_handle = ACPI_HANDLE(&pdev->dev);
+       root_handle = ACPI_HANDLE(ar->dev);
        if (!root_handle)
                return -EOPNOTSUPP;
 
index 56cb183..37b3bd6 100644 (file)
@@ -543,7 +543,7 @@ static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi)
                goto out;
 
        if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
-               ath10k_err(ar, "capablity req rejected: %d\n", resp->resp.error);
+               ath10k_err(ar, "capability req rejected: %d\n", resp->resp.error);
                ret = -EINVAL;
                goto out;
        }
@@ -623,7 +623,7 @@ static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi)
                goto out;
        }
 
-       ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi host capablity request completed\n");
+       ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi host capability request completed\n");
        return 0;
 
 out:
@@ -657,7 +657,7 @@ ath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi *qmi)
                               wlfw_ind_register_req_msg_v01_ei, &req);
        if (ret < 0) {
                qmi_txn_cancel(&txn);
-               ath10k_err(ar, "failed to send indication registed request: %d\n", ret);
+               ath10k_err(ar, "failed to send indication registered request: %d\n", ret);
                goto out;
        }
 
@@ -931,9 +931,9 @@ static int ath10k_qmi_setup_msa_resources(struct ath10k_qmi *qmi, u32 msa_size)
                qmi->msa_mem_size = resource_size(&r);
                qmi->msa_va = devm_memremap(dev, qmi->msa_pa, qmi->msa_mem_size,
                                            MEMREMAP_WT);
-               if (!qmi->msa_pa) {
+               if (IS_ERR(qmi->msa_va)) {
                        dev_err(dev, "failed to map memory region: %pa\n", &r.start);
-                       return -EBUSY;
+                       return PTR_ERR(qmi->msa_va);
                }
        } else {
                qmi->msa_va = dmam_alloc_coherent(dev, msa_size,
index 310674d..dfbfe67 100644 (file)
@@ -572,6 +572,7 @@ struct rx_msdu_start {
 #define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_LSB  0
 #define RX_MSDU_END_INFO0_FIRST_MSDU                BIT(14)
 #define RX_MSDU_END_INFO0_LAST_MSDU                 BIT(15)
+#define RX_MSDU_END_INFO0_MSDU_LIMIT_ERR            BIT(18)
 #define RX_MSDU_END_INFO0_PRE_DELIM_ERR             BIT(30)
 #define RX_MSDU_END_INFO0_RESERVED_3B               BIT(31)
 
@@ -676,6 +677,12 @@ struct rx_msdu_end {
  *             Indicates the last MSDU of the A-MSDU.  MPDU end status is
  *             only valid when last_msdu is set.
  *
+ *msdu_limit_error
+ *             Indicates that the MSDU threshold was exceeded and thus
+ *             all the rest of the MSDUs will not be scattered and
+ *             will not be decapsulated but will be received in RAW format
+ *             as a single MSDU buffer.
+ *
  *reserved_3a
  *             Reserved: HW should fill with zero.  FW should ignore.
  *
index 8d3d9bc..54efe6b 100644 (file)
@@ -46,14 +46,14 @@ static char *const ce_name[] = {
        "WLAN_CE_11",
 };
 
-static struct ath10k_wcn3990_vreg_info vreg_cfg[] = {
-       {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
-       {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
-       {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
-       {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
+static struct ath10k_vreg_info vreg_cfg[] = {
+       {NULL, "vdd-0.8-cx-mx", 800000, 850000, 0, 0, false},
+       {NULL, "vdd-1.8-xo", 1800000, 1850000, 0, 0, false},
+       {NULL, "vdd-1.3-rfa", 1300000, 1350000, 0, 0, false},
+       {NULL, "vdd-3.3-ch0", 3300000, 3350000, 0, 0, false},
 };
 
-static struct ath10k_wcn3990_clk_info clk_cfg[] = {
+static struct ath10k_clk_info clk_cfg[] = {
        {NULL, "cxo_ref_clk_pin", 0, false},
 };
 
@@ -474,14 +474,14 @@ static struct service_to_pipe target_service_to_ce_map_wlan[] = {
        },
 };
 
-void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value)
+static void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
 
        iowrite32(value, ar_snoc->mem + offset);
 }
 
-u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset)
+static u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
        u32 val;
@@ -918,7 +918,9 @@ static void ath10k_snoc_buffer_cleanup(struct ath10k *ar)
 
 static void ath10k_snoc_hif_stop(struct ath10k *ar)
 {
-       ath10k_snoc_irq_disable(ar);
+       if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+               ath10k_snoc_irq_disable(ar);
+
        napi_synchronize(&ar->napi);
        napi_disable(&ar->napi);
        ath10k_snoc_buffer_cleanup(ar);
@@ -927,10 +929,14 @@ static void ath10k_snoc_hif_stop(struct ath10k *ar)
 
 static int ath10k_snoc_hif_start(struct ath10k *ar)
 {
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
        napi_enable(&ar->napi);
        ath10k_snoc_irq_enable(ar);
        ath10k_snoc_rx_post(ar);
 
+       clear_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags);
+
        ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
 
        return 0;
@@ -994,7 +1000,8 @@ static int ath10k_snoc_wlan_enable(struct ath10k *ar)
 
 static void ath10k_snoc_wlan_disable(struct ath10k *ar)
 {
-       ath10k_qmi_wlan_disable(ar);
+       if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+               ath10k_qmi_wlan_disable(ar);
 }
 
 static void ath10k_snoc_hif_power_down(struct ath10k *ar)
@@ -1091,6 +1098,11 @@ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget)
        struct ath10k *ar = container_of(ctx, struct ath10k, napi);
        int done = 0;
 
+       if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
+               napi_complete(ctx);
+               return done;
+       }
+
        ath10k_ce_per_engine_service_any(ar);
        done = ath10k_htt_txrx_compl_task(ar, budget);
 
@@ -1187,17 +1199,29 @@ int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type)
        struct ath10k_bus_params bus_params;
        int ret;
 
+       if (test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags))
+               return 0;
+
        switch (type) {
        case ATH10K_QMI_EVENT_FW_READY_IND:
+               if (test_bit(ATH10K_SNOC_FLAG_REGISTERED, &ar_snoc->flags)) {
+                       queue_work(ar->workqueue, &ar->restart_work);
+                       break;
+               }
+
                bus_params.dev_type = ATH10K_DEV_TYPE_LL;
                bus_params.chip_id = ar_snoc->target_info.soc_version;
                ret = ath10k_core_register(ar, &bus_params);
                if (ret) {
-                       ath10k_err(ar, "failed to register driver core: %d\n",
+                       ath10k_err(ar, "Failed to register driver core: %d\n",
                                   ret);
+                       return ret;
                }
+               set_bit(ATH10K_SNOC_FLAG_REGISTERED, &ar_snoc->flags);
                break;
        case ATH10K_QMI_EVENT_FW_DOWN_IND:
+               set_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags);
+               set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
                break;
        default:
                ath10k_err(ar, "invalid fw indication: %llx\n", type);
@@ -1246,7 +1270,7 @@ static void ath10k_snoc_release_resource(struct ath10k *ar)
 }
 
 static int ath10k_get_vreg_info(struct ath10k *ar, struct device *dev,
-                               struct ath10k_wcn3990_vreg_info *vreg_info)
+                               struct ath10k_vreg_info *vreg_info)
 {
        struct regulator *reg;
        int ret = 0;
@@ -1284,7 +1308,7 @@ done:
 }
 
 static int ath10k_get_clk_info(struct ath10k *ar, struct device *dev,
-                              struct ath10k_wcn3990_clk_info *clk_info)
+                              struct ath10k_clk_info *clk_info)
 {
        struct clk *handle;
        int ret = 0;
@@ -1311,10 +1335,80 @@ static int ath10k_get_clk_info(struct ath10k *ar, struct device *dev,
        return ret;
 }
 
-static int ath10k_wcn3990_vreg_on(struct ath10k *ar)
+static int __ath10k_snoc_vreg_on(struct ath10k *ar,
+                                struct ath10k_vreg_info *vreg_info)
+{
+       int ret;
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being enabled\n",
+                  vreg_info->name);
+
+       ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
+                                   vreg_info->max_v);
+       if (ret) {
+               ath10k_err(ar,
+                          "failed to set regulator %s voltage-min: %d voltage-max: %d\n",
+                          vreg_info->name, vreg_info->min_v, vreg_info->max_v);
+               return ret;
+       }
+
+       if (vreg_info->load_ua) {
+               ret = regulator_set_load(vreg_info->reg, vreg_info->load_ua);
+               if (ret < 0) {
+                       ath10k_err(ar, "failed to set regulator %s load: %d\n",
+                                  vreg_info->name, vreg_info->load_ua);
+                       goto err_set_load;
+               }
+       }
+
+       ret = regulator_enable(vreg_info->reg);
+       if (ret) {
+               ath10k_err(ar, "failed to enable regulator %s\n",
+                          vreg_info->name);
+               goto err_enable;
+       }
+
+       if (vreg_info->settle_delay)
+               udelay(vreg_info->settle_delay);
+
+       return 0;
+
+err_enable:
+       regulator_set_load(vreg_info->reg, 0);
+err_set_load:
+       regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
+
+       return ret;
+}
+
+static int __ath10k_snoc_vreg_off(struct ath10k *ar,
+                                 struct ath10k_vreg_info *vreg_info)
+{
+       int ret;
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being disabled\n",
+                  vreg_info->name);
+
+       ret = regulator_disable(vreg_info->reg);
+       if (ret)
+               ath10k_err(ar, "failed to disable regulator %s\n",
+                          vreg_info->name);
+
+       ret = regulator_set_load(vreg_info->reg, 0);
+       if (ret < 0)
+               ath10k_err(ar, "failed to set load %s\n", vreg_info->name);
+
+       ret = regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
+       if (ret)
+               ath10k_err(ar, "failed to set voltage %s\n", vreg_info->name);
+
+       return ret;
+}
+
+static int ath10k_snoc_vreg_on(struct ath10k *ar)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_wcn3990_vreg_info *vreg_info;
+       struct ath10k_vreg_info *vreg_info;
        int ret = 0;
        int i;
 
@@ -1324,62 +1418,30 @@ static int ath10k_wcn3990_vreg_on(struct ath10k *ar)
                if (!vreg_info->reg)
                        continue;
 
-               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being enabled\n",
-                          vreg_info->name);
-
-               ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
-                                           vreg_info->max_v);
-               if (ret) {
-                       ath10k_err(ar,
-                                  "failed to set regulator %s voltage-min: %d voltage-max: %d\n",
-                                  vreg_info->name, vreg_info->min_v, vreg_info->max_v);
-                       goto err_reg_config;
-               }
-
-               if (vreg_info->load_ua) {
-                       ret = regulator_set_load(vreg_info->reg,
-                                                vreg_info->load_ua);
-                       if (ret < 0) {
-                               ath10k_err(ar,
-                                          "failed to set regulator %s load: %d\n",
-                                          vreg_info->name,
-                                          vreg_info->load_ua);
-                               goto err_reg_config;
-                       }
-               }
-
-               ret = regulator_enable(vreg_info->reg);
-               if (ret) {
-                       ath10k_err(ar, "failed to enable regulator %s\n",
-                                  vreg_info->name);
+               ret = __ath10k_snoc_vreg_on(ar, vreg_info);
+               if (ret)
                        goto err_reg_config;
-               }
-
-               if (vreg_info->settle_delay)
-                       udelay(vreg_info->settle_delay);
        }
 
        return 0;
 
 err_reg_config:
-       for (; i >= 0; i--) {
+       for (i = i - 1; i >= 0; i--) {
                vreg_info = &ar_snoc->vreg[i];
 
                if (!vreg_info->reg)
                        continue;
 
-               regulator_disable(vreg_info->reg);
-               regulator_set_load(vreg_info->reg, 0);
-               regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
+               __ath10k_snoc_vreg_off(ar, vreg_info);
        }
 
        return ret;
 }
 
-static int ath10k_wcn3990_vreg_off(struct ath10k *ar)
+static int ath10k_snoc_vreg_off(struct ath10k *ar)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_wcn3990_vreg_info *vreg_info;
+       struct ath10k_vreg_info *vreg_info;
        int ret = 0;
        int i;
 
@@ -1389,33 +1451,16 @@ static int ath10k_wcn3990_vreg_off(struct ath10k *ar)
                if (!vreg_info->reg)
                        continue;
 
-               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being disabled\n",
-                          vreg_info->name);
-
-               ret = regulator_disable(vreg_info->reg);
-               if (ret)
-                       ath10k_err(ar, "failed to disable regulator %s\n",
-                                  vreg_info->name);
-
-               ret = regulator_set_load(vreg_info->reg, 0);
-               if (ret < 0)
-                       ath10k_err(ar, "failed to set load %s\n",
-                                  vreg_info->name);
-
-               ret = regulator_set_voltage(vreg_info->reg, 0,
-                                           vreg_info->max_v);
-               if (ret)
-                       ath10k_err(ar, "failed to set voltage %s\n",
-                                  vreg_info->name);
+               ret = __ath10k_snoc_vreg_off(ar, vreg_info);
        }
 
        return ret;
 }
 
-static int ath10k_wcn3990_clk_init(struct ath10k *ar)
+static int ath10k_snoc_clk_init(struct ath10k *ar)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_wcn3990_clk_info *clk_info;
+       struct ath10k_clk_info *clk_info;
        int ret = 0;
        int i;
 
@@ -1449,7 +1494,7 @@ static int ath10k_wcn3990_clk_init(struct ath10k *ar)
        return 0;
 
 err_clock_config:
-       for (; i >= 0; i--) {
+       for (i = i - 1; i >= 0; i--) {
                clk_info = &ar_snoc->clk[i];
 
                if (!clk_info->handle)
@@ -1461,10 +1506,10 @@ err_clock_config:
        return ret;
 }
 
-static int ath10k_wcn3990_clk_deinit(struct ath10k *ar)
+static int ath10k_snoc_clk_deinit(struct ath10k *ar)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_wcn3990_clk_info *clk_info;
+       struct ath10k_clk_info *clk_info;
        int i;
 
        for (i = 0; i < ARRAY_SIZE(clk_cfg); i++) {
@@ -1488,18 +1533,18 @@ static int ath10k_hw_power_on(struct ath10k *ar)
 
        ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power on\n");
 
-       ret = ath10k_wcn3990_vreg_on(ar);
+       ret = ath10k_snoc_vreg_on(ar);
        if (ret)
                return ret;
 
-       ret = ath10k_wcn3990_clk_init(ar);
+       ret = ath10k_snoc_clk_init(ar);
        if (ret)
                goto vreg_off;
 
        return ret;
 
 vreg_off:
-       ath10k_wcn3990_vreg_off(ar);
+       ath10k_snoc_vreg_off(ar);
        return ret;
 }
 
@@ -1509,9 +1554,9 @@ static int ath10k_hw_power_off(struct ath10k *ar)
 
        ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power off\n");
 
-       ath10k_wcn3990_clk_deinit(ar);
+       ath10k_snoc_clk_deinit(ar);
 
-       ret = ath10k_wcn3990_vreg_off(ar);
+       ret = ath10k_snoc_vreg_off(ar);
 
        return ret;
 }
@@ -1609,7 +1654,6 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
        }
 
        ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc probe\n");
-       ath10k_warn(ar, "Warning: SNOC support is still work-in-progress, it will not work properly!");
 
        return 0;
 
@@ -1628,8 +1672,17 @@ err_core_destroy:
 static int ath10k_snoc_remove(struct platform_device *pdev)
 {
        struct ath10k *ar = platform_get_drvdata(pdev);
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
 
        ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc remove\n");
+
+       reinit_completion(&ar->driver_recovery);
+
+       if (test_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags))
+               wait_for_completion_timeout(&ar->driver_recovery, 3 * HZ);
+
+       set_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags);
+
        ath10k_core_unregister(ar);
        ath10k_hw_power_off(ar);
        ath10k_snoc_free_irq(ar);
@@ -1641,12 +1694,12 @@ static int ath10k_snoc_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver ath10k_snoc_driver = {
-               .probe  = ath10k_snoc_probe,
-               .remove = ath10k_snoc_remove,
-               .driver = {
-                       .name   = "ath10k_snoc",
-                       .of_match_table = ath10k_snoc_dt_match,
-               },
+       .probe  = ath10k_snoc_probe,
+       .remove = ath10k_snoc_remove,
+       .driver = {
+               .name   = "ath10k_snoc",
+               .of_match_table = ath10k_snoc_dt_match,
+       },
 };
 module_platform_driver(ath10k_snoc_driver);
 
index e1d2d66..2b2f23c 100644 (file)
@@ -53,7 +53,7 @@ struct ath10k_snoc_ce_irq {
        u32 irq_line;
 };
 
-struct ath10k_wcn3990_vreg_info {
+struct ath10k_vreg_info {
        struct regulator *reg;
        const char *name;
        u32 min_v;
@@ -63,13 +63,19 @@ struct ath10k_wcn3990_vreg_info {
        bool required;
 };
 
-struct ath10k_wcn3990_clk_info {
+struct ath10k_clk_info {
        struct clk *handle;
        const char *name;
        u32 freq;
        bool required;
 };
 
+enum ath10k_snoc_flags {
+       ATH10K_SNOC_FLAG_REGISTERED,
+       ATH10K_SNOC_FLAG_UNREGISTERING,
+       ATH10K_SNOC_FLAG_RECOVERY,
+};
+
 struct ath10k_snoc {
        struct platform_device *dev;
        struct ath10k *ar;
@@ -81,9 +87,10 @@ struct ath10k_snoc {
        struct ath10k_snoc_ce_irq ce_irqs[CE_COUNT_MAX];
        struct ath10k_ce ce;
        struct timer_list rx_post_retry;
-       struct ath10k_wcn3990_vreg_info *vreg;
-       struct ath10k_wcn3990_clk_info *clk;
+       struct ath10k_vreg_info *vreg;
+       struct ath10k_clk_info *clk;
        struct ath10k_qmi *qmi;
+       unsigned long int flags;
 };
 
 static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)
@@ -91,8 +98,6 @@ static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)
        return (struct ath10k_snoc *)ar->drv_priv;
 }
 
-void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value);
-u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset);
 int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type);
 
 #endif /* _SNOC_H_ */
index aa8978a..fe35edc 100644 (file)
@@ -140,6 +140,9 @@ void ath10k_thermal_set_throttling(struct ath10k *ar)
 
        lockdep_assert_held(&ar->conf_mutex);
 
+       if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
+               return;
+
        if (!ar->wmi.ops->gen_pdev_set_quiet_mode)
                return;
 
@@ -165,6 +168,9 @@ int ath10k_thermal_register(struct ath10k *ar)
        struct device *hwmon_dev;
        int ret;
 
+       if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
+               return 0;
+
        cdev = thermal_cooling_device_register("ath10k_thermal", ar,
                                               &ath10k_thermal_ops);
 
@@ -216,6 +222,9 @@ err_cooling_destroy:
 
 void ath10k_thermal_unregister(struct ath10k *ar)
 {
+       if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
+               return;
+
        sysfs_remove_link(&ar->dev->kobj, "cooling_device");
        thermal_cooling_device_unregister(ar->thermal.cdev);
 }
index 7978a77..0466307 100644 (file)
@@ -219,6 +219,9 @@ struct wmi_ops {
        struct sk_buff *(*gen_echo)(struct ath10k *ar, u32 value);
        struct sk_buff *(*gen_pdev_get_tpc_table_cmdid)(struct ath10k *ar,
                                                        u32 param);
+       struct sk_buff *(*gen_bb_timing)
+                       (struct ath10k *ar,
+                        const struct wmi_bb_timing_cfg_arg *arg);
 
 };
 
@@ -1576,4 +1579,21 @@ ath10k_wmi_report_radar_found(struct ath10k *ar,
                                   ar->wmi.cmd->radar_found_cmdid);
 }
 
+static inline int
+ath10k_wmi_pdev_bb_timing(struct ath10k *ar,
+                         const struct wmi_bb_timing_cfg_arg *arg)
+{
+       struct sk_buff *skb;
+
+       if (!ar->wmi.ops->gen_bb_timing)
+               return -EOPNOTSUPP;
+
+       skb = ar->wmi.ops->gen_bb_timing(ar, arg);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->set_bb_timing_cmdid);
+}
 #endif
index bab8b25..892bd8c 100644 (file)
@@ -621,7 +621,7 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
                ath10k_wmi_event_mgmt_tx_compl(ar, skb);
                break;
        default:
-               ath10k_warn(ar, "Unknown eventid: %d\n", id);
+               ath10k_dbg(ar, ATH10K_DBG_WMI, "Unknown eventid: %d\n", id);
                break;
        }
 
@@ -762,6 +762,9 @@ static int ath10k_wmi_tlv_op_pull_ch_info_ev(struct ath10k *ar,
        arg->noise_floor = ev->noise_floor;
        arg->rx_clear_count = ev->rx_clear_count;
        arg->cycle_count = ev->cycle_count;
+       if (test_bit(ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL,
+                    ar->running_fw->fw_file.fw_features))
+               arg->mac_clk_mhz = ev->mac_clk_mhz;
 
        kfree(tb);
        return 0;
@@ -3452,7 +3455,6 @@ ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar,
        struct wmi_tlv *tlv;
        struct sk_buff *skb;
        __le32 *channel_list;
-       u16 tlv_len;
        size_t len;
        void *ptr;
        u32 i;
@@ -3510,8 +3512,6 @@ ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar,
        /* nlo_configured_parameters(nlo_list) */
        cmd->no_of_ssids = __cpu_to_le32(min_t(u8, pno->uc_networks_count,
                                               WMI_NLO_MAX_SSIDS));
-       tlv_len = __le32_to_cpu(cmd->no_of_ssids) *
-               sizeof(struct nlo_configured_parameters);
 
        tlv = ptr;
        tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
index 92c25f5..e07e990 100644 (file)
@@ -1564,6 +1564,9 @@ wmi_tlv_svc_map_ext(const __le32 *in, unsigned long *out, size_t len)
        SVCMAP(WMI_TLV_SERVICE_SPOOF_MAC_SUPPORT,
               WMI_SERVICE_SPOOF_MAC_SUPPORT,
               WMI_TLV_MAX_SERVICE);
+       SVCMAP(WMI_TLV_SERVICE_THERM_THROT,
+              WMI_SERVICE_THERM_THROT,
+              WMI_TLV_MAX_SERVICE);
 }
 
 #undef SVCMAP
@@ -1579,6 +1582,16 @@ struct ath10k_mgmt_tx_pkt_addr {
        dma_addr_t paddr;
 };
 
+struct chan_info_params {
+       u32 err_code;
+       u32 freq;
+       u32 cmd_flags;
+       u32 noise_floor;
+       u32 rx_clear_count;
+       u32 cycle_count;
+       u32 mac_clk_mhz;
+};
+
 struct wmi_tlv_mgmt_tx_compl_ev {
        __le32 desc_id;
        __le32 status;
index 25e8fa7..ba83740 100644 (file)
@@ -539,6 +539,7 @@ static struct wmi_cmd_map wmi_10_2_4_cmd_map = {
                WMI_10_2_PDEV_BSS_CHAN_INFO_REQUEST_CMDID,
        .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED,
        .radar_found_cmdid = WMI_CMD_UNSUPPORTED,
+       .set_bb_timing_cmdid = WMI_10_2_PDEV_SET_BB_TIMING_CONFIG_CMDID,
 };
 
 /* 10.4 WMI cmd track */
@@ -825,6 +826,7 @@ static struct wmi_vdev_param_map wmi_vdev_param_map = {
        .meru_vc = WMI_VDEV_PARAM_UNSUPPORTED,
        .rx_decap_type = WMI_VDEV_PARAM_UNSUPPORTED,
        .bw_nss_ratemask = WMI_VDEV_PARAM_UNSUPPORTED,
+       .disable_4addr_src_lrn = WMI_VDEV_PARAM_UNSUPPORTED,
 };
 
 /* 10.X WMI VDEV param map */
@@ -900,6 +902,7 @@ static struct wmi_vdev_param_map wmi_10x_vdev_param_map = {
        .meru_vc = WMI_VDEV_PARAM_UNSUPPORTED,
        .rx_decap_type = WMI_VDEV_PARAM_UNSUPPORTED,
        .bw_nss_ratemask = WMI_VDEV_PARAM_UNSUPPORTED,
+       .disable_4addr_src_lrn = WMI_VDEV_PARAM_UNSUPPORTED,
 };
 
 static struct wmi_vdev_param_map wmi_10_2_4_vdev_param_map = {
@@ -974,6 +977,7 @@ static struct wmi_vdev_param_map wmi_10_2_4_vdev_param_map = {
        .meru_vc = WMI_VDEV_PARAM_UNSUPPORTED,
        .rx_decap_type = WMI_VDEV_PARAM_UNSUPPORTED,
        .bw_nss_ratemask = WMI_VDEV_PARAM_UNSUPPORTED,
+       .disable_4addr_src_lrn = WMI_VDEV_PARAM_UNSUPPORTED,
 };
 
 static struct wmi_vdev_param_map wmi_10_4_vdev_param_map = {
@@ -1051,6 +1055,7 @@ static struct wmi_vdev_param_map wmi_10_4_vdev_param_map = {
        .bw_nss_ratemask = WMI_10_4_VDEV_PARAM_BW_NSS_RATEMASK,
        .inc_tsf = WMI_10_4_VDEV_PARAM_TSF_INCREMENT,
        .dec_tsf = WMI_10_4_VDEV_PARAM_TSF_DECREMENT,
+       .disable_4addr_src_lrn = WMI_10_4_VDEV_PARAM_DISABLE_4_ADDR_SRC_LRN,
 };
 
 static struct wmi_pdev_param_map wmi_pdev_param_map = {
@@ -2554,60 +2559,69 @@ static int ath10k_wmi_10_4_op_pull_ch_info_ev(struct ath10k *ar,
        return 0;
 }
 
-void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
+/*
+ * Handle the channel info event for firmware which only sends one
+ * chan_info event per scanned channel.
+ */
+static void ath10k_wmi_event_chan_info_unpaired(struct ath10k *ar,
+                                               struct chan_info_params *params)
 {
-       struct wmi_ch_info_ev_arg arg = {};
        struct survey_info *survey;
-       u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count;
-       int idx, ret;
+       int idx;
 
-       ret = ath10k_wmi_pull_ch_info(ar, skb, &arg);
-       if (ret) {
-               ath10k_warn(ar, "failed to parse chan info event: %d\n", ret);
+       if (params->cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
+               ath10k_dbg(ar, ATH10K_DBG_WMI, "chan info report completed\n");
                return;
        }
 
-       err_code = __le32_to_cpu(arg.err_code);
-       freq = __le32_to_cpu(arg.freq);
-       cmd_flags = __le32_to_cpu(arg.cmd_flags);
-       noise_floor = __le32_to_cpu(arg.noise_floor);
-       rx_clear_count = __le32_to_cpu(arg.rx_clear_count);
-       cycle_count = __le32_to_cpu(arg.cycle_count);
+       idx = freq_to_idx(ar, params->freq);
+       if (idx >= ARRAY_SIZE(ar->survey)) {
+               ath10k_warn(ar, "chan info: invalid frequency %d (idx %d out of bounds)\n",
+                           params->freq, idx);
+               return;
+       }
 
-       ath10k_dbg(ar, ATH10K_DBG_WMI,
-                  "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
-                  err_code, freq, cmd_flags, noise_floor, rx_clear_count,
-                  cycle_count);
+       survey = &ar->survey[idx];
 
-       spin_lock_bh(&ar->data_lock);
+       if (!params->mac_clk_mhz)
+               return;
 
-       switch (ar->scan.state) {
-       case ATH10K_SCAN_IDLE:
-       case ATH10K_SCAN_STARTING:
-               ath10k_warn(ar, "received chan info event without a scan request, ignoring\n");
-               goto exit;
-       case ATH10K_SCAN_RUNNING:
-       case ATH10K_SCAN_ABORTING:
-               break;
-       }
+       memset(survey, 0, sizeof(*survey));
 
-       idx = freq_to_idx(ar, freq);
+       survey->noise = params->noise_floor;
+       survey->time = (params->cycle_count / params->mac_clk_mhz) / 1000;
+       survey->time_busy = (params->rx_clear_count / params->mac_clk_mhz) / 1000;
+       survey->filled |= SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |
+                         SURVEY_INFO_TIME_BUSY;
+}
+
+/*
+ * Handle the channel info event for firmware which sends chan_info
+ * event in pairs(start and stop events) for every scanned channel.
+ */
+static void ath10k_wmi_event_chan_info_paired(struct ath10k *ar,
+                                             struct chan_info_params *params)
+{
+       struct survey_info *survey;
+       int idx;
+
+       idx = freq_to_idx(ar, params->freq);
        if (idx >= ARRAY_SIZE(ar->survey)) {
                ath10k_warn(ar, "chan info: invalid frequency %d (idx %d out of bounds)\n",
-                           freq, idx);
-               goto exit;
+                           params->freq, idx);
+               return;
        }
 
-       if (cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
+       if (params->cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
                if (ar->ch_info_can_report_survey) {
                        survey = &ar->survey[idx];
-                       survey->noise = noise_floor;
+                       survey->noise = params->noise_floor;
                        survey->filled = SURVEY_INFO_NOISE_DBM;
 
                        ath10k_hw_fill_survey_time(ar,
                                                   survey,
-                                                  cycle_count,
-                                                  rx_clear_count,
+                                                  params->cycle_count,
+                                                  params->rx_clear_count,
                                                   ar->survey_last_cycle_count,
                                                   ar->survey_last_rx_clear_count);
                }
@@ -2617,10 +2631,55 @@ void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
                ar->ch_info_can_report_survey = true;
        }
 
-       if (!(cmd_flags & WMI_CHAN_INFO_FLAG_PRE_COMPLETE)) {
-               ar->survey_last_rx_clear_count = rx_clear_count;
-               ar->survey_last_cycle_count = cycle_count;
+       if (!(params->cmd_flags & WMI_CHAN_INFO_FLAG_PRE_COMPLETE)) {
+               ar->survey_last_rx_clear_count = params->rx_clear_count;
+               ar->survey_last_cycle_count = params->cycle_count;
        }
+}
+
+void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct chan_info_params ch_info_param;
+       struct wmi_ch_info_ev_arg arg = {};
+       int ret;
+
+       ret = ath10k_wmi_pull_ch_info(ar, skb, &arg);
+       if (ret) {
+               ath10k_warn(ar, "failed to parse chan info event: %d\n", ret);
+               return;
+       }
+
+       ch_info_param.err_code = __le32_to_cpu(arg.err_code);
+       ch_info_param.freq = __le32_to_cpu(arg.freq);
+       ch_info_param.cmd_flags = __le32_to_cpu(arg.cmd_flags);
+       ch_info_param.noise_floor = __le32_to_cpu(arg.noise_floor);
+       ch_info_param.rx_clear_count = __le32_to_cpu(arg.rx_clear_count);
+       ch_info_param.cycle_count = __le32_to_cpu(arg.cycle_count);
+       ch_info_param.mac_clk_mhz = __le32_to_cpu(arg.mac_clk_mhz);
+
+       ath10k_dbg(ar, ATH10K_DBG_WMI,
+                  "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
+                  ch_info_param.err_code, ch_info_param.freq, ch_info_param.cmd_flags,
+                  ch_info_param.noise_floor, ch_info_param.rx_clear_count,
+                  ch_info_param.cycle_count);
+
+       spin_lock_bh(&ar->data_lock);
+
+       switch (ar->scan.state) {
+       case ATH10K_SCAN_IDLE:
+       case ATH10K_SCAN_STARTING:
+               ath10k_warn(ar, "received chan info event without a scan request, ignoring\n");
+               goto exit;
+       case ATH10K_SCAN_RUNNING:
+       case ATH10K_SCAN_ABORTING:
+               break;
+       }
+
+       if (test_bit(ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL,
+                    ar->running_fw->fw_file.fw_features))
+               ath10k_wmi_event_chan_info_unpaired(ar, &ch_info_param);
+       else
+               ath10k_wmi_event_chan_info_paired(ar, &ch_info_param);
 
 exit:
        spin_unlock_bh(&ar->data_lock);
@@ -8785,6 +8844,27 @@ ath10k_wmi_barrier(struct ath10k *ar)
        return 0;
 }
 
+static struct sk_buff *
+ath10k_wmi_10_2_4_op_gen_bb_timing(struct ath10k *ar,
+                                  const struct wmi_bb_timing_cfg_arg *arg)
+{
+       struct wmi_pdev_bb_timing_cfg_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
+
+       cmd = (struct wmi_pdev_bb_timing_cfg_cmd *)skb->data;
+       cmd->bb_tx_timing = __cpu_to_le32(arg->bb_tx_timing);
+       cmd->bb_xpa_timing = __cpu_to_le32(arg->bb_xpa_timing);
+
+       ath10k_dbg(ar, ATH10K_DBG_WMI,
+                  "wmi pdev bb_tx_timing 0x%x bb_xpa_timing 0x%x\n",
+                  arg->bb_tx_timing, arg->bb_xpa_timing);
+       return skb;
+}
+
 static const struct wmi_ops wmi_ops = {
        .rx = ath10k_wmi_op_rx,
        .map_svc = wmi_main_svc_map,
@@ -9058,6 +9138,7 @@ static const struct wmi_ops wmi_10_2_4_ops = {
        .gen_pdev_enable_adaptive_cca =
                ath10k_wmi_op_gen_pdev_enable_adaptive_cca,
        .get_vdev_subtype = ath10k_wmi_10_2_4_op_get_vdev_subtype,
+       .gen_bb_timing = ath10k_wmi_10_2_4_op_gen_bb_timing,
        /* .gen_bcn_tmpl not implemented */
        /* .gen_prb_tmpl not implemented */
        /* .gen_p2p_go_bcn_ie not implemented */
index f7badd0..2034ccc 100644 (file)
@@ -205,6 +205,9 @@ enum wmi_service {
        WMI_SERVICE_SPOOF_MAC_SUPPORT,
        WMI_SERVICE_TX_DATA_ACK_RSSI,
        WMI_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT,
+       WMI_SERVICE_VDEV_DISABLE_4_ADDR_SRC_LRN_SUPPORT,
+       WMI_SERVICE_BB_TIMING_CONFIG_SUPPORT,
+       WMI_SERVICE_THERM_THROT,
 
        /* keep last */
        WMI_SERVICE_MAX,
@@ -244,6 +247,9 @@ enum wmi_10x_service {
        WMI_10X_SERVICE_PEER_STATS,
        WMI_10X_SERVICE_RESET_CHIP,
        WMI_10X_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS,
+       WMI_10X_SERVICE_VDEV_BCN_RATE_CONTROL,
+       WMI_10X_SERVICE_PER_PACKET_SW_ENCRYPT,
+       WMI_10X_SERVICE_BB_TIMING_CONFIG_SUPPORT,
 };
 
 enum wmi_main_service {
@@ -359,6 +365,9 @@ enum wmi_10_4_service {
        WMI_10_4_SERVICE_PEER_TID_CONFIGS_SUPPORT,
        WMI_10_4_SERVICE_VDEV_BCN_RATE_CONTROL,
        WMI_10_4_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT,
+       WMI_10_4_SERVICE_HTT_ASSERT_TRIGGER_SUPPORT,
+       WMI_10_4_SERVICE_VDEV_FILTER_NEIGHBOR_RX_PACKETS,
+       WMI_10_4_SERVICE_VDEV_DISABLE_4_ADDR_SRC_LRN_SUPPORT,
 };
 
 static inline char *wmi_service_name(int service_id)
@@ -568,6 +577,8 @@ static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out,
               WMI_SERVICE_RESET_CHIP, len);
        SVCMAP(WMI_10X_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS,
               WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, len);
+       SVCMAP(WMI_10X_SERVICE_BB_TIMING_CONFIG_SUPPORT,
+              WMI_SERVICE_BB_TIMING_CONFIG_SUPPORT, len);
 }
 
 static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out,
@@ -786,6 +797,8 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out,
               WMI_SERVICE_TX_DATA_ACK_RSSI, len);
        SVCMAP(WMI_10_4_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT,
               WMI_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT, len);
+       SVCMAP(WMI_10_4_SERVICE_VDEV_DISABLE_4_ADDR_SRC_LRN_SUPPORT,
+              WMI_SERVICE_VDEV_DISABLE_4_ADDR_SRC_LRN_SUPPORT, len);
 }
 
 #undef SVCMAP
@@ -986,6 +999,7 @@ struct wmi_cmd_map {
        u32 pdev_wds_entry_list_cmdid;
        u32 tdls_set_offchan_mode_cmdid;
        u32 radar_found_cmdid;
+       u32 set_bb_timing_cmdid;
 };
 
 /*
@@ -1601,6 +1615,8 @@ enum wmi_10_2_cmd_id {
        WMI_10_2_SET_LTEU_CONFIG_CMDID,
        WMI_10_2_SET_CCA_PARAMS,
        WMI_10_2_PDEV_BSS_CHAN_INFO_REQUEST_CMDID,
+       WMI_10_2_FWTEST_CMDID,
+       WMI_10_2_PDEV_SET_BB_TIMING_CONFIG_CMDID,
        WMI_10_2_PDEV_UTF_CMDID = WMI_10_2_END_CMDID - 1,
 };
 
@@ -4984,6 +5000,7 @@ enum wmi_rate_preamble {
        (((preamble) << 6) | ((nss) << 4) | (rate))
 #define ATH10K_HW_AMPDU(flags)         ((flags) & 0x1)
 #define ATH10K_HW_BA_FAIL(flags)       (((flags) >> 1) & 0x3)
+#define ATH10K_FW_SKIPPED_RATE_CTRL(flags)     (((flags) >> 6) & 0x1)
 
 #define ATH10K_VHT_MCS_NUM     10
 #define ATH10K_BW_NUM          4
@@ -4991,6 +5008,7 @@ enum wmi_rate_preamble {
 #define ATH10K_LEGACY_NUM      12
 #define ATH10K_GI_NUM          2
 #define ATH10K_HT_MCS_NUM      32
+#define ATH10K_RATE_TABLE_NUM  320
 
 /* Value to disable fixed rate setting */
 #define WMI_FIXED_RATE_NONE    (0xff)
@@ -5064,6 +5082,7 @@ struct wmi_vdev_param_map {
        u32 bw_nss_ratemask;
        u32 inc_tsf;
        u32 dec_tsf;
+       u32 disable_4addr_src_lrn;
 };
 
 #define WMI_VDEV_PARAM_UNSUPPORTED 0
@@ -5403,8 +5422,20 @@ enum wmi_10_4_vdev_param {
        WMI_10_4_VDEV_PARAM_ATF_SSID_SCHED_POLICY,
        WMI_10_4_VDEV_PARAM_DISABLE_DYN_BW_RTS,
        WMI_10_4_VDEV_PARAM_TSF_DECREMENT,
+       WMI_10_4_VDEV_PARAM_SELFGEN_FIXED_RATE,
+       WMI_10_4_VDEV_PARAM_AMPDU_SUBFRAME_SIZE_PER_AC,
+       WMI_10_4_VDEV_PARAM_NSS_VHT160,
+       WMI_10_4_VDEV_PARAM_NSS_VHT80_80,
+       WMI_10_4_VDEV_PARAM_AMSDU_SUBFRAME_SIZE_PER_AC,
+       WMI_10_4_VDEV_PARAM_DISABLE_CABQ,
+       WMI_10_4_VDEV_PARAM_SIFS_TRIGGER_RATE,
+       WMI_10_4_VDEV_PARAM_TX_POWER,
+       WMI_10_4_VDEV_PARAM_ENABLE_DISABLE_RTT_RESPONDER_ROLE,
+       WMI_10_4_VDEV_PARAM_DISABLE_4_ADDR_SRC_LRN,
 };
 
+#define WMI_VDEV_DISABLE_4_ADDR_SRC_LRN 1
+
 #define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0)
 #define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1)
 #define WMI_VDEV_PARAM_TXBF_SU_TX_BFER BIT(2)
@@ -6441,6 +6472,14 @@ struct wmi_chan_info_event {
        __le32 noise_floor;
        __le32 rx_clear_count;
        __le32 cycle_count;
+       __le32 chan_tx_pwr_range;
+       __le32 chan_tx_pwr_tp;
+       __le32 rx_frame_count;
+       __le32 my_bss_rx_cycle_count;
+       __le32 rx_11b_mode_data_duration;
+       __le32 tx_frame_cnt;
+       __le32 mac_clk_mhz;
+
 } __packed;
 
 struct wmi_10_4_chan_info_event {
@@ -6669,6 +6708,10 @@ struct wmi_ch_info_ev_arg {
        __le32 chan_tx_pwr_range;
        __le32 chan_tx_pwr_tp;
        __le32 rx_frame_count;
+       __le32 my_bss_rx_cycle_count;
+       __le32 rx_11b_mode_data_duration;
+       __le32 tx_frame_cnt;
+       __le32 mac_clk_mhz;
 };
 
 /* From 10.4 firmware, not sure all have the same values. */
@@ -7140,6 +7183,23 @@ struct wmi_pdev_chan_info_req_cmd {
        __le32 reserved;
 } __packed;
 
+/* bb timing register configurations */
+struct wmi_bb_timing_cfg_arg {
+       /* Tx_end to pa off timing */
+       u32 bb_tx_timing;
+
+       /* Tx_end to external pa off timing */
+       u32 bb_xpa_timing;
+};
+
+struct wmi_pdev_bb_timing_cfg_cmd {
+       /* Tx_end to pa off timing */
+       __le32 bb_tx_timing;
+
+       /* Tx_end to external pa off timing */
+       __le32 bb_xpa_timing;
+} __packed;
+
 struct ath10k;
 struct ath10k_vif;
 struct ath10k_fw_stats_pdev;
index 51b26b3..36d4245 100644 (file)
@@ -135,7 +135,7 @@ static void ath10k_wow_convert_8023_to_80211
               &old_hdr_mask->h_proto,
               sizeof(old_hdr_mask->h_proto));
 
-       /* Caculate new pkt_offset */
+       /* Calculate new pkt_offset */
        if (old->pkt_offset < ETH_ALEN)
                new->pkt_offset = old->pkt_offset +
                        offsetof(struct ieee80211_hdr_3addr, addr1);
@@ -146,7 +146,7 @@ static void ath10k_wow_convert_8023_to_80211
        else
                new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
 
-       /* Caculate new hdr end offset */
+       /* Calculate new hdr end offset */
        if (total_len > ETH_HLEN)
                hdr_80211_end_offset = hdr_len + rfc_len;
        else if (total_len > offsetof(struct ethhdr, h_proto))
index e121187..59dd508 100644 (file)
@@ -291,7 +291,7 @@ static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
        }
 
        if (!test_bit(WLAN_ENABLED, &vif->flags)) {
-               ath6kl_err("wlan disabled\n");
+               ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "wlan disabled\n");
                return false;
        }
 
@@ -939,7 +939,7 @@ static int ath6kl_set_probed_ssids(struct ath6kl *ar,
                else
                        ssid_list[i].flag = ANY_SSID_FLAG;
 
-               if (n_match_ssid == 0)
+               if (ar->wiphy->max_match_sets != 0 && n_match_ssid == 0)
                        ssid_list[i].flag |= MATCH_SSID_FLAG;
        }
 
@@ -1093,7 +1093,7 @@ void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
        if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
                for (i = 0; i < vif->scan_req->n_ssids; i++) {
                        ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
-                                                 i + 1, DISABLE_SSID_FLAG,
+                                                 i, DISABLE_SSID_FLAG,
                                                  0, NULL);
                }
        }
index cb59016..5e7ea83 100644 (file)
@@ -389,6 +389,7 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel)
                if (!ik->valid || ik->key_type != WAPI_CRYPT)
                        break;
                /* for WAPI, we need to set the delayed group key, continue: */
+               /* fall through */
        case WPA_PSK_AUTH:
        case WPA2_PSK_AUTH:
        case (WPA_PSK_AUTH | WPA2_PSK_AUTH):
index 1f35230..ceca23a 100644 (file)
@@ -116,7 +116,7 @@ config ATH9K_DFS_CERTIFIED
          except increase code size.
 
 config ATH9K_DYNACK
-       bool "Atheros ath9k ACK timeout estimation algorithm (EXPERIMENTAL)"
+       bool "Atheros ath9k ACK timeout estimation algorithm"
        depends on ATH9K
        default n
        ---help---
index 11d6f97..dae9540 100644 (file)
@@ -586,7 +586,7 @@ static void ar5008_hw_init_chain_masks(struct ath_hw *ah)
                        REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7);
                        break;
                }
-               /* else: fall through */
+               /* fall through */
        case 0x1:
        case 0x2:
        case 0x7:
index 7132918..6f32b8d 100644 (file)
@@ -119,7 +119,7 @@ static int ar9002_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
                                aModeRefSel = 2;
                        if (aModeRefSel)
                                break;
-                       /* else: fall through */
+                       /* fall through */
                case 1:
                default:
                        aModeRefSel = 0;
index 0fe9c83..9899661 100644 (file)
@@ -1055,17 +1055,15 @@ void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep)
 static void ar9003_mci_send_2g5g_status(struct ath_hw *ah, bool wait_done)
 {
        struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
-       u32 new_flags, to_set, to_clear;
+       u32 to_set, to_clear;
 
        if (!mci->update_2g5g || (mci->bt_state == MCI_BT_SLEEP))
                return;
 
        if (mci->is_2g) {
-               new_flags = MCI_2G_FLAGS;
                to_clear = MCI_2G_FLAGS_CLEAR_MASK;
                to_set = MCI_2G_FLAGS_SET_MASK;
        } else {
-               new_flags = MCI_5G_FLAGS;
                to_clear = MCI_5G_FLAGS_CLEAR_MASK;
                to_set = MCI_5G_FLAGS_SET_MASK;
        }
index 21ba209..0fca44e 100644 (file)
@@ -272,7 +272,7 @@ struct ath_node {
 #endif
        u8 key_idx[4];
 
-       u32 ackto;
+       int ackto;
        struct list_head list;
 };
 
index 7334c9b..f112fa5 100644 (file)
  * ath_dynack_ewma - EWMA (Exponentially Weighted Moving Average) calculation
  *
  */
-static inline u32 ath_dynack_ewma(u32 old, u32 new)
+static inline int ath_dynack_ewma(int old, int new)
 {
-       return (new * (EWMA_DIV - EWMA_LEVEL) + old * EWMA_LEVEL) / EWMA_DIV;
+       if (old > 0)
+               return (new * (EWMA_DIV - EWMA_LEVEL) +
+                       old * EWMA_LEVEL) / EWMA_DIV;
+       else
+               return new;
 }
 
 /**
@@ -82,10 +86,10 @@ static inline bool ath_dynack_bssidmask(struct ath_hw *ah, const u8 *mac)
  */
 static void ath_dynack_compute_ackto(struct ath_hw *ah)
 {
-       struct ath_node *an;
-       u32 to = 0;
-       struct ath_dynack *da = &ah->dynack;
        struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_dynack *da = &ah->dynack;
+       struct ath_node *an;
+       int to = 0;
 
        list_for_each_entry(an, &da->nodes, list)
                if (an->ackto > to)
@@ -144,7 +148,8 @@ static void ath_dynack_compute_to(struct ath_hw *ah)
                                        an->ackto = ath_dynack_ewma(an->ackto,
                                                                    ackto);
                                        ath_dbg(ath9k_hw_common(ah), DYNACK,
-                                               "%pM to %u\n", dst, an->ackto);
+                                               "%pM to %d [%u]\n", dst,
+                                               an->ackto, ackto);
                                        if (time_is_before_jiffies(da->lto)) {
                                                ath_dynack_compute_ackto(ah);
                                                da->lto = jiffies + COMPUTE_TO;
@@ -166,18 +171,21 @@ static void ath_dynack_compute_to(struct ath_hw *ah)
  * @ah: ath hw
  * @skb: socket buffer
  * @ts: tx status info
+ * @sta: station pointer
  *
  */
 void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
-                            struct ath_tx_status *ts)
+                            struct ath_tx_status *ts,
+                            struct ieee80211_sta *sta)
 {
-       u8 ridx;
        struct ieee80211_hdr *hdr;
        struct ath_dynack *da = &ah->dynack;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       u32 dur = ts->duration;
+       u8 ridx;
 
-       if ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !da->enabled)
+       if (!da->enabled || (info->flags & IEEE80211_TX_CTL_NO_ACK))
                return;
 
        spin_lock_bh(&da->qlock);
@@ -187,11 +195,19 @@ void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
        /* late ACK */
        if (ts->ts_status & ATH9K_TXERR_XRETRY) {
                if (ieee80211_is_assoc_req(hdr->frame_control) ||
-                   ieee80211_is_assoc_resp(hdr->frame_control)) {
+                   ieee80211_is_assoc_resp(hdr->frame_control) ||
+                   ieee80211_is_auth(hdr->frame_control)) {
                        ath_dbg(common, DYNACK, "late ack\n");
+
                        ath9k_hw_setslottime(ah, (LATEACK_TO - 3) / 2);
                        ath9k_hw_set_ack_timeout(ah, LATEACK_TO);
                        ath9k_hw_set_cts_timeout(ah, LATEACK_TO);
+                       if (sta) {
+                               struct ath_node *an;
+
+                               an = (struct ath_node *)sta->drv_priv;
+                               an->ackto = -1;
+                       }
                        da->lto = jiffies + LATEACK_DELAY;
                }
 
@@ -202,14 +218,13 @@ void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
        ridx = ts->ts_rateindex;
 
        da->st_rbf.ts[da->st_rbf.t_rb].tstamp = ts->ts_tstamp;
-       da->st_rbf.ts[da->st_rbf.t_rb].dur = ts->duration;
        ether_addr_copy(da->st_rbf.addr[da->st_rbf.t_rb].h_dest, hdr->addr1);
        ether_addr_copy(da->st_rbf.addr[da->st_rbf.t_rb].h_src, hdr->addr2);
 
        if (!(info->status.rates[ridx].flags & IEEE80211_TX_RC_MCS)) {
-               u32 phy, sifs;
                const struct ieee80211_rate *rate;
                struct ieee80211_tx_rate *rates = info->status.rates;
+               u32 phy;
 
                rate = &common->sbands[info->band].bitrates[rates[ridx].idx];
                if (info->band == NL80211_BAND_2GHZ &&
@@ -218,19 +233,18 @@ void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
                else
                        phy = WLAN_RC_PHY_OFDM;
 
-               sifs = ath_dynack_get_sifs(ah, phy);
-               da->st_rbf.ts[da->st_rbf.t_rb].dur -= sifs;
+               dur -= ath_dynack_get_sifs(ah, phy);
        }
-
-       ath_dbg(common, DYNACK, "{%pM} tx sample %u [dur %u][h %u-t %u]\n",
-               hdr->addr1, da->st_rbf.ts[da->st_rbf.t_rb].tstamp,
-               da->st_rbf.ts[da->st_rbf.t_rb].dur, da->st_rbf.h_rb,
-               (da->st_rbf.t_rb + 1) % ATH_DYN_BUF);
+       da->st_rbf.ts[da->st_rbf.t_rb].dur = dur;
 
        INCR(da->st_rbf.t_rb, ATH_DYN_BUF);
        if (da->st_rbf.t_rb == da->st_rbf.h_rb)
                INCR(da->st_rbf.h_rb, ATH_DYN_BUF);
 
+       ath_dbg(common, DYNACK, "{%pM} tx sample %u [dur %u][h %u-t %u]\n",
+               hdr->addr1, ts->ts_tstamp, dur, da->st_rbf.h_rb,
+               da->st_rbf.t_rb);
+
        ath_dynack_compute_to(ah);
 
        spin_unlock_bh(&da->qlock);
@@ -251,20 +265,19 @@ void ath_dynack_sample_ack_ts(struct ath_hw *ah, struct sk_buff *skb,
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
-       if (!ath_dynack_bssidmask(ah, hdr->addr1) || !da->enabled)
+       if (!da->enabled || !ath_dynack_bssidmask(ah, hdr->addr1))
                return;
 
        spin_lock_bh(&da->qlock);
        da->ack_rbf.tstamp[da->ack_rbf.t_rb] = ts;
 
-       ath_dbg(common, DYNACK, "rx sample %u [h %u-t %u]\n",
-               da->ack_rbf.tstamp[da->ack_rbf.t_rb],
-               da->ack_rbf.h_rb, (da->ack_rbf.t_rb + 1) % ATH_DYN_BUF);
-
        INCR(da->ack_rbf.t_rb, ATH_DYN_BUF);
        if (da->ack_rbf.t_rb == da->ack_rbf.h_rb)
                INCR(da->ack_rbf.h_rb, ATH_DYN_BUF);
 
+       ath_dbg(common, DYNACK, "rx sample %u [h %u-t %u]\n",
+               ts, da->ack_rbf.h_rb, da->ack_rbf.t_rb);
+
        ath_dynack_compute_to(ah);
 
        spin_unlock_bh(&da->qlock);
index 6d7bef9..cf60224 100644 (file)
@@ -86,7 +86,8 @@ void ath_dynack_node_deinit(struct ath_hw *ah, struct ath_node *an);
 void ath_dynack_init(struct ath_hw *ah);
 void ath_dynack_sample_ack_ts(struct ath_hw *ah, struct sk_buff *skb, u32 ts);
 void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
-                            struct ath_tx_status *ts);
+                            struct ath_tx_status *ts,
+                            struct ieee80211_sta *sta);
 #else
 static inline void ath_dynack_init(struct ath_hw *ah) {}
 static inline void ath_dynack_node_init(struct ath_hw *ah,
@@ -97,7 +98,8 @@ static inline void ath_dynack_sample_ack_ts(struct ath_hw *ah,
                                            struct sk_buff *skb, u32 ts) {}
 static inline void ath_dynack_sample_tx_ts(struct ath_hw *ah,
                                           struct sk_buff *skb,
-                                          struct ath_tx_status *ts) {}
+                                          struct ath_tx_status *ts,
+                                          struct ieee80211_sta *sta) {}
 #endif
 
 #endif /* DYNACK_H */
index bb319f2..8581d91 100644 (file)
@@ -2279,6 +2279,7 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
        case NL80211_IFTYPE_ADHOC:
                REG_SET_BIT(ah, AR_TXCFG,
                            AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
+               /* fall through */
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_AP:
                REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
index 1e3b5f4..f23cb2f 100644 (file)
@@ -1251,6 +1251,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        struct ath_vif *avp = (void *)vif->drv_priv;
        struct ath_node *an = &avp->mcast_node;
 
+       mutex_lock(&sc->mutex);
        if (IS_ENABLED(CONFIG_ATH9K_TX99)) {
                if (sc->cur_chan->nvifs >= 1) {
                        mutex_unlock(&sc->mutex);
@@ -1259,8 +1260,6 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
                sc->tx99_vif = vif;
        }
 
-       mutex_lock(&sc->mutex);
-
        ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type);
        sc->cur_chan->nvifs++;
 
index 25b3fc8..f448d57 100644 (file)
@@ -629,7 +629,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                                if (bf == bf->bf_lastbf)
                                        ath_dynack_sample_tx_ts(sc->sc_ah,
                                                                bf->bf_mpdu,
-                                                               ts);
+                                                               ts, sta);
                        }
 
                        ath_tx_complete_buf(sc, bf, txq, &bf_head, sta, ts,
@@ -773,7 +773,8 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq,
                        memcpy(info->control.rates, bf->rates,
                               sizeof(info->control.rates));
                        ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok);
-                       ath_dynack_sample_tx_ts(sc->sc_ah, bf->bf_mpdu, ts);
+                       ath_dynack_sample_tx_ts(sc->sc_ah, bf->bf_mpdu, ts,
+                                               sta);
                }
                ath_tx_complete_buf(sc, bf, txq, bf_head, sta, ts, txok);
        } else
index 7050632..f7c2f19 100644 (file)
@@ -766,6 +766,7 @@ static void carl9170_rx_untie_data(struct ar9170 *ar, u8 *buf, int len)
 
                        goto drop;
                }
+               /* fall through */
 
        case AR9170_RX_STATUS_MPDU_MIDDLE:
                /*  These are just data + mac status */
index 8c75651..2407931 100644 (file)
@@ -830,10 +830,12 @@ static bool carl9170_tx_rts_check(struct ar9170 *ar,
        case CARL9170_ERP_AUTO:
                if (ampdu)
                        break;
+               /* fall through */
 
        case CARL9170_ERP_MAC80211:
                if (!(rate->flags & IEEE80211_TX_RC_USE_RTS_CTS))
                        break;
+               /* fall through */
 
        case CARL9170_ERP_RTS:
                if (likely(!multi))
@@ -854,6 +856,7 @@ static bool carl9170_tx_cts_check(struct ar9170 *ar,
        case CARL9170_ERP_MAC80211:
                if (!(rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT))
                        break;
+               /* fall through */
 
        case CARL9170_ERP_CTS:
                return true;
index d18e81f..9b2f9f5 100644 (file)
@@ -51,6 +51,19 @@ static struct ieee80211_channel wil_60ghz_channels[] = {
        CHAN60G(4, 0),
 };
 
+static void
+wil_memdup_ie(u8 **pdst, size_t *pdst_len, const u8 *src, size_t src_len)
+{
+       kfree(*pdst);
+       *pdst = NULL;
+       *pdst_len = 0;
+       if (src_len > 0) {
+               *pdst = kmemdup(src, src_len, GFP_KERNEL);
+               if (*pdst)
+                       *pdst_len = src_len;
+       }
+}
+
 static int wil_num_supported_channels(struct wil6210_priv *wil)
 {
        int num_channels = ARRAY_SIZE(wil_60ghz_channels);
@@ -1441,11 +1454,19 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
 
        rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
                                params->key, key_usage);
-       if (!rc && !IS_ERR(cs))
+       if (!rc && !IS_ERR(cs)) {
+               /* update local storage used for AP recovery */
+               if (key_usage == WMI_KEY_USE_TX_GROUP && params->key &&
+                   params->key_len <= WMI_MAX_KEY_LEN) {
+                       vif->gtk_index = key_index;
+                       memcpy(vif->gtk, params->key, params->key_len);
+                       vif->gtk_len = params->key_len;
+               }
                /* in FT set crypto will take place upon receiving
                 * WMI_RING_EN_EVENTID event
                 */
                wil_set_crypto_rx(key_index, key_usage, cs, params);
+       }
 
        return rc;
 }
@@ -1634,6 +1655,14 @@ static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
        u16 len = 0, proberesp_len = 0;
        u8 *ies = NULL, *proberesp;
 
+       /* update local storage used for AP recovery */
+       wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, bcon->probe_resp,
+                     bcon->probe_resp_len);
+       wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len,
+                     bcon->proberesp_ies, bcon->proberesp_ies_len);
+       wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len,
+                     bcon->assocresp_ies, bcon->assocresp_ies_len);
+
        proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
                                                    bcon->probe_resp_len,
                                                    &proberesp_len);
@@ -1735,6 +1764,9 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
        vif->channel = chan;
        vif->hidden_ssid = hidden_ssid;
        vif->pbss = pbss;
+       vif->bi = bi;
+       memcpy(vif->ssid, ssid, ssid_len);
+       vif->ssid_len = ssid_len;
 
        netif_carrier_on(ndev);
        if (!wil_has_other_active_ifaces(wil, ndev, false, true))
@@ -1761,11 +1793,64 @@ out:
        return rc;
 }
 
+void wil_cfg80211_ap_recovery(struct wil6210_priv *wil)
+{
+       int rc, i;
+       struct wiphy *wiphy = wil_to_wiphy(wil);
+
+       for (i = 0; i < wil->max_vifs; i++) {
+               struct wil6210_vif *vif = wil->vifs[i];
+               struct net_device *ndev;
+               struct cfg80211_beacon_data bcon = {};
+               struct key_params key_params = {};
+
+               if (!vif || vif->ssid_len == 0)
+                       continue;
+
+               ndev = vif_to_ndev(vif);
+               bcon.proberesp_ies = vif->proberesp_ies;
+               bcon.assocresp_ies = vif->assocresp_ies;
+               bcon.probe_resp = vif->proberesp;
+               bcon.proberesp_ies_len = vif->proberesp_ies_len;
+               bcon.assocresp_ies_len = vif->assocresp_ies_len;
+               bcon.probe_resp_len = vif->proberesp_len;
+
+               wil_info(wil,
+                        "AP (vif %d) recovery: privacy %d, bi %d, channel %d, hidden %d, pbss %d\n",
+                        i, vif->privacy, vif->bi, vif->channel,
+                        vif->hidden_ssid, vif->pbss);
+               wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
+                                 vif->ssid, vif->ssid_len, true);
+               rc = _wil_cfg80211_start_ap(wiphy, ndev,
+                                           vif->ssid, vif->ssid_len,
+                                           vif->privacy, vif->bi,
+                                           vif->channel, &bcon,
+                                           vif->hidden_ssid, vif->pbss);
+               if (rc) {
+                       wil_err(wil, "vif %d recovery failed (%d)\n", i, rc);
+                       continue;
+               }
+
+               if (!vif->privacy || vif->gtk_len == 0)
+                       continue;
+
+               key_params.key = vif->gtk;
+               key_params.key_len = vif->gtk_len;
+               key_params.seq_len = IEEE80211_GCMP_PN_LEN;
+               rc = wil_cfg80211_add_key(wiphy, ndev, vif->gtk_index, false,
+                                         NULL, &key_params);
+               if (rc)
+                       wil_err(wil, "vif %d recovery add key failed (%d)\n",
+                               i, rc);
+       }
+}
+
 static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
                                      struct net_device *ndev,
                                      struct cfg80211_beacon_data *bcon)
 {
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       struct wireless_dev *wdev = ndev->ieee80211_ptr;
        struct wil6210_vif *vif = ndev_to_vif(ndev);
        int rc;
        u32 privacy = 0;
@@ -1778,15 +1863,16 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
                             bcon->tail_len))
                privacy = 1;
 
+       memcpy(vif->ssid, wdev->ssid, wdev->ssid_len);
+       vif->ssid_len = wdev->ssid_len;
+
        /* in case privacy has changed, need to restart the AP */
        if (vif->privacy != privacy) {
-               struct wireless_dev *wdev = ndev->ieee80211_ptr;
-
                wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
                             vif->privacy, privacy);
 
-               rc = _wil_cfg80211_start_ap(wiphy, ndev, wdev->ssid,
-                                           wdev->ssid_len, privacy,
+               rc = _wil_cfg80211_start_ap(wiphy, ndev, vif->ssid,
+                                           vif->ssid_len, privacy,
                                            wdev->beacon_interval,
                                            vif->channel, bcon,
                                            vif->hidden_ssid,
@@ -1876,6 +1962,12 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
 
        wmi_pcp_stop(vif);
        clear_bit(wil_vif_ft_roam, vif->status);
+       vif->ssid_len = 0;
+       wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, NULL, 0);
+       wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len, NULL, 0);
+       wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len, NULL, 0);
+       memset(vif->gtk, 0, WMI_MAX_KEY_LEN);
+       vif->gtk_len = 0;
 
        if (last)
                __wil_down(wil);
@@ -1923,7 +2015,7 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy,
                     params->mac, params->reason_code, vif->mid);
 
        mutex_lock(&wil->mutex);
-       wil6210_disconnect(vif, params->mac, params->reason_code, false);
+       wil6210_disconnect(vif, params->mac, params->reason_code);
        mutex_unlock(&wil->mutex);
 
        return 0;
index aa50813..835c902 100644 (file)
@@ -124,7 +124,7 @@ static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil,
        seq_puts(s, "}\n");
 }
 
-static int wil_ring_debugfs_show(struct seq_file *s, void *data)
+static int ring_show(struct seq_file *s, void *data)
 {
        uint i;
        struct wil6210_priv *wil = s->private;
@@ -183,18 +183,7 @@ static int wil_ring_debugfs_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int wil_ring_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_ring_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_ring = {
-       .open           = wil_ring_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(ring);
 
 static void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil,
                            struct wil_status_ring *sring)
@@ -240,7 +229,7 @@ static void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil,
        seq_puts(s, "}\n");
 }
 
-static int wil_srings_debugfs_show(struct seq_file *s, void *data)
+static int srings_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        int i = 0;
@@ -251,18 +240,7 @@ static int wil_srings_debugfs_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int wil_srings_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_srings_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_srings = {
-       .open           = wil_srings_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(srings);
 
 static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
                            const char *prefix)
@@ -348,7 +326,7 @@ static void wil_print_mbox_ring(struct seq_file *s, const char *prefix,
        wil_halp_unvote(wil);
 }
 
-static int wil_mbox_debugfs_show(struct seq_file *s, void *data)
+static int mbox_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        int ret;
@@ -366,18 +344,7 @@ static int wil_mbox_debugfs_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int wil_mbox_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_mbox_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_mbox = {
-       .open           = wil_mbox_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(mbox);
 
 static int wil_debugfs_iomem_x32_set(void *data, u64 val)
 {
@@ -624,7 +591,7 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
        return 0;
 }
 
-static int wil_memread_debugfs_show(struct seq_file *s, void *data)
+static int memread_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        void __iomem *a;
@@ -645,18 +612,7 @@ static int wil_memread_debugfs_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int wil_memread_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_memread_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_memread = {
-       .open           = wil_memread_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(memread);
 
 static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
                                    size_t count, loff_t *ppos)
@@ -664,10 +620,10 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
        enum { max_count = 4096 };
        struct wil_blob_wrapper *wil_blob = file->private_data;
        struct wil6210_priv *wil = wil_blob->wil;
-       loff_t pos = *ppos;
+       loff_t aligned_pos, pos = *ppos;
        size_t available = wil_blob->blob.size;
        void *buf;
-       size_t ret;
+       size_t unaligned_bytes, aligned_count, ret;
        int rc;
 
        if (test_bit(wil_status_suspending, wil_blob->wil->status) ||
@@ -685,7 +641,12 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
        if (count > max_count)
                count = max_count;
 
-       buf = kmalloc(count, GFP_KERNEL);
+       /* set pos to 4 bytes aligned */
+       unaligned_bytes = pos % 4;
+       aligned_pos = pos - unaligned_bytes;
+       aligned_count = count + unaligned_bytes;
+
+       buf = kmalloc(aligned_count, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
@@ -696,9 +657,9 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
        }
 
        wil_memcpy_fromio_32(buf, (const void __iomem *)
-                            wil_blob->blob.data + pos, count);
+                            wil_blob->blob.data + aligned_pos, aligned_count);
 
-       ret = copy_to_user(user_buf, buf, count);
+       ret = copy_to_user(user_buf, buf + unaligned_bytes, count);
 
        wil_pm_runtime_put(wil);
 
@@ -962,6 +923,8 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
        int rc;
        void *frame;
 
+       memset(&params, 0, sizeof(params));
+
        if (!len)
                return -EINVAL;
 
@@ -1053,7 +1016,7 @@ static void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb)
 }
 
 /*---------Tx/Rx descriptor------------*/
-static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
+static int txdesc_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        struct wil_ring *ring;
@@ -1146,21 +1109,10 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int wil_txdesc_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_txdesc_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_txdesc = {
-       .open           = wil_txdesc_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(txdesc);
 
 /*---------Tx/Rx status message------------*/
-static int wil_status_msg_debugfs_show(struct seq_file *s, void *data)
+static int status_msg_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        int sring_idx = dbg_sring_index;
@@ -1202,19 +1154,7 @@ static int wil_status_msg_debugfs_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int wil_status_msg_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_status_msg_debugfs_show,
-                          inode->i_private);
-}
-
-static const struct file_operations fops_status_msg = {
-       .open           = wil_status_msg_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(status_msg);
 
 static int wil_print_rx_buff(struct seq_file *s, struct list_head *lh)
 {
@@ -1232,7 +1172,7 @@ static int wil_print_rx_buff(struct seq_file *s, struct list_head *lh)
        return i;
 }
 
-static int wil_rx_buff_mgmt_debugfs_show(struct seq_file *s, void *data)
+static int rx_buff_mgmt_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        struct wil_rx_buff_mgmt *rbm = &wil->rx_buff_mgmt;
@@ -1257,19 +1197,7 @@ static int wil_rx_buff_mgmt_debugfs_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int wil_rx_buff_mgmt_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_rx_buff_mgmt_debugfs_show,
-                          inode->i_private);
-}
-
-static const struct file_operations fops_rx_buff_mgmt = {
-       .open           = wil_rx_buff_mgmt_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(rx_buff_mgmt);
 
 /*---------beamforming------------*/
 static char *wil_bfstatus_str(u32 status)
@@ -1299,7 +1227,7 @@ static bool is_all_zeros(void * const x_, size_t sz)
        return true;
 }
 
-static int wil_bf_debugfs_show(struct seq_file *s, void *data)
+static int bf_show(struct seq_file *s, void *data)
 {
        int rc;
        int i;
@@ -1353,18 +1281,7 @@ static int wil_bf_debugfs_show(struct seq_file *s, void *data)
        }
        return 0;
 }
-
-static int wil_bf_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_bf_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_bf = {
-       .open           = wil_bf_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(bf);
 
 /*---------temp------------*/
 static void print_temp(struct seq_file *s, const char *prefix, s32 t)
@@ -1381,7 +1298,7 @@ static void print_temp(struct seq_file *s, const char *prefix, s32 t)
        }
 }
 
-static int wil_temp_debugfs_show(struct seq_file *s, void *data)
+static int temp_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        s32 t_m, t_r;
@@ -1397,21 +1314,10 @@ static int wil_temp_debugfs_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int wil_temp_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_temp_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_temp = {
-       .open           = wil_temp_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(temp);
 
 /*---------freq------------*/
-static int wil_freq_debugfs_show(struct seq_file *s, void *data)
+static int freq_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr;
@@ -1421,21 +1327,10 @@ static int wil_freq_debugfs_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int wil_freq_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_freq_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_freq = {
-       .open           = wil_freq_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(freq);
 
 /*---------link------------*/
-static int wil_link_debugfs_show(struct seq_file *s, void *data)
+static int link_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        struct station_info *sinfo;
@@ -1487,21 +1382,10 @@ out:
        kfree(sinfo);
        return rc;
 }
-
-static int wil_link_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_link_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_link = {
-       .open           = wil_link_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(link);
 
 /*---------info------------*/
-static int wil_info_debugfs_show(struct seq_file *s, void *data)
+static int info_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        struct net_device *ndev = wil->main_ndev;
@@ -1536,18 +1420,7 @@ static int wil_info_debugfs_show(struct seq_file *s, void *data)
 #undef CHECK_QSTATE
        return 0;
 }
-
-static int wil_info_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_info_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_info = {
-       .open           = wil_info_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(info);
 
 /*---------recovery------------*/
 /* mode = [manual|auto]
@@ -1663,7 +1536,7 @@ has_keys:
        seq_puts(s, "\n");
 }
 
-static int wil_sta_debugfs_show(struct seq_file *s, void *data)
+static int sta_show(struct seq_file *s, void *data)
 __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
 {
        struct wil6210_priv *wil = s->private;
@@ -1745,20 +1618,9 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
 
        return 0;
 }
+DEFINE_SHOW_ATTRIBUTE(sta);
 
-static int wil_sta_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_sta_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_sta = {
-       .open           = wil_sta_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
-
-static int wil_mids_debugfs_show(struct seq_file *s, void *data)
+static int mids_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
        struct wil6210_vif *vif;
@@ -1781,18 +1643,7 @@ static int wil_mids_debugfs_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int wil_mids_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wil_mids_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations fops_mids = {
-       .open           = wil_mids_seq_open,
-       .release        = single_release,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+DEFINE_SHOW_ATTRIBUTE(mids);
 
 static int wil_tx_latency_debugfs_show(struct seq_file *s, void *data)
 __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
@@ -2436,23 +2287,23 @@ static const struct {
        umode_t mode;
        const struct file_operations *fops;
 } dbg_files[] = {
-       {"mbox",        0444,           &fops_mbox},
-       {"rings",       0444,           &fops_ring},
-       {"stations", 0444,              &fops_sta},
-       {"mids",        0444,           &fops_mids},
-       {"desc",        0444,           &fops_txdesc},
-       {"bf",          0444,           &fops_bf},
-       {"mem_val",     0644,           &fops_memread},
+       {"mbox",        0444,           &mbox_fops},
+       {"rings",       0444,           &ring_fops},
+       {"stations", 0444,              &sta_fops},
+       {"mids",        0444,           &mids_fops},
+       {"desc",        0444,           &txdesc_fops},
+       {"bf",          0444,           &bf_fops},
+       {"mem_val",     0644,           &memread_fops},
        {"rxon",        0244,           &fops_rxon},
        {"tx_mgmt",     0244,           &fops_txmgmt},
        {"wmi_send", 0244,              &fops_wmi},
        {"back",        0644,           &fops_back},
        {"pmccfg",      0644,           &fops_pmccfg},
        {"pmcdata",     0444,           &fops_pmcdata},
-       {"temp",        0444,           &fops_temp},
-       {"freq",        0444,           &fops_freq},
-       {"link",        0444,           &fops_link},
-       {"info",        0444,           &fops_info},
+       {"temp",        0444,           &temp_fops},
+       {"freq",        0444,           &freq_fops},
+       {"link",        0444,           &link_fops},
+       {"info",        0444,           &info_fops},
        {"recovery", 0644,              &fops_recovery},
        {"led_cfg",     0644,           &fops_led_cfg},
        {"led_blink_time",      0644,   &fops_led_blink_time},
@@ -2460,9 +2311,9 @@ static const struct {
        {"fw_version",  0444,           &fops_fw_version},
        {"suspend_stats",       0644,   &fops_suspend_stats},
        {"compressed_rx_status", 0644,  &fops_compressed_rx_status},
-       {"srings",      0444,           &fops_srings},
-       {"status_msg",  0444,           &fops_status_msg},
-       {"rx_buff_mgmt",        0444,   &fops_rx_buff_mgmt},
+       {"srings",      0444,           &srings_fops},
+       {"status_msg",  0444,           &status_msg_fops},
+       {"rx_buff_mgmt",        0444,   &rx_buff_mgmt_fops},
        {"tx_latency",  0644,           &fops_tx_latency},
        {"link_stats",  0644,           &fops_link_stats},
        {"link_stats_global",   0644,   &fops_link_stats_global},
index 398900a..5b7de00 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/moduleparam.h>
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
 
 #include "wil6210.h"
 #include "txrx.h"
@@ -80,7 +81,7 @@ static const struct kernel_param_ops mtu_max_ops = {
 module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, 0444);
 MODULE_PARM_DESC(mtu_max, " Max MTU value.");
 
-static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
+static uint rx_ring_order;
 static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
 static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT;
 
@@ -214,8 +215,21 @@ static void wil_ring_fini_tx(struct wil6210_priv *wil, int id)
        wil->txrx_ops.ring_fini_tx(wil, ring);
 }
 
-static void wil_disconnect_cid(struct wil6210_vif *vif, int cid,
-                              u16 reason_code, bool from_event)
+static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
+{
+       int i;
+
+       for (i = 0; i < WIL6210_MAX_CID; i++) {
+               if (wil->sta[i].mid == mid &&
+                   wil->sta[i].status == wil_sta_connected)
+                       return true;
+       }
+
+       return false;
+}
+
+static void wil_disconnect_cid_complete(struct wil6210_vif *vif, int cid,
+                                       u16 reason_code)
 __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
 {
        uint i;
@@ -226,24 +240,14 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
        int min_ring_id = wil_get_min_tx_ring_id(wil);
 
        might_sleep();
-       wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n",
+       wil_dbg_misc(wil,
+                    "disconnect_cid_complete: CID %d, MID %d, status %d\n",
                     cid, sta->mid, sta->status);
-       /* inform upper/lower layers */
+       /* inform upper layers */
        if (sta->status != wil_sta_unused) {
                if (vif->mid != sta->mid) {
                        wil_err(wil, "STA MID mismatch with VIF MID(%d)\n",
                                vif->mid);
-                       /* let FW override sta->mid but be more strict with
-                        * user space requests
-                        */
-                       if (!from_event)
-                               return;
-               }
-               if (!from_event) {
-                       bool del_sta = (wdev->iftype == NL80211_IFTYPE_AP) ?
-                                               disable_ap_sme : false;
-                       wmi_disconnect_sta(vif, sta->addr, reason_code,
-                                          true, del_sta);
                }
 
                switch (wdev->iftype) {
@@ -283,36 +287,20 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
        sta->stats.tx_latency_min_us = U32_MAX;
 }
 
-static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
-               if (wil->sta[i].mid == mid &&
-                   wil->sta[i].status == wil_sta_connected)
-                       return true;
-       }
-
-       return false;
-}
-
-static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
-                               u16 reason_code, bool from_event)
+static void _wil6210_disconnect_complete(struct wil6210_vif *vif,
+                                        const u8 *bssid, u16 reason_code)
 {
        struct wil6210_priv *wil = vif_to_wil(vif);
        int cid = -ENOENT;
        struct net_device *ndev;
        struct wireless_dev *wdev;
 
-       if (unlikely(!vif))
-               return;
-
        ndev = vif_to_ndev(vif);
        wdev = vif_to_wdev(vif);
 
        might_sleep();
-       wil_info(wil, "bssid=%pM, reason=%d, ev%s\n", bssid,
-                reason_code, from_event ? "+" : "-");
+       wil_info(wil, "disconnect_complete: bssid=%pM, reason=%d\n",
+                bssid, reason_code);
 
        /* Cases are:
         * - disconnect single STA, still connected
@@ -327,14 +315,15 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
        if (bssid && !is_broadcast_ether_addr(bssid) &&
            !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
                cid = wil_find_cid(wil, vif->mid, bssid);
-               wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
+               wil_dbg_misc(wil,
+                            "Disconnect complete %pM, CID=%d, reason=%d\n",
                             bssid, cid, reason_code);
                if (cid >= 0) /* disconnect 1 peer */
-                       wil_disconnect_cid(vif, cid, reason_code, from_event);
+                       wil_disconnect_cid_complete(vif, cid, reason_code);
        } else { /* all */
-               wil_dbg_misc(wil, "Disconnect all\n");
+               wil_dbg_misc(wil, "Disconnect complete all\n");
                for (cid = 0; cid < WIL6210_MAX_CID; cid++)
-                       wil_disconnect_cid(vif, cid, reason_code, from_event);
+                       wil_disconnect_cid_complete(vif, cid, reason_code);
        }
 
        /* link state */
@@ -380,6 +369,82 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
        }
 }
 
+static int wil_disconnect_cid(struct wil6210_vif *vif, int cid,
+                             u16 reason_code)
+{
+       struct wil6210_priv *wil = vif_to_wil(vif);
+       struct wireless_dev *wdev = vif_to_wdev(vif);
+       struct wil_sta_info *sta = &wil->sta[cid];
+       bool del_sta = false;
+
+       might_sleep();
+       wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n",
+                    cid, sta->mid, sta->status);
+
+       if (sta->status == wil_sta_unused)
+               return 0;
+
+       if (vif->mid != sta->mid) {
+               wil_err(wil, "STA MID mismatch with VIF MID(%d)\n", vif->mid);
+               return -EINVAL;
+       }
+
+       /* inform lower layers */
+       if (wdev->iftype == NL80211_IFTYPE_AP && disable_ap_sme)
+               del_sta = true;
+
+       /* disconnect by sending command disconnect/del_sta and wait
+        * synchronously for WMI_DISCONNECT_EVENTID event.
+        */
+       return wmi_disconnect_sta(vif, sta->addr, reason_code, del_sta);
+}
+
+static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
+                               u16 reason_code)
+{
+       struct wil6210_priv *wil;
+       struct net_device *ndev;
+       int cid = -ENOENT;
+
+       if (unlikely(!vif))
+               return;
+
+       wil = vif_to_wil(vif);
+       ndev = vif_to_ndev(vif);
+
+       might_sleep();
+       wil_info(wil, "disconnect bssid=%pM, reason=%d\n", bssid, reason_code);
+
+       /* Cases are:
+        * - disconnect single STA, still connected
+        * - disconnect single STA, already disconnected
+        * - disconnect all
+        *
+        * For "disconnect all", there are 3 options:
+        * - bssid == NULL
+        * - bssid is broadcast address (ff:ff:ff:ff:ff:ff)
+        * - bssid is our MAC address
+        */
+       if (bssid && !is_broadcast_ether_addr(bssid) &&
+           !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
+               cid = wil_find_cid(wil, vif->mid, bssid);
+               wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
+                            bssid, cid, reason_code);
+               if (cid >= 0) /* disconnect 1 peer */
+                       wil_disconnect_cid(vif, cid, reason_code);
+       } else { /* all */
+               wil_dbg_misc(wil, "Disconnect all\n");
+               for (cid = 0; cid < WIL6210_MAX_CID; cid++)
+                       wil_disconnect_cid(vif, cid, reason_code);
+       }
+
+       /* call event handler manually after processing wmi_call,
+        * to avoid deadlock - disconnect event handler acquires
+        * wil->mutex while it is already held here
+        */
+       _wil6210_disconnect_complete(vif, bssid, reason_code);
+}
+
 void wil_disconnect_worker(struct work_struct *work)
 {
        struct wil6210_vif *vif = container_of(work,
@@ -485,10 +550,11 @@ static void wil_fw_error_worker(struct work_struct *work)
        if (wil_wait_for_recovery(wil) != 0)
                return;
 
+       rtnl_lock();
        mutex_lock(&wil->mutex);
        /* Needs adaptation for multiple VIFs
         * need to go over all VIFs and consider the appropriate
-        * recovery.
+        * recovery because each one can have different iftype.
         */
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
@@ -500,15 +566,24 @@ static void wil_fw_error_worker(struct work_struct *work)
                break;
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
-               wil_info(wil, "No recovery for AP-like interface\n");
-               /* recovery in these modes is done by upper layers */
+               if (no_fw_recovery) /* upper layers do recovery */
+                       break;
+               /* silent recovery, upper layers will see disconnect */
+               __wil_down(wil);
+               __wil_up(wil);
+               mutex_unlock(&wil->mutex);
+               wil_cfg80211_ap_recovery(wil);
+               mutex_lock(&wil->mutex);
+               wil_info(wil, "... completed\n");
                break;
        default:
                wil_err(wil, "No recovery - unknown interface type %d\n",
                        wdev->iftype);
                break;
        }
+
        mutex_unlock(&wil->mutex);
+       rtnl_unlock();
 }
 
 static int wil_find_free_ring(struct wil6210_priv *wil)
@@ -694,20 +769,41 @@ void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps)
  * @vif: virtual interface context
  * @bssid: peer to disconnect, NULL to disconnect all
  * @reason_code: Reason code for the Disassociation frame
- * @from_event: whether is invoked from FW event handler
  *
- * Disconnect and release associated resources. If invoked not from the
- * FW event handler, issue WMI command(s) to trigger MAC disconnect.
+ * Disconnect and release associated resources. Issue WMI
+ * command(s) to trigger MAC disconnect. When command was issued
+ * successfully, call the wil6210_disconnect_complete function
+ * to handle the event synchronously
  */
 void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
-                       u16 reason_code, bool from_event)
+                       u16 reason_code)
+{
+       struct wil6210_priv *wil = vif_to_wil(vif);
+
+       wil_dbg_misc(wil, "disconnecting\n");
+
+       del_timer_sync(&vif->connect_timer);
+       _wil6210_disconnect(vif, bssid, reason_code);
+}
+
+/**
+ * wil6210_disconnect_complete - handle disconnect event
+ * @vif: virtual interface context
+ * @bssid: peer to disconnect, NULL to disconnect all
+ * @reason_code: Reason code for the Disassociation frame
+ *
+ * Release associated resources and indicate upper layers the
+ * connection is terminated.
+ */
+void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid,
+                                u16 reason_code)
 {
        struct wil6210_priv *wil = vif_to_wil(vif);
 
-       wil_dbg_misc(wil, "disconnect\n");
+       wil_dbg_misc(wil, "got disconnect\n");
 
        del_timer_sync(&vif->connect_timer);
-       _wil6210_disconnect(vif, bssid, reason_code, from_event);
+       _wil6210_disconnect_complete(vif, bssid, reason_code);
 }
 
 void wil_priv_deinit(struct wil6210_priv *wil)
@@ -998,10 +1094,13 @@ static int wil_target_reset(struct wil6210_priv *wil, int no_flash)
 
        wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name);
 
-       /* Clear MAC link up */
-       wil_s(wil, RGF_HP_CTRL, BIT(15));
-       wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_HPAL_PERST_FROM_PAD);
-       wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST);
+       if (wil->hw_version < HW_VER_TALYN) {
+               /* Clear MAC link up */
+               wil_s(wil, RGF_HP_CTRL, BIT(15));
+               wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0,
+                     BIT_HPAL_PERST_FROM_PAD);
+               wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST);
+       }
 
        wil_halt_cpu(wil);
 
@@ -1398,8 +1497,15 @@ static void wil_pre_fw_config(struct wil6210_priv *wil)
        wil6210_clear_irq(wil);
        /* CAF_ICR - clear and mask */
        /* it is W1C, clear by writing back same value */
-       wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
-       wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
+       if (wil->hw_version < HW_VER_TALYN_MB) {
+               wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
+               wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
+       } else {
+               wil_s(wil,
+                     RGF_CAF_ICR_TALYN_MB + offsetof(struct RGF_ICR, ICR), 0);
+               wil_w(wil, RGF_CAF_ICR_TALYN_MB +
+                     offsetof(struct RGF_ICR, IMV), ~0);
+       }
        /* clear PAL_UNIT_ICR (potential D0->D3 leftover)
         * In Talyn-MB host cannot access this register due to
         * access control, hence PAL_UNIT_ICR is cleared by the FW
@@ -1511,7 +1617,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
                if (vif) {
                        cancel_work_sync(&vif->disconnect_worker);
                        wil6210_disconnect(vif, NULL,
-                                          WLAN_REASON_DEAUTH_LEAVING, false);
+                                          WLAN_REASON_DEAUTH_LEAVING);
                }
        }
        wil_bcast_fini_all(wil);
@@ -1681,7 +1787,12 @@ int __wil_up(struct wil6210_priv *wil)
                return rc;
 
        /* Rx RING. After MAC and beacon */
-       rc = wil->txrx_ops.rx_init(wil, 1 << rx_ring_order);
+       if (rx_ring_order == 0)
+               rx_ring_order = wil->hw_version < HW_VER_TALYN_MB ?
+                       WIL_RX_RING_SIZE_ORDER_DEFAULT :
+                       WIL_RX_RING_SIZE_ORDER_TALYN_DEFAULT;
+
+       rc = wil->txrx_ops.rx_init(wil, rx_ring_order);
        if (rc)
                return rc;
 
index 7a78a06..b4e0eb1 100644 (file)
@@ -345,8 +345,7 @@ wil_vif_alloc(struct wil6210_priv *wil, const char *name,
        ndev->ieee80211_ptr = wdev;
        ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
                            NETIF_F_SG | NETIF_F_GRO |
-                           NETIF_F_TSO | NETIF_F_TSO6 |
-                           NETIF_F_RXHASH;
+                           NETIF_F_TSO | NETIF_F_TSO6;
 
        ndev->features |= ndev->hw_features;
        SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
@@ -513,7 +512,7 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid)
        }
 
        mutex_lock(&wil->mutex);
-       wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
+       wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING);
        mutex_unlock(&wil->mutex);
 
        ndev = vif_to_ndev(vif);
index cc5f263..3e1c831 100644 (file)
@@ -743,14 +743,6 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
 
        stats = &wil->sta[cid].stats;
 
-       if (ndev->features & NETIF_F_RXHASH)
-               /* fake L4 to ensure it won't be re-calculated later
-                * set hash to any non-zero value to activate rps
-                * mechanism, core will be chosen according
-                * to user-level rps configuration.
-                */
-               skb_set_hash(skb, 1, PKT_HASH_TYPE_L4);
-
        skb_orphan(skb);
 
        if (security && (wil->txrx_ops.rx_crypto_check(wil, skb) != 0)) {
@@ -880,7 +872,7 @@ static void wil_rx_buf_len_init(struct wil6210_priv *wil)
        }
 }
 
-static int wil_rx_init(struct wil6210_priv *wil, u16 size)
+static int wil_rx_init(struct wil6210_priv *wil, uint order)
 {
        struct wil_ring *vring = &wil->ring_rx;
        int rc;
@@ -894,7 +886,7 @@ static int wil_rx_init(struct wil6210_priv *wil, u16 size)
 
        wil_rx_buf_len_init(wil);
 
-       vring->size = size;
+       vring->size = 1 << order;
        vring->is_rx = true;
        rc = wil_vring_alloc(wil, vring);
        if (rc)
@@ -1403,6 +1395,8 @@ found:
                        wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
                        wil_set_da_for_vring(wil, skb2, i);
                        wil_tx_ring(wil, vif, v2, skb2);
+                       /* successful call to wil_tx_ring takes skb2 ref */
+                       dev_kfree_skb_any(skb2);
                } else {
                        wil_err(wil, "skb_copy failed\n");
                }
index 2bbae75..05a8348 100644 (file)
@@ -160,7 +160,7 @@ static int wil_ring_alloc_skb_edma(struct wil6210_priv *wil,
                                   struct wil_ring *ring, u32 i)
 {
        struct device *dev = wil_to_dev(wil);
-       unsigned int sz = ALIGN(wil->rx_buf_len, 4);
+       unsigned int sz = wil->rx_buf_len;
        dma_addr_t pa;
        u16 buff_id;
        struct list_head *active = &wil->rx_buff_mgmt.active;
@@ -234,9 +234,10 @@ static int wil_rx_refill_edma(struct wil6210_priv *wil)
        struct wil_ring *ring = &wil->ring_rx;
        u32 next_head;
        int rc = 0;
-       u32 swtail = *ring->edma_rx_swtail.va;
+       ring->swtail = *ring->edma_rx_swtail.va;
 
-       for (; next_head = wil_ring_next_head(ring), (next_head != swtail);
+       for (; next_head = wil_ring_next_head(ring),
+            (next_head != ring->swtail);
             ring->swhead = next_head) {
                rc = wil_ring_alloc_skb_edma(wil, ring, ring->swhead);
                if (unlikely(rc)) {
@@ -264,43 +265,26 @@ static void wil_move_all_rx_buff_to_free_list(struct wil6210_priv *wil,
                                              struct wil_ring *ring)
 {
        struct device *dev = wil_to_dev(wil);
-       u32 next_tail;
-       u32 swhead = (ring->swhead + 1) % ring->size;
+       struct list_head *active = &wil->rx_buff_mgmt.active;
        dma_addr_t pa;
-       u16 dmalen;
 
-       for (; next_tail = wil_ring_next_tail(ring), (next_tail != swhead);
-            ring->swtail = next_tail) {
-               struct wil_rx_enhanced_desc dd, *d = &dd;
-               struct wil_rx_enhanced_desc *_d =
-                       (struct wil_rx_enhanced_desc *)
-                       &ring->va[ring->swtail].rx.enhanced;
-               struct sk_buff *skb;
-               u16 buff_id;
+       while (!list_empty(active)) {
+               struct wil_rx_buff *rx_buff =
+                       list_first_entry(active, struct wil_rx_buff, list);
+               struct sk_buff *skb = rx_buff->skb;
 
-               *d = *_d;
-
-               /* Extract the SKB from the rx_buff management array */
-               buff_id = __le16_to_cpu(d->mac.buff_id);
-               if (buff_id >= wil->rx_buff_mgmt.size) {
-                       wil_err(wil, "invalid buff_id %d\n", buff_id);
-                       continue;
-               }
-               skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb;
-               wil->rx_buff_mgmt.buff_arr[buff_id].skb = NULL;
                if (unlikely(!skb)) {
-                       wil_err(wil, "No Rx skb at buff_id %d\n", buff_id);
+                       wil_err(wil, "No Rx skb at buff_id %d\n", rx_buff->id);
                } else {
-                       pa = wil_rx_desc_get_addr_edma(&d->dma);
-                       dmalen = le16_to_cpu(d->dma.length);
-                       dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
-
+                       rx_buff->skb = NULL;
+                       memcpy(&pa, skb->cb, sizeof(pa));
+                       dma_unmap_single(dev, pa, wil->rx_buf_len,
+                                        DMA_FROM_DEVICE);
                        kfree_skb(skb);
                }
 
                /* Move the buffer from the active to the free list */
-               list_move(&wil->rx_buff_mgmt.buff_arr[buff_id].list,
-                         &wil->rx_buff_mgmt.free);
+               list_move(&rx_buff->list, &wil->rx_buff_mgmt.free);
        }
 }
 
@@ -357,8 +341,8 @@ static int wil_init_rx_sring(struct wil6210_priv *wil,
        struct wil_status_ring *sring = &wil->srings[ring_id];
        int rc;
 
-       wil_dbg_misc(wil, "init RX sring: size=%u, ring_id=%u\n", sring->size,
-                    ring_id);
+       wil_dbg_misc(wil, "init RX sring: size=%u, ring_id=%u\n",
+                    status_ring_size, ring_id);
 
        memset(&sring->rx_data, 0, sizeof(sring->rx_data));
 
@@ -602,20 +586,20 @@ static bool wil_is_rx_idle_edma(struct wil6210_priv *wil)
 
 static void wil_rx_buf_len_init_edma(struct wil6210_priv *wil)
 {
+       /* RX buffer size must be aligned to 4 bytes */
        wil->rx_buf_len = rx_large_buf ?
                WIL_MAX_ETH_MTU : WIL_EDMA_RX_BUF_LEN_DEFAULT;
 }
 
-static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size)
+static int wil_rx_init_edma(struct wil6210_priv *wil, uint desc_ring_order)
 {
-       u16 status_ring_size;
+       u16 status_ring_size, desc_ring_size = 1 << desc_ring_order;
        struct wil_ring *ring = &wil->ring_rx;
        int rc;
        size_t elem_size = wil->use_compressed_rx_status ?
                sizeof(struct wil_rx_status_compressed) :
                sizeof(struct wil_rx_status_extended);
        int i;
-       u16 max_rx_pl_per_desc;
 
        /* In SW reorder one must use extended status messages */
        if (wil->use_compressed_rx_status && !wil->use_rx_hw_reordering) {
@@ -623,7 +607,12 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size)
                        "compressed RX status cannot be used with SW reorder\n");
                return -EINVAL;
        }
-
+       if (wil->rx_status_ring_order <= desc_ring_order)
+               /* make sure sring is larger than desc ring */
+               wil->rx_status_ring_order = desc_ring_order + 1;
+       if (wil->rx_buff_id_count <= desc_ring_size)
+               /* make sure we will not run out of buff_ids */
+               wil->rx_buff_id_count = desc_ring_size + 512;
        if (wil->rx_status_ring_order < WIL_SRING_SIZE_ORDER_MIN ||
            wil->rx_status_ring_order > WIL_SRING_SIZE_ORDER_MAX)
                wil->rx_status_ring_order = WIL_RX_SRING_SIZE_ORDER_DEFAULT;
@@ -636,8 +625,6 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size)
 
        wil_rx_buf_len_init_edma(wil);
 
-       max_rx_pl_per_desc = ALIGN(wil->rx_buf_len, 4);
-
        /* Use debugfs dbg_num_rx_srings if set, reserve one sring for TX */
        if (wil->num_rx_status_rings > WIL6210_MAX_STATUS_RINGS - 1)
                wil->num_rx_status_rings = WIL6210_MAX_STATUS_RINGS - 1;
@@ -645,7 +632,7 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size)
        wil_dbg_misc(wil, "rx_init: allocate %d status rings\n",
                     wil->num_rx_status_rings);
 
-       rc = wil_wmi_cfg_def_rx_offload(wil, max_rx_pl_per_desc);
+       rc = wil_wmi_cfg_def_rx_offload(wil, wil->rx_buf_len);
        if (rc)
                return rc;
 
@@ -834,23 +821,24 @@ static int wil_rx_error_check_edma(struct wil6210_priv *wil,
                wil_dbg_txrx(wil, "L2 RX error, l2_rx_status=0x%x\n",
                             l2_rx_status);
                /* Due to HW issue, KEY error will trigger a MIC error */
-               if (l2_rx_status & WIL_RX_EDMA_ERROR_MIC) {
-                       wil_dbg_txrx(wil,
-                                    "L2 MIC/KEY error, dropping packet\n");
+               if (l2_rx_status == WIL_RX_EDMA_ERROR_MIC) {
+                       wil_err_ratelimited(wil,
+                                           "L2 MIC/KEY error, dropping packet\n");
                        stats->rx_mic_error++;
                }
-               if (l2_rx_status & WIL_RX_EDMA_ERROR_KEY) {
-                       wil_dbg_txrx(wil, "L2 KEY error, dropping packet\n");
+               if (l2_rx_status == WIL_RX_EDMA_ERROR_KEY) {
+                       wil_err_ratelimited(wil,
+                                           "L2 KEY error, dropping packet\n");
                        stats->rx_key_error++;
                }
-               if (l2_rx_status & WIL_RX_EDMA_ERROR_REPLAY) {
-                       wil_dbg_txrx(wil,
-                                    "L2 REPLAY error, dropping packet\n");
+               if (l2_rx_status == WIL_RX_EDMA_ERROR_REPLAY) {
+                       wil_err_ratelimited(wil,
+                                           "L2 REPLAY error, dropping packet\n");
                        stats->rx_replay++;
                }
-               if (l2_rx_status & WIL_RX_EDMA_ERROR_AMSDU) {
-                       wil_dbg_txrx(wil,
-                                    "L2 AMSDU error, dropping packet\n");
+               if (l2_rx_status == WIL_RX_EDMA_ERROR_AMSDU) {
+                       wil_err_ratelimited(wil,
+                                           "L2 AMSDU error, dropping packet\n");
                        stats->rx_amsdu_error++;
                }
                return -EFAULT;
@@ -881,7 +869,7 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
        struct sk_buff *skb;
        dma_addr_t pa;
        struct wil_ring_rx_data *rxdata = &sring->rx_data;
-       unsigned int sz = ALIGN(wil->rx_buf_len, 4);
+       unsigned int sz = wil->rx_buf_len;
        struct wil_net_stats *stats = NULL;
        u16 dmalen;
        int cid;
index a7fe929..343516a 100644 (file)
@@ -23,9 +23,9 @@
 #define WIL_SRING_SIZE_ORDER_MIN       (WIL_RING_SIZE_ORDER_MIN)
 #define WIL_SRING_SIZE_ORDER_MAX       (WIL_RING_SIZE_ORDER_MAX)
 /* RX sring order should be bigger than RX ring order */
-#define WIL_RX_SRING_SIZE_ORDER_DEFAULT        (11)
+#define WIL_RX_SRING_SIZE_ORDER_DEFAULT        (12)
 #define WIL_TX_SRING_SIZE_ORDER_DEFAULT        (12)
-#define WIL_RX_BUFF_ARR_SIZE_DEFAULT (1536)
+#define WIL_RX_BUFF_ARR_SIZE_DEFAULT (2600)
 
 #define WIL_DEFAULT_RX_STATUS_RING_ID 0
 #define WIL_RX_DESC_RING_ID 0
index abb8201..0f3be3f 100644 (file)
@@ -81,6 +81,7 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
 
 #define WIL_TX_Q_LEN_DEFAULT           (4000)
 #define WIL_RX_RING_SIZE_ORDER_DEFAULT (10)
+#define WIL_RX_RING_SIZE_ORDER_TALYN_DEFAULT   (11)
 #define WIL_TX_RING_SIZE_ORDER_DEFAULT (12)
 #define WIL_BCAST_RING_SIZE_ORDER_DEFAULT      (7)
 #define WIL_BCAST_MCS0_LIMIT           (1024) /* limit for MCS0 frame size */
@@ -319,6 +320,7 @@ struct RGF_ICR {
 /* MAC timer, usec, for packet lifetime */
 #define RGF_MAC_MTRL_COUNTER_0         (0x886aa8)
 
+#define RGF_CAF_ICR_TALYN_MB           (0x8893d4) /* struct RGF_ICR */
 #define RGF_CAF_ICR                    (0x88946c) /* struct RGF_ICR */
 #define RGF_CAF_OSC_CONTROL            (0x88afa4)
        #define BIT_CAF_OSC_XTAL_EN             BIT(0)
@@ -613,7 +615,7 @@ struct wil_txrx_ops {
                              int cid, int tid);
        irqreturn_t (*irq_tx)(int irq, void *cookie);
        /* RX ops */
-       int (*rx_init)(struct wil6210_priv *wil, u16 ring_size);
+       int (*rx_init)(struct wil6210_priv *wil, uint ring_order);
        void (*rx_fini)(struct wil6210_priv *wil);
        int (*wmi_addba_rx_resp)(struct wil6210_priv *wil, u8 mid, u8 cid,
                                 u8 tid, u8 token, u16 status, bool amsdu,
@@ -848,6 +850,14 @@ struct wil6210_vif {
        u8 hidden_ssid; /* relevant in AP mode */
        u32 ap_isolate; /* no intra-BSS communication */
        bool pbss;
+       int bi;
+       u8 *proberesp, *proberesp_ies, *assocresp_ies;
+       size_t proberesp_len, proberesp_ies_len, assocresp_ies_len;
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+       size_t ssid_len;
+       u8 gtk_index;
+       u8 gtk[WMI_MAX_KEY_LEN];
+       size_t gtk_len;
        int bcast_ring;
        struct cfg80211_bss *bss; /* connected bss, relevant in STA mode */
        int locally_generated_disc; /* relevant in STA mode */
@@ -1220,8 +1230,8 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct wil_ring *vring);
 int wmi_update_ft_ies(struct wil6210_vif *vif, u16 ie_len, const void *ie);
 int wmi_rxon(struct wil6210_priv *wil, bool on);
 int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
-int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac,
-                      u16 reason, bool full_disconnect, bool del_sta);
+int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, u16 reason,
+                      bool del_sta);
 int wmi_addba(struct wil6210_priv *wil, u8 mid,
              u8 ringid, u8 size, u16 timeout);
 int wmi_delba_tx(struct wil6210_priv *wil, u8 mid, u8 ringid, u16 reason);
@@ -1276,6 +1286,7 @@ int wmi_stop_discovery(struct wil6210_vif *vif);
 int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                         struct cfg80211_mgmt_tx_params *params,
                         u64 *cookie);
+void wil_cfg80211_ap_recovery(struct wil6210_priv *wil);
 int wil_cfg80211_iface_combinations_from_fw(
        struct wil6210_priv *wil,
        const struct wil_fw_record_concurrency *conc);
@@ -1306,7 +1317,9 @@ void wil_abort_scan(struct wil6210_vif *vif, bool sync);
 void wil_abort_scan_all_vifs(struct wil6210_priv *wil, bool sync);
 void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps);
 void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
-                       u16 reason_code, bool from_event);
+                       u16 reason_code);
+void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid,
+                                u16 reason_code);
 void wil_probe_client_flush(struct wil6210_vif *vif);
 void wil_probe_client_worker(struct work_struct *work);
 void wil_disconnect_worker(struct work_struct *work);
index 4859f0e..345f059 100644 (file)
@@ -1018,7 +1018,7 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len)
                wil_err(wil, "config tx vring failed for CID %d, rc (%d)\n",
                        evt->cid, rc);
                wmi_disconnect_sta(vif, wil->sta[evt->cid].addr,
-                                  WLAN_REASON_UNSPECIFIED, false, false);
+                                  WLAN_REASON_UNSPECIFIED, false);
        } else {
                wil_info(wil, "successful connection to CID %d\n", evt->cid);
        }
@@ -1112,7 +1112,24 @@ static void wmi_evt_disconnect(struct wil6210_vif *vif, int id,
        }
 
        mutex_lock(&wil->mutex);
-       wil6210_disconnect(vif, evt->bssid, reason_code, true);
+       wil6210_disconnect_complete(vif, evt->bssid, reason_code);
+       if (disable_ap_sme) {
+               struct wireless_dev *wdev = vif_to_wdev(vif);
+               struct net_device *ndev = vif_to_ndev(vif);
+
+               /* disconnect event in disable_ap_sme mode means link loss */
+               switch (wdev->iftype) {
+               /* AP-like interface */
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_P2P_GO:
+                       /* notify hostapd about link loss */
+                       cfg80211_cqm_pktloss_notify(ndev, evt->bssid, 0,
+                                                   GFP_KERNEL);
+                       break;
+               default:
+                       break;
+               }
+       }
        mutex_unlock(&wil->mutex);
 }
 
@@ -1637,7 +1654,7 @@ wmi_evt_auth_status(struct wil6210_vif *vif, int id, void *d, int len)
        return;
 
 fail:
-       wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID, false);
+       wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID);
 }
 
 static void
@@ -1766,7 +1783,7 @@ wmi_evt_reassoc_status(struct wil6210_vif *vif, int id, void *d, int len)
        return;
 
 fail:
-       wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID, false);
+       wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID);
 }
 
 /**
@@ -1949,16 +1966,17 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len,
 {
        int rc;
        unsigned long remain;
+       ulong flags;
 
        mutex_lock(&wil->wmi_mutex);
 
-       spin_lock(&wil->wmi_ev_lock);
+       spin_lock_irqsave(&wil->wmi_ev_lock, flags);
        wil->reply_id = reply_id;
        wil->reply_mid = mid;
        wil->reply_buf = reply;
        wil->reply_size = reply_size;
        reinit_completion(&wil->wmi_call);
-       spin_unlock(&wil->wmi_ev_lock);
+       spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
 
        rc = __wmi_send(wil, cmdid, mid, buf, len);
        if (rc)
@@ -1978,12 +1996,12 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len,
        }
 
 out:
-       spin_lock(&wil->wmi_ev_lock);
+       spin_lock_irqsave(&wil->wmi_ev_lock, flags);
        wil->reply_id = 0;
        wil->reply_mid = U8_MAX;
        wil->reply_buf = NULL;
        wil->reply_size = 0;
-       spin_unlock(&wil->wmi_ev_lock);
+       spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
 
        mutex_unlock(&wil->wmi_mutex);
 
@@ -2560,12 +2578,11 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf)
        return 0;
 }
 
-int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac,
-                      u16 reason, bool full_disconnect, bool del_sta)
+int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, u16 reason,
+                      bool del_sta)
 {
        struct wil6210_priv *wil = vif_to_wil(vif);
        int rc;
-       u16 reason_code;
        struct wmi_disconnect_sta_cmd disc_sta_cmd = {
                .disconnect_reason = cpu_to_le16(reason),
        };
@@ -2598,21 +2615,8 @@ int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac,
                wil_fw_error_recovery(wil);
                return rc;
        }
+       wil->sinfo_gen++;
 
-       if (full_disconnect) {
-               /* call event handler manually after processing wmi_call,
-                * to avoid deadlock - disconnect event handler acquires
-                * wil->mutex while it is already held here
-                */
-               reason_code = le16_to_cpu(reply.evt.protocol_reason_status);
-
-               wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
-                           reply.evt.bssid, reason_code,
-                           reply.evt.disconnect_reason);
-
-               wil->sinfo_gen++;
-               wil6210_disconnect(vif, reply.evt.bssid, reason_code, true);
-       }
        return 0;
 }
 
@@ -3145,7 +3149,7 @@ static void wmi_event_handle(struct wil6210_priv *wil,
 
                if (mid == MID_BROADCAST)
                        mid = 0;
-               if (mid >= wil->max_vifs) {
+               if (mid >= ARRAY_SIZE(wil->vifs) || mid >= wil->max_vifs) {
                        wil_dbg_wmi(wil, "invalid mid %d, event skipped\n",
                                    mid);
                        return;
index fba8560..3e41457 100644 (file)
@@ -4,6 +4,7 @@ config B43
        select BCMA if B43_BCMA
        select SSB if B43_SSB
        select FW_LOADER
+       select CORDIC
        ---help---
          b43 is a driver for the Broadcom 43xx series wireless devices.
 
index 85f2ca9..98c4fa5 100644 (file)
@@ -604,50 +604,3 @@ void b43_phy_force_clock(struct b43_wldev *dev, bool force)
 #endif
        }
 }
-
-/* http://bcm-v4.sipsolutions.net/802.11/PHY/Cordic */
-struct b43_c32 b43_cordic(int theta)
-{
-       static const u32 arctg[] = {
-               2949120, 1740967, 919879, 466945, 234379, 117304,
-                 58666,   29335,  14668,   7334,   3667,   1833,
-                   917,     458,    229,    115,     57,     29,
-       };
-       u8 i;
-       s32 tmp;
-       s8 signx = 1;
-       u32 angle = 0;
-       struct b43_c32 ret = { .i = 39797, .q = 0, };
-
-       while (theta > (180 << 16))
-               theta -= (360 << 16);
-       while (theta < -(180 << 16))
-               theta += (360 << 16);
-
-       if (theta > (90 << 16)) {
-               theta -= (180 << 16);
-               signx = -1;
-       } else if (theta < -(90 << 16)) {
-               theta += (180 << 16);
-               signx = -1;
-       }
-
-       for (i = 0; i <= 17; i++) {
-               if (theta > angle) {
-                       tmp = ret.i - (ret.q >> i);
-                       ret.q += ret.i >> i;
-                       ret.i = tmp;
-                       angle += arctg[i];
-               } else {
-                       tmp = ret.i + (ret.q >> i);
-                       ret.q -= ret.i >> i;
-                       ret.i = tmp;
-                       angle -= arctg[i];
-               }
-       }
-
-       ret.i *= signx;
-       ret.q *= signx;
-
-       return ret;
-}
index 57a1ad8..4213cac 100644 (file)
@@ -7,13 +7,6 @@
 
 struct b43_wldev;
 
-/* Complex number using 2 32-bit signed integers */
-struct b43_c32 { s32 i, q; };
-
-#define CORDIC_CONVERT(value)  (((value) >= 0) ? \
-                                ((((value) >> 15) + 1) >> 1) : \
-                                -((((-(value)) >> 15) + 1) >> 1))
-
 /* PHY register routing bits */
 #define B43_PHYROUTE                   0x0C00 /* PHY register routing bits mask */
 #define  B43_PHYROUTE_BASE             0x0000 /* Base registers */
@@ -450,6 +443,4 @@ bool b43_is_40mhz(struct b43_wldev *dev);
 
 void b43_phy_force_clock(struct b43_wldev *dev, bool force);
 
-struct b43_c32 b43_cordic(int theta);
-
 #endif /* LINUX_B43_PHY_COMMON_H_ */
index 6922cbb..46408a5 100644 (file)
@@ -23,6 +23,7 @@
 
 */
 
+#include <linux/cordic.h>
 #include <linux/slab.h>
 
 #include "b43.h"
@@ -1780,9 +1781,9 @@ static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max)
 {
        struct b43_phy_lp *lpphy = dev->phy.lp;
        u16 buf[64];
-       int i, samples = 0, angle = 0;
+       int i, samples = 0, theta = 0;
        int rotation = (((36 * freq) / 20) << 16) / 100;
-       struct b43_c32 sample;
+       struct cordic_iq sample;
 
        lpphy->tx_tone_freq = freq;
 
@@ -1798,10 +1799,10 @@ static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max)
        }
 
        for (i = 0; i < samples; i++) {
-               sample = b43_cordic(angle);
-               angle += rotation;
-               buf[i] = CORDIC_CONVERT((sample.i * max) & 0xFF) << 8;
-               buf[i] |= CORDIC_CONVERT((sample.q * max) & 0xFF);
+               sample = cordic_calc_iq(CORDIC_FIXED(theta));
+               theta += rotation;
+               buf[i] = CORDIC_FLOAT((sample.i * max) & 0xFF) << 8;
+               buf[i] |= CORDIC_FLOAT((sample.q * max) & 0xFF);
        }
 
        b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf);
index 44ab080..77d7cd5 100644 (file)
@@ -23,6 +23,7 @@
 
 */
 
+#include <linux/cordic.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/types.h>
@@ -1513,7 +1514,7 @@ static void b43_radio_init2055(struct b43_wldev *dev)
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/LoadSampleTable */
 static int b43_nphy_load_samples(struct b43_wldev *dev,
-                                       struct b43_c32 *samples, u16 len) {
+                                       struct cordic_iq *samples, u16 len) {
        struct b43_phy_n *nphy = dev->phy.n;
        u16 i;
        u32 *data;
@@ -1544,7 +1545,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
 {
        int i;
        u16 bw, len, rot, angle;
-       struct b43_c32 *samples;
+       struct cordic_iq *samples;
 
        bw = b43_is_40mhz(dev) ? 40 : 20;
        len = bw << 3;
@@ -1561,7 +1562,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
                len = bw << 1;
        }
 
-       samples = kcalloc(len, sizeof(struct b43_c32), GFP_KERNEL);
+       samples = kcalloc(len, sizeof(struct cordic_iq), GFP_KERNEL);
        if (!samples) {
                b43err(dev->wl, "allocation for samples generation failed\n");
                return 0;
@@ -1570,10 +1571,10 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
        angle = 0;
 
        for (i = 0; i < len; i++) {
-               samples[i] = b43_cordic(angle);
+               samples[i] = cordic_calc_iq(CORDIC_FIXED(angle));
                angle += rot;
-               samples[i].q = CORDIC_CONVERT(samples[i].q * max);
-               samples[i].i = CORDIC_CONVERT(samples[i].i * max);
+               samples[i].q = CORDIC_FLOAT(samples[i].q * max);
+               samples[i].i = CORDIC_FLOAT(samples[i].i * max);
        }
 
        i = b43_nphy_load_samples(dev, samples, len);
@@ -5894,7 +5895,6 @@ static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev,
        struct ieee80211_channel *channel = dev->wl->hw->conf.chandef.chan;
        struct b43_ppr *ppr = &nphy->tx_pwr_max_ppr;
        u8 max; /* qdBm */
-       bool tx_pwr_state;
 
        if (nphy->tx_pwr_last_recalc_freq == channel->center_freq &&
            nphy->tx_pwr_last_recalc_limit == phy->desired_txpower)
@@ -5930,7 +5930,6 @@ static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev,
        b43_ppr_apply_min(dev, ppr, INT_TO_Q52(8));
 
        /* Apply */
-       tx_pwr_state = nphy->txpwrctrl;
        b43_mac_suspend(dev);
        b43_nphy_tx_power_ctl_setup(dev);
        if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) {
@@ -6043,7 +6042,6 @@ static int b43_phy_initn(struct b43_wldev *dev)
        u8 tx_pwr_state;
        struct nphy_txgains target;
        u16 tmp;
-       enum nl80211_band tmp2;
        bool do_rssi_cal;
 
        u16 clip[2];
@@ -6137,7 +6135,6 @@ static int b43_phy_initn(struct b43_wldev *dev)
                b43_phy_write(dev, B43_NPHY_DUP40_BL, 0x9A4);
        }
 
-       tmp2 = b43_current_band(dev->wl);
        if (b43_nphy_ipa(dev)) {
                b43_phy_set(dev, B43_NPHY_PAPD_EN0, 0x1);
                b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ0, 0x007F,
index 1f5a9b9..22fd95a 100644 (file)
@@ -54,3 +54,5 @@ brcmfmac-$(CONFIG_BRCM_TRACING) += \
                tracepoint.o
 brcmfmac-$(CONFIG_OF) += \
                of.o
+brcmfmac-$(CONFIG_DMI) += \
+               dmi.o
index b2ad212..d64bf23 100644 (file)
@@ -983,6 +983,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373),
+       BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012),
        { /* end: all zeroes */ }
 };
 MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
index 230a378..3530123 100644 (file)
@@ -5196,10 +5196,17 @@ static struct cfg80211_ops brcmf_cfg80211_ops = {
        .del_pmk = brcmf_cfg80211_del_pmk,
 };
 
-struct cfg80211_ops *brcmf_cfg80211_get_ops(void)
+struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings)
 {
-       return kmemdup(&brcmf_cfg80211_ops, sizeof(brcmf_cfg80211_ops),
+       struct cfg80211_ops *ops;
+
+       ops = kmemdup(&brcmf_cfg80211_ops, sizeof(brcmf_cfg80211_ops),
                       GFP_KERNEL);
+
+       if (ops && settings->roamoff)
+               ops->update_connect_params = NULL;
+
+       return ops;
 }
 
 struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
@@ -6005,7 +6012,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
                         * for subsequent chanspecs.
                         */
                        channel->flags = IEEE80211_CHAN_NO_HT40 |
-                                        IEEE80211_CHAN_NO_80MHZ;
+                                        IEEE80211_CHAN_NO_80MHZ |
+                                        IEEE80211_CHAN_NO_160MHZ;
                        ch.bw = BRCMU_CHAN_BW_20;
                        cfg->d11inf.encchspec(&ch);
                        chaninfo = ch.chspec;
@@ -6308,6 +6316,16 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
                .tx = 0xffff,
                .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
                      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_AP] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+                     BIT(IEEE80211_STYPE_AUTH >> 4) |
+                     BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+                     BIT(IEEE80211_STYPE_ACTION >> 4)
        }
 };
 
@@ -6638,6 +6656,12 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
 
        brcmf_configure_arp_nd_offload(ifp, true);
 
+       err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_FAKEFRAG, 1);
+       if (err) {
+               brcmf_err("failed to set frameburst mode\n");
+               goto default_conf_out;
+       }
+
        cfg->dongle_up = true;
 default_conf_out:
 
index a4aec00..9a6287f 100644 (file)
@@ -404,7 +404,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
 void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg);
 s32 brcmf_cfg80211_up(struct net_device *ndev);
 s32 brcmf_cfg80211_down(struct net_device *ndev);
-struct cfg80211_ops *brcmf_cfg80211_get_ops(void);
+struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings);
 enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp);
 
 struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
index 927d62b..22534bf 100644 (file)
@@ -165,6 +165,7 @@ struct sbconfig {
 #define SRCI_LSS_MASK          0x00f00000
 #define SRCI_LSS_SHIFT         20
 #define        SRCI_SRNB_MASK          0xf0
+#define        SRCI_SRNB_MASK_EXT      0x100
 #define        SRCI_SRNB_SHIFT         4
 #define        SRCI_SRBSZ_MASK         0xf
 #define        SRCI_SRBSZ_SHIFT        0
@@ -592,7 +593,13 @@ static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize,
                if (lss != 0)
                        *ramsize += (1 << ((lss - 1) + SR_BSZ_BASE));
        } else {
-               nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+               /* length of SRAM Banks increased for corerev greater than 23 */
+               if (sr->pub.rev >= 23) {
+                       nb = (coreinfo & (SRCI_SRNB_MASK | SRCI_SRNB_MASK_EXT))
+                               >> SRCI_SRNB_SHIFT;
+               } else {
+                       nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+               }
                for (i = 0; i < nb; i++) {
                        retent = brcmf_chip_socram_banksize(sr, i, &banksize);
                        *ramsize += banksize;
@@ -779,7 +786,7 @@ static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr,
                                      u32 *regbase, u32 *wrapbase)
 {
        u8 desc;
-       u32 val;
+       u32 val, szdesc;
        u8 mpnum = 0;
        u8 stype, sztype, wraptype;
 
@@ -825,14 +832,15 @@ static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr,
 
                /* next size descriptor can be skipped */
                if (sztype == DMP_SLAVE_SIZE_DESC) {
-                       val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+                       szdesc = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
                        /* skip upper size descriptor if present */
-                       if (val & DMP_DESC_ADDRSIZE_GT32)
+                       if (szdesc & DMP_DESC_ADDRSIZE_GT32)
                                brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
                }
 
-               /* only look for 4K register regions */
-               if (sztype != DMP_SLAVE_SIZE_4K)
+               /* look for 4K or 8K register regions */
+               if (sztype != DMP_SLAVE_SIZE_4K &&
+                   sztype != DMP_SLAVE_SIZE_8K)
                        continue;
 
                stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S;
@@ -889,7 +897,8 @@ int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci)
 
                /* need core with ports */
                if (nmw + nsw == 0 &&
-                   id != BCMA_CORE_PMU)
+                   id != BCMA_CORE_PMU &&
+                   id != BCMA_CORE_GCI)
                        continue;
 
                /* try to obtain register address info */
@@ -1356,6 +1365,16 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
                addr = CORE_CC_REG(base, sr_control1);
                reg = chip->ops->read32(chip->ctx, addr);
                return reg != 0;
+       case CY_CC_4373_CHIP_ID:
+               /* explicitly check SR engine enable bit */
+               addr = CORE_CC_REG(base, sr_control0);
+               reg = chip->ops->read32(chip->ctx, addr);
+               return (reg & CC_SR_CTL0_ENABLE_MASK) != 0;
+       case CY_CC_43012_CHIP_ID:
+               addr = CORE_CC_REG(pmu->base, retention_ctl);
+               reg = chip->ops->read32(chip->ctx, addr);
+               return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
+                              PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
        default:
                addr = CORE_CC_REG(pmu->base, pmucapabilities_ext);
                reg = chip->ops->read32(chip->ctx, addr);
index 94044a7..1f1e95a 100644 (file)
@@ -214,7 +214,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
        err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
                                       sizeof(ifp->mac_addr));
        if (err < 0) {
-               brcmf_err("Retreiving cur_etheraddr failed, %d\n", err);
+               brcmf_err("Retrieving cur_etheraddr failed, %d\n", err);
                goto done;
        }
        memcpy(ifp->drvr->wiphy->perm_addr, ifp->drvr->mac, ETH_ALEN);
@@ -269,7 +269,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
        strcpy(buf, "ver");
        err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf));
        if (err < 0) {
-               brcmf_err("Retreiving version information failed, %d\n",
+               brcmf_err("Retrieving version information failed, %d\n",
                          err);
                goto done;
        }
@@ -448,7 +448,8 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
                }
        }
        if (!found) {
-               /* No platform data for this device, try OF (Open Firwmare) */
+               /* No platform data for this device, try OF and DMI data */
+               brcmf_dmi_probe(settings, chip, chiprev);
                brcmf_of_probe(dev, bus_type, settings);
        }
        return settings;
index a34642c..4ce56be 100644 (file)
@@ -59,6 +59,7 @@ struct brcmf_mp_device {
        bool            iapp;
        bool            ignore_probe_fail;
        struct brcmfmac_pd_cc *country_codes;
+       const char      *board_type;
        union {
                struct brcmfmac_sdio_pd sdio;
        } bus;
@@ -74,4 +75,11 @@ void brcmf_release_module_param(struct brcmf_mp_device *module_param);
 /* Sets dongle media info (drv_version, mac address). */
 int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
 
+#ifdef CONFIG_DMI
+void brcmf_dmi_probe(struct brcmf_mp_device *settings, u32 chip, u32 chiprev);
+#else
+static inline void
+brcmf_dmi_probe(struct brcmf_mp_device *settings, u32 chip, u32 chiprev) {}
+#endif
+
 #endif /* BRCMFMAC_COMMON_H */
index b1f702f..860a437 100644 (file)
@@ -1130,7 +1130,7 @@ int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings)
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       ops = brcmf_cfg80211_get_ops();
+       ops = brcmf_cfg80211_get_ops(settings);
        if (!ops)
                return -ENOMEM;
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c
new file mode 100644 (file)
index 0000000..51d76ac
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2018 Hans de Goede <hdegoede@redhat.com>
+ *
+ * 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.
+ */
+
+#include <linux/dmi.h>
+#include <linux/mod_devicetable.h>
+#include "core.h"
+#include "common.h"
+#include "brcm_hw_ids.h"
+
+/* The DMI data never changes so we can use a static buf for this */
+static char dmi_board_type[128];
+
+struct brcmf_dmi_data {
+       u32 chip;
+       u32 chiprev;
+       const char *board_type;
+};
+
+/* NOTE: Please keep all entries sorted alphabetically */
+
+static const struct brcmf_dmi_data gpd_win_pocket_data = {
+       BRCM_CC_4356_CHIP_ID, 2, "gpd-win-pocket"
+};
+
+static const struct brcmf_dmi_data jumper_ezpad_mini3_data = {
+       BRCM_CC_43430_CHIP_ID, 0, "jumper-ezpad-mini3"
+};
+
+static const struct brcmf_dmi_data meegopad_t08_data = {
+       BRCM_CC_43340_CHIP_ID, 2, "meegopad-t08"
+};
+
+static const struct dmi_system_id dmi_platform_data[] = {
+       {
+               /* Match for the GPDwin which unfortunately uses somewhat
+                * generic dmi strings, which is why we test for 4 strings.
+                * Comparing against 23 other byt/cht boards, board_vendor
+                * and board_name are unique to the GPDwin, where as only one
+                * other board has the same board_serial and 3 others have
+                * the same default product_name. Also the GPDwin is the
+                * only device to have both board_ and product_name not set.
+                */
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+                       DMI_MATCH(DMI_BOARD_NAME, "Default string"),
+                       DMI_MATCH(DMI_BOARD_SERIAL, "Default string"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
+               },
+               .driver_data = (void *)&gpd_win_pocket_data,
+       },
+       {
+               /* Jumper EZpad mini3 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"),
+                       /* jumperx.T87.KFBNEEA02 with the version-nr dropped */
+                       DMI_MATCH(DMI_BIOS_VERSION, "jumperx.T87.KFBNEEA"),
+               },
+               .driver_data = (void *)&jumper_ezpad_mini3_data,
+       },
+       {
+               /* Meegopad T08 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
+                       DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
+                       DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
+               },
+               .driver_data = (void *)&meegopad_t08_data,
+       },
+       {}
+};
+
+void brcmf_dmi_probe(struct brcmf_mp_device *settings, u32 chip, u32 chiprev)
+{
+       const struct dmi_system_id *match;
+       const struct brcmf_dmi_data *data;
+       const char *sys_vendor;
+       const char *product_name;
+
+       /* Some models have DMI strings which are too generic, e.g.
+        * "Default string", we use a quirk table for these.
+        */
+       for (match = dmi_first_match(dmi_platform_data);
+            match;
+            match = dmi_first_match(match + 1)) {
+               data = match->driver_data;
+
+               if (data->chip == chip && data->chiprev == chiprev) {
+                       settings->board_type = data->board_type;
+                       return;
+               }
+       }
+
+       /* Not found in the quirk-table, use sys_vendor-product_name */
+       sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+       product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
+       if (sys_vendor && product_name) {
+               snprintf(dmi_board_type, sizeof(dmi_board_type), "%s-%s",
+                        sys_vendor, product_name);
+               settings->board_type = dmi_board_type;
+       }
+}
index 9095b83..14b9489 100644 (file)
@@ -14,6 +14,7 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <linux/efi.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/device.h>
@@ -445,6 +446,75 @@ struct brcmf_fw {
 
 static void brcmf_fw_request_done(const struct firmware *fw, void *ctx);
 
+#ifdef CONFIG_EFI
+/* In some cases the EFI-var stored nvram contains "ccode=ALL" or "ccode=XV"
+ * to specify "worldwide" compatible settings, but these 2 ccode-s do not work
+ * properly. "ccode=ALL" causes channels 12 and 13 to not be available,
+ * "ccode=XV" causes all 5GHz channels to not be available. So we replace both
+ * with "ccode=X2" which allows channels 12+13 and 5Ghz channels in
+ * no-Initiate-Radiation mode. This means that we will never send on these
+ * channels without first having received valid wifi traffic on the channel.
+ */
+static void brcmf_fw_fix_efi_nvram_ccode(char *data, unsigned long data_len)
+{
+       char *ccode;
+
+       ccode = strnstr((char *)data, "ccode=ALL", data_len);
+       if (!ccode)
+               ccode = strnstr((char *)data, "ccode=XV\r", data_len);
+       if (!ccode)
+               return;
+
+       ccode[6] = 'X';
+       ccode[7] = '2';
+       ccode[8] = '\r';
+}
+
+static u8 *brcmf_fw_nvram_from_efi(size_t *data_len_ret)
+{
+       const u16 name[] = { 'n', 'v', 'r', 'a', 'm', 0 };
+       struct efivar_entry *nvram_efivar;
+       unsigned long data_len = 0;
+       u8 *data = NULL;
+       int err;
+
+       nvram_efivar = kzalloc(sizeof(*nvram_efivar), GFP_KERNEL);
+       if (!nvram_efivar)
+               return NULL;
+
+       memcpy(&nvram_efivar->var.VariableName, name, sizeof(name));
+       nvram_efivar->var.VendorGuid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61,
+                                               0xb5, 0x1f, 0x43, 0x26,
+                                               0x81, 0x23, 0xd1, 0x13);
+
+       err = efivar_entry_size(nvram_efivar, &data_len);
+       if (err)
+               goto fail;
+
+       data = kmalloc(data_len, GFP_KERNEL);
+       if (!data)
+               goto fail;
+
+       err = efivar_entry_get(nvram_efivar, NULL, &data_len, data);
+       if (err)
+               goto fail;
+
+       brcmf_fw_fix_efi_nvram_ccode(data, data_len);
+       brcmf_info("Using nvram EFI variable\n");
+
+       kfree(nvram_efivar);
+       *data_len_ret = data_len;
+       return data;
+
+fail:
+       kfree(data);
+       kfree(nvram_efivar);
+       return NULL;
+}
+#else
+static inline u8 *brcmf_fw_nvram_from_efi(size_t *data_len) { return NULL; }
+#endif
+
 static void brcmf_fw_free_request(struct brcmf_fw_request *req)
 {
        struct brcmf_fw_item *item;
@@ -463,11 +533,12 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
 {
        struct brcmf_fw *fwctx = ctx;
        struct brcmf_fw_item *cur;
+       bool free_bcm47xx_nvram = false;
+       bool kfree_nvram = false;
        u32 nvram_length = 0;
        void *nvram = NULL;
        u8 *data = NULL;
        size_t data_len;
-       bool raw_nvram;
 
        brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
 
@@ -476,12 +547,13 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
        if (fw && fw->data) {
                data = (u8 *)fw->data;
                data_len = fw->size;
-               raw_nvram = false;
        } else {
-               data = bcm47xx_nvram_get_contents(&data_len);
-               if (!data && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
+               if ((data = bcm47xx_nvram_get_contents(&data_len)))
+                       free_bcm47xx_nvram = true;
+               else if ((data = brcmf_fw_nvram_from_efi(&data_len)))
+                       kfree_nvram = true;
+               else if (!(cur->flags & BRCMF_FW_REQF_OPTIONAL))
                        goto fail;
-               raw_nvram = true;
        }
 
        if (data)
@@ -489,8 +561,11 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
                                             fwctx->req->domain_nr,
                                             fwctx->req->bus_nr);
 
-       if (raw_nvram)
+       if (free_bcm47xx_nvram)
                bcm47xx_nvram_release_contents(data);
+       if (kfree_nvram)
+               kfree(data);
+
        release_firmware(fw);
        if (!nvram && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
                goto fail;
@@ -504,90 +579,75 @@ fail:
        return -ENOENT;
 }
 
-static int brcmf_fw_request_next_item(struct brcmf_fw *fwctx, bool async)
+static int brcmf_fw_complete_request(const struct firmware *fw,
+                                    struct brcmf_fw *fwctx)
 {
-       struct brcmf_fw_item *cur;
-       const struct firmware *fw = NULL;
-       int ret;
-
-       cur = &fwctx->req->items[fwctx->curpos];
-
-       brcmf_dbg(TRACE, "%srequest for %s\n", async ? "async " : "",
-                 cur->path);
-
-       if (async)
-               ret = request_firmware_nowait(THIS_MODULE, true, cur->path,
-                                             fwctx->dev, GFP_KERNEL, fwctx,
-                                             brcmf_fw_request_done);
-       else
-               ret = request_firmware(&fw, cur->path, fwctx->dev);
-
-       if (ret < 0) {
-               brcmf_fw_request_done(NULL, fwctx);
-       } else if (!async && fw) {
-               brcmf_dbg(TRACE, "firmware %s %sfound\n", cur->path,
-                         fw ? "" : "not ");
-               if (cur->type == BRCMF_FW_TYPE_BINARY)
-                       cur->binary = fw;
-               else if (cur->type == BRCMF_FW_TYPE_NVRAM)
-                       brcmf_fw_request_nvram_done(fw, fwctx);
-               else
-                       release_firmware(fw);
-
-               return -EAGAIN;
-       }
-       return 0;
-}
-
-static void brcmf_fw_request_done(const struct firmware *fw, void *ctx)
-{
-       struct brcmf_fw *fwctx = ctx;
-       struct brcmf_fw_item *cur;
+       struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos];
        int ret = 0;
 
-       cur = &fwctx->req->items[fwctx->curpos];
-
-       brcmf_dbg(TRACE, "enter: firmware %s %sfound\n", cur->path,
-                 fw ? "" : "not ");
-
-       if (!fw)
-               ret = -ENOENT;
+       brcmf_dbg(TRACE, "firmware %s %sfound\n", cur->path, fw ? "" : "not ");
 
        switch (cur->type) {
        case BRCMF_FW_TYPE_NVRAM:
                ret = brcmf_fw_request_nvram_done(fw, fwctx);
                break;
        case BRCMF_FW_TYPE_BINARY:
-               cur->binary = fw;
+               if (fw)
+                       cur->binary = fw;
+               else
+                       ret = -ENOENT;
                break;
        default:
                /* something fishy here so bail out early */
                brcmf_err("unknown fw type: %d\n", cur->type);
                release_firmware(fw);
                ret = -EINVAL;
-               goto fail;
        }
 
-       if (ret < 0 && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
-               goto fail;
+       return (cur->flags & BRCMF_FW_REQF_OPTIONAL) ? 0 : ret;
+}
 
-       do {
-               if (++fwctx->curpos == fwctx->req->n_items) {
-                       ret = 0;
-                       goto done;
-               }
+static int brcmf_fw_request_firmware(const struct firmware **fw,
+                                    struct brcmf_fw *fwctx)
+{
+       struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos];
+       int ret;
 
-               ret = brcmf_fw_request_next_item(fwctx, false);
-       } while (ret == -EAGAIN);
+       /* nvram files are board-specific, first try a board-specific path */
+       if (cur->type == BRCMF_FW_TYPE_NVRAM && fwctx->req->board_type) {
+               char alt_path[BRCMF_FW_NAME_LEN];
 
-       return;
+               strlcpy(alt_path, cur->path, BRCMF_FW_NAME_LEN);
+               /* strip .txt at the end */
+               alt_path[strlen(alt_path) - 4] = 0;
+               strlcat(alt_path, ".", BRCMF_FW_NAME_LEN);
+               strlcat(alt_path, fwctx->req->board_type, BRCMF_FW_NAME_LEN);
+               strlcat(alt_path, ".txt", BRCMF_FW_NAME_LEN);
 
-fail:
-       brcmf_dbg(TRACE, "failed err=%d: dev=%s, fw=%s\n", ret,
-                 dev_name(fwctx->dev), cur->path);
-       brcmf_fw_free_request(fwctx->req);
-       fwctx->req = NULL;
-done:
+               ret = request_firmware(fw, alt_path, fwctx->dev);
+               if (ret == 0)
+                       return ret;
+       }
+
+       return request_firmware(fw, cur->path, fwctx->dev);
+}
+
+static void brcmf_fw_request_done(const struct firmware *fw, void *ctx)
+{
+       struct brcmf_fw *fwctx = ctx;
+       int ret;
+
+       ret = brcmf_fw_complete_request(fw, fwctx);
+
+       while (ret == 0 && ++fwctx->curpos < fwctx->req->n_items) {
+               brcmf_fw_request_firmware(&fw, fwctx);
+               ret = brcmf_fw_complete_request(fw, ctx);
+       }
+
+       if (ret) {
+               brcmf_fw_free_request(fwctx->req);
+               fwctx->req = NULL;
+       }
        fwctx->done(fwctx->dev, ret, fwctx->req);
        kfree(fwctx);
 }
@@ -611,7 +671,9 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
                           void (*fw_cb)(struct device *dev, int err,
                                         struct brcmf_fw_request *req))
 {
+       struct brcmf_fw_item *first = &req->items[0];
        struct brcmf_fw *fwctx;
+       int ret;
 
        brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
        if (!fw_cb)
@@ -628,7 +690,12 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
        fwctx->req = req;
        fwctx->done = fw_cb;
 
-       brcmf_fw_request_next_item(fwctx, true);
+       ret = request_firmware_nowait(THIS_MODULE, true, first->path,
+                                     fwctx->dev, GFP_KERNEL, fwctx,
+                                     brcmf_fw_request_done);
+       if (ret < 0)
+               brcmf_fw_request_done(NULL, fwctx);
+
        return 0;
 }
 
@@ -641,8 +708,9 @@ brcmf_fw_alloc_request(u32 chip, u32 chiprev,
        struct brcmf_fw_request *fwreq;
        char chipname[12];
        const char *mp_path;
+       size_t mp_path_len;
        u32 i, j;
-       char end;
+       char end = '\0';
        size_t reqsz;
 
        for (i = 0; i < table_size; i++) {
@@ -667,7 +735,10 @@ brcmf_fw_alloc_request(u32 chip, u32 chiprev,
                   mapping_table[i].fw_base, chipname);
 
        mp_path = brcmf_mp_global.firmware_path;
-       end = mp_path[strlen(mp_path) - 1];
+       mp_path_len = strnlen(mp_path, BRCMF_FW_ALTPATH_LEN);
+       if (mp_path_len)
+               end = mp_path[mp_path_len - 1];
+
        fwreq->n_items = n_fwnames;
 
        for (j = 0; j < n_fwnames; j++) {
index 2893e56..a0834be 100644 (file)
@@ -70,6 +70,7 @@ struct brcmf_fw_request {
        u16 domain_nr;
        u16 bus_nr;
        u32 n_items;
+       const char *board_type;
        struct brcmf_fw_item items[0];
 };
 
index 63b1287..b6b183b 100644 (file)
@@ -80,6 +80,7 @@
 #define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON  201
 #define BRCMF_C_SET_ASSOC_PREFER               205
 #define BRCMF_C_GET_VALID_CHANNELS             217
+#define BRCMF_C_SET_FAKEFRAG                   219
 #define BRCMF_C_GET_KEY_PRIMARY                        235
 #define BRCMF_C_SET_KEY_PRIMARY                        236
 #define BRCMF_C_SET_SCAN_PASSIVE_TIME          258
index d5bb81e..39ac1bb 100644 (file)
 
 #define BRCMF_VHT_CAP_MCS_MAP_NSS_MAX  8
 
+#define BRCMF_HE_CAP_MCS_MAP_NSS_MAX   8
+
 /* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each
  * ioctl. It is relatively small because firmware has small maximum size input
  * playload restriction for ioctls.
@@ -601,13 +603,37 @@ struct brcmf_sta_info_le {
        __le32 rx_pkts_retried;        /* # rx with retry bit set */
        __le32 tx_rate_fallback;       /* lowest fallback TX rate */
 
-       /* Fields valid for ver >= 5 */
-       struct {
-               __le32 count;                                   /* # rates in this set */
-               u8 rates[BRCMF_MAXRATES_IN_SET];                /* rates in 500kbps units w/hi bit set if basic */
-               u8 mcs[BRCMF_MCSSET_LEN];                       /* supported mcs index bit map */
-               __le16 vht_mcs[BRCMF_VHT_CAP_MCS_MAP_NSS_MAX];  /* supported mcs index bit map per nss */
-       } rateset_adv;
+       union {
+               struct {
+                       struct {
+                               __le32 count;                                   /* # rates in this set */
+                               u8 rates[BRCMF_MAXRATES_IN_SET];                /* rates in 500kbps units w/hi bit set if basic */
+                               u8 mcs[BRCMF_MCSSET_LEN];                       /* supported mcs index bit map */
+                               __le16 vht_mcs[BRCMF_VHT_CAP_MCS_MAP_NSS_MAX];  /* supported mcs index bit map per nss */
+                       } rateset_adv;
+               } v5;
+
+               struct {
+                       __le32 rx_dur_total;    /* total user RX duration (estimated) */
+                       __le16 chanspec;        /** chanspec this sta is on */
+                       __le16 pad_1;
+                       struct {
+                               __le16 version;                                 /* version */
+                               __le16 len;                                     /* length */
+                               __le32 count;                                   /* # rates in this set */
+                               u8 rates[BRCMF_MAXRATES_IN_SET];                /* rates in 500kbps units w/hi bit set if basic */
+                               u8 mcs[BRCMF_MCSSET_LEN];                       /* supported mcs index bit map */
+                               __le16 vht_mcs[BRCMF_VHT_CAP_MCS_MAP_NSS_MAX];  /* supported mcs index bit map per nss */
+                               __le16 he_mcs[BRCMF_HE_CAP_MCS_MAP_NSS_MAX];    /* supported he mcs index bit map per nss */
+                       } rateset_adv;          /* rateset along with mcs index bitmap */
+                       __le16 wpauth;          /* authentication type */
+                       u8 algo;                /* crypto algorithm */
+                       u8 pad_2;
+                       __le32 tx_rspec;        /* Rate of last successful tx frame */
+                       __le32 rx_rspec;        /* Rate of last successful rx frame */
+                       __le32 wnm_cap;         /* wnm capabilities */
+               } v7;
+       };
 };
 
 struct brcmf_chanspec_list {
index f3cbf78..02759eb 100644 (file)
@@ -511,6 +511,7 @@ struct brcmf_fws_info {
        struct work_struct fws_dequeue_work;
        u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
        int fifo_credit[BRCMF_FWS_FIFO_COUNT];
+       int init_fifo_credit[BRCMF_FWS_FIFO_COUNT];
        int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1];
        int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
        u32 fifo_credit_map;
@@ -1237,6 +1238,9 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
        }
 
        fws->fifo_credit[fifo] += credits;
+       if (fws->fifo_credit[fifo] > fws->init_fifo_credit[fifo])
+               fws->fifo_credit[fifo] = fws->init_fifo_credit[fifo];
+
 }
 
 static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
@@ -1451,9 +1455,10 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
 
 static int
 brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
-                     u32 genbit, u16 seq)
+                     u32 genbit, u16 seq, u8 compcnt)
 {
        u32 fifo;
+       u8 cnt = 0;
        int ret;
        bool remove_from_hanger = true;
        struct sk_buff *skb;
@@ -1464,60 +1469,71 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
        brcmf_dbg(DATA, "flags %d\n", flags);
 
        if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
-               fws->stats.txs_discard++;
+               fws->stats.txs_discard += compcnt;
        else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) {
-               fws->stats.txs_supp_core++;
+               fws->stats.txs_supp_core += compcnt;
                remove_from_hanger = false;
        } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) {
-               fws->stats.txs_supp_ps++;
+               fws->stats.txs_supp_ps += compcnt;
                remove_from_hanger = false;
        } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)
-               fws->stats.txs_tossed++;
+               fws->stats.txs_tossed += compcnt;
        else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)
-               fws->stats.txs_host_tossed++;
+               fws->stats.txs_host_tossed += compcnt;
        else
                brcmf_err("unexpected txstatus\n");
 
-       ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
-                                     remove_from_hanger);
-       if (ret != 0) {
-               brcmf_err("no packet in hanger slot: hslot=%d\n", hslot);
-               return ret;
-       }
+       while (cnt < compcnt) {
+               ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
+                                             remove_from_hanger);
+               if (ret != 0) {
+                       brcmf_err("no packet in hanger slot: hslot=%d\n",
+                                 hslot);
+                       goto cont;
+               }
 
-       skcb = brcmf_skbcb(skb);
-       entry = skcb->mac;
-       if (WARN_ON(!entry)) {
-               brcmu_pkt_buf_free_skb(skb);
-               return -EINVAL;
-       }
-       entry->transit_count--;
-       if (entry->suppressed && entry->suppr_transit_count)
-               entry->suppr_transit_count--;
+               skcb = brcmf_skbcb(skb);
+               entry = skcb->mac;
+               if (WARN_ON(!entry)) {
+                       brcmu_pkt_buf_free_skb(skb);
+                       goto cont;
+               }
+               entry->transit_count--;
+               if (entry->suppressed && entry->suppr_transit_count)
+                       entry->suppr_transit_count--;
 
-       brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name, flags,
-                 skcb->htod, seq);
+               brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name,
+                         flags, skcb->htod, seq);
 
-       /* pick up the implicit credit from this packet */
-       fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
-       if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) ||
-           (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
-           (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) {
-               brcmf_fws_return_credits(fws, fifo, 1);
-               brcmf_fws_schedule_deq(fws);
-       }
-       brcmf_fws_macdesc_return_req_credit(skb);
+               /* pick up the implicit credit from this packet */
+               fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
+               if (fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT ||
+                   (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
+                   flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED) {
+                       brcmf_fws_return_credits(fws, fifo, 1);
+                       brcmf_fws_schedule_deq(fws);
+               }
+               brcmf_fws_macdesc_return_req_credit(skb);
 
-       ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
-       if (ret) {
-               brcmu_pkt_buf_free_skb(skb);
-               return -EINVAL;
+               ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
+               if (ret) {
+                       brcmu_pkt_buf_free_skb(skb);
+                       goto cont;
+               }
+               if (!remove_from_hanger)
+                       ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
+                                                           genbit, seq);
+               if (remove_from_hanger || ret)
+                       brcmf_txfinalize(ifp, skb, true);
+
+cont:
+               hslot = (hslot + 1) & (BRCMF_FWS_TXSTAT_HSLOT_MASK >>
+                                      BRCMF_FWS_TXSTAT_HSLOT_SHIFT);
+               if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
+                       seq = (seq + 1) & BRCMF_SKB_HTOD_SEQ_NR_MASK;
+
+               cnt++;
        }
-       if (!remove_from_hanger)
-               ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
-                                                   genbit, seq);
-       if (remove_from_hanger || ret)
-               brcmf_txfinalize(ifp, skb, true);
 
        return 0;
 }
@@ -1543,7 +1559,8 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
        return BRCMF_FWS_RET_OK_SCHEDULE;
 }
 
-static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
+static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 type,
+                                      u8 *data)
 {
        __le32 status_le;
        __le16 seq_le;
@@ -1552,23 +1569,31 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
        u32 genbit;
        u8 flags;
        u16 seq;
+       u8 compcnt;
+       u8 compcnt_offset = BRCMF_FWS_TYPE_TXSTATUS_LEN;
 
-       fws->stats.txs_indicate++;
        memcpy(&status_le, data, sizeof(status_le));
        status = le32_to_cpu(status_le);
        flags = brcmf_txstatus_get_field(status, FLAGS);
        hslot = brcmf_txstatus_get_field(status, HSLOT);
        genbit = brcmf_txstatus_get_field(status, GENERATION);
        if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
-               memcpy(&seq_le, &data[BRCMF_FWS_TYPE_PKTTAG_LEN],
+               memcpy(&seq_le, &data[BRCMF_FWS_TYPE_TXSTATUS_LEN],
                       sizeof(seq_le));
                seq = le16_to_cpu(seq_le);
+               compcnt_offset += BRCMF_FWS_TYPE_SEQ_LEN;
        } else {
                seq = 0;
        }
 
+       if (type == BRCMF_FWS_TYPE_COMP_TXSTATUS)
+               compcnt = data[compcnt_offset];
+       else
+               compcnt = 1;
+       fws->stats.txs_indicate += compcnt;
+
        brcmf_fws_lock(fws);
-       brcmf_fws_txs_process(fws, flags, hslot, genbit, seq);
+       brcmf_fws_txs_process(fws, flags, hslot, genbit, seq, compcnt);
        brcmf_fws_unlock(fws);
        return BRCMF_FWS_RET_OK_NOSCHEDULE;
 }
@@ -1595,19 +1620,21 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
                brcmf_err("event payload too small (%d)\n", e->datalen);
                return -EINVAL;
        }
-       if (fws->creditmap_received)
-               return 0;
 
        fws->creditmap_received = true;
 
        brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
        brcmf_fws_lock(fws);
        for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
-               if (*credits)
+               fws->fifo_credit[i] += credits[i] - fws->init_fifo_credit[i];
+               fws->init_fifo_credit[i] = credits[i];
+               if (fws->fifo_credit[i] > 0)
                        fws->fifo_credit_map |= 1 << i;
                else
                        fws->fifo_credit_map &= ~(1 << i);
-               fws->fifo_credit[i] = *credits++;
+               WARN_ONCE(fws->fifo_credit[i] < 0,
+                         "fifo_credit[%d] is negative(%d)\n", i,
+                         fws->fifo_credit[i]);
        }
        brcmf_fws_schedule_deq(fws);
        brcmf_fws_unlock(fws);
@@ -1882,8 +1909,6 @@ void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
 
                err = BRCMF_FWS_RET_OK_NOSCHEDULE;
                switch (type) {
-               case BRCMF_FWS_TYPE_COMP_TXSTATUS:
-                       break;
                case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
                        rd = (struct brcmf_skb_reorder_data *)skb->cb;
                        rd->reorder = data;
@@ -1906,7 +1931,8 @@ void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
                        err = brcmf_fws_request_indicate(fws, type, data);
                        break;
                case BRCMF_FWS_TYPE_TXSTATUS:
-                       brcmf_fws_txstatus_indicate(fws, data);
+               case BRCMF_FWS_TYPE_COMP_TXSTATUS:
+                       brcmf_fws_txstatus_indicate(fws, type, data);
                        break;
                case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
                        err = brcmf_fws_fifocreditback_indicate(fws, data);
@@ -1995,7 +2021,7 @@ static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
                fws->stats.rollback_failed++;
                hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
                brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
-                                     hslot, 0, 0);
+                                     hslot, 0, 0, 1);
        } else {
                fws->stats.rollback_success++;
                brcmf_fws_return_credits(fws, fifo, 1);
@@ -2013,7 +2039,7 @@ static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
        }
 
        for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) {
-               if (fws->fifo_credit[lender_ac]) {
+               if (fws->fifo_credit[lender_ac] > 0) {
                        fws->credits_borrowed[lender_ac]++;
                        fws->fifo_credit[lender_ac]--;
                        if (fws->fifo_credit[lender_ac] == 0)
@@ -2210,8 +2236,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
                        }
                        continue;
                }
-               while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
-                      (fifo == BRCMF_FWS_FIFO_BCMC))) {
+               while ((fws->fifo_credit[fifo] > 0) ||
+                      ((!fws->bcmc_credit_check) &&
+                       (fifo == BRCMF_FWS_FIFO_BCMC))) {
                        skb = brcmf_fws_deq(fws, fifo);
                        if (!skb)
                                break;
@@ -2222,7 +2249,7 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
                                break;
                }
                if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
-                   (fws->fifo_credit[fifo] == 0) &&
+                   (fws->fifo_credit[fifo] <= 0) &&
                    (!fws->bus_flow_blocked)) {
                        while (brcmf_fws_borrow_credit(fws) == 0) {
                                skb = brcmf_fws_deq(fws, fifo);
@@ -2455,7 +2482,8 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
        }
        brcmf_fws_lock(fws);
        hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
-       brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0);
+       brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0,
+                             1);
        brcmf_fws_unlock(fws);
 }
 
index aee6e59..84e3373 100644 (file)
@@ -27,11 +27,20 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
                    struct brcmf_mp_device *settings)
 {
        struct brcmfmac_sdio_pd *sdio = &settings->bus.sdio;
-       struct device_node *np = dev->of_node;
+       struct device_node *root, *np = dev->of_node;
+       struct property *prop;
        int irq;
        u32 irqf;
        u32 val;
 
+       /* Set board-type to the first string of the machine compatible prop */
+       root = of_find_node_by_path("/");
+       if (root) {
+               prop = of_find_property(root, "compatible", NULL);
+               settings->board_type = of_prop_next_string(prop, NULL);
+               of_node_put(root);
+       }
+
        if (!np || bus_type != BRCMF_BUSTYPE_SDIO ||
            !of_device_is_compatible(np, "brcm,bcm4329-fmac"))
                return;
index 5dea569..16d7dda 100644 (file)
@@ -1785,6 +1785,7 @@ brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo)
        fwreq->items[BRCMF_PCIE_FW_CODE].type = BRCMF_FW_TYPE_BINARY;
        fwreq->items[BRCMF_PCIE_FW_NVRAM].type = BRCMF_FW_TYPE_NVRAM;
        fwreq->items[BRCMF_PCIE_FW_NVRAM].flags = BRCMF_FW_REQF_OPTIONAL;
+       fwreq->board_type = devinfo->settings->board_type;
        /* NVRAM reserves PCI domain 0 for Broadcom's SDK faked bus */
        fwreq->domain_nr = pci_domain_nr(devinfo->pdev->bus) + 1;
        fwreq->bus_nr = devinfo->pdev->bus->number;
@@ -2018,6 +2019,7 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = {
 static const struct pci_device_id brcmf_pcie_devid_table[] = {
        BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID),
        BRCMF_PCIE_DEVICE_SUB(0x4355, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4355),
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_RAW_DEVICE_ID),
        BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID),
        BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
        BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
index b2e1ab5..0cd5b8d 100644 (file)
 #define DCMD_RESP_TIMEOUT      msecs_to_jiffies(2500)
 #define CTL_DONE_TIMEOUT       msecs_to_jiffies(2500)
 
+/* watermark expressed in number of words */
+#define DEFAULT_F2_WATERMARK    0x8
+#define CY_4373_F2_WATERMARK    0x40
+#define CY_43012_F2_WATERMARK    0x60
+
 #ifdef DEBUG
 
 #define BRCMF_TRAP_INFO_SIZE   80
@@ -138,6 +143,8 @@ struct rte_console {
 /* 1: isolate internal sdio signals, put external pads in tri-state; requires
  * sdio bus power cycle to clear (rev 9) */
 #define SBSDIO_DEVCTL_PADS_ISO         0x08
+/* 1: enable F2 Watermark */
+#define SBSDIO_DEVCTL_F2WM_ENAB                0x10
 /* Force SD->SB reset mapping (rev 11) */
 #define SBSDIO_DEVCTL_SB_RST_CTL       0x30
 /*   Determined by CoreControl bit */
@@ -618,6 +625,7 @@ BRCMF_FW_DEF(43455, "brcmfmac43455-sdio");
 BRCMF_FW_DEF(4354, "brcmfmac4354-sdio");
 BRCMF_FW_DEF(4356, "brcmfmac4356-sdio");
 BRCMF_FW_DEF(4373, "brcmfmac4373-sdio");
+BRCMF_FW_DEF(43012, "brcmfmac43012-sdio");
 
 static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
        BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
@@ -637,7 +645,8 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
        BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, 43455),
        BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354),
        BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
-       BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373)
+       BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373),
+       BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012)
 };
 
 static void pkt_align(struct sk_buff *p, int len, int align)
@@ -671,6 +680,14 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
        /* 1st KSO write goes to AOS wake up core if device is asleep  */
        brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err);
 
+       /* In case of 43012 chip, the chip could go down immediately after
+        * KSO bit is cleared. So the further reads of KSO register could
+        * fail. Thereby just bailing out immediately after clearing KSO
+        * bit, to avoid polling of KSO bit.
+        */
+       if (!on && bus->ci->chip == CY_CC_43012_CHIP_ID)
+               return err;
+
        if (on) {
                /* device WAKEUP through KSO:
                 * write bit 0 & read back until
@@ -2396,6 +2413,14 @@ static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len)
        return ret;
 }
 
+static bool brcmf_chip_is_ulp(struct brcmf_chip *ci)
+{
+       if (ci->chip == CY_CC_43012_CHIP_ID)
+               return true;
+       else
+               return false;
+}
+
 static void brcmf_sdio_bus_stop(struct device *dev)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
@@ -2403,7 +2428,7 @@ static void brcmf_sdio_bus_stop(struct device *dev)
        struct brcmf_sdio *bus = sdiodev->bus;
        struct brcmf_core *core = bus->sdio_core;
        u32 local_hostintmask;
-       u8 saveclk;
+       u8 saveclk, bpreq;
        int err;
 
        brcmf_dbg(TRACE, "Enter\n");
@@ -2430,9 +2455,14 @@ static void brcmf_sdio_bus_stop(struct device *dev)
                /* Force backplane clocks to assure F2 interrupt propagates */
                saveclk = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
                                            &err);
-               if (!err)
-                       brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                                          (saveclk | SBSDIO_FORCE_HT), &err);
+               if (!err) {
+                       bpreq = saveclk;
+                       bpreq |= brcmf_chip_is_ulp(bus->ci) ?
+                               SBSDIO_HT_AVAIL_REQ : SBSDIO_FORCE_HT;
+                       brcmf_sdiod_writeb(sdiodev,
+                                          SBSDIO_FUNC1_CHIPCLKCSR,
+                                          bpreq, &err);
+               }
                if (err)
                        brcmf_err("Failed to force clock for F2: err %d\n",
                                  err);
@@ -3322,20 +3352,49 @@ err:
        return bcmerror;
 }
 
+static bool brcmf_sdio_aos_no_decode(struct brcmf_sdio *bus)
+{
+       if (bus->ci->chip == CY_CC_43012_CHIP_ID ||
+           bus->ci->chip == CY_CC_4373_CHIP_ID ||
+           bus->ci->chip == BRCM_CC_4339_CHIP_ID ||
+           bus->ci->chip == BRCM_CC_4345_CHIP_ID ||
+           bus->ci->chip == BRCM_CC_4354_CHIP_ID)
+               return true;
+       else
+               return false;
+}
+
 static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
 {
        int err = 0;
        u8 val;
+       u8 wakeupctrl;
+       u8 cardcap;
+       u8 chipclkcsr;
 
        brcmf_dbg(TRACE, "Enter\n");
 
+       if (brcmf_chip_is_ulp(bus->ci)) {
+               wakeupctrl = SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT;
+               chipclkcsr = SBSDIO_HT_AVAIL_REQ;
+       } else {
+               wakeupctrl = SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
+               chipclkcsr = SBSDIO_FORCE_HT;
+       }
+
+       if (brcmf_sdio_aos_no_decode(bus)) {
+               cardcap = SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC;
+       } else {
+               cardcap = (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
+                          SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT);
+       }
+
        val = brcmf_sdiod_readb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err);
        if (err) {
                brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n");
                return;
        }
-
-       val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
+       val |= 1 << wakeupctrl;
        brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err);
        if (err) {
                brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n");
@@ -3344,8 +3403,7 @@ static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
 
        /* Add CMD14 Support */
        brcmf_sdiod_func0_wb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
-                            (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
-                             SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT),
+                            cardcap,
                             &err);
        if (err) {
                brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n");
@@ -3353,7 +3411,7 @@ static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
        }
 
        brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                          SBSDIO_FORCE_HT, &err);
+                          chipclkcsr, &err);
        if (err) {
                brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n");
                return;
@@ -4045,7 +4103,8 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
        const struct firmware *code;
        void *nvram;
        u32 nvram_len;
-       u8 saveclk;
+       u8 saveclk, bpreq;
+       u8 devctl;
 
        brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err);
 
@@ -4078,8 +4137,11 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
        /* Force clocks on backplane to be sure F2 interrupt propagates */
        saveclk = brcmf_sdiod_readb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR, &err);
        if (!err) {
+               bpreq = saveclk;
+               bpreq |= brcmf_chip_is_ulp(bus->ci) ?
+                       SBSDIO_HT_AVAIL_REQ : SBSDIO_FORCE_HT;
                brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR,
-                                  (saveclk | SBSDIO_FORCE_HT), &err);
+                                  bpreq, &err);
        }
        if (err) {
                brcmf_err("Failed to force clock for F2: err %d\n", err);
@@ -4101,8 +4163,37 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
                brcmf_sdiod_writel(sdiod, core->base + SD_REG(hostintmask),
                                   bus->hostintmask, NULL);
 
-
-               brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, 8, &err);
+               switch (sdiod->func1->device) {
+               case SDIO_DEVICE_ID_CYPRESS_4373:
+                       brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
+                                 CY_4373_F2_WATERMARK);
+                       brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
+                                          CY_4373_F2_WATERMARK, &err);
+                       devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL,
+                                                  &err);
+                       devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
+                       brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl,
+                                          &err);
+                       brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL,
+                                          CY_4373_F2_WATERMARK |
+                                          SBSDIO_MESBUSYCTRL_ENAB, &err);
+                       break;
+               case SDIO_DEVICE_ID_CYPRESS_43012:
+                       brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
+                                 CY_43012_F2_WATERMARK);
+                       brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
+                                          CY_43012_F2_WATERMARK, &err);
+                       devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL,
+                                                  &err);
+                       devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
+                       brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl,
+                                          &err);
+                       break;
+               default:
+                       brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
+                                          DEFAULT_F2_WATERMARK, &err);
+                       break;
+               }
        } else {
                /* Disable F2 again */
                sdio_disable_func(sdiod->func2);
@@ -4174,6 +4265,7 @@ brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus)
 
        fwreq->items[BRCMF_SDIO_FW_CODE].type = BRCMF_FW_TYPE_BINARY;
        fwreq->items[BRCMF_SDIO_FW_NVRAM].type = BRCMF_FW_TYPE_NVRAM;
+       fwreq->board_type = bus->sdiodev->settings->board_type;
 
        return fwreq;
 }
index 7faed83..34b0311 100644 (file)
@@ -77,7 +77,7 @@
 #define SBSDIO_GPIO_OUT                        0x10006
 /* gpio enable */
 #define SBSDIO_GPIO_EN                 0x10007
-/* rev < 7, watermark for sdio device */
+/* rev < 7, watermark for sdio device TX path */
 #define SBSDIO_WATERMARK               0x10008
 /* control busy signal generation */
 #define SBSDIO_DEVICE_CTL              0x10009
 #define SBSDIO_FUNC1_RFRAMEBCHI                0x1001C
 /* MesBusyCtl (rev 11) */
 #define SBSDIO_FUNC1_MESBUSYCTRL       0x1001D
+/* Watermark for sdio device RX path */
+#define SBSDIO_MESBUSY_RXFIFO_WM_MASK  0x7F
+#define SBSDIO_MESBUSY_RXFIFO_WM_SHIFT 0
+/* Enable busy capability for MES access */
+#define SBSDIO_MESBUSYCTRL_ENAB                0x80
+#define SBSDIO_MESBUSYCTRL_ENAB_SHIFT  7
+
 /* Sdio Core Rev 12 */
 #define SBSDIO_FUNC1_WAKEUPCTRL                0x1001E
 #define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK                0x1
index 81ff558..6188275 100644 (file)
@@ -846,8 +846,8 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw,
                status = brcms_c_aggregatable(wl->wlc, tid);
                spin_unlock_bh(&wl->lock);
                if (!status) {
-                       brcms_err(wl->wlc->hw->d11core,
-                                 "START: tid %d is not agg\'able\n", tid);
+                       brcms_dbg_ht(wl->wlc->hw->d11core,
+                                    "START: tid %d is not agg\'able\n", tid);
                        return -EINVAL;
                }
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
index 4960f7d..e9e8337 100644 (file)
@@ -220,13 +220,6 @@ enum phy_cal_mode {
 #define BB_MULT_MASK           0x0000ffff
 #define BB_MULT_VALID_MASK     0x80000000
 
-#define CORDIC_AG      39797
-#define        CORDIC_NI       18
-#define        FIXED(X)        ((s32)((X) << 16))
-
-#define        FLOAT(X) \
-       (((X) >= 0) ? ((((X) >> 15) + 1) >> 1) : -((((-(X)) >> 15) + 1) >> 1))
-
 #define PHY_CHAIN_TX_DISABLE_TEMP      115
 #define PHY_HYSTERESIS_DELTATEMP       5
 
index 9fb0d9f..e78a93a 100644 (file)
@@ -3447,8 +3447,8 @@ wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val,
 
                theta += rot;
 
-               i_samp = (u16) (FLOAT(tone_samp.i * max_val) & 0x3ff);
-               q_samp = (u16) (FLOAT(tone_samp.q * max_val) & 0x3ff);
+               i_samp = (u16)(CORDIC_FLOAT(tone_samp.i * max_val) & 0x3ff);
+               q_samp = (u16)(CORDIC_FLOAT(tone_samp.q * max_val) & 0x3ff);
                data_buf[t] = (i_samp << 10) | q_samp;
        }
 
index a57f271..f4f5e90 100644 (file)
@@ -23089,8 +23089,8 @@ wlc_phy_gen_load_samples_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val,
 
                theta += rot;
 
-               tone_buf[t].q = (s32) FLOAT(tone_buf[t].q * max_val);
-               tone_buf[t].i = (s32) FLOAT(tone_buf[t].i * max_val);
+               tone_buf[t].q = (s32)CORDIC_FLOAT(tone_buf[t].q * max_val);
+               tone_buf[t].i = (s32)CORDIC_FLOAT(tone_buf[t].i * max_val);
        }
 
        wlc_phy_loadsampletable_nphy(pi, tone_buf, num_samps);
index e7584b8..8ac3482 100644 (file)
@@ -128,7 +128,7 @@ static void brcmu_d11n_decchspec(struct brcmu_chan *ch)
                }
                break;
        default:
-               WARN_ON_ONCE(1);
+               WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                break;
        }
 
@@ -140,7 +140,7 @@ static void brcmu_d11n_decchspec(struct brcmu_chan *ch)
                ch->band = BRCMU_CHAN_BAND_2G;
                break;
        default:
-               WARN_ON_ONCE(1);
+               WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                break;
        }
 }
@@ -167,7 +167,7 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
                        ch->sb = BRCMU_CHAN_SB_U;
                        ch->control_ch_num += CH_10MHZ_APART;
                } else {
-                       WARN_ON_ONCE(1);
+                       WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                }
                break;
        case BRCMU_CHSPEC_D11AC_BW_80:
@@ -188,11 +188,14 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
                        ch->control_ch_num += CH_30MHZ_APART;
                        break;
                default:
-                       WARN_ON_ONCE(1);
+                       WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                        break;
                }
                break;
        case BRCMU_CHSPEC_D11AC_BW_160:
+               ch->bw = BRCMU_CHAN_BW_160;
+               ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
+                                        BRCMU_CHSPEC_D11AC_SB_SHIFT);
                switch (ch->sb) {
                case BRCMU_CHAN_SB_LLL:
                        ch->control_ch_num -= CH_70MHZ_APART;
@@ -219,13 +222,13 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
                        ch->control_ch_num += CH_70MHZ_APART;
                        break;
                default:
-                       WARN_ON_ONCE(1);
+                       WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                        break;
                }
                break;
        case BRCMU_CHSPEC_D11AC_BW_8080:
        default:
-               WARN_ON_ONCE(1);
+               WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                break;
        }
 
@@ -237,7 +240,7 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
                ch->band = BRCMU_CHAN_BAND_2G;
                break;
        default:
-               WARN_ON_ONCE(1);
+               WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                break;
        }
 }
index 686f7a8..839980d 100644 (file)
@@ -60,6 +60,7 @@
 #define BRCM_CC_43664_CHIP_ID          43664
 #define BRCM_CC_4371_CHIP_ID           0x4371
 #define CY_CC_4373_CHIP_ID             0x4373
+#define CY_CC_43012_CHIP_ID            43012
 
 /* USB Device IDs */
 #define BRCM_USB_43143_DEVICE_ID       0xbd1e
@@ -74,6 +75,7 @@
 /* PCIE Device IDs */
 #define BRCM_PCIE_4350_DEVICE_ID       0x43a3
 #define BRCM_PCIE_4354_DEVICE_ID       0x43df
+#define BRCM_PCIE_4354_RAW_DEVICE_ID   0x4354
 #define BRCM_PCIE_4356_DEVICE_ID       0x43ec
 #define BRCM_PCIE_43567_DEVICE_ID      0x43d3
 #define BRCM_PCIE_43570_DEVICE_ID      0x43d9
index e1fd499..de8225e 100644 (file)
@@ -269,6 +269,25 @@ struct chipcregs {
 /* GSIO (spi/i2c) present, rev >= 37 */
 #define        CC_CAP2_GSIO            0x00000002
 
+/* sr_control0, rev >= 48 */
+#define CC_SR_CTL0_ENABLE_MASK                 BIT(0)
+#define CC_SR_CTL0_ENABLE_SHIFT                0
+#define CC_SR_CTL0_EN_SR_ENG_CLK_SHIFT 1 /* sr_clk to sr_memory enable */
+#define CC_SR_CTL0_RSRC_TRIGGER_SHIFT  2 /* Rising edge resource trigger 0 to
+                                          * sr_engine
+                                          */
+#define CC_SR_CTL0_MIN_DIV_SHIFT       6 /* Min division value for fast clk
+                                          * in sr_engine
+                                          */
+#define CC_SR_CTL0_EN_SBC_STBY_SHIFT           16
+#define CC_SR_CTL0_EN_SR_ALP_CLK_MASK_SHIFT    18
+#define CC_SR_CTL0_EN_SR_HT_CLK_SHIFT          19
+#define CC_SR_CTL0_ALLOW_PIC_SHIFT     20 /* Allow pic to separate power
+                                           * domains
+                                           */
+#define CC_SR_CTL0_MAX_SR_LQ_CLK_CNT_SHIFT     25
+#define CC_SR_CTL0_EN_MEM_DISABLE_FOR_SLEEP    30
+
 /* pmucapabilities */
 #define PCAP_REV_MASK  0x000000ff
 #define PCAP_RC_MASK   0x00001f00
index 04dd7a9..5512c7f 100644 (file)
@@ -5462,7 +5462,7 @@ static int proc_BSSList_open( struct inode *inode, struct file *file ) {
            we have to add a spin lock... */
        rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
        while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
-               ptr += sprintf(ptr, "%pM %*s rssi = %d",
+               ptr += sprintf(ptr, "%pM %.*s rssi = %d",
                               BSSList_rid.bssid,
                                (int)BSSList_rid.ssidLen,
                                BSSList_rid.ssid,
index d6ec44d..5623955 100644 (file)
@@ -15,9 +15,9 @@ config IPW2100
           A driver for the Intel PRO/Wireless 2100 Network 
          Connection 802.11b wireless network adapter.
 
-          See <file:Documentation/networking/README.ipw2100> for information on
-          the capabilities currently enabled in this driver and for tips
-          for debugging issues and problems.
+          See <file:Documentation/networking/device_drivers/intel/ipw2100.txt>
+         for information on the capabilities currently enabled in this driver
+         and for tips for debugging issues and problems.
 
          In order to use this driver, you will need a firmware image for it.
           You can obtain the firmware from
@@ -77,8 +77,8 @@ config IPW2200
           A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network
          Connection adapters. 
 
-          See <file:Documentation/networking/README.ipw2200> for 
-         information on the capabilities currently enabled in this 
+          See <file:Documentation/networking/device_drivers/intel/ipw2200.txt>
+         for information on the capabilities currently enabled in this
          driver and for tips for debugging issues and problems.
 
          In order to use this driver, you will need a firmware image for it.
index 910db46..52e5ed2 100644 (file)
@@ -5603,12 +5603,8 @@ static void shim__set_security(struct net_device *dev,
 
        if ((sec->flags & SEC_ACTIVE_KEY) &&
            priv->ieee->sec.active_key != sec->active_key) {
-               if (sec->active_key <= 3) {
-                       priv->ieee->sec.active_key = sec->active_key;
-                       priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
-               } else
-                       priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
-
+               priv->ieee->sec.active_key = sec->active_key;
+               priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
                priv->status |= STATUS_SECURITY_UPDATED;
        }
 
@@ -8370,7 +8366,7 @@ static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw)
        if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
                printk(KERN_WARNING DRV_NAME ": Firmware image not compatible "
                       "(detected version id of %u). "
-                      "See Documentation/networking/README.ipw2100\n",
+                      "See Documentation/networking/device_drivers/intel/ipw2100.txt\n",
                       h->version);
                return 1;
        }
index bbdca13..fa400f9 100644 (file)
@@ -10722,11 +10722,8 @@ static void shim__set_security(struct net_device *dev,
        }
 
        if (sec->flags & SEC_ACTIVE_KEY) {
-               if (sec->active_key <= 3) {
-                       priv->ieee->sec.active_key = sec->active_key;
-                       priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
-               } else
-                       priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+               priv->ieee->sec.active_key = sec->active_key;
+               priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
                priv->status |= STATUS_SECURITY_UPDATED;
        } else
                priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
index e8983c6..a697edd 100644 (file)
@@ -781,7 +781,7 @@ il3945_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta,
 
        switch (scale_action) {
        case -1:
-               /* Decrese rate */
+               /* Decrease rate */
                if (low != RATE_INVALID)
                        idx = low;
                break;
index 280cd8a..6b4488a 100644 (file)
@@ -559,7 +559,7 @@ il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in)
                        decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK;
                        break;
                }
-               /* fall through if TTAK OK */
+               /* fall through if TTAK OK */
        default:
                if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK))
                        decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
index 6514baf..a2f86cb 100644 (file)
@@ -2695,6 +2695,7 @@ il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr,
                if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
                    RX_RES_STATUS_BAD_KEY_TTAK)
                        break;
+               /* fall through */
 
        case RX_RES_STATUS_SEC_TYPE_WEP:
                if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
@@ -2704,6 +2705,7 @@ il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr,
                        D_RX("Packet destroyed\n");
                        return -1;
                }
+               /* fall through */
        case RX_RES_STATUS_SEC_TYPE_CCMP:
                if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
                    RX_RES_STATUS_DECRYPT_OK) {
index e5a2fc7..491ca3c 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 && MAC80211 && HAS_IOMEM
+       depends on PCI && HAS_IOMEM
        select FW_LOADER
        ---help---
          Select to build the driver supporting the:
@@ -53,6 +53,7 @@ config IWLWIFI_LEDS
 
 config IWLDVM
        tristate "Intel Wireless WiFi DVM Firmware support"
+       depends on MAC80211
        help
          This is the driver that supports the DVM firmware. The list
          of the devices that use this firmware is available here:
@@ -61,6 +62,7 @@ config IWLDVM
 config IWLMVM
        tristate "Intel Wireless WiFi MVM Firmware support"
        select WANT_DEV_COREDUMP
+       depends on MAC80211
        help
          This is the driver that supports the MVM firmware. The list
          of the devices that use this firmware is available here:
index 04e376c..ff41987 100644 (file)
@@ -11,6 +11,7 @@ iwlwifi-objs          += pcie/ctxt-info.o pcie/ctxt-info-gen3.o
 iwlwifi-objs           += pcie/trans-gen2.o pcie/tx-gen2.o
 iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o
 iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o
+iwlwifi-objs           += iwl-dbg-tlv.o
 iwlwifi-objs           += iwl-trans.o
 iwlwifi-objs           += fw/notif-wait.o
 iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o fw/dbg.o
index 76b5ddb..1ff388b 100644 (file)
@@ -48,7 +48,7 @@
 static const struct iwl_base_params iwl1000_base_params = {
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .max_tfd_queue_size = 256,
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
        .pll_cfg = true,
        .max_ll_items = OTP_MAX_LL_ITEMS_1000,
        .shadow_ram_support = false,
index e7e4584..a6ec7ad 100644 (file)
@@ -57,7 +57,7 @@
 #define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE __stringify(api) ".ucode"
 
 static const struct iwl_base_params iwl2000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .max_tfd_queue_size = 256,
        .max_ll_items = OTP_MAX_LL_ITEMS_2x00,
@@ -71,7 +71,7 @@ static const struct iwl_base_params iwl2000_base_params = {
 
 
 static const struct iwl_base_params iwl2030_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .max_tfd_queue_size = 256,
        .max_ll_items = OTP_MAX_LL_ITEMS_2x00,
index da5d5f9..7e65073 100644 (file)
 #include "iwl-config.h"
 
 /* Highest firmware API version supported */
-#define IWL_22000_UCODE_API_MAX        41
+#define IWL_22000_UCODE_API_MAX        43
 
 /* Lowest firmware API version supported */
 #define IWL_22000_UCODE_API_MIN        39
 
 /* NVM versions */
 #define IWL_22000_NVM_VERSION          0x0a1d
-#define IWL_22000_TX_POWER_VERSION     0xffff /* meaningless */
 
 /* Memory offsets and lengths */
 #define IWL_22000_DCCM_OFFSET          0x800000 /* LMAC1 */
 #define IWL_QU_B_JF_B_MODULE_FIRMWARE(api) \
        IWL_QU_B_JF_B_FW_PRE __stringify(api) ".ucode"
 
-#define NVM_HW_SECTION_NUM_FAMILY_22000                10
-
 static const struct iwl_base_params iwl_22000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_22000,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
        .num_of_queues = 512,
        .max_tfd_queue_size = 256,
        .shadow_ram_support = true,
@@ -121,7 +118,7 @@ static const struct iwl_base_params iwl_22000_base_params = {
 };
 
 static const struct iwl_base_params iwl_22560_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_22000,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
        .num_of_queues = 512,
        .max_tfd_queue_size = 65536,
        .shadow_ram_support = true,
@@ -142,7 +139,7 @@ static const struct iwl_ht_params iwl_22000_ht_params = {
        .ucode_api_max = IWL_22000_UCODE_API_MAX,                       \
        .ucode_api_min = IWL_22000_UCODE_API_MIN,                       \
        .led_mode = IWL_LED_RF_STATE,                                   \
-       .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_22000,          \
+       .nvm_hw_section_num = 10,                                       \
        .non_shared_ant = ANT_B,                                        \
        .dccm_offset = IWL_22000_DCCM_OFFSET,                           \
        .dccm_len = IWL_22000_DCCM_LEN,                                 \
@@ -157,7 +154,6 @@ static const struct iwl_ht_params iwl_22000_ht_params = {
        .mac_addr_from_csr = true,                                      \
        .ht_params = &iwl_22000_ht_params,                              \
        .nvm_ver = IWL_22000_NVM_VERSION,                               \
-       .nvm_calib_ver = IWL_22000_TX_POWER_VERSION,                    \
        .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,            \
        .use_tfh = true,                                                \
        .rf_id = true,                                                  \
@@ -323,7 +319,6 @@ MODULE_FIRMWARE(IWL_22000_HR_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_JF_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_HR_A_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_HR_B_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
-MODULE_FIRMWARE(IWL_22000_QU_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_HR_B_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 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));
index 30e62a7..fbb18d0 100644 (file)
@@ -66,7 +66,7 @@
 #define IWL6030_MODULE_FIRMWARE(api) IWL6030_FW_PRE __stringify(api) ".ucode"
 
 static const struct iwl_base_params iwl6000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .max_tfd_queue_size = 256,
        .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
@@ -79,7 +79,7 @@ static const struct iwl_base_params iwl6000_base_params = {
 };
 
 static const struct iwl_base_params iwl6050_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .max_tfd_queue_size = 256,
        .max_ll_items = OTP_MAX_LL_ITEMS_6x50,
@@ -92,7 +92,7 @@ static const struct iwl_base_params iwl6050_base_params = {
 };
 
 static const struct iwl_base_params iwl6000_g2_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .max_tfd_queue_size = 256,
        .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
index c973bfa..289e3c3 100644 (file)
 
 /* NVM versions */
 #define IWL7260_NVM_VERSION            0x0a1d
-#define IWL7260_TX_POWER_VERSION       0xffff /* meaningless */
 #define IWL3160_NVM_VERSION            0x709
-#define IWL3160_TX_POWER_VERSION       0xffff /* meaningless */
 #define IWL3165_NVM_VERSION            0x709
-#define IWL3165_TX_POWER_VERSION       0xffff /* meaningless */
 #define IWL3168_NVM_VERSION            0xd01
-#define IWL3168_TX_POWER_VERSION       0xffff /* meaningless */
 #define IWL7265_NVM_VERSION            0x0a1d
-#define IWL7265_TX_POWER_VERSION       0xffff /* meaningless */
 #define IWL7265D_NVM_VERSION           0x0c11
-#define IWL7265_TX_POWER_VERSION       0xffff /* meaningless */
 
 /* DCCM offsets and lengths */
 #define IWL7000_DCCM_OFFSET            0x800000
 #define IWL7265D_FW_PRE "iwlwifi-7265D-"
 #define IWL7265D_MODULE_FIRMWARE(api) IWL7265D_FW_PRE __stringify(api) ".ucode"
 
-#define NVM_HW_SECTION_NUM_FAMILY_7000         0
-
 static const struct iwl_base_params iwl7000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_16K,
        .num_of_queues = 31,
        .max_tfd_queue_size = 256,
        .shadow_ram_support = true,
@@ -159,7 +151,7 @@ static const struct iwl_ht_params iwl7000_ht_params = {
        .device_family = IWL_DEVICE_FAMILY_7000,                \
        .base_params = &iwl7000_base_params,                    \
        .led_mode = IWL_LED_RF_STATE,                           \
-       .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000,   \
+       .nvm_hw_section_num = 0,                                \
        .non_shared_ant = ANT_A,                                \
        .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
        .dccm_offset = IWL7000_DCCM_OFFSET,                     \
@@ -191,7 +183,6 @@ const struct iwl_cfg iwl7260_2ac_cfg = {
        IWL_DEVICE_7000,
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL7260_NVM_VERSION,
-       .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
        .host_interrupt_operation_mode = true,
        .lp_xtal_workaround = true,
        .dccm_len = IWL7260_DCCM_LEN,
@@ -203,7 +194,6 @@ const struct iwl_cfg iwl7260_2ac_cfg_high_temp = {
        IWL_DEVICE_7000,
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL7260_NVM_VERSION,
-       .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
        .high_temp = true,
        .host_interrupt_operation_mode = true,
        .lp_xtal_workaround = true,
@@ -217,7 +207,6 @@ const struct iwl_cfg iwl7260_2n_cfg = {
        IWL_DEVICE_7000,
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL7260_NVM_VERSION,
-       .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
        .host_interrupt_operation_mode = true,
        .lp_xtal_workaround = true,
        .dccm_len = IWL7260_DCCM_LEN,
@@ -229,7 +218,6 @@ const struct iwl_cfg iwl7260_n_cfg = {
        IWL_DEVICE_7000,
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL7260_NVM_VERSION,
-       .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
        .host_interrupt_operation_mode = true,
        .lp_xtal_workaround = true,
        .dccm_len = IWL7260_DCCM_LEN,
@@ -241,7 +229,6 @@ const struct iwl_cfg iwl3160_2ac_cfg = {
        IWL_DEVICE_7000,
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL3160_NVM_VERSION,
-       .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
        .host_interrupt_operation_mode = true,
        .dccm_len = IWL3160_DCCM_LEN,
 };
@@ -252,7 +239,6 @@ const struct iwl_cfg iwl3160_2n_cfg = {
        IWL_DEVICE_7000,
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL3160_NVM_VERSION,
-       .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
        .host_interrupt_operation_mode = true,
        .dccm_len = IWL3160_DCCM_LEN,
 };
@@ -263,7 +249,6 @@ const struct iwl_cfg iwl3160_n_cfg = {
        IWL_DEVICE_7000,
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL3160_NVM_VERSION,
-       .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
        .host_interrupt_operation_mode = true,
        .dccm_len = IWL3160_DCCM_LEN,
 };
@@ -291,7 +276,6 @@ const struct iwl_cfg iwl3165_2ac_cfg = {
        IWL_DEVICE_7005D,
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL3165_NVM_VERSION,
-       .nvm_calib_ver = IWL3165_TX_POWER_VERSION,
        .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
        .dccm_len = IWL7265_DCCM_LEN,
 };
@@ -302,7 +286,6 @@ const struct iwl_cfg iwl3168_2ac_cfg = {
        IWL_DEVICE_3008,
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL3168_NVM_VERSION,
-       .nvm_calib_ver = IWL3168_TX_POWER_VERSION,
        .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
        .dccm_len = IWL7265_DCCM_LEN,
        .nvm_type = IWL_NVM_SDP,
@@ -314,7 +297,6 @@ const struct iwl_cfg iwl7265_2ac_cfg = {
        IWL_DEVICE_7005,
        .ht_params = &iwl7265_ht_params,
        .nvm_ver = IWL7265_NVM_VERSION,
-       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
        .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
        .dccm_len = IWL7265_DCCM_LEN,
 };
@@ -325,7 +307,6 @@ const struct iwl_cfg iwl7265_2n_cfg = {
        IWL_DEVICE_7005,
        .ht_params = &iwl7265_ht_params,
        .nvm_ver = IWL7265_NVM_VERSION,
-       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
        .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
        .dccm_len = IWL7265_DCCM_LEN,
 };
@@ -336,7 +317,6 @@ const struct iwl_cfg iwl7265_n_cfg = {
        IWL_DEVICE_7005,
        .ht_params = &iwl7265_ht_params,
        .nvm_ver = IWL7265_NVM_VERSION,
-       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
        .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
        .dccm_len = IWL7265_DCCM_LEN,
 };
@@ -347,7 +327,6 @@ const struct iwl_cfg iwl7265d_2ac_cfg = {
        IWL_DEVICE_7005D,
        .ht_params = &iwl7265_ht_params,
        .nvm_ver = IWL7265D_NVM_VERSION,
-       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
        .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
        .dccm_len = IWL7265_DCCM_LEN,
 };
@@ -358,7 +337,6 @@ const struct iwl_cfg iwl7265d_2n_cfg = {
        IWL_DEVICE_7005D,
        .ht_params = &iwl7265_ht_params,
        .nvm_ver = IWL7265D_NVM_VERSION,
-       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
        .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
        .dccm_len = IWL7265_DCCM_LEN,
 };
@@ -369,7 +347,6 @@ const struct iwl_cfg iwl7265d_n_cfg = {
        IWL_DEVICE_7005D,
        .ht_params = &iwl7265_ht_params,
        .nvm_ver = IWL7265D_NVM_VERSION,
-       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
        .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
        .dccm_len = IWL7265_DCCM_LEN,
 };
index 348c40f..d7d17c1 100644 (file)
@@ -75,7 +75,6 @@
 
 /* NVM versions */
 #define IWL8000_NVM_VERSION            0x0a1d
-#define IWL8000_TX_POWER_VERSION       0xffff /* meaningless */
 
 /* Memory offsets and lengths */
 #define IWL8260_DCCM_OFFSET            0x800000
 #define IWL8265_MODULE_FIRMWARE(api) \
        IWL8265_FW_PRE __stringify(api) ".ucode"
 
-#define NVM_HW_SECTION_NUM_FAMILY_8000         10
 #define DEFAULT_NVM_FILE_FAMILY_8000C          "nvmData-8000C"
 
 static const struct iwl_base_params iwl8000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
        .num_of_queues = 31,
        .max_tfd_queue_size = 256,
        .shadow_ram_support = true,
@@ -139,7 +137,7 @@ static const struct iwl_tt_params iwl8000_tt_params = {
        .device_family = IWL_DEVICE_FAMILY_8000,                        \
        .base_params = &iwl8000_base_params,                            \
        .led_mode = IWL_LED_RF_STATE,                                   \
-       .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000,           \
+       .nvm_hw_section_num = 10,                                       \
        .features = NETIF_F_RXCSUM,                                     \
        .non_shared_ant = ANT_A,                                        \
        .dccm_offset = IWL8260_DCCM_OFFSET,                             \
@@ -177,7 +175,6 @@ const struct iwl_cfg iwl8260_2n_cfg = {
        IWL_DEVICE_8260,
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
-       .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
 };
 
 const struct iwl_cfg iwl8260_2ac_cfg = {
@@ -186,7 +183,6 @@ const struct iwl_cfg iwl8260_2ac_cfg = {
        IWL_DEVICE_8260,
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
-       .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
        .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
@@ -196,7 +192,6 @@ const struct iwl_cfg iwl8265_2ac_cfg = {
        IWL_DEVICE_8265,
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
-       .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
        .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .vht_mu_mimo_supported = true,
 };
@@ -207,7 +202,6 @@ const struct iwl_cfg iwl8275_2ac_cfg = {
        IWL_DEVICE_8265,
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
-       .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
        .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .vht_mu_mimo_supported = true,
 };
@@ -218,7 +212,6 @@ const struct iwl_cfg iwl4165_2ac_cfg = {
        IWL_DEVICE_8000,
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
-       .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
        .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
index d55fd23..f211413 100644 (file)
 #include "fw/file.h"
 
 /* Highest firmware API version supported */
-#define IWL9000_UCODE_API_MAX  41
+#define IWL9000_UCODE_API_MAX  43
 
 /* Lowest firmware API version supported */
 #define IWL9000_UCODE_API_MIN  30
 
 /* NVM versions */
 #define IWL9000_NVM_VERSION            0x0a1d
-#define IWL9000_TX_POWER_VERSION       0xffff /* meaningless */
 
 /* Memory offsets and lengths */
 #define IWL9000_DCCM_OFFSET            0x800000
 #define IWL9260B_MODULE_FIRMWARE(api) \
        IWL9260B_FW_PRE __stringify(api) ".ucode"
 
-#define NVM_HW_SECTION_NUM_FAMILY_9000         10
-
 static const struct iwl_base_params iwl9000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_9000,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
        .num_of_queues = 31,
        .max_tfd_queue_size = 256,
        .shadow_ram_support = true,
@@ -137,7 +134,7 @@ static const struct iwl_tt_params iwl9000_tt_params = {
        .device_family = IWL_DEVICE_FAMILY_9000,                        \
        .base_params = &iwl9000_base_params,                            \
        .led_mode = IWL_LED_RF_STATE,                                   \
-       .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_9000,           \
+       .nvm_hw_section_num = 10,                                       \
        .non_shared_ant = ANT_B,                                        \
        .dccm_offset = IWL9000_DCCM_OFFSET,                             \
        .dccm_len = IWL9000_DCCM_LEN,                                   \
@@ -157,17 +154,17 @@ static const struct iwl_tt_params iwl9000_tt_params = {
        .min_umac_error_event_table = 0x800000,                         \
        .csr = &iwl_csr_v1,                                             \
        .d3_debug_data_base_addr = 0x401000,                            \
-       .d3_debug_data_length = 92 * 1024
+       .d3_debug_data_length = 92 * 1024,                              \
+       .ht_params = &iwl9000_ht_params,                                \
+       .nvm_ver = IWL9000_NVM_VERSION,                                 \
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
 
 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,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
 const struct iwl_cfg iwl9260_2ac_cfg = {
@@ -175,10 +172,6 @@ const struct iwl_cfg iwl9260_2ac_cfg = {
        .fw_name_pre = IWL9260A_FW_PRE,
        .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
 const struct iwl_cfg iwl9260_killer_2ac_cfg = {
@@ -186,10 +179,6 @@ const struct iwl_cfg iwl9260_killer_2ac_cfg = {
        .fw_name_pre = IWL9260A_FW_PRE,
        .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
 const struct iwl_cfg iwl9270_2ac_cfg = {
@@ -197,10 +186,6 @@ const struct iwl_cfg iwl9270_2ac_cfg = {
        .fw_name_pre = IWL9260A_FW_PRE,
        .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
 const struct iwl_cfg iwl9460_2ac_cfg = {
@@ -208,10 +193,6 @@ const struct iwl_cfg iwl9460_2ac_cfg = {
        .fw_name_pre = IWL9260A_FW_PRE,
        .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
 const struct iwl_cfg iwl9460_2ac_cfg_soc = {
@@ -220,10 +201,6 @@ const struct iwl_cfg iwl9460_2ac_cfg_soc = {
        .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
        .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .integrated = true,
        .soc_latency = 5000,
 };
@@ -234,10 +211,6 @@ const struct iwl_cfg iwl9461_2ac_cfg_soc = {
                .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
                .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
                IWL_DEVICE_9000,
-               .ht_params = &iwl9000_ht_params,
-               .nvm_ver = IWL9000_NVM_VERSION,
-               .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-               .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
                .integrated = true,
                .soc_latency = 5000,
 };
@@ -248,10 +221,6 @@ const struct iwl_cfg iwl9462_2ac_cfg_soc = {
                .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
                .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
                IWL_DEVICE_9000,
-               .ht_params = &iwl9000_ht_params,
-               .nvm_ver = IWL9000_NVM_VERSION,
-               .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-               .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
                .integrated = true,
                .soc_latency = 5000,
 };
@@ -261,10 +230,6 @@ const struct iwl_cfg iwl9560_2ac_cfg = {
        .fw_name_pre = IWL9260A_FW_PRE,
        .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
 const struct iwl_cfg iwl9560_2ac_cfg_soc = {
@@ -273,10 +238,6 @@ const struct iwl_cfg iwl9560_2ac_cfg_soc = {
        .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
        .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .integrated = true,
        .soc_latency = 5000,
 };
@@ -287,10 +248,6 @@ const struct iwl_cfg iwl9560_killer_2ac_cfg_soc = {
        .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
        .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .integrated = true,
        .soc_latency = 5000,
 };
@@ -301,10 +258,6 @@ const struct iwl_cfg iwl9560_killer_s_2ac_cfg_soc = {
        .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
        .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .integrated = true,
        .soc_latency = 5000,
 };
@@ -315,10 +268,6 @@ const struct iwl_cfg iwl9460_2ac_cfg_shared_clk = {
        .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
        .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .integrated = true,
        .soc_latency = 5000,
        .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
@@ -330,10 +279,6 @@ const struct iwl_cfg iwl9461_2ac_cfg_shared_clk = {
        .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
        .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .integrated = true,
        .soc_latency = 5000,
        .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
@@ -345,10 +290,6 @@ const struct iwl_cfg iwl9462_2ac_cfg_shared_clk = {
        .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
        .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .integrated = true,
        .soc_latency = 5000,
        .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
@@ -360,10 +301,6 @@ const struct iwl_cfg iwl9560_2ac_cfg_shared_clk = {
        .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
        .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .integrated = true,
        .soc_latency = 5000,
        .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
@@ -375,10 +312,6 @@ const struct iwl_cfg iwl9560_killer_2ac_cfg_shared_clk = {
        .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
        .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .integrated = true,
        .soc_latency = 5000,
        .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
@@ -390,10 +323,6 @@ const struct iwl_cfg iwl9560_killer_s_2ac_cfg_shared_clk = {
        .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
        .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
        IWL_DEVICE_9000,
-       .ht_params = &iwl9000_ht_params,
-       .nvm_ver = IWL9000_NVM_VERSION,
-       .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
        .integrated = true,
        .soc_latency = 5000,
        .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
index 1088ff0..c219bca 100644 (file)
@@ -1224,6 +1224,23 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
        return 0;
 }
 
+static int iwl_nvm_check_version(struct iwl_nvm_data *data,
+                                struct iwl_trans *trans)
+{
+       if (data->nvm_version >= trans->cfg->nvm_ver ||
+           data->calib_version >= trans->cfg->nvm_calib_ver) {
+               IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n",
+                              data->nvm_version, data->calib_version);
+               return 0;
+       }
+
+       IWL_ERR(trans,
+               "Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
+               data->nvm_version, trans->cfg->nvm_ver,
+               data->calib_version,  trans->cfg->nvm_calib_ver);
+       return -EINVAL;
+}
+
 static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
                                                 const struct iwl_cfg *cfg,
                                                 const struct iwl_fw *fw,
index 2439e98..7492dfb 100644 (file)
@@ -6,6 +6,7 @@
  * GPL LICENSE SUMMARY
  *
  * Copyright(c) 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
@@ -26,6 +27,7 @@
  * BSD LICENSE
  *
  * Copyright(c) 2017        Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -81,7 +83,7 @@
 #define ACPI_WRDS_WIFI_DATA_SIZE       (ACPI_SAR_TABLE_SIZE + 2)
 #define ACPI_EWRD_WIFI_DATA_SIZE       ((ACPI_SAR_PROFILE_NUM - 1) * \
                                         ACPI_SAR_TABLE_SIZE + 3)
-#define ACPI_WGDS_WIFI_DATA_SIZE       18
+#define ACPI_WGDS_WIFI_DATA_SIZE       19
 #define ACPI_WRDD_WIFI_DATA_SIZE       2
 #define ACPI_SPLC_WIFI_DATA_SIZE       2
 
index 7f645b6..5e88fa2 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
@@ -127,17 +129,6 @@ struct iwl_phy_cfg_cmd {
        struct iwl_calib_ctrl calib_control;
 } __packed;
 
-#define PHY_CFG_RADIO_TYPE     (BIT(0) | BIT(1))
-#define PHY_CFG_RADIO_STEP     (BIT(2) | BIT(3))
-#define PHY_CFG_RADIO_DASH     (BIT(4) | BIT(5))
-#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7))
-#define PHY_CFG_TX_CHAIN_A     BIT(8)
-#define PHY_CFG_TX_CHAIN_B     BIT(9)
-#define PHY_CFG_TX_CHAIN_C     BIT(10)
-#define PHY_CFG_RX_CHAIN_A     BIT(12)
-#define PHY_CFG_RX_CHAIN_B     BIT(13)
-#define PHY_CFG_RX_CHAIN_C     BIT(14)
-
 /*
  * enum iwl_dc2dc_config_id - flag ids
  *
index eff3249..fdc54a5 100644 (file)
@@ -104,6 +104,11 @@ enum iwl_data_path_subcmd_ids {
         */
        HE_AIR_SNIFFER_CONFIG_CMD = 0x13,
 
+       /**
+        * @RX_NO_DATA_NOTIF: &struct iwl_rx_no_data
+        */
+       RX_NO_DATA_NOTIF = 0xF5,
+
        /**
         * @TLC_MNG_UPDATE_NOTIF: &struct iwl_tlc_update_notif
         */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h
new file mode 100644 (file)
index 0000000..ab82b7a
--- /dev/null
@@ -0,0 +1,401 @@
+/******************************************************************************
+ *
+ * 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) 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) 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_dbg_tlv_h__
+#define __iwl_fw_dbg_tlv_h__
+
+#include <linux/bitops.h>
+
+/*
+ * struct iwl_fw_ini_header: Common Header for all debug group TLV's structures
+ * @tlv_version: version info
+ * @apply_point: &enum iwl_fw_ini_apply_point
+ * @data: TLV data followed
+ **/
+struct iwl_fw_ini_header {
+       __le32 tlv_version;
+       __le32 apply_point;
+       u8 data[];
+} __packed; /* FW_INI_HEADER_TLV_S */
+
+/**
+ * struct iwl_fw_ini_allocation_tlv - (IWL_FW_INI_TLV_TYPE_BUFFER_ALLOCATION)
+ * buffer allocation TLV - for debug
+ *
+ * @iwl_fw_ini_header: header
+ * @allocation_id: &enum iwl_fw_ini_allocation_id - to bind allocation and hcmd
+ *     if needed (DBGC1/DBGC2/SDFX/...)
+ * @buffer_location: type of iwl_fw_ini_buffer_location
+ * @size: size in bytes
+ * @max_fragments: the maximum allowed fragmentation in the desired memory
+ *     allocation above
+ * @min_frag_size: the minimum allowed fragmentation size in bytes
+*/
+struct iwl_fw_ini_allocation_tlv {
+       struct iwl_fw_ini_header header;
+       __le32 allocation_id;
+       __le32 buffer_location;
+       __le32 size;
+       __le32 max_fragments;
+       __le32 min_frag_size;
+} __packed; /* FW_INI_BUFFER_ALLOCATION_TLV_S_VER_1 */
+
+/**
+ * struct iwl_fw_ini_hcmd (IWL_FW_INI_TLV_TYPE_HCMD)
+ * Generic Host command pass through TLV
+ *
+ * @id: the debug configuration command type for instance: 0xf6 / 0xf5 / DHC
+ * @group: the desired cmd group
+ * @padding: all zeros for dword alignment
+ * @data: all of the relevant command (0xf6/0xf5) to be sent
+*/
+struct iwl_fw_ini_hcmd {
+       u8 id;
+       u8 group;
+       __le16 padding;
+       u8 data[0];
+} __packed; /* FW_INI_HCMD_S */
+
+/**
+ * struct iwl_fw_ini_hcmd_tlv
+ * @header: header
+ * @hcmd: a variable length host-command to be sent to apply the configuration.
+ */
+struct iwl_fw_ini_hcmd_tlv {
+       struct iwl_fw_ini_header header;
+       struct iwl_fw_ini_hcmd hcmd;
+} __packed; /* FW_INI_HCMD_TLV_S_VER_1 */
+
+/*
+ * struct iwl_fw_ini_debug_flow_tlv (IWL_FW_INI_TLV_TYPE_DEBUG_FLOW)
+ *
+ * @header: header
+ * @debug_flow_cfg: &enum iwl_fw_ini_debug_flow
+ */
+struct iwl_fw_ini_debug_flow_tlv {
+       struct iwl_fw_ini_header header;
+       __le32 debug_flow_cfg;
+} __packed; /* FW_INI_DEBUG_FLOW_TLV_S_VER_1 */
+
+#define IWL_FW_INI_MAX_REGION_ID       20
+#define IWL_FW_INI_MAX_NAME            32
+/**
+ * struct iwl_fw_ini_region_cfg
+ * @region_id: ID of this dump configuration
+ * @region_type: &enum iwl_fw_ini_region_type
+ * @num_regions: amount of regions in the address array.
+ * @allocation_id: For DRAM type field substitutes for allocation_id.
+ * @name_len: name length
+ * @name: file name to use for this region
+ * @size: size of the data, in bytes.(unused for IWL_FW_INI_REGION_DRAM_BUFFER)
+ * @start_addr: array of addresses. (unused for IWL_FW_INI_REGION_DRAM_BUFFER)
+ */
+struct iwl_fw_ini_region_cfg {
+       __le32 region_id;
+       __le32 region_type;
+       __le32 name_len;
+       u8 name[IWL_FW_INI_MAX_NAME];
+       union {
+               __le32 num_regions;
+               __le32 allocation_id;
+       };
+       __le32 size;
+       __le32 start_addr[];
+} __packed; /* FW_INI_REGION_CONFIG_S */
+
+/**
+ * struct iwl_fw_ini_region_tlv - (IWL_FW_INI_TLV_TYPE_REGION_CFG)
+ * DUMP sections define IDs and triggers that use those IDs TLV
+ * @header: header
+ * @num_regions: how many different region section and IDs are coming next
+ * @iwl_fw_ini_dump dump_config: list of dump configurations
+ */
+struct iwl_fw_ini_region_tlv {
+       struct iwl_fw_ini_header header;
+       __le32 num_regions;
+       struct iwl_fw_ini_region_cfg region_config[];
+} __packed; /* FW_INI_REGION_CFG_S */
+
+/**
+ * struct iwl_fw_ini_trigger - (IWL_FW_INI_TLV_TYPE_DUMP_CFG)
+ * Region sections define IDs and triggers that use those IDs TLV
+ *
+ * @trigger_id: enum &iwl_fw_ini_tigger_id
+ * @ignore_default: override FW TLV with binary TLV
+ * @dump_delay: delay from trigger fire to dump, in usec
+ * @occurrences: max amount of times to be fired
+ * @ignore_consec: ignore consecutive triggers, in usec
+ * @force_restart: force FW restart
+ * @multi_dut: initiate debug dump data on several DUTs
+ * @trigger_data: generic data to be utilized per trigger
+ * @num_regions: number of dump regions defined for this trigger
+ * @data: region IDs
+ */
+struct iwl_fw_ini_trigger {
+       __le32 trigger_id;
+       __le32 ignore_default;
+       __le32 dump_delay;
+       __le32 occurrences;
+       __le32 ignore_consec;
+       __le32 force_restart;
+       __le32 multi_dut;
+       __le32 trigger_data;
+       __le32 num_regions;
+       __le32 data[];
+} __packed; /* FW_INI_TRIGGER_CONFIG_S */
+
+/**
+ * struct iwl_fw_ini_trigger_tlv - (IWL_FW_INI_TLV_TYPE_TRIGGERS_CFG)
+ * DUMP sections define IDs and triggers that use those IDs TLV
+ *
+ * @header: header
+ * @num_triggers: how many different triggers section and IDs are coming next
+ * @trigger_config: list of trigger configurations
+ */
+struct iwl_fw_ini_trigger_tlv {
+       struct iwl_fw_ini_header header;
+       __le32 num_triggers;
+       struct iwl_fw_ini_trigger trigger_config[];
+} __packed; /* FW_INI_TRIGGER_CFG_S */
+
+/**
+ * enum iwl_fw_ini_trigger_id
+ * @IWL_FW_TRIGGER_ID_FW_ASSERT: FW assert
+ * @IWL_FW_TRIGGER_ID_FW_TFD_Q_HANG: TFD queue hang
+ * @IWL_FW_TRIGGER_ID_FW_HW_ERROR: HW assert
+ * @IWL_FW_TRIGGER_ID_FW_TRIGGER_ERROR: FW error notification
+ * @IWL_FW_TRIGGER_ID_FW_TRIGGER_WARNING: FW warning notification
+ * @IWL_FW_TRIGGER_ID_FW_TRIGGER_INFO: FW info notification
+ * @IWL_FW_TRIGGER_ID_FW_TRIGGER_DEBUG: FW debug notification
+ * @IWL_FW_TRIGGER_ID_USER_TRIGGER: User trigger
+ * @IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_INACTIVITY: peer inactivity
+ * @FW_DEBUG_TLV_TRIGGER_ID_HOST_DID_INITIATED_EVENT: undefined
+ * @IWL_FW_TRIGGER_ID_HOST_TX_LATENCY_THRESHOLD_CROSSED: TX latency
+ *     threshold was crossed
+ * @IWL_FW_TRIGGER_ID_HOST_TX_RESPONSE_STATUS_FAILED: TX failed
+ * @IWL_FW_TRIGGER_ID_HOST_OS_REQ_DEAUTH_PEER: Deauth initiated by host
+ * @IWL_FW_TRIGGER_ID_HOST_STOP_GO_REQUEST: stop GO request
+ * @IWL_FW_TRIGGER_ID_HOST_START_GO_REQUEST: start GO request
+ * @IWL_FW_TRIGGER_ID_HOST_JOIN_GROUP_REQUEST: join P2P group request
+ * @IWL_FW_TRIGGER_ID_HOST_SCAN_START: scan started event
+ * @IWL_FW_TRIGGER_ID_HOST_SCAN_SUBMITTED: undefined
+ * @IWL_FW_TRIGGER_ID_HOST_SCAN_PARAMS: undefined
+ * @IWL_FW_TRIGGER_ID_HOST_CHECK_FOR_HANG: undefined
+ * @IWL_FW_TRIGGER_ID_HOST_BAR_RECEIVED: BAR frame was received
+ * @IWL_FW_TRIGGER_ID_HOST_AGG_TX_RESPONSE_STATUS_FAILED: agg TX failed
+ * @IWL_FW_TRIGGER_ID_HOST_EAPOL_TX_RESPONSE_FAILED: EAPOL TX failed
+ * @IWL_FW_TRIGGER_ID_HOST_FAKE_TX_RESPONSE_SUSPECTED: suspicious TX response
+ * @IWL_FW_TRIGGER_ID_HOST_AUTH_REQ_FROM_ASSOC_CLIENT: received suspicious auth
+ * @IWL_FW_TRIGGER_ID_HOST_ROAM_COMPLETE: roaming was completed
+ * @IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAST_FAILED: fast assoc failed
+ * @IWL_FW_TRIGGER_ID_HOST_D3_START: D3 start
+ * @IWL_FW_TRIGGER_ID_HOST_D3_END: D3 end
+ * @IWL_FW_TRIGGER_ID_HOST_BSS_MISSED_BEACONS: missed beacon events
+ * @IWL_FW_TRIGGER_ID_HOST_P2P_CLIENT_MISSED_BEACONS: P2P missed beacon events
+ * @IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_TX_FAILURES:  undefined
+ * @IWL_FW_TRIGGER_ID_HOST_TX_WFD_ACTION_FRAME_FAILED: undefined
+ * @IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAILED: authentication / association
+ *     failed
+ * @IWL_FW_TRIGGER_ID_HOST_SCAN_COMPLETE: scan complete event
+ * @IWL_FW_TRIGGER_ID_HOST_SCAN_ABORT: scan abort complete
+ * @IWL_FW_TRIGGER_ID_HOST_NIC_ALIVE: nic alive message was received
+ * @IWL_FW_TRIGGER_ID_HOST_CHANNEL_SWITCH_COMPLETE: CSA was completed
+ * @IWL_FW_TRIGGER_ID_NUM: number of trigger IDs
+ */
+enum iwl_fw_ini_trigger_id {
+       /* Errors triggers */
+       IWL_FW_TRIGGER_ID_FW_ASSERT                             = 1,
+       IWL_FW_TRIGGER_ID_FW_TFD_Q_HANG                         = 2,
+       IWL_FW_TRIGGER_ID_FW_HW_ERROR                           = 3,
+       /* Generic triggers */
+       IWL_FW_TRIGGER_ID_FW_TRIGGER_ERROR                      = 4,
+       IWL_FW_TRIGGER_ID_FW_TRIGGER_WARNING                    = 5,
+       IWL_FW_TRIGGER_ID_FW_TRIGGER_INFO                       = 6,
+       IWL_FW_TRIGGER_ID_FW_TRIGGER_DEBUG                      = 7,
+       /* User Trigger */
+       IWL_FW_TRIGGER_ID_USER_TRIGGER                          = 8,
+       /* Host triggers */
+       IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_INACTIVITY           = 9,
+       IWL_FW_TRIGGER_ID_HOST_DID_INITIATED_EVENT              = 10,
+       IWL_FW_TRIGGER_ID_HOST_TX_LATENCY_THRESHOLD_CROSSED     = 11,
+       IWL_FW_TRIGGER_ID_HOST_TX_RESPONSE_STATUS_FAILED        = 12,
+       IWL_FW_TRIGGER_ID_HOST_OS_REQ_DEAUTH_PEER               = 13,
+       IWL_FW_TRIGGER_ID_HOST_STOP_GO_REQUEST                  = 14,
+       IWL_FW_TRIGGER_ID_HOST_START_GO_REQUEST                 = 15,
+       IWL_FW_TRIGGER_ID_HOST_JOIN_GROUP_REQUEST               = 16,
+       IWL_FW_TRIGGER_ID_HOST_SCAN_START                       = 17,
+       IWL_FW_TRIGGER_ID_HOST_SCAN_SUBITTED                    = 18,
+       IWL_FW_TRIGGER_ID_HOST_SCAN_PARAMS                      = 19,
+       IWL_FW_TRIGGER_ID_HOST_CHECK_FOR_HANG                   = 20,
+       IWL_FW_TRIGGER_ID_HOST_BAR_RECEIVED                     = 21,
+       IWL_FW_TRIGGER_ID_HOST_AGG_TX_RESPONSE_STATUS_FAILED    = 22,
+       IWL_FW_TRIGGER_ID_HOST_EAPOL_TX_RESPONSE_FAILED         = 23,
+       IWL_FW_TRIGGER_ID_HOST_FAKE_TX_RESPONSE_SUSPECTED       = 24,
+       IWL_FW_TRIGGER_ID_HOST_AUTH_REQ_FROM_ASSOC_CLIENT       = 25,
+       IWL_FW_TRIGGER_ID_HOST_ROAM_COMPLETE                    = 26,
+       IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAST_FAILED           = 27,
+       IWL_FW_TRIGGER_ID_HOST_D3_START                         = 28,
+       IWL_FW_TRIGGER_ID_HOST_D3_END                           = 29,
+       IWL_FW_TRIGGER_ID_HOST_BSS_MISSED_BEACONS               = 30,
+       IWL_FW_TRIGGER_ID_HOST_P2P_CLIENT_MISSED_BEACONS        = 31,
+       IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_TX_FAILURES          = 32,
+       IWL_FW_TRIGGER_ID_HOST_TX_WFD_ACTION_FRAME_FAILED       = 33,
+       IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAILED                = 34,
+       IWL_FW_TRIGGER_ID_HOST_SCAN_COMPLETE                    = 35,
+       IWL_FW_TRIGGER_ID_HOST_SCAN_ABORT                       = 36,
+       IWL_FW_TRIGGER_ID_HOST_NIC_ALIVE                        = 37,
+       IWL_FW_TRIGGER_ID_HOST_CHANNEL_SWITCH_COMPLETE          = 38,
+       IWL_FW_TRIGGER_ID_NUM,
+}; /* FW_INI_TRIGGER_ID_E_VER_1 */
+
+/**
+ * enum iwl_fw_ini_apply_point
+ * @IWL_FW_INI_APPLY_INVALID: invalid
+ * @IWL_FW_INI_APPLY_EARLY: pre loading FW
+ * @IWL_FW_INI_APPLY_AFTER_ALIVE: first cmd from host after alive
+ * @IWL_FW_INI_APPLY_POST_INIT: last cmd in initialization sequence
+ * @IWL_FW_INI_APPLY_MISSED_BEACONS: missed beacons notification
+ * @IWL_FW_INI_APPLY_SCAN_COMPLETE: scan completed
+ * @IWL_FW_INI_APPLY_NUM: number of apply points
+*/
+enum iwl_fw_ini_apply_point {
+       IWL_FW_INI_APPLY_INVALID,
+       IWL_FW_INI_APPLY_EARLY,
+       IWL_FW_INI_APPLY_AFTER_ALIVE,
+       IWL_FW_INI_APPLY_POST_INIT,
+       IWL_FW_INI_APPLY_MISSED_BEACONS,
+       IWL_FW_INI_APPLY_SCAN_COMPLETE,
+       IWL_FW_INI_APPLY_NUM,
+}; /* FW_INI_APPLY_POINT_E_VER_1 */
+
+/**
+ * enum iwl_fw_ini_allocation_id
+ * @IWL_FW_INI_ALLOCATION_INVALID: invalid
+ * @IWL_FW_INI_ALLOCATION_ID_DBGC1: allocation meant for DBGC1 configuration
+ * @IWL_FW_INI_ALLOCATION_ID_DBGC2: allocation meant for DBGC2 configuration
+ * @IWL_FW_INI_ALLOCATION_ID_DBGC3: allocation meant for DBGC3 configuration
+ * @IWL_FW_INI_ALLOCATION_ID_SDFX: for SDFX module
+ * @IWL_FW_INI_ALLOCATION_ID_FW_DUMP: used for crash and runtime dumps
+ * @IWL_FW_INI_ALLOCATION_ID_USER_DEFINED: for future user scenarios
+*/
+enum iwl_fw_ini_allocation_id {
+       IWL_FW_INI_ALLOCATION_INVALID,
+       IWL_FW_INI_ALLOCATION_ID_DBGC1,
+       IWL_FW_INI_ALLOCATION_ID_DBGC2,
+       IWL_FW_INI_ALLOCATION_ID_DBGC3,
+       IWL_FW_INI_ALLOCATION_ID_SDFX,
+       IWL_FW_INI_ALLOCATION_ID_FW_DUMP,
+       IWL_FW_INI_ALLOCATION_ID_USER_DEFINED,
+}; /* FW_INI_ALLOCATION_ID_E_VER_1 */
+
+/**
+ * enum iwl_fw_ini_buffer_location
+ * @IWL_FW_INI_LOCATION_INVALID: invalid
+ * @IWL_FW_INI_LOCATION_SRAM_PATH: SRAM location
+ * @IWL_FW_INI_LOCATION_DRAM_PATH: DRAM location
+ */
+enum iwl_fw_ini_buffer_location {
+       IWL_FW_INI_LOCATION_SRAM_INVALID,
+       IWL_FW_INI_LOCATION_SRAM_PATH,
+       IWL_FW_INI_LOCATION_DRAM_PATH,
+}; /* FW_INI_BUFFER_LOCATION_E_VER_1 */
+
+/**
+ * enum iwl_fw_ini_debug_flow
+ * @IWL_FW_INI_DEBUG_INVALID: invalid
+ * @IWL_FW_INI_DEBUG_DBTR_FLOW: undefined
+ * @IWL_FW_INI_DEBUG_TB2DTF_FLOW: undefined
+ */
+enum iwl_fw_ini_debug_flow {
+       IWL_FW_INI_DEBUG_INVALID,
+       IWL_FW_INI_DEBUG_DBTR_FLOW,
+       IWL_FW_INI_DEBUG_TB2DTF_FLOW,
+}; /* FW_INI_DEBUG_FLOW_E_VER_1 */
+
+/**
+ * enum iwl_fw_ini_region_type
+ * @IWL_FW_INI_REGION_INVALID: invalid
+ * @IWL_FW_INI_REGION_DEVICE_MEMORY: device internal memory
+ * @IWL_FW_INI_REGION_PERIPHERY_MAC: periphery registers of MAC
+ * @IWL_FW_INI_REGION_PERIPHERY_PHY: periphery registers of PHY
+ * @IWL_FW_INI_REGION_PERIPHERY_AUX: periphery registers of AUX
+ * @IWL_FW_INI_REGION_DRAM_BUFFER: DRAM buffer
+ * @IWL_FW_INI_REGION_DRAM_IMR: IMR memory
+ * @IWL_FW_INI_REGION_INTERNAL_BUFFER: undefined
+ * @IWL_FW_INI_REGION_TXF: TX fifos
+ * @IWL_FW_INI_REGION_RXF: RX fifo
+ * @IWL_FW_INI_REGION_PAGING: paging memory
+ * @IWL_FW_INI_REGION_CSR: CSR registers
+ * @IWL_FW_INI_REGION_NUM: number of region types
+ */
+enum iwl_fw_ini_region_type {
+       IWL_FW_INI_REGION_INVALID,
+       IWL_FW_INI_REGION_DEVICE_MEMORY,
+       IWL_FW_INI_REGION_PERIPHERY_MAC,
+       IWL_FW_INI_REGION_PERIPHERY_PHY,
+       IWL_FW_INI_REGION_PERIPHERY_AUX,
+       IWL_FW_INI_REGION_DRAM_BUFFER,
+       IWL_FW_INI_REGION_DRAM_IMR,
+       IWL_FW_INI_REGION_INTERNAL_BUFFER,
+       IWL_FW_INI_REGION_TXF,
+       IWL_FW_INI_REGION_RXF,
+       IWL_FW_INI_REGION_PAGING,
+       IWL_FW_INI_REGION_CSR,
+       IWL_FW_INI_REGION_NUM
+}; /* FW_INI_REGION_TYPE_E_VER_1*/
+
+#endif
index 1dd23f8..7a3f7b7 100644 (file)
@@ -151,9 +151,9 @@ enum iwl_tsf_id {
  * @beacon_time: beacon transmit time in system time
  * @beacon_tsf: beacon transmit time in TSF
  * @bi: beacon interval in TU
- * @bi_reciprocal: 2^32 / bi
+ * @reserved1: reserved
  * @dtim_interval: dtim transmit time in TU
- * @dtim_reciprocal: 2^32 / dtim_interval
+ * @reserved2: reserved
  * @mcast_qid: queue ID for multicast traffic.
  *     NOTE: obsolete from VER2 and on
  * @beacon_template: beacon template ID
@@ -162,9 +162,9 @@ struct iwl_mac_data_ap {
        __le32 beacon_time;
        __le64 beacon_tsf;
        __le32 bi;
-       __le32 bi_reciprocal;
+       __le32 reserved1;
        __le32 dtim_interval;
-       __le32 dtim_reciprocal;
+       __le32 reserved2;
        __le32 mcast_qid;
        __le32 beacon_template;
 } __packed; /* AP_MAC_DATA_API_S_VER_2 */
@@ -174,26 +174,34 @@ struct iwl_mac_data_ap {
  * @beacon_time: beacon transmit time in system time
  * @beacon_tsf: beacon transmit time in TSF
  * @bi: beacon interval in TU
- * @bi_reciprocal: 2^32 / bi
+ * @reserved: reserved
  * @beacon_template: beacon template ID
  */
 struct iwl_mac_data_ibss {
        __le32 beacon_time;
        __le64 beacon_tsf;
        __le32 bi;
-       __le32 bi_reciprocal;
+       __le32 reserved;
        __le32 beacon_template;
 } __packed; /* IBSS_MAC_DATA_API_S_VER_1 */
 
+/**
+ * enum iwl_mac_data_policy - policy of the data path for this MAC
+ * @TWT_SUPPORTED: twt is supported
+ */
+enum iwl_mac_data_policy {
+       TWT_SUPPORTED   = BIT(0),
+};
+
 /**
  * struct iwl_mac_data_sta - configuration data for station MAC context
  * @is_assoc: 1 for associated state, 0 otherwise
  * @dtim_time: DTIM arrival time in system time
  * @dtim_tsf: DTIM arrival time in TSF
  * @bi: beacon interval in TU, applicable only when associated
- * @bi_reciprocal: 2^32 / bi , applicable only when associated
+ * @reserved1: reserved
  * @dtim_interval: DTIM interval in TU, applicable only when associated
- * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated
+ * @data_policy: see &enum iwl_mac_data_policy
  * @listen_interval: in beacon intervals, applicable only when associated
  * @assoc_id: unique ID assigned by the AP during association
  * @assoc_beacon_arrive_time: TSF of first beacon after association
@@ -203,13 +211,13 @@ struct iwl_mac_data_sta {
        __le32 dtim_time;
        __le64 dtim_tsf;
        __le32 bi;
-       __le32 bi_reciprocal;
+       __le32 reserved1;
        __le32 dtim_interval;
-       __le32 dtim_reciprocal;
+       __le32 data_policy;
        __le32 listen_interval;
        __le32 assoc_id;
        __le32 assoc_beacon_arrive_time;
-} __packed; /* STA_MAC_DATA_API_S_VER_1 */
+} __packed; /* STA_MAC_DATA_API_S_VER_2 */
 
 /**
  * struct iwl_mac_data_go - configuration data for P2P GO MAC context
@@ -233,7 +241,7 @@ struct iwl_mac_data_go {
 struct iwl_mac_data_p2p_sta {
        struct iwl_mac_data_sta sta;
        __le32 ctwin;
-} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */
+} __packed; /* P2P_STA_MAC_DATA_API_S_VER_2 */
 
 /**
  * struct iwl_mac_data_pibss - Pseudo IBSS config data
@@ -378,13 +386,6 @@ struct iwl_mac_ctx_cmd {
        };
 } __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */
 
-static inline u32 iwl_mvm_reciprocal(u32 v)
-{
-       if (!v)
-               return 0;
-       return 0xFFFFFFFF / v;
-}
-
 #define IWL_NONQOS_SEQ_GET     0x1
 #define IWL_NONQOS_SEQ_SET     0x2
 struct iwl_nonqos_seq_query_cmd {
@@ -442,7 +443,7 @@ struct iwl_he_backoff_conf {
  * Support for Nss x BW (or RU) matrix:
  *     (0=SISO, 1=MIMO2) x (0-20MHz, 1-40MHz, 2-80MHz, 3-160MHz)
  * Each entry contains 2 QAM thresholds for 8us and 16us:
- *     0=BPSK, 1=QPSK, 2=16QAM, 3=64QAM, 4=256QAM, 5=1024QAM, 6/7=RES
+ *     0=BPSK, 1=QPSK, 2=16QAM, 3=64QAM, 4=256QAM, 5=1024QAM, 6=RES, 7=NONE
  * i.e. QAM_th1 < QAM_th2 such if TX uses QAM_tx:
  *     QAM_tx < QAM_th1            --> PPE=0us
  *     QAM_th1 <= QAM_tx < QAM_th2 --> PPE=8us
index 0537496..0791a85 100644 (file)
@@ -345,66 +345,98 @@ enum iwl_rx_mpdu_mac_info {
        IWL_RX_MPDU_PHY_PHY_INDEX_MASK          = 0xf0,
 };
 
-/*
- * enum iwl_rx_he_phy - HE PHY data
- */
-enum iwl_rx_he_phy {
-       IWL_RX_HE_PHY_BEAM_CHNG                 = BIT(0),
-       IWL_RX_HE_PHY_UPLINK                    = BIT(1),
-       IWL_RX_HE_PHY_BSS_COLOR_MASK            = 0xfc,
-       IWL_RX_HE_PHY_SPATIAL_REUSE_MASK        = 0xf00,
-       IWL_RX_HE_PHY_SU_EXT_BW10               = BIT(12),
-       IWL_RX_HE_PHY_TXOP_DUR_MASK             = 0xfe000,
-       IWL_RX_HE_PHY_LDPC_EXT_SYM              = BIT(20),
-       IWL_RX_HE_PHY_PRE_FEC_PAD_MASK          = 0x600000,
-       IWL_RX_HE_PHY_PE_DISAMBIG               = BIT(23),
-       IWL_RX_HE_PHY_DOPPLER                   = BIT(24),
+/* TSF overload low dword */
+enum iwl_rx_phy_data0 {
+       /* info type: HE any */
+       IWL_RX_PHY_DATA0_HE_BEAM_CHNG                           = 0x00000001,
+       IWL_RX_PHY_DATA0_HE_UPLINK                              = 0x00000002,
+       IWL_RX_PHY_DATA0_HE_BSS_COLOR_MASK                      = 0x000000fc,
+       IWL_RX_PHY_DATA0_HE_SPATIAL_REUSE_MASK                  = 0x00000f00,
+       /* 1 bit reserved */
+       IWL_RX_PHY_DATA0_HE_TXOP_DUR_MASK                       = 0x000fe000,
+       IWL_RX_PHY_DATA0_HE_LDPC_EXT_SYM                        = 0x00100000,
+       IWL_RX_PHY_DATA0_HE_PRE_FEC_PAD_MASK                    = 0x00600000,
+       IWL_RX_PHY_DATA0_HE_PE_DISAMBIG                         = 0x00800000,
+       IWL_RX_PHY_DATA0_HE_DOPPLER                             = 0x01000000,
        /* 6 bits reserved */
-       IWL_RX_HE_PHY_DELIM_EOF                 = BIT(31),
+       IWL_RX_PHY_DATA0_HE_DELIM_EOF                           = 0x80000000,
+};
+
+enum iwl_rx_phy_info_type {
+       IWL_RX_PHY_INFO_TYPE_NONE                               = 0,
+       IWL_RX_PHY_INFO_TYPE_CCK                                = 1,
+       IWL_RX_PHY_INFO_TYPE_OFDM_LGCY                          = 2,
+       IWL_RX_PHY_INFO_TYPE_HT                                 = 3,
+       IWL_RX_PHY_INFO_TYPE_VHT_SU                             = 4,
+       IWL_RX_PHY_INFO_TYPE_VHT_MU                             = 5,
+       IWL_RX_PHY_INFO_TYPE_HE_SU                              = 6,
+       IWL_RX_PHY_INFO_TYPE_HE_MU                              = 7,
+       IWL_RX_PHY_INFO_TYPE_HE_TB                              = 8,
+       IWL_RX_PHY_INFO_TYPE_HE_MU_EXT                          = 9,
+       IWL_RX_PHY_INFO_TYPE_HE_TB_EXT                          = 10,
+};
 
-       /* second dword - common data */
-       IWL_RX_HE_PHY_HE_LTF_NUM_MASK           = 0xe000000000ULL,
-       IWL_RX_HE_PHY_RU_ALLOC_SEC80            = BIT_ULL(32 + 8),
+/* TSF overload high dword */
+enum iwl_rx_phy_data1 {
+       /*
+        * check this first - if TSF overload is set,
+        * see &enum iwl_rx_phy_info_type
+        */
+       IWL_RX_PHY_DATA1_INFO_TYPE_MASK                         = 0xf0000000,
+
+       /* info type: HT/VHT/HE any */
+       IWL_RX_PHY_DATA1_LSIG_LEN_MASK                          = 0x0fff0000,
+
+       /* info type: HE MU/MU-EXT */
+       IWL_RX_PHY_DATA1_HE_MU_SIGB_COMPRESSION                 = 0x00000001,
+       IWL_RX_PHY_DATA1_HE_MU_SIBG_SYM_OR_USER_NUM_MASK        = 0x0000001e,
+
+       /* info type: HE any */
+       IWL_RX_PHY_DATA1_HE_LTF_NUM_MASK                        = 0x000000e0,
+       IWL_RX_PHY_DATA1_HE_RU_ALLOC_SEC80                      = 0x00000100,
        /* trigger encoded */
-       IWL_RX_HE_PHY_RU_ALLOC_MASK             = 0xfe0000000000ULL,
-       IWL_RX_HE_PHY_INFO_TYPE_MASK            = 0xf000000000000000ULL,
-       IWL_RX_HE_PHY_INFO_TYPE_SU              = 0x0, /* TSF low valid (first DW) */
-       IWL_RX_HE_PHY_INFO_TYPE_MU              = 0x1, /* TSF low/high valid (both DWs) */
-       IWL_RX_HE_PHY_INFO_TYPE_MU_EXT_INFO     = 0x2, /* same + SIGB-common0/1/2 valid */
-       IWL_RX_HE_PHY_INFO_TYPE_TB              = 0x3, /* TSF low/high valid (both DWs) */
-
-       /* second dword - MU data */
-       IWL_RX_HE_PHY_MU_SIGB_COMPRESSION               = BIT_ULL(32 + 0),
-       IWL_RX_HE_PHY_MU_SIBG_SYM_OR_USER_NUM_MASK      = 0x1e00000000ULL,
-       IWL_RX_HE_PHY_MU_SIGB_MCS_MASK                  = 0xf000000000000ULL,
-       IWL_RX_HE_PHY_MU_SIGB_DCM                       = BIT_ULL(32 + 21),
-       IWL_RX_HE_PHY_MU_PREAMBLE_PUNC_TYPE_MASK        = 0xc0000000000000ULL,
-
-       /* second dword - TB data */
-       IWL_RX_HE_PHY_TB_PILOT_TYPE                     = BIT_ULL(32 + 0),
-       IWL_RX_HE_PHY_TB_LOW_SS_MASK                    = 0xe00000000ULL
+       IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK                       = 0x0000fe00,
+
+       /* info type: HE TB/TX-EXT */
+       IWL_RX_PHY_DATA1_HE_TB_PILOT_TYPE                       = 0x00000001,
+       IWL_RX_PHY_DATA1_HE_TB_LOW_SS_MASK                      = 0x0000000e,
 };
 
-enum iwl_rx_he_sigb_common0 {
+/* goes into Metadata DW 7 */
+enum iwl_rx_phy_data2 {
+       /* info type: HE MU-EXT */
        /* the a1/a2/... is what the PHY/firmware calls the values */
-       IWL_RX_HE_SIGB_COMMON0_CH1_RU0          = 0x000000ff, /* a1 */
-       IWL_RX_HE_SIGB_COMMON0_CH1_RU2          = 0x0000ff00, /* a2 */
-       IWL_RX_HE_SIGB_COMMON0_CH2_RU0          = 0x00ff0000, /* b1 */
-       IWL_RX_HE_SIGB_COMMON0_CH2_RU2          = 0xff000000, /* b2 */
+       IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU0              = 0x000000ff, /* a1 */
+       IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU2              = 0x0000ff00, /* a2 */
+       IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU0              = 0x00ff0000, /* b1 */
+       IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU2              = 0xff000000, /* b2 */
+
+       /* info type: HE TB-EXT */
+       IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE1          = 0x0000000f,
+       IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE2          = 0x000000f0,
+       IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE3          = 0x00000f00,
+       IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE4          = 0x0000f000,
 };
 
-enum iwl_rx_he_sigb_common1 {
-       IWL_RX_HE_SIGB_COMMON1_CH1_RU1          = 0x000000ff, /* c1 */
-       IWL_RX_HE_SIGB_COMMON1_CH1_RU3          = 0x0000ff00, /* c2 */
-       IWL_RX_HE_SIGB_COMMON1_CH2_RU1          = 0x00ff0000, /* d1 */
-       IWL_RX_HE_SIGB_COMMON1_CH2_RU3          = 0xff000000, /* d2 */
+/* goes into Metadata DW 8 */
+enum iwl_rx_phy_data3 {
+       /* info type: HE MU-EXT */
+       IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU1              = 0x000000ff, /* c1 */
+       IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU3              = 0x0000ff00, /* c2 */
+       IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU1              = 0x00ff0000, /* d1 */
+       IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU3              = 0xff000000, /* d2 */
 };
 
-enum iwl_rx_he_sigb_common2 {
-       IWL_RX_HE_SIGB_COMMON2_CH1_CTR_RU       = 0x0001,
-       IWL_RX_HE_SIGB_COMMON2_CH2_CTR_RU       = 0x0002,
-       IWL_RX_HE_SIGB_COMMON2_CH1_CRC_OK       = 0x0004,
-       IWL_RX_HE_SIGB_COMMON2_CH2_CRC_OK       = 0x0008,
+/* goes into Metadata DW 4 high 16 bits */
+enum iwl_rx_phy_data4 {
+       /* info type: HE MU-EXT */
+       IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CTR_RU                   = 0x0001,
+       IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CTR_RU                   = 0x0002,
+       IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CRC_OK                   = 0x0004,
+       IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CRC_OK                   = 0x0008,
+       IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_MCS_MASK                = 0x00f0,
+       IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_DCM                     = 0x0100,
+       IWL_RX_PHY_DATA4_HE_MU_EXT_PREAMBLE_PUNC_TYPE_MASK      = 0x0600,
 };
 
 /**
@@ -419,9 +451,9 @@ struct iwl_rx_mpdu_desc_v1 {
                __le32 rss_hash;
 
                /**
-                * @sigb_common0: for HE sniffer, HE-SIG-B common part 0
+                * @phy_data2: depends on info type (see @phy_data1)
                 */
-               __le32 sigb_common0;
+               __le32 phy_data2;
        };
 
        /* DW8 - carries filter_match only when rpa_en == 1 */
@@ -432,9 +464,9 @@ struct iwl_rx_mpdu_desc_v1 {
                __le32 filter_match;
 
                /**
-                * @sigb_common1: for HE sniffer, HE-SIG-B common part 1
+                * @phy_data3: depends on info type (see @phy_data1)
                 */
-               __le32 sigb_common1;
+               __le32 phy_data3;
        };
 
        /* DW9 */
@@ -472,12 +504,19 @@ struct iwl_rx_mpdu_desc_v1 {
                 * %IWL_RX_MPDU_PHY_TSF_OVERLOAD isn't set
                 */
                __le64 tsf_on_air_rise;
-               /**
-                * @he_phy_data:
-                * HE PHY data, see &enum iwl_rx_he_phy, valid
-                * only if %IWL_RX_MPDU_PHY_TSF_OVERLOAD is set
-                */
-               __le64 he_phy_data;
+
+               struct {
+                       /**
+                        * @phy_data0: depends on info_type, see @phy_data1
+                        */
+                       __le32 phy_data0;
+                       /**
+                        * @phy_data1: valid only if
+                        * %IWL_RX_MPDU_PHY_TSF_OVERLOAD is set,
+                        * see &enum iwl_rx_phy_data1.
+                        */
+                       __le32 phy_data1;
+               };
        };
 } __packed;
 
@@ -493,9 +532,9 @@ struct iwl_rx_mpdu_desc_v3 {
                __le32 filter_match;
 
                /**
-                * @sigb_common0: for HE sniffer, HE-SIG-B common part 0
+                * @phy_data2: depends on info type (see @phy_data1)
                 */
-               __le32 sigb_common0;
+               __le32 phy_data2;
        };
 
        /* DW8 - carries rss_hash only when rpa_en == 1 */
@@ -506,9 +545,9 @@ struct iwl_rx_mpdu_desc_v3 {
                __le32 rss_hash;
 
                /**
-                * @sigb_common1: for HE sniffer, HE-SIG-B common part 1
+                * @phy_data3: depends on info type (see @phy_data1)
                 */
-               __le32 sigb_common1;
+               __le32 phy_data3;
        };
        /* DW9 */
        /**
@@ -556,12 +595,19 @@ struct iwl_rx_mpdu_desc_v3 {
                 * %IWL_RX_MPDU_PHY_TSF_OVERLOAD isn't set
                 */
                __le64 tsf_on_air_rise;
-               /**
-                * @he_phy_data:
-                * HE PHY data, see &enum iwl_rx_he_phy, valid
-                * only if %IWL_RX_MPDU_PHY_TSF_OVERLOAD is set
-                */
-               __le64 he_phy_data;
+
+               struct {
+                       /**
+                        * @phy_data0: depends on info_type, see @phy_data1
+                        */
+                       __le32 phy_data0;
+                       /**
+                        * @phy_data1: valid only if
+                        * %IWL_RX_MPDU_PHY_TSF_OVERLOAD is set,
+                        * see &enum iwl_rx_phy_data1.
+                        */
+                       __le32 phy_data1;
+               };
        };
        /* DW16 & DW17 */
        /**
@@ -613,9 +659,9 @@ struct iwl_rx_mpdu_desc {
                __le16 l3l4_flags;
 
                /**
-                * @sigb_common2: for HE sniffer, HE-SIG-B common part 2
+                * @phy_data4: depends on info type, see phy_data1
                 */
-               __le16 sigb_common2;
+               __le16 phy_data4;
        };
        /* DW5 */
        /**
@@ -651,6 +697,55 @@ struct iwl_rx_mpdu_desc {
 #define IWL_CD_STTS_WIFI_STATUS_POS    4
 #define IWL_CD_STTS_WIFI_STATUS_MSK    0xF0
 
+#define RX_NO_DATA_CHAIN_A_POS         0
+#define RX_NO_DATA_CHAIN_A_MSK         (0xff << RX_NO_DATA_CHAIN_A_POS)
+#define RX_NO_DATA_CHAIN_B_POS         8
+#define RX_NO_DATA_CHAIN_B_MSK         (0xff << RX_NO_DATA_CHAIN_B_POS)
+#define RX_NO_DATA_CHANNEL_POS         16
+#define RX_NO_DATA_CHANNEL_MSK         (0xff << RX_NO_DATA_CHANNEL_POS)
+
+#define RX_NO_DATA_INFO_TYPE_POS       0
+#define RX_NO_DATA_INFO_TYPE_MSK       (0xff << RX_NO_DATA_INFO_TYPE_POS)
+#define RX_NO_DATA_INFO_TYPE_NONE      0
+#define RX_NO_DATA_INFO_TYPE_RX_ERR    1
+#define RX_NO_DATA_INFO_TYPE_NDP       2
+#define RX_NO_DATA_INFO_TYPE_MU_UNMATCHED      3
+#define RX_NO_DATA_INFO_TYPE_HE_TB_UNMATCHED   4
+
+#define RX_NO_DATA_INFO_ERR_POS                8
+#define RX_NO_DATA_INFO_ERR_MSK                (0xff << RX_NO_DATA_INFO_ERR_POS)
+#define RX_NO_DATA_INFO_ERR_NONE       0
+#define RX_NO_DATA_INFO_ERR_BAD_PLCP   1
+#define RX_NO_DATA_INFO_ERR_UNSUPPORTED_RATE   2
+#define RX_NO_DATA_INFO_ERR_NO_DELIM           3
+#define RX_NO_DATA_INFO_ERR_BAD_MAC_HDR        4
+
+#define RX_NO_DATA_FRAME_TIME_POS      0
+#define RX_NO_DATA_FRAME_TIME_MSK      (0xfffff << RX_NO_DATA_FRAME_TIME_POS)
+
+/**
+ * struct iwl_rx_no_data - RX no data descriptor
+ * @info: 7:0 frame type, 15:8 RX error type
+ * @rssi: 7:0 energy chain-A,
+ *     15:8 chain-B, measured at FINA time (FINA_ENERGY), 16:23 channel
+ * @on_air_rise_time: GP2 during on air rise
+ * @fr_time: frame time
+ * @rate: rate/mcs of frame
+ * @phy_info: &enum iwl_rx_phy_data0 and &enum iwl_rx_phy_info_type
+ * @rx_vec: DW-12:9 raw RX vectors from DSP according to modulation type.
+ *     for VHT: OFDM_RX_VECTOR_SIGA1_OUT, OFDM_RX_VECTOR_SIGA2_OUT
+ *     for HE: OFDM_RX_VECTOR_HE_SIGA1_OUT, OFDM_RX_VECTOR_HE_SIGA2_OUT
+ */
+struct iwl_rx_no_data {
+       __le32 info;
+       __le32 rssi;
+       __le32 on_air_rise_time;
+       __le32 fr_time;
+       __le32 rate;
+       __le32 phy_info[2];
+       __le32 rx_vec[3];
+} __packed; /* RX_NO_DATA_NTFY_API_S_VER_1 */
+
 /**
  * enum iwl_completion_desc_transfer_status -  transfer status (bits 1-3)
  * @IWL_CD_STTS_UNUSED: unused
index c167570..2a19b17 100644 (file)
@@ -225,22 +225,18 @@ static void iwl_fwrt_dump_txf(struct iwl_fw_runtime *fwrt,
        *dump_data = iwl_fw_error_next_data(*dump_data);
 }
 
-static void iwl_fw_dump_fifos(struct iwl_fw_runtime *fwrt,
-                             struct iwl_fw_error_dump_data **dump_data)
+static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt,
+                           struct iwl_fw_error_dump_data **dump_data)
 {
-       struct iwl_fw_error_dump_fifo *fifo_hdr;
        struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
-       u32 *fifo_data;
-       u32 fifo_len;
        unsigned long flags;
-       int i, j;
 
-       IWL_DEBUG_INFO(fwrt, "WRT FIFO dump\n");
+       IWL_DEBUG_INFO(fwrt, "WRT RX FIFO dump\n");
 
        if (!iwl_trans_grab_nic_access(fwrt->trans, &flags))
                return;
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_RXF)) {
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) {
                /* Pull RXF1 */
                iwl_fwrt_dump_rxf(fwrt, dump_data,
                                  cfg->lmac[0].rxfifo1_size, 0, 0);
@@ -254,7 +250,25 @@ static void iwl_fw_dump_fifos(struct iwl_fw_runtime *fwrt,
                                          LMAC2_PRPH_OFFSET, 2);
        }
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_TXF)) {
+       iwl_trans_release_nic_access(fwrt->trans, &flags);
+}
+
+static void iwl_fw_dump_txf(struct iwl_fw_runtime *fwrt,
+                           struct iwl_fw_error_dump_data **dump_data)
+{
+       struct iwl_fw_error_dump_fifo *fifo_hdr;
+       struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
+       u32 *fifo_data;
+       u32 fifo_len;
+       unsigned long flags;
+       int i, j;
+
+       IWL_DEBUG_INFO(fwrt, "WRT TX FIFO dump\n");
+
+       if (!iwl_trans_grab_nic_access(fwrt->trans, &flags))
+               return;
+
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) {
                /* Pull TXF data from LMAC1 */
                for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) {
                        /* Mark the number of TXF we're pulling now */
@@ -279,7 +293,7 @@ static void iwl_fw_dump_fifos(struct iwl_fw_runtime *fwrt,
                }
        }
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_INTERNAL_TXF) &&
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) &&
            fw_has_capa(&fwrt->fw->ucode_capa,
                        IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) {
                /* Pull UMAC internal TXF data from all TXFs */
@@ -591,20 +605,42 @@ static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt,
        IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n", dump_mem->type);
 }
 
+static void iwl_fw_dump_named_mem(struct iwl_fw_runtime *fwrt,
+                                 struct iwl_fw_error_dump_data **dump_data,
+                                 u32 len, u32 ofs, u8 *name, u8 name_len)
+{
+       struct iwl_fw_error_dump_named_mem *dump_mem;
+
+       if (!len)
+               return;
+
+       (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+       (*dump_data)->len = cpu_to_le32(len + sizeof(*dump_mem));
+       dump_mem = (void *)(*dump_data)->data;
+       dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_NAMED_MEM);
+       dump_mem->offset = cpu_to_le32(ofs);
+       dump_mem->name_len = name_len;
+       memcpy(dump_mem->name, name, name_len);
+       iwl_trans_read_mem_bytes(fwrt->trans, ofs, dump_mem->data, len);
+       *dump_data = iwl_fw_error_next_data(*dump_data);
+
+       IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n", dump_mem->type);
+}
+
 #define ADD_LEN(len, item_len, const_len) \
        do {size_t item = item_len; len += (!!item) * const_len + item; } \
        while (0)
 
-static int iwl_fw_fifo_len(struct iwl_fw_runtime *fwrt,
-                          struct iwl_fwrt_shared_mem_cfg *mem_cfg)
+static int iwl_fw_rxf_len(struct iwl_fw_runtime *fwrt,
+                         struct iwl_fwrt_shared_mem_cfg *mem_cfg)
 {
        size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) +
                         sizeof(struct iwl_fw_error_dump_fifo);
        u32 fifo_len = 0;
        int i;
 
-       if (!(fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_RXF)))
-               goto dump_txf;
+       if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF))
+               return 0;
 
        /* Count RXF2 size */
        ADD_LEN(fifo_len, mem_cfg->rxfifo2_size, hdr_len);
@@ -613,8 +649,18 @@ static int iwl_fw_fifo_len(struct iwl_fw_runtime *fwrt,
        for (i = 0; i < mem_cfg->num_lmacs; i++)
                ADD_LEN(fifo_len, mem_cfg->lmac[i].rxfifo1_size, hdr_len);
 
-dump_txf:
-       if (!(fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_TXF)))
+       return fifo_len;
+}
+
+static int iwl_fw_txf_len(struct iwl_fw_runtime *fwrt,
+                         struct iwl_fwrt_shared_mem_cfg *mem_cfg)
+{
+       size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) +
+                        sizeof(struct iwl_fw_error_dump_fifo);
+       u32 fifo_len = 0;
+       int i;
+
+       if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF))
                goto dump_internal_txf;
 
        /* Count TXF sizes */
@@ -627,7 +673,7 @@ dump_txf:
        }
 
 dump_internal_txf:
-       if (!((fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_INTERNAL_TXF)) &&
+       if (!(iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) &&
              fw_has_capa(&fwrt->fw->ucode_capa,
                          IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)))
                goto out;
@@ -639,6 +685,32 @@ out:
        return fifo_len;
 }
 
+static void iwl_dump_paging(struct iwl_fw_runtime *fwrt,
+                           struct iwl_fw_error_dump_data **data)
+{
+       int i;
+
+       IWL_DEBUG_INFO(fwrt, "WRT paging dump\n");
+       for (i = 1; i < fwrt->num_of_paging_blk + 1; i++) {
+               struct iwl_fw_error_dump_paging *paging;
+               struct page *pages =
+                       fwrt->fw_paging_db[i].fw_paging_block;
+               dma_addr_t addr = fwrt->fw_paging_db[i].fw_paging_phys;
+
+               (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
+               (*data)->len = cpu_to_le32(sizeof(*paging) +
+                                            PAGING_BLOCK_SIZE);
+               paging =  (void *)(*data)->data;
+               paging->index = cpu_to_le32(i);
+               dma_sync_single_for_cpu(fwrt->trans->dev, addr,
+                                       PAGING_BLOCK_SIZE,
+                                       DMA_BIDIRECTIONAL);
+               memcpy(paging->data, page_address(pages),
+                      PAGING_BLOCK_SIZE);
+               (*data) = iwl_fw_error_next_data(*data);
+       }
+}
+
 static struct iwl_fw_error_dump_file *
 _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
                   struct iwl_fw_dump_ptrs *fw_error_dump)
@@ -655,13 +727,8 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->cfg->smem_len;
        u32 sram2_len = fwrt->fw->dbg.n_mem_tlv ?
                                0 : fwrt->trans->cfg->dccm2_len;
-       bool monitor_dump_only = false;
        int i;
 
-       if (fwrt->dump.trig &&
-           fwrt->dump.trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)
-               monitor_dump_only = true;
-
        /* SRAM - include stack CCM if driver knows the values for it */
        if (!fwrt->trans->cfg->dccm_offset || !fwrt->trans->cfg->dccm_len) {
                const struct fw_img *img;
@@ -676,26 +743,27 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
 
        /* reading RXF/TXF sizes */
        if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) {
-               fifo_len = iwl_fw_fifo_len(fwrt, mem_cfg);
+               fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg);
+               fifo_len += iwl_fw_txf_len(fwrt, mem_cfg);
 
                /* Make room for PRPH registers */
                if (!fwrt->trans->cfg->gen2 &&
-                   fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_PRPH))
+                  iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PRPH))
                        prph_len += iwl_fw_get_prph_len(fwrt);
 
                if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000 &&
-                   fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_RADIO_REG))
+                   iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG))
                        radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ;
        }
 
        file_len = sizeof(*dump_file) + fifo_len + prph_len + radio_len;
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_DEV_FW_INFO))
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO))
                file_len += sizeof(*dump_data) + sizeof(*dump_info);
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM_CFG))
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG))
                file_len += sizeof(*dump_data) + sizeof(*dump_smem_cfg);
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM)) {
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) {
                size_t hdr_len = sizeof(*dump_data) +
                                 sizeof(struct iwl_fw_error_dump_mem);
 
@@ -712,10 +780,7 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        }
 
        /* Make room for fw's virtual image pages, if it exists */
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING) &&
-           !fwrt->trans->cfg->gen2 &&
-           fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size &&
-           fwrt->fw_paging_db[0].fw_paging_block)
+       if (iwl_fw_dbg_is_paging_enabled(fwrt))
                file_len += fwrt->num_of_paging_blk *
                        (sizeof(*dump_data) +
                         sizeof(struct iwl_fw_error_dump_paging) +
@@ -727,12 +792,12 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        }
 
        /* If we only want a monitor dump, reset the file length */
-       if (monitor_dump_only) {
+       if (fwrt->dump.monitor_only) {
                file_len = sizeof(*dump_file) + sizeof(*dump_data) * 2 +
                           sizeof(*dump_info) + sizeof(*dump_smem_cfg);
        }
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_ERROR_INFO) &&
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) &&
            fwrt->dump.desc)
                file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
                            fwrt->dump.desc->len;
@@ -746,7 +811,7 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
        dump_data = (void *)dump_file->data;
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_DEV_FW_INFO)) {
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO)) {
                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
                dump_data->len = cpu_to_le32(sizeof(*dump_info));
                dump_info = (void *)dump_data->data;
@@ -763,11 +828,12 @@ _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_data = iwl_fw_error_next_data(dump_data);
        }
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM_CFG)) {
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG)) {
                /* Dump shared memory configuration */
                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_CFG);
                dump_data->len = cpu_to_le32(sizeof(*dump_smem_cfg));
@@ -799,12 +865,13 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
 
        /* We only dump the FIFOs if the FW is in error state */
        if (fifo_len) {
-               iwl_fw_dump_fifos(fwrt, &dump_data);
+               iwl_fw_dump_rxf(fwrt, &dump_data);
+               iwl_fw_dump_txf(fwrt, &dump_data);
                if (radio_len)
                        iwl_read_radio_regs(fwrt, &dump_data);
        }
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_ERROR_INFO) &&
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) &&
            fwrt->dump.desc) {
                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
                dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
@@ -817,10 +884,10 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        }
 
        /* In case we only want monitor dump, skip to dump trasport data */
-       if (monitor_dump_only)
+       if (fwrt->dump.monitor_only)
                goto out;
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM)) {
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) {
                const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem =
                        fwrt->fw->dbg.mem_tlv;
 
@@ -865,30 +932,8 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        }
 
        /* Dump fw's virtual image */
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING) &&
-           !fwrt->trans->cfg->gen2 &&
-           fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size &&
-           fwrt->fw_paging_db[0].fw_paging_block) {
-               IWL_DEBUG_INFO(fwrt, "WRT paging dump\n");
-               for (i = 1; i < fwrt->num_of_paging_blk + 1; i++) {
-                       struct iwl_fw_error_dump_paging *paging;
-                       struct page *pages =
-                               fwrt->fw_paging_db[i].fw_paging_block;
-                       dma_addr_t addr = fwrt->fw_paging_db[i].fw_paging_phys;
-
-                       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
-                       dump_data->len = cpu_to_le32(sizeof(*paging) +
-                                                    PAGING_BLOCK_SIZE);
-                       paging = (void *)dump_data->data;
-                       paging->index = cpu_to_le32(i);
-                       dma_sync_single_for_cpu(fwrt->trans->dev, addr,
-                                               PAGING_BLOCK_SIZE,
-                                               DMA_BIDIRECTIONAL);
-                       memcpy(paging->data, page_address(pages),
-                              PAGING_BLOCK_SIZE);
-                       dump_data = iwl_fw_error_next_data(dump_data);
-               }
-       }
+       if (iwl_fw_dbg_is_paging_enabled(fwrt))
+               iwl_dump_paging(fwrt, &dump_data);
 
        if (prph_len) {
                iwl_dump_prph(fwrt->trans, &dump_data,
@@ -906,12 +951,245 @@ out:
        return dump_file;
 }
 
+static void iwl_dump_prph_ini(struct iwl_trans *trans,
+                             struct iwl_fw_error_dump_data **data,
+                             struct iwl_fw_ini_region_cfg *reg)
+{
+       struct iwl_fw_error_dump_prph *prph;
+       unsigned long flags;
+       u32 i, size = le32_to_cpu(reg->num_regions);
+
+       IWL_DEBUG_INFO(trans, "WRT PRPH dump\n");
+
+       if (!iwl_trans_grab_nic_access(trans, &flags))
+               return;
+
+       for (i = 0; i < size; i++) {
+               (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
+               (*data)->len = cpu_to_le32(le32_to_cpu(reg->size) +
+                                          sizeof(*prph));
+               prph = (void *)(*data)->data;
+               prph->prph_start = reg->start_addr[i];
+               prph->data[0] = cpu_to_le32(iwl_read_prph_no_grab(trans,
+                                                                 le32_to_cpu(prph->prph_start)));
+               *data = iwl_fw_error_next_data(*data);
+       }
+       iwl_trans_release_nic_access(trans, &flags);
+}
+
+static void iwl_dump_csr_ini(struct iwl_trans *trans,
+                            struct iwl_fw_error_dump_data **data,
+                            struct iwl_fw_ini_region_cfg *reg)
+{
+       int i, num = le32_to_cpu(reg->num_regions);
+       u32 size = le32_to_cpu(reg->size);
+
+       IWL_DEBUG_INFO(trans, "WRT CSR dump\n");
+
+       for (i = 0; i < num; i++) {
+               u32 add = le32_to_cpu(reg->start_addr[i]);
+               __le32 *val;
+               int j;
+
+               (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_CSR);
+               (*data)->len = cpu_to_le32(size);
+               val = (void *)(*data)->data;
+
+               for (j = 0; j < size; j += 4)
+                       *val++ = cpu_to_le32(iwl_trans_read32(trans, j + add));
+
+               *data = iwl_fw_error_next_data(*data);
+       }
+}
+
+static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt,
+                                     struct iwl_fw_ini_trigger *trigger)
+{
+       int i, num, size = 0, hdr_len = sizeof(struct iwl_fw_error_dump_data);
+
+       if (!trigger || !trigger->num_regions)
+               return 0;
+
+       num = le32_to_cpu(trigger->num_regions);
+       for (i = 0; i < num; i++) {
+               u32 reg_id = le32_to_cpu(trigger->data[i]);
+               struct iwl_fw_ini_region_cfg *reg;
+               enum iwl_fw_ini_region_type type;
+               u32 num_entries;
+
+               if (WARN_ON(reg_id >= ARRAY_SIZE(fwrt->dump.active_regs)))
+                       continue;
+
+               reg = fwrt->dump.active_regs[reg_id].reg;
+               if (WARN(!reg, "Unassigned region %d\n", reg_id))
+                       continue;
+
+               type = le32_to_cpu(reg->region_type);
+               num_entries = le32_to_cpu(reg->num_regions);
+
+               switch (type) {
+               case IWL_FW_INI_REGION_DEVICE_MEMORY:
+                       size += hdr_len +
+                               sizeof(struct iwl_fw_error_dump_named_mem) +
+                               le32_to_cpu(reg->size);
+                       break;
+               case IWL_FW_INI_REGION_PERIPHERY_MAC:
+               case IWL_FW_INI_REGION_PERIPHERY_PHY:
+               case IWL_FW_INI_REGION_PERIPHERY_AUX:
+                       size += num_entries *
+                               (hdr_len +
+                                sizeof(struct iwl_fw_error_dump_prph) +
+                                sizeof(u32));
+                       break;
+               case IWL_FW_INI_REGION_TXF:
+                       size += iwl_fw_txf_len(fwrt, &fwrt->smem_cfg);
+                       break;
+               case IWL_FW_INI_REGION_RXF:
+                       size += iwl_fw_rxf_len(fwrt, &fwrt->smem_cfg);
+                       break;
+               case IWL_FW_INI_REGION_PAGING:
+                       if (!iwl_fw_dbg_is_paging_enabled(fwrt))
+                               break;
+                       size += fwrt->num_of_paging_blk *
+                               (hdr_len +
+                                sizeof(struct iwl_fw_error_dump_paging) +
+                                PAGING_BLOCK_SIZE);
+                       break;
+               case IWL_FW_INI_REGION_CSR:
+                       size += num_entries *
+                               (hdr_len + le32_to_cpu(reg->size));
+                       break;
+               case IWL_FW_INI_REGION_DRAM_BUFFER:
+                       /* Transport takes care of DRAM dumping */
+               case IWL_FW_INI_REGION_INTERNAL_BUFFER:
+               case IWL_FW_INI_REGION_DRAM_IMR:
+                       /* Undefined yet */
+               default:
+                       break;
+               }
+       }
+       return size;
+}
+
+static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt,
+                                   struct iwl_fw_ini_trigger *trigger,
+                                   struct iwl_fw_error_dump_data **data,
+                                   u32 *dump_mask)
+{
+       int i, num = le32_to_cpu(trigger->num_regions);
+
+       for (i = 0; i < num; i++) {
+               u32 reg_id = le32_to_cpu(trigger->data[i]);
+               enum iwl_fw_ini_region_type type;
+               struct iwl_fw_ini_region_cfg *reg;
+
+               if (reg_id >= ARRAY_SIZE(fwrt->dump.active_regs))
+                       continue;
+
+               reg = fwrt->dump.active_regs[reg_id].reg;
+               /* Don't warn, get_trigger_len already warned */
+               if (!reg)
+                       continue;
+
+               type = le32_to_cpu(reg->region_type);
+               switch (type) {
+               case IWL_FW_INI_REGION_DEVICE_MEMORY:
+                       if (WARN_ON(le32_to_cpu(reg->num_regions) > 1))
+                               continue;
+                       iwl_fw_dump_named_mem(fwrt, data,
+                                             le32_to_cpu(reg->size),
+                                             le32_to_cpu(reg->start_addr[0]),
+                                             reg->name,
+                                             le32_to_cpu(reg->name_len));
+                       break;
+               case IWL_FW_INI_REGION_PERIPHERY_MAC:
+               case IWL_FW_INI_REGION_PERIPHERY_PHY:
+               case IWL_FW_INI_REGION_PERIPHERY_AUX:
+                       iwl_dump_prph_ini(fwrt->trans, data, reg);
+                       break;
+               case IWL_FW_INI_REGION_DRAM_BUFFER:
+                       *dump_mask |= 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;
+                       break;
+               case IWL_FW_INI_REGION_TXF:
+                       iwl_fw_dump_txf(fwrt, data);
+                       break;
+               case IWL_FW_INI_REGION_RXF:
+                       iwl_fw_dump_rxf(fwrt, data);
+                       break;
+               case IWL_FW_INI_REGION_CSR:
+                       iwl_dump_csr_ini(fwrt->trans, data, reg);
+                       break;
+               case IWL_FW_INI_REGION_DRAM_IMR:
+               case IWL_FW_INI_REGION_INTERNAL_BUFFER:
+                       /* This is undefined yet */
+               default:
+                       break;
+               }
+       }
+}
+
+static struct iwl_fw_error_dump_file *
+_iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt,
+                      struct iwl_fw_dump_ptrs *fw_error_dump,
+                      u32 *dump_mask)
+{
+       int size, id = le32_to_cpu(fwrt->dump.desc->trig_desc.type);
+       struct iwl_fw_error_dump_data *dump_data;
+       struct iwl_fw_error_dump_file *dump_file;
+       struct iwl_fw_ini_trigger *trigger, *ext;
+
+       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;
+
+       trigger = fwrt->dump.active_trigs[id].conf;
+       ext = fwrt->dump.active_trigs[id].conf_ext;
+
+       size = sizeof(*dump_file);
+       size += iwl_fw_ini_get_trigger_len(fwrt, trigger);
+       size += iwl_fw_ini_get_trigger_len(fwrt, ext);
+
+       if (!size)
+               return NULL;
+
+       dump_file = vzalloc(size);
+       if (!dump_file)
+               return NULL;
+
+       fw_error_dump->fwrt_ptr = dump_file;
+
+       dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
+       dump_data = (void *)dump_file->data;
+       dump_file->file_len = cpu_to_le32(size);
+
+       *dump_mask = 0;
+       if (trigger)
+               iwl_fw_ini_dump_trigger(fwrt, trigger, &dump_data, dump_mask);
+       if (ext)
+               iwl_fw_ini_dump_trigger(fwrt, ext, &dump_data, dump_mask);
+
+       return dump_file;
+}
+
 void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt)
 {
        struct iwl_fw_dump_ptrs *fw_error_dump;
        struct iwl_fw_error_dump_file *dump_file;
        struct scatterlist *sg_dump_data;
        u32 file_len;
+       u32 dump_mask = fwrt->fw->dbg.dump_mask;
 
        IWL_DEBUG_INFO(fwrt, "WRT dump start\n");
 
@@ -925,14 +1203,21 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt)
        if (!fw_error_dump)
                goto out;
 
-       dump_file = _iwl_fw_error_dump(fwrt, fw_error_dump);
+       if (fwrt->trans->ini_valid)
+               dump_file = _iwl_fw_error_ini_dump(fwrt, fw_error_dump,
+                                                  &dump_mask);
+       else
+               dump_file = _iwl_fw_error_dump(fwrt, fw_error_dump);
+
        if (!dump_file) {
                kfree(fw_error_dump);
                goto out;
        }
 
-       fw_error_dump->trans_ptr = iwl_trans_dump_data(fwrt->trans,
-                                                      fwrt->dump.trig);
+       if (!fwrt->trans->ini_valid && fwrt->dump.monitor_only)
+               dump_mask &= IWL_FW_ERROR_DUMP_FW_MONITOR;
+
+       fw_error_dump->trans_ptr = iwl_trans_dump_data(fwrt->trans, dump_mask);
        file_len = le32_to_cpu(dump_file->file_len);
        fw_error_dump->fwrt_len = file_len;
        if (fw_error_dump->trans_ptr) {
@@ -973,6 +1258,14 @@ const struct iwl_fw_dump_desc iwl_dump_desc_assert = {
 };
 IWL_EXPORT_SYMBOL(iwl_dump_desc_assert);
 
+void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt)
+{
+       IWL_INFO(fwrt, "error dump due to fw assert\n");
+       fwrt->dump.desc = &iwl_dump_desc_assert;
+       iwl_fw_error_dump(fwrt);
+}
+IWL_EXPORT_SYMBOL(iwl_fw_assert_error_dump);
+
 void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt)
 {
        struct iwl_fw_dump_desc *iwl_dump_desc_no_alive =
@@ -998,7 +1291,8 @@ void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt)
 IWL_EXPORT_SYMBOL(iwl_fw_alive_error_dump);
 
 int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
-                           const struct iwl_fw_dump_desc *desc, void *trigger,
+                           const struct iwl_fw_dump_desc *desc,
+                           bool monitor_only,
                            unsigned int delay)
 {
        /*
@@ -1028,7 +1322,7 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
                 le32_to_cpu(desc->trig_desc.type));
 
        fwrt->dump.desc = desc;
-       fwrt->dump.trig = trigger;
+       fwrt->dump.monitor_only = monitor_only;
 
        schedule_delayed_work(&fwrt->dump.wk, delay);
 
@@ -1036,13 +1330,14 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
 }
 IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_desc);
 
-int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
-                      enum iwl_fw_dbg_trigger trig,
-                      const char *str, size_t len,
-                      struct iwl_fw_dbg_trigger_tlv *trigger)
+int _iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
+                       enum iwl_fw_dbg_trigger trig,
+                       const char *str, size_t len,
+                       struct iwl_fw_dbg_trigger_tlv *trigger)
 {
        struct iwl_fw_dump_desc *desc;
        unsigned int delay = 0;
+       bool monitor_only = false;
 
        if (trigger) {
                u16 occurrences = le16_to_cpu(trigger->occurrences) - 1;
@@ -1059,6 +1354,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
 
                trigger->occurrences = cpu_to_le16(occurrences);
                delay = le16_to_cpu(trigger->trig_dis_ms);
+               monitor_only = trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY;
        }
 
        desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
@@ -1070,7 +1366,48 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
        desc->trig_desc.type = cpu_to_le32(trig);
        memcpy(desc->trig_desc.data, str, len);
 
-       return iwl_fw_dbg_collect_desc(fwrt, desc, trigger, delay);
+       return iwl_fw_dbg_collect_desc(fwrt, desc, monitor_only, delay);
+}
+IWL_EXPORT_SYMBOL(_iwl_fw_dbg_collect);
+
+int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
+                      u32 id, const char *str, size_t len)
+{
+       struct iwl_fw_dump_desc *desc;
+       u32 occur, delay;
+
+       if (!fwrt->trans->ini_valid)
+               return _iwl_fw_dbg_collect(fwrt, id, str, len, NULL);
+
+       if (id == FW_DBG_TRIGGER_USER)
+               id = IWL_FW_TRIGGER_ID_USER_TRIGGER;
+
+       if (WARN_ON(!fwrt->dump.active_trigs[id].active))
+               return -EINVAL;
+
+       delay = le32_to_cpu(fwrt->dump.active_trigs[id].conf->ignore_consec);
+       occur = le32_to_cpu(fwrt->dump.active_trigs[id].conf->occurrences);
+       if (!occur)
+               return 0;
+
+       if (le32_to_cpu(fwrt->dump.active_trigs[id].conf->force_restart)) {
+               IWL_WARN(fwrt, "Force restart: trigger %d fired.\n", id);
+               iwl_force_nmi(fwrt->trans);
+               return 0;
+       }
+
+       desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
+       if (!desc)
+               return -ENOMEM;
+
+       occur--;
+       fwrt->dump.active_trigs[id].conf->occurrences = cpu_to_le32(occur);
+
+       desc->len = len;
+       desc->trig_desc.type = cpu_to_le32(id);
+       memcpy(desc->trig_desc.data, str, len);
+
+       return iwl_fw_dbg_collect_desc(fwrt, desc, true, delay);
 }
 IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect);
 
@@ -1081,6 +1418,9 @@ int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
        int ret, len = 0;
        char buf[64];
 
+       if (fwrt->trans->ini_valid)
+               return 0;
+
        if (fmt) {
                va_list ap;
 
@@ -1097,8 +1437,8 @@ int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
                len = strlen(buf) + 1;
        }
 
-       ret = iwl_fw_dbg_collect(fwrt, le32_to_cpu(trigger->id), buf, len,
-                                trigger);
+       ret = _iwl_fw_dbg_collect(fwrt, le32_to_cpu(trigger->id), buf, len,
+                                 trigger);
 
        if (ret)
                return ret;
@@ -1224,3 +1564,217 @@ void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt)
                                 cfg->d3_debug_data_length);
 }
 IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data);
+
+static void
+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_host_cmd hcmd = {
+               .id = LDBG_CONFIG_CMD,
+               .flags = CMD_ASYNC,
+               .data[0] = &cont_rec,
+               .len[0] = sizeof(cont_rec),
+       };
+       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);
+
+       /* TODO: alloc fragments if needed */
+       if (!virtual_addr)
+               IWL_ERR(fwrt, "Failed to allocate debug memory\n");
+
+       if (WARN_ON_ONCE(trans->num_blocks == ARRAY_SIZE(trans->fw_mon)))
+               return;
+
+       trans->fw_mon[trans->num_blocks].block = virtual_addr;
+       trans->fw_mon[trans->num_blocks].physical = phys_addr;
+       trans->fw_mon[trans->num_blocks].size = size;
+       trans->num_blocks++;
+
+       IWL_DEBUG_FW(trans, "Allocated debug block of size %d\n", size);
+
+       /* First block is assigned via registers / context info */
+       if (trans->num_blocks == 1)
+               return;
+
+       cmd->num_frags = cpu_to_le32(1);
+       cmd->fragments[0].address = cpu_to_le64(phys_addr);
+       cmd->fragments[0].size = alloc->size;
+       cmd->allocation_id = alloc->allocation_id;
+       cmd->buffer_location = alloc->buffer_location;
+
+       iwl_trans_send_cmd(trans, &hcmd);
+}
+
+static void iwl_fw_dbg_send_hcmd(struct iwl_fw_runtime *fwrt,
+                                struct iwl_ucode_tlv *tlv)
+{
+       struct iwl_fw_ini_hcmd_tlv *hcmd_tlv = (void *)&tlv->data[0];
+       struct iwl_fw_ini_hcmd *data = &hcmd_tlv->hcmd;
+       u16 len = le32_to_cpu(tlv->length) - sizeof(*hcmd_tlv);
+
+       struct iwl_host_cmd hcmd = {
+               .id = WIDE_ID(data->group, data->id),
+               .len = { len, },
+               .data = { data->data, },
+       };
+
+       iwl_trans_send_cmd(fwrt->trans, &hcmd);
+}
+
+static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt,
+                                     struct iwl_fw_ini_region_tlv *tlv,
+                                     bool ext, enum iwl_fw_ini_apply_point pnt)
+{
+       void *iter = (void *)tlv->region_config;
+       int i, size = le32_to_cpu(tlv->num_regions);
+
+       for (i = 0; i < size; i++) {
+               struct iwl_fw_ini_region_cfg *reg = iter;
+               int id = le32_to_cpu(reg->region_id);
+               struct iwl_fw_ini_active_regs *active;
+
+               if (WARN(id >= ARRAY_SIZE(fwrt->dump.active_regs),
+                        "Invalid region id %d for apply point %d\n", id, pnt))
+                       break;
+
+               active = &fwrt->dump.active_regs[id];
+
+               if (ext && active->apply_point == pnt)
+                       IWL_WARN(fwrt->trans,
+                                "External region TLV overrides FW default %x\n",
+                                id);
+
+               IWL_DEBUG_FW(fwrt,
+                            "%s: apply point %d, activating region ID %d\n",
+                            __func__, pnt, id);
+
+               active->reg = reg;
+               active->apply_point = pnt;
+
+               if (le32_to_cpu(reg->region_type) !=
+                   IWL_FW_INI_REGION_DRAM_BUFFER)
+                       iter += le32_to_cpu(reg->num_regions) * sizeof(__le32);
+
+               iter += sizeof(*reg);
+       }
+}
+
+static void iwl_fw_dbg_update_triggers(struct iwl_fw_runtime *fwrt,
+                                      struct iwl_fw_ini_trigger_tlv *tlv,
+                                      bool ext,
+                                      enum iwl_fw_ini_apply_point apply_point)
+{
+       int i, size = le32_to_cpu(tlv->num_triggers);
+       void *iter = (void *)tlv->trigger_config;
+
+       for (i = 0; i < size; i++) {
+               struct iwl_fw_ini_trigger *trig = iter;
+               struct iwl_fw_ini_active_triggers *active;
+               int id = le32_to_cpu(trig->trigger_id);
+               u32 num;
+
+               if (WARN_ON(id >= ARRAY_SIZE(fwrt->dump.active_trigs)))
+                       break;
+
+               active = &fwrt->dump.active_trigs[id];
+
+               if (active->apply_point != apply_point) {
+                       active->conf = NULL;
+                       active->conf_ext = NULL;
+               }
+
+               num = le32_to_cpu(trig->num_regions);
+
+               if (ext && active->apply_point == apply_point) {
+                       num += le32_to_cpu(active->conf->num_regions);
+                       if (trig->ignore_default) {
+                               active->conf_ext = active->conf;
+                               active->conf = trig;
+                       } else {
+                               active->conf_ext = trig;
+                       }
+               } else {
+                       active->conf = trig;
+               }
+
+               /* Since zero means infinity - just set to -1 */
+               if (!le32_to_cpu(trig->occurrences))
+                       trig->occurrences = cpu_to_le32(-1);
+               if (!le32_to_cpu(trig->ignore_consec))
+                       trig->ignore_consec = cpu_to_le32(-1);
+
+               iter += sizeof(*trig) +
+                       le32_to_cpu(trig->num_regions) * sizeof(__le32);
+
+               active->active = num;
+               active->apply_point = apply_point;
+       }
+}
+
+static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
+                                   struct iwl_apply_point_data *data,
+                                   enum iwl_fw_ini_apply_point pnt,
+                                   bool ext)
+{
+       void *iter = data->data;
+
+       while (iter && iter < data->data + data->size) {
+               struct iwl_ucode_tlv *tlv = iter;
+               void *ini_tlv = (void *)tlv->data;
+               u32 type = le32_to_cpu(tlv->type);
+
+               switch (type) {
+               case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION:
+                       iwl_fw_dbg_buffer_allocation(fwrt, ini_tlv);
+                       break;
+               case IWL_UCODE_TLV_TYPE_HCMD:
+                       if (pnt < IWL_FW_INI_APPLY_AFTER_ALIVE) {
+                               IWL_ERR(fwrt,
+                                       "Invalid apply point %x for host command\n",
+                                       pnt);
+                               goto next;
+                       }
+                       iwl_fw_dbg_send_hcmd(fwrt, tlv);
+                       break;
+               case IWL_UCODE_TLV_TYPE_REGIONS:
+                       iwl_fw_dbg_update_regions(fwrt, ini_tlv, ext, pnt);
+                       break;
+               case IWL_UCODE_TLV_TYPE_TRIGGERS:
+                       iwl_fw_dbg_update_triggers(fwrt, ini_tlv, ext, pnt);
+                       break;
+               case IWL_UCODE_TLV_TYPE_DEBUG_FLOW:
+                       break;
+               default:
+                       WARN_ONCE(1, "Invalid TLV %x for apply point\n", type);
+                       break;
+               }
+next:
+               iter += sizeof(*tlv) + le32_to_cpu(tlv->length);
+       }
+}
+
+void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
+                           enum iwl_fw_ini_apply_point apply_point)
+{
+       void *data = &fwrt->trans->apply_points[apply_point];
+
+       _iwl_fw_dbg_apply_point(fwrt, data, apply_point, false);
+
+       data = &fwrt->trans->apply_points_ext[apply_point];
+       _iwl_fw_dbg_apply_point(fwrt, data, apply_point, true);
+}
+IWL_EXPORT_SYMBOL(iwl_fw_dbg_apply_point);
index 6f8d325..6aabbdd 100644 (file)
@@ -72,6 +72,7 @@
 #include "file.h"
 #include "error-dump.h"
 #include "api/commands.h"
+#include "api/dbg-tlv.h"
 
 /**
  * struct iwl_fw_dump_desc - describes the dump
@@ -101,17 +102,19 @@ 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.trig = NULL;
+       fwrt->dump.rt_status = 0;
 }
 
 void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt);
 int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
                            const struct iwl_fw_dump_desc *desc,
-                           void *trigger, unsigned int delay);
+                           bool monitor_only, unsigned int delay);
+int _iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
+                       enum iwl_fw_dbg_trigger trig,
+                       const char *str, size_t len,
+                       struct iwl_fw_dbg_trigger_tlv *trigger);
 int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
-                      enum iwl_fw_dbg_trigger trig,
-                      const char *str, size_t len,
-                      struct iwl_fw_dbg_trigger_tlv *trigger);
+                      u32 id, const char *str, size_t len);
 int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
                            struct iwl_fw_dbg_trigger_tlv *trigger,
                            const char *fmt, ...) __printf(3, 4);
@@ -193,6 +196,9 @@ _iwl_fw_dbg_trigger_on(struct iwl_fw_runtime *fwrt,
 {
        struct iwl_fw_dbg_trigger_tlv *trig;
 
+       if (fwrt->trans->ini_valid)
+               return NULL;
+
        if (!iwl_fw_dbg_trigger_enabled(fwrt->fw, id))
                return NULL;
 
@@ -210,6 +216,37 @@ _iwl_fw_dbg_trigger_on(struct iwl_fw_runtime *fwrt,
        _iwl_fw_dbg_trigger_on((fwrt), (wdev), (id));           \
 })
 
+static inline bool
+_iwl_fw_ini_trigger_on(struct iwl_fw_runtime *fwrt,
+                      const enum iwl_fw_dbg_trigger id)
+{
+       struct iwl_fw_ini_active_triggers *trig = &fwrt->dump.active_trigs[id];
+       u32 ms;
+
+       if (!fwrt->trans->ini_valid)
+               return false;
+
+       if (!trig || !trig->active)
+               return false;
+
+       ms = le32_to_cpu(trig->conf->ignore_consec);
+       if (ms)
+               ms /= USEC_PER_MSEC;
+
+       if (iwl_fw_dbg_no_trig_window(fwrt, id, ms)) {
+               IWL_WARN(fwrt, "Trigger %d fired in no-collect window\n", id);
+               return false;
+       }
+
+       return true;
+}
+
+#define iwl_fw_ini_trigger_on(fwrt, wdev, id) ({               \
+       BUILD_BUG_ON(!__builtin_constant_p(id));                \
+       BUILD_BUG_ON((id) >= IWL_FW_TRIGGER_ID_NUM);            \
+       _iwl_fw_ini_trigger_on((fwrt), (wdev), (id));           \
+})
+
 static inline void
 _iwl_fw_dbg_trigger_simple_stop(struct iwl_fw_runtime *fwrt,
                                struct wireless_dev *wdev,
@@ -263,6 +300,9 @@ _iwl_fw_dbg_stop_recording(struct iwl_trans *trans,
        iwl_write_prph(trans, DBGC_IN_SAMPLE, 0);
        udelay(100);
        iwl_write_prph(trans, DBGC_OUT_CTRL, 0);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       trans->dbg_rec_on = false;
+#endif
 }
 
 static inline void
@@ -293,6 +333,14 @@ _iwl_fw_dbg_restart_recording(struct iwl_trans *trans,
        }
 }
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static inline void iwl_fw_set_dbg_rec_on(struct iwl_fw_runtime *fwrt)
+{
+       if (fwrt->fw->dbg.dest_tlv && fwrt->cur_fw_img == IWL_UCODE_REGULAR)
+               fwrt->trans->dbg_rec_on = true;
+}
+#endif
+
 static inline void
 iwl_fw_dbg_restart_recording(struct iwl_fw_runtime *fwrt,
                             struct iwl_fw_dbg_params *params)
@@ -301,6 +349,9 @@ iwl_fw_dbg_restart_recording(struct iwl_fw_runtime *fwrt,
                _iwl_fw_dbg_restart_recording(fwrt->trans, params);
        else
                iwl_fw_dbg_start_stop_hcmd(fwrt, true);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       iwl_fw_set_dbg_rec_on(fwrt);
+#endif
 }
 
 static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt)
@@ -310,12 +361,25 @@ static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt)
 
 void iwl_fw_error_dump_wk(struct work_struct *work);
 
+static inline bool iwl_fw_dbg_type_on(struct iwl_fw_runtime *fwrt, u32 type)
+{
+       return (fwrt->fw->dbg.dump_mask & BIT(type) || fwrt->trans->ini_valid);
+}
+
 static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt)
 {
        return fw_has_capa(&fwrt->fw->ucode_capa,
                           IWL_UCODE_TLV_CAPA_D3_DEBUG) &&
                fwrt->trans->cfg->d3_debug_data_length &&
-               fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA);
+               iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_D3_DEBUG_DATA);
+}
+
+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->fw->img[fwrt->cur_fw_img].paging_mem_size &&
+               fwrt->fw_paging_db[0].fw_paging_block;
 }
 
 void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt);
@@ -366,6 +430,10 @@ static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {}
 
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
 
+void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt);
 void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt);
 void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt);
+void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
+                           enum iwl_fw_ini_apply_point apply_point);
+
 #endif  /* __iwl_fw_dbg_h__ */
index 6fede17..65faecf 100644 (file)
@@ -187,6 +187,8 @@ 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
+ *     if the dump collection was not initiated by an assert, the value is 0
  */
 struct iwl_fw_error_dump_info {
        __le32 device_family;
@@ -194,6 +196,7 @@ 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;
 } __packed;
 
 /**
@@ -249,6 +252,7 @@ struct iwl_fw_error_dump_prph {
 enum iwl_fw_error_dump_mem_type {
        IWL_FW_ERROR_DUMP_MEM_SRAM,
        IWL_FW_ERROR_DUMP_MEM_SMEM,
+       IWL_FW_ERROR_DUMP_MEM_NAMED_MEM = 10,
 };
 
 /**
@@ -263,6 +267,22 @@ struct iwl_fw_error_dump_mem {
        u8 data[];
 };
 
+/**
+ * struct iwl_fw_error_dump_named_mem - chunk of memory
+ * @type: &enum iwl_fw_error_dump_mem_type
+ * @offset: the offset from which the memory was read
+ * @name_len: name length
+ * @name: file name
+ * @data: the content of the memory
+ */
+struct iwl_fw_error_dump_named_mem {
+       __le32 type;
+       __le32 offset;
+       u8 name_len;
+       u8 name[32];
+       u8 data[];
+};
+
 /**
  * struct iwl_fw_error_dump_rb - content of an Receive Buffer
  * @index: the index of the Receive Buffer in the Rx queue
index 6005a41..81f557c 100644 (file)
@@ -91,6 +91,8 @@ struct iwl_ucode_header {
        } u;
 };
 
+#define IWL_UCODE_INI_TLV_GROUP        BIT(24)
+
 /*
  * new TLV uCode file layout
  *
@@ -141,6 +143,11 @@ enum iwl_ucode_tlv_type {
        IWL_UCODE_TLV_FW_GSCAN_CAPA     = 50,
        IWL_UCODE_TLV_FW_MEM_SEG        = 51,
        IWL_UCODE_TLV_IML               = 52,
+       IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION    = IWL_UCODE_INI_TLV_GROUP | 0x1,
+       IWL_UCODE_TLV_TYPE_HCMD                 = IWL_UCODE_INI_TLV_GROUP | 0x2,
+       IWL_UCODE_TLV_TYPE_REGIONS              = IWL_UCODE_INI_TLV_GROUP | 0x3,
+       IWL_UCODE_TLV_TYPE_TRIGGERS             = IWL_UCODE_INI_TLV_GROUP | 0x4,
+       IWL_UCODE_TLV_TYPE_DEBUG_FLOW           = IWL_UCODE_INI_TLV_GROUP | 0x5,
 
        /* TLVs 0x1000-0x2000 are for internal driver usage */
        IWL_UCODE_TLV_FW_DBG_DUMP_LST   = 0x1000,
index 54dbbd9..1233316 100644 (file)
@@ -65,6 +65,8 @@
 #define __iwl_fw_img_h__
 #include <linux/types.h>
 
+#include "api/dbg-tlv.h"
+
 #include "file.h"
 #include "error-dump.h"
 
@@ -220,6 +222,30 @@ struct iwl_fw_dbg {
        u32 dump_mask;
 };
 
+/**
+ * struct iwl_fw_ini_active_triggers
+ * @active: is this trigger active
+ * @apply_point: last apply point that updated this trigger
+ * @conf: active trigger
+ * @conf_ext: second trigger, contains extra regions to dump
+ */
+struct iwl_fw_ini_active_triggers {
+       bool active;
+       enum iwl_fw_ini_apply_point apply_point;
+       struct iwl_fw_ini_trigger *conf;
+       struct iwl_fw_ini_trigger *conf_ext;
+};
+
+/**
+ * struct iwl_fw_ini_active_regs
+ * @reg: active region from TLV
+ * @apply_point: apply point where it became active
+ */
+struct iwl_fw_ini_active_regs {
+       struct iwl_fw_ini_region_cfg *reg;
+       enum iwl_fw_ini_apply_point apply_point;
+};
+
 /**
  * struct iwl_fw - variables associated with the firmware
  *
index 6b95d0e..4f7090f 100644 (file)
@@ -64,6 +64,7 @@
 #include "iwl-trans.h"
 #include "img.h"
 #include "fw/api/debug.h"
+#include "fw/api/dbg-tlv.h"
 #include "fw/api/paging.h"
 #include "iwl-eeprom-parse.h"
 
@@ -131,14 +132,17 @@ struct iwl_fw_runtime {
        /* debug */
        struct {
                const struct iwl_fw_dump_desc *desc;
-               const struct iwl_fw_dbg_trigger_tlv *trig;
+               bool monitor_only;
                struct delayed_work wk;
 
                u8 conf;
 
                /* ts of the beginning of a non-collect fw dbg data period */
-               unsigned long non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1];
+               unsigned long non_collect_ts_start[IWL_FW_TRIGGER_ID_NUM - 1];
                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;
        } dump;
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        struct {
@@ -154,7 +158,11 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans,
                        const struct iwl_fw_runtime_ops *ops, void *ops_ctx,
                        struct dentry *dbgfs_dir);
 
-void iwl_fw_runtime_exit(struct iwl_fw_runtime *fwrt);
+static inline void iwl_fw_runtime_free(struct iwl_fw_runtime *fwrt)
+{
+       kfree(fwrt->dump.d3_debug_data);
+       fwrt->dump.d3_debug_data = NULL;
+}
 
 void iwl_fw_runtime_suspend(struct iwl_fw_runtime *fwrt);
 
index 5eb906a..91861a9 100644 (file)
@@ -265,11 +265,9 @@ struct iwl_tt_params {
 #define EEPROM_REGULATORY_BAND_NO_HT40         0
 
 /* lower blocks contain EEPROM image and calibration data */
-#define OTP_LOW_IMAGE_SIZE             (2 * 512 * sizeof(u16)) /* 2 KB */
-#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */
-#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */
-#define OTP_LOW_IMAGE_SIZE_FAMILY_9000 OTP_LOW_IMAGE_SIZE_FAMILY_8000
-#define OTP_LOW_IMAGE_SIZE_FAMILY_22000        OTP_LOW_IMAGE_SIZE_FAMILY_9000
+#define OTP_LOW_IMAGE_SIZE_2K          (2 * 512 * sizeof(u16))  /*  2 KB */
+#define OTP_LOW_IMAGE_SIZE_16K         (16 * 512 * sizeof(u16)) /* 16 KB */
+#define OTP_LOW_IMAGE_SIZE_32K         (32 * 512 * sizeof(u16)) /* 32 KB */
 
 struct iwl_eeprom_params {
        const u8 regulatory_bands[7];
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c
new file mode 100644 (file)
index 0000000..43d815c
--- /dev/null
@@ -0,0 +1,231 @@
+/******************************************************************************
+ *
+ * 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) 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ *
+ * 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) 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/firmware.h>
+#include "iwl-trans.h"
+#include "iwl-dbg-tlv.h"
+
+void iwl_fw_dbg_copy_tlv(struct iwl_trans *trans, struct iwl_ucode_tlv *tlv,
+                        bool ext)
+{
+       struct iwl_apply_point_data *data;
+       struct iwl_fw_ini_header *header = (void *)&tlv->data[0];
+       u32 apply_point = le32_to_cpu(header->apply_point);
+
+       int copy_size = le32_to_cpu(tlv->length) + sizeof(*tlv);
+
+       if (WARN_ONCE(apply_point >= IWL_FW_INI_APPLY_NUM,
+                     "Invalid apply point id %d\n", apply_point))
+               return;
+
+       if (ext)
+               data = &trans->apply_points_ext[apply_point];
+       else
+               data = &trans->apply_points[apply_point];
+
+       /*
+        * Make sure we still have room to copy this TLV. Offset points to the
+        * location the last copy ended.
+        */
+       if (WARN_ONCE(data->offset + copy_size > data->size,
+                     "Not enough memory for apply point %d\n",
+                     apply_point))
+               return;
+
+       memcpy(data->data + data->offset, (void *)tlv, copy_size);
+       data->offset += copy_size;
+}
+
+void iwl_alloc_dbg_tlv(struct iwl_trans *trans, size_t len, const u8 *data,
+                      bool ext)
+{
+       struct iwl_ucode_tlv *tlv;
+       u32 size[IWL_FW_INI_APPLY_NUM] = {0};
+       int i;
+
+       while (len >= sizeof(*tlv)) {
+               u32 tlv_len, tlv_type, apply;
+               struct iwl_fw_ini_header *hdr;
+
+               len -= sizeof(*tlv);
+               tlv = (void *)data;
+
+               tlv_len = le32_to_cpu(tlv->length);
+               tlv_type = le32_to_cpu(tlv->type);
+
+               if (len < tlv_len)
+                       return;
+
+               len -= ALIGN(tlv_len, 4);
+               data += sizeof(*tlv) + ALIGN(tlv_len, 4);
+
+               if (!(tlv_type & IWL_UCODE_INI_TLV_GROUP))
+                       continue;
+
+               hdr = (void *)&tlv->data[0];
+               apply = le32_to_cpu(hdr->apply_point);
+
+               IWL_DEBUG_FW(trans, "Read TLV %x, apply point %d\n",
+                            le32_to_cpu(tlv->type), apply);
+
+               if (WARN_ON(apply >= IWL_FW_INI_APPLY_NUM))
+                       continue;
+
+               size[apply] += sizeof(*tlv) + tlv_len;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(size); i++) {
+               void *mem;
+
+               if (!size[i])
+                       continue;
+
+               mem = kzalloc(size[i], GFP_KERNEL);
+
+               if (!mem) {
+                       IWL_ERR(trans, "No memory for apply point %d\n", i);
+                       return;
+               }
+
+               if (ext) {
+                       trans->apply_points_ext[i].data = mem;
+                       trans->apply_points_ext[i].size = size[i];
+               } else {
+                       trans->apply_points[i].data = mem;
+                       trans->apply_points[i].size = size[i];
+               }
+
+               trans->ini_valid = true;
+       }
+}
+
+void iwl_fw_dbg_free(struct iwl_trans *trans)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(trans->apply_points); i++) {
+               kfree(trans->apply_points[i].data);
+               trans->apply_points[i].size = 0;
+               trans->apply_points[i].offset = 0;
+
+               kfree(trans->apply_points_ext[i].data);
+               trans->apply_points_ext[i].size = 0;
+               trans->apply_points_ext[i].offset = 0;
+       }
+}
+
+static int iwl_parse_fw_dbg_tlv(struct iwl_trans *trans, const u8 *data,
+                               size_t len)
+{
+       struct iwl_ucode_tlv *tlv;
+       enum iwl_ucode_tlv_type tlv_type;
+       u32 tlv_len;
+
+       while (len >= sizeof(*tlv)) {
+               len -= sizeof(*tlv);
+               tlv = (void *)data;
+
+               tlv_len = le32_to_cpu(tlv->length);
+               tlv_type = le32_to_cpu(tlv->type);
+
+               if (len < tlv_len) {
+                       IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
+                               len, tlv_len);
+                       return -EINVAL;
+               }
+               len -= ALIGN(tlv_len, 4);
+               data += sizeof(*tlv) + ALIGN(tlv_len, 4);
+
+               switch (tlv_type) {
+               case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION:
+               case IWL_UCODE_TLV_TYPE_HCMD:
+               case IWL_UCODE_TLV_TYPE_REGIONS:
+               case IWL_UCODE_TLV_TYPE_TRIGGERS:
+               case IWL_UCODE_TLV_TYPE_DEBUG_FLOW:
+                       iwl_fw_dbg_copy_tlv(trans, tlv, true);
+                       break;
+               default:
+                       WARN_ONCE(1, "Invalid TLV %x\n", tlv_type);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+void iwl_load_fw_dbg_tlv(struct device *dev, struct iwl_trans *trans)
+{
+       const struct firmware *fw;
+       int res;
+
+       if (trans->external_ini_loaded || !iwlwifi_mod_params.enable_ini)
+               return;
+
+       res = request_firmware(&fw, "iwl-dbg-tlv.ini", dev);
+       if (res)
+               return;
+
+       iwl_alloc_dbg_tlv(trans, fw->size, fw->data, true);
+       iwl_parse_fw_dbg_tlv(trans, fw->data, fw->size);
+
+       trans->external_ini_loaded = true;
+       release_firmware(fw);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h
new file mode 100644 (file)
index 0000000..222cd78
--- /dev/null
@@ -0,0 +1,87 @@
+/******************************************************************************
+ *
+ * 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) 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ *
+ * 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) 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_dbg_tlv_h__
+#define __iwl_dbg_tlv_h__
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+/**
+ * struct iwl_apply_point_data
+ * @data: start address of this apply point data
+ * @size total size of the data
+ * @offset: current offset of the copied data
+ */
+struct iwl_apply_point_data {
+       void *data;
+       int size;
+       int offset;
+};
+
+struct iwl_trans;
+void iwl_load_fw_dbg_tlv(struct device *dev, struct iwl_trans *trans);
+void iwl_fw_dbg_free(struct iwl_trans *trans);
+void iwl_fw_dbg_copy_tlv(struct iwl_trans *trans, struct iwl_ucode_tlv *tlv,
+                        bool ext);
+void iwl_alloc_dbg_tlv(struct iwl_trans *trans, size_t len, const u8 *data,
+                      bool ext);
+
+#endif /* __iwl_dbg_tlv_h__*/
index ba41d23..bf1be98 100644 (file)
@@ -72,6 +72,7 @@
 #include "iwl-op-mode.h"
 #include "iwl-agn-hw.h"
 #include "fw/img.h"
+#include "iwl-dbg-tlv.h"
 #include "iwl-config.h"
 #include "iwl-modparams.h"
 
@@ -645,6 +646,9 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
 
        len -= sizeof(*ucode);
 
+       if (iwlwifi_mod_params.enable_ini)
+               iwl_alloc_dbg_tlv(drv->trans, len, data, false);
+
        while (len >= sizeof(*tlv)) {
                len -= sizeof(*tlv);
                tlv = (void *)data;
@@ -1086,6 +1090,14 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                                return -ENOMEM;
                        break;
                        }
+               case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION:
+               case IWL_UCODE_TLV_TYPE_HCMD:
+               case IWL_UCODE_TLV_TYPE_REGIONS:
+               case IWL_UCODE_TLV_TYPE_TRIGGERS:
+               case IWL_UCODE_TLV_TYPE_DEBUG_FLOW:
+                       if (iwlwifi_mod_params.enable_ini)
+                               iwl_fw_dbg_copy_tlv(drv->trans, tlv, false);
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
@@ -1565,7 +1577,7 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans)
        if (!drv->dbgfs_drv) {
                IWL_ERR(drv, "failed to create debugfs directory\n");
                ret = -ENOMEM;
-               goto err_free_drv;
+               goto err_free_tlv;
        }
 
        /* Create transport layer debugfs dir */
@@ -1590,7 +1602,8 @@ err_fw:
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 err_free_dbgfs:
        debugfs_remove_recursive(drv->dbgfs_drv);
-err_free_drv:
+err_free_tlv:
+       iwl_fw_dbg_free(drv->trans);
 #endif
        kfree(drv);
 err:
@@ -1616,9 +1629,13 @@ void iwl_drv_stop(struct iwl_drv *drv)
        mutex_unlock(&iwlwifi_opmode_table_mtx);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
+       drv->trans->ops->debugfs_cleanup(drv->trans);
+
        debugfs_remove_recursive(drv->dbgfs_drv);
 #endif
 
+       iwl_fw_dbg_free(drv->trans);
+
        kfree(drv);
 }
 
@@ -1749,6 +1766,10 @@ MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)");
 module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, uint, 0644);
 MODULE_PARM_DESC(uapsd_disable,
                 "disable U-APSD functionality bitmap 1: BSS 2: P2P Client (default: 3)");
+module_param_named(enable_ini, iwlwifi_mod_params.enable_ini,
+                  bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(enable_ini,
+                "Enable debug INI TLV FW debug infrastructure (default: 0");
 
 /*
  * set bt_coex_active to true, uCode will do kill/defer
index 4e3422a..75940ac 100644 (file)
@@ -927,22 +927,3 @@ iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg,
        return NULL;
 }
 IWL_EXPORT_SYMBOL(iwl_parse_eeprom_data);
-
-/* helper functions */
-int iwl_nvm_check_version(struct iwl_nvm_data *data,
-                            struct iwl_trans *trans)
-{
-       if (data->nvm_version >= trans->cfg->nvm_ver ||
-           data->calib_version >= trans->cfg->nvm_calib_ver) {
-               IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n",
-                              data->nvm_version, data->calib_version);
-               return 0;
-       }
-
-       IWL_ERR(trans,
-               "Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
-               data->nvm_version, trans->cfg->nvm_ver,
-               data->calib_version,  trans->cfg->nvm_calib_ver);
-       return -EINVAL;
-}
-IWL_EXPORT_SYMBOL(iwl_nvm_check_version);
index d910bda..2375d30 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2015 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) 2005 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2015 Intel Mobile Communications GmbH
+ * Copyright (C) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -117,9 +119,6 @@ struct iwl_nvm_data *
 iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg,
                      const u8 *eeprom, size_t eeprom_size);
 
-int iwl_nvm_check_version(struct iwl_nvm_data *data,
-                         struct iwl_trans *trans);
-
 int iwl_init_sband_channels(struct iwl_nvm_data *data,
                            struct ieee80211_supported_band *sband,
                            int n_channels, enum nl80211_band band);
index 6fc8dac..73b1c46 100644 (file)
@@ -122,6 +122,7 @@ enum iwl_uapsd_disable {
  * @fw_monitor: allow to use firmware monitor
  * @disable_11ac: disable VHT capabilities, default = false.
  * @remove_when_gone: remove an inaccessible device from the PCIe bus.
+ * @enable_ini: enable new FW debug infratructure (INI TLVs)
  */
 struct iwl_mod_params {
        int swcrypto;
@@ -148,6 +149,7 @@ struct iwl_mod_params {
         */
        bool disable_11ax;
        bool remove_when_gone;
+       bool enable_ini;
 };
 
 #endif /* #__iwl_modparams_h__ */
index 96e101d..d9afedc 100644 (file)
@@ -465,101 +465,185 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
        vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map;
 }
 
-static struct ieee80211_sband_iftype_data iwl_he_capa = {
-       .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP),
-       .he_cap = {
-               .has_he = true,
-               .he_cap_elem = {
-                       .mac_cap_info[0] =
-                               IEEE80211_HE_MAC_CAP0_HTC_HE |
-                               IEEE80211_HE_MAC_CAP0_TWT_REQ,
-                       .mac_cap_info[1] =
-                               IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
-                               IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
-                       .mac_cap_info[2] =
-                               IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP |
-                               IEEE80211_HE_MAC_CAP2_MU_CASCADING |
-                               IEEE80211_HE_MAC_CAP2_ACK_EN,
-                       .mac_cap_info[3] =
-                               IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
-                               IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2,
-                       .mac_cap_info[4] =
-                               IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU |
-                               IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39,
-                       .mac_cap_info[5] =
-                               IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B40 |
-                               IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B41 |
-                               IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU,
-                       .phy_cap_info[0] =
-                               IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
-                               IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
-                               IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G,
-                       .phy_cap_info[1] =
-                               IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
-                               IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
-                               IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
-                               IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS,
-                       .phy_cap_info[2] =
-                               IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
-                               IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
-                               IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
-                               IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
-                               IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO,
-                       .phy_cap_info[3] =
-                               IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK |
-                               IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 |
-                               IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK |
-                               IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1,
-                       .phy_cap_info[4] =
-                               IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
-                               IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 |
-                               IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8,
-                       .phy_cap_info[5] =
-                               IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 |
-                               IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2 |
-                               IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
-                               IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK,
-                       .phy_cap_info[6] =
-                               IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
-                               IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
-                               IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
-                               IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
-                               IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB |
-                               IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO |
-                               IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT,
-                       .phy_cap_info[7] =
-                               IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR |
-                               IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI |
-                               IEEE80211_HE_PHY_CAP7_MAX_NC_1,
-                       .phy_cap_info[8] =
-                               IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI |
-                               IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
-                               IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
-                               IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU |
-                               IEEE80211_HE_PHY_CAP8_DCM_MAX_BW_160_OR_80P80_MHZ,
-                       .phy_cap_info[9] =
-                               IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK |
-                               IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
-                               IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB,
+static struct ieee80211_sband_iftype_data iwl_he_capa[] = {
+       {
+               .types_mask = BIT(NL80211_IFTYPE_STATION),
+               .he_cap = {
+                       .has_he = true,
+                       .he_cap_elem = {
+                               .mac_cap_info[0] =
+                                       IEEE80211_HE_MAC_CAP0_HTC_HE |
+                                       IEEE80211_HE_MAC_CAP0_TWT_REQ,
+                               .mac_cap_info[1] =
+                                       IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
+                                       IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
+                               .mac_cap_info[2] =
+                                       IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP |
+                                       IEEE80211_HE_MAC_CAP2_MU_CASCADING |
+                                       IEEE80211_HE_MAC_CAP2_ACK_EN,
+                               .mac_cap_info[3] =
+                                       IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
+                                       IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2,
+                               .mac_cap_info[4] =
+                                       IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU |
+                                       IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39,
+                               .mac_cap_info[5] =
+                                       IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B40 |
+                                       IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B41 |
+                                       IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU,
+                               .phy_cap_info[0] =
+                                       IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+                                       IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+                                       IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G,
+                               .phy_cap_info[1] =
+                                       IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
+                                       IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
+                                       IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
+                                       IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS,
+                               .phy_cap_info[2] =
+                                       IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+                                       IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
+                                       IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
+                                       IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
+                                       IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO,
+                               .phy_cap_info[3] =
+                                       IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK |
+                                       IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 |
+                                       IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK |
+                                       IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1,
+                               .phy_cap_info[4] =
+                                       IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+                                       IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 |
+                                       IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8,
+                               .phy_cap_info[5] =
+                                       IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 |
+                                       IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2 |
+                                       IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+                                       IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK,
+                               .phy_cap_info[6] =
+                                       IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+                                       IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+                                       IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
+                                       IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
+                                       IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB |
+                                       IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO |
+                                       IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT,
+                               .phy_cap_info[7] =
+                                       IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR |
+                                       IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI |
+                                       IEEE80211_HE_PHY_CAP7_MAX_NC_1,
+                               .phy_cap_info[8] =
+                                       IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI |
+                                       IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
+                                       IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
+                                       IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU |
+                                       IEEE80211_HE_PHY_CAP8_DCM_MAX_BW_160_OR_80P80_MHZ,
+                               .phy_cap_info[9] =
+                                       IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK |
+                                       IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+                                       IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB,
+                       },
+                       /*
+                        * Set default Tx/Rx HE MCS NSS Support field.
+                        * Indicate support for up to 2 spatial streams and all
+                        * MCS, without any special cases
+                        */
+                       .he_mcs_nss_supp = {
+                               .rx_mcs_80 = cpu_to_le16(0xfffa),
+                               .tx_mcs_80 = cpu_to_le16(0xfffa),
+                               .rx_mcs_160 = cpu_to_le16(0xfffa),
+                               .tx_mcs_160 = cpu_to_le16(0xfffa),
+                               .rx_mcs_80p80 = cpu_to_le16(0xffff),
+                               .tx_mcs_80p80 = cpu_to_le16(0xffff),
+                       },
+                       /*
+                        * Set default PPE thresholds, with PPET16 set to 0,
+                        * PPET8 set to 7
+                        */
+                       .ppe_thres = {0x61, 0x1c, 0xc7, 0x71},
                },
-               /*
-                * Set default Tx/Rx HE MCS NSS Support field. Indicate support
-                * for up to 2 spatial streams and all MCS, without any special
-                * cases
-                */
-               .he_mcs_nss_supp = {
-                       .rx_mcs_80 = cpu_to_le16(0xfffa),
-                       .tx_mcs_80 = cpu_to_le16(0xfffa),
-                       .rx_mcs_160 = cpu_to_le16(0xfffa),
-                       .tx_mcs_160 = cpu_to_le16(0xfffa),
-                       .rx_mcs_80p80 = cpu_to_le16(0xffff),
-                       .tx_mcs_80p80 = cpu_to_le16(0xffff),
+       },
+       {
+               .types_mask = BIT(NL80211_IFTYPE_AP),
+               .he_cap = {
+                       .has_he = true,
+                       .he_cap_elem = {
+                               .mac_cap_info[0] =
+                                       IEEE80211_HE_MAC_CAP0_HTC_HE |
+                                       IEEE80211_HE_MAC_CAP0_TWT_RES,
+                               .mac_cap_info[1] =
+                                       IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
+                                       IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
+                               .mac_cap_info[2] =
+                                       IEEE80211_HE_MAC_CAP2_BSR |
+                                       IEEE80211_HE_MAC_CAP2_MU_CASCADING |
+                                       IEEE80211_HE_MAC_CAP2_ACK_EN,
+                               .mac_cap_info[3] =
+                                       IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
+                                       IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2,
+                               .mac_cap_info[4] =
+                                       IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU,
+                               .phy_cap_info[0] =
+                                       IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+                                       IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+                                       IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G,
+                               .phy_cap_info[1] =
+                                       IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
+                                       IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS,
+                               .phy_cap_info[2] =
+                                       IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+                                       IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
+                                       IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ,
+                               .phy_cap_info[3] =
+                                       IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK |
+                                       IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 |
+                                       IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK |
+                                       IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1,
+                               .phy_cap_info[4] =
+                                       IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+                                       IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 |
+                                       IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8,
+                               .phy_cap_info[5] =
+                                       IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 |
+                                       IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2 |
+                                       IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+                                       IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK,
+                               .phy_cap_info[6] =
+                                       IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+                                       IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+                                       IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT,
+                               .phy_cap_info[7] =
+                                       IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI |
+                                       IEEE80211_HE_PHY_CAP7_MAX_NC_1,
+                               .phy_cap_info[8] =
+                                       IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI |
+                                       IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
+                                       IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
+                                       IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU |
+                                       IEEE80211_HE_PHY_CAP8_DCM_MAX_BW_160_OR_80P80_MHZ,
+                               .phy_cap_info[9] =
+                                       IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+                                       IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB,
+                       },
+                       /*
+                        * Set default Tx/Rx HE MCS NSS Support field.
+                        * Indicate support for up to 2 spatial streams and all
+                        * MCS, without any special cases
+                        */
+                       .he_mcs_nss_supp = {
+                               .rx_mcs_80 = cpu_to_le16(0xfffa),
+                               .tx_mcs_80 = cpu_to_le16(0xfffa),
+                               .rx_mcs_160 = cpu_to_le16(0xfffa),
+                               .tx_mcs_160 = cpu_to_le16(0xfffa),
+                               .rx_mcs_80p80 = cpu_to_le16(0xffff),
+                               .tx_mcs_80p80 = cpu_to_le16(0xffff),
+                       },
+                       /*
+                        * Set default PPE thresholds, with PPET16 set to 0,
+                        * PPET8 set to 7
+                        */
+                       .ppe_thres = {0x61, 0x1c, 0xc7, 0x71},
                },
-               /*
-                * Set default PPE thresholds, with PPET16 set to 0, PPET8 set
-                * to 7
-                */
-               .ppe_thres = {0x61, 0x1c, 0xc7, 0x71},
        },
 };
 
@@ -568,20 +652,24 @@ static void iwl_init_he_hw_capab(struct ieee80211_supported_band *sband,
 {
        if (sband->band == NL80211_BAND_2GHZ ||
            sband->band == NL80211_BAND_5GHZ)
-               sband->iftype_data = &iwl_he_capa;
+               sband->iftype_data = iwl_he_capa;
        else
                return;
 
-       sband->n_iftype_data = 1;
+       sband->n_iftype_data = ARRAY_SIZE(iwl_he_capa);
 
        /* If not 2x2, we need to indicate 1x1 in the Midamble RX Max NSTS */
        if ((tx_chains & rx_chains) != ANT_AB) {
-               iwl_he_capa.he_cap.he_cap_elem.phy_cap_info[1] &=
-                       ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
-               iwl_he_capa.he_cap.he_cap_elem.phy_cap_info[2] &=
-                       ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS;
-               iwl_he_capa.he_cap.he_cap_elem.phy_cap_info[7] &=
-                       ~IEEE80211_HE_PHY_CAP7_MAX_NC_MASK;
+               int i;
+
+               for (i = 0; i < sband->n_iftype_data; i++) {
+                       iwl_he_capa[i].he_cap.he_cap_elem.phy_cap_info[1] &=
+                               ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
+                       iwl_he_capa[i].he_cap.he_cap_elem.phy_cap_info[2] &=
+                               ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS;
+                       iwl_he_capa[i].he_cap.he_cap_elem.phy_cap_info[7] &=
+                               ~IEEE80211_HE_PHY_CAP7_MAX_NC_MASK;
+               }
        }
 }
 
index 0f51c7b..9d89b7d 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  * Copyright(c) 2016        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) 2005 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  * Copyright(c) 2016        Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #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_END_ADDR_VER2         (0xa03c20)
+#define MON_BUFF_WRPTR_VER2            (0xa03c24)
+#define MON_BUFF_CYCLE_CNT_VER2                (0xa03c28)
+#define MON_BUFF_SHIFT_VER2            (0x8)
 
 #define MON_DMARB_RD_CTL_ADDR          (0xa03c60)
 #define MON_DMARB_RD_DATA_ADDR         (0xa03c5c)
@@ -394,6 +402,7 @@ enum aux_misc_master1_en {
 #define AUX_MISC_MASTER1_SMPHR_STATUS  0xA20800
 #define RSA_ENABLE                     0xA24B08
 #define PREG_AUX_BUS_WPROT_0           0xA04CC0
+#define PREG_PRPH_WPROT_0              0xA04CE0
 #define SB_CPU_1_STATUS                        0xA01E30
 #define SB_CPU_2_STATUS                        0xA01E34
 #define UMAG_SB_CPU_1_STATUS           0xA038C0
@@ -420,4 +429,8 @@ enum {
 #define UREG_CHICK             (0xA05C00)
 #define UREG_CHICK_MSI_ENABLE  BIT(24)
 #define UREG_CHICK_MSIX_ENABLE BIT(25)
+
+#define HPM_DEBUG                      0xA03440
+#define PERSISTENCE_BIT                        BIT(12)
+#define PREG_WFPM_ACCESS               BIT(12)
 #endif                         /* __iwl_prph_h__ */
index 26b3c73..a7009cd 100644 (file)
@@ -73,6 +73,8 @@
 #include "iwl-op-mode.h"
 #include "fw/api/cmdhdr.h"
 #include "fw/api/txq.h"
+#include "fw/api/dbg-tlv.h"
+#include "iwl-dbg-tlv.h"
 
 /**
  * DOC: Transport layer - what is it ?
@@ -534,6 +536,8 @@ struct iwl_trans_rxq_dma_data {
  * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last
  *     TX'ed commands and similar. The buffer will be vfree'd by the caller.
  *     Note that the transport must fill in the proper file headers.
+ * @debugfs_cleanup: used in the driver unload flow to make a proper cleanup
+ *     of the trans debugfs
  */
 struct iwl_trans_ops {
 
@@ -602,8 +606,8 @@ struct iwl_trans_ops {
        void (*resume)(struct iwl_trans *trans);
 
        struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans,
-                                                const struct iwl_fw_dbg_trigger_tlv
-                                                *trigger);
+                                                u32 dump_mask);
+       void (*debugfs_cleanup)(struct iwl_trans *trans);
 };
 
 /**
@@ -679,7 +683,6 @@ enum iwl_plat_pm_mode {
  * enter/exit (in msecs).
  */
 #define IWL_TRANS_IDLE_TIMEOUT 2000
-#define IWL_MAX_DEBUG_ALLOCATIONS      1
 
 /**
  * struct iwl_dram_data
@@ -734,6 +737,7 @@ struct iwl_dram_data {
  * @runtime_pm_mode: the runtime power management mode in use.  This
  *     mode is set during the initialization phase and is not
  *     supposed to change during runtime.
+ * @dbg_rec_on: true iff there is a fw debug recording currently active
  */
 struct iwl_trans {
        const struct iwl_trans_ops *ops;
@@ -774,17 +778,23 @@ struct iwl_trans {
        struct lockdep_map sync_cmd_lockdep_map;
 #endif
 
+       struct iwl_apply_point_data apply_points[IWL_FW_INI_APPLY_NUM];
+       struct iwl_apply_point_data apply_points_ext[IWL_FW_INI_APPLY_NUM];
+
+       bool external_ini_loaded;
+       bool ini_valid;
+
        const struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv;
        const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
        struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv;
-       u32 dbg_dump_mask;
        u8 dbg_n_dest_reg;
        int num_blocks;
-       struct iwl_dram_data fw_mon[IWL_MAX_DEBUG_ALLOCATIONS];
+       struct iwl_dram_data fw_mon[IWL_FW_INI_APPLY_NUM];
 
        enum iwl_plat_pm_mode system_pm_mode;
        enum iwl_plat_pm_mode runtime_pm_mode;
        bool suspending;
+       bool dbg_rec_on;
 
        /* pointer to trans specific struct */
        /*Ensure that this pointer will always be aligned to sizeof pointer */
@@ -897,12 +907,11 @@ static inline void iwl_trans_resume(struct iwl_trans *trans)
 }
 
 static inline struct iwl_trans_dump_data *
-iwl_trans_dump_data(struct iwl_trans *trans,
-                   const struct iwl_fw_dbg_trigger_tlv *trigger)
+iwl_trans_dump_data(struct iwl_trans *trans, u32 dump_mask)
 {
        if (!trans->ops->dump_data)
                return NULL;
-       return trans->ops->dump_data(trans, trigger);
+       return trans->ops->dump_data(trans, dump_mask);
 }
 
 static inline struct iwl_device_cmd *
index 843f3b4..01b5338 100644 (file)
@@ -1811,8 +1811,7 @@ static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm,
                n_matches = 0;
        }
 
-       net_detect = kzalloc(sizeof(*net_detect) +
-                            (n_matches * sizeof(net_detect->matches[0])),
+       net_detect = kzalloc(struct_size(net_detect, matches, n_matches),
                             GFP_KERNEL);
        if (!net_detect || !n_matches)
                goto out_report_nd;
@@ -1827,8 +1826,7 @@ static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm,
                for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; j++)
                        n_channels += hweight8(fw_match->matching_channels[j]);
 
-               match = kzalloc(sizeof(*match) +
-                               (n_channels * sizeof(*match->channels)),
+               match = kzalloc(struct_size(match, channels, n_channels),
                                GFP_KERNEL);
                if (!match)
                        goto out_report_nd;
@@ -1956,7 +1954,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
                set_bit(STATUS_FW_ERROR, &mvm->trans->status);
                iwl_mvm_dump_nic_error_log(mvm);
                iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
-                                       NULL, 0);
+                                       false, 0);
                ret = 1;
                goto err;
        }
index 1aa6c7e..33b0af2 100644 (file)
@@ -1299,10 +1299,11 @@ static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
        int len;
 
        len = scnprintf(buf, sizeof(buf) - 1,
-                       "traffic=%d\ndbgfs=%d\nvcmd=%d\n",
+                       "traffic=%d\ndbgfs=%d\nvcmd=%d\nvif_type=%d\n",
                        !!(mvmvif->low_latency & LOW_LATENCY_TRAFFIC),
                        !!(mvmvif->low_latency & LOW_LATENCY_DEBUGFS),
-                       !!(mvmvif->low_latency & LOW_LATENCY_VCMD));
+                       !!(mvmvif->low_latency & LOW_LATENCY_VCMD),
+                       !!(mvmvif->low_latency & LOW_LATENCY_VIF_TYPE));
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
@@ -1440,15 +1441,6 @@ static ssize_t iwl_dbgfs_quota_min_read(struct file *file,
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
-static const char * const chanwidths[] = {
-       [NL80211_CHAN_WIDTH_20_NOHT] = "noht",
-       [NL80211_CHAN_WIDTH_20] = "ht20",
-       [NL80211_CHAN_WIDTH_40] = "ht40",
-       [NL80211_CHAN_WIDTH_80] = "vht80",
-       [NL80211_CHAN_WIDTH_80P80] = "vht80p80",
-       [NL80211_CHAN_WIDTH_160] = "vht160",
-};
-
 #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
        _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
 #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
index 3b6b3d8..52c361a 100644 (file)
@@ -1284,7 +1284,7 @@ static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm,
                return 0;
 
        iwl_fw_dbg_collect(&mvm->fwrt, FW_DBG_TRIGGER_USER, buf,
-                          (count - 1), NULL);
+                          (count - 1));
 
        iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
 
index dade206..0d6c313 100644 (file)
@@ -377,6 +377,9 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
                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);
+#endif
        clear_bit(IWL_FWRT_STATUS_WAIT_ALIVE, &mvm->fwrt.status);
 
        return 0;
@@ -407,6 +410,7 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
        if (ret) {
                IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
+               iwl_fw_assert_error_dump(&mvm->fwrt);
                goto error;
        }
 
@@ -543,7 +547,9 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        if (mvm->nvm_file_name)
                iwl_mvm_load_nvm_to_nic(mvm);
 
-       WARN_ON(iwl_nvm_check_version(mvm->nvm_data, mvm->trans));
+       WARN_ONCE(mvm->nvm_data->nvm_version < mvm->trans->cfg->nvm_ver,
+                 "Too old NVM version (0x%0x, required = 0x%0x)",
+                 mvm->nvm_data->nvm_version, mvm->trans->cfg->nvm_ver);
 
        /*
         * abort after reading the nvm in case RF Kill is on, we will complete
@@ -881,6 +887,15 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
        int ret, i, j;
        u16 cmd_wide_id =  WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT);
 
+       /*
+        * This command is not supported on earlier firmware versions.
+        * Unfortunately, we don't have a TLV API flag to rely on, so
+        * rely on the major version which is in the first byte of
+        * ucode_ver.
+        */
+       if (IWL_UCODE_SERIAL(mvm->fw->ucode_ver) < 41)
+               return 0;
+
        ret = iwl_mvm_sar_get_wgds_table(mvm);
        if (ret < 0) {
                IWL_DEBUG_RADIO(mvm,
@@ -893,7 +908,7 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
        IWL_DEBUG_RADIO(mvm, "Sending GEO_TX_POWER_LIMIT\n");
 
        BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES * ACPI_WGDS_NUM_BANDS *
-                    ACPI_WGDS_TABLE_SIZE !=  ACPI_WGDS_WIFI_DATA_SIZE);
+                    ACPI_WGDS_TABLE_SIZE + 1 !=  ACPI_WGDS_WIFI_DATA_SIZE);
 
        BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES > IWL_NUM_GEO_PROFILES);
 
@@ -928,6 +943,11 @@ static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm)
        return -ENOENT;
 }
 
+static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm)
+{
+       return -ENOENT;
+}
+
 static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
 {
        return 0;
@@ -954,8 +974,11 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm)
                IWL_DEBUG_RADIO(mvm,
                                "WRDS SAR BIOS table invalid or unavailable. (%d)\n",
                                ret);
-               /* if not available, don't fail and don't bother with EWRD */
-               return 0;
+               /*
+                * If not available, don't fail and don't bother with EWRD.
+                * Return 1 to tell that we can't use WGDS either.
+                */
+               return 1;
        }
 
        ret = iwl_mvm_sar_get_ewrd_table(mvm);
@@ -968,9 +991,13 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm)
        /* choose profile 1 (WRDS) as default for both chains */
        ret = iwl_mvm_sar_select_profile(mvm, 1, 1);
 
-       /* if we don't have profile 0 from BIOS, just skip it */
+       /*
+        * If we don't have profile 0 from BIOS, just skip it.  This
+        * means that SAR Geo will not be enabled either, even if we
+        * have other valid profiles.
+        */
        if (ret == -ENOENT)
-               return 0;
+               return 1;
 
        return ret;
 }
@@ -1002,10 +1029,14 @@ static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm)
        if (ret)
                return ret;
 
+       iwl_fw_dbg_apply_point(&mvm->fwrt, IWL_FW_INI_APPLY_EARLY);
+
        ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
        if (ret)
                return ret;
 
+       iwl_fw_dbg_apply_point(&mvm->fwrt, IWL_FW_INI_APPLY_AFTER_ALIVE);
+
        return iwl_init_paging(&mvm->fwrt, mvm->fwrt.cur_fw_img);
 }
 
@@ -1024,6 +1055,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        ret = iwl_mvm_load_rt_fw(mvm);
        if (ret) {
                IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
+               iwl_fw_assert_error_dump(&mvm->fwrt);
                goto error;
        }
 
@@ -1033,11 +1065,13 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        if (ret)
                IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
 
-       mvm->fwrt.dump.conf = FW_DBG_INVALID;
-       /* if we have a destination, assume EARLY START */
-       if (mvm->fw->dbg.dest_tlv)
-               mvm->fwrt.dump.conf = FW_DBG_START_FROM_ALIVE;
-       iwl_fw_start_dbg_conf(&mvm->fwrt, FW_DBG_START_FROM_ALIVE);
+       if (!mvm->trans->ini_valid) {
+               mvm->fwrt.dump.conf = FW_DBG_INVALID;
+               /* if we have a destination, assume EARLY START */
+               if (mvm->fw->dbg.dest_tlv)
+                       mvm->fwrt.dump.conf = FW_DBG_START_FROM_ALIVE;
+               iwl_fw_start_dbg_conf(&mvm->fwrt, FW_DBG_START_FROM_ALIVE);
+       }
 
        ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
        if (ret)
@@ -1168,11 +1202,19 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
 
        ret = iwl_mvm_sar_init(mvm);
-       if (ret)
-               goto error;
+       if (ret == 0) {
+               ret = iwl_mvm_sar_geo_init(mvm);
+       } else if (ret > 0 && !iwl_mvm_sar_get_wgds_table(mvm)) {
+               /*
+                * If basic SAR is not available, we check for WGDS,
+                * which should *not* be available either.  If it is
+                * available, issue an error, because we can't use SAR
+                * Geo without basic SAR.
+                */
+               IWL_ERR(mvm, "BIOS contains WGDS but no WRDS\n");
+       }
 
-       ret = iwl_mvm_sar_geo_init(mvm);
-       if (ret)
+       if (ret < 0)
                goto error;
 
        iwl_mvm_leds_sync(mvm);
index 6486cfb..7779951 100644 (file)
@@ -767,13 +767,8 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
        }
 
        ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int);
-       ctxt_sta->bi_reciprocal =
-               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
        ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
                                              vif->bss_conf.dtim_period);
-       ctxt_sta->dtim_reciprocal =
-               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
-                                              vif->bss_conf.dtim_period));
 
        ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval);
        ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid);
@@ -782,8 +777,30 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
                cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
 
        if (vif->bss_conf.assoc && vif->bss_conf.he_support &&
-           !iwlwifi_mod_params.disable_11ax)
+           !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);
+               }
+       }
+
 
        return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
 }
@@ -832,8 +849,6 @@ static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
 
        /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */
        cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int);
-       cmd.ibss.bi_reciprocal =
-               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
 
        /* TODO: Assumes that the beacon id == mac context id */
        cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id);
@@ -965,11 +980,8 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm,
        tx->tx_flags = cpu_to_le32(tx_flags);
 
        if (!fw_has_capa(&mvm->fw->ucode_capa,
-                        IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) {
-               mvm->mgmt_last_antenna_idx =
-                       iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
-                                            mvm->mgmt_last_antenna_idx);
-       }
+                        IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION))
+               iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx);
 
        tx->rate_n_flags =
                cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
@@ -1182,14 +1194,12 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
                IWL_DEBUG_HC(mvm, "No need to receive beacons\n");
        }
 
+       if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax)
+               cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX);
+
        ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);
-       ctxt_ap->bi_reciprocal =
-               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
        ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
                                             vif->bss_conf.dtim_period);
-       ctxt_ap->dtim_reciprocal =
-               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
-                                              vif->bss_conf.dtim_period));
 
        if (!fw_has_api(&mvm->fw->ucode_capa,
                        IWL_UCODE_TLV_API_STA_TYPE))
@@ -1522,6 +1532,8 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
                                                   IEEE80211_IFACE_ITER_NORMAL,
                                                   iwl_mvm_beacon_loss_iterator,
                                                   mb);
+
+       iwl_fw_dbg_apply_point(&mvm->fwrt, IWL_FW_INI_APPLY_MISSED_BEACONS);
 }
 
 void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm,
index 505b038..97dc464 100644 (file)
@@ -301,8 +301,12 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
                goto out;
        }
 
-       if (changed)
-               *changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE);
+       if (changed) {
+               u32 status = le32_to_cpu(resp->status);
+
+               *changed = (status == MCC_RESP_NEW_CHAN_PROFILE ||
+                           status == MCC_RESP_ILLEGAL);
+       }
 
        regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg,
                                      __le32_to_cpu(resp->n_channels),
@@ -419,6 +423,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
        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);
 
        if (iwl_mvm_has_tlc_offload(mvm)) {
                ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
@@ -809,6 +814,21 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
            !ieee80211_is_bufferable_mmpdu(hdr->frame_control))
                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) {
+               struct iwl_mvm_vif *mvmvif =
+                       iwl_mvm_vif_from_mac80211(info->control.vif);
+               u8 ap_sta_id = READ_ONCE(mvmvif->ap_sta_id);
+
+               if (ap_sta_id < IWL_MVM_STATION_COUNT) {
+                       /* mac80211 holds rcu read lock */
+                       sta = rcu_dereference(mvm->fw_id_to_mac_id[ap_sta_id]);
+                       if (IS_ERR_OR_NULL(sta))
+                               goto drop;
+               }
+       }
+
        if (sta) {
                if (iwl_mvm_defer_tx(mvm, sta, skb))
                        return;
@@ -1109,6 +1129,8 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
        }
        ret = iwl_mvm_up(mvm);
 
+       iwl_fw_dbg_apply_point(&mvm->fwrt, IWL_FW_INI_APPLY_POST_INIT);
+
        if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
                /* Something went wrong - we need to finish some cleanup
                 * that normally iwl_mvm_mac_restart_complete() below
@@ -2001,7 +2023,13 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
        if (sta->he_cap.he_cap_elem.mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR)
                sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_BQR_SUPP);
 
-       /* If PPE Thresholds exist, parse them into a FW-familiar format */
+       /*
+        * Initialize the PPE thresholds to "None" (7), as described in Table
+        * 9-262ac of 80211.ax/D3.0.
+        */
+       memset(&sta_ctxt_cmd.pkt_ext, 7, sizeof(sta_ctxt_cmd.pkt_ext));
+
+       /* If PPE Thresholds exist, parse them into a FW-familiar format. */
        if (sta->he_cap.he_cap_elem.phy_cap_info[6] &
            IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
                u8 nss = (sta->he_cap.ppe_thres[0] &
@@ -2379,6 +2407,12 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        /* must be set before quota calculations */
        mvmvif->ap_ibss_active = true;
 
+       if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) {
+               iwl_mvm_vif_set_low_latency(mvmvif, true,
+                                           LOW_LATENCY_VIF_TYPE);
+               iwl_mvm_send_low_latency_cmd(mvm, true, mvmvif->id);
+       }
+
        /* power updated needs to be done before quotas */
        iwl_mvm_power_update_mac(mvm);
 
@@ -2441,6 +2475,12 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
        mvmvif->ap_ibss_active = false;
        mvm->ap_last_beacon_gp2 = 0;
 
+       if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) {
+               iwl_mvm_vif_set_low_latency(mvmvif, false,
+                                           LOW_LATENCY_VIF_TYPE);
+               iwl_mvm_send_low_latency_cmd(mvm, false,  mvmvif->id);
+       }
+
        iwl_mvm_bt_coex_vif_change(mvm);
 
        iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS);
@@ -2941,6 +2981,9 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                if (vif->type == NL80211_IFTYPE_AP) {
                        mvmvif->ap_assoc_sta_count++;
                        iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+                       if (vif->bss_conf.he_support &&
+                           !iwlwifi_mod_params.disable_11ax)
+                               iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id);
                }
 
                iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
@@ -3351,7 +3394,7 @@ static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait,
        resp = (void *)pkt->data;
 
        IWL_DEBUG_TE(mvm,
-                    "Aux ROC: Recieved response from ucode: status=%d uid=%d\n",
+                    "Aux ROC: Received response from ucode: status=%d uid=%d\n",
                     resp->status, resp->event_unique_id);
 
        te_data->uid = le32_to_cpu(resp->event_unique_id);
@@ -4444,10 +4487,6 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
                sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
        }
 
-       if (!fw_has_capa(&mvm->fw->ucode_capa,
-                        IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
-               return;
-
        /* if beacon filtering isn't on mac80211 does it anyway */
        if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER))
                return;
index 7ba5bc2..1aa690e 100644 (file)
@@ -303,11 +303,13 @@ enum iwl_bt_force_ant_mode {
 * @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)
 */
 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),
 };
 
 /**
@@ -844,7 +846,6 @@ struct iwl_mvm {
        u16 hw_queue_to_mac80211[IWL_MAX_TVQM_QUEUES];
 
        struct iwl_mvm_dqa_txq_info queue_info[IWL_MAX_HW_QUEUES];
-       spinlock_t queue_info_lock; /* For syncing queue mgmt operations */
        struct work_struct add_stream_wk; /* To add streams to queues */
 
        atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES];
@@ -1521,6 +1522,11 @@ static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm)
               mvm->fw->valid_rx_ant;
 }
 
+static inline void iwl_mvm_toggle_tx_ant(struct iwl_mvm *mvm, u8 *ant)
+{
+       *ant = iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), *ant);
+}
+
 static inline u32 iwl_mvm_get_phy_config(struct iwl_mvm *mvm)
 {
        u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN |
@@ -1550,6 +1556,8 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
                        struct iwl_rx_cmd_buffer *rxb);
 void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                        struct iwl_rx_cmd_buffer *rxb, int queue);
+void iwl_mvm_rx_monitor_ndp(struct iwl_mvm *mvm, struct napi_struct *napi,
+                           struct iwl_rx_cmd_buffer *rxb, int queue);
 void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi,
                              struct iwl_rx_cmd_buffer *rxb, int queue);
 int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask,
@@ -1846,6 +1854,8 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 /* get SystemLowLatencyMode - only needed for beacon threshold? */
 bool iwl_mvm_low_latency(struct iwl_mvm *mvm);
 bool iwl_mvm_low_latency_band(struct iwl_mvm *mvm, enum nl80211_band band);
+void iwl_mvm_send_low_latency_cmd(struct iwl_mvm *mvm, bool low_latency,
+                                 u16 mac_id);
 
 /* get VMACLowLatencyMode */
 static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
index 3633f27..6fc5cc1 100644 (file)
@@ -539,9 +539,8 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
        }
 
        IWL_DEBUG_LAR(mvm,
-                     "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n",
-                     status, mcc, mcc >> 8, mcc & 0xff,
-                     !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels);
+                     "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') n_chans: %d\n",
+                     status, mcc, mcc >> 8, mcc & 0xff, n_channels);
 
 exit:
        iwl_free_resp(&cmd);
index 0e20925..30c5127 100644 (file)
@@ -676,7 +676,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        INIT_LIST_HEAD(&mvm->aux_roc_te_list);
        INIT_LIST_HEAD(&mvm->async_handlers_list);
        spin_lock_init(&mvm->time_event_lock);
-       spin_lock_init(&mvm->queue_info_lock);
 
        INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
        INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
@@ -770,7 +769,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        memcpy(trans->dbg_conf_tlv, mvm->fw->dbg.conf_tlv,
               sizeof(trans->dbg_conf_tlv));
        trans->dbg_trigger_tlv = mvm->fw->dbg.trigger_tlv;
-       trans->dbg_dump_mask = mvm->fw->dbg.dump_mask;
 
        trans->iml = mvm->fw->iml;
        trans->iml_len = mvm->fw->iml_len;
@@ -846,6 +844,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        iwl_mvm_tof_init(mvm);
 
+       iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx);
+
        return op_mode;
 
  out_unregister:
@@ -858,6 +858,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        iwl_mvm_thermal_exit(mvm);
  out_free:
        iwl_fw_flush_dump(&mvm->fwrt);
+       iwl_fw_runtime_free(&mvm->fwrt);
 
        if (iwlmvm_mod_params.init_dbg)
                return op_mode;
@@ -910,6 +911,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
 
        iwl_mvm_tof_clean(mvm);
 
+       iwl_fw_runtime_free(&mvm->fwrt);
        mutex_destroy(&mvm->mutex);
        mutex_destroy(&mvm->d0i3_suspend_mutex);
 
@@ -1071,6 +1073,8 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode,
                iwl_mvm_rx_queue_notif(mvm, rxb, 0);
        else if (cmd == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE))
                iwl_mvm_rx_frame_release(mvm, napi, rxb, 0);
+       else if (cmd == WIDE_ID(DATA_PATH_GROUP, RX_NO_DATA_NOTIF))
+               iwl_mvm_rx_monitor_ndp(mvm, napi, rxb, 0);
        else
                iwl_mvm_rx_common(mvm, rxb, pkt);
 }
@@ -1108,11 +1112,7 @@ static void iwl_mvm_async_cb(struct iwl_op_mode *op_mode,
 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;
-
-       spin_lock_bh(&mvm->queue_info_lock);
-       mq = mvm->hw_queue_to_mac80211[hw_queue];
-       spin_unlock_bh(&mvm->queue_info_lock);
+       unsigned long mq = mvm->hw_queue_to_mac80211[hw_queue];
 
        iwl_mvm_stop_mac_queues(mvm, mq);
 }
@@ -1138,11 +1138,7 @@ void iwl_mvm_start_mac_queues(struct iwl_mvm *mvm, unsigned long mq)
 static void iwl_mvm_wake_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;
-
-       spin_lock_bh(&mvm->queue_info_lock);
-       mq = mvm->hw_queue_to_mac80211[hw_queue];
-       spin_unlock_bh(&mvm->queue_info_lock);
+       unsigned long mq = mvm->hw_queue_to_mac80211[hw_queue];
 
        iwl_mvm_start_mac_queues(mvm, mq);
 }
@@ -1240,7 +1236,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
         */
        if (!mvm->fw_restart && fw_error) {
                iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
-                                       NULL, 0);
+                                       false, 0);
        } else if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
                struct iwl_mvm_reprobe *reprobe;
 
index 7a98e1a..dabbc04 100644 (file)
@@ -98,8 +98,12 @@ static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta)
 {
        struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
        struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+       struct ieee80211_sta_he_cap *he_cap = &sta->he_cap;
        u8 supp = 0;
 
+       if (he_cap && he_cap->has_he)
+               return 0;
+
        if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
                supp |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ);
        if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
index ef62483..6653a23 100644 (file)
@@ -593,31 +593,28 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
        int hyst = vif->bss_conf.cqm_rssi_hyst;
        u16 id = le32_to_cpu(data->mac_id);
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u16 vif_id = mvmvif->id;
 
        /* This doesn't need the MAC ID check since it's not taking the
         * data copied into the "data" struct, but rather the data from
         * the notification directly.
         */
-       if (data->general) {
-               u16 vif_id = mvmvif->id;
-
-               if (iwl_mvm_is_cdb_supported(mvm)) {
-                       struct mvm_statistics_general_cdb *general =
-                               data->general;
-
-                       mvmvif->beacon_stats.num_beacons =
-                               le32_to_cpu(general->beacon_counter[vif_id]);
-                       mvmvif->beacon_stats.avg_signal =
-                               -general->beacon_average_energy[vif_id];
-               } else {
-                       struct mvm_statistics_general_v8 *general =
-                               data->general;
-
-                       mvmvif->beacon_stats.num_beacons =
-                               le32_to_cpu(general->beacon_counter[vif_id]);
-                       mvmvif->beacon_stats.avg_signal =
-                               -general->beacon_average_energy[vif_id];
-               }
+       if (iwl_mvm_is_cdb_supported(mvm)) {
+               struct mvm_statistics_general_cdb *general =
+                       data->general;
+
+               mvmvif->beacon_stats.num_beacons =
+                       le32_to_cpu(general->beacon_counter[vif_id]);
+               mvmvif->beacon_stats.avg_signal =
+                       -general->beacon_average_energy[vif_id];
+       } else {
+               struct mvm_statistics_general_v8 *general =
+                       data->general;
+
+               mvmvif->beacon_stats.num_beacons =
+                       le32_to_cpu(general->beacon_counter[vif_id]);
+               mvmvif->beacon_stats.avg_signal =
+                       -general->beacon_average_energy[vif_id];
        }
 
        if (mvmvif->id != id)
index 26ac940..7bd8676 100644 (file)
@@ -200,7 +200,8 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
 {
        struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
 
-       if (iwl_mvm_check_pn(mvm, skb, queue, sta)) {
+       if (!(rx_status->flag & RX_FLAG_NO_PSDU) &&
+           iwl_mvm_check_pn(mvm, skb, queue, sta)) {
                kfree_skb(skb);
        } else {
                unsigned int radiotap_len = 0;
@@ -863,68 +864,66 @@ static void iwl_mvm_flip_address(u8 *addr)
        ether_addr_copy(addr, mac_addr);
 }
 
-static void iwl_mvm_decode_he_sigb(struct iwl_mvm *mvm,
-                                  struct iwl_rx_mpdu_desc *desc,
-                                  u32 rate_n_flags,
-                                  struct ieee80211_radiotap_he_mu *he_mu)
-{
-       u32 sigb0, sigb1;
-       u16 sigb2;
-
-       if (mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) {
-               sigb0 = le32_to_cpu(desc->v3.sigb_common0);
-               sigb1 = le32_to_cpu(desc->v3.sigb_common1);
-       } else {
-               sigb0 = le32_to_cpu(desc->v1.sigb_common0);
-               sigb1 = le32_to_cpu(desc->v1.sigb_common1);
-       }
+struct iwl_mvm_rx_phy_data {
+       enum iwl_rx_phy_info_type info_type;
+       __le32 d0, d1, d2, d3;
+       __le16 d4;
+};
 
-       sigb2 = le16_to_cpu(desc->sigb_common2);
+static void iwl_mvm_decode_he_mu_ext(struct iwl_mvm *mvm,
+                                    struct iwl_mvm_rx_phy_data *phy_data,
+                                    u32 rate_n_flags,
+                                    struct ieee80211_radiotap_he_mu *he_mu)
+{
+       u32 phy_data2 = le32_to_cpu(phy_data->d2);
+       u32 phy_data3 = le32_to_cpu(phy_data->d3);
+       u16 phy_data4 = le16_to_cpu(phy_data->d4);
 
-       if (FIELD_GET(IWL_RX_HE_SIGB_COMMON2_CH1_CRC_OK, sigb2)) {
+       if (FIELD_GET(IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CRC_OK, phy_data4)) {
                he_mu->flags1 |=
                        cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN |
                                    IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN);
 
                he_mu->flags1 |=
-                       le16_encode_bits(FIELD_GET(IWL_RX_HE_SIGB_COMMON2_CH1_CTR_RU,
-                                                  sigb2),
+                       le16_encode_bits(FIELD_GET(IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CTR_RU,
+                                                  phy_data4),
                                         IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU);
 
-               he_mu->ru_ch1[0] = FIELD_GET(IWL_RX_HE_SIGB_COMMON0_CH1_RU0,
-                                            sigb0);
-               he_mu->ru_ch1[1] = FIELD_GET(IWL_RX_HE_SIGB_COMMON1_CH1_RU1,
-                                            sigb1);
-               he_mu->ru_ch1[2] = FIELD_GET(IWL_RX_HE_SIGB_COMMON0_CH1_RU2,
-                                            sigb0);
-               he_mu->ru_ch1[3] = FIELD_GET(IWL_RX_HE_SIGB_COMMON1_CH1_RU3,
-                                            sigb1);
+               he_mu->ru_ch1[0] = FIELD_GET(IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU0,
+                                            phy_data2);
+               he_mu->ru_ch1[1] = FIELD_GET(IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU1,
+                                            phy_data3);
+               he_mu->ru_ch1[2] = FIELD_GET(IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU2,
+                                            phy_data2);
+               he_mu->ru_ch1[3] = FIELD_GET(IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU3,
+                                            phy_data3);
        }
 
-       if (FIELD_GET(IWL_RX_HE_SIGB_COMMON2_CH2_CRC_OK, sigb2) &&
+       if (FIELD_GET(IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CRC_OK, phy_data4) &&
            (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) != RATE_MCS_CHAN_WIDTH_20) {
                he_mu->flags1 |=
                        cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN |
                                    IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN);
 
                he_mu->flags2 |=
-                       le16_encode_bits(FIELD_GET(IWL_RX_HE_SIGB_COMMON2_CH2_CTR_RU,
-                                                  sigb2),
+                       le16_encode_bits(FIELD_GET(IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CTR_RU,
+                                                  phy_data4),
                                         IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU);
 
-               he_mu->ru_ch2[0] = FIELD_GET(IWL_RX_HE_SIGB_COMMON0_CH2_RU0,
-                                            sigb0);
-               he_mu->ru_ch2[1] = FIELD_GET(IWL_RX_HE_SIGB_COMMON1_CH2_RU1,
-                                            sigb1);
-               he_mu->ru_ch2[2] = FIELD_GET(IWL_RX_HE_SIGB_COMMON0_CH2_RU2,
-                                            sigb0);
-               he_mu->ru_ch2[3] = FIELD_GET(IWL_RX_HE_SIGB_COMMON1_CH2_RU3,
-                                            sigb1);
+               he_mu->ru_ch2[0] = FIELD_GET(IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU0,
+                                            phy_data2);
+               he_mu->ru_ch2[1] = FIELD_GET(IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU1,
+                                            phy_data3);
+               he_mu->ru_ch2[2] = FIELD_GET(IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU2,
+                                            phy_data2);
+               he_mu->ru_ch2[3] = FIELD_GET(IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU3,
+                                            phy_data3);
        }
 }
 
 static void
-iwl_mvm_decode_he_phy_ru_alloc(u64 he_phy_data, u32 rate_n_flags,
+iwl_mvm_decode_he_phy_ru_alloc(struct iwl_mvm_rx_phy_data *phy_data,
+                              u32 rate_n_flags,
                               struct ieee80211_radiotap_he *he,
                               struct ieee80211_radiotap_he_mu *he_mu,
                               struct ieee80211_rx_status *rx_status)
@@ -937,7 +936,7 @@ iwl_mvm_decode_he_phy_ru_alloc(u64 he_phy_data, u32 rate_n_flags,
         * happen though as management frames where we need
         * the TSF/timers are not be transmitted in HE-MU.
         */
-       u8 ru = FIELD_GET(IWL_RX_HE_PHY_RU_ALLOC_MASK, he_phy_data);
+       u8 ru = le32_get_bits(phy_data->d1, IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK);
        u8 offs = 0;
 
        rx_status->bw = RATE_INFO_BW_HE_RU;
@@ -976,7 +975,7 @@ iwl_mvm_decode_he_phy_ru_alloc(u64 he_phy_data, u32 rate_n_flags,
                                      IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET);
        he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN |
                                 IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN);
-       if (he_phy_data & IWL_RX_HE_PHY_RU_ALLOC_SEC80)
+       if (phy_data->d1 & cpu_to_le32(IWL_RX_PHY_DATA1_HE_RU_ALLOC_SEC80))
                he->data2 |=
                        cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC);
 
@@ -996,106 +995,122 @@ iwl_mvm_decode_he_phy_ru_alloc(u64 he_phy_data, u32 rate_n_flags,
 }
 
 static void iwl_mvm_decode_he_phy_data(struct iwl_mvm *mvm,
-                                      struct iwl_rx_mpdu_desc *desc,
+                                      struct iwl_mvm_rx_phy_data *phy_data,
                                       struct ieee80211_radiotap_he *he,
                                       struct ieee80211_radiotap_he_mu *he_mu,
                                       struct ieee80211_rx_status *rx_status,
-                                      u64 he_phy_data, u32 rate_n_flags,
-                                      int queue)
+                                      u32 rate_n_flags, int queue)
 {
-       u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
-       bool sigb_data;
-       u16 d1known = IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN |
-                     IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN |
-                     IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN |
-                     IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN |
-                     IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN;
-       u16 d2known = IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN |
-                     IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN |
-                     IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN;
-
-       he->data1 |= cpu_to_le16(d1known);
-       he->data2 |= cpu_to_le16(d2known);
-       he->data3 |= le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_BSS_COLOR_MASK,
-                                               he_phy_data),
-                                     IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR);
-       he->data3 |= le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_UPLINK,
-                                               he_phy_data),
-                                     IEEE80211_RADIOTAP_HE_DATA3_UL_DL);
-       he->data3 |= le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_LDPC_EXT_SYM,
-                                               he_phy_data),
-                                     IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG);
-       he->data4 |= le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_SPATIAL_REUSE_MASK,
-                                               he_phy_data),
-                                     IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE);
-       he->data5 |= le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_PRE_FEC_PAD_MASK,
-                                               he_phy_data),
-                                     IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD);
-       he->data5 |= le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_PE_DISAMBIG,
-                                               he_phy_data),
-                                     IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG);
-       he->data6 |= le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_TXOP_DUR_MASK,
-                                               he_phy_data),
-                                     IEEE80211_RADIOTAP_HE_DATA6_TXOP);
-       he->data6 |= le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_DOPPLER,
-                                               he_phy_data),
-                                     IEEE80211_RADIOTAP_HE_DATA6_DOPPLER);
-
-       switch (he_type) {
-       case RATE_MCS_HE_TYPE_MU:
+       switch (phy_data->info_type) {
+       case IWL_RX_PHY_INFO_TYPE_NONE:
+       case IWL_RX_PHY_INFO_TYPE_CCK:
+       case IWL_RX_PHY_INFO_TYPE_OFDM_LGCY:
+       case IWL_RX_PHY_INFO_TYPE_HT:
+       case IWL_RX_PHY_INFO_TYPE_VHT_SU:
+       case IWL_RX_PHY_INFO_TYPE_VHT_MU:
+               return;
+       case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT:
+               he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN |
+                                        IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN |
+                                        IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN |
+                                        IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN);
+               he->data4 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE1),
+                                             IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1);
+               he->data4 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE2),
+                                             IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2);
+               he->data4 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE3),
+                                             IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3);
+               he->data4 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE4),
+                                             IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4);
+               /* fall through */
+       case IWL_RX_PHY_INFO_TYPE_HE_SU:
+       case IWL_RX_PHY_INFO_TYPE_HE_MU:
+       case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
+       case IWL_RX_PHY_INFO_TYPE_HE_TB:
+               /* HE common */
+               he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN |
+                                        IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN |
+                                        IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN |
+                                        IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN);
+               he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN |
+                                        IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN |
+                                        IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN |
+                                        IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN);
+               he->data3 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA0_HE_BSS_COLOR_MASK),
+                                             IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR);
+               if (phy_data->info_type != IWL_RX_PHY_INFO_TYPE_HE_TB &&
+                   phy_data->info_type != IWL_RX_PHY_INFO_TYPE_HE_TB_EXT) {
+                       he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN);
+                       he->data3 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA0_HE_UPLINK),
+                                                     IEEE80211_RADIOTAP_HE_DATA3_UL_DL);
+               }
+               he->data3 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA0_HE_LDPC_EXT_SYM),
+                                             IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG);
+               he->data4 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA0_HE_SPATIAL_REUSE_MASK),
+                                             IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE);
+               he->data5 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA0_HE_PRE_FEC_PAD_MASK),
+                                             IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD);
+               he->data5 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA0_HE_PE_DISAMBIG),
+                                             IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG);
+               he->data5 |= le16_encode_bits(le32_get_bits(phy_data->d1,
+                                                           IWL_RX_PHY_DATA1_HE_LTF_NUM_MASK),
+                                             IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS);
+               he->data6 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA0_HE_TXOP_DUR_MASK),
+                                             IEEE80211_RADIOTAP_HE_DATA6_TXOP);
+               he->data6 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA0_HE_DOPPLER),
+                                             IEEE80211_RADIOTAP_HE_DATA6_DOPPLER);
+               break;
+       }
+
+       switch (phy_data->info_type) {
+       case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
                he_mu->flags1 |=
-                       le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_MU_SIGB_DCM,
-                                                  he_phy_data),
+                       le16_encode_bits(le16_get_bits(phy_data->d4,
+                                                      IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_DCM),
                                         IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM);
                he_mu->flags1 |=
-                       le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_MU_SIGB_MCS_MASK,
-                                                  he_phy_data),
+                       le16_encode_bits(le16_get_bits(phy_data->d4,
+                                                      IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_MCS_MASK),
                                         IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS);
                he_mu->flags2 |=
-                       le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_MU_SIBG_SYM_OR_USER_NUM_MASK,
-                                                 he_phy_data),
-                                       IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS);
+                       le16_encode_bits(le16_get_bits(phy_data->d4,
+                                                      IWL_RX_PHY_DATA4_HE_MU_EXT_PREAMBLE_PUNC_TYPE_MASK),
+                                        IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW);
+               iwl_mvm_decode_he_mu_ext(mvm, phy_data, rate_n_flags, he_mu);
+               /* fall through */
+       case IWL_RX_PHY_INFO_TYPE_HE_MU:
                he_mu->flags2 |=
-                       le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_MU_SIGB_COMPRESSION,
-                                                  he_phy_data),
-                                        IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP);
+                       le16_encode_bits(le32_get_bits(phy_data->d1,
+                                                      IWL_RX_PHY_DATA1_HE_MU_SIBG_SYM_OR_USER_NUM_MASK),
+                                        IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS);
                he_mu->flags2 |=
-                       le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_MU_PREAMBLE_PUNC_TYPE_MASK,
-                                                  he_phy_data),
-                                        IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW);
-
-               sigb_data = FIELD_GET(IWL_RX_HE_PHY_INFO_TYPE_MASK,
-                                     he_phy_data) ==
-                               IWL_RX_HE_PHY_INFO_TYPE_MU_EXT_INFO;
-               if (sigb_data)
-                       iwl_mvm_decode_he_sigb(mvm, desc, rate_n_flags, he_mu);
+                       le16_encode_bits(le32_get_bits(phy_data->d1,
+                                                      IWL_RX_PHY_DATA1_HE_MU_SIGB_COMPRESSION),
+                                        IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP);
                /* fall through */
-       case RATE_MCS_HE_TYPE_TRIG:
-               he->data2 |=
-                       cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN);
-               he->data5 |=
-                       le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_HE_LTF_NUM_MASK,
-                                                  he_phy_data),
-                                        IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS);
-               break;
-       case RATE_MCS_HE_TYPE_SU:
-       case RATE_MCS_HE_TYPE_EXT_SU:
-               he->data1 |=
-                       cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN);
-               he->data3 |=
-                       le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_BEAM_CHNG,
-                                                  he_phy_data),
-                                        IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE);
-               break;
-       }
-
-       switch (FIELD_GET(IWL_RX_HE_PHY_INFO_TYPE_MASK, he_phy_data)) {
-       case IWL_RX_HE_PHY_INFO_TYPE_MU:
-       case IWL_RX_HE_PHY_INFO_TYPE_MU_EXT_INFO:
-       case IWL_RX_HE_PHY_INFO_TYPE_TB:
-               iwl_mvm_decode_he_phy_ru_alloc(he_phy_data, rate_n_flags,
+       case IWL_RX_PHY_INFO_TYPE_HE_TB:
+       case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT:
+               iwl_mvm_decode_he_phy_ru_alloc(phy_data, rate_n_flags,
                                               he, he_mu, rx_status);
                break;
+       case IWL_RX_PHY_INFO_TYPE_HE_SU:
+               he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN);
+               he->data3 |= le16_encode_bits(le32_get_bits(phy_data->d0,
+                                                           IWL_RX_PHY_DATA0_HE_BEAM_CHNG),
+                                             IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE);
+               break;
        default:
                /* nothing */
                break;
@@ -1103,13 +1118,10 @@ static void iwl_mvm_decode_he_phy_data(struct iwl_mvm *mvm,
 }
 
 static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb,
-                         struct iwl_rx_mpdu_desc *desc,
+                         struct iwl_mvm_rx_phy_data *phy_data,
                          u32 rate_n_flags, u16 phy_info, int queue)
 {
        struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
-       /* this is invalid e.g. because puncture type doesn't allow 0b11 */
-#define HE_PHY_DATA_INVAL ((u64)-1)
-       u64 he_phy_data = HE_PHY_DATA_INVAL;
        struct ieee80211_radiotap_he *he = NULL;
        struct ieee80211_radiotap_he_mu *he_mu = NULL;
        u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
@@ -1136,49 +1148,41 @@ static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb,
        radiotap_len += sizeof(known);
        rx_status->flag |= RX_FLAG_RADIOTAP_HE;
 
-       if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) {
-               if (mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560)
-                       he_phy_data = le64_to_cpu(desc->v3.he_phy_data);
-               else
-                       he_phy_data = le64_to_cpu(desc->v1.he_phy_data);
-
-               if (he_type == RATE_MCS_HE_TYPE_MU) {
-                       he_mu = skb_put_data(skb, &mu_known, sizeof(mu_known));
-                       radiotap_len += sizeof(mu_known);
-                       rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU;
-               }
+       if (phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU ||
+           phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU_EXT) {
+               he_mu = skb_put_data(skb, &mu_known, sizeof(mu_known));
+               radiotap_len += sizeof(mu_known);
+               rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU;
        }
 
        /* temporarily hide the radiotap data */
        __skb_pull(skb, radiotap_len);
 
-       if (he_phy_data != HE_PHY_DATA_INVAL &&
-           he_type == RATE_MCS_HE_TYPE_SU) {
+       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 (FIELD_GET(IWL_RX_HE_PHY_DELIM_EOF, he_phy_data))
+                       if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF))
                                rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
                }
        }
 
-       if (he_phy_data != HE_PHY_DATA_INVAL)
-               iwl_mvm_decode_he_phy_data(mvm, desc, he, he_mu, rx_status,
-                                          he_phy_data, rate_n_flags, queue);
+       if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
+               iwl_mvm_decode_he_phy_data(mvm, phy_data, he, he_mu, rx_status,
+                                          rate_n_flags, queue);
 
        /* update aggregation data for monitor sake on default queue */
-       if (!queue && (phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
+       if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) &&
+           (phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
                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_phy_data != HE_PHY_DATA_INVAL &&
                    (he_type == RATE_MCS_HE_TYPE_MU ||
                     he_type == RATE_MCS_HE_TYPE_SU)) {
                        rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
-                       if (FIELD_GET(IWL_RX_HE_PHY_DELIM_EOF,
-                                     he_phy_data))
+                       if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF))
                                rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
                }
        }
@@ -1261,43 +1265,34 @@ static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb,
                break;
        }
 
-       he->data5 |= le16_encode_bits(ltf, IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE);
-
-       if (he_type == RATE_MCS_HE_TYPE_SU ||
-           he_type == RATE_MCS_HE_TYPE_EXT_SU) {
-               u16 val;
-
-               /* LTF syms correspond to streams */
-               he->data2 |=
-                       cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN);
-               switch (rx_status->nss) {
-               case 1:
-                       val = 0;
-                       break;
-               case 2:
-                       val = 1;
-                       break;
-               case 3:
-               case 4:
-                       val = 2;
-                       break;
-               case 5:
-               case 6:
-                       val = 3;
-                       break;
-               case 7:
-               case 8:
-                       val = 4;
-                       break;
-               default:
-                       WARN_ONCE(1, "invalid nss: %d\n",
-                                 rx_status->nss);
-                       val = 0;
-               }
+       he->data5 |= le16_encode_bits(ltf,
+                                     IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE);
+}
 
-               he->data5 |=
-                       le16_encode_bits(val,
-                                        IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS);
+static void iwl_mvm_decode_lsig(struct sk_buff *skb,
+                               struct iwl_mvm_rx_phy_data *phy_data)
+{
+       struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_radiotap_lsig *lsig;
+
+       switch (phy_data->info_type) {
+       case IWL_RX_PHY_INFO_TYPE_HT:
+       case IWL_RX_PHY_INFO_TYPE_VHT_SU:
+       case IWL_RX_PHY_INFO_TYPE_VHT_MU:
+       case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT:
+       case IWL_RX_PHY_INFO_TYPE_HE_SU:
+       case IWL_RX_PHY_INFO_TYPE_HE_MU:
+       case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
+       case IWL_RX_PHY_INFO_TYPE_HE_TB:
+               lsig = skb_put(skb, sizeof(*lsig));
+               lsig->data1 = cpu_to_le16(IEEE80211_RADIOTAP_LSIG_DATA1_LENGTH_KNOWN);
+               lsig->data2 = le16_encode_bits(le32_get_bits(phy_data->d1,
+                                                            IWL_RX_PHY_DATA1_LSIG_LEN_MASK),
+                                              IEEE80211_RADIOTAP_LSIG_DATA2_LENGTH);
+               rx_status->flag |= RX_FLAG_RADIOTAP_LSIG;
+               break;
+       default:
+               break;
        }
 }
 
@@ -1315,6 +1310,10 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
        struct sk_buff *skb;
        u8 crypt_len = 0, channel, energy_a, energy_b;
        size_t desc_size;
+       struct iwl_mvm_rx_phy_data phy_data = {
+               .d4 = desc->phy_data4,
+               .info_type = IWL_RX_PHY_INFO_TYPE_NONE,
+       };
 
        if (unlikely(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)))
                return;
@@ -1326,6 +1325,11 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                energy_a = desc->v3.energy_a;
                energy_b = desc->v3.energy_b;
                desc_size = sizeof(*desc);
+
+               phy_data.d0 = desc->v3.phy_data0;
+               phy_data.d1 = desc->v3.phy_data1;
+               phy_data.d2 = desc->v3.phy_data2;
+               phy_data.d3 = desc->v3.phy_data3;
        } else {
                rate_n_flags = le32_to_cpu(desc->v1.rate_n_flags);
                channel = desc->v1.channel;
@@ -1333,8 +1337,18 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                energy_a = desc->v1.energy_a;
                energy_b = desc->v1.energy_b;
                desc_size = IWL_RX_DESC_SIZE_V1;
+
+               phy_data.d0 = desc->v1.phy_data0;
+               phy_data.d1 = desc->v1.phy_data1;
+               phy_data.d2 = desc->v1.phy_data2;
+               phy_data.d3 = desc->v1.phy_data3;
        }
 
+       if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
+               phy_data.info_type =
+                       le32_get_bits(phy_data.d1,
+                                     IWL_RX_PHY_DATA1_INFO_TYPE_MASK);
+
        hdr = (void *)(pkt->data + desc_size);
        /* Dont use dev_alloc_skb(), we'll have enough headroom once
         * ieee80211_hdr pulled.
@@ -1373,7 +1387,10 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
        }
 
        if (rate_n_flags & RATE_MCS_HE_MSK)
-               iwl_mvm_rx_he(mvm, skb, desc, rate_n_flags, phy_info, queue);
+               iwl_mvm_rx_he(mvm, skb, &phy_data, rate_n_flags,
+                             phy_info, queue);
+
+       iwl_mvm_decode_lsig(skb, &phy_data);
 
        rx_status = IEEE80211_SKB_RXCB(skb);
 
@@ -1422,12 +1439,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
        /* update aggregation data for monitor sake on default queue */
        if (!queue && (phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
                bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE;
-               u64 he_phy_data;
-
-               if (mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560)
-                       he_phy_data = le64_to_cpu(desc->v3.he_phy_data);
-               else
-                       he_phy_data = le64_to_cpu(desc->v1.he_phy_data);
 
                rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
                rx_status->ampdu_reference = mvm->ampdu_ref;
@@ -1596,6 +1607,129 @@ out:
        rcu_read_unlock();
 }
 
+void iwl_mvm_rx_monitor_ndp(struct iwl_mvm *mvm, struct napi_struct *napi,
+                           struct iwl_rx_cmd_buffer *rxb, int queue)
+{
+       struct ieee80211_rx_status *rx_status;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_rx_no_data *desc = (void *)pkt->data;
+       u32 rate_n_flags = le32_to_cpu(desc->rate);
+       u32 gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time);
+       u32 rssi = le32_to_cpu(desc->rssi);
+       u32 info_type = le32_to_cpu(desc->info) & RX_NO_DATA_INFO_TYPE_MSK;
+       u16 phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD;
+       struct ieee80211_sta *sta = NULL;
+       struct sk_buff *skb;
+       u8 channel, energy_a, energy_b;
+       struct iwl_mvm_rx_phy_data phy_data = {
+               .d0 = desc->phy_info[0],
+               .info_type = IWL_RX_PHY_INFO_TYPE_NONE,
+       };
+
+       if (unlikely(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)))
+               return;
+
+       /* Currently only NDP type is supported */
+       if (info_type != RX_NO_DATA_INFO_TYPE_NDP)
+               return;
+
+       energy_a = (rssi & RX_NO_DATA_CHAIN_A_MSK) >> RX_NO_DATA_CHAIN_A_POS;
+       energy_b = (rssi & RX_NO_DATA_CHAIN_B_MSK) >> RX_NO_DATA_CHAIN_B_POS;
+       channel = (rssi & RX_NO_DATA_CHANNEL_MSK) >> RX_NO_DATA_CHANNEL_POS;
+
+       phy_data.info_type =
+               le32_get_bits(desc->phy_info[1],
+                             IWL_RX_PHY_DATA1_INFO_TYPE_MASK);
+
+       /* Dont use dev_alloc_skb(), we'll have enough headroom once
+        * ieee80211_hdr pulled.
+        */
+       skb = alloc_skb(128, GFP_ATOMIC);
+       if (!skb) {
+               IWL_ERR(mvm, "alloc_skb failed\n");
+               return;
+       }
+
+       rx_status = IEEE80211_SKB_RXCB(skb);
+
+       /* 0-length PSDU */
+       rx_status->flag |= RX_FLAG_NO_PSDU;
+       /* currently this is the only type for which we get this notif */
+       rx_status->zero_length_psdu_type =
+               IEEE80211_RADIOTAP_ZERO_LEN_PSDU_SOUNDING;
+
+       /* This may be overridden by iwl_mvm_rx_he() to HE_RU */
+       switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+       case RATE_MCS_CHAN_WIDTH_20:
+               break;
+       case RATE_MCS_CHAN_WIDTH_40:
+               rx_status->bw = RATE_INFO_BW_40;
+               break;
+       case RATE_MCS_CHAN_WIDTH_80:
+               rx_status->bw = RATE_INFO_BW_80;
+               break;
+       case RATE_MCS_CHAN_WIDTH_160:
+               rx_status->bw = RATE_INFO_BW_160;
+               break;
+       }
+
+       if (rate_n_flags & RATE_MCS_HE_MSK)
+               iwl_mvm_rx_he(mvm, skb, &phy_data, rate_n_flags,
+                             phy_info, queue);
+
+       iwl_mvm_decode_lsig(skb, &phy_data);
+
+       rx_status->device_timestamp = gp2_on_air_rise;
+       rx_status->band = channel > 14 ? NL80211_BAND_5GHZ :
+               NL80211_BAND_2GHZ;
+       rx_status->freq = ieee80211_channel_to_frequency(channel,
+                                                        rx_status->band);
+       iwl_mvm_get_signal_strength(mvm, rx_status, rate_n_flags, energy_a,
+                                   energy_b);
+
+       rcu_read_lock();
+
+       if (!(rate_n_flags & RATE_MCS_CCK_MSK) &&
+           rate_n_flags & RATE_MCS_SGI_MSK)
+               rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+       if (rate_n_flags & RATE_HT_MCS_GF_MSK)
+               rx_status->enc_flags |= RX_ENC_FLAG_HT_GF;
+       if (rate_n_flags & RATE_MCS_LDPC_MSK)
+               rx_status->enc_flags |= RX_ENC_FLAG_LDPC;
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >>
+                               RATE_MCS_STBC_POS;
+               rx_status->encoding = RX_ENC_HT;
+               rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+               rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
+       } 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)) {
+               int rate = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
+                                                              rx_status->band);
+
+               if (WARN(rate < 0 || rate > 0xFF,
+                        "Invalid rate flags 0x%x, band %d,\n",
+                        rate_n_flags, rx_status->band)) {
+                       kfree_skb(skb);
+                       goto out;
+               }
+               rx_status->rate_idx = rate;
+       }
+
+       iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta);
+out:
+       rcu_read_unlock();
+}
 void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi,
                              struct iwl_rx_cmd_buffer *rxb, int queue)
 {
index cfb784f..86d598d 100644 (file)
@@ -205,9 +205,7 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum nl80211_band band,
 {
        u32 tx_ant;
 
-       mvm->scan_last_antenna_idx =
-               iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
-                                    mvm->scan_last_antenna_idx);
+       iwl_mvm_toggle_tx_ant(mvm, &mvm->scan_last_antenna_idx);
        tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
 
        if (band == NL80211_BAND_2GHZ && !no_cck)
@@ -1895,6 +1893,8 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
                mvm->last_ebs_successful = false;
 
        mvm->scan_uid_status[uid] = 0;
+
+       iwl_fw_dbg_apply_point(&mvm->fwrt, IWL_FW_INI_APPLY_SCAN_COMPLETE);
 }
 
 void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
index 1887d2b..e280098 100644 (file)
@@ -319,9 +319,7 @@ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue,
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        sta_id = mvm->queue_info[queue].ra_sta_id;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        rcu_read_lock();
 
@@ -372,25 +370,17 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue,
                return -EINVAL;
 
        if (iwl_mvm_has_new_tx_api(mvm)) {
-               spin_lock_bh(&mvm->queue_info_lock);
-
                if (remove_mac_queue)
                        mvm->hw_queue_to_mac80211[queue] &=
                                ~BIT(mac80211_queue);
 
-               spin_unlock_bh(&mvm->queue_info_lock);
-
                iwl_trans_txq_free(mvm->trans, queue);
 
                return 0;
        }
 
-       spin_lock_bh(&mvm->queue_info_lock);
-
-       if (WARN_ON(mvm->queue_info[queue].tid_bitmap == 0)) {
-               spin_unlock_bh(&mvm->queue_info_lock);
+       if (WARN_ON(mvm->queue_info[queue].tid_bitmap == 0))
                return 0;
-       }
 
        mvm->queue_info[queue].tid_bitmap &= ~BIT(tid);
 
@@ -426,10 +416,8 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue,
                            mvm->hw_queue_to_mac80211[queue]);
 
        /* If the queue is still enabled - nothing left to do in this func */
-       if (cmd.action == SCD_CFG_ENABLE_QUEUE) {
-               spin_unlock_bh(&mvm->queue_info_lock);
+       if (cmd.action == SCD_CFG_ENABLE_QUEUE)
                return 0;
-       }
 
        cmd.sta_id = mvm->queue_info[queue].ra_sta_id;
        cmd.tid = mvm->queue_info[queue].txq_tid;
@@ -448,8 +436,6 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue,
        /* Regardless if this is a reserved TXQ for a STA - mark it as false */
        mvm->queue_info[queue].reserved = false;
 
-       spin_unlock_bh(&mvm->queue_info_lock);
-
        iwl_trans_txq_disable(mvm->trans, queue, false);
        ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags,
                                   sizeof(struct iwl_scd_txq_cfg_cmd), &cmd);
@@ -474,10 +460,8 @@ static int iwl_mvm_get_queue_agg_tids(struct iwl_mvm *mvm, int queue)
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        sta_id = mvm->queue_info[queue].ra_sta_id;
        tid_bitmap = mvm->queue_info[queue].tid_bitmap;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
                                        lockdep_is_held(&mvm->mutex));
@@ -516,10 +500,8 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue)
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        sta_id = mvm->queue_info[queue].ra_sta_id;
        tid_bitmap = mvm->queue_info[queue].tid_bitmap;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        rcu_read_lock();
 
@@ -545,6 +527,16 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue)
 
        rcu_read_unlock();
 
+       /*
+        * The TX path may have been using this TXQ_ID from the tid_data,
+        * so make sure it's no longer running so that we can safely reuse
+        * this TXQ later. We've set all the TIDs to IWL_MVM_INVALID_QUEUE
+        * above, but nothing guarantees we've stopped using them. Thus,
+        * without this, we could get to iwl_mvm_disable_txq() and remove
+        * the queue while still sending frames to it.
+        */
+       synchronize_net();
+
        return disable_agg_tids;
 }
 
@@ -562,11 +554,9 @@ 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;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        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;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        same_sta = sta_id == new_sta_id;
 
@@ -610,7 +600,6 @@ static int iwl_mvm_get_shared_queue(struct iwl_mvm *mvm,
         * by the inactivity checker.
         */
        lockdep_assert_held(&mvm->mutex);
-       lockdep_assert_held(&mvm->queue_info_lock);
 
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
@@ -696,10 +685,7 @@ static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
         * value 3 and VO with value 0, so to check if ac X is lower than ac Y
         * we need to check if the numerical value of X is LARGER than of Y.
         */
-       spin_lock_bh(&mvm->queue_info_lock);
        if (ac <= mvm->queue_info[queue].mac80211_ac && !force) {
-               spin_unlock_bh(&mvm->queue_info_lock);
-
                IWL_DEBUG_TX_QUEUES(mvm,
                                    "No redirection needed on TXQ #%d\n",
                                    queue);
@@ -711,7 +697,6 @@ static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
        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;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        IWL_DEBUG_TX_QUEUES(mvm, "Redirecting TXQ #%d to FIFO #%d\n",
                            queue, iwl_mvm_ac_to_tx_fifo[ac]);
@@ -737,9 +722,7 @@ static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
        iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, wdg_timeout);
 
        /* Update the TID "owner" of the queue */
-       spin_lock_bh(&mvm->queue_info_lock);
        mvm->queue_info[queue].txq_tid = tid;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        /* TODO: Work-around SCD bug when moving back by multiples of 0x40 */
 
@@ -748,9 +731,7 @@ static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
                             cmd.sta_id, tid, IWL_FRAME_LIMIT, ssn);
 
        /* Update AC marking of the queue */
-       spin_lock_bh(&mvm->queue_info_lock);
        mvm->queue_info[queue].mac80211_ac = ac;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        /*
         * Mark queue as shared in transport if shared
@@ -773,7 +754,7 @@ static int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id,
 {
        int i;
 
-       lockdep_assert_held(&mvm->queue_info_lock);
+       lockdep_assert_held(&mvm->mutex);
 
        /* This should not be hit with new TX path */
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
@@ -853,11 +834,8 @@ static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue,
 {
        bool enable_queue = true;
 
-       spin_lock_bh(&mvm->queue_info_lock);
-
        /* Make sure this TID isn't already enabled */
        if (mvm->queue_info[queue].tid_bitmap & BIT(tid)) {
-               spin_unlock_bh(&mvm->queue_info_lock);
                IWL_ERR(mvm, "Trying to enable TXQ %d with existing TID %d\n",
                        queue, tid);
                return false;
@@ -893,8 +871,6 @@ static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue,
                            queue, mvm->queue_info[queue].tid_bitmap,
                            mvm->hw_queue_to_mac80211[queue]);
 
-       spin_unlock_bh(&mvm->queue_info_lock);
-
        return enable_queue;
 }
 
@@ -949,9 +925,7 @@ static void iwl_mvm_change_queue_tid(struct iwl_mvm *mvm, int queue)
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        tid_bitmap = mvm->queue_info[queue].tid_bitmap;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        if (WARN(!tid_bitmap, "TXQ %d has no tids assigned to it\n", queue))
                return;
@@ -968,9 +942,7 @@ static void iwl_mvm_change_queue_tid(struct iwl_mvm *mvm, int queue)
                return;
        }
 
-       spin_lock_bh(&mvm->queue_info_lock);
        mvm->queue_info[queue].txq_tid = tid;
-       spin_unlock_bh(&mvm->queue_info_lock);
        IWL_DEBUG_TX_QUEUES(mvm, "Changed TXQ %d ownership to tid %d\n",
                            queue, tid);
 }
@@ -992,10 +964,8 @@ static void iwl_mvm_unshare_queue(struct iwl_mvm *mvm, int queue)
 
        lockdep_assert_held(&mvm->mutex);
 
-       spin_lock_bh(&mvm->queue_info_lock);
        sta_id = mvm->queue_info[queue].ra_sta_id;
        tid_bitmap = mvm->queue_info[queue].tid_bitmap;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        /* Find TID for queue, and make sure it is the only one on the queue */
        tid = find_first_bit(&tid_bitmap, IWL_MAX_TID_COUNT + 1);
@@ -1052,9 +1022,7 @@ static void iwl_mvm_unshare_queue(struct iwl_mvm *mvm, int queue)
                }
        }
 
-       spin_lock_bh(&mvm->queue_info_lock);
        mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY;
-       spin_unlock_bh(&mvm->queue_info_lock);
 }
 
 /*
@@ -1073,7 +1041,7 @@ static bool iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm,
        int tid;
 
        lockdep_assert_held(&mvmsta->lock);
-       lockdep_assert_held(&mvm->queue_info_lock);
+       lockdep_assert_held(&mvm->mutex);
 
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return false;
@@ -1174,8 +1142,6 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta)
        if (iwl_mvm_has_new_tx_api(mvm))
                return -ENOSPC;
 
-       spin_lock_bh(&mvm->queue_info_lock);
-
        rcu_read_lock();
 
        /* we skip the CMD queue below by starting at 1 */
@@ -1230,12 +1196,7 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta)
 
                mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
-               /* this isn't so nice, but works OK due to the way we loop */
-               spin_unlock(&mvm->queue_info_lock);
-
-               /* and we need this locking order */
-               spin_lock(&mvmsta->lock);
-               spin_lock(&mvm->queue_info_lock);
+               spin_lock_bh(&mvmsta->lock);
                ret = iwl_mvm_remove_inactive_tids(mvm, mvmsta, i,
                                                   inactive_tid_bitmap,
                                                   &unshare_queues,
@@ -1243,11 +1204,10 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta)
                if (ret >= 0 && free_queue < 0)
                        free_queue = ret;
                /* only unlock sta lock - we still need the queue info lock */
-               spin_unlock(&mvmsta->lock);
+               spin_unlock_bh(&mvmsta->lock);
        }
 
        rcu_read_unlock();
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        /* Reconfigure queues requiring reconfiguation */
        for_each_set_bit(i, &unshare_queues, IWL_MAX_HW_QUEUES)
@@ -1294,10 +1254,9 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
 
        spin_lock_bh(&mvmsta->lock);
        tfd_queue_mask = mvmsta->tfd_queue_msk;
+       ssn = IEEE80211_SEQ_TO_SN(mvmsta->tid_data[tid].seq_number);
        spin_unlock_bh(&mvmsta->lock);
 
-       spin_lock_bh(&mvm->queue_info_lock);
-
        /*
         * Non-QoS, QoS NDP and MGMT frames should go to a MGMT queue, if one
         * exists
@@ -1327,12 +1286,8 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
                                                IWL_MVM_DQA_MIN_DATA_QUEUE,
                                                IWL_MVM_DQA_MAX_DATA_QUEUE);
        if (queue < 0) {
-               spin_unlock_bh(&mvm->queue_info_lock);
-
                /* try harder - perhaps kill an inactive queue */
                queue = iwl_mvm_inactivity_check(mvm, mvmsta->sta_id);
-
-               spin_lock_bh(&mvm->queue_info_lock);
        }
 
        /* No free queue - we'll have to share */
@@ -1353,8 +1308,6 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
        if (queue > 0 && !shared_queue)
                mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY;
 
-       spin_unlock_bh(&mvm->queue_info_lock);
-
        /* This shouldn't happen - out of queues */
        if (WARN_ON(queue <= 0)) {
                IWL_ERR(mvm, "No available queues for tid %d on sta_id %d\n",
@@ -1388,13 +1341,8 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
                }
        }
 
-       ssn = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
        inc_ssn = iwl_mvm_enable_txq(mvm, queue, mac_queue,
                                     ssn, &cfg, wdg_timeout);
-       if (inc_ssn) {
-               ssn = (ssn + 1) & IEEE80211_SCTL_SEQ;
-               le16_add_cpu(&hdr->seq_ctrl, 0x10);
-       }
 
        /*
         * Mark queue as shared in transport if shared
@@ -1411,8 +1359,10 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
         * this ra/tid in our Tx path since we stop the Qdisc when we
         * need to allocate a new TFD queue.
         */
-       if (inc_ssn)
+       if (inc_ssn) {
                mvmsta->tid_data[tid].seq_number += 0x10;
+               ssn = (ssn + 1) & IEEE80211_SCTL_SEQ;
+       }
        mvmsta->tid_data[tid].txq_id = queue;
        mvmsta->tfd_queue_msk |= BIT(queue);
        queue_state = mvmsta->tid_data[tid].state;
@@ -1556,8 +1506,6 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm,
        /* run the general cleanup/unsharing of queues */
        iwl_mvm_inactivity_check(mvm, IWL_MVM_INVALID_STA);
 
-       spin_lock_bh(&mvm->queue_info_lock);
-
        /* Make sure we have free resources for this STA */
        if (vif_type == NL80211_IFTYPE_STATION && !sta->tdls &&
            !mvm->queue_info[IWL_MVM_DQA_BSS_CLIENT_QUEUE].tid_bitmap &&
@@ -1569,19 +1517,15 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm,
                                                IWL_MVM_DQA_MIN_DATA_QUEUE,
                                                IWL_MVM_DQA_MAX_DATA_QUEUE);
        if (queue < 0) {
-               spin_unlock_bh(&mvm->queue_info_lock);
                /* try again - this time kick out a queue if needed */
                queue = iwl_mvm_inactivity_check(mvm, mvmsta->sta_id);
                if (queue < 0) {
                        IWL_ERR(mvm, "No available queues for new station\n");
                        return -ENOSPC;
                }
-               spin_lock_bh(&mvm->queue_info_lock);
        }
        mvm->queue_info[queue].status = IWL_MVM_QUEUE_RESERVED;
 
-       spin_unlock_bh(&mvm->queue_info_lock);
-
        mvmsta->reserved_queue = queue;
 
        IWL_DEBUG_TX_QUEUES(mvm, "Reserving data queue #%d for sta_id %d\n",
@@ -1822,6 +1766,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
        if (iwl_mvm_has_tlc_offload(mvm))
                iwl_mvm_rs_add_sta(mvm, mvm_sta);
 
+       iwl_mvm_toggle_tx_ant(mvm, &mvm_sta->tx_ant);
+
 update_fw:
        ret = iwl_mvm_sta_send_to_fw(mvm, sta, sta_update, sta_flags);
        if (ret)
@@ -2004,18 +1950,14 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
                 * is still marked as IWL_MVM_QUEUE_RESERVED, and
                 * should be manually marked as free again
                 */
-               spin_lock_bh(&mvm->queue_info_lock);
                status = &mvm->queue_info[reserved_txq].status;
                if (WARN((*status != IWL_MVM_QUEUE_RESERVED) &&
                         (*status != IWL_MVM_QUEUE_FREE),
                         "sta_id %d reserved txq %d status %d",
-                        sta_id, reserved_txq, *status)) {
-                       spin_unlock_bh(&mvm->queue_info_lock);
+                        sta_id, reserved_txq, *status))
                        return -EINVAL;
-               }
 
                *status = IWL_MVM_QUEUE_FREE;
-               spin_unlock_bh(&mvm->queue_info_lock);
        }
 
        if (vif->type == NL80211_IFTYPE_STATION &&
@@ -2873,8 +2815,6 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return -EIO;
        }
 
-       spin_lock(&mvm->queue_info_lock);
-
        /*
         * Note the possible cases:
         *  1. An enabled TXQ - TXQ needs to become agg'ed
@@ -2889,7 +2829,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                if (txq_id < 0) {
                        ret = txq_id;
                        IWL_ERR(mvm, "Failed to allocate agg queue\n");
-                       goto release_locks;
+                       goto out;
                }
 
                /* TXQ hasn't yet been enabled, so mark it only as reserved */
@@ -2900,11 +2840,9 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                IWL_DEBUG_TX_QUEUES(mvm,
                                    "Can't start tid %d agg on shared queue!\n",
                                    tid);
-               goto release_locks;
+               goto out;
        }
 
-       spin_unlock(&mvm->queue_info_lock);
-
        IWL_DEBUG_TX_QUEUES(mvm,
                            "AGG for tid %d will be on queue #%d\n",
                            tid, txq_id);
@@ -2935,10 +2873,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        }
 
        ret = 0;
-       goto out;
 
-release_locks:
-       spin_unlock(&mvm->queue_info_lock);
 out:
        spin_unlock_bh(&mvmsta->lock);
 
@@ -3007,9 +2942,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        cfg.fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
 
-       spin_lock_bh(&mvm->queue_info_lock);
        queue_status = mvm->queue_info[queue].status;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        /* Maybe there is no need to even alloc a queue... */
        if (mvm->queue_info[queue].status == IWL_MVM_QUEUE_READY)
@@ -3055,9 +2988,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        }
 
        /* No need to mark as reserved */
-       spin_lock_bh(&mvm->queue_info_lock);
        mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
 out:
        /*
@@ -3083,10 +3014,11 @@ static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm,
 {
        u16 txq_id = tid_data->txq_id;
 
+       lockdep_assert_held(&mvm->mutex);
+
        if (iwl_mvm_has_new_tx_api(mvm))
                return;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        /*
         * The TXQ is marked as reserved only if no traffic came through yet
         * This means no traffic has been sent on this TID (agg'd or not), so
@@ -3098,8 +3030,6 @@ static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm,
                mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_FREE;
                tid_data->txq_id = IWL_MVM_INVALID_QUEUE;
        }
-
-       spin_unlock_bh(&mvm->queue_info_lock);
 }
 
 int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
index de1a0a2..d52cd88 100644 (file)
@@ -397,6 +397,9 @@ struct iwl_mvm_rxq_dup_data {
  * @ptk_pn: per-queue PTK PN data structures
  * @dup_data: per queue duplicate packet detection data
  * @deferred_traffic_tid_map: indication bitmap of deferred traffic per-TID
+ * @tx_ant: the index of the antenna to use for data tx to this station. Only
+ *     used during connection establishment (e.g. for the 4 way handshake
+ *     exchange).
  *
  * When mac80211 creates a station it reserves some space (hw->sta_data_size)
  * in the structure for use by driver. This structure is placed in that
@@ -439,6 +442,7 @@ struct iwl_mvm_sta {
        u8 agg_tids;
        u8 sleep_tx_count;
        u8 avg_energy;
+       u8 tx_ant;
 };
 
 u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data);
index ec57682..995fe2a 100644 (file)
@@ -302,13 +302,30 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
                                            offload_assist));
 }
 
+static u32 iwl_mvm_get_tx_ant(struct iwl_mvm *mvm,
+                             struct ieee80211_tx_info *info,
+                             struct ieee80211_sta *sta, __le16 fc)
+{
+       if (info->band == NL80211_BAND_2GHZ &&
+           !iwl_mvm_bt_coex_is_shared_ant_avail(mvm))
+               return mvm->cfg->non_shared_ant << RATE_MCS_ANT_POS;
+
+       if (sta && ieee80211_is_data(fc)) {
+               struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+               return BIT(mvmsta->tx_ant) << RATE_MCS_ANT_POS;
+       }
+
+       return BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
+}
+
 static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm,
                               struct ieee80211_tx_info *info,
                               struct ieee80211_sta *sta)
 {
        int rate_idx;
        u8 rate_plcp;
-       u32 rate_flags;
+       u32 rate_flags = 0;
 
        /* HT rate doesn't make sense for a non data frame */
        WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS,
@@ -332,13 +349,6 @@ static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm,
        /* Get PLCP rate for tx_cmd->rate_n_flags */
        rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx);
 
-       if (info->band == NL80211_BAND_2GHZ &&
-           !iwl_mvm_bt_coex_is_shared_ant_avail(mvm))
-               rate_flags = mvm->cfg->non_shared_ant << RATE_MCS_ANT_POS;
-       else
-               rate_flags =
-                       BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
-
        /* Set CCK flag as needed */
        if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
                rate_flags |= RATE_MCS_CCK_MSK;
@@ -346,6 +356,14 @@ static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm,
        return (u32)rate_plcp | rate_flags;
 }
 
+static u32 iwl_mvm_get_tx_rate_n_flags(struct iwl_mvm *mvm,
+                                      struct ieee80211_tx_info *info,
+                                      struct ieee80211_sta *sta, __le16 fc)
+{
+       return iwl_mvm_get_tx_rate(mvm, info, sta) |
+               iwl_mvm_get_tx_ant(mvm, info, sta, fc);
+}
+
 /*
  * Sets the fields in the Tx cmd that are rate related
  */
@@ -373,20 +391,21 @@ void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
         */
 
        if (ieee80211_is_data(fc) && sta) {
-               tx_cmd->initial_rate_index = 0;
-               tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
-               return;
+               struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+               if (mvmsta->sta_state >= IEEE80211_STA_AUTHORIZED) {
+                       tx_cmd->initial_rate_index = 0;
+                       tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
+                       return;
+               }
        } else if (ieee80211_is_back_req(fc)) {
                tx_cmd->tx_flags |=
                        cpu_to_le32(TX_CMD_FLG_ACK | TX_CMD_FLG_BAR);
        }
 
-       mvm->mgmt_last_antenna_idx =
-               iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
-                                    mvm->mgmt_last_antenna_idx);
-
        /* Set the rate in the TX cmd */
-       tx_cmd->rate_n_flags = cpu_to_le32(iwl_mvm_get_tx_rate(mvm, info, sta));
+       tx_cmd->rate_n_flags =
+               cpu_to_le32(iwl_mvm_get_tx_rate_n_flags(mvm, info, sta, fc));
 }
 
 static inline void iwl_mvm_set_tx_cmd_pn(struct ieee80211_tx_info *info,
@@ -491,6 +510,8 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
                u16 offload_assist = 0;
                u32 rate_n_flags = 0;
                u16 flags = 0;
+               struct iwl_mvm_sta *mvmsta = sta ?
+                       iwl_mvm_sta_from_mac80211(sta) : NULL;
 
                if (ieee80211_is_data_qos(hdr->frame_control)) {
                        u8 *qc = ieee80211_get_qos_ctl(hdr);
@@ -510,10 +531,16 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
                if (!info->control.hw_key)
                        flags |= IWL_TX_FLAGS_ENCRYPT_DIS;
 
-               /* For data packets rate info comes from the fw */
-               if (!(ieee80211_is_data(hdr->frame_control) && sta)) {
+               /*
+                * For data packets rate info comes from the fw. Only
+                * set rate/antenna during connection establishment.
+                */
+               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(mvm, info, sta);
+                       rate_n_flags =
+                               iwl_mvm_get_tx_rate_n_flags(mvm, info, sta,
+                                                           hdr->frame_control);
                }
 
                if (mvm->trans->cfg->device_family >=
@@ -681,22 +708,12 @@ out:
 int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb);
        struct ieee80211_tx_info info;
        struct iwl_device_cmd *dev_cmd;
        u8 sta_id;
        int hdrlen = ieee80211_hdrlen(hdr->frame_control);
        __le16 fc = hdr->frame_control;
-       int queue;
-
-       /* IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used
-        * in 2 different types of vifs, P2P & STATION. P2P uses the offchannel
-        * queue. STATION (HS2.0) uses the auxiliary context of the FW,
-        * and hence needs to be sent on the aux queue
-        */
-       if (skb_info->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
-           skb_info->control.vif->type == NL80211_IFTYPE_STATION)
-               skb_info->hw_queue = mvm->aux_queue;
+       int queue = -1;
 
        memcpy(&info, skb->cb, sizeof(info));
 
@@ -708,18 +725,6 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
                          info.hw_queue != info.control.vif->cab_queue)))
                return -1;
 
-       queue = info.hw_queue;
-
-       /*
-        * If the interface on which the frame is sent is the P2P_DEVICE
-        * or an AP/GO interface use the broadcast station associated
-        * with it; otherwise if the interface is a managed interface
-        * use the AP station associated with it for multicast traffic
-        * (this is not possible for unicast packets as a TLDS discovery
-        * response are sent without a station entry); otherwise use the
-        * AUX station.
-        */
-       sta_id = mvm->aux_sta.sta_id;
        if (info.control.vif) {
                struct iwl_mvm_vif *mvmvif =
                        iwl_mvm_vif_from_mac80211(info.control.vif);
@@ -734,20 +739,28 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
 
                        queue = iwl_mvm_get_ctrl_vif_queue(mvm, &info,
                                                           hdr->frame_control);
-                       if (queue < 0)
-                               return -1;
-               } else if (info.control.vif->type == NL80211_IFTYPE_STATION &&
-                          is_multicast_ether_addr(hdr->addr1)) {
-                       u8 ap_sta_id = READ_ONCE(mvmvif->ap_sta_id);
 
-                       if (ap_sta_id != IWL_MVM_INVALID_STA)
-                               sta_id = ap_sta_id;
                } 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) {
+                       /*
+                        * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets
+                        * that can be used in 2 different types of vifs, P2P &
+                        * STATION.
+                        * P2P uses the offchannel queue.
+                        * STATION (HS2.0) uses the auxiliary context of the FW,
+                        * and hence needs to be sent on the aux queue.
+                        */
+                       sta_id = mvm->aux_sta.sta_id;
+                       queue = mvm->aux_queue;
                }
        }
 
+       if (queue < 0)
+               return -1;
+
        if (unlikely(ieee80211_is_probe_resp(fc)))
                iwl_mvm_probe_resp_set_noa(mvm, skb);
 
@@ -1160,11 +1173,11 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
                 * If we have timed-out TIDs - schedule the worker that will
                 * reconfig the queues and update them
                 *
-                * Note that the mvm->queue_info_lock isn't being taken here in
-                * order to not serialize the TX flow. This isn't dangerous
-                * because scheduling mvm->add_stream_wk can't ruin the state,
-                * and if we DON'T schedule it due to some race condition then
-                * next TX we get here we will.
+                * Note that the no lock is taken here in order to not serialize
+                * the TX flow. This isn't dangerous because scheduling
+                * mvm->add_stream_wk can't ruin the state, and if we DON'T
+                * schedule it due to some race condition then next TX we get
+                * here we will.
                 */
                if (unlikely(mvm->queue_info[txq_id].status ==
                             IWL_MVM_QUEUE_SHARED &&
@@ -1451,7 +1464,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                iwl_mvm_get_agg_status(mvm, tx_resp);
        u32 status = le16_to_cpu(agg_status->status);
        u16 ssn = iwl_mvm_get_scd_ssn(mvm, tx_resp);
-       struct iwl_mvm_sta *mvmsta;
        struct sk_buff_head skbs;
        u8 skb_freed = 0;
        u8 lq_color;
@@ -1501,6 +1513,10 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        break;
                }
 
+               if ((status & TX_STATUS_MSK) != TX_STATUS_SUCCESS &&
+                   ieee80211_is_mgmt(hdr->frame_control))
+                       iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx);
+
                /*
                 * If we are freeing multiple frames, mark all the frames
                 * but the first one as acked, since they were acknowledged
@@ -1595,11 +1611,15 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                goto out;
 
        if (!IS_ERR(sta)) {
-               mvmsta = iwl_mvm_sta_from_mac80211(sta);
+               struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
                iwl_mvm_tx_airtime(mvm, mvmsta,
                                   le16_to_cpu(tx_resp->wireless_media_time));
 
+               if ((status & TX_STATUS_MSK) != TX_STATUS_SUCCESS &&
+                   mvmsta->sta_state < IEEE80211_STA_AUTHORIZED)
+                       iwl_mvm_toggle_tx_ant(mvm, &mvmsta->tx_ant);
+
                if (sta->wme && tid != IWL_MGMT_TID) {
                        struct iwl_mvm_tid_data *tid_data =
                                &mvmsta->tid_data[tid];
@@ -1654,10 +1674,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        mvmsta->next_status_eosp = false;
                        ieee80211_sta_eosp(sta);
                }
-       } else {
-               mvmsta = NULL;
        }
-
 out:
        rcu_read_unlock();
 }
@@ -1800,8 +1817,6 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
                return;
        }
 
-       spin_lock_bh(&mvmsta->lock);
-
        __skb_queue_head_init(&reclaimed_skbs);
 
        /*
@@ -1811,6 +1826,8 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
         */
        iwl_trans_reclaim(mvm->trans, txq, index, &reclaimed_skbs);
 
+       spin_lock_bh(&mvmsta->lock);
+
        tid_data->next_reclaimed = index;
 
        iwl_mvm_check_ratid_empty(mvm, sta, tid);
index 818e118..d116c6a 100644 (file)
@@ -285,6 +285,7 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
        return last_idx;
 }
 
+#define FW_SYSASSERT_CPU_MASK 0xf0000000
 static const struct {
        const char *name;
        u8 num;
@@ -301,6 +302,9 @@ static const struct {
        { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
        { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
        { "NMI_INTERRUPT_HOST", 0x66 },
+       { "NMI_INTERRUPT_LMAC_FATAL", 0x70 },
+       { "NMI_INTERRUPT_UMAC_FATAL", 0x71 },
+       { "NMI_INTERRUPT_OTHER_LMAC_FATAL", 0x73 },
        { "NMI_INTERRUPT_ACTION_PT", 0x7C },
        { "NMI_INTERRUPT_UNKNOWN", 0x84 },
        { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
@@ -312,7 +316,7 @@ static const char *desc_lookup(u32 num)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++)
-               if (advanced_lookup[i].num == num)
+               if (advanced_lookup[i].num == (num & ~FW_SYSASSERT_CPU_MASK))
                        return advanced_lookup[i].name;
 
        /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
@@ -536,6 +540,9 @@ 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;
+
        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",
@@ -618,13 +625,9 @@ int iwl_mvm_reconfig_scd(struct iwl_mvm *mvm, int queue, int fifo, int sta_id,
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        if (WARN(mvm->queue_info[queue].tid_bitmap == 0,
-                "Trying to reconfig unallocated queue %d\n", queue)) {
-               spin_unlock_bh(&mvm->queue_info_lock);
+                "Trying to reconfig unallocated queue %d\n", queue))
                return -ENXIO;
-       }
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        IWL_DEBUG_TX_QUEUES(mvm, "Reconfig SCD for TXQ #%d\n", queue);
 
@@ -768,6 +771,29 @@ bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm)
        return result;
 }
 
+void iwl_mvm_send_low_latency_cmd(struct iwl_mvm *mvm,
+                                 bool low_latency, u16 mac_id)
+{
+       struct iwl_mac_low_latency_cmd cmd = {
+               .mac_id = cpu_to_le32(mac_id)
+       };
+
+       if (!fw_has_capa(&mvm->fw->ucode_capa,
+                        IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA))
+               return;
+
+       if (low_latency) {
+               /* currently we don't care about the direction */
+               cmd.low_latency_rx = 1;
+               cmd.low_latency_tx = 1;
+       }
+
+       if (iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(LOW_LATENCY_CMD,
+                                                MAC_CONF_GROUP, 0),
+                                0, sizeof(cmd), &cmd))
+               IWL_ERR(mvm, "Failed to send low latency command\n");
+}
+
 int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                               bool low_latency,
                               enum iwl_mvm_low_latency_cause cause)
@@ -786,24 +812,7 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        if (low_latency == prev)
                return 0;
 
-       if (fw_has_capa(&mvm->fw->ucode_capa,
-                       IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA)) {
-               struct iwl_mac_low_latency_cmd cmd = {
-                       .mac_id = cpu_to_le32(mvmvif->id)
-               };
-
-               if (low_latency) {
-                       /* currently we don't care about the direction */
-                       cmd.low_latency_rx = 1;
-                       cmd.low_latency_tx = 1;
-               }
-               res = iwl_mvm_send_cmd_pdu(mvm,
-                                          iwl_cmd_id(LOW_LATENCY_CMD,
-                                                     MAC_CONF_GROUP, 0),
-                                          0, sizeof(cmd), &cmd);
-               if (res)
-                       IWL_ERR(mvm, "Failed to send low latency command\n");
-       }
+       iwl_mvm_send_low_latency_cmd(mvm, low_latency, mvmvif->id);
 
        res = iwl_mvm_update_quotas(mvm, false, NULL);
        if (res)
@@ -1372,6 +1381,7 @@ void iwl_mvm_pause_tcm(struct iwl_mvm *mvm, bool with_cancel)
 void iwl_mvm_resume_tcm(struct iwl_mvm *mvm)
 {
        int mac;
+       bool low_latency = false;
 
        spin_lock_bh(&mvm->tcm.lock);
        mvm->tcm.ts = jiffies;
@@ -1383,10 +1393,23 @@ void iwl_mvm_resume_tcm(struct iwl_mvm *mvm)
                memset(&mdata->tx.pkts, 0, sizeof(mdata->tx.pkts));
                memset(&mdata->rx.airtime, 0, sizeof(mdata->rx.airtime));
                memset(&mdata->tx.airtime, 0, sizeof(mdata->tx.airtime));
+
+               if (mvm->tcm.result.low_latency[mac])
+                       low_latency = true;
        }
        /* The TCM data needs to be reset before "paused" flag changes */
        smp_mb();
        mvm->tcm.paused = false;
+
+       /*
+        * if the current load is not low or low latency is active, force
+        * re-evaluation to cover the case of no traffic.
+        */
+       if (mvm->tcm.result.global_load > IWL_MVM_TRAFFIC_LOW)
+               schedule_delayed_work(&mvm->tcm.work, MVM_TCM_PERIOD);
+       else if (low_latency)
+               schedule_delayed_work(&mvm->tcm.work, MVM_LL_PERIOD);
+
        spin_unlock_bh(&mvm->tcm.lock);
 }
 
index 05ed4fb..ceb3aa0 100644 (file)
@@ -94,11 +94,14 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
                cpu_to_le64(trans_pcie->rxq->bd_dma);
 
        /* Configure debug, for integration */
-       iwl_pcie_alloc_fw_monitor(trans, 0);
-       prph_sc_ctrl->hwm_cfg.hwm_base_addr =
-               cpu_to_le64(trans->fw_mon[0].physical);
-       prph_sc_ctrl->hwm_cfg.hwm_size =
-               cpu_to_le32(trans->fw_mon[0].size);
+       if (!trans->ini_valid)
+               iwl_pcie_alloc_fw_monitor(trans, 0);
+       if (trans->num_blocks) {
+               prph_sc_ctrl->hwm_cfg.hwm_base_addr =
+                       cpu_to_le64(trans->fw_mon[0].physical);
+               prph_sc_ctrl->hwm_cfg.hwm_size =
+                       cpu_to_le32(trans->fw_mon[0].size);
+       }
 
        /* allocate ucode sections in dram and set addresses */
        ret = iwl_pcie_init_fw_sec(trans, fw, &prph_scratch->dram);
index 6f45a03..7f4aaa8 100644 (file)
@@ -227,7 +227,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
        iwl_enable_interrupts(trans);
 
        /* Configure debug, if exists */
-       if (trans->dbg_dest_tlv)
+       if (iwl_pcie_dbg_on(trans))
                iwl_pcie_apply_destination(trans);
 
        /* kick FW self load */
index 9e01521..353581c 100644 (file)
@@ -513,6 +513,56 @@ 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, 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, 0x0060, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0064, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x00A0, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x00A4, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0230, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0234, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0238, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x023C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0260, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0264, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x02A0, iwl9462_2ac_cfg_soc)},
+       {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, 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, 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, 0x0060, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0064, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x00A0, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x00A4, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0230, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0234, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0238, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x023C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0260, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0264, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x02A0, iwl9462_2ac_cfg_soc)},
+       {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, 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)},
@@ -832,7 +882,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {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_2ac_cfg_jf)},
+       {IWL_PCI_DEVICE(0x34F0, 0x0310, iwl22000_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)},
index f9c4c64..d6fc6ce 100644 (file)
@@ -378,6 +378,23 @@ struct iwl_tso_hdr_page {
        u8 *pos;
 };
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+/**
+ * enum iwl_fw_mon_dbgfs_state - the different states of the monitor_data
+ * debugfs file
+ *
+ * @IWL_FW_MON_DBGFS_STATE_CLOSED: the file is closed.
+ * @IWL_FW_MON_DBGFS_STATE_OPEN: the file is open.
+ * @IWL_FW_MON_DBGFS_STATE_DISABLED: the file is disabled, once this state is
+ *     set the file can no longer be used.
+ */
+enum iwl_fw_mon_dbgfs_state {
+       IWL_FW_MON_DBGFS_STATE_CLOSED,
+       IWL_FW_MON_DBGFS_STATE_OPEN,
+       IWL_FW_MON_DBGFS_STATE_DISABLED,
+};
+#endif
+
 /**
  * enum iwl_shared_irq_flags - level of sharing for irq
  * @IWL_SHARED_IRQ_NON_RX: interrupt vector serves non rx causes.
@@ -414,6 +431,26 @@ struct iwl_self_init_dram {
        int paging_cnt;
 };
 
+/**
+ * struct cont_rec: continuous recording data structure
+ * @prev_wr_ptr: the last address that was read in monitor_data
+ *     debugfs file
+ * @prev_wrap_cnt: the wrap count that was used during the last read in
+ *     monitor_data debugfs file
+ * @state: the state of monitor_data debugfs file as described
+ *     in &iwl_fw_mon_dbgfs_state enum
+ * @mutex: locked while reading from monitor_data debugfs file
+ */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+struct cont_rec {
+       u32 prev_wr_ptr;
+       u32 prev_wrap_cnt;
+       u8  state;
+       /* Used to sync monitor_data debugfs file with driver unload flow */
+       struct mutex mutex;
+};
+#endif
+
 /**
  * struct iwl_trans_pcie - PCIe transport specific data
  * @rxq: all the RX queue data
@@ -451,6 +488,9 @@ struct iwl_self_init_dram {
  * @reg_lock: protect hw register access
  * @mutex: to protect stop_device / start_fw / start_hw
  * @cmd_in_flight: true when we have a host command in flight
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ * @fw_mon_data: fw continuous recording data
+#endif
  * @msix_entries: array of MSI-X entries
  * @msix_enabled: true if managed to enable MSI-X
  * @shared_vec_mask: the type of causes the shared vector handles
@@ -538,6 +578,10 @@ struct iwl_trans_pcie {
        bool cmd_hold_nic_awake;
        bool ref_cmd_in_flight;
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       struct cont_rec fw_mon_data;
+#endif
+
        struct msix_entry msix_entries[IWL_MAX_RX_HW_QUEUES];
        bool msix_enabled;
        u8 shared_vec_mask;
@@ -965,6 +1009,11 @@ static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
        __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
 }
 
+static inline bool iwl_pcie_dbg_on(struct iwl_trans *trans)
+{
+       return (trans->dbg_dest_tlv || trans->ini_valid);
+}
+
 void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state);
 void iwl_trans_pcie_dump_regs(struct iwl_trans *trans);
 
index 5bafb3f..f97aea5 100644 (file)
@@ -71,6 +71,7 @@
 #include <linux/vmalloc.h>
 #include <linux/pm_runtime.h>
 #include <linux/module.h>
+#include <linux/wait.h>
 
 #include "iwl-drv.h"
 #include "iwl-trans.h"
@@ -923,6 +924,20 @@ void iwl_pcie_apply_destination(struct iwl_trans *trans)
        const struct iwl_fw_dbg_dest_tlv_v1 *dest = trans->dbg_dest_tlv;
        int i;
 
+       if (trans->ini_valid) {
+               if (!trans->num_blocks)
+                       return;
+
+               iwl_write_prph(trans, MON_BUFF_BASE_ADDR_VER2,
+                              trans->fw_mon[0].physical >>
+                              MON_BUFF_SHIFT_VER2);
+               iwl_write_prph(trans, MON_BUFF_END_ADDR_VER2,
+                              (trans->fw_mon[0].physical +
+                               trans->fw_mon[0].size - 256) >>
+                              MON_BUFF_SHIFT_VER2);
+               return;
+       }
+
        IWL_INFO(trans, "Applying debug destination %s\n",
                 get_fw_dbg_mode_string(dest->monitor_mode));
 
@@ -1025,7 +1040,7 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
                                       (trans->fw_mon[0].physical +
                                        trans->fw_mon[0].size) >> 4);
                }
-       } else if (trans->dbg_dest_tlv) {
+       } else if (iwl_pcie_dbg_on(trans)) {
                iwl_pcie_apply_destination(trans);
        }
 
@@ -1046,7 +1061,7 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans,
        IWL_DEBUG_FW(trans, "working with %s CPU\n",
                     image->is_dual_cpus ? "Dual" : "Single");
 
-       if (trans->dbg_dest_tlv)
+       if (iwl_pcie_dbg_on(trans))
                iwl_pcie_apply_destination(trans);
 
        IWL_DEBUG_POWER(trans, "Original WFPM value = 0x%08X\n",
@@ -1729,6 +1744,7 @@ static int iwl_pcie_init_msix_handler(struct pci_dev *pdev,
 static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       u32 hpm;
        int err;
 
        lockdep_assert_held(&trans_pcie->mutex);
@@ -1739,6 +1755,17 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
                return err;
        }
 
+       hpm = iwl_trans_read_prph(trans, HPM_DEBUG);
+       if (hpm != 0xa5a5a5a0 && (hpm & PERSISTENCE_BIT)) {
+               if (iwl_trans_read_prph(trans, PREG_PRPH_WPROT_0) &
+                   PREG_WFPM_ACCESS) {
+                       IWL_ERR(trans,
+                               "Error, can not clear persistence bit\n");
+                       return -EPERM;
+               }
+               iwl_trans_write_prph(trans, HPM_DEBUG, hpm & ~PERSISTENCE_BIT);
+       }
+
        iwl_trans_pcie_sw_reset(trans);
 
        err = iwl_pcie_apm_init(trans);
@@ -2697,6 +2724,137 @@ static ssize_t iwl_dbgfs_rfkill_write(struct file *file,
        return count;
 }
 
+static int iwl_dbgfs_monitor_data_open(struct inode *inode,
+                                      struct file *file)
+{
+       struct iwl_trans *trans = inode->i_private;
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       if (!trans->dbg_dest_tlv ||
+           trans->dbg_dest_tlv->monitor_mode != EXTERNAL_MODE) {
+               IWL_ERR(trans, "Debug destination is not set to DRAM\n");
+               return -ENOENT;
+       }
+
+       if (trans_pcie->fw_mon_data.state != IWL_FW_MON_DBGFS_STATE_CLOSED)
+               return -EBUSY;
+
+       trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_OPEN;
+       return simple_open(inode, file);
+}
+
+static int iwl_dbgfs_monitor_data_release(struct inode *inode,
+                                         struct file *file)
+{
+       struct iwl_trans_pcie *trans_pcie =
+               IWL_TRANS_GET_PCIE_TRANS(inode->i_private);
+
+       if (trans_pcie->fw_mon_data.state == IWL_FW_MON_DBGFS_STATE_OPEN)
+               trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED;
+       return 0;
+}
+
+static bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count,
+                                 void *buf, ssize_t *size,
+                                 ssize_t *bytes_copied)
+{
+       int buf_size_left = count - *bytes_copied;
+
+       buf_size_left = buf_size_left - (buf_size_left % sizeof(u32));
+       if (*size > buf_size_left)
+               *size = buf_size_left;
+
+       *size -= copy_to_user(user_buf, buf, *size);
+       *bytes_copied += *size;
+
+       if (buf_size_left == *size)
+               return true;
+       return false;
+}
+
+static ssize_t iwl_dbgfs_monitor_data_read(struct file *file,
+                                          char __user *user_buf,
+                                          size_t count, loff_t *ppos)
+{
+       struct iwl_trans *trans = file->private_data;
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       void *cpu_addr = (void *)trans->fw_mon[0].block, *curr_buf;
+       struct cont_rec *data = &trans_pcie->fw_mon_data;
+       u32 write_ptr_addr, wrap_cnt_addr, write_ptr, wrap_cnt;
+       ssize_t size, bytes_copied = 0;
+       bool b_full;
+
+       if (trans->dbg_dest_tlv) {
+               write_ptr_addr =
+                       le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
+               wrap_cnt_addr = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
+       } else {
+               write_ptr_addr = MON_BUFF_WRPTR;
+               wrap_cnt_addr = MON_BUFF_CYCLE_CNT;
+       }
+
+       if (unlikely(!trans->dbg_rec_on))
+               return 0;
+
+       mutex_lock(&data->mutex);
+       if (data->state ==
+           IWL_FW_MON_DBGFS_STATE_DISABLED) {
+               mutex_unlock(&data->mutex);
+               return 0;
+       }
+
+       /* write_ptr position in bytes rather then DW */
+       write_ptr = iwl_read_prph(trans, write_ptr_addr) * sizeof(u32);
+       wrap_cnt = iwl_read_prph(trans, wrap_cnt_addr);
+
+       if (data->prev_wrap_cnt == wrap_cnt) {
+               size = write_ptr - data->prev_wr_ptr;
+               curr_buf = cpu_addr + data->prev_wr_ptr;
+               b_full = iwl_write_to_user_buf(user_buf, count,
+                                              curr_buf, &size,
+                                              &bytes_copied);
+               data->prev_wr_ptr += size;
+
+       } else if (data->prev_wrap_cnt == wrap_cnt - 1 &&
+                  write_ptr < data->prev_wr_ptr) {
+               size = trans->fw_mon[0].size - data->prev_wr_ptr;
+               curr_buf = cpu_addr + data->prev_wr_ptr;
+               b_full = iwl_write_to_user_buf(user_buf, count,
+                                              curr_buf, &size,
+                                              &bytes_copied);
+               data->prev_wr_ptr += size;
+
+               if (!b_full) {
+                       size = write_ptr;
+                       b_full = iwl_write_to_user_buf(user_buf, count,
+                                                      cpu_addr, &size,
+                                                      &bytes_copied);
+                       data->prev_wr_ptr = size;
+                       data->prev_wrap_cnt++;
+               }
+       } else {
+               if (data->prev_wrap_cnt == wrap_cnt - 1 &&
+                   write_ptr > data->prev_wr_ptr)
+                       IWL_WARN(trans,
+                                "write pointer passed previous write pointer, start copying from the beginning\n");
+               else if (!unlikely(data->prev_wrap_cnt == 0 &&
+                                  data->prev_wr_ptr == 0))
+                       IWL_WARN(trans,
+                                "monitor data is out of sync, start copying from the beginning\n");
+
+               size = write_ptr;
+               b_full = iwl_write_to_user_buf(user_buf, count,
+                                              cpu_addr, &size,
+                                              &bytes_copied);
+               data->prev_wr_ptr = size;
+               data->prev_wrap_cnt = wrap_cnt;
+       }
+
+       mutex_unlock(&data->mutex);
+
+       return bytes_copied;
+}
+
 DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
 DEBUGFS_READ_FILE_OPS(fh_reg);
 DEBUGFS_READ_FILE_OPS(rx_queue);
@@ -2704,6 +2862,12 @@ DEBUGFS_READ_FILE_OPS(tx_queue);
 DEBUGFS_WRITE_FILE_OPS(csr);
 DEBUGFS_READ_WRITE_FILE_OPS(rfkill);
 
+static const struct file_operations iwl_dbgfs_monitor_data_ops = {
+       .read = iwl_dbgfs_monitor_data_read,
+       .open = iwl_dbgfs_monitor_data_open,
+       .release = iwl_dbgfs_monitor_data_release,
+};
+
 /* Create the debugfs files and directories */
 int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
 {
@@ -2715,12 +2879,23 @@ int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
        DEBUGFS_ADD_FILE(csr, dir, 0200);
        DEBUGFS_ADD_FILE(fh_reg, dir, 0400);
        DEBUGFS_ADD_FILE(rfkill, dir, 0600);
+       DEBUGFS_ADD_FILE(monitor_data, dir, 0400);
        return 0;
 
 err:
        IWL_ERR(trans, "failed to create the trans debugfs entry\n");
        return -ENOMEM;
 }
+
+static void iwl_trans_pcie_debugfs_cleanup(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct cont_rec *data = &trans_pcie->fw_mon_data;
+
+       mutex_lock(&data->mutex);
+       data->state = IWL_FW_MON_DBGFS_STATE_DISABLED;
+       mutex_unlock(&data->mutex);
+}
 #endif /*CONFIG_IWLWIFI_DEBUGFS */
 
 static u32 iwl_trans_pcie_get_cmdlen(struct iwl_trans *trans, void *tfd)
@@ -2854,6 +3029,34 @@ iwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans,
        return monitor_len;
 }
 
+static void
+iwl_trans_pcie_dump_pointers(struct iwl_trans *trans,
+                            struct iwl_fw_error_dump_fw_mon *fw_mon_data)
+{
+       u32 base, write_ptr, wrap_cnt;
+
+       /* If there was a dest TLV - use the values from there */
+       if (trans->ini_valid) {
+               base = MON_BUFF_BASE_ADDR_VER2;
+               write_ptr = MON_BUFF_WRPTR_VER2;
+               wrap_cnt = MON_BUFF_CYCLE_CNT_VER2;
+       } else if (trans->dbg_dest_tlv) {
+               write_ptr = le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
+               wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
+               base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+       } else {
+               base = MON_BUFF_BASE_ADDR;
+               write_ptr = MON_BUFF_WRPTR;
+               wrap_cnt = MON_BUFF_CYCLE_CNT;
+       }
+       fw_mon_data->fw_mon_wr_ptr =
+               cpu_to_le32(iwl_read_prph(trans, write_ptr));
+       fw_mon_data->fw_mon_cycle_cnt =
+               cpu_to_le32(iwl_read_prph(trans, wrap_cnt));
+       fw_mon_data->fw_mon_base_ptr =
+               cpu_to_le32(iwl_read_prph(trans, base));
+}
+
 static u32
 iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
                            struct iwl_fw_error_dump_data **data,
@@ -2863,30 +3066,14 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
 
        if ((trans->num_blocks &&
             trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) ||
-           trans->dbg_dest_tlv) {
+            (trans->dbg_dest_tlv && !trans->ini_valid) ||
+            (trans->ini_valid && trans->num_blocks)) {
                struct iwl_fw_error_dump_fw_mon *fw_mon_data;
-               u32 base, write_ptr, wrap_cnt;
-
-               /* If there was a dest TLV - use the values from there */
-               if (trans->dbg_dest_tlv) {
-                       write_ptr =
-                               le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
-                       wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
-                       base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
-               } else {
-                       base = MON_BUFF_BASE_ADDR;
-                       write_ptr = MON_BUFF_WRPTR;
-                       wrap_cnt = MON_BUFF_CYCLE_CNT;
-               }
 
                (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
                fw_mon_data = (void *)(*data)->data;
-               fw_mon_data->fw_mon_wr_ptr =
-                       cpu_to_le32(iwl_read_prph(trans, write_ptr));
-               fw_mon_data->fw_mon_cycle_cnt =
-                       cpu_to_le32(iwl_read_prph(trans, wrap_cnt));
-               fw_mon_data->fw_mon_base_ptr =
-                       cpu_to_le32(iwl_read_prph(trans, base));
+
+               iwl_trans_pcie_dump_pointers(trans, fw_mon_data);
 
                len += sizeof(**data) + sizeof(*fw_mon_data);
                if (trans->num_blocks) {
@@ -2896,6 +3083,7 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
 
                        monitor_len = trans->fw_mon[0].size;
                } else if (trans->dbg_dest_tlv->monitor_mode == SMEM_MODE) {
+                       u32 base = le32_to_cpu(fw_mon_data->fw_mon_base_ptr);
                        /*
                         * Update pointers to reflect actual values after
                         * shifting
@@ -2978,7 +3166,7 @@ static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, int *len)
 
 static struct iwl_trans_dump_data
 *iwl_trans_pcie_dump_data(struct iwl_trans *trans,
-                         const struct iwl_fw_dbg_trigger_tlv *trigger)
+                         u32 dump_mask)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_fw_error_dump_data *data;
@@ -2990,7 +3178,10 @@ static struct iwl_trans_dump_data
        int i, ptr;
        bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) &&
                        !trans->cfg->mq_rx_supported &&
-                       trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_RB);
+                       dump_mask & BIT(IWL_FW_ERROR_DUMP_RB);
+
+       if (!dump_mask)
+               return NULL;
 
        /* transport dump header */
        len = sizeof(*dump_data);
@@ -3002,11 +3193,7 @@ static struct iwl_trans_dump_data
        /* FW monitor */
        monitor_len = iwl_trans_get_fw_monitor_len(trans, &len);
 
-       if (trigger && (trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)) {
-               if (!(trans->dbg_dump_mask &
-                     BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)))
-                       return NULL;
-
+       if (dump_mask == BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)) {
                dump_data = vzalloc(len);
                if (!dump_data)
                        return NULL;
@@ -3019,11 +3206,11 @@ static struct iwl_trans_dump_data
        }
 
        /* CSR registers */
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
                len += sizeof(*data) + IWL_CSR_TO_DUMP;
 
        /* FH registers */
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) {
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) {
                if (trans->cfg->gen2)
                        len += sizeof(*data) +
                               (FH_MEM_UPPER_BOUND_GEN2 -
@@ -3048,8 +3235,7 @@ static struct iwl_trans_dump_data
        }
 
        /* Paged memory for gen2 HW */
-       if (trans->cfg->gen2 &&
-           trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING))
+       if (trans->cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING))
                for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++)
                        len += sizeof(*data) +
                               sizeof(struct iwl_fw_error_dump_paging) +
@@ -3062,7 +3248,7 @@ static struct iwl_trans_dump_data
        len = 0;
        data = (void *)dump_data->data;
 
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD)) {
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD)) {
                u16 tfd_size = trans_pcie->tfd_size;
 
                data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
@@ -3096,16 +3282,15 @@ static struct iwl_trans_dump_data
                data = iwl_fw_error_next_data(data);
        }
 
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
                len += iwl_trans_pcie_dump_csr(trans, &data);
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS))
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS))
                len += iwl_trans_pcie_fh_regs_dump(trans, &data);
        if (dump_rbs)
                len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs);
 
        /* Paged memory for gen2 HW */
-       if (trans->cfg->gen2 &&
-           trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) {
+       if (trans->cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) {
                for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) {
                        struct iwl_fw_error_dump_paging *paging;
                        dma_addr_t addr =
@@ -3125,7 +3310,7 @@ static struct iwl_trans_dump_data
                        len += sizeof(*data) + sizeof(*paging) + page_len;
                }
        }
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR))
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR))
                len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
 
        dump_data->len = len;
@@ -3202,6 +3387,9 @@ static const struct iwl_trans_ops trans_ops_pcie = {
 
        .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
        .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup,
+#endif
 };
 
 static const struct iwl_trans_ops trans_ops_pcie_gen2 = {
@@ -3221,6 +3409,9 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = {
        .txq_free = iwl_trans_pcie_dyn_txq_free,
        .wait_txq_empty = iwl_trans_pcie_wait_txq_empty,
        .rxq_dma_data = iwl_trans_pcie_rxq_dma_data,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup,
+#endif
 };
 
 struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
@@ -3392,8 +3583,26 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
 #if IS_ENABLED(CONFIG_IWLMVM)
        trans->hw_rf_id = iwl_read32(trans, CSR_HW_RF_ID);
 
-       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)) {
+       if (cfg == &iwl22000_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;
+               } 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;
+               } 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_HRCDB)) {
+                       IWL_ERR(trans, "RF ID HRCDB is not supported\n");
+                       ret = -EINVAL;
+                       goto out_no_pci;
+               } else {
+                       IWL_ERR(trans, "Unrecognized RF ID 0x%08x\n",
+                               CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id));
+                       ret = -EINVAL;
+                       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)) {
                u32 hw_status;
 
                hw_status = iwl_read_prph(trans, UMAG_GEN_HW_STATUS);
@@ -3454,6 +3663,11 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        trans->runtime_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
 #endif /* CONFIG_IWLWIFI_PCIE_RTPM */
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED;
+       mutex_init(&trans_pcie->fw_mon_data.mutex);
+#endif
+
        return trans;
 
 out_free_ict:
index e880f69..156ca1b 100644 (file)
@@ -238,7 +238,7 @@ static int iwl_pcie_gen2_build_amsdu(struct iwl_trans *trans,
 {
 #ifdef CONFIG_INET
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload;
+       struct iwl_tx_cmd_gen2 *tx_cmd = (void *)dev_cmd->payload;
        struct ieee80211_hdr *hdr = (void *)skb->data;
        unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
        unsigned int mss = skb_shinfo(skb)->gso_size;
@@ -583,18 +583,6 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
 
        spin_lock(&txq->lock);
 
-       if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) {
-               struct iwl_tx_cmd_gen3 *tx_cmd_gen3 =
-                       (void *)dev_cmd->payload;
-
-               cmd_len = le16_to_cpu(tx_cmd_gen3->len);
-       } else {
-               struct iwl_tx_cmd_gen2 *tx_cmd_gen2 =
-                       (void *)dev_cmd->payload;
-
-               cmd_len = le16_to_cpu(tx_cmd_gen2->len);
-       }
-
        if (iwl_queue_space(trans, txq) < txq->high_mark) {
                iwl_stop_queue(trans, txq);
 
@@ -632,6 +620,18 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
                return -1;
        }
 
+       if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) {
+               struct iwl_tx_cmd_gen3 *tx_cmd_gen3 =
+                       (void *)dev_cmd->payload;
+
+               cmd_len = le16_to_cpu(tx_cmd_gen3->len);
+       } else {
+               struct iwl_tx_cmd_gen2 *tx_cmd_gen2 =
+                       (void *)dev_cmd->payload;
+
+               cmd_len = le16_to_cpu(tx_cmd_gen2->len);
+       }
+
        /* Set up entry for this TFD in Tx byte-count array */
        iwl_pcie_gen2_update_byte_tbl(trans_pcie, txq, cmd_len,
                                      iwl_pcie_gen2_get_num_tbs(trans, tfd));
@@ -1228,8 +1228,7 @@ int iwl_trans_pcie_txq_alloc_response(struct iwl_trans *trans,
        /* Place first TFD at index corresponding to start sequence number */
        txq->read_ptr = wr_ptr;
        txq->write_ptr = wr_ptr;
-       iwl_write_direct32(trans, HBUS_TARG_WRPTR,
-                          (txq->write_ptr) | (qid << 16));
+
        IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d\n", qid);
 
        iwl_free_resp(hcmd);
index 87b7225..ee990a7 100644 (file)
@@ -1160,10 +1160,11 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
                         */
                        iwl_trans_tx(trans, skb, dev_cmd_ptr, txq_id);
                }
-               spin_lock_bh(&txq->lock);
 
                if (iwl_queue_space(trans, txq) > txq->low_mark)
                        iwl_wake_queue(trans, txq);
+
+               spin_lock_bh(&txq->lock);
        }
 
        if (txq->read_ptr == txq->write_ptr) {
@@ -1245,11 +1246,11 @@ void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
 
        if (idx >= trans->cfg->base_params->max_tfd_queue_size ||
            (!iwl_queue_used(txq, idx))) {
-               IWL_ERR(trans,
-                       "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
-                       __func__, txq_id, idx,
-                       trans->cfg->base_params->max_tfd_queue_size,
-                       txq->write_ptr, txq->read_ptr);
+               WARN_ONCE(test_bit(txq_id, trans_pcie->queue_used),
+                         "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
+                         __func__, txq_id, idx,
+                         trans->cfg->base_params->max_tfd_queue_size,
+                         txq->write_ptr, txq->read_ptr);
                return;
        }
 
index 012930d..b0e7c0a 100644 (file)
@@ -690,7 +690,7 @@ static int prism2_open(struct net_device *dev)
                /* Master radio interface is needed for all operation, so open
                 * it automatically when any virtual net_device is opened. */
                local->master_dev_auto_open = 1;
-               dev_open(local->dev);
+               dev_open(local->dev, NULL);
        }
 
        netif_device_attach(dev);
index 21bb684..40a8b94 100644 (file)
@@ -908,6 +908,7 @@ static int ezusb_access_ltv(struct ezusb_priv *upriv,
        case EZUSB_CTX_REQ_SUBMITTED:
                if (!ctx->in_rid)
                        break;
+               /* fall through */
        default:
                err("%s: Unexpected context state %d", __func__,
                    state);
index ce9d4db..b0eb58a 100644 (file)
@@ -235,6 +235,7 @@ isl38xx_in_queue(isl38xx_control_block *cb, int queue)
                /* send queues */
        case ISL38XX_CB_TX_MGMTQ:
                BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE);
+               /* fall through */
 
        case ISL38XX_CB_TX_DATA_LQ:
        case ISL38XX_CB_TX_DATA_HQ:
index 334717b..3ccf2a4 100644 (file)
@@ -1691,6 +1691,7 @@ static int prism54_get_encodeext(struct net_device *ndev,
        case DOT11_AUTH_BOTH:
        case DOT11_AUTH_SK:
                wrqu->encoding.flags |= IW_ENCODE_RESTRICTED;
+               /* fall through */
        case DOT11_AUTH_OS:
        default:
                wrqu->encoding.flags |= IW_ENCODE_OPEN;
index 325176d..ad6d3a5 100644 (file)
@@ -932,6 +932,7 @@ islpci_set_state(islpci_private *priv, islpci_state_t new_state)
        switch (new_state) {
        case PRV_STATE_OFF:
                priv->state_off++;
+               /* fall through */
        default:
                priv->state = new_state;
                break;
index aa80582..3a4b878 100644 (file)
@@ -374,6 +374,20 @@ static const struct ieee80211_rate hwsim_rates[] = {
        { .bitrate = 540 }
 };
 
+static const u32 hwsim_ciphers[] = {
+       WLAN_CIPHER_SUITE_WEP40,
+       WLAN_CIPHER_SUITE_WEP104,
+       WLAN_CIPHER_SUITE_TKIP,
+       WLAN_CIPHER_SUITE_CCMP,
+       WLAN_CIPHER_SUITE_CCMP_256,
+       WLAN_CIPHER_SUITE_GCMP,
+       WLAN_CIPHER_SUITE_GCMP_256,
+       WLAN_CIPHER_SUITE_AES_CMAC,
+       WLAN_CIPHER_SUITE_BIP_CMAC_256,
+       WLAN_CIPHER_SUITE_BIP_GMAC_128,
+       WLAN_CIPHER_SUITE_BIP_GMAC_256,
+};
+
 #define OUI_QCA 0x001374
 #define QCA_NL80211_SUBCMD_TEST 1
 enum qca_nl80211_vendor_subcmds {
@@ -451,48 +465,6 @@ static const struct nl80211_vendor_cmd_info mac80211_hwsim_vendor_events[] = {
        { .vendor_id = OUI_QCA, .subcmd = 1 },
 };
 
-static const struct ieee80211_iface_limit hwsim_if_limits[] = {
-       { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
-       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) |
-                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
-#ifdef CONFIG_MAC80211_MESH
-                                BIT(NL80211_IFTYPE_MESH_POINT) |
-#endif
-                                BIT(NL80211_IFTYPE_AP) |
-                                BIT(NL80211_IFTYPE_P2P_GO) },
-       /* must be last, see hwsim_if_comb */
-       { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }
-};
-
-static const struct ieee80211_iface_combination hwsim_if_comb[] = {
-       {
-               .limits = hwsim_if_limits,
-               /* remove the last entry which is P2P_DEVICE */
-               .n_limits = ARRAY_SIZE(hwsim_if_limits) - 1,
-               .max_interfaces = 2048,
-               .num_different_channels = 1,
-               .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
-                                      BIT(NL80211_CHAN_WIDTH_20) |
-                                      BIT(NL80211_CHAN_WIDTH_40) |
-                                      BIT(NL80211_CHAN_WIDTH_80) |
-                                      BIT(NL80211_CHAN_WIDTH_160),
-       },
-};
-
-static const struct ieee80211_iface_combination hwsim_if_comb_p2p_dev[] = {
-       {
-               .limits = hwsim_if_limits,
-               .n_limits = ARRAY_SIZE(hwsim_if_limits),
-               .max_interfaces = 2048,
-               .num_different_channels = 1,
-               .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
-                                      BIT(NL80211_CHAN_WIDTH_20) |
-                                      BIT(NL80211_CHAN_WIDTH_40) |
-                                      BIT(NL80211_CHAN_WIDTH_80) |
-                                      BIT(NL80211_CHAN_WIDTH_160),
-       },
-};
-
 static spinlock_t hwsim_radio_lock;
 static LIST_HEAD(hwsim_radios);
 static struct rhashtable hwsim_radios_rht;
@@ -515,6 +487,10 @@ struct mac80211_hwsim_data {
        struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)];
        struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)];
        struct ieee80211_iface_combination if_combination;
+       struct ieee80211_iface_limit if_limits[3];
+       int n_if_limits;
+
+       u32 ciphers[ARRAY_SIZE(hwsim_ciphers)];
 
        struct mac_address addresses[2];
        int channels, idx;
@@ -642,6 +618,8 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
        [HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG },
        [HWSIM_ATTR_FREQ] = { .type = NLA_U32 },
        [HWSIM_ATTR_PERM_ADDR] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
+       [HWSIM_ATTR_IFTYPE_SUPPORT] = { .type = NLA_U32 },
+       [HWSIM_ATTR_CIPHER_SUPPORT] = { .type = NLA_BINARY },
 };
 
 static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
@@ -2414,6 +2392,9 @@ struct hwsim_new_radio_params {
        const char *hwname;
        bool no_vif;
        const u8 *perm_addr;
+       u32 iftypes;
+       u32 *ciphers;
+       u8 n_ciphers;
 };
 
 static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
@@ -2630,6 +2611,27 @@ static void mac80211_hswim_he_capab(struct ieee80211_supported_band *sband)
        sband->n_iftype_data = 1;
 }
 
+#ifdef CONFIG_MAC80211_MESH
+#define HWSIM_MESH_BIT BIT(NL80211_IFTYPE_MESH_POINT)
+#else
+#define HWSIM_MESH_BIT 0
+#endif
+
+#define HWSIM_DEFAULT_IF_LIMIT \
+       (BIT(NL80211_IFTYPE_STATION) | \
+        BIT(NL80211_IFTYPE_P2P_CLIENT) | \
+        BIT(NL80211_IFTYPE_AP) | \
+        BIT(NL80211_IFTYPE_P2P_GO) | \
+        HWSIM_MESH_BIT)
+
+#define HWSIM_IFTYPE_SUPPORT_MASK \
+       (BIT(NL80211_IFTYPE_STATION) | \
+        BIT(NL80211_IFTYPE_AP) | \
+        BIT(NL80211_IFTYPE_P2P_CLIENT) | \
+        BIT(NL80211_IFTYPE_P2P_GO) | \
+        BIT(NL80211_IFTYPE_ADHOC) | \
+        BIT(NL80211_IFTYPE_MESH_POINT))
+
 static int mac80211_hwsim_new_radio(struct genl_info *info,
                                    struct hwsim_new_radio_params *param)
 {
@@ -2641,6 +2643,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
        const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
        struct net *net;
        int idx;
+       int n_limits = 0;
 
        if (WARN_ON(param->channels > 1 && !param->use_chanctx))
                return -EINVAL;
@@ -2716,26 +2719,60 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
        if (info)
                data->portid = info->snd_portid;
 
+       /* setup interface limits, only on interface types we support */
+       if (param->iftypes & BIT(NL80211_IFTYPE_ADHOC)) {
+               data->if_limits[n_limits].max = 1;
+               data->if_limits[n_limits].types = BIT(NL80211_IFTYPE_ADHOC);
+               n_limits++;
+       }
+
+       if (param->iftypes & HWSIM_DEFAULT_IF_LIMIT) {
+               data->if_limits[n_limits].max = 2048;
+               /*
+                * For this case, we may only support a subset of
+                * HWSIM_DEFAULT_IF_LIMIT, therefore we only want to add the
+                * bits that both param->iftype & HWSIM_DEFAULT_IF_LIMIT have.
+                */
+               data->if_limits[n_limits].types =
+                                       HWSIM_DEFAULT_IF_LIMIT & param->iftypes;
+               n_limits++;
+       }
+
+       if (param->iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) {
+               data->if_limits[n_limits].max = 1;
+               data->if_limits[n_limits].types =
+                                               BIT(NL80211_IFTYPE_P2P_DEVICE);
+               n_limits++;
+       }
+
        if (data->use_chanctx) {
                hw->wiphy->max_scan_ssids = 255;
                hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
                hw->wiphy->max_remain_on_channel_duration = 1000;
-               hw->wiphy->iface_combinations = &data->if_combination;
-               if (param->p2p_device)
-                       data->if_combination = hwsim_if_comb_p2p_dev[0];
-               else
-                       data->if_combination = hwsim_if_comb[0];
-               hw->wiphy->n_iface_combinations = 1;
-               /* For channels > 1 DFS is not allowed */
                data->if_combination.radar_detect_widths = 0;
                data->if_combination.num_different_channels = data->channels;
-       } else if (param->p2p_device) {
-               hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
-               hw->wiphy->n_iface_combinations =
-                       ARRAY_SIZE(hwsim_if_comb_p2p_dev);
        } else {
-               hw->wiphy->iface_combinations = hwsim_if_comb;
-               hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
+               data->if_combination.num_different_channels = 1;
+               data->if_combination.radar_detect_widths =
+                                       BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                       BIT(NL80211_CHAN_WIDTH_20) |
+                                       BIT(NL80211_CHAN_WIDTH_40) |
+                                       BIT(NL80211_CHAN_WIDTH_80) |
+                                       BIT(NL80211_CHAN_WIDTH_160);
+       }
+
+       data->if_combination.n_limits = n_limits;
+       data->if_combination.max_interfaces = 2048;
+       data->if_combination.limits = data->if_limits;
+
+       hw->wiphy->iface_combinations = &data->if_combination;
+       hw->wiphy->n_iface_combinations = 1;
+
+       if (param->ciphers) {
+               memcpy(data->ciphers, param->ciphers,
+                      param->n_ciphers * sizeof(u32));
+               hw->wiphy->cipher_suites = data->ciphers;
+               hw->wiphy->n_cipher_suites = param->n_ciphers;
        }
 
        INIT_DELAYED_WORK(&data->roc_start, hw_roc_start);
@@ -2744,15 +2781,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
 
        hw->queues = 5;
        hw->offchannel_tx_hw_queue = 4;
-       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
-                                    BIT(NL80211_IFTYPE_AP) |
-                                    BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                                    BIT(NL80211_IFTYPE_P2P_GO) |
-                                    BIT(NL80211_IFTYPE_ADHOC) |
-                                    BIT(NL80211_IFTYPE_MESH_POINT);
-
-       if (param->p2p_device)
-               hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
 
        ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
        ieee80211_hw_set(hw, CHANCTX_STA_CSA);
@@ -2778,6 +2806,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
                               NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
        wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
 
+       hw->wiphy->interface_modes = param->iftypes;
+
        /* ask mac80211 to reserve space for magic */
        hw->vif_data_size = sizeof(struct hwsim_vif_priv);
        hw->sta_data_size = sizeof(struct hwsim_sta_priv);
@@ -2884,6 +2914,10 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
 
        wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
 
+       tasklet_hrtimer_init(&data->beacon_timer,
+                            mac80211_hwsim_beacon,
+                            CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+
        err = ieee80211_register_hw(hw);
        if (err < 0) {
                pr_debug("mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
@@ -2908,10 +2942,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
                                    data->debugfs,
                                    data, &hwsim_simulate_radar);
 
-       tasklet_hrtimer_init(&data->beacon_timer,
-                            mac80211_hwsim_beacon,
-                            CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
-
        spin_lock_bh(&hwsim_radio_lock);
        err = rhashtable_insert_fast(&hwsim_radios_rht, &data->rht,
                                     hwsim_rht_params);
@@ -3293,6 +3323,29 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2,
        return 0;
 }
 
+/* ensures ciphers only include ciphers listed in 'hwsim_ciphers' array */
+static bool hwsim_known_ciphers(const u32 *ciphers, int n_ciphers)
+{
+       int i;
+
+       for (i = 0; i < n_ciphers; i++) {
+               int j;
+               int found = 0;
+
+               for (j = 0; j < ARRAY_SIZE(hwsim_ciphers); j++) {
+                       if (ciphers[i] == hwsim_ciphers[j]) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found)
+                       return false;
+       }
+
+       return true;
+}
+
 static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
 {
        struct hwsim_new_radio_params param = { 0 };
@@ -3321,15 +3374,6 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
        if (info->attrs[HWSIM_ATTR_NO_VIF])
                param.no_vif = true;
 
-       if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
-               hwname = kasprintf(GFP_KERNEL, "%.*s",
-                                  nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
-                                  (char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
-               if (!hwname)
-                       return -ENOMEM;
-               param.hwname = hwname;
-       }
-
        if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
                param.use_chanctx = true;
        else
@@ -3342,10 +3386,8 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
        if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
                u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
 
-               if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) {
-                       kfree(hwname);
+               if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
                        return -EINVAL;
-               }
 
                idx = array_index_nospec(idx,
                                         ARRAY_SIZE(hwsim_world_regdom_custom));
@@ -3358,14 +3400,72 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
                        GENL_SET_ERR_MSG(info,"MAC is no valid source addr");
                        NL_SET_BAD_ATTR(info->extack,
                                        info->attrs[HWSIM_ATTR_PERM_ADDR]);
-                       kfree(hwname);
                        return -EINVAL;
                }
 
-
                param.perm_addr = nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]);
        }
 
+       if (info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]) {
+               param.iftypes =
+                       nla_get_u32(info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]);
+
+               if (param.iftypes & ~HWSIM_IFTYPE_SUPPORT_MASK) {
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT],
+                                           "cannot support more iftypes than kernel");
+                       return -EINVAL;
+               }
+       } else {
+               param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK;
+       }
+
+       /* ensure both flag and iftype support is honored */
+       if (param.p2p_device ||
+           param.iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) {
+               param.iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
+               param.p2p_device = true;
+       }
+
+       if (info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]) {
+               u32 len = nla_len(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]);
+
+               param.ciphers =
+                       nla_data(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]);
+
+               if (len % sizeof(u32)) {
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
+                                           "bad cipher list length");
+                       return -EINVAL;
+               }
+
+               param.n_ciphers = len / sizeof(u32);
+
+               if (param.n_ciphers > ARRAY_SIZE(hwsim_ciphers)) {
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
+                                           "too many ciphers specified");
+                       return -EINVAL;
+               }
+
+               if (!hwsim_known_ciphers(param.ciphers, param.n_ciphers)) {
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
+                                           "unsupported ciphers specified");
+                       return -EINVAL;
+               }
+       }
+
+       if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
+               hwname = kasprintf(GFP_KERNEL, "%.*s",
+                                  nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
+                                  (char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
+               if (!hwname)
+                       return -ENOMEM;
+               param.hwname = hwname;
+       }
+
        ret = mac80211_hwsim_new_radio(info, &param);
        kfree(hwname);
        return ret;
@@ -3703,16 +3803,16 @@ static int __init init_mac80211_hwsim(void)
        if (err)
                goto out_unregister_pernet;
 
+       err = hwsim_init_netlink();
+       if (err)
+               goto out_unregister_driver;
+
        hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim");
        if (IS_ERR(hwsim_class)) {
                err = PTR_ERR(hwsim_class);
-               goto out_unregister_driver;
+               goto out_exit_netlink;
        }
 
-       err = hwsim_init_netlink();
-       if (err < 0)
-               goto out_unregister_driver;
-
        for (i = 0; i < radios; i++) {
                struct hwsim_new_radio_params param = { 0 };
 
@@ -3785,6 +3885,7 @@ static int __init init_mac80211_hwsim(void)
 
                param.p2p_device = support_p2p_device;
                param.use_chanctx = channels > 1;
+               param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK;
 
                err = mac80211_hwsim_new_radio(NULL, &param);
                if (err < 0)
@@ -3818,6 +3919,8 @@ out_free_mon:
        free_netdev(hwsim_mon);
 out_free_radios:
        mac80211_hwsim_free();
+out_exit_netlink:
+       hwsim_exit_netlink();
 out_unregister_driver:
        platform_driver_unregister(&mac80211_hwsim_driver);
 out_unregister_pernet:
index 0fe3199..a1ef845 100644 (file)
@@ -132,6 +132,8 @@ enum {
  * @HWSIM_ATTR_TX_INFO_FLAGS: additional flags for corresponding
  *     rates of %HWSIM_ATTR_TX_INFO
  * @HWSIM_ATTR_PERM_ADDR: permanent mac address of new radio
+ * @HWSIM_ATTR_IFTYPE_SUPPORT: u32 attribute of supported interface types bits
+ * @HWSIM_ATTR_CIPHER_SUPPORT: u32 array of supported cipher types
  * @__HWSIM_ATTR_MAX: enum limit
  */
 
@@ -160,6 +162,8 @@ enum {
        HWSIM_ATTR_PAD,
        HWSIM_ATTR_TX_INFO_FLAGS,
        HWSIM_ATTR_PERM_ADDR,
+       HWSIM_ATTR_IFTYPE_SUPPORT,
+       HWSIM_ATTR_CIPHER_SUPPORT,
        __HWSIM_ATTR_MAX,
 };
 #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
index 504d6e0..7c3224b 100644 (file)
@@ -796,15 +796,13 @@ static void if_spi_h2c(struct if_spi_card *card,
 {
        struct lbs_private *priv = card->priv;
        int err = 0;
-       u16 int_type, port_reg;
+       u16 port_reg;
 
        switch (type) {
        case MVMS_DAT:
-               int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER;
                port_reg = IF_SPI_DATA_RDWRPORT_REG;
                break;
        case MVMS_CMD:
-               int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER;
                port_reg = IF_SPI_CMD_RDWRPORT_REG;
                break;
        default:
index e2addd8..5d75c97 100644 (file)
@@ -696,11 +696,10 @@ void mwifiex_11n_delba(struct mwifiex_private *priv, int tid)
                                "Send delba to tid=%d, %pM\n",
                                tid, rx_reor_tbl_ptr->ta);
                        mwifiex_send_delba(priv, tid, rx_reor_tbl_ptr->ta, 0);
-                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
-                                              flags);
-                       return;
+                       goto exit;
                }
        }
+exit:
        spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 }
 
index 8e63d14..5380fba 100644 (file)
@@ -103,8 +103,6 @@ static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload)
  * There could be holes in the buffer, which are skipped by the function.
  * Since the buffer is linear, the function uses rotation to simulate
  * circular buffer.
- *
- * The caller must hold rx_reorder_tbl_lock spinlock.
  */
 static void
 mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv,
@@ -113,21 +111,25 @@ mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv,
 {
        int pkt_to_send, i;
        void *rx_tmp_ptr;
+       unsigned long flags;
 
        pkt_to_send = (start_win > tbl->start_win) ?
                      min((start_win - tbl->start_win), tbl->win_size) :
                      tbl->win_size;
 
        for (i = 0; i < pkt_to_send; ++i) {
+               spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
                rx_tmp_ptr = NULL;
                if (tbl->rx_reorder_ptr[i]) {
                        rx_tmp_ptr = tbl->rx_reorder_ptr[i];
                        tbl->rx_reorder_ptr[i] = NULL;
                }
+               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
                if (rx_tmp_ptr)
                        mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr);
        }
 
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        /*
         * We don't have a circular buffer, hence use rotation to simulate
         * circular buffer
@@ -138,6 +140,7 @@ mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv,
        }
 
        tbl->start_win = start_win;
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 }
 
 /*
@@ -147,8 +150,6 @@ mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv,
  * The start window is adjusted automatically when a hole is located.
  * Since the buffer is linear, the function uses rotation to simulate
  * circular buffer.
- *
- * The caller must hold rx_reorder_tbl_lock spinlock.
  */
 static void
 mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
@@ -156,15 +157,22 @@ mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
 {
        int i, j, xchg;
        void *rx_tmp_ptr;
+       unsigned long flags;
 
        for (i = 0; i < tbl->win_size; ++i) {
-               if (!tbl->rx_reorder_ptr[i])
+               spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+               if (!tbl->rx_reorder_ptr[i]) {
+                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+                                              flags);
                        break;
+               }
                rx_tmp_ptr = tbl->rx_reorder_ptr[i];
                tbl->rx_reorder_ptr[i] = NULL;
+               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
                mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr);
        }
 
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        /*
         * We don't have a circular buffer, hence use rotation to simulate
         * circular buffer
@@ -177,6 +185,7 @@ mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
                }
        }
        tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1);
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 }
 
 /*
@@ -184,8 +193,6 @@ mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
  *
  * The function stops the associated timer and dispatches all the
  * pending packets in the Rx reorder table before deletion.
- *
- * The caller must hold rx_reorder_tbl_lock spinlock.
  */
 static void
 mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
@@ -211,7 +218,11 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
 
        del_timer_sync(&tbl->timer_context.timer);
        tbl->timer_context.timer_is_set = false;
+
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        list_del(&tbl->list);
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
        kfree(tbl->rx_reorder_ptr);
        kfree(tbl);
 
@@ -224,17 +235,22 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
 /*
  * This function returns the pointer to an entry in Rx reordering
  * table which matches the given TA/TID pair.
- *
- * The caller must hold rx_reorder_tbl_lock spinlock.
  */
 struct mwifiex_rx_reorder_tbl *
 mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta)
 {
        struct mwifiex_rx_reorder_tbl *tbl;
+       unsigned long flags;
 
-       list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list)
-               if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid)
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) {
+               if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) {
+                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+                                              flags);
                        return tbl;
+               }
+       }
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
        return NULL;
 }
@@ -251,9 +267,14 @@ void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta)
                return;
 
        spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
-       list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list)
-               if (!memcmp(tbl->ta, ta, ETH_ALEN))
+       list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) {
+               if (!memcmp(tbl->ta, ta, ETH_ALEN)) {
+                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+                                              flags);
                        mwifiex_del_rx_reorder_entry(priv, tbl);
+                       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+               }
+       }
        spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
        return;
@@ -262,18 +283,24 @@ void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta)
 /*
  * This function finds the last sequence number used in the packets
  * buffered in Rx reordering table.
- *
- * The caller must hold rx_reorder_tbl_lock spinlock.
  */
 static int
 mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx)
 {
        struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr;
+       struct mwifiex_private *priv = ctx->priv;
+       unsigned long flags;
        int i;
 
-       for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i)
-               if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) {
+               if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) {
+                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+                                              flags);
                        return i;
+               }
+       }
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
        return -1;
 }
@@ -291,22 +318,17 @@ mwifiex_flush_data(struct timer_list *t)
        struct reorder_tmr_cnxt *ctx =
                from_timer(ctx, t, timer);
        int start_win, seq_num;
-       unsigned long flags;
 
        ctx->timer_is_set = false;
-       spin_lock_irqsave(&ctx->priv->rx_reorder_tbl_lock, flags);
        seq_num = mwifiex_11n_find_last_seq_num(ctx);
 
-       if (seq_num < 0) {
-               spin_unlock_irqrestore(&ctx->priv->rx_reorder_tbl_lock, flags);
+       if (seq_num < 0)
                return;
-       }
 
        mwifiex_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num);
        start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1);
        mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr,
                                                 start_win);
-       spin_unlock_irqrestore(&ctx->priv->rx_reorder_tbl_lock, flags);
 }
 
 /*
@@ -333,14 +355,11 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
         * If we get a TID, ta pair which is already present dispatch all the
         * the packets and move the window size until the ssn
         */
-       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
        if (tbl) {
                mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num);
-               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
                return;
        }
-       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
        /* if !tbl then create one */
        new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL);
        if (!new_node)
@@ -551,20 +570,16 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
        int prev_start_win, start_win, end_win, win_size;
        u16 pkt_index;
        bool init_window_shift = false;
-       unsigned long flags;
        int ret = 0;
 
-       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
        if (!tbl) {
-               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
                if (pkt_type != PKT_TYPE_BAR)
                        mwifiex_11n_dispatch_pkt(priv, payload);
                return ret;
        }
 
        if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) {
-               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
                mwifiex_11n_dispatch_pkt(priv, payload);
                return ret;
        }
@@ -651,8 +666,6 @@ done:
        if (!tbl->timer_context.timer_is_set ||
            prev_start_win != tbl->start_win)
                mwifiex_11n_rxreorder_timer_restart(tbl);
-
-       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
        return ret;
 }
 
@@ -681,18 +694,14 @@ mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac,
                    peer_mac, tid, initiator);
 
        if (cleanup_rx_reorder_tbl) {
-               spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
                tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
                                                                 peer_mac);
                if (!tbl) {
-                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
-                                              flags);
                        mwifiex_dbg(priv->adapter, EVENT,
                                    "event: TID, TA not found in table\n");
                        return;
                }
                mwifiex_del_rx_reorder_entry(priv, tbl);
-               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
        } else {
                ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac);
                if (!ptx_tbl) {
@@ -726,7 +735,6 @@ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
        int tid, win_size;
        struct mwifiex_rx_reorder_tbl *tbl;
        uint16_t block_ack_param_set;
-       unsigned long flags;
 
        block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
 
@@ -740,20 +748,17 @@ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
                mwifiex_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n",
                            add_ba_rsp->peer_mac_addr, tid);
 
-               spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
                tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
                                                     add_ba_rsp->peer_mac_addr);
                if (tbl)
                        mwifiex_del_rx_reorder_entry(priv, tbl);
 
-               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
                return 0;
        }
 
        win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
                    >> BLOCKACKPARAM_WINSIZE_POS;
 
-       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
                                             add_ba_rsp->peer_mac_addr);
        if (tbl) {
@@ -764,7 +769,6 @@ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
                else
                        tbl->amsdu = false;
        }
-       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
        mwifiex_dbg(priv->adapter, CMD,
                    "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n",
@@ -804,8 +808,11 @@ void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv)
 
        spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        list_for_each_entry_safe(del_tbl_ptr, tmp_node,
-                                &priv->rx_reorder_tbl_ptr, list)
+                                &priv->rx_reorder_tbl_ptr, list) {
+               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
                mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr);
+               spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       }
        INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
        spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
@@ -929,7 +936,6 @@ void mwifiex_11n_rxba_sync_event(struct mwifiex_private *priv,
        int tlv_buf_left = len;
        int ret;
        u8 *tmp;
-       unsigned long flags;
 
        mwifiex_dbg_dump(priv->adapter, EVT_D, "RXBA_SYNC event:",
                         event_buf, len);
@@ -949,18 +955,14 @@ void mwifiex_11n_rxba_sync_event(struct mwifiex_private *priv,
                            tlv_rxba->mac, tlv_rxba->tid, tlv_seq_num,
                            tlv_bitmap_len);
 
-               spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
                rx_reor_tbl_ptr =
                        mwifiex_11n_get_rx_reorder_tbl(priv, tlv_rxba->tid,
                                                       tlv_rxba->mac);
                if (!rx_reor_tbl_ptr) {
-                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
-                                              flags);
                        mwifiex_dbg(priv->adapter, ERROR,
                                    "Can not find rx_reorder_tbl!");
                        return;
                }
-               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
                for (i = 0; i < tlv_bitmap_len; i++) {
                        for (j = 0 ; j < 8; j++) {
index adc8843..1467af2 100644 (file)
@@ -1275,27 +1275,27 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy,
 }
 
 static void
-mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo,
+mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 rateinfo, u8 htinfo,
                     struct rate_info *rate)
 {
        struct mwifiex_adapter *adapter = priv->adapter;
 
        if (adapter->is_hw_11ac_capable) {
                /* bit[1-0]: 00=LG 01=HT 10=VHT */
-               if (tx_htinfo & BIT(0)) {
+               if (htinfo & BIT(0)) {
                        /* HT */
-                       rate->mcs = priv->tx_rate;
+                       rate->mcs = rateinfo;
                        rate->flags |= RATE_INFO_FLAGS_MCS;
                }
-               if (tx_htinfo & BIT(1)) {
+               if (htinfo & BIT(1)) {
                        /* VHT */
-                       rate->mcs = priv->tx_rate & 0x0F;
+                       rate->mcs = rateinfo & 0x0F;
                        rate->flags |= RATE_INFO_FLAGS_VHT_MCS;
                }
 
-               if (tx_htinfo & (BIT(1) | BIT(0))) {
+               if (htinfo & (BIT(1) | BIT(0))) {
                        /* HT or VHT */
-                       switch (tx_htinfo & (BIT(3) | BIT(2))) {
+                       switch (htinfo & (BIT(3) | BIT(2))) {
                        case 0:
                                rate->bw = RATE_INFO_BW_20;
                                break;
@@ -1310,29 +1310,51 @@ mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo,
                                break;
                        }
 
-                       if (tx_htinfo & BIT(4))
+                       if (htinfo & BIT(4))
                                rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
 
-                       if ((priv->tx_rate >> 4) == 1)
+                       if ((rateinfo >> 4) == 1)
                                rate->nss = 2;
                        else
                                rate->nss = 1;
                }
        } else {
                /*
-                * Bit 0 in tx_htinfo indicates that current Tx rate
-                * is 11n rate. Valid MCS index values for us are 0 to 15.
+                * Bit 0 in htinfo indicates that current rate is 11n. Valid
+                * MCS index values for us are 0 to 15.
                 */
-               if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) {
-                       rate->mcs = priv->tx_rate;
+               if ((htinfo & BIT(0)) && (rateinfo < 16)) {
+                       rate->mcs = rateinfo;
                        rate->flags |= RATE_INFO_FLAGS_MCS;
                        rate->bw = RATE_INFO_BW_20;
-                       if (tx_htinfo & BIT(1))
+                       if (htinfo & BIT(1))
                                rate->bw = RATE_INFO_BW_40;
-                       if (tx_htinfo & BIT(2))
+                       if (htinfo & BIT(2))
                                rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
                }
        }
+
+       /* Decode legacy rates for non-HT. */
+       if (!(htinfo & (BIT(0) | BIT(1)))) {
+               /* Bitrates in multiples of 100kb/s. */
+               static const int legacy_rates[] = {
+                       [0] = 10,
+                       [1] = 20,
+                       [2] = 55,
+                       [3] = 110,
+                       [4] = 60, /* MWIFIEX_RATE_INDEX_OFDM0 */
+                       [5] = 60,
+                       [6] = 90,
+                       [7] = 120,
+                       [8] = 180,
+                       [9] = 240,
+                       [10] = 360,
+                       [11] = 480,
+                       [12] = 540,
+               };
+               if (rateinfo < ARRAY_SIZE(legacy_rates))
+                       rate->legacy = legacy_rates[rateinfo];
+       }
 }
 
 /*
@@ -1375,7 +1397,8 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
                sinfo->tx_packets = node->stats.tx_packets;
                sinfo->tx_failed = node->stats.tx_failed;
 
-               mwifiex_parse_htinfo(priv, node->stats.last_tx_htinfo,
+               mwifiex_parse_htinfo(priv, priv->tx_rate,
+                                    node->stats.last_tx_htinfo,
                                     &sinfo->txrate);
                sinfo->txrate.legacy = node->stats.last_tx_rate * 5;
 
@@ -1401,7 +1424,8 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
                         HostCmd_ACT_GEN_GET, DTIM_PERIOD_I,
                         &priv->dtim_period, true);
 
-       mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate);
+       mwifiex_parse_htinfo(priv, priv->tx_rate, priv->tx_htinfo,
+                            &sinfo->txrate);
 
        sinfo->signal_avg = priv->bcn_rssi_avg;
        sinfo->rx_bytes = priv->stats.rx_bytes;
@@ -1412,6 +1436,10 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
        /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */
        sinfo->txrate.legacy = rate * 5;
 
+       sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
+       mwifiex_parse_htinfo(priv, priv->rxpd_rate, priv->rxpd_htinfo,
+                            &sinfo->rxrate);
+
        if (priv->bss_mode == NL80211_IFTYPE_STATION) {
                sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BSS_PARAM);
                sinfo->bss_param.flags = 0;
index cce7025..cbe4493 100644 (file)
@@ -273,15 +273,13 @@ mwifiex_histogram_read(struct file *file, char __user *ubuf,
                     "total samples = %d\n",
                     atomic_read(&phist_data->num_samples));
 
-       p += sprintf(p, "rx rates (in Mbps): 0=1M   1=2M");
-       p += sprintf(p, "2=5.5M  3=11M   4=6M   5=9M  6=12M\n");
-       p += sprintf(p, "7=18M  8=24M  9=36M  10=48M  11=54M");
-       p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");
+       p += sprintf(p,
+                    "rx rates (in Mbps): 0=1M   1=2M 2=5.5M  3=11M   4=6M   5=9M  6=12M\n"
+                    "7=18M  8=24M  9=36M  10=48M  11=54M 12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");
 
        if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
-               p += sprintf(p, "44-53=MCS0-9(VHT:BW20)");
-               p += sprintf(p, "54-63=MCS0-9(VHT:BW40)");
-               p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n");
+               p += sprintf(p,
+                            "44-53=MCS0-9(VHT:BW20) 54-63=MCS0-9(VHT:BW40) 64-73=MCS0-9(VHT:BW80)\n\n");
        } else {
                p += sprintf(p, "\n");
        }
@@ -310,7 +308,7 @@ mwifiex_histogram_read(struct file *file, char __user *ubuf,
        for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) {
                value = atomic_read(&phist_data->noise_flr[i]);
                if (value)
-                       p += sprintf(p, "noise_flr[-%02ddBm] = %d\n",
+                       p += sprintf(p, "noise_flr[%02ddBm] = %d\n",
                                (int)(i-128), value);
        }
        for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) {
index 75cbd60..6845eb5 100644 (file)
@@ -363,6 +363,7 @@ static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
                                                    (const u8 *)hdr,
                                                    hdr->len + sizeof(struct ieee_types_header)))
                                break;
+                       /* fall through */
                default:
                        memcpy(gen_ie->ie_buffer + ie_len, hdr,
                               hdr->len + sizeof(struct ieee_types_header));
index 8e483b0..935778e 100644 (file)
@@ -1882,15 +1882,17 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
                                            ETH_ALEN))
                                        mwifiex_update_curr_bss_params(priv,
                                                                       bss);
-                               cfg80211_put_bss(priv->wdev.wiphy, bss);
-                       }
 
-                       if ((chan->flags & IEEE80211_CHAN_RADAR) ||
-                           (chan->flags & IEEE80211_CHAN_NO_IR)) {
-                               mwifiex_dbg(adapter, INFO,
-                                           "radar or passive channel %d\n",
-                                           channel);
-                               mwifiex_save_hidden_ssid_channels(priv, bss);
+                               if ((chan->flags & IEEE80211_CHAN_RADAR) ||
+                                   (chan->flags & IEEE80211_CHAN_NO_IR)) {
+                                       mwifiex_dbg(adapter, INFO,
+                                                   "radar or passive channel %d\n",
+                                                   channel);
+                                       mwifiex_save_hidden_ssid_channels(priv,
+                                                                         bss);
+                               }
+
+                               cfg80211_put_bss(priv->wdev.wiphy, bss);
                        }
                }
        } else {
index 00fcbda..fb28a5c 100644 (file)
@@ -152,14 +152,17 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv,
                mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len);
        }
 
-       priv->rxpd_rate = local_rx_pd->rx_rate;
-
-       priv->rxpd_htinfo = local_rx_pd->ht_info;
+       /* Only stash RX bitrate for unicast packets. */
+       if (likely(!is_multicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest))) {
+               priv->rxpd_rate = local_rx_pd->rx_rate;
+               priv->rxpd_htinfo = local_rx_pd->ht_info;
+       }
 
        if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
            GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
-               adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate,
-                                                      priv->rxpd_htinfo);
+               adj_rx_rate = mwifiex_adjust_data_rate(priv,
+                                                      local_rx_pd->rx_rate,
+                                                      local_rx_pd->ht_info);
                mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr,
                                      local_rx_pd->nf);
        }
index a83c5af..5ce85d5 100644 (file)
@@ -421,15 +421,12 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
                spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
        }
 
-       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        if (!priv->ap_11n_enabled ||
            (!mwifiex_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) &&
            (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) {
                ret = mwifiex_handle_uap_rx_forward(priv, skb);
-               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
                return ret;
        }
-       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
        /* Reorder and send to kernel */
        pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type);
index 0ccbcd7..c30d8f5 100644 (file)
@@ -1,6 +1,12 @@
 config MT76_CORE
        tristate
 
+config MT76_LEDS
+       bool
+       depends on MT76_CORE
+       depends on LEDS_CLASS=y || MT76_CORE=LEDS_CLASS
+       default y
+
 config MT76_USB
        tristate
        depends on MT76_CORE
index 9b8d748..1a45cb3 100644 (file)
@@ -14,7 +14,8 @@ CFLAGS_mt76x02_trace.o := -I$(src)
 
 mt76x02-lib-y := mt76x02_util.o mt76x02_mac.o mt76x02_mcu.o \
                 mt76x02_eeprom.o mt76x02_phy.o mt76x02_mmio.o \
-                mt76x02_txrx.o mt76x02_trace.o
+                mt76x02_txrx.o mt76x02_trace.o mt76x02_debugfs.o \
+                mt76x02_dfs.o
 
 mt76x02-usb-y := mt76x02_usb_mcu.o mt76x02_usb_core.o
 
index f7fbd70..e2ba263 100644 (file)
@@ -157,17 +157,20 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, enum mt76_txq_id qid, bool flush)
                if (entry.schedule)
                        q->swq_queued--;
 
-               if (entry.skb)
+               q->tail = (q->tail + 1) % q->ndesc;
+               q->queued--;
+
+               if (entry.skb) {
+                       spin_unlock_bh(&q->lock);
                        dev->drv->tx_complete_skb(dev, q, &entry, flush);
+                       spin_lock_bh(&q->lock);
+               }
 
                if (entry.txwi) {
                        mt76_put_txwi(dev, entry.txwi);
-                       wake = true;
+                       wake = !flush;
                }
 
-               q->tail = (q->tail + 1) % q->ndesc;
-               q->queued--;
-
                if (!flush && q->tail == last)
                        last = ioread32(&q->regs->dma_idx);
        }
@@ -258,6 +261,7 @@ int mt76_dma_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
                return -ENOMEM;
        }
 
+       skb->prev = skb->next = NULL;
        dma_sync_single_for_cpu(dev->dev, t->dma_addr, sizeof(t->txwi),
                                DMA_TO_DEVICE);
        ret = dev->drv->tx_prepare_skb(dev, &t->txwi, skb, q, wcid, sta,
index 2a699e8..7b926df 100644 (file)
@@ -285,6 +285,7 @@ mt76_alloc_device(unsigned int size, const struct ieee80211_ops *ops)
        spin_lock_init(&dev->cc_lock);
        mutex_init(&dev->mutex);
        init_waitqueue_head(&dev->tx_wait);
+       skb_queue_head_init(&dev->status_list);
 
        return dev;
 }
@@ -326,6 +327,7 @@ int mt76_register_device(struct mt76_dev *dev, bool vht,
        ieee80211_hw_set(hw, TX_FRAG_LIST);
        ieee80211_hw_set(hw, MFP_CAPABLE);
        ieee80211_hw_set(hw, AP_LINK_PS);
+       ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
 
        wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
 
@@ -345,9 +347,11 @@ int mt76_register_device(struct mt76_dev *dev, bool vht,
        mt76_check_sband(dev, NL80211_BAND_2GHZ);
        mt76_check_sband(dev, NL80211_BAND_5GHZ);
 
-       ret = mt76_led_init(dev);
-       if (ret)
-               return ret;
+       if (IS_ENABLED(CONFIG_MT76_LEDS)) {
+               ret = mt76_led_init(dev);
+               if (ret)
+                       return ret;
+       }
 
        return ieee80211_register_hw(hw);
 }
@@ -357,6 +361,7 @@ void mt76_unregister_device(struct mt76_dev *dev)
 {
        struct ieee80211_hw *hw = dev->hw;
 
+       mt76_tx_status_check(dev, NULL, true);
        ieee80211_unregister_hw(hw);
        mt76_tx_free(dev);
 }
@@ -627,3 +632,80 @@ void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q,
        mt76_rx_complete(dev, &frames, napi);
 }
 EXPORT_SYMBOL_GPL(mt76_rx_poll_complete);
+
+static int
+mt76_sta_add(struct mt76_dev *dev, struct ieee80211_vif *vif,
+            struct ieee80211_sta *sta)
+{
+       struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
+       int ret;
+       int i;
+
+       mutex_lock(&dev->mutex);
+
+       ret = dev->drv->sta_add(dev, vif, sta);
+       if (ret)
+               goto out;
+
+       for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+               struct mt76_txq *mtxq;
+
+               if (!sta->txq[i])
+                       continue;
+
+               mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
+               mtxq->wcid = wcid;
+
+               mt76_txq_init(dev, sta->txq[i]);
+       }
+
+       rcu_assign_pointer(dev->wcid[wcid->idx], wcid);
+
+out:
+       mutex_unlock(&dev->mutex);
+
+       return ret;
+}
+
+static void
+mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
+               struct ieee80211_sta *sta)
+{
+       struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
+       int idx = wcid->idx;
+       int i;
+
+       rcu_assign_pointer(dev->wcid[idx], NULL);
+       synchronize_rcu();
+
+       mutex_lock(&dev->mutex);
+
+       if (dev->drv->sta_remove)
+               dev->drv->sta_remove(dev, vif, sta);
+
+       mt76_tx_status_check(dev, wcid, true);
+       for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+               mt76_txq_remove(dev, sta->txq[i]);
+       mt76_wcid_free(dev->wcid_mask, idx);
+
+       mutex_unlock(&dev->mutex);
+}
+
+int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta,
+                  enum ieee80211_sta_state old_state,
+                  enum ieee80211_sta_state new_state)
+{
+       struct mt76_dev *dev = hw->priv;
+
+       if (old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE)
+               return mt76_sta_add(dev, vif, sta);
+
+       if (old_state == IEEE80211_STA_NONE &&
+                new_state == IEEE80211_STA_NOTEXIST)
+               mt76_sta_remove(dev, vif, sta);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt76_sta_state);
index 3bfa7f5..5cd508a 100644 (file)
@@ -135,9 +135,8 @@ struct mt76_queue {
 };
 
 struct mt76_mcu_ops {
-       struct sk_buff *(*mcu_msg_alloc)(const void *data, int len);
-       int (*mcu_send_msg)(struct mt76_dev *dev, struct sk_buff *skb,
-                           int cmd, bool wait_resp);
+       int (*mcu_send_msg)(struct mt76_dev *dev, int cmd, const void *data,
+                           int len, bool wait_resp);
        int (*mcu_wr_rp)(struct mt76_dev *dev, u32 base,
                         const struct mt76_reg_pair *rp, int len);
        int (*mcu_rd_rp)(struct mt76_dev *dev, u32 base,
@@ -195,6 +194,8 @@ struct mt76_wcid {
        u8 tx_rate_nss;
        s8 max_txpwr_adj;
        bool sw_iv;
+
+       u8 packet_id;
 };
 
 struct mt76_txq {
@@ -233,6 +234,22 @@ struct mt76_rx_tid {
        struct sk_buff *reorder_buf[];
 };
 
+#define MT_TX_CB_DMA_DONE              BIT(0)
+#define MT_TX_CB_TXS_DONE              BIT(1)
+#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_TX_STATUS_SKB_TIMEOUT       HZ
+
+struct mt76_tx_cb {
+       unsigned long jiffies;
+       u8 wcid;
+       u8 pktid;
+       u8 flags;
+};
+
 enum {
        MT76_STATE_INITIALIZED,
        MT76_STATE_RUNNING,
@@ -271,6 +288,12 @@ struct mt76_driver_ops {
 
        void (*sta_ps)(struct mt76_dev *dev, struct ieee80211_sta *sta,
                       bool ps);
+
+       int (*sta_add)(struct mt76_dev *dev, struct ieee80211_vif *vif,
+                      struct ieee80211_sta *sta);
+
+       void (*sta_remove)(struct mt76_dev *dev, struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta);
 };
 
 struct mt76_channel_state {
@@ -400,6 +423,7 @@ struct mt76_dev {
        const struct mt76_queue_ops *queue_ops;
 
        wait_queue_head_t tx_wait;
+       struct sk_buff_head status_list;
 
        unsigned long wcid_mask[MT76_N_WCIDS / BITS_PER_LONG];
 
@@ -484,7 +508,6 @@ struct mt76_rx_status {
 #define mt76_wr_rp(dev, ...)   (dev)->mt76.bus->wr_rp(&((dev)->mt76), __VA_ARGS__)
 #define mt76_rd_rp(dev, ...)   (dev)->mt76.bus->rd_rp(&((dev)->mt76), __VA_ARGS__)
 
-#define mt76_mcu_msg_alloc(dev, ...)   (dev)->mt76.mcu_ops->mcu_msg_alloc(__VA_ARGS__)
 #define mt76_mcu_send_msg(dev, ...)    (dev)->mt76.mcu_ops->mcu_send_msg(&((dev)->mt76), __VA_ARGS__)
 
 #define mt76_set(dev, offset, val)     mt76_rmw(dev, offset, 0, val)
@@ -594,6 +617,13 @@ wcid_to_sta(struct mt76_wcid *wcid)
        return container_of(ptr, struct ieee80211_sta, drv_priv);
 }
 
+static inline struct mt76_tx_cb *mt76_tx_skb_cb(struct sk_buff *skb)
+{
+       BUILD_BUG_ON(sizeof(struct mt76_tx_cb) >
+                    sizeof(IEEE80211_SKB_CB(skb)->status.status_driver_data));
+       return ((void *) IEEE80211_SKB_CB(skb)->status.status_driver_data);
+}
+
 int mt76_dma_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
                          struct sk_buff *skb, struct mt76_wcid *wcid,
                          struct ieee80211_sta *sta);
@@ -625,6 +655,26 @@ void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid);
 void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
                         struct ieee80211_key_conf *key);
 
+void mt76_tx_status_lock(struct mt76_dev *dev, struct sk_buff_head *list)
+                        __acquires(&dev->status_list.lock);
+void mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
+                          __releases(&dev->status_list.lock);
+
+int mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid,
+                          struct sk_buff *skb);
+struct sk_buff *mt76_tx_status_skb_get(struct mt76_dev *dev,
+                                      struct mt76_wcid *wcid, int pktid,
+                                      struct sk_buff_head *list);
+void mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb,
+                            struct sk_buff_head *list);
+void mt76_tx_complete_skb(struct mt76_dev *dev, struct sk_buff *skb);
+void mt76_tx_status_check(struct mt76_dev *dev, struct mt76_wcid *wcid,
+                         bool flush);
+int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta,
+                  enum ieee80211_sta_state old_state,
+                  enum ieee80211_sta_state new_state);
+
 struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb);
 
 /* internal */
@@ -668,8 +718,6 @@ int mt76u_vendor_request(struct mt76_dev *dev, u8 req,
                         void *buf, size_t len);
 void mt76u_single_wr(struct mt76_dev *dev, const u8 req,
                     const u16 offset, const u32 val);
-u32 mt76u_rr(struct mt76_dev *dev, u32 addr);
-void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val);
 int mt76u_init(struct mt76_dev *dev, struct usb_interface *intf);
 void mt76u_deinit(struct mt76_dev *dev);
 int mt76u_buf_alloc(struct mt76_dev *dev, struct mt76u_buf *buf,
index 2067297..aa22ba9 100644 (file)
@@ -2,11 +2,9 @@ obj-$(CONFIG_MT76x0U) += mt76x0u.o
 obj-$(CONFIG_MT76x0E) += mt76x0e.o
 obj-$(CONFIG_MT76x0_COMMON) += mt76x0-common.o
 
-mt76x0-common-y := \
-       init.o main.o trace.o eeprom.o phy.o \
-       mac.o debugfs.o
+mt76x0-common-y := init.o main.o eeprom.o phy.o
+
 mt76x0u-y := usb.o usb_mcu.o
 mt76x0e-y := pci.o pci_mcu.o
 
 # ccflags-y := -DDEBUG
-CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x0/debugfs.c
deleted file mode 100644 (file)
index 3224e5b..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
- * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation
- *
- * 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 <linux/debugfs.h>
-
-#include "mt76x0.h"
-#include "eeprom.h"
-
-static int
-mt76x0_ampdu_stat_read(struct seq_file *file, void *data)
-{
-       struct mt76x02_dev *dev = file->private;
-       int i, j;
-
-#define stat_printf(grp, off, name)                                    \
-       seq_printf(file, #name ":\t%llu\n", dev->stats.grp[off])
-
-       stat_printf(rx_stat, 0, rx_crc_err);
-       stat_printf(rx_stat, 1, rx_phy_err);
-       stat_printf(rx_stat, 2, rx_false_cca);
-       stat_printf(rx_stat, 3, rx_plcp_err);
-       stat_printf(rx_stat, 4, rx_fifo_overflow);
-       stat_printf(rx_stat, 5, rx_duplicate);
-
-       stat_printf(tx_stat, 0, tx_fail_cnt);
-       stat_printf(tx_stat, 1, tx_bcn_cnt);
-       stat_printf(tx_stat, 2, tx_success);
-       stat_printf(tx_stat, 3, tx_retransmit);
-       stat_printf(tx_stat, 4, tx_zero_len);
-       stat_printf(tx_stat, 5, tx_underflow);
-
-       stat_printf(aggr_stat, 0, non_aggr_tx);
-       stat_printf(aggr_stat, 1, aggr_tx);
-
-       stat_printf(zero_len_del, 0, tx_zero_len_del);
-       stat_printf(zero_len_del, 1, rx_zero_len_del);
-#undef stat_printf
-
-       seq_puts(file, "Aggregations stats:\n");
-       for (i = 0; i < 4; i++) {
-               for (j = 0; j < 8; j++)
-                       seq_printf(file, "%08llx ",
-                                  dev->stats.aggr_n[i * 8 + j]);
-               seq_putc(file, '\n');
-       }
-
-       seq_printf(file, "recent average AMPDU len: %d\n",
-                  atomic_read(&dev->avg_ampdu_len));
-
-       return 0;
-}
-
-static int
-mt76x0_ampdu_stat_open(struct inode *inode, struct file *f)
-{
-       return single_open(f, mt76x0_ampdu_stat_read, inode->i_private);
-}
-
-static const struct file_operations fops_ampdu_stat = {
-       .open = mt76x0_ampdu_stat_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
-
-void mt76x0_init_debugfs(struct mt76x02_dev *dev)
-{
-       struct dentry *dir;
-
-       dir = mt76_register_debugfs(&dev->mt76);
-       if (!dir)
-               return;
-
-       debugfs_create_file("ampdu_stat", S_IRUSR, dir, dev, &fops_ampdu_stat);
-}
index ab4fd6e..497e762 100644 (file)
@@ -135,9 +135,6 @@ static s8 mt76x0_get_delta(struct mt76x02_dev *dev)
        struct cfg80211_chan_def *chandef = &dev->mt76.chandef;
        u8 val;
 
-       if (mt76x0_tssi_enabled(dev))
-               return 0;
-
        if (chandef->width == NL80211_CHAN_WIDTH_80) {
                val = mt76x02_eeprom_get(dev, MT_EE_5G_TARGET_POWER) >> 8;
        } else if (chandef->width == NL80211_CHAN_WIDTH_40) {
@@ -160,8 +157,8 @@ void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev)
        struct ieee80211_channel *chan = dev->mt76.chandef.chan;
        bool is_2ghz = chan->band == NL80211_BAND_2GHZ;
        struct mt76_rate_power *t = &dev->mt76.rate_power;
-       s8 delta = mt76x0_get_delta(dev);
        u16 val, addr;
+       s8 delta;
 
        memset(t, 0, sizeof(*t));
 
@@ -211,6 +208,7 @@ void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev)
        t->vht[7] = s6_to_s8(val);
        t->vht[8] = s6_to_s8(val >> 8);
 
+       delta = mt76x0_tssi_enabled(dev) ? 0 : mt76x0_get_delta(dev);
        mt76x02_add_rate_power_offset(t, delta);
 }
 
@@ -233,6 +231,20 @@ void mt76x0_get_power_info(struct mt76x02_dev *dev, u8 *info)
        u16 data;
        int i;
 
+       if (mt76x0_tssi_enabled(dev)) {
+               s8 target_power;
+
+               if (chan->band == NL80211_BAND_5GHZ)
+                       data = mt76x02_eeprom_get(dev, MT_EE_5G_TARGET_POWER);
+               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;
+
+               return;
+       }
+
        for (i = 0; i < ARRAY_SIZE(chan_map); i++) {
                if (chan_map[i].chan <= chan->hw_value) {
                        offset = chan_map[i].offset;
@@ -340,8 +352,6 @@ int mt76x0_eeprom_init(struct mt76x02_dev *dev)
        mt76x0_set_freq_offset(dev);
        mt76x0_set_temp_offset(dev);
 
-       dev->mt76.chainmask = 0x0101;
-
        return 0;
 }
 
index 4a94088..87b575f 100644 (file)
@@ -16,7 +16,6 @@
 
 #include "mt76x0.h"
 #include "eeprom.h"
-#include "trace.h"
 #include "mcu.h"
 #include "initvals.h"
 
@@ -113,7 +112,7 @@ static int mt76x0_init_bbp(struct mt76x02_dev *dev)
 {
        int ret, i;
 
-       ret = mt76x0_wait_bbp_ready(dev);
+       ret = mt76x0_phy_wait_bbp_ready(dev);
        if (ret)
                return ret;
 
@@ -134,80 +133,28 @@ static int mt76x0_init_bbp(struct mt76x02_dev *dev)
 
 static void mt76x0_init_mac_registers(struct mt76x02_dev *dev)
 {
-       u32 reg;
-
        RANDOM_WRITE(dev, common_mac_reg_table);
 
-       mt76x02_set_beacon_offsets(dev);
-
        /* Enable PBF and MAC clock SYS_CTRL[11:10] = 0x3 */
        RANDOM_WRITE(dev, mt76x0_mac_reg_table);
 
        /* Release BBP and MAC reset MAC_SYS_CTRL[1:0] = 0x0 */
-       reg = mt76_rr(dev, MT_MAC_SYS_CTRL);
-       reg &= ~0x3;
-       mt76_wr(dev, MT_MAC_SYS_CTRL, reg);
+       mt76_clear(dev, MT_MAC_SYS_CTRL, 0x3);
 
        /* Set 0x141C[15:12]=0xF */
-       reg = mt76_rr(dev, MT_EXT_CCA_CFG);
-       reg |= 0x0000F000;
-       mt76_wr(dev, MT_EXT_CCA_CFG, reg);
+       mt76_set(dev, MT_EXT_CCA_CFG, 0xf000);
 
        mt76_clear(dev, MT_FCE_L2_STUFF, MT_FCE_L2_STUFF_WR_MPDU_LEN_EN);
 
        /*
-               TxRing 9 is for Mgmt frame.
-               TxRing 8 is for In-band command frame.
-               WMM_RG0_TXQMA: This register setting is for FCE to define the rule of TxRing 9.
-               WMM_RG1_TXQMA: This register setting is for FCE to define the rule of TxRing 8.
-       */
-       reg = mt76_rr(dev, MT_WMM_CTRL);
-       reg &= ~0x000003FF;
-       reg |= 0x00000201;
-       mt76_wr(dev, MT_WMM_CTRL, reg);
-}
-
-static int mt76x0_init_wcid_mem(struct mt76x02_dev *dev)
-{
-       u32 *vals;
-       int i;
-
-       vals = kmalloc(sizeof(*vals) * MT76_N_WCIDS * 2, GFP_KERNEL);
-       if (!vals)
-               return -ENOMEM;
-
-       for (i = 0; i < MT76_N_WCIDS; i++)  {
-               vals[i * 2] = 0xffffffff;
-               vals[i * 2 + 1] = 0x00ffffff;
-       }
-
-       mt76_wr_copy(dev, MT_WCID_ADDR_BASE, vals, MT76_N_WCIDS * 2);
-       kfree(vals);
-       return 0;
-}
-
-static void mt76x0_init_key_mem(struct mt76x02_dev *dev)
-{
-       u32 vals[4] = {};
-
-       mt76_wr_copy(dev, MT_SKEY_MODE_BASE_0, vals, ARRAY_SIZE(vals));
-}
-
-static int mt76x0_init_wcid_attr_mem(struct mt76x02_dev *dev)
-{
-       u32 *vals;
-       int i;
-
-       vals = kmalloc(sizeof(*vals) * MT76_N_WCIDS * 2, GFP_KERNEL);
-       if (!vals)
-               return -ENOMEM;
-
-       for (i = 0; i < MT76_N_WCIDS * 2; i++)
-               vals[i] = 1;
-
-       mt76_wr_copy(dev, MT_WCID_ATTR_BASE, vals, MT76_N_WCIDS * 2);
-       kfree(vals);
-       return 0;
+        * tx_ring 9 is for mgmt frame
+        * tx_ring 8 is for in-band command frame.
+        * WMM_RG0_TXQMA: this register setting is for FCE to
+        *                define the rule of tx_ring 9
+        * WMM_RG1_TXQMA: this register setting is for FCE to
+        *                define the rule of tx_ring 8
+        */
+       mt76_rmw(dev, MT_WMM_CTRL, 0x3ff, 0x201);
 }
 
 static void mt76x0_reset_counters(struct mt76x02_dev *dev)
@@ -270,7 +217,7 @@ EXPORT_SYMBOL_GPL(mt76x0_mac_stop);
 
 int mt76x0_init_hardware(struct mt76x02_dev *dev)
 {
-       int ret;
+       int ret, i, k;
 
        if (!mt76x02_wait_for_wpdma(&dev->mt76, 1000))
                return -EIO;
@@ -280,7 +227,7 @@ int mt76x0_init_hardware(struct mt76x02_dev *dev)
                return -ETIMEDOUT;
 
        mt76x0_reset_csr_bbp(dev);
-       ret = mt76x02_mcu_function_select(dev, Q_SELECT, 1, false);
+       ret = mt76x02_mcu_function_select(dev, Q_SELECT, 1);
        if (ret)
                return ret;
 
@@ -295,20 +242,12 @@ int mt76x0_init_hardware(struct mt76x02_dev *dev)
 
        dev->mt76.rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG);
 
-       ret = mt76x0_init_wcid_mem(dev);
-       if (ret)
-               return ret;
+       for (i = 0; i < 16; i++)
+               for (k = 0; k < 4; k++)
+                       mt76x02_mac_shared_key_setup(dev, i, k, NULL);
 
-       mt76x0_init_key_mem(dev);
-
-       ret = mt76x0_init_wcid_attr_mem(dev);
-       if (ret)
-               return ret;
-
-       mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN |
-                                            MT_BEACON_TIME_CFG_SYNC_MODE |
-                                            MT_BEACON_TIME_CFG_TBTT_EN |
-                                            MT_BEACON_TIME_CFG_BEACON_TX));
+       for (i = 0; i < 256; i++)
+               mt76x02_mac_wcid_setup(dev, i, 0, NULL);
 
        mt76x0_reset_counters(dev);
 
@@ -317,6 +256,7 @@ int mt76x0_init_hardware(struct mt76x02_dev *dev)
                return ret;
 
        mt76x0_phy_init(dev);
+       mt76x02_init_beacon_config(dev);
 
        return 0;
 }
@@ -339,7 +279,6 @@ mt76x0_alloc_device(struct device *pdev,
 
        dev = container_of(mdev, struct mt76x02_dev, mt76);
        mutex_init(&dev->phy_mutex);
-       atomic_set(&dev->avg_ampdu_len, 1);
 
        return dev;
 }
@@ -347,49 +286,21 @@ EXPORT_SYMBOL_GPL(mt76x0_alloc_device);
 
 int mt76x0_register_device(struct mt76x02_dev *dev)
 {
-       struct mt76_dev *mdev = &dev->mt76;
-       struct ieee80211_hw *hw = mdev->hw;
-       struct wiphy *wiphy = hw->wiphy;
        int ret;
 
-       /* Reserve WCID 0 for mcast - thanks to this APs WCID will go to
-        * entry no. 1 like it does in the vendor driver.
-        */
-       mdev->wcid_mask[0] |= 1;
-
-       /* init fake wcid for monitor interfaces */
-       mdev->global_wcid.idx = 0xff;
-       mdev->global_wcid.hw_key_idx = -1;
-
-       /* init antenna configuration */
-       mdev->antenna_mask = 1;
-
-       hw->queues = 4;
-       hw->max_rates = 1;
-       hw->max_report_rates = 7;
-       hw->max_rate_tries = 1;
-       hw->extra_tx_headroom = 2;
-       if (mt76_is_usb(dev))
-               hw->extra_tx_headroom += sizeof(struct mt76x02_txwi) +
-                                        MT_DMA_HDR_LEN;
-
-       hw->sta_data_size = sizeof(struct mt76x02_sta);
-       hw->vif_data_size = sizeof(struct mt76x02_vif);
-
-       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
-
-       INIT_DELAYED_WORK(&dev->mac_work, mt76x0_mac_work);
+       mt76x02_init_device(dev);
+       mt76x02_config_mac_addr_list(dev);
 
-       ret = mt76_register_device(mdev, true, mt76x02_rates,
+       ret = mt76_register_device(&dev->mt76, true, mt76x02_rates,
                                   ARRAY_SIZE(mt76x02_rates));
        if (ret)
                return ret;
 
        /* overwrite unsupported features */
-       if (mdev->cap.has_5ghz)
+       if (dev->mt76.cap.has_5ghz)
                mt76x0_vht_cap_mask(&dev->mt76.sband_5g.sband);
 
-       mt76x0_init_debugfs(dev);
+       mt76x02_init_debugfs(dev);
 
        return 0;
 }
index 236dce6..a165792 100644 (file)
@@ -37,14 +37,14 @@ static const struct mt76_reg_pair common_mac_reg_table[] = {
        { MT_PBF_RX_MAX_PCNT,           0x0000fe9f },
        { MT_TX_RETRY_CFG,              0x47d01f0f },
        { MT_AUTO_RSP_CFG,              0x00000013 },
-       { MT_CCK_PROT_CFG,              0x05740003 },
-       { MT_OFDM_PROT_CFG,             0x05740003 },
+       { MT_CCK_PROT_CFG,              0x07f40003 },
+       { MT_OFDM_PROT_CFG,             0x07f42004 },
        { MT_PBF_CFG,                   0x00f40006 },
        { MT_WPDMA_GLO_CFG,             0x00000030 },
-       { MT_GF20_PROT_CFG,             0x01744004 },
-       { MT_GF40_PROT_CFG,             0x03f44084 },
-       { MT_MM20_PROT_CFG,             0x01744004 },
-       { MT_MM40_PROT_CFG,             0x03f54084 },
+       { MT_GF20_PROT_CFG,             0x01742004 },
+       { MT_GF40_PROT_CFG,             0x03f42084 },
+       { MT_MM20_PROT_CFG,             0x01742004 },
+       { MT_MM40_PROT_CFG,             0x03f42084 },
        { MT_TXOP_CTRL_CFG,             0x0000583f },
        { MT_TX_RTS_CFG,                0x00092b20 },
        { MT_EXP_ACK_TIME,              0x002400ca },
@@ -85,6 +85,9 @@ static const struct mt76_reg_pair mt76x0_mac_reg_table[] = {
        { MT_HT_CTRL_CFG,               0x000001FF },
        { MT_TXOP_HLDR_ET,              0x00000000 },
        { MT_PN_PAD_MODE,               0x00000003 },
+       { MT_TX_PROT_CFG6,              0xe3f42004 },
+       { MT_TX_PROT_CFG7,              0xe3f42084 },
+       { MT_TX_PROT_CFG8,              0xe3f42104 },
 };
 
 static const struct mt76_reg_pair mt76x0_bbp_init_tab[] = {
index 95d43ef..56c6fa7 100644 (file)
 #ifndef __MT76X0U_PHY_INITVALS_H
 #define __MT76X0U_PHY_INITVALS_H
 
-#define RF_REG_PAIR(bank, reg, value)                          \
-       { (bank) << 16 | (reg), value }
-
-
 static const struct mt76_reg_pair mt76x0_rf_central_tab[] = {
-/*
-       Bank 0 - For central blocks: BG, PLL, XTAL, LO, ADC/DAC
-*/
-       { MT_RF(0, 1), 0x01},
-       { MT_RF(0, 2), 0x11},
-
-       /*
-               R3 ~ R7: VCO Cal.
-       */
-       { MT_RF(0, 3), 0x73}, /* VCO Freq Cal - No Bypass, VCO Amp Cal - No Bypass */
-       { MT_RF(0, 4), 0x30}, /* R4 b<7>=1, VCO cal */
-       { MT_RF(0, 5), 0x00},
-       { MT_RF(0, 6), 0x41}, /* Set the open loop amplitude to middle since bypassing amplitude calibration */
-       { MT_RF(0, 7), 0x00},
-
-       /*
-               XO
-       */
-       { MT_RF(0,  8), 0x00},
-       { MT_RF(0,  9), 0x00},
-       { MT_RF(0, 10), 0x0C},
-       { MT_RF(0, 11), 0x00},
-       { MT_RF(0, 12), 0x00},
-
-       /*
-               BG
-       */
-       { MT_RF(0, 13), 0x00},
-       { MT_RF(0, 14), 0x00},
-       { MT_RF(0, 15), 0x00},
-
-       /*
-               LDO
-       */
-       { MT_RF(0, 19), 0x20},
-       /*
-               XO
-       */
-       { MT_RF(0, 20), 0x22},
-       { MT_RF(0, 21), 0x12},
-       { MT_RF(0, 23), 0x00},
-       { MT_RF(0, 24), 0x33}, /* See band selection for R24<1:0> */
-       { MT_RF(0, 25), 0x00},
-
-       /*
-               PLL, See Freq Selection
-       */
-       { MT_RF(0, 26), 0x00},
-       { MT_RF(0, 27), 0x00},
-       { MT_RF(0, 28), 0x00},
-       { MT_RF(0, 29), 0x00},
-       { MT_RF(0, 30), 0x00},
-       { MT_RF(0, 31), 0x00},
-       { MT_RF(0, 32), 0x00},
-       { MT_RF(0, 33), 0x00},
-       { MT_RF(0, 34), 0x00},
-       { MT_RF(0, 35), 0x00},
-       { MT_RF(0, 36), 0x00},
-       { MT_RF(0, 37), 0x00},
-
-       /*
-               LO Buffer
-       */
-       { MT_RF(0, 38), 0x2F},
-
-       /*
-               Test Ports
-       */
-       { MT_RF(0, 64), 0x00},
-       { MT_RF(0, 65), 0x80},
-       { MT_RF(0, 66), 0x01},
-       { MT_RF(0, 67), 0x04},
-
-       /*
-               ADC/DAC
-       */
-       { MT_RF(0, 68), 0x00},
-       { MT_RF(0, 69), 0x08},
-       { MT_RF(0, 70), 0x08},
-       { MT_RF(0, 71), 0x40},
-       { MT_RF(0, 72), 0xD0},
-       { MT_RF(0, 73), 0x93},
+       { MT_RF(0,  1), 0x01 },
+       { MT_RF(0,  2), 0x11 },
+       /* R3 ~ R7: VCO Cal */
+       { MT_RF(0,  3), 0x73 }, /* VCO Freq Cal */
+       { MT_RF(0,  4), 0x30 }, /* R4 b<7>=1, VCO cal */
+       { MT_RF(0,  5), 0x00 },
+       { MT_RF(0,  6), 0x41 },
+       { MT_RF(0,  7), 0x00 },
+       { MT_RF(0,  8), 0x00 },
+       { MT_RF(0,  9), 0x00 },
+       { MT_RF(0, 10), 0x0C },
+       { MT_RF(0, 11), 0x00 },
+       { MT_RF(0, 12), 0x00 },
+       /* BG */
+       { MT_RF(0, 13), 0x00 },
+       { MT_RF(0, 14), 0x00 },
+       { MT_RF(0, 15), 0x00 },
+       /* LDO */
+       { MT_RF(0, 19), 0x20 },
+       { MT_RF(0, 20), 0x22 },
+       { MT_RF(0, 21), 0x12 },
+       { MT_RF(0, 23), 0x00 },
+       { MT_RF(0, 24), 0x33 },
+       { MT_RF(0, 25), 0x00 },
+       /* PLL */
+       { MT_RF(0, 26), 0x00 },
+       { MT_RF(0, 27), 0x00 },
+       { MT_RF(0, 28), 0x00 },
+       { MT_RF(0, 29), 0x00 },
+       { MT_RF(0, 30), 0x00 },
+       { MT_RF(0, 31), 0x00 },
+       { MT_RF(0, 32), 0x00 },
+       { MT_RF(0, 33), 0x00 },
+       { MT_RF(0, 34), 0x00 },
+       { MT_RF(0, 35), 0x00 },
+       { MT_RF(0, 36), 0x00 },
+       { MT_RF(0, 37), 0x00 },
+       /* LO Buffer */
+       { MT_RF(0, 38), 0x2F },
+       /* Test Ports */
+       { MT_RF(0, 64), 0x00 },
+       { MT_RF(0, 65), 0x80 },
+       { MT_RF(0, 66), 0x01 },
+       { MT_RF(0, 67), 0x04 },
+       /* ADC-DAC */
+       { MT_RF(0, 68), 0x00 },
+       { MT_RF(0, 69), 0x08 },
+       { MT_RF(0, 70), 0x08 },
+       { MT_RF(0, 71), 0x40 },
+       { MT_RF(0, 72), 0xD0 },
+       { MT_RF(0, 73), 0x93 },
 };
 
 static const struct mt76_reg_pair mt76x0_rf_2g_channel_0_tab[] = {
-/*
-       Bank 5 - Channel 0 2G RF registers
-*/
-       /*
-               RX logic operation
-       */
-       /* RF_R00 Change in SelectBand6590 */
-
-       { MT_RF(5, 2), 0x0C}, /* 5G+2G (MT7610U) */
-       { MT_RF(5, 3), 0x00},
-
-       /*
-               TX logic operation
-       */
-       { MT_RF(5, 4), 0x00},
-       { MT_RF(5, 5), 0x84},
-       { MT_RF(5, 6), 0x02},
-
-       /*
-               LDO
-       */
-       { MT_RF(5, 7), 0x00},
-       { MT_RF(5, 8), 0x00},
-       { MT_RF(5, 9), 0x00},
-
-       /*
-               RX
-       */
-       { MT_RF(5, 10), 0x51},
-       { MT_RF(5, 11), 0x22},
-       { MT_RF(5, 12), 0x22},
-       { MT_RF(5, 13), 0x0F},
-       { MT_RF(5, 14), 0x47}, /* Increase mixer current for more gain */
-       { MT_RF(5, 15), 0x25},
-       { MT_RF(5, 16), 0xC7}, /* Tune LNA2 tank */
-       { MT_RF(5, 17), 0x00},
-       { MT_RF(5, 18), 0x00},
-       { MT_RF(5, 19), 0x30}, /* Improve max Pin */
-       { MT_RF(5, 20), 0x33},
-       { MT_RF(5, 21), 0x02},
-       { MT_RF(5, 22), 0x32}, /* Tune LNA1 tank */
-       { MT_RF(5, 23), 0x00},
-       { MT_RF(5, 24), 0x25},
-       { MT_RF(5, 26), 0x00},
-       { MT_RF(5, 27), 0x12},
-       { MT_RF(5, 28), 0x0F},
-       { MT_RF(5, 29), 0x00},
-
-       /*
-               LOGEN
-       */
-       { MT_RF(5, 30), 0x51}, /* Tune LOGEN tank */
-       { MT_RF(5, 31), 0x35},
-       { MT_RF(5, 32), 0x31},
-       { MT_RF(5, 33), 0x31},
-       { MT_RF(5, 34), 0x34},
-       { MT_RF(5, 35), 0x03},
-       { MT_RF(5, 36), 0x00},
-
-       /*
-               TX
-       */
-       { MT_RF(5, 37), 0xDD}, /* Improve 3.2GHz spur */
-       { MT_RF(5, 38), 0xB3},
-       { MT_RF(5, 39), 0x33},
-       { MT_RF(5, 40), 0xB1},
-       { MT_RF(5, 41), 0x71},
-       { MT_RF(5, 42), 0xF2},
-       { MT_RF(5, 43), 0x47},
-       { MT_RF(5, 44), 0x77},
-       { MT_RF(5, 45), 0x0E},
-       { MT_RF(5, 46), 0x10},
-       { MT_RF(5, 47), 0x00},
-       { MT_RF(5, 48), 0x53},
-       { MT_RF(5, 49), 0x03},
-       { MT_RF(5, 50), 0xEF},
-       { MT_RF(5, 51), 0xC7},
-       { MT_RF(5, 52), 0x62},
-       { MT_RF(5, 53), 0x62},
-       { MT_RF(5, 54), 0x00},
-       { MT_RF(5, 55), 0x00},
-       { MT_RF(5, 56), 0x0F},
-       { MT_RF(5, 57), 0x0F},
-       { MT_RF(5, 58), 0x16},
-       { MT_RF(5, 59), 0x16},
-       { MT_RF(5, 60), 0x10},
-       { MT_RF(5, 61), 0x10},
-       { MT_RF(5, 62), 0xD0},
-       { MT_RF(5, 63), 0x6C},
-       { MT_RF(5, 64), 0x58},
-       { MT_RF(5, 65), 0x58},
-       { MT_RF(5, 66), 0xF2},
-       { MT_RF(5, 67), 0xE8},
-       { MT_RF(5, 68), 0xF0},
-       { MT_RF(5, 69), 0xF0},
-       { MT_RF(5, 127), 0x04},
+       /* RX logic operation */
+       { MT_RF(5,  2), 0x0C }, /* 5G+2G */
+       { MT_RF(5,  3), 0x00 },
+       /* TX logic operation */
+       { MT_RF(5,  4), 0x00 },
+       { MT_RF(5,  5), 0x84 },
+       { MT_RF(5,  6), 0x02 },
+       /* LDO */
+       { MT_RF(5,  7), 0x00 },
+       { MT_RF(5,  8), 0x00 },
+       { MT_RF(5,  9), 0x00 },
+       /* RX */
+       { MT_RF(5, 10), 0x51 },
+       { MT_RF(5, 11), 0x22 },
+       { MT_RF(5, 12), 0x22 },
+       { MT_RF(5, 13), 0x0F },
+       { MT_RF(5, 14), 0x47 },
+       { MT_RF(5, 15), 0x25 },
+       { MT_RF(5, 16), 0xC7 },
+       { MT_RF(5, 17), 0x00 },
+       { MT_RF(5, 18), 0x00 },
+       { MT_RF(5, 19), 0x30 },
+       { MT_RF(5, 20), 0x33 },
+       { MT_RF(5, 21), 0x02 },
+       { MT_RF(5, 22), 0x32 },
+       { MT_RF(5, 23), 0x00 },
+       { MT_RF(5, 24), 0x25 },
+       { MT_RF(5, 26), 0x00 },
+       { MT_RF(5, 27), 0x12 },
+       { MT_RF(5, 28), 0x0F },
+       { MT_RF(5, 29), 0x00 },
+       /* LOGEN */
+       { MT_RF(5, 30), 0x51 },
+       { MT_RF(5, 31), 0x35 },
+       { MT_RF(5, 32), 0x31 },
+       { MT_RF(5, 33), 0x31 },
+       { MT_RF(5, 34), 0x34 },
+       { MT_RF(5, 35), 0x03 },
+       { MT_RF(5, 36), 0x00 },
+       /* TX */
+       { MT_RF(5, 37), 0xDD },
+       { MT_RF(5, 38), 0xB3 },
+       { MT_RF(5, 39), 0x33 },
+       { MT_RF(5, 40), 0xB1 },
+       { MT_RF(5, 41), 0x71 },
+       { MT_RF(5, 42), 0xF2 },
+       { MT_RF(5, 43), 0x47 },
+       { MT_RF(5, 44), 0x77 },
+       { MT_RF(5, 45), 0x0E },
+       { MT_RF(5, 46), 0x10 },
+       { MT_RF(5, 47), 0x00 },
+       { MT_RF(5, 48), 0x53 },
+       { MT_RF(5, 49), 0x03 },
+       { MT_RF(5, 50), 0xEF },
+       { MT_RF(5, 51), 0xC7 },
+       { MT_RF(5, 52), 0x62 },
+       { MT_RF(5, 53), 0x62 },
+       { MT_RF(5, 54), 0x00 },
+       { MT_RF(5, 55), 0x00 },
+       { MT_RF(5, 56), 0x0F },
+       { MT_RF(5, 57), 0x0F },
+       { MT_RF(5, 58), 0x16 },
+       { MT_RF(5, 59), 0x16 },
+       { MT_RF(5, 60), 0x10 },
+       { MT_RF(5, 61), 0x10 },
+       { MT_RF(5, 62), 0xD0 },
+       { MT_RF(5, 63), 0x6C },
+       { MT_RF(5, 64), 0x58 },
+       { MT_RF(5, 65), 0x58 },
+       { MT_RF(5, 66), 0xF2 },
+       { MT_RF(5, 67), 0xE8 },
+       { MT_RF(5, 68), 0xF0 },
+       { MT_RF(5, 69), 0xF0 },
+       { MT_RF(5, 127), 0x04 },
 };
 
 static const struct mt76_reg_pair mt76x0_rf_5g_channel_0_tab[] = {
-/*
-       Bank 6 - Channel 0 5G RF registers
-*/
-       /*
-               RX logic operation
-       */
-       /* RF_R00 Change in SelectBandmt76x0 */
-
-       { MT_RF(6, 2), 0x0C},
-       { MT_RF(6, 3), 0x00},
-
-       /*
-               TX logic operation
-       */
-       { MT_RF(6, 4), 0x00},
-       { MT_RF(6, 5), 0x84},
-       { MT_RF(6, 6), 0x02},
-
-       /*
-               LDO
-       */
-       { MT_RF(6, 7), 0x00},
-       { MT_RF(6, 8), 0x00},
-       { MT_RF(6, 9), 0x00},
-
-       /*
-               RX
-       */
-       { MT_RF(6, 10), 0x00},
-       { MT_RF(6, 11), 0x01},
-
-       { MT_RF(6, 13), 0x23},
-       { MT_RF(6, 14), 0x00},
-       { MT_RF(6, 15), 0x04},
-       { MT_RF(6, 16), 0x22},
-
-       { MT_RF(6, 18), 0x08},
-       { MT_RF(6, 19), 0x00},
-       { MT_RF(6, 20), 0x00},
-       { MT_RF(6, 21), 0x00},
-       { MT_RF(6, 22), 0xFB},
-
-       /*
-               LOGEN5G
-       */
-       { MT_RF(6, 25), 0x76},
-       { MT_RF(6, 26), 0x24},
-       { MT_RF(6, 27), 0x04},
-       { MT_RF(6, 28), 0x00},
-       { MT_RF(6, 29), 0x00},
-
-       /*
-               TX
-       */
-       { MT_RF(6, 37), 0xBB},
-       { MT_RF(6, 38), 0xB3},
-
-       { MT_RF(6, 40), 0x33},
-       { MT_RF(6, 41), 0x33},
-
-       { MT_RF(6, 43), 0x03},
-       { MT_RF(6, 44), 0xB3},
-
-       { MT_RF(6, 46), 0x17},
-       { MT_RF(6, 47), 0x0E},
-       { MT_RF(6, 48), 0x10},
-       { MT_RF(6, 49), 0x07},
-
-       { MT_RF(6, 62), 0x00},
-       { MT_RF(6, 63), 0x00},
-       { MT_RF(6, 64), 0xF1},
-       { MT_RF(6, 65), 0x0F},
+       /* RX logic operation */
+       { MT_RF(6, 2), 0x0C },
+       { MT_RF(6, 3), 0x00 },
+       /* TX logic operation */
+       { MT_RF(6, 4), 0x00 },
+       { MT_RF(6, 5), 0x84 },
+       { MT_RF(6, 6), 0x02 },
+       /* LDO */
+       { MT_RF(6, 7), 0x00 },
+       { MT_RF(6, 8), 0x00 },
+       { MT_RF(6, 9), 0x00 },
+       /* RX */
+       { MT_RF(6, 10), 0x00 },
+       { MT_RF(6, 11), 0x01 },
+       { MT_RF(6, 13), 0x23 },
+       { MT_RF(6, 14), 0x00 },
+       { MT_RF(6, 15), 0x04 },
+       { MT_RF(6, 16), 0x22 },
+       { MT_RF(6, 18), 0x08 },
+       { MT_RF(6, 19), 0x00 },
+       { MT_RF(6, 20), 0x00 },
+       { MT_RF(6, 21), 0x00 },
+       { MT_RF(6, 22), 0xFB },
+       /* LOGEN5G */
+       { MT_RF(6, 25), 0x76 },
+       { MT_RF(6, 26), 0x24 },
+       { MT_RF(6, 27), 0x04 },
+       { MT_RF(6, 28), 0x00 },
+       { MT_RF(6, 29), 0x00 },
+       /* TX */
+       { MT_RF(6, 37), 0xBB },
+       { MT_RF(6, 38), 0xB3 },
+       { MT_RF(6, 40), 0x33 },
+       { MT_RF(6, 41), 0x33 },
+       { MT_RF(6, 43), 0x03 },
+       { MT_RF(6, 44), 0xB3 },
+       { MT_RF(6, 46), 0x17 },
+       { MT_RF(6, 47), 0x0E },
+       { MT_RF(6, 48), 0x10 },
+       { MT_RF(6, 49), 0x07 },
+       { MT_RF(6, 62), 0x00 },
+       { MT_RF(6, 63), 0x00 },
+       { MT_RF(6, 64), 0xF1 },
+       { MT_RF(6, 65), 0x0F },
 };
 
 static const struct mt76_reg_pair mt76x0_rf_vga_channel_0_tab[] = {
-/*
-       Bank 7 - Channel 0 VGA RF registers
-*/
        /* E3 CR */
-       { MT_RF(7, 0), 0x47}, /* Allow BBP/MAC to do calibration */
-       { MT_RF(7, 1), 0x00},
-       { MT_RF(7, 2), 0x00},
-       { MT_RF(7, 3), 0x00},
-       { MT_RF(7, 4), 0x00},
-
-       { MT_RF(7, 10), 0x13},
-       { MT_RF(7, 11), 0x0F},
-       { MT_RF(7, 12), 0x13}, /* For dcoc */
-       { MT_RF(7, 13), 0x13}, /* For dcoc */
-       { MT_RF(7, 14), 0x13}, /* For dcoc */
-       { MT_RF(7, 15), 0x20}, /* For dcoc */
-       { MT_RF(7, 16), 0x22}, /* For dcoc */
-
-       { MT_RF(7, 17), 0x7C},
-
-       { MT_RF(7, 18), 0x00},
-       { MT_RF(7, 19), 0x00},
-       { MT_RF(7, 20), 0x00},
-       { MT_RF(7, 21), 0xF1},
-       { MT_RF(7, 22), 0x11},
-       { MT_RF(7, 23), 0xC2},
-       { MT_RF(7, 24), 0x41},
-       { MT_RF(7, 25), 0x20},
-       { MT_RF(7, 26), 0x40},
-       { MT_RF(7, 27), 0xD7},
-       { MT_RF(7, 28), 0xA2},
-       { MT_RF(7, 29), 0x60},
-       { MT_RF(7, 30), 0x49},
-       { MT_RF(7, 31), 0x20},
-       { MT_RF(7, 32), 0x44},
-       { MT_RF(7, 33), 0xC1},
-       { MT_RF(7, 34), 0x60},
-       { MT_RF(7, 35), 0xC0},
-
-       { MT_RF(7, 61), 0x01},
-
-       { MT_RF(7, 72), 0x3C},
-       { MT_RF(7, 73), 0x34},
-       { MT_RF(7, 74), 0x00},
+       { MT_RF(7,  0), 0x47 },
+       { MT_RF(7,  1), 0x00 },
+       { MT_RF(7,  2), 0x00 },
+       { MT_RF(7,  3), 0x00 },
+       { MT_RF(7,  4), 0x00 },
+       { MT_RF(7, 10), 0x13 },
+       { MT_RF(7, 11), 0x0F },
+       { MT_RF(7, 12), 0x13 },
+       { MT_RF(7, 13), 0x13 },
+       { MT_RF(7, 14), 0x13 },
+       { MT_RF(7, 15), 0x20 },
+       { MT_RF(7, 16), 0x22 },
+       { MT_RF(7, 17), 0x7C },
+       { MT_RF(7, 18), 0x00 },
+       { MT_RF(7, 19), 0x00 },
+       { MT_RF(7, 20), 0x00 },
+       { MT_RF(7, 21), 0xF1 },
+       { MT_RF(7, 22), 0x11 },
+       { MT_RF(7, 23), 0xC2 },
+       { MT_RF(7, 24), 0x41 },
+       { MT_RF(7, 25), 0x20 },
+       { MT_RF(7, 26), 0x40 },
+       { MT_RF(7, 27), 0xD7 },
+       { MT_RF(7, 28), 0xA2 },
+       { MT_RF(7, 29), 0x60 },
+       { MT_RF(7, 30), 0x49 },
+       { MT_RF(7, 31), 0x20 },
+       { MT_RF(7, 32), 0x44 },
+       { MT_RF(7, 33), 0xC1 },
+       { MT_RF(7, 34), 0x60 },
+       { MT_RF(7, 35), 0xC0 },
+       { MT_RF(7, 61), 0x01 },
+       { MT_RF(7, 72), 0x3C },
+       { MT_RF(7, 73), 0x34 },
+       { MT_RF(7, 74), 0x00 },
 };
 
 static const struct mt76x0_rf_switch_item mt76x0_rf_bw_switch_tab[] = {
-       /*   Bank,              Register,       Bw/Band,        Value */
-       { MT_RF(0, 17),         RF_G_BAND | RF_BW_20,   0x00},
-       { MT_RF(0, 17),         RF_G_BAND | RF_BW_40,   0x00},
-       { MT_RF(0, 17),         RF_A_BAND | RF_BW_20,   0x00},
-       { MT_RF(0, 17),         RF_A_BAND | RF_BW_40,   0x00},
-       { MT_RF(0, 17),         RF_A_BAND | RF_BW_80,   0x00},
-
-       /* TODO: need to check B7.R6 & B7.R7 setting for 2.4G again @20121112 */
-       { MT_RF(7,  6),         RF_G_BAND | RF_BW_20,   0x40},
-       { MT_RF(7,  6),         RF_G_BAND | RF_BW_40,   0x1C},
-       { MT_RF(7,  6),         RF_A_BAND | RF_BW_20,   0x40},
-       { MT_RF(7,  6),         RF_A_BAND | RF_BW_40,   0x20},
-       { MT_RF(7,  6),         RF_A_BAND | RF_BW_80,   0x10},
-
-       { MT_RF(7,  7),         RF_G_BAND | RF_BW_20,   0x40},
-       { MT_RF(7,  7),         RF_G_BAND | RF_BW_40,   0x20},
-       { MT_RF(7,  7),         RF_A_BAND | RF_BW_20,   0x40},
-       { MT_RF(7,  7),         RF_A_BAND | RF_BW_40,   0x20},
-       { MT_RF(7,  7),         RF_A_BAND | RF_BW_80,   0x10},
-
-       { MT_RF(7,  8),         RF_G_BAND | RF_BW_20,   0x03},
-       { MT_RF(7,  8),         RF_G_BAND | RF_BW_40,   0x01},
-       { MT_RF(7,  8),         RF_A_BAND | RF_BW_20,   0x03},
-       { MT_RF(7,  8),         RF_A_BAND | RF_BW_40,   0x01},
-       { MT_RF(7,  8),         RF_A_BAND | RF_BW_80,   0x00},
-
-       /* TODO: need to check B7.R58 & B7.R59 setting for 2.4G again @20121112 */
-       { MT_RF(7, 58),         RF_G_BAND | RF_BW_20,   0x40},
-       { MT_RF(7, 58),         RF_G_BAND | RF_BW_40,   0x40},
-       { MT_RF(7, 58),         RF_A_BAND | RF_BW_20,   0x40},
-       { MT_RF(7, 58),         RF_A_BAND | RF_BW_40,   0x40},
-       { MT_RF(7, 58),         RF_A_BAND | RF_BW_80,   0x10},
-
-       { MT_RF(7, 59),         RF_G_BAND | RF_BW_20,   0x40},
-       { MT_RF(7, 59),         RF_G_BAND | RF_BW_40,   0x40},
-       { MT_RF(7, 59),         RF_A_BAND | RF_BW_20,   0x40},
-       { MT_RF(7, 59),         RF_A_BAND | RF_BW_40,   0x40},
-       { MT_RF(7, 59),         RF_A_BAND | RF_BW_80,   0x10},
-
-       { MT_RF(7, 60),         RF_G_BAND | RF_BW_20,   0xAA},
-       { MT_RF(7, 60),         RF_G_BAND | RF_BW_40,   0xAA},
-       { MT_RF(7, 60),         RF_A_BAND | RF_BW_20,   0xAA},
-       { MT_RF(7, 60),         RF_A_BAND | RF_BW_40,   0xAA},
-       { MT_RF(7, 60),         RF_A_BAND | RF_BW_80,   0xAA},
-
-       { MT_RF(7, 76),         RF_BW_20,       0x40},
-       { MT_RF(7, 76),         RF_BW_40,       0x40},
-       { MT_RF(7, 76),         RF_BW_80,       0x10},
-
-       { MT_RF(7, 77),         RF_BW_20,       0x40},
-       { MT_RF(7, 77),         RF_BW_40,       0x40},
-       { MT_RF(7, 77),         RF_BW_80,       0x10},
+       /* bank, reg    bw/band                 value */
+       { MT_RF(0, 17), RF_G_BAND | RF_BW_20,   0x00 },
+       { MT_RF(0, 17), RF_G_BAND | RF_BW_40,   0x00 },
+       { MT_RF(0, 17), RF_A_BAND | RF_BW_20,   0x00 },
+       { MT_RF(0, 17), RF_A_BAND | RF_BW_40,   0x00 },
+       { MT_RF(0, 17), RF_A_BAND | RF_BW_80,   0x00 },
+       { MT_RF(7,  6), RF_G_BAND | RF_BW_20,   0x40 },
+       { MT_RF(7,  6), RF_G_BAND | RF_BW_40,   0x1C },
+       { MT_RF(7,  6), RF_A_BAND | RF_BW_20,   0x40 },
+       { MT_RF(7,  6), RF_A_BAND | RF_BW_40,   0x20 },
+       { MT_RF(7,  6), RF_A_BAND | RF_BW_80,   0x10 },
+       { MT_RF(7,  7), RF_G_BAND | RF_BW_20,   0x40 },
+       { MT_RF(7,  7), RF_G_BAND | RF_BW_40,   0x20 },
+       { MT_RF(7,  7), RF_A_BAND | RF_BW_20,   0x40 },
+       { MT_RF(7,  7), RF_A_BAND | RF_BW_40,   0x20 },
+       { MT_RF(7,  7), RF_A_BAND | RF_BW_80,   0x10 },
+       { MT_RF(7,  8), RF_G_BAND | RF_BW_20,   0x03 },
+       { MT_RF(7,  8), RF_G_BAND | RF_BW_40,   0x01 },
+       { MT_RF(7,  8), RF_A_BAND | RF_BW_20,   0x03 },
+       { MT_RF(7,  8), RF_A_BAND | RF_BW_40,   0x01 },
+       { MT_RF(7,  8), RF_A_BAND | RF_BW_80,   0x00 },
+       { MT_RF(7, 58), RF_G_BAND | RF_BW_20,   0x40 },
+       { MT_RF(7, 58), RF_G_BAND | RF_BW_40,   0x40 },
+       { MT_RF(7, 58), RF_A_BAND | RF_BW_20,   0x40 },
+       { MT_RF(7, 58), RF_A_BAND | RF_BW_40,   0x40 },
+       { MT_RF(7, 58), RF_A_BAND | RF_BW_80,   0x10 },
+       { MT_RF(7, 59), RF_G_BAND | RF_BW_20,   0x40 },
+       { MT_RF(7, 59), RF_G_BAND | RF_BW_40,   0x40 },
+       { MT_RF(7, 59), RF_A_BAND | RF_BW_20,   0x40 },
+       { MT_RF(7, 59), RF_A_BAND | RF_BW_40,   0x40 },
+       { MT_RF(7, 59), RF_A_BAND | RF_BW_80,   0x10 },
+       { MT_RF(7, 60), RF_G_BAND | RF_BW_20,   0xAA },
+       { MT_RF(7, 60), RF_G_BAND | RF_BW_40,   0xAA },
+       { MT_RF(7, 60), RF_A_BAND | RF_BW_20,   0xAA },
+       { MT_RF(7, 60), RF_A_BAND | RF_BW_40,   0xAA },
+       { MT_RF(7, 60), RF_A_BAND | RF_BW_80,   0xAA },
+       { MT_RF(7, 76), RF_BW_20,               0x40 },
+       { MT_RF(7, 76), RF_BW_40,               0x40 },
+       { MT_RF(7, 76), RF_BW_80,               0x10 },
+       { MT_RF(7, 77), RF_BW_20,               0x40 },
+       { MT_RF(7, 77), RF_BW_40,               0x40 },
+       { MT_RF(7, 77), RF_BW_80,               0x10 },
 };
 
 static const struct mt76x0_rf_switch_item mt76x0_rf_band_switch_tab[] = {
-       /*   Bank,              Register,       Bw/Band,                Value */
-       { MT_RF(0, 16),         RF_G_BAND,              0x20},
-       { MT_RF(0, 16),         RF_A_BAND,              0x20},
-
-       { MT_RF(0, 18),         RF_G_BAND,              0x00},
-       { MT_RF(0, 18),         RF_A_BAND,              0x00},
-
-       { MT_RF(0, 39),         RF_G_BAND,              0x36},
-       { MT_RF(0, 39),         RF_A_BAND_LB,   0x34},
-       { MT_RF(0, 39),         RF_A_BAND_MB,   0x33},
-       { MT_RF(0, 39),         RF_A_BAND_HB,   0x31},
-       { MT_RF(0, 39),         RF_A_BAND_11J,  0x36},
-
-       { MT_RF(6, 12),         RF_A_BAND_LB,   0x44},
-       { MT_RF(6, 12),         RF_A_BAND_MB,   0x44},
-       { MT_RF(6, 12),         RF_A_BAND_HB,   0x55},
-       { MT_RF(6, 12),         RF_A_BAND_11J,  0x44},
-
-       { MT_RF(6, 17),         RF_A_BAND_LB,   0x02},
-       { MT_RF(6, 17),         RF_A_BAND_MB,   0x00},
-       { MT_RF(6, 17),         RF_A_BAND_HB,   0x00},
-       { MT_RF(6, 17),         RF_A_BAND_11J,  0x05},
-
-       { MT_RF(6, 24),         RF_A_BAND_LB,   0xA1},
-       { MT_RF(6, 24),         RF_A_BAND_MB,   0x41},
-       { MT_RF(6, 24),         RF_A_BAND_HB,   0x21},
-       { MT_RF(6, 24),         RF_A_BAND_11J,  0xE1},
-
-       { MT_RF(6, 39),         RF_A_BAND_LB,   0x36},
-       { MT_RF(6, 39),         RF_A_BAND_MB,   0x34},
-       { MT_RF(6, 39),         RF_A_BAND_HB,   0x32},
-       { MT_RF(6, 39),         RF_A_BAND_11J,  0x37},
-
-       { MT_RF(6, 42),         RF_A_BAND_LB,   0xFB},
-       { MT_RF(6, 42),         RF_A_BAND_MB,   0xF3},
-       { MT_RF(6, 42),         RF_A_BAND_HB,   0xEB},
-       { MT_RF(6, 42),         RF_A_BAND_11J,  0xEB},
-
-       /* Move R6-R45, R50~R59 to mt76x0_RF_INT_PA_5G_Channel_0_RegTb/mt76x0_RF_EXT_PA_5G_Channel_0_RegTb */
-
-       { MT_RF(6, 127),        RF_G_BAND,              0x84},
-       { MT_RF(6, 127),        RF_A_BAND,              0x04},
-
-       { MT_RF(7, 5),          RF_G_BAND,              0x40},
-       { MT_RF(7, 5),          RF_A_BAND,              0x00},
-
-       { MT_RF(7, 9),          RF_G_BAND,              0x00},
-       { MT_RF(7, 9),          RF_A_BAND,              0x00},
-
-       { MT_RF(7, 70),         RF_G_BAND,              0x00},
-       { MT_RF(7, 70),         RF_A_BAND,              0x6D},
-
-       { MT_RF(7, 71),         RF_G_BAND,              0x00},
-       { MT_RF(7, 71),         RF_A_BAND,              0xB0},
-
-       { MT_RF(7, 78),         RF_G_BAND,              0x00},
-       { MT_RF(7, 78),         RF_A_BAND,              0x55},
-
-       { MT_RF(7, 79),         RF_G_BAND,              0x00},
-       { MT_RF(7, 79),         RF_A_BAND,              0x55},
+       /* bank, reg            bw/band         value */
+       { MT_RF(0,  16),        RF_G_BAND,      0x20 },
+       { MT_RF(0,  16),        RF_A_BAND,      0x20 },
+       { MT_RF(0,  18),        RF_G_BAND,      0x00 },
+       { MT_RF(0,  18),        RF_A_BAND,      0x00 },
+       { MT_RF(0,  39),        RF_G_BAND,      0x36 },
+       { MT_RF(0,  39),        RF_A_BAND_LB,   0x34 },
+       { MT_RF(0,  39),        RF_A_BAND_MB,   0x33 },
+       { MT_RF(0,  39),        RF_A_BAND_HB,   0x31 },
+       { MT_RF(0,  39),        RF_A_BAND_11J,  0x36 },
+       { MT_RF(6,  12),        RF_A_BAND_LB,   0x44 },
+       { MT_RF(6,  12),        RF_A_BAND_MB,   0x44 },
+       { MT_RF(6,  12),        RF_A_BAND_HB,   0x55 },
+       { MT_RF(6,  12),        RF_A_BAND_11J,  0x44 },
+       { MT_RF(6,  17),        RF_A_BAND_LB,   0x02 },
+       { MT_RF(6,  17),        RF_A_BAND_MB,   0x00 },
+       { MT_RF(6,  17),        RF_A_BAND_HB,   0x00 },
+       { MT_RF(6,  17),        RF_A_BAND_11J,  0x05 },
+       { MT_RF(6,  24),        RF_A_BAND_LB,   0xA1 },
+       { MT_RF(6,  24),        RF_A_BAND_MB,   0x41 },
+       { MT_RF(6,  24),        RF_A_BAND_HB,   0x21 },
+       { MT_RF(6,  24),        RF_A_BAND_11J,  0xE1 },
+       { MT_RF(6,  39),        RF_A_BAND_LB,   0x36 },
+       { MT_RF(6,  39),        RF_A_BAND_MB,   0x34 },
+       { MT_RF(6,  39),        RF_A_BAND_HB,   0x32 },
+       { MT_RF(6,  39),        RF_A_BAND_11J,  0x37 },
+       { MT_RF(6,  42),        RF_A_BAND_LB,   0xFB },
+       { MT_RF(6,  42),        RF_A_BAND_MB,   0xF3 },
+       { MT_RF(6,  42),        RF_A_BAND_HB,   0xEB },
+       { MT_RF(6,  42),        RF_A_BAND_11J,  0xEB },
+       { MT_RF(6, 127),        RF_G_BAND,      0x84 },
+       { MT_RF(6, 127),        RF_A_BAND,      0x04 },
+       { MT_RF(7,   5),        RF_G_BAND,      0x40 },
+       { MT_RF(7,   5),        RF_A_BAND,      0x00 },
+       { MT_RF(7,   9),        RF_G_BAND,      0x00 },
+       { MT_RF(7,   9),        RF_A_BAND,      0x00 },
+       { MT_RF(7,  70),        RF_G_BAND,      0x00 },
+       { MT_RF(7,  70),        RF_A_BAND,      0x6D },
+       { MT_RF(7,  71),        RF_G_BAND,      0x00 },
+       { MT_RF(7,  71),        RF_A_BAND,      0xB0 },
+       { MT_RF(7,  78),        RF_G_BAND,      0x00 },
+       { MT_RF(7,  78),        RF_A_BAND,      0x55 },
+       { MT_RF(7,  79),        RF_G_BAND,      0x00 },
+       { MT_RF(7,  79),        RF_A_BAND,      0x55 },
 };
 
 static const struct mt76x0_freq_item mt76x0_frequency_plan[] = {
-       {1,     RF_G_BAND,      0x02, 0x3F, 0x28, 0xDD, 0xE2, 0x40, 0x02, 0x40, 0x02, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3}, /* Freq 2412 */
-       {2,     RF_G_BAND,      0x02, 0x3F, 0x3C, 0xDD, 0xE4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA1, 0, 0x30, 0, 0, 0x1}, /* Freq 2417 */
-       {3,     RF_G_BAND,      0x02, 0x3F, 0x3C, 0xDD, 0xE2, 0x40, 0x07, 0x40, 0x0B, 0, 0, 1, 0x50, 0, 0x30, 0, 0, 0x0}, /* Freq 2422 */
-       {4,     RF_G_BAND,      0x02, 0x3F, 0x28, 0xDD, 0xD4, 0x40, 0x02, 0x40, 0x09, 0, 0, 1, 0x50, 0, 0x30, 0, 0, 0x0}, /* Freq 2427 */
-       {5,     RF_G_BAND,      0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA2, 0, 0x30, 0, 0, 0x1}, /* Freq 2432 */
-       {6,     RF_G_BAND,      0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x07, 0, 0, 1, 0xA2, 0, 0x30, 0, 0, 0x1}, /* Freq 2437 */
-       {7,     RF_G_BAND,      0x02, 0x3F, 0x28, 0xDD, 0xE2, 0x40, 0x02, 0x40, 0x07, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3}, /* Freq 2442 */
-       {8,     RF_G_BAND,      0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA3, 0, 0x30, 0, 0, 0x1}, /* Freq 2447 */
-       {9,     RF_G_BAND,      0x02, 0x3F, 0x3C, 0xDD, 0xF2, 0x40, 0x07, 0x40, 0x0D, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3}, /* Freq 2452 */
-       {10,    RF_G_BAND,      0x02, 0x3F, 0x28, 0xDD, 0xD4, 0x40, 0x02, 0x40, 0x09, 0, 0, 1, 0x51, 0, 0x30, 0, 0, 0x0}, /* Freq 2457 */
-       {11,    RF_G_BAND,      0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA4, 0, 0x30, 0, 0, 0x1}, /* Freq 2462 */
-       {12,    RF_G_BAND,      0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x07, 0, 0, 1, 0xA4, 0, 0x30, 0, 0, 0x1}, /* Freq 2467 */
-       {13,    RF_G_BAND,      0x02, 0x3F, 0x28, 0xDD, 0xF2, 0x40, 0x02, 0x40, 0x02, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3}, /* Freq 2472 */
-       {14,    RF_G_BAND,      0x02, 0x3F, 0x28, 0xDD, 0xF2, 0x40, 0x02, 0x40, 0x04, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3}, /* Freq 2484 */
-
-       {183,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3}, /* Freq 4915 */
-       {184,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x00, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3}, /* Freq 4920 */
-       {185,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3}, /* Freq 4925 */
-       {187,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3}, /* Freq 4935 */
-       {188,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3}, /* Freq 4940 */
-       {189,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3}, /* Freq 4945 */
-       {192,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3}, /* Freq 4960 */
-       {196,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3}, /* Freq 4980 */
-
-       {36,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5180 */
-       {37,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5185 */
-       {38,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5190 */
-       {39,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5195 */
-       {40,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5200 */
-       {41,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5205 */
-       {42,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5210 */
-       {43,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5215 */
-       {44,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5220 */
-       {45,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5225 */
-       {46,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5230 */
-       {47,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5235 */
-       {48,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5240 */
-       {49,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5245 */
-       {50,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5250 */
-       {51,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5255 */
-       {52,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5260 */
-       {53,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5265 */
-       {54,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5270 */
-       {55,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3}, /* Freq 5275 */
-       {56,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3}, /* Freq 5280 */
-       {57,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3}, /* Freq 5285 */
-       {58,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3}, /* Freq 5290 */
-       {59,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3}, /* Freq 5295 */
-       {60,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3}, /* Freq 5300 */
-       {61,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3}, /* Freq 5305 */
-       {62,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3}, /* Freq 5310 */
-       {63,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3}, /* Freq 5315 */
-       {64,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3}, /* Freq 5320 */
-
-       {100,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3}, /* Freq 5500 */
-       {101,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3}, /* Freq 5505 */
-       {102,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3}, /* Freq 5510 */
-       {103,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3}, /* Freq 5515 */
-       {104,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5520 */
-       {105,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5525 */
-       {106,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5530 */
-       {107,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5535 */
-       {108,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5540 */
-       {109,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5545 */
-       {110,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5550 */
-       {111,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5555 */
-       {112,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5560 */
-       {113,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5565 */
-       {114,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5570 */
-       {115,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5575 */
-       {116,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5580 */
-       {117,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5585 */
-       {118,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5590 */
-       {119,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5595 */
-       {120,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5600 */
-       {121,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5605 */
-       {122,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5610 */
-       {123,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5615 */
-       {124,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5620 */
-       {125,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5625 */
-       {126,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5630 */
-       {127,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3}, /* Freq 5635 */
-       {128,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5640 */
-       {129,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5645 */
-       {130,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5650 */
-       {131,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5655 */
-       {132,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5660 */
-       {133,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5665 */
-       {134,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5670 */
-       {135,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5675 */
-       {136,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5680 */
-
-       {137,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5685 */
-       {138,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5690 */
-       {139,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5695 */
-       {140,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5700 */
-       {141,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5705 */
-       {142,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5710 */
-       {143,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5715 */
-       {144,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5720 */
-       {145,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5725 */
-       {146,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5730 */
-       {147,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5735 */
-       {148,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5740 */
-       {149,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5745 */
-       {150,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5750 */
-       {151,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3}, /* Freq 5755 */
-       {152,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5760 */
-       {153,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5765 */
-       {154,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5770 */
-       {155,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5775 */
-       {156,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5780 */
-       {157,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5785 */
-       {158,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5790 */
-       {159,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5795 */
-       {160,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5800 */
-       {161,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5805 */
-       {162,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5810 */
-       {163,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5815 */
-       {164,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5820 */
-       {165,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5825 */
-       {166,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5830 */
-       {167,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5835 */
-       {168,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5840 */
-       {169,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5845 */
-       {170,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5850 */
-       {171,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5855 */
-       {172,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5860 */
-       {173,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3}, /* Freq 5865 */
+       {   1,  RF_G_BAND,                      0x02, 0x3F, 0x28, 0xDD, 0xE2, 0x40, 0x02, 0x40, 0x02, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3 }, /* Freq 2412 */
+       {   2,  RF_G_BAND,                      0x02, 0x3F, 0x3C, 0xDD, 0xE4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA1, 0, 0x30, 0, 0, 0x1 }, /* Freq 2417 */
+       {   3,  RF_G_BAND,                      0x02, 0x3F, 0x3C, 0xDD, 0xE2, 0x40, 0x07, 0x40, 0x0B, 0, 0, 1, 0x50, 0, 0x30, 0, 0, 0x0 }, /* Freq 2422 */
+       {   4,  RF_G_BAND,                      0x02, 0x3F, 0x28, 0xDD, 0xD4, 0x40, 0x02, 0x40, 0x09, 0, 0, 1, 0x50, 0, 0x30, 0, 0, 0x0 }, /* Freq 2427 */
+       {   5,  RF_G_BAND,                      0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA2, 0, 0x30, 0, 0, 0x1 }, /* Freq 2432 */
+       {   6,  RF_G_BAND,                      0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x07, 0, 0, 1, 0xA2, 0, 0x30, 0, 0, 0x1 }, /* Freq 2437 */
+       {   7,  RF_G_BAND,                      0x02, 0x3F, 0x28, 0xDD, 0xE2, 0x40, 0x02, 0x40, 0x07, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3 }, /* Freq 2442 */
+       {   8,  RF_G_BAND,                      0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA3, 0, 0x30, 0, 0, 0x1 }, /* Freq 2447 */
+       {   9,  RF_G_BAND,                      0x02, 0x3F, 0x3C, 0xDD, 0xF2, 0x40, 0x07, 0x40, 0x0D, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3 }, /* Freq 2452 */
+       {  10,  RF_G_BAND,                      0x02, 0x3F, 0x28, 0xDD, 0xD4, 0x40, 0x02, 0x40, 0x09, 0, 0, 1, 0x51, 0, 0x30, 0, 0, 0x0 }, /* Freq 2457 */
+       {  11,  RF_G_BAND,                      0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA4, 0, 0x30, 0, 0, 0x1 }, /* Freq 2462 */
+       {  12,  RF_G_BAND,                      0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x07, 0, 0, 1, 0xA4, 0, 0x30, 0, 0, 0x1 }, /* Freq 2467 */
+       {  13,  RF_G_BAND,                      0x02, 0x3F, 0x28, 0xDD, 0xF2, 0x40, 0x02, 0x40, 0x02, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 2472 */
+       {  14,  RF_G_BAND,                      0x02, 0x3F, 0x28, 0xDD, 0xF2, 0x40, 0x02, 0x40, 0x04, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 2484 */
+       { 183,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3 }, /* Freq 4915 */
+       { 184,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x00, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4920 */
+       { 185,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4925 */
+       { 187,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4935 */
+       { 188,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4940 */
+       { 189,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4945 */
+       { 192,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4960 */
+       { 196,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4980 */
+       {  36,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5180 */
+       {  37,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5185 */
+       {  38,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5190 */
+       {  39,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5195 */
+       {  40,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5200 */
+       {  41,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5205 */
+       {  42,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5210 */
+       {  43,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5215 */
+       {  44,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5220 */
+       {  45,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5225 */
+       {  46,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5230 */
+       {  47,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5235 */
+       {  48,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5240 */
+       {  49,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5245 */
+       {  50,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5250 */
+       {  51,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5255 */
+       {  52,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5260 */
+       {  53,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5265 */
+       {  54,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5270 */
+       {  55,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5275 */
+       {  56,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5280 */
+       {  57,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5285 */
+       {  58,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5290 */
+       {  59,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5295 */
+       {  60,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5300 */
+       {  61,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5305 */
+       {  62,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5310 */
+       {  63,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5315 */
+       {  64,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5320 */
+       { 100,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3 }, /* Freq 5500 */
+       { 101,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3 }, /* Freq 5505 */
+       { 102,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3 }, /* Freq 5510 */
+       { 103,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3 }, /* Freq 5515 */
+       { 104,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5520 */
+       { 105,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5525 */
+       { 106,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5530 */
+       { 107,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5535 */
+       { 108,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5540 */
+       { 109,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5545 */
+       { 110,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5550 */
+       { 111,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5555 */
+       { 112,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5560 */
+       { 113,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5565 */
+       { 114,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5570 */
+       { 115,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5575 */
+       { 116,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5580 */
+       { 117,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5585 */
+       { 118,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5590 */
+       { 119,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5595 */
+       { 120,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5600 */
+       { 121,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5605 */
+       { 122,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5610 */
+       { 123,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5615 */
+       { 124,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5620 */
+       { 125,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5625 */
+       { 126,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5630 */
+       { 127,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5635 */
+       { 128,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5640 */
+       { 129,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5645 */
+       { 130,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5650 */
+       { 131,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5655 */
+       { 132,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5660 */
+       { 133,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5665 */
+       { 134,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5670 */
+       { 135,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5675 */
+       { 136,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5680 */
+       { 137,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5685 */
+       { 138,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5690 */
+       { 139,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5695 */
+       { 140,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5700 */
+       { 141,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5705 */
+       { 142,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5710 */
+       { 143,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5715 */
+       { 144,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5720 */
+       { 145,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5725 */
+       { 146,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5730 */
+       { 147,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5735 */
+       { 148,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5740 */
+       { 149,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5745 */
+       { 150,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5750 */
+       { 151,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5755 */
+       { 152,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5760 */
+       { 153,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5765 */
+       { 154,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5770 */
+       { 155,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5775 */
+       { 156,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5780 */
+       { 157,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5785 */
+       { 158,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5790 */
+       { 159,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5795 */
+       { 160,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5800 */
+       { 161,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5805 */
+       { 162,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5810 */
+       { 163,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5815 */
+       { 164,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5820 */
+       { 165,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5825 */
+       { 166,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5830 */
+       { 167,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5835 */
+       { 168,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5840 */
+       { 169,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5845 */
+       { 170,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5850 */
+       { 171,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5855 */
+       { 172,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5860 */
+       { 173,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5865 */
 };
 
 static const struct mt76x0_freq_item mt76x0_sdm_frequency_plan[] = {
-       {1,     RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0xCCCC,  0x3}, /* Freq 2412 */
-       {2,     RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0x12222, 0x3}, /* Freq 2417 */
-       {3,     RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0x17777, 0x3}, /* Freq 2422 */
-       {4,     RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0x1CCCC, 0x3}, /* Freq 2427 */
-       {5,     RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0x22222, 0x3}, /* Freq 2432 */
-       {6,     RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0x27777, 0x3}, /* Freq 2437 */
-       {7,     RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0x2CCCC, 0x3}, /* Freq 2442 */
-       {8,     RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0x32222, 0x3}, /* Freq 2447 */
-       {9,     RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0x37777, 0x3}, /* Freq 2452 */
-       {10,    RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0x3CCCC, 0x3}, /* Freq 2457 */
-       {11,    RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0x2222, 0x3}, /* Freq 2462 */
-       {12,    RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0x7777, 0x3}, /* Freq 2467 */
-       {13,    RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0xCCCC, 0x3}, /* Freq 2472 */
-       {14,    RF_G_BAND,      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0x19999, 0x3}, /* Freq 2484 */
-
-       {183,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x28, 0, 0x0, 0x8, 0x3D555, 0x3}, /* Freq 4915 */
-       {184,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0x0,     0x3}, /* Freq 4920 */
-       {185,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0x2AAA,  0x3}, /* Freq 4925 */
-       {187,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0x8000,  0x3}, /* Freq 4935 */
-       {188,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0xAAAA,  0x3}, /* Freq 4940 */
-       {189,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0xD555,  0x3}, /* Freq 4945 */
-       {192,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0x15555, 0x3}, /* Freq 4960 */
-       {196,   (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x29, 0, 0x0, 0x8, 0x20000, 0x3}, /* Freq 4980 */
-
-       {36,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0xAAAA,  0x3}, /* Freq 5180 */
-       {37,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0xD555,  0x3}, /* Freq 5185 */
-       {38,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x10000, 0x3}, /* Freq 5190 */
-       {39,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x12AAA, 0x3}, /* Freq 5195 */
-       {40,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x15555, 0x3}, /* Freq 5200 */
-       {41,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x18000, 0x3}, /* Freq 5205 */
-       {42,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x1AAAA, 0x3}, /* Freq 5210 */
-       {43,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x1D555, 0x3}, /* Freq 5215 */
-       {44,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x20000, 0x3}, /* Freq 5220 */
-       {45,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x22AAA, 0x3}, /* Freq 5225 */
-       {46,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x25555, 0x3}, /* Freq 5230 */
-       {47,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x28000, 0x3}, /* Freq 5235 */
-       {48,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x2AAAA, 0x3}, /* Freq 5240 */
-       {49,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x2D555, 0x3}, /* Freq 5245 */
-       {50,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x30000, 0x3}, /* Freq 5250 */
-       {51,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x32AAA, 0x3}, /* Freq 5255 */
-       {52,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x35555, 0x3}, /* Freq 5260 */
-       {53,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x38000, 0x3}, /* Freq 5265 */
-       {54,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x3AAAA, 0x3}, /* Freq 5270 */
-       {55,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2B, 0, 0x0, 0x8, 0x3D555, 0x3}, /* Freq 5275 */
-       {56,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2C, 0, 0x0, 0x8, 0x00000, 0x3}, /* Freq 5280 */
-       {57,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2C, 0, 0x0, 0x8, 0x02AAA, 0x3}, /* Freq 5285 */
-       {58,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2C, 0, 0x0, 0x8, 0x05555, 0x3}, /* Freq 5290 */
-       {59,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2C, 0, 0x0, 0x8, 0x08000, 0x3}, /* Freq 5295 */
-       {60,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2C, 0, 0x0, 0x8, 0x0AAAA, 0x3}, /* Freq 5300 */
-       {61,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2C, 0, 0x0, 0x8, 0x0D555, 0x3}, /* Freq 5305 */
-       {62,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2C, 0, 0x0, 0x8, 0x10000, 0x3}, /* Freq 5310 */
-       {63,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2C, 0, 0x0, 0x8, 0x12AAA, 0x3}, /* Freq 5315 */
-       {64,    (RF_A_BAND | RF_A_BAND_LB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2C, 0, 0x0, 0x8, 0x15555, 0x3}, /* Freq 5320 */
-
-       {100,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2D, 0, 0x0, 0x8, 0x35555, 0x3}, /* Freq 5500 */
-       {101,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2D, 0, 0x0, 0x8, 0x38000, 0x3}, /* Freq 5505 */
-       {102,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2D, 0, 0x0, 0x8, 0x3AAAA, 0x3}, /* Freq 5510 */
-       {103,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2D, 0, 0x0, 0x8, 0x3D555, 0x3}, /* Freq 5515 */
-       {104,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x00000, 0x3}, /* Freq 5520 */
-       {105,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x02AAA, 0x3}, /* Freq 5525 */
-       {106,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x05555, 0x3}, /* Freq 5530 */
-       {107,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x08000, 0x3}, /* Freq 5535 */
-       {108,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x0AAAA, 0x3}, /* Freq 5540 */
-       {109,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x0D555, 0x3}, /* Freq 5545 */
-       {110,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x10000, 0x3}, /* Freq 5550 */
-       {111,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x12AAA, 0x3}, /* Freq 5555 */
-       {112,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x15555, 0x3}, /* Freq 5560 */
-       {113,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x18000, 0x3}, /* Freq 5565 */
-       {114,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x1AAAA, 0x3}, /* Freq 5570 */
-       {115,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x1D555, 0x3}, /* Freq 5575 */
-       {116,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x20000, 0x3}, /* Freq 5580 */
-       {117,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x22AAA, 0x3}, /* Freq 5585 */
-       {118,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x25555, 0x3}, /* Freq 5590 */
-       {119,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x28000, 0x3}, /* Freq 5595 */
-       {120,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x2AAAA, 0x3}, /* Freq 5600 */
-       {121,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x2D555, 0x3}, /* Freq 5605 */
-       {122,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x30000, 0x3}, /* Freq 5610 */
-       {123,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x32AAA, 0x3}, /* Freq 5615 */
-       {124,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x35555, 0x3}, /* Freq 5620 */
-       {125,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x38000, 0x3}, /* Freq 5625 */
-       {126,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x3AAAA, 0x3}, /* Freq 5630 */
-       {127,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2E, 0, 0x0, 0x8, 0x3D555, 0x3}, /* Freq 5635 */
-       {128,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x00000, 0x3}, /* Freq 5640 */
-       {129,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x02AAA, 0x3}, /* Freq 5645 */
-       {130,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x05555, 0x3}, /* Freq 5650 */
-       {131,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x08000, 0x3}, /* Freq 5655 */
-       {132,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x0AAAA, 0x3}, /* Freq 5660 */
-       {133,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x0D555, 0x3}, /* Freq 5665 */
-       {134,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x10000, 0x3}, /* Freq 5670 */
-       {135,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x12AAA, 0x3}, /* Freq 5675 */
-       {136,   (RF_A_BAND | RF_A_BAND_MB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x15555, 0x3}, /* Freq 5680 */
-
-       {137,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x18000, 0x3}, /* Freq 5685 */
-       {138,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x1AAAA, 0x3}, /* Freq 5690 */
-       {139,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x1D555, 0x3}, /* Freq 5695 */
-       {140,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x20000, 0x3}, /* Freq 5700 */
-       {141,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x22AAA, 0x3}, /* Freq 5705 */
-       {142,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x25555, 0x3}, /* Freq 5710 */
-       {143,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x28000, 0x3}, /* Freq 5715 */
-       {144,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x2AAAA, 0x3}, /* Freq 5720 */
-       {145,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x2D555, 0x3}, /* Freq 5725 */
-       {146,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x30000, 0x3}, /* Freq 5730 */
-       {147,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x32AAA, 0x3}, /* Freq 5735 */
-       {148,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x35555, 0x3}, /* Freq 5740 */
-       {149,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x38000, 0x3}, /* Freq 5745 */
-       {150,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x3AAAA, 0x3}, /* Freq 5750 */
-       {151,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x2F, 0, 0x0, 0x8, 0x3D555, 0x3}, /* Freq 5755 */
-       {152,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x00000, 0x3}, /* Freq 5760 */
-       {153,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x02AAA, 0x3}, /* Freq 5765 */
-       {154,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x05555, 0x3}, /* Freq 5770 */
-       {155,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x08000, 0x3}, /* Freq 5775 */
-       {156,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x0AAAA, 0x3}, /* Freq 5780 */
-       {157,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x0D555, 0x3}, /* Freq 5785 */
-       {158,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x10000, 0x3}, /* Freq 5790 */
-       {159,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x12AAA, 0x3}, /* Freq 5795 */
-       {160,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x15555, 0x3}, /* Freq 5800 */
-       {161,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x18000, 0x3}, /* Freq 5805 */
-       {162,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x1AAAA, 0x3}, /* Freq 5810 */
-       {163,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x1D555, 0x3}, /* Freq 5815 */
-       {164,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x20000, 0x3}, /* Freq 5820 */
-       {165,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x22AAA, 0x3}, /* Freq 5825 */
-       {166,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x25555, 0x3}, /* Freq 5830 */
-       {167,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x28000, 0x3}, /* Freq 5835 */
-       {168,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x2AAAA, 0x3}, /* Freq 5840 */
-       {169,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x2D555, 0x3}, /* Freq 5845 */
-       {170,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x30000, 0x3}, /* Freq 5850 */
-       {171,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x32AAA, 0x3}, /* Freq 5855 */
-       {172,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x35555, 0x3}, /* Freq 5860 */
-       {173,   (RF_A_BAND | RF_A_BAND_HB),      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0/*0 -> 1*/, 0, 0, 0x30, 0, 0x0, 0x8, 0x38000, 0x3}, /* Freq 5865 */
+       {   1,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x0CCCC, 0x3 }, /* Freq 2412 */
+       {   2,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x12222, 0x3 }, /* Freq 2417 */
+       {   3,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x17777, 0x3 }, /* Freq 2422 */
+       {   4,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x1CCCC, 0x3 }, /* Freq 2427 */
+       {   5,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x22222, 0x3 }, /* Freq 2432 */
+       {   6,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x27777, 0x3 }, /* Freq 2437 */
+       {   7,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x2CCCC, 0x3 }, /* Freq 2442 */
+       {   8,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x32222, 0x3 }, /* Freq 2447 */
+       {   9,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x37777, 0x3 }, /* Freq 2452 */
+       {  10,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x3CCCC, 0x3 }, /* Freq 2457 */
+       {  11,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x02222, 0x3 }, /* Freq 2462 */
+       {  12,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x07777, 0x3 }, /* Freq 2467 */
+       {  13,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x0CCCC, 0x3 }, /* Freq 2472 */
+       {  14,  RF_G_BAND,                      0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x19999, 0x3 }, /* Freq 2484 */
+       { 183,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x3D555, 0x3 }, /* Freq 4915 */
+       { 184,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x00000, 0x3 }, /* Freq 4920 */
+       { 185,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x02AAA, 0x3 }, /* Freq 4925 */
+       { 187,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x08000, 0x3 }, /* Freq 4935 */
+       { 188,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 4940 */
+       { 189,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 4945 */
+       { 192,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 4960 */
+       { 196,  (RF_A_BAND | RF_A_BAND_11J),    0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x20000, 0x3 }, /* Freq 4980 */
+       {  36,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 5180 */
+       {  37,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 5185 */
+       {  38,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x10000, 0x3 }, /* Freq 5190 */
+       {  39,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x12AAA, 0x3 }, /* Freq 5195 */
+       {  40,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 5200 */
+       {  41,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x18000, 0x3 }, /* Freq 5205 */
+       {  42,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x1AAAA, 0x3 }, /* Freq 5210 */
+       {  43,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x1D555, 0x3 }, /* Freq 5215 */
+       {  44,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x20000, 0x3 }, /* Freq 5220 */
+       {  45,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x22AAA, 0x3 }, /* Freq 5225 */
+       {  46,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x25555, 0x3 }, /* Freq 5230 */
+       {  47,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x28000, 0x3 }, /* Freq 5235 */
+       {  48,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x2AAAA, 0x3 }, /* Freq 5240 */
+       {  49,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x2D555, 0x3 }, /* Freq 5245 */
+       {  50,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x30000, 0x3 }, /* Freq 5250 */
+       {  51,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x32AAA, 0x3 }, /* Freq 5255 */
+       {  52,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x35555, 0x3 }, /* Freq 5260 */
+       {  53,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x38000, 0x3 }, /* Freq 5265 */
+       {  54,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x3AAAA, 0x3 }, /* Freq 5270 */
+       {  55,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x3D555, 0x3 }, /* Freq 5275 */
+       {  56,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x00000, 0x3 }, /* Freq 5280 */
+       {  57,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x02AAA, 0x3 }, /* Freq 5285 */
+       {  58,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x05555, 0x3 }, /* Freq 5290 */
+       {  59,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x08000, 0x3 }, /* Freq 5295 */
+       {  60,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 5300 */
+       {  61,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 5305 */
+       {  62,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x10000, 0x3 }, /* Freq 5310 */
+       {  63,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x12AAA, 0x3 }, /* Freq 5315 */
+       {  64,  (RF_A_BAND | RF_A_BAND_LB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 5320 */
+       { 100,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2D, 0, 0x0, 0x8, 0x35555, 0x3 }, /* Freq 5500 */
+       { 101,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2D, 0, 0x0, 0x8, 0x38000, 0x3 }, /* Freq 5505 */
+       { 102,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2D, 0, 0x0, 0x8, 0x3AAAA, 0x3 }, /* Freq 5510 */
+       { 103,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2D, 0, 0x0, 0x8, 0x3D555, 0x3 }, /* Freq 5515 */
+       { 104,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x00000, 0x3 }, /* Freq 5520 */
+       { 105,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x02AAA, 0x3 }, /* Freq 5525 */
+       { 106,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x05555, 0x3 }, /* Freq 5530 */
+       { 107,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x08000, 0x3 }, /* Freq 5535 */
+       { 108,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 5540 */
+       { 109,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 5545 */
+       { 110,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x10000, 0x3 }, /* Freq 5550 */
+       { 111,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x12AAA, 0x3 }, /* Freq 5555 */
+       { 112,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 5560 */
+       { 113,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x18000, 0x3 }, /* Freq 5565 */
+       { 114,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x1AAAA, 0x3 }, /* Freq 5570 */
+       { 115,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x1D555, 0x3 }, /* Freq 5575 */
+       { 116,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x20000, 0x3 }, /* Freq 5580 */
+       { 117,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x22AAA, 0x3 }, /* Freq 5585 */
+       { 118,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x25555, 0x3 }, /* Freq 5590 */
+       { 119,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x28000, 0x3 }, /* Freq 5595 */
+       { 120,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x2AAAA, 0x3 }, /* Freq 5600 */
+       { 121,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x2D555, 0x3 }, /* Freq 5605 */
+       { 122,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x30000, 0x3 }, /* Freq 5610 */
+       { 123,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x32AAA, 0x3 }, /* Freq 5615 */
+       { 124,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x35555, 0x3 }, /* Freq 5620 */
+       { 125,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x38000, 0x3 }, /* Freq 5625 */
+       { 126,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x3AAAA, 0x3 }, /* Freq 5630 */
+       { 127,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x3D555, 0x3 }, /* Freq 5635 */
+       { 128,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x00000, 0x3 }, /* Freq 5640 */
+       { 129,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x02AAA, 0x3 }, /* Freq 5645 */
+       { 130,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x05555, 0x3 }, /* Freq 5650 */
+       { 131,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x08000, 0x3 }, /* Freq 5655 */
+       { 132,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 5660 */
+       { 133,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 5665 */
+       { 134,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x10000, 0x3 }, /* Freq 5670 */
+       { 135,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x12AAA, 0x3 }, /* Freq 5675 */
+       { 136,  (RF_A_BAND | RF_A_BAND_MB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 5680 */
+       { 137,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x18000, 0x3 }, /* Freq 5685 */
+       { 138,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x1AAAA, 0x3 }, /* Freq 5690 */
+       { 139,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x1D555, 0x3 }, /* Freq 5695 */
+       { 140,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x20000, 0x3 }, /* Freq 5700 */
+       { 141,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x22AAA, 0x3 }, /* Freq 5705 */
+       { 142,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x25555, 0x3 }, /* Freq 5710 */
+       { 143,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x28000, 0x3 }, /* Freq 5715 */
+       { 144,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x2AAAA, 0x3 }, /* Freq 5720 */
+       { 145,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x2D555, 0x3 }, /* Freq 5725 */
+       { 146,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x30000, 0x3 }, /* Freq 5730 */
+       { 147,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x32AAA, 0x3 }, /* Freq 5735 */
+       { 148,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x35555, 0x3 }, /* Freq 5740 */
+       { 149,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x38000, 0x3 }, /* Freq 5745 */
+       { 150,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x3AAAA, 0x3 }, /* Freq 5750 */
+       { 151,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x3D555, 0x3 }, /* Freq 5755 */
+       { 152,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x00000, 0x3 }, /* Freq 5760 */
+       { 153,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x02AAA, 0x3 }, /* Freq 5765 */
+       { 154,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x05555, 0x3 }, /* Freq 5770 */
+       { 155,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x08000, 0x3 }, /* Freq 5775 */
+       { 156,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 5780 */
+       { 157,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 5785 */
+       { 158,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x10000, 0x3 }, /* Freq 5790 */
+       { 159,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x12AAA, 0x3 }, /* Freq 5795 */
+       { 160,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 5800 */
+       { 161,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x18000, 0x3 }, /* Freq 5805 */
+       { 162,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x1AAAA, 0x3 }, /* Freq 5810 */
+       { 163,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x1D555, 0x3 }, /* Freq 5815 */
+       { 164,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x20000, 0x3 }, /* Freq 5820 */
+       { 165,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x22AAA, 0x3 }, /* Freq 5825 */
+       { 166,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x25555, 0x3 }, /* Freq 5830 */
+       { 167,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x28000, 0x3 }, /* Freq 5835 */
+       { 168,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x2AAAA, 0x3 }, /* Freq 5840 */
+       { 169,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x2D555, 0x3 }, /* Freq 5845 */
+       { 170,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x30000, 0x3 }, /* Freq 5850 */
+       { 171,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x32AAA, 0x3 }, /* Freq 5855 */
+       { 172,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x35555, 0x3 }, /* Freq 5860 */
+       { 173,  (RF_A_BAND | RF_A_BAND_HB),     0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x38000, 0x3 }, /* Freq 5865 */
 };
 
 static const u8 mt76x0_sdm_channel[] = {
-       183, 185, 43, 45, 54, 55, 57, 58, 102, 103, 105, 106, 115, 117, 126, 127, 129, 130, 139, 141, 150, 151, 153, 154, 163, 165
+       183, 185,  43,  45,
+       54,   55,  57,  58,
+       102, 103, 105, 106,
+       115, 117, 126, 127,
+       129, 130, 139, 141,
+       150, 151, 153, 154,
+       163, 165
 };
 
 static const struct mt76x0_rf_switch_item mt76x0_rf_ext_pa_tab[] = {
-       { MT_RF(6, 45),         RF_A_BAND_LB,   0x63},
-       { MT_RF(6, 45),         RF_A_BAND_MB,   0x43},
-       { MT_RF(6, 45),         RF_A_BAND_HB,   0x33},
-       { MT_RF(6, 45),         RF_A_BAND_11J,  0x73},
-
-       { MT_RF(6, 50),         RF_A_BAND_LB,   0x02},
-       { MT_RF(6, 50),         RF_A_BAND_MB,   0x02},
-       { MT_RF(6, 50),         RF_A_BAND_HB,   0x02},
-       { MT_RF(6, 50),         RF_A_BAND_11J,  0x02},
-
-       { MT_RF(6, 51),         RF_A_BAND_LB,   0x02},
-       { MT_RF(6, 51),         RF_A_BAND_MB,   0x02},
-       { MT_RF(6, 51),         RF_A_BAND_HB,   0x02},
-       { MT_RF(6, 51),         RF_A_BAND_11J,  0x02},
-
-       { MT_RF(6, 52),         RF_A_BAND_LB,   0x08},
-       { MT_RF(6, 52),         RF_A_BAND_MB,   0x08},
-       { MT_RF(6, 52),         RF_A_BAND_HB,   0x08},
-       { MT_RF(6, 52),         RF_A_BAND_11J,  0x08},
-
-       { MT_RF(6, 53),         RF_A_BAND_LB,   0x08},
-       { MT_RF(6, 53),         RF_A_BAND_MB,   0x08},
-       { MT_RF(6, 53),         RF_A_BAND_HB,   0x08},
-       { MT_RF(6, 53),         RF_A_BAND_11J,  0x08},
-
-       { MT_RF(6, 54),         RF_A_BAND_LB,   0x0A},
-       { MT_RF(6, 54),         RF_A_BAND_MB,   0x0A},
-       { MT_RF(6, 54),         RF_A_BAND_HB,   0x0A},
-       { MT_RF(6, 54),         RF_A_BAND_11J,  0x0A},
-
-       { MT_RF(6, 55),         RF_A_BAND_LB,   0x0A},
-       { MT_RF(6, 55),         RF_A_BAND_MB,   0x0A},
-       { MT_RF(6, 55),         RF_A_BAND_HB,   0x0A},
-       { MT_RF(6, 55),         RF_A_BAND_11J,  0x0A},
-
-       { MT_RF(6, 56),         RF_A_BAND_LB,   0x05},
-       { MT_RF(6, 56),         RF_A_BAND_MB,   0x05},
-       { MT_RF(6, 56),         RF_A_BAND_HB,   0x05},
-       { MT_RF(6, 56),         RF_A_BAND_11J,  0x05},
-
-       { MT_RF(6, 57),         RF_A_BAND_LB,   0x05},
-       { MT_RF(6, 57),         RF_A_BAND_MB,   0x05},
-       { MT_RF(6, 57),         RF_A_BAND_HB,   0x05},
-       { MT_RF(6, 57),         RF_A_BAND_11J,  0x05},
-
-       { MT_RF(6, 58),         RF_A_BAND_LB,   0x05},
-       { MT_RF(6, 58),         RF_A_BAND_MB,   0x03},
-       { MT_RF(6, 58),         RF_A_BAND_HB,   0x02},
-       { MT_RF(6, 58),         RF_A_BAND_11J,  0x07},
-
-       { MT_RF(6, 59),         RF_A_BAND_LB,   0x05},
-       { MT_RF(6, 59),         RF_A_BAND_MB,   0x03},
-       { MT_RF(6, 59),         RF_A_BAND_HB,   0x02},
-       { MT_RF(6, 59),         RF_A_BAND_11J,  0x07},
+       { MT_RF(6, 45), RF_A_BAND_LB,   0x63 },
+       { MT_RF(6, 45), RF_A_BAND_MB,   0x43 },
+       { MT_RF(6, 45), RF_A_BAND_HB,   0x33 },
+       { MT_RF(6, 45), RF_A_BAND_11J,  0x73 },
+       { MT_RF(6, 50), RF_A_BAND_LB,   0x02 },
+       { MT_RF(6, 50), RF_A_BAND_MB,   0x02 },
+       { MT_RF(6, 50), RF_A_BAND_HB,   0x02 },
+       { MT_RF(6, 50), RF_A_BAND_11J,  0x02 },
+       { MT_RF(6, 51), RF_A_BAND_LB,   0x02 },
+       { MT_RF(6, 51), RF_A_BAND_MB,   0x02 },
+       { MT_RF(6, 51), RF_A_BAND_HB,   0x02 },
+       { MT_RF(6, 51), RF_A_BAND_11J,  0x02 },
+       { MT_RF(6, 52), RF_A_BAND_LB,   0x08 },
+       { MT_RF(6, 52), RF_A_BAND_MB,   0x08 },
+       { MT_RF(6, 52), RF_A_BAND_HB,   0x08 },
+       { MT_RF(6, 52), RF_A_BAND_11J,  0x08 },
+       { MT_RF(6, 53), RF_A_BAND_LB,   0x08 },
+       { MT_RF(6, 53), RF_A_BAND_MB,   0x08 },
+       { MT_RF(6, 53), RF_A_BAND_HB,   0x08 },
+       { MT_RF(6, 53), RF_A_BAND_11J,  0x08 },
+       { MT_RF(6, 54), RF_A_BAND_LB,   0x0A },
+       { MT_RF(6, 54), RF_A_BAND_MB,   0x0A },
+       { MT_RF(6, 54), RF_A_BAND_HB,   0x0A },
+       { MT_RF(6, 54), RF_A_BAND_11J,  0x0A },
+       { MT_RF(6, 55), RF_A_BAND_LB,   0x0A },
+       { MT_RF(6, 55), RF_A_BAND_MB,   0x0A },
+       { MT_RF(6, 55), RF_A_BAND_HB,   0x0A },
+       { MT_RF(6, 55), RF_A_BAND_11J,  0x0A },
+       { MT_RF(6, 56), RF_A_BAND_LB,   0x05 },
+       { MT_RF(6, 56), RF_A_BAND_MB,   0x05 },
+       { MT_RF(6, 56), RF_A_BAND_HB,   0x05 },
+       { MT_RF(6, 56), RF_A_BAND_11J,  0x05 },
+       { MT_RF(6, 57), RF_A_BAND_LB,   0x05 },
+       { MT_RF(6, 57), RF_A_BAND_MB,   0x05 },
+       { MT_RF(6, 57), RF_A_BAND_HB,   0x05 },
+       { MT_RF(6, 57), RF_A_BAND_11J,  0x05 },
+       { MT_RF(6, 58), RF_A_BAND_LB,   0x05 },
+       { MT_RF(6, 58), RF_A_BAND_MB,   0x03 },
+       { MT_RF(6, 58), RF_A_BAND_HB,   0x02 },
+       { MT_RF(6, 58), RF_A_BAND_11J,  0x07 },
+       { MT_RF(6, 59), RF_A_BAND_LB,   0x05 },
+       { MT_RF(6, 59), RF_A_BAND_MB,   0x03 },
+       { MT_RF(6, 59), RF_A_BAND_HB,   0x02 },
+       { MT_RF(6, 59), RF_A_BAND_11J,  0x07 },
 };
 
 #endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/mac.c b/drivers/net/wireless/mediatek/mt76/mt76x0/mac.c
deleted file mode 100644 (file)
index 7a422c5..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
- * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation
- *
- * 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 <linux/etherdevice.h>
-
-#include "mt76x0.h"
-#include "trace.h"
-
-void mt76x0_mac_set_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];
-       bool ht_rts[4] = {};
-       int i;
-
-       prot[0] = MT_PROT_NAV_SHORT |
-                 MT_PROT_TXOP_ALLOW_ALL |
-                 MT_PROT_RTS_THR_EN;
-       prot[1] = prot[0];
-       if (legacy_prot)
-               prot[1] |= MT_PROT_CTRL_CTS2SELF;
-
-       prot[2] = prot[4] = MT_PROT_NAV_SHORT | MT_PROT_TXOP_ALLOW_BW20;
-       prot[3] = prot[5] = MT_PROT_NAV_SHORT | MT_PROT_TXOP_ALLOW_ALL;
-
-       if (legacy_prot) {
-               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;
-       } else {
-               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;
-       }
-
-       switch (mode) {
-       case IEEE80211_HT_OP_MODE_PROTECTION_NONE:
-               break;
-
-       case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
-               ht_rts[0] = ht_rts[1] = ht_rts[2] = ht_rts[3] = true;
-               break;
-
-       case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
-               ht_rts[1] = ht_rts[3] = true;
-               break;
-
-       case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
-               ht_rts[0] = ht_rts[1] = ht_rts[2] = ht_rts[3] = true;
-               break;
-       }
-
-       if (non_gf)
-               ht_rts[2] = ht_rts[3] = true;
-
-       for (i = 0; i < 4; i++)
-               if (ht_rts[i])
-                       prot[i + 2] |= MT_PROT_CTRL_RTS_CTS;
-
-       for (i = 0; i < 6; i++)
-               mt76_wr(dev, MT_CCK_PROT_CFG + i * 4, prot[i]);
-}
-
-void mt76x0_mac_set_short_preamble(struct mt76x02_dev *dev, bool short_preamb)
-{
-       if (short_preamb)
-               mt76_set(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT);
-       else
-               mt76_clear(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT);
-}
-
-void mt76x0_mac_config_tsf(struct mt76x02_dev *dev, bool enable, int interval)
-{
-       u32 val = mt76_rr(dev, MT_BEACON_TIME_CFG);
-
-       val &= ~(MT_BEACON_TIME_CFG_TIMER_EN |
-                MT_BEACON_TIME_CFG_SYNC_MODE |
-                MT_BEACON_TIME_CFG_TBTT_EN);
-
-       if (!enable) {
-               mt76_wr(dev, MT_BEACON_TIME_CFG, val);
-               return;
-       }
-
-       val &= ~MT_BEACON_TIME_CFG_INTVAL;
-       val |= FIELD_PREP(MT_BEACON_TIME_CFG_INTVAL, interval << 4) |
-               MT_BEACON_TIME_CFG_TIMER_EN |
-               MT_BEACON_TIME_CFG_SYNC_MODE |
-               MT_BEACON_TIME_CFG_TBTT_EN;
-}
-
-static void mt76x0_check_mac_err(struct mt76x02_dev *dev)
-{
-       u32 val = mt76_rr(dev, 0x10f4);
-
-       if (!(val & BIT(29)) || !(val & (BIT(7) | BIT(5))))
-               return;
-
-       dev_err(dev->mt76.dev, "Error: MAC specific condition occurred\n");
-
-       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_RESET_CSR);
-}
-void mt76x0_mac_work(struct work_struct *work)
-{
-       struct mt76x02_dev *dev = container_of(work, struct mt76x02_dev,
-                                              mac_work.work);
-       struct {
-               u32 addr_base;
-               u32 span;
-               u64 *stat_base;
-       } spans[] = {
-               { MT_RX_STAT_0, 3,      dev->stats.rx_stat },
-               { MT_TX_STA_0,  3,      dev->stats.tx_stat },
-               { MT_TX_AGG_STAT,       1,      dev->stats.aggr_stat },
-               { MT_MPDU_DENSITY_CNT,  1,      dev->stats.zero_len_del },
-               { MT_TX_AGG_CNT_BASE0,  8,      &dev->stats.aggr_n[0] },
-               { MT_TX_AGG_CNT_BASE1,  8,      &dev->stats.aggr_n[16] },
-       };
-       u32 sum, n;
-       int i, j, k;
-
-       /* Note: using MCU_RANDOM_READ is actually slower then reading all the
-        *       registers by hand.  MCU takes ca. 20ms to complete read of 24
-        *       registers while reading them one by one will takes roughly
-        *       24*200us =~ 5ms.
-        */
-
-       k = 0;
-       n = 0;
-       sum = 0;
-       for (i = 0; i < ARRAY_SIZE(spans); i++)
-               for (j = 0; j < spans[i].span; j++) {
-                       u32 val = mt76_rr(dev, spans[i].addr_base + j * 4);
-
-                       spans[i].stat_base[j * 2] += val & 0xffff;
-                       spans[i].stat_base[j * 2 + 1] += val >> 16;
-
-                       /* Calculate average AMPDU length */
-                       if (spans[i].addr_base != MT_TX_AGG_CNT_BASE0 &&
-                           spans[i].addr_base != MT_TX_AGG_CNT_BASE1)
-                               continue;
-
-                       n += (val >> 16) + (val & 0xffff);
-                       sum += (val & 0xffff) * (1 + k * 2) +
-                               (val >> 16) * (2 + k * 2);
-                       k++;
-               }
-
-       atomic_set(&dev->avg_ampdu_len, n ? DIV_ROUND_CLOSEST(sum, n) : 1);
-
-       mt76x0_check_mac_err(dev);
-
-       ieee80211_queue_delayed_work(dev->mt76.hw, &dev->mac_work, 10 * HZ);
-}
-
-void mt76x0_mac_set_ampdu_factor(struct mt76x02_dev *dev)
-{
-       struct ieee80211_sta *sta;
-       struct mt76_wcid *wcid;
-       void *msta;
-       u8 min_factor = 3;
-       int i;
-
-       rcu_read_lock();
-       for (i = 0; i < ARRAY_SIZE(dev->mt76.wcid); i++) {
-               wcid = rcu_dereference(dev->mt76.wcid[i]);
-               if (!wcid)
-                       continue;
-
-               msta = container_of(wcid, struct mt76x02_sta, wcid);
-               sta = container_of(msta, struct ieee80211_sta, drv_priv);
-
-               min_factor = min(min_factor, sta->ht_cap.ampdu_factor);
-       }
-       rcu_read_unlock();
-
-       mt76_wr(dev, MT_MAX_LEN_CFG, 0xa0fff |
-                  FIELD_PREP(MT_MAX_LEN_CFG_AMPDU, min_factor));
-}
index 9273d2d..a803a9b 100644 (file)
@@ -22,9 +22,23 @@ mt76x0_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef)
        int ret;
 
        cancel_delayed_work_sync(&dev->cal_work);
+       if (mt76_is_mmio(dev)) {
+               tasklet_disable(&dev->pre_tbtt_tasklet);
+               tasklet_disable(&dev->dfs_pd.dfs_tasklet);
+       }
 
        mt76_set_channel(&dev->mt76);
        ret = mt76x0_phy_set_channel(dev, chandef);
+
+       /* channel cycle counters read-and-clear */
+       mt76_rr(dev, MT_CH_IDLE);
+       mt76_rr(dev, MT_CH_BUSY);
+
+       if (mt76_is_mmio(dev)) {
+               mt76x02_dfs_init_params(dev);
+               tasklet_enable(&dev->pre_tbtt_tasklet);
+               tasklet_enable(&dev->dfs_pd.dfs_tasklet);
+       }
        mt76_txq_schedule_all(&dev->mt76);
 
        return ret;
@@ -64,89 +78,3 @@ int mt76x0_config(struct ieee80211_hw *hw, u32 changed)
        return ret;
 }
 EXPORT_SYMBOL_GPL(mt76x0_config);
-
-static void
-mt76x0_addr_wr(struct mt76x02_dev *dev, const u32 offset, const u8 *addr)
-{
-       mt76_wr(dev, offset, get_unaligned_le32(addr));
-       mt76_wr(dev, offset + 4, addr[4] | addr[5] << 8);
-}
-
-void mt76x0_bss_info_changed(struct ieee80211_hw *hw,
-                            struct ieee80211_vif *vif,
-                            struct ieee80211_bss_conf *info, u32 changed)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       mutex_lock(&dev->mt76.mutex);
-
-       if (changed & BSS_CHANGED_BSSID) {
-               mt76x0_addr_wr(dev, MT_MAC_BSSID_DW0, info->bssid);
-
-               /* Note: this is a hack because beacon_int is not changed
-                *       on leave nor is any more appropriate event generated.
-                *       rt2x00 doesn't seem to be bothered though.
-                */
-               if (is_zero_ether_addr(info->bssid))
-                       mt76x0_mac_config_tsf(dev, false, 0);
-       }
-
-       if (changed & BSS_CHANGED_BASIC_RATES) {
-               mt76_wr(dev, MT_LEGACY_BASIC_RATE, info->basic_rates);
-               mt76_wr(dev, MT_VHT_HT_FBK_CFG0, 0x65432100);
-               mt76_wr(dev, MT_VHT_HT_FBK_CFG1, 0xedcba980);
-               mt76_wr(dev, MT_LG_FBK_CFG0, 0xedcba988);
-               mt76_wr(dev, MT_LG_FBK_CFG1, 0x00002100);
-       }
-
-       if (changed & BSS_CHANGED_BEACON_INT)
-               mt76x0_mac_config_tsf(dev, true, info->beacon_int);
-
-       if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
-               mt76x0_mac_set_protection(dev, info->use_cts_prot,
-                                          info->ht_operation_mode);
-
-       if (changed & BSS_CHANGED_ERP_PREAMBLE)
-               mt76x0_mac_set_short_preamble(dev, info->use_short_preamble);
-
-       if (changed & BSS_CHANGED_ERP_SLOT) {
-               int slottime = info->use_short_slot ? 9 : 20;
-
-               mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG,
-                              MT_BKOFF_SLOT_CFG_SLOTTIME, slottime);
-       }
-
-       if (changed & BSS_CHANGED_ASSOC)
-               mt76x0_phy_recalibrate_after_assoc(dev);
-
-       mutex_unlock(&dev->mt76.mutex);
-}
-EXPORT_SYMBOL_GPL(mt76x0_bss_info_changed);
-
-void mt76x0_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                   const u8 *mac_addr)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       set_bit(MT76_SCANNING, &dev->mt76.state);
-}
-EXPORT_SYMBOL_GPL(mt76x0_sw_scan);
-
-void mt76x0_sw_scan_complete(struct ieee80211_hw *hw,
-                            struct ieee80211_vif *vif)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       clear_bit(MT76_SCANNING, &dev->mt76.state);
-}
-EXPORT_SYMBOL_GPL(mt76x0_sw_scan_complete);
-
-int mt76x0_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       mt76_rmw_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH, value);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(mt76x0_set_rts_threshold);
index 2187baf..46629f6 100644 (file)
 #include "../mt76x02.h"
 #include "eeprom.h"
 
-#define MT_CALIBRATE_INTERVAL          (4 * HZ)
+#define MT7610E_FIRMWARE               "mediatek/mt7610e.bin"
+#define MT7650E_FIRMWARE               "mediatek/mt7650e.bin"
+
+#define MT7610U_FIRMWARE               "mediatek/mt7610u.bin"
 
 #define MT_USB_AGGR_SIZE_LIMIT         21 /* * 1024B */
 #define MT_USB_AGGR_TIMEOUT            0x80 /* * 33ns */
 
 static inline bool is_mt7610e(struct mt76x02_dev *dev)
 {
-       /* TODO */
-       return false;
+       if (!mt76_is_mmio(dev))
+               return false;
+
+       return mt76_chip(&dev->mt76) == 0x7610;
 }
 
-void mt76x0_init_debugfs(struct mt76x02_dev *dev);
+static inline bool is_mt7630(struct mt76x02_dev *dev)
+{
+       return mt76_chip(&dev->mt76) == 0x7630;
+}
 
 /* Init */
 struct mt76x02_dev *
@@ -54,30 +62,12 @@ int mt76x0_mac_start(struct mt76x02_dev *dev);
 void mt76x0_mac_stop(struct mt76x02_dev *dev);
 
 int mt76x0_config(struct ieee80211_hw *hw, u32 changed);
-void mt76x0_bss_info_changed(struct ieee80211_hw *hw,
-                            struct ieee80211_vif *vif,
-                            struct ieee80211_bss_conf *info, u32 changed);
-void mt76x0_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                   const u8 *mac_addr);
-void mt76x0_sw_scan_complete(struct ieee80211_hw *hw,
-                            struct ieee80211_vif *vif);
-int mt76x0_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
 
 /* PHY */
 void mt76x0_phy_init(struct mt76x02_dev *dev);
-int mt76x0_wait_bbp_ready(struct mt76x02_dev *dev);
+int mt76x0_phy_wait_bbp_ready(struct mt76x02_dev *dev);
 int mt76x0_phy_set_channel(struct mt76x02_dev *dev,
                            struct cfg80211_chan_def *chandef);
-void mt76x0_phy_recalibrate_after_assoc(struct mt76x02_dev *dev);
 void mt76x0_phy_set_txpower(struct mt76x02_dev *dev);
 void mt76x0_phy_calibrate(struct mt76x02_dev *dev, bool power_on);
-
-/* MAC */
-void mt76x0_mac_work(struct work_struct *work);
-void mt76x0_mac_set_protection(struct mt76x02_dev *dev, bool legacy_prot,
-                               int ht_mode);
-void mt76x0_mac_set_short_preamble(struct mt76x02_dev *dev, bool short_preamb);
-void mt76x0_mac_config_tsf(struct mt76x02_dev *dev, bool enable, int interval);
-void mt76x0_mac_set_ampdu_factor(struct mt76x02_dev *dev);
-
 #endif
index 522c860..d895b6f 100644 (file)
@@ -68,6 +68,19 @@ static void mt76x0e_stop(struct ieee80211_hw *hw)
        mutex_unlock(&dev->mt76.mutex);
 }
 
+static void
+mt76x0e_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+             u32 queues, bool drop)
+{
+}
+
+static int
+mt76x0e_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+               bool set)
+{
+       return 0;
+}
+
 static const struct ieee80211_ops mt76x0e_ops = {
        .tx = mt76x02_tx,
        .start = mt76x0e_start,
@@ -76,15 +89,22 @@ static const struct ieee80211_ops mt76x0e_ops = {
        .remove_interface = mt76x02_remove_interface,
        .config = mt76x0_config,
        .configure_filter = mt76x02_configure_filter,
-       .sta_add = mt76x02_sta_add,
-       .sta_remove = mt76x02_sta_remove,
+       .bss_info_changed = mt76x02_bss_info_changed,
+       .sta_state = mt76_sta_state,
        .set_key = mt76x02_set_key,
        .conf_tx = mt76x02_conf_tx,
-       .sw_scan_start = mt76x0_sw_scan,
-       .sw_scan_complete = mt76x0_sw_scan_complete,
+       .sw_scan_start = mt76x02_sw_scan,
+       .sw_scan_complete = mt76x02_sw_scan_complete,
        .ampdu_action = mt76x02_ampdu_action,
        .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,
+       .flush = mt76x0e_flush,
+       .set_tim = mt76x0e_set_tim,
+       .release_buffered_frames = mt76_release_buffered_frames,
+       .set_coverage_class = mt76x02_set_coverage_class,
+       .set_rts_threshold = mt76x02_set_rts_threshold,
 };
 
 static int mt76x0e_register_device(struct mt76x02_dev *dev)
@@ -135,10 +155,14 @@ mt76x0e_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        static const struct mt76_driver_ops drv_ops = {
                .txwi_size = sizeof(struct mt76x02_txwi),
+               .update_survey = mt76x02_update_channel,
                .tx_prepare_skb = mt76x02_tx_prepare_skb,
                .tx_complete_skb = mt76x02_tx_complete_skb,
                .rx_skb = mt76x02_queue_rx_skb,
                .rx_poll_complete = mt76x02_rx_poll_complete,
+               .sta_ps = mt76x02_sta_ps,
+               .sta_add = mt76x02_sta_add,
+               .sta_remove = mt76x02_sta_remove,
        };
        struct mt76x02_dev *dev;
        int ret;
@@ -185,6 +209,7 @@ error:
 static void mt76x0e_cleanup(struct mt76x02_dev *dev)
 {
        clear_bit(MT76_STATE_INITIALIZED, &dev->mt76.state);
+       tasklet_disable(&dev->pre_tbtt_tasklet);
        mt76x0_chip_onoff(dev, false, false);
        mt76x0e_stop_hw(dev);
        mt76x02_dma_cleanup(dev);
@@ -209,6 +234,8 @@ static const struct pci_device_id mt76x0e_device_table[] = {
 };
 
 MODULE_DEVICE_TABLE(pci, mt76x0e_device_table);
+MODULE_FIRMWARE(MT7610E_FIRMWARE);
+MODULE_FIRMWARE(MT7650E_FIRMWARE);
 MODULE_LICENSE("Dual BSD/GPL");
 
 static struct pci_driver mt76x0e_driver = {
index 5698612..490c186 100644 (file)
@@ -19,9 +19,6 @@
 #include "mt76x0.h"
 #include "mcu.h"
 
-#define MT7610E_FIRMWARE       "mediatek/mt7610e.bin"
-#define MT7650E_FIRMWARE       "mediatek/mt7650e.bin"
-
 #define MT_MCU_IVB_ADDR                (MT_MCU_ILM_ADDR + 0x54000 - MT_MCU_IVB_SIZE)
 
 static int mt76x0e_load_firmware(struct mt76x02_dev *dev)
@@ -130,7 +127,6 @@ out:
 int mt76x0e_mcu_init(struct mt76x02_dev *dev)
 {
        static const struct mt76_mcu_ops mt76x0e_mcu_ops = {
-               .mcu_msg_alloc = mt76x02_mcu_msg_alloc,
                .mcu_send_msg = mt76x02_mcu_msg_send,
        };
        int err;
index cf02495..1eb1a80 100644 (file)
@@ -20,7 +20,6 @@
 #include "mt76x0.h"
 #include "mcu.h"
 #include "eeprom.h"
-#include "trace.h"
 #include "phy.h"
 #include "initvals.h"
 #include "initvals_phy.h"
@@ -49,12 +48,12 @@ mt76x0_rf_csr_wr(struct mt76x02_dev *dev, u32 offset, u8 value)
        }
 
        mt76_wr(dev, MT_RF_CSR_CFG,
-                  FIELD_PREP(MT_RF_CSR_CFG_DATA, value) |
-                  FIELD_PREP(MT_RF_CSR_CFG_REG_BANK, bank) |
-                  FIELD_PREP(MT_RF_CSR_CFG_REG_ID, reg) |
-                  MT_RF_CSR_CFG_WR |
-                  MT_RF_CSR_CFG_KICK);
-       trace_mt76x0_rf_write(&dev->mt76, bank, offset, value);
+               FIELD_PREP(MT_RF_CSR_CFG_DATA, value) |
+               FIELD_PREP(MT_RF_CSR_CFG_REG_BANK, bank) |
+               FIELD_PREP(MT_RF_CSR_CFG_REG_ID, reg) |
+               MT_RF_CSR_CFG_WR |
+               MT_RF_CSR_CFG_KICK);
+
 out:
        mutex_unlock(&dev->phy_mutex);
 
@@ -86,19 +85,18 @@ static int mt76x0_rf_csr_rr(struct mt76x02_dev *dev, u32 offset)
                goto out;
 
        mt76_wr(dev, MT_RF_CSR_CFG,
-                  FIELD_PREP(MT_RF_CSR_CFG_REG_BANK, bank) |
-                  FIELD_PREP(MT_RF_CSR_CFG_REG_ID, reg) |
-                  MT_RF_CSR_CFG_KICK);
+               FIELD_PREP(MT_RF_CSR_CFG_REG_BANK, bank) |
+               FIELD_PREP(MT_RF_CSR_CFG_REG_ID, reg) |
+               MT_RF_CSR_CFG_KICK);
 
        if (!mt76_poll(dev, MT_RF_CSR_CFG, MT_RF_CSR_CFG_KICK, 0, 100))
                goto out;
 
        val = mt76_rr(dev, MT_RF_CSR_CFG);
        if (FIELD_GET(MT_RF_CSR_CFG_REG_ID, val) == reg &&
-           FIELD_GET(MT_RF_CSR_CFG_REG_BANK, val) == bank) {
+           FIELD_GET(MT_RF_CSR_CFG_REG_BANK, val) == bank)
                ret = FIELD_GET(MT_RF_CSR_CFG_DATA, val);
-               trace_mt76x0_rf_read(&dev->mt76, bank, offset, ret);
-       }
+
 out:
        mutex_unlock(&dev->phy_mutex);
 
@@ -110,7 +108,7 @@ out:
 }
 
 static int
-rf_wr(struct mt76x02_dev *dev, u32 offset, u8 val)
+mt76x0_rf_wr(struct mt76x02_dev *dev, u32 offset, u8 val)
 {
        if (mt76_is_usb(dev)) {
                struct mt76_reg_pair pair = {
@@ -126,8 +124,7 @@ rf_wr(struct mt76x02_dev *dev, u32 offset, u8 val)
        }
 }
 
-static int
-rf_rr(struct mt76x02_dev *dev, u32 offset)
+static int mt76x0_rf_rr(struct mt76x02_dev *dev, u32 offset)
 {
        int ret;
        u32 val;
@@ -149,38 +146,36 @@ rf_rr(struct mt76x02_dev *dev, u32 offset)
 }
 
 static int
-rf_rmw(struct mt76x02_dev *dev, u32 offset, u8 mask, u8 val)
+mt76x0_rf_rmw(struct mt76x02_dev *dev, u32 offset, u8 mask, u8 val)
 {
        int ret;
 
-       ret = rf_rr(dev, offset);
+       ret = mt76x0_rf_rr(dev, offset);
        if (ret < 0)
                return ret;
+
        val |= ret & ~mask;
-       ret = rf_wr(dev, offset, val);
-       if (ret)
-               return ret;
 
-       return val;
+       ret = mt76x0_rf_wr(dev, offset, val);
+       return ret ? ret : val;
 }
 
 static int
-rf_set(struct mt76x02_dev *dev, u32 offset, u8 val)
+mt76x0_rf_set(struct mt76x02_dev *dev, u32 offset, u8 val)
 {
-       return rf_rmw(dev, offset, 0, val);
+       return mt76x0_rf_rmw(dev, offset, 0, val);
 }
 
-#if 0
 static int
-rf_clear(struct mt76x02_dev *dev, u32 offset, u8 mask)
+mt76x0_rf_clear(struct mt76x02_dev *dev, u32 offset, u8 mask)
 {
-       return rf_rmw(dev, offset, mask, 0);
+       return mt76x0_rf_rmw(dev, offset, mask, 0);
 }
-#endif
 
 static void
-mt76x0_rf_csr_wr_rp(struct mt76x02_dev *dev, const struct mt76_reg_pair *data,
-                   int n)
+mt76x0_phy_rf_csr_wr_rp(struct mt76x02_dev *dev,
+                       const struct mt76_reg_pair *data,
+                       int n)
 {
        while (n-- > 0) {
                mt76x0_rf_csr_wr(dev, data->reg, data->value);
@@ -190,12 +185,12 @@ mt76x0_rf_csr_wr_rp(struct mt76x02_dev *dev, const struct mt76_reg_pair *data,
 
 #define RF_RANDOM_WRITE(dev, tab) do {                                 \
        if (mt76_is_mmio(dev))                                          \
-               mt76x0_rf_csr_wr_rp(dev, tab, ARRAY_SIZE(tab));         \
+               mt76x0_phy_rf_csr_wr_rp(dev, tab, ARRAY_SIZE(tab));     \
        else                                                            \
                mt76_wr_rp(dev, MT_MCU_MEMMAP_RF, tab, ARRAY_SIZE(tab));\
 } while (0)
 
-int mt76x0_wait_bbp_ready(struct mt76x02_dev *dev)
+int mt76x0_phy_wait_bbp_ready(struct mt76x02_dev *dev)
 {
        int i = 20;
        u32 val;
@@ -215,62 +210,6 @@ int mt76x0_wait_bbp_ready(struct mt76x02_dev *dev)
        return 0;
 }
 
-static void mt76x0_vco_cal(struct mt76x02_dev *dev, u8 channel)
-{
-       u8 val;
-
-       val = rf_rr(dev, MT_RF(0, 4));
-       if ((val & 0x70) != 0x30)
-               return;
-
-       /*
-        * Calibration Mode - Open loop, closed loop, and amplitude:
-        * B0.R06.[0]: 1
-        * B0.R06.[3:1] bp_close_code: 100
-        * B0.R05.[7:0] bp_open_code: 0x0
-        * B0.R04.[2:0] cal_bits: 000
-        * B0.R03.[2:0] startup_time: 011
-        * B0.R03.[6:4] settle_time:
-        *  80MHz channel: 110
-        *  40MHz channel: 101
-        *  20MHz channel: 100
-        */
-       val = rf_rr(dev, MT_RF(0, 6));
-       val &= ~0xf;
-       val |= 0x09;
-       rf_wr(dev, MT_RF(0, 6), val);
-
-       val = rf_rr(dev, MT_RF(0, 5));
-       if (val != 0)
-               rf_wr(dev, MT_RF(0, 5), 0x0);
-
-       val = rf_rr(dev, MT_RF(0, 4));
-       val &= ~0x07;
-       rf_wr(dev, MT_RF(0, 4), val);
-
-       val = rf_rr(dev, MT_RF(0, 3));
-       val &= ~0x77;
-       if (channel == 1 || channel == 7 || channel == 9 || channel >= 13) {
-               val |= 0x63;
-       } else if (channel == 3 || channel == 4 || channel == 10) {
-               val |= 0x53;
-       } else if (channel == 2 || channel == 5 || channel == 6 ||
-                  channel == 8 || channel == 11 || channel == 12) {
-               val |= 0x43;
-       } else {
-               WARN(1, "Unknown channel %u\n", channel);
-               return;
-       }
-       rf_wr(dev, MT_RF(0, 3), val);
-
-       /* TODO replace by mt76x0_rf_set(dev, MT_RF(0, 4), BIT(7)); */
-       val = rf_rr(dev, MT_RF(0, 4));
-       val = ((val & ~(0x80)) | 0x80);
-       rf_wr(dev, MT_RF(0, 4), val);
-
-       msleep(2);
-}
-
 static void
 mt76x0_phy_set_band(struct mt76x02_dev *dev, enum nl80211_band band)
 {
@@ -278,8 +217,8 @@ mt76x0_phy_set_band(struct mt76x02_dev *dev, enum nl80211_band band)
        case NL80211_BAND_2GHZ:
                RF_RANDOM_WRITE(dev, mt76x0_rf_2g_channel_0_tab);
 
-               rf_wr(dev, MT_RF(5, 0), 0x45);
-               rf_wr(dev, MT_RF(6, 0), 0x44);
+               mt76x0_rf_wr(dev, MT_RF(5, 0), 0x45);
+               mt76x0_rf_wr(dev, MT_RF(6, 0), 0x44);
 
                mt76_wr(dev, MT_TX_ALC_VGA3, 0x00050007);
                mt76_wr(dev, MT_TX0_RF_GAIN_CORR, 0x003E0002);
@@ -287,8 +226,8 @@ mt76x0_phy_set_band(struct mt76x02_dev *dev, enum nl80211_band band)
        case NL80211_BAND_5GHZ:
                RF_RANDOM_WRITE(dev, mt76x0_rf_5g_channel_0_tab);
 
-               rf_wr(dev, MT_RF(5, 0), 0x44);
-               rf_wr(dev, MT_RF(6, 0), 0x45);
+               mt76x0_rf_wr(dev, MT_RF(5, 0), 0x44);
+               mt76x0_rf_wr(dev, MT_RF(6, 0), 0x45);
 
                mt76_wr(dev, MT_TX_ALC_VGA3, 0x00000005);
                mt76_wr(dev, MT_TX0_RF_GAIN_CORR, 0x01010102);
@@ -301,18 +240,17 @@ mt76x0_phy_set_band(struct mt76x02_dev *dev, enum nl80211_band band)
 static void
 mt76x0_phy_set_chan_rf_params(struct mt76x02_dev *dev, u8 channel, u16 rf_bw_band)
 {
+       const struct mt76x0_freq_item *freq_item;
        u16 rf_band = rf_bw_band & 0xff00;
        u16 rf_bw = rf_bw_band & 0x00ff;
        enum nl80211_band band;
+       bool b_sdm = false;
        u32 mac_reg;
-       u8 rf_val;
        int i;
-       bool bSDM = false;
-       const struct mt76x0_freq_item *freq_item;
 
        for (i = 0; i < ARRAY_SIZE(mt76x0_sdm_channel); i++) {
                if (channel == mt76x0_sdm_channel[i]) {
-                       bSDM = true;
+                       b_sdm = true;
                        break;
                }
        }
@@ -321,108 +259,84 @@ mt76x0_phy_set_chan_rf_params(struct mt76x02_dev *dev, u8 channel, u16 rf_bw_ban
                if (channel == mt76x0_frequency_plan[i].channel) {
                        rf_band = mt76x0_frequency_plan[i].band;
 
-                       if (bSDM)
+                       if (b_sdm)
                                freq_item = &(mt76x0_sdm_frequency_plan[i]);
                        else
                                freq_item = &(mt76x0_frequency_plan[i]);
 
-                       rf_wr(dev, MT_RF(0, 37), freq_item->pllR37);
-                       rf_wr(dev, MT_RF(0, 36), freq_item->pllR36);
-                       rf_wr(dev, MT_RF(0, 35), freq_item->pllR35);
-                       rf_wr(dev, MT_RF(0, 34), freq_item->pllR34);
-                       rf_wr(dev, MT_RF(0, 33), freq_item->pllR33);
+                       mt76x0_rf_wr(dev, MT_RF(0, 37), freq_item->pllR37);
+                       mt76x0_rf_wr(dev, MT_RF(0, 36), freq_item->pllR36);
+                       mt76x0_rf_wr(dev, MT_RF(0, 35), freq_item->pllR35);
+                       mt76x0_rf_wr(dev, MT_RF(0, 34), freq_item->pllR34);
+                       mt76x0_rf_wr(dev, MT_RF(0, 33), freq_item->pllR33);
 
-                       rf_val = rf_rr(dev, MT_RF(0, 32));
-                       rf_val &= ~0xE0;
-                       rf_val |= freq_item->pllR32_b7b5;
-                       rf_wr(dev, MT_RF(0, 32), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 32), 0xe0,
+                                     freq_item->pllR32_b7b5);
 
                        /* R32<4:0> pll_den: (Denomina - 8) */
-                       rf_val = rf_rr(dev, MT_RF(0, 32));
-                       rf_val &= ~0x1F;
-                       rf_val |= freq_item->pllR32_b4b0;
-                       rf_wr(dev, MT_RF(0, 32), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 32), MT_RF_PLL_DEN_MASK,
+                                     freq_item->pllR32_b4b0);
 
                        /* R31<7:5> */
-                       rf_val = rf_rr(dev, MT_RF(0, 31));
-                       rf_val &= ~0xE0;
-                       rf_val |= freq_item->pllR31_b7b5;
-                       rf_wr(dev, MT_RF(0, 31), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 31), 0xe0,
+                                     freq_item->pllR31_b7b5);
 
                        /* R31<4:0> pll_k(Nominator) */
-                       rf_val = rf_rr(dev, MT_RF(0, 31));
-                       rf_val &= ~0x1F;
-                       rf_val |= freq_item->pllR31_b4b0;
-                       rf_wr(dev, MT_RF(0, 31), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 31), MT_RF_PLL_K_MASK,
+                                     freq_item->pllR31_b4b0);
 
                        /* R30<7> sdm_reset_n */
-                       rf_val = rf_rr(dev, MT_RF(0, 30));
-                       rf_val &= ~0x80;
-                       if (bSDM) {
-                               rf_wr(dev, MT_RF(0, 30), rf_val);
-                               rf_val |= 0x80;
-                               rf_wr(dev, MT_RF(0, 30), rf_val);
+                       if (b_sdm) {
+                               mt76x0_rf_clear(dev, MT_RF(0, 30),
+                                               MT_RF_SDM_RESET_MASK);
+                               mt76x0_rf_set(dev, MT_RF(0, 30),
+                                             MT_RF_SDM_RESET_MASK);
                        } else {
-                               rf_val |= freq_item->pllR30_b7;
-                               rf_wr(dev, MT_RF(0, 30), rf_val);
+                               mt76x0_rf_rmw(dev, MT_RF(0, 30),
+                                             MT_RF_SDM_RESET_MASK,
+                                             freq_item->pllR30_b7);
                        }
 
                        /* R30<6:2> sdmmash_prbs,sin */
-                       rf_val = rf_rr(dev, MT_RF(0, 30));
-                       rf_val &= ~0x7C;
-                       rf_val |= freq_item->pllR30_b6b2;
-                       rf_wr(dev, MT_RF(0, 30), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 30),
+                                     MT_RF_SDM_MASH_PRBS_MASK,
+                                     freq_item->pllR30_b6b2);
 
                        /* R30<1> sdm_bp */
-                       rf_val = rf_rr(dev, MT_RF(0, 30));
-                       rf_val &= ~0x02;
-                       rf_val |= (freq_item->pllR30_b1 << 1);
-                       rf_wr(dev, MT_RF(0, 30), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 30), MT_RF_SDM_BP_MASK,
+                                     freq_item->pllR30_b1 << 1);
 
                        /* R30<0> R29<7:0> (hex) pll_n */
-                       rf_val = freq_item->pll_n & 0x00FF;
-                       rf_wr(dev, MT_RF(0, 29), rf_val);
+                       mt76x0_rf_wr(dev, MT_RF(0, 29),
+                                    freq_item->pll_n & 0xff);
 
-                       rf_val = rf_rr(dev, MT_RF(0, 30));
-                       rf_val &= ~0x1;
-                       rf_val |= ((freq_item->pll_n >> 8) & 0x0001);
-                       rf_wr(dev, MT_RF(0, 30), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 30), 0x1,
+                                     (freq_item->pll_n >> 8) & 0x1);
 
                        /* R28<7:6> isi_iso */
-                       rf_val = rf_rr(dev, MT_RF(0, 28));
-                       rf_val &= ~0xC0;
-                       rf_val |= freq_item->pllR28_b7b6;
-                       rf_wr(dev, MT_RF(0, 28), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 28), MT_RF_ISI_ISO_MASK,
+                                     freq_item->pllR28_b7b6);
 
                        /* R28<5:4> pfd_dly */
-                       rf_val = rf_rr(dev, MT_RF(0, 28));
-                       rf_val &= ~0x30;
-                       rf_val |= freq_item->pllR28_b5b4;
-                       rf_wr(dev, MT_RF(0, 28), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 28), MT_RF_PFD_DLY_MASK,
+                                     freq_item->pllR28_b5b4);
 
                        /* R28<3:2> clksel option */
-                       rf_val = rf_rr(dev, MT_RF(0, 28));
-                       rf_val &= ~0x0C;
-                       rf_val |= freq_item->pllR28_b3b2;
-                       rf_wr(dev, MT_RF(0, 28), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 28), MT_RF_CLK_SEL_MASK,
+                                     freq_item->pllR28_b3b2);
 
                        /* R28<1:0> R27<7:0> R26<7:0> (hex) sdm_k */
-                       rf_val = freq_item->pll_sdm_k & 0x000000FF;
-                       rf_wr(dev, MT_RF(0, 26), rf_val);
-
-                       rf_val = ((freq_item->pll_sdm_k >> 8) & 0x000000FF);
-                       rf_wr(dev, MT_RF(0, 27), rf_val);
+                       mt76x0_rf_wr(dev, MT_RF(0, 26),
+                                    freq_item->pll_sdm_k & 0xff);
+                       mt76x0_rf_wr(dev, MT_RF(0, 27),
+                                    (freq_item->pll_sdm_k >> 8) & 0xff);
 
-                       rf_val = rf_rr(dev, MT_RF(0, 28));
-                       rf_val &= ~0x3;
-                       rf_val |= ((freq_item->pll_sdm_k >> 16) & 0x0003);
-                       rf_wr(dev, MT_RF(0, 28), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 28), 0x3,
+                                     (freq_item->pll_sdm_k >> 16) & 0x3);
 
                        /* R24<1:0> xo_div */
-                       rf_val = rf_rr(dev, MT_RF(0, 24));
-                       rf_val &= ~0x3;
-                       rf_val |= freq_item->pllR24_b1b0;
-                       rf_wr(dev, MT_RF(0, 24), rf_val);
+                       mt76x0_rf_rmw(dev, MT_RF(0, 24), MT_RF_XO_DIV_MASK,
+                                     freq_item->pllR24_b1b0);
 
                        break;
                }
@@ -430,25 +344,26 @@ mt76x0_phy_set_chan_rf_params(struct mt76x02_dev *dev, u8 channel, u16 rf_bw_ban
 
        for (i = 0; i < ARRAY_SIZE(mt76x0_rf_bw_switch_tab); i++) {
                if (rf_bw == mt76x0_rf_bw_switch_tab[i].bw_band) {
-                       rf_wr(dev, mt76x0_rf_bw_switch_tab[i].rf_bank_reg,
-                                  mt76x0_rf_bw_switch_tab[i].value);
+                       mt76x0_rf_wr(dev,
+                                    mt76x0_rf_bw_switch_tab[i].rf_bank_reg,
+                                    mt76x0_rf_bw_switch_tab[i].value);
                } else if ((rf_bw == (mt76x0_rf_bw_switch_tab[i].bw_band & 0xFF)) &&
                           (rf_band & mt76x0_rf_bw_switch_tab[i].bw_band)) {
-                       rf_wr(dev, mt76x0_rf_bw_switch_tab[i].rf_bank_reg,
-                                  mt76x0_rf_bw_switch_tab[i].value);
+                       mt76x0_rf_wr(dev,
+                                    mt76x0_rf_bw_switch_tab[i].rf_bank_reg,
+                                    mt76x0_rf_bw_switch_tab[i].value);
                }
        }
 
        for (i = 0; i < ARRAY_SIZE(mt76x0_rf_band_switch_tab); i++) {
                if (mt76x0_rf_band_switch_tab[i].bw_band & rf_band) {
-                       rf_wr(dev, mt76x0_rf_band_switch_tab[i].rf_bank_reg,
-                                  mt76x0_rf_band_switch_tab[i].value);
+                       mt76x0_rf_wr(dev,
+                                    mt76x0_rf_band_switch_tab[i].rf_bank_reg,
+                                    mt76x0_rf_band_switch_tab[i].value);
                }
        }
 
-       mac_reg = mt76_rr(dev, MT_RF_MISC);
-       mac_reg &= ~0xC; /* Clear 0x518[3:2] */
-       mt76_wr(dev, MT_RF_MISC, mac_reg);
+       mt76_clear(dev, MT_RF_MISC, 0xc);
 
        band = (rf_band & RF_G_BAND) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
        if (mt76x02_ext_pa_enabled(dev, band)) {
@@ -457,21 +372,17 @@ mt76x0_phy_set_chan_rf_params(struct mt76x02_dev *dev, u8 channel, u16 rf_bw_ban
                        [2]1'b1: enable external A band PA, 1'b0: disable external A band PA
                        [3]1'b1: enable external G band PA, 1'b0: disable external G band PA
                */
-               if (rf_band & RF_A_BAND) {
-                       mac_reg = mt76_rr(dev, MT_RF_MISC);
-                       mac_reg |= 0x4;
-                       mt76_wr(dev, MT_RF_MISC, mac_reg);
-               } else {
-                       mac_reg = mt76_rr(dev, MT_RF_MISC);
-                       mac_reg |= 0x8;
-                       mt76_wr(dev, MT_RF_MISC, mac_reg);
-               }
+               if (rf_band & RF_A_BAND)
+                       mt76_set(dev, MT_RF_MISC, BIT(2));
+               else
+                       mt76_set(dev, MT_RF_MISC, BIT(3));
 
                /* External PA */
                for (i = 0; i < ARRAY_SIZE(mt76x0_rf_ext_pa_tab); i++)
                        if (mt76x0_rf_ext_pa_tab[i].bw_band & rf_band)
-                               rf_wr(dev, mt76x0_rf_ext_pa_tab[i].rf_bank_reg,
-                                          mt76x0_rf_ext_pa_tab[i].value);
+                               mt76x0_rf_wr(dev,
+                                       mt76x0_rf_ext_pa_tab[i].rf_bank_reg,
+                                       mt76x0_rf_ext_pa_tab[i].value);
        }
 
        if (rf_band & RF_G_BAND) {
@@ -516,27 +427,53 @@ mt76x0_phy_set_chan_bbp_params(struct mt76x02_dev *dev, u16 rf_bw_band)
        }
 }
 
-static void mt76x0_ant_select(struct mt76x02_dev *dev)
+static void mt76x0_phy_ant_select(struct mt76x02_dev *dev)
 {
-       struct ieee80211_channel *chan = dev->mt76.chandef.chan;
-
-       /* single antenna mode */
-       if (chan->band == NL80211_BAND_2GHZ) {
-               mt76_rmw(dev, MT_COEXCFG3,
-                        BIT(5) | BIT(4) | BIT(3) | BIT(2), BIT(1));
-               mt76_rmw(dev, MT_WLAN_FUN_CTRL, BIT(5), BIT(6));
+       u16 ee_ant = mt76x02_eeprom_get(dev, MT_EE_ANTENNA);
+       u16 nic_conf2 = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_2);
+       u32 wlan, coex3, cmb;
+       bool ant_div;
+
+       wlan = mt76_rr(dev, MT_WLAN_FUN_CTRL);
+       cmb = mt76_rr(dev, MT_CMB_CTRL);
+       coex3 = mt76_rr(dev, MT_COEXCFG3);
+
+       cmb   &= ~(BIT(14) | BIT(12));
+       wlan  &= ~(BIT(6) | BIT(5));
+       coex3 &= ~GENMASK(5, 2);
+
+       if (ee_ant & MT_EE_ANTENNA_DUAL) {
+               /* dual antenna mode */
+               ant_div = !(nic_conf2 & MT_EE_NIC_CONF_2_ANT_OPT) &&
+                         (nic_conf2 & MT_EE_NIC_CONF_2_ANT_DIV);
+               if (ant_div)
+                       cmb |= BIT(12);
+               else
+                       coex3 |= BIT(4);
+               coex3 |= BIT(3);
+               if (dev->mt76.cap.has_2ghz)
+                       wlan |= BIT(6);
        } else {
-               mt76_rmw(dev, MT_COEXCFG3, BIT(5) | BIT(2),
-                        BIT(4) | BIT(3));
-               mt76_clear(dev, MT_WLAN_FUN_CTRL,
-                          BIT(6) | BIT(5));
+               /* sigle antenna mode */
+               if (dev->mt76.cap.has_5ghz) {
+                       coex3 |= BIT(3) | BIT(4);
+               } else {
+                       wlan |= BIT(6);
+                       coex3 |= BIT(1);
+               }
        }
-       mt76_clear(dev, MT_CMB_CTRL, BIT(14) | BIT(12));
+
+       if (is_mt7630(dev))
+               cmb |= BIT(14) | BIT(11);
+
+       mt76_wr(dev, MT_WLAN_FUN_CTRL, wlan);
+       mt76_wr(dev, MT_CMB_CTRL, cmb);
        mt76_clear(dev, MT_COEXCFG0, BIT(2));
+       mt76_wr(dev, MT_COEXCFG3, coex3);
 }
 
 static void
-mt76x0_bbp_set_bw(struct mt76x02_dev *dev, enum nl80211_chan_width width)
+mt76x0_phy_bbp_set_bw(struct mt76x02_dev *dev, enum nl80211_chan_width width)
 {
        enum { BW_20 = 0, BW_40 = 1, BW_80 = 2, BW_10 = 4};
        int bw;
@@ -563,7 +500,346 @@ mt76x0_bbp_set_bw(struct mt76x02_dev *dev, enum nl80211_chan_width width)
                return ;
        }
 
-       mt76x02_mcu_function_select(dev, BW_SETTING, bw, false);
+       mt76x02_mcu_function_select(dev, BW_SETTING, bw);
+}
+
+static void mt76x0_phy_tssi_dc_calibrate(struct mt76x02_dev *dev)
+{
+       struct ieee80211_channel *chan = dev->mt76.chandef.chan;
+       u32 val;
+
+       if (chan->band == NL80211_BAND_5GHZ)
+               mt76x0_rf_clear(dev, MT_RF(0, 67), 0xf);
+
+       /* bypass ADDA control */
+       mt76_wr(dev, MT_RF_SETTING_0, 0x60002237);
+       mt76_wr(dev, MT_RF_BYPASS_0, 0xffffffff);
+
+       /* bbp sw reset */
+       mt76_set(dev, MT_BBP(CORE, 4), BIT(0));
+       usleep_range(500, 1000);
+       mt76_clear(dev, MT_BBP(CORE, 4), BIT(0));
+
+       val = (chan->band == NL80211_BAND_5GHZ) ? 0x80055 : 0x80050;
+       mt76_wr(dev, MT_BBP(CORE, 34), val);
+
+       /* enable TX with DAC0 input */
+       mt76_wr(dev, MT_BBP(TXBE, 6), BIT(31));
+
+       mt76_poll_msec(dev, MT_BBP(CORE, 34), BIT(4), 0, 200);
+       dev->cal.tssi_dc = mt76_rr(dev, MT_BBP(CORE, 35)) & 0xff;
+
+       /* stop bypass ADDA */
+       mt76_wr(dev, MT_RF_BYPASS_0, 0);
+       /* stop TX */
+       mt76_wr(dev, MT_BBP(TXBE, 6), 0);
+       /* bbp sw reset */
+       mt76_set(dev, MT_BBP(CORE, 4), BIT(0));
+       usleep_range(500, 1000);
+       mt76_clear(dev, MT_BBP(CORE, 4), BIT(0));
+
+       if (chan->band == NL80211_BAND_5GHZ)
+               mt76x0_rf_rmw(dev, MT_RF(0, 67), 0xf, 0x4);
+}
+
+static int
+mt76x0_phy_tssi_adc_calibrate(struct mt76x02_dev *dev, s16 *ltssi,
+                             u8 *info)
+{
+       struct ieee80211_channel *chan = dev->mt76.chandef.chan;
+       u32 val;
+
+       val = (chan->band == NL80211_BAND_5GHZ) ? 0x80055 : 0x80050;
+       mt76_wr(dev, MT_BBP(CORE, 34), val);
+
+       if (!mt76_poll_msec(dev, MT_BBP(CORE, 34), BIT(4), 0, 200)) {
+               mt76_clear(dev, MT_BBP(CORE, 34), BIT(4));
+               return -ETIMEDOUT;
+       }
+
+       *ltssi = mt76_rr(dev, MT_BBP(CORE, 35)) & 0xff;
+       if (chan->band == NL80211_BAND_5GHZ)
+               *ltssi += 128;
+
+       /* set packet info#1 mode */
+       mt76_wr(dev, MT_BBP(CORE, 34), 0x80041);
+       info[0] = mt76_rr(dev, MT_BBP(CORE, 35)) & 0xff;
+
+       /* set packet info#2 mode */
+       mt76_wr(dev, MT_BBP(CORE, 34), 0x80042);
+       info[1] = mt76_rr(dev, MT_BBP(CORE, 35)) & 0xff;
+
+       /* set packet info#3 mode */
+       mt76_wr(dev, MT_BBP(CORE, 34), 0x80043);
+       info[2] = mt76_rr(dev, MT_BBP(CORE, 35)) & 0xff;
+
+       return 0;
+}
+
+static u8 mt76x0_phy_get_rf_pa_mode(struct mt76x02_dev *dev,
+                                   int index, u8 tx_rate)
+{
+       u32 val, reg;
+
+       reg = (index == 1) ? MT_RF_PA_MODE_CFG1 : MT_RF_PA_MODE_CFG0;
+       val = mt76_rr(dev, reg);
+       return (val & (3 << (tx_rate * 2))) >> (tx_rate * 2);
+}
+
+static int
+mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode,
+                           u8 *info, s8 *target_power,
+                           s8 *target_pa_power)
+{
+       u8 tx_rate, cur_power;
+
+       cur_power = mt76_rr(dev, MT_TX_ALC_CFG_0) & MT_TX_ALC_CFG_0_CH_INIT_0;
+       switch (tx_mode) {
+       case 0:
+               /* cck rates */
+               tx_rate = (info[0] & 0x60) >> 5;
+               if (tx_rate > 3)
+                       return -EINVAL;
+
+               *target_power = cur_power + dev->mt76.rate_power.cck[tx_rate];
+               *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, tx_rate);
+               break;
+       case 1: {
+               u8 index;
+
+               /* ofdm rates */
+               tx_rate = (info[0] & 0xf0) >> 4;
+               switch (tx_rate) {
+               case 0xb:
+                       index = 0;
+                       break;
+               case 0xf:
+                       index = 1;
+                       break;
+               case 0xa:
+                       index = 2;
+                       break;
+               case 0xe:
+                       index = 3;
+                       break;
+               case 0x9:
+                       index = 4;
+                       break;
+               case 0xd:
+                       index = 5;
+                       break;
+               case 0x8:
+                       index = 6;
+                       break;
+               case 0xc:
+                       index = 7;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               *target_power = cur_power + dev->mt76.rate_power.ofdm[index];
+               *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, index + 4);
+               break;
+       }
+       case 4:
+               /* vht rates */
+               tx_rate = info[1] & 0xf;
+               if (tx_rate > 9)
+                       return -EINVAL;
+
+               *target_power = cur_power + dev->mt76.rate_power.vht[tx_rate];
+               *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 1, tx_rate);
+               break;
+       default:
+               /* ht rates */
+               tx_rate = info[1] & 0x7f;
+               if (tx_rate > 9)
+                       return -EINVAL;
+
+               *target_power = cur_power + dev->mt76.rate_power.ht[tx_rate];
+               *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 1, tx_rate);
+               break;
+       }
+
+       return 0;
+}
+
+static s16 mt76x0_phy_lin2db(u16 val)
+{
+       u32 mantissa = val << 4;
+       int ret, data;
+       s16 exp = -4;
+
+       while (mantissa < BIT(15)) {
+               mantissa <<= 1;
+               if (--exp < -20)
+                       return -10000;
+       }
+       while (mantissa > 0xffff) {
+               mantissa >>= 1;
+               if (++exp > 20)
+                       return -10000;
+       }
+
+       /* s(15,0) */
+       if (mantissa <= 47104)
+               data = mantissa + (mantissa >> 3) + (mantissa >> 4) - 38400;
+       else
+               data = mantissa - (mantissa >> 3) - (mantissa >> 6) - 23040;
+       data = max_t(int, 0, data);
+
+       ret = ((15 + exp) << 15) + data;
+       ret = (ret << 2) + (ret << 1) + (ret >> 6) + (ret >> 7);
+       return ret >> 10;
+}
+
+static int
+mt76x0_phy_get_delta_power(struct mt76x02_dev *dev, u8 tx_mode,
+                          s8 target_power, s8 target_pa_power,
+                          s16 ltssi)
+{
+       struct ieee80211_channel *chan = dev->mt76.chandef.chan;
+       int tssi_target = target_power << 12, tssi_slope;
+       int tssi_offset, tssi_db, ret;
+       u32 data;
+       u16 val;
+
+       if (chan->band == NL80211_BAND_5GHZ) {
+               u8 bound[7];
+               int i, err;
+
+               err = mt76x02_eeprom_copy(dev, MT_EE_TSSI_BOUND1, bound,
+                                         sizeof(bound));
+               if (err < 0)
+                       return err;
+
+               for (i = 0; i < ARRAY_SIZE(bound); i++) {
+                       if (chan->hw_value <= bound[i] || !bound[i])
+                               break;
+               }
+               val = mt76x02_eeprom_get(dev, MT_EE_TSSI_SLOPE_5G + i * 2);
+
+               tssi_offset = val >> 8;
+               if ((tssi_offset >= 64 && tssi_offset <= 127) ||
+                   (tssi_offset & BIT(7)))
+                       tssi_offset -= BIT(8);
+       } else {
+               val = mt76x02_eeprom_get(dev, MT_EE_TSSI_SLOPE_2G);
+
+               tssi_offset = val >> 8;
+               if (tssi_offset & BIT(7))
+                       tssi_offset -= BIT(8);
+       }
+       tssi_slope = val & 0xff;
+
+       switch (target_pa_power) {
+       case 1:
+               if (chan->band == NL80211_BAND_2GHZ)
+                       tssi_target += 29491; /* 3.6 * 8192 */
+               /* fall through */
+       case 0:
+               break;
+       default:
+               tssi_target += 4424; /* 0.54 * 8192 */
+               break;
+       }
+
+       if (!tx_mode) {
+               data = mt76_rr(dev, MT_BBP(CORE, 1));
+               if (is_mt7630(dev) && mt76_is_mmio(dev)) {
+                       int offset;
+
+                       /* 2.3 * 8192 or 1.5 * 8192 */
+                       offset = (data & BIT(5)) ? 18841 : 12288;
+                       tssi_target += offset;
+               } else if (data & BIT(5)) {
+                       /* 0.8 * 8192 */
+                       tssi_target += 6554;
+               }
+       }
+
+       data = mt76_rr(dev, MT_BBP(TXBE, 4));
+       switch (data & 0x3) {
+       case 1:
+               tssi_target -= 49152; /* -6db * 8192 */
+               break;
+       case 2:
+               tssi_target -= 98304; /* -12db * 8192 */
+               break;
+       case 3:
+               tssi_target += 49152; /* 6db * 8192 */
+               break;
+       default:
+               break;
+       }
+
+       tssi_db = mt76x0_phy_lin2db(ltssi - dev->cal.tssi_dc) * tssi_slope;
+       if (chan->band == NL80211_BAND_5GHZ) {
+               tssi_db += ((tssi_offset - 50) << 10); /* offset s4.3 */
+               tssi_target -= tssi_db;
+               if (ltssi > 254 && tssi_target > 0) {
+                       /* upper saturate */
+                       tssi_target = 0;
+               }
+       } else {
+               tssi_db += (tssi_offset << 9); /* offset s3.4 */
+               tssi_target -= tssi_db;
+               /* upper-lower saturate */
+               if ((ltssi > 126 && tssi_target > 0) ||
+                   ((ltssi - dev->cal.tssi_dc) < 1 && tssi_target < 0)) {
+                       tssi_target = 0;
+               }
+       }
+
+       if ((dev->cal.tssi_target ^ tssi_target) < 0 &&
+           dev->cal.tssi_target > -4096 && dev->cal.tssi_target < 4096 &&
+           tssi_target > -4096 && tssi_target < 4096) {
+               if ((tssi_target < 0 &&
+                    tssi_target + dev->cal.tssi_target > 0) ||
+                   (tssi_target > 0 &&
+                    tssi_target + dev->cal.tssi_target <= 0))
+                       tssi_target = 0;
+               else
+                       dev->cal.tssi_target = tssi_target;
+       } else {
+               dev->cal.tssi_target = tssi_target;
+       }
+
+       /* make the compensate value to the nearest compensate code */
+       if (tssi_target > 0)
+               tssi_target += 2048;
+       else
+               tssi_target -= 2048;
+       tssi_target >>= 12;
+
+       ret = mt76_get_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP);
+       if (ret & BIT(5))
+               ret -= BIT(6);
+       ret += tssi_target;
+
+       ret = min_t(int, 31, ret);
+       return max_t(int, -32, ret);
+}
+
+static void mt76x0_phy_tssi_calibrate(struct mt76x02_dev *dev)
+{
+       s8 target_power, target_pa_power;
+       u8 tssi_info[3], tx_mode;
+       s16 ltssi;
+       s8 val;
+
+       if (mt76x0_phy_tssi_adc_calibrate(dev, &ltssi, tssi_info) < 0)
+               return;
+
+       tx_mode = tssi_info[0] & 0x7;
+       if (mt76x0_phy_get_target_power(dev, tx_mode, tssi_info,
+                                       &target_power, &target_pa_power) < 0)
+               return;
+
+       val = mt76x0_phy_get_delta_power(dev, tx_mode, target_power,
+                                        target_pa_power, ltssi);
+       mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP, val);
 }
 
 void mt76x0_phy_set_txpower(struct mt76x02_dev *dev)
@@ -571,8 +847,8 @@ void mt76x0_phy_set_txpower(struct mt76x02_dev *dev)
        struct mt76_rate_power *t = &dev->mt76.rate_power;
        u8 info[2];
 
-       mt76x0_get_power_info(dev, info);
        mt76x0_get_tx_power_per_rate(dev);
+       mt76x0_get_power_info(dev, info);
 
        mt76x02_add_rate_power_offset(t, info[0]);
        mt76x02_limit_rate_power(t, dev->mt76.txpower_conf);
@@ -585,14 +861,25 @@ void mt76x0_phy_set_txpower(struct mt76x02_dev *dev)
 void mt76x0_phy_calibrate(struct mt76x02_dev *dev, bool power_on)
 {
        struct ieee80211_channel *chan = dev->mt76.chandef.chan;
+       int is_5ghz = (chan->band == NL80211_BAND_5GHZ) ? 1 : 0;
        u32 val, tx_alc, reg_val;
 
+       if (is_mt7630(dev))
+               return;
+
        if (power_on) {
-               mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0, false);
-               mt76x02_mcu_calibrate(dev, MCU_CAL_VCO, chan->hw_value,
-                                     false);
+               mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0);
+               mt76x02_mcu_calibrate(dev, MCU_CAL_VCO, chan->hw_value);
                usleep_range(10, 20);
-               /* XXX: tssi */
+
+               if (mt76x0_tssi_enabled(dev)) {
+                       mt76_wr(dev, MT_MAC_SYS_CTRL,
+                               MT_MAC_SYS_CTRL_ENABLE_RX);
+                       mt76x0_phy_tssi_dc_calibrate(dev);
+                       mt76_wr(dev, MT_MAC_SYS_CTRL,
+                               MT_MAC_SYS_CTRL_ENABLE_TX |
+                               MT_MAC_SYS_CTRL_ENABLE_RX);
+               }
        }
 
        tx_alc = mt76_rr(dev, MT_TX_ALC_CFG_0);
@@ -602,7 +889,7 @@ void mt76x0_phy_calibrate(struct mt76x02_dev *dev, bool power_on)
        reg_val = mt76_rr(dev, MT_BBP(IBI, 9));
        mt76_wr(dev, MT_BBP(IBI, 9), 0xffffff7e);
 
-       if (chan->band == NL80211_BAND_5GHZ) {
+       if (is_5ghz) {
                if (chan->hw_value < 100)
                        val = 0x701;
                else if (chan->hw_value < 140)
@@ -613,14 +900,14 @@ void mt76x0_phy_calibrate(struct mt76x02_dev *dev, bool power_on)
                val = 0x600;
        }
 
-       mt76x02_mcu_calibrate(dev, MCU_CAL_FULL, val, false);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_FULL, val);
        msleep(350);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_LC, 1, false);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_LC, is_5ghz);
        usleep_range(15000, 20000);
 
        mt76_wr(dev, MT_BBP(IBI, 9), reg_val);
        mt76_wr(dev, MT_TX_ALC_CFG_0, tx_alc);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, 1, false);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, 1);
 }
 EXPORT_SYMBOL_GPL(mt76x0_phy_calibrate);
 
@@ -684,7 +971,7 @@ int mt76x0_phy_set_channel(struct mt76x02_dev *dev,
        }
 
        if (mt76_is_usb(dev)) {
-               mt76x0_bbp_set_bw(dev, chandef->width);
+               mt76x0_phy_bbp_set_bw(dev, chandef->width);
        } else {
                if (chandef->width == NL80211_CHAN_WIDTH_80 ||
                    chandef->width == NL80211_CHAN_WIDTH_40)
@@ -696,7 +983,6 @@ int mt76x0_phy_set_channel(struct mt76x02_dev *dev,
        mt76x02_phy_set_bw(dev, chandef->width, ch_group_index);
        mt76x02_phy_set_band(dev, chandef->chan->band,
                             ch_group_index & 1);
-       mt76x0_ant_select(dev);
 
        mt76_rmw(dev, MT_EXT_CCA_CFG,
                 (MT_EXT_CCA_CFG_CCA0 |
@@ -710,29 +996,21 @@ int mt76x0_phy_set_channel(struct mt76x02_dev *dev,
        mt76x0_phy_set_chan_rf_params(dev, channel, rf_bw_band);
 
        /* set Japan Tx filter at channel 14 */
-       val = mt76_rr(dev, MT_BBP(CORE, 1));
        if (channel == 14)
-               val |= 0x20;
+               mt76_set(dev, MT_BBP(CORE, 1), 0x20);
        else
-               val &= ~0x20;
-       mt76_wr(dev, MT_BBP(CORE, 1), val);
+               mt76_clear(dev, MT_BBP(CORE, 1), 0x20);
 
        mt76x0_read_rx_gain(dev);
        mt76x0_phy_set_chan_bbp_params(dev, rf_bw_band);
-       mt76x02_init_agc_gain(dev);
-
-       if (mt76_is_usb(dev)) {
-               mt76x0_vco_cal(dev, channel);
-       } else {
-               /* enable vco */
-               rf_set(dev, MT_RF(0, 4), BIT(7));
-       }
 
+       /* enable vco */
+       mt76x0_rf_set(dev, MT_RF(0, 4), BIT(7));
        if (scan)
                return 0;
 
-       if (mt76_is_mmio(dev))
-               mt76x0_phy_calibrate(dev, false);
+       mt76x02_init_agc_gain(dev);
+       mt76x0_phy_calibrate(dev, false);
        mt76x0_phy_set_txpower(dev);
 
        ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work,
@@ -741,55 +1019,21 @@ int mt76x0_phy_set_channel(struct mt76x02_dev *dev,
        return 0;
 }
 
-void mt76x0_phy_recalibrate_after_assoc(struct mt76x02_dev *dev)
-{
-       u32 tx_alc, reg_val;
-       u8 channel = dev->mt76.chandef.chan->hw_value;
-       int is_5ghz = (dev->mt76.chandef.chan->band == NL80211_BAND_5GHZ) ? 1 : 0;
-
-       mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0, false);
-
-       mt76x0_vco_cal(dev, channel);
-
-       tx_alc = mt76_rr(dev, MT_TX_ALC_CFG_0);
-       mt76_wr(dev, MT_TX_ALC_CFG_0, 0);
-       usleep_range(500, 700);
-
-       reg_val = mt76_rr(dev, MT_BBP(IBI, 9));
-       mt76_wr(dev, MT_BBP(IBI, 9), 0xffffff7e);
-
-       mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, 0, false);
-
-       mt76x02_mcu_calibrate(dev, MCU_CAL_LC, is_5ghz, false);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_LOFT, is_5ghz, false);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz, false);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_TX_GROUP_DELAY, is_5ghz, false);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_RXIQ, is_5ghz, false);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_RX_GROUP_DELAY, is_5ghz, false);
-
-       mt76_wr(dev, MT_BBP(IBI, 9), reg_val);
-       mt76_wr(dev, MT_TX_ALC_CFG_0, tx_alc);
-       msleep(100);
-
-       mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, 1, false);
-}
-
-static void mt76x0_temp_sensor(struct mt76x02_dev *dev)
+static void mt76x0_phy_temp_sensor(struct mt76x02_dev *dev)
 {
        u8 rf_b7_73, rf_b0_66, rf_b0_67;
        s8 val;
 
-       rf_b7_73 = rf_rr(dev, MT_RF(7, 73));
-       rf_b0_66 = rf_rr(dev, MT_RF(0, 66));
-       rf_b0_67 = rf_rr(dev, MT_RF(0, 67));
+       rf_b7_73 = mt76x0_rf_rr(dev, MT_RF(7, 73));
+       rf_b0_66 = mt76x0_rf_rr(dev, MT_RF(0, 66));
+       rf_b0_67 = mt76x0_rf_rr(dev, MT_RF(0, 67));
 
-       rf_wr(dev, MT_RF(7, 73), 0x02);
-       rf_wr(dev, MT_RF(0, 66), 0x23);
-       rf_wr(dev, MT_RF(0, 67), 0x01);
+       mt76x0_rf_wr(dev, MT_RF(7, 73), 0x02);
+       mt76x0_rf_wr(dev, MT_RF(0, 66), 0x23);
+       mt76x0_rf_wr(dev, MT_RF(0, 67), 0x01);
 
        mt76_wr(dev, MT_BBP(CORE, 34), 0x00080055);
-
-       if (!mt76_poll(dev, MT_BBP(CORE, 34), BIT(4), 0, 2000)) {
+       if (!mt76_poll_msec(dev, MT_BBP(CORE, 34), BIT(4), 0, 200)) {
                mt76_clear(dev, MT_BBP(CORE, 34), BIT(4));
                goto done;
        }
@@ -799,8 +1043,7 @@ static void mt76x0_temp_sensor(struct mt76x02_dev *dev)
 
        if (abs(val - dev->cal.temp_vco) > 20) {
                mt76x02_mcu_calibrate(dev, MCU_CAL_VCO,
-                                     dev->mt76.chandef.chan->hw_value,
-                                     false);
+                                     dev->mt76.chandef.chan->hw_value);
                dev->cal.temp_vco = val;
        }
        if (abs(val - dev->cal.temp) > 30) {
@@ -809,18 +1052,20 @@ static void mt76x0_temp_sensor(struct mt76x02_dev *dev)
        }
 
 done:
-       rf_wr(dev, MT_RF(7, 73), rf_b7_73);
-       rf_wr(dev, MT_RF(0, 66), rf_b0_66);
-       rf_wr(dev, MT_RF(0, 67), rf_b0_67);
+       mt76x0_rf_wr(dev, MT_RF(7, 73), rf_b7_73);
+       mt76x0_rf_wr(dev, MT_RF(0, 66), rf_b0_66);
+       mt76x0_rf_wr(dev, MT_RF(0, 67), rf_b0_67);
 }
 
 static void mt76x0_phy_set_gain_val(struct mt76x02_dev *dev)
 {
        u8 gain = dev->cal.agc_gain_cur[0] - dev->cal.agc_gain_adjust;
-       u32 val = 0x122c << 16 | 0xf2;
 
-       mt76_wr(dev, MT_BBP(AGC, 8),
-               val | FIELD_PREP(MT_BBP_AGC_GAIN, gain));
+       mt76_rmw_field(dev, MT_BBP(AGC, 8), MT_BBP_AGC_GAIN, gain);
+
+       if ((dev->mt76.chandef.chan->flags & IEEE80211_CHAN_RADAR) &&
+           !is_mt7630(dev))
+               mt76x02_phy_dfs_adjust_agc(dev);
 }
 
 static void
@@ -835,7 +1080,8 @@ mt76x0_phy_update_channel_gain(struct mt76x02_dev *dev)
        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));
 
-       gain_change = (dev->cal.low_gain & 2) ^ (low_gain & 2);
+       gain_change = dev->cal.low_gain < 0 ||
+                     (dev->cal.low_gain & 2) ^ (low_gain & 2);
        dev->cal.low_gain = low_gain;
 
        if (!gain_change) {
@@ -860,20 +1106,65 @@ static void mt76x0_phy_calibration_work(struct work_struct *work)
                                               cal_work.work);
 
        mt76x0_phy_update_channel_gain(dev);
-       if (!mt76x0_tssi_enabled(dev))
-               mt76x0_temp_sensor(dev);
+       if (mt76x0_tssi_enabled(dev))
+               mt76x0_phy_tssi_calibrate(dev);
+       else
+               mt76x0_phy_temp_sensor(dev);
 
        ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work,
-                                    MT_CALIBRATE_INTERVAL);
+                                    4 * MT_CALIBRATE_INTERVAL);
 }
 
-static void mt76x0_rf_init(struct mt76x02_dev *dev)
+static void mt76x0_rf_patch_reg_array(struct mt76x02_dev *dev,
+                                     const struct mt76_reg_pair *rp, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               u32 reg = rp[i].reg;
+               u8 val = rp[i].value;
+
+               switch (reg) {
+               case MT_RF(0, 3):
+                       if (mt76_is_mmio(dev)) {
+                               if (is_mt7630(dev))
+                                       val = 0x70;
+                               else
+                                       val = 0x63;
+                       } else {
+                               val = 0x73;
+                       }
+                       break;
+               case MT_RF(0, 21):
+                       if (is_mt7610e(dev))
+                               val = 0x10;
+                       else
+                               val = 0x12;
+                       break;
+               case MT_RF(5, 2):
+                       if (is_mt7630(dev))
+                               val = 0x1d;
+                       else if (is_mt7610e(dev))
+                               val = 0x00;
+                       else
+                               val = 0x0c;
+                       break;
+               default:
+                       break;
+               }
+               mt76x0_rf_wr(dev, reg, val);
+       }
+}
+
+static void mt76x0_phy_rf_init(struct mt76x02_dev *dev)
 {
        int i;
        u8 val;
 
-       RF_RANDOM_WRITE(dev, mt76x0_rf_central_tab);
-       RF_RANDOM_WRITE(dev, mt76x0_rf_2g_channel_0_tab);
+       mt76x0_rf_patch_reg_array(dev, mt76x0_rf_central_tab,
+                                 ARRAY_SIZE(mt76x0_rf_central_tab));
+       mt76x0_rf_patch_reg_array(dev, mt76x0_rf_2g_channel_0_tab,
+                                 ARRAY_SIZE(mt76x0_rf_2g_channel_0_tab));
        RF_RANDOM_WRITE(dev, mt76x0_rf_5g_channel_0_tab);
        RF_RANDOM_WRITE(dev, mt76x0_rf_vga_channel_0_tab);
 
@@ -881,16 +1172,16 @@ static void mt76x0_rf_init(struct mt76x02_dev *dev)
                const struct mt76x0_rf_switch_item *item = &mt76x0_rf_bw_switch_tab[i];
 
                if (item->bw_band == RF_BW_20)
-                       rf_wr(dev, item->rf_bank_reg, item->value);
+                       mt76x0_rf_wr(dev, item->rf_bank_reg, item->value);
                else if (((RF_G_BAND | RF_BW_20) & item->bw_band) == (RF_G_BAND | RF_BW_20))
-                       rf_wr(dev, item->rf_bank_reg, item->value);
+                       mt76x0_rf_wr(dev, item->rf_bank_reg, item->value);
        }
 
        for (i = 0; i < ARRAY_SIZE(mt76x0_rf_band_switch_tab); i++) {
                if (mt76x0_rf_band_switch_tab[i].bw_band & RF_G_BAND) {
-                       rf_wr(dev,
-                             mt76x0_rf_band_switch_tab[i].rf_bank_reg,
-                             mt76x0_rf_band_switch_tab[i].value);
+                       mt76x0_rf_wr(dev,
+                                    mt76x0_rf_band_switch_tab[i].rf_bank_reg,
+                                    mt76x0_rf_band_switch_tab[i].value);
                }
        }
 
@@ -899,32 +1190,29 @@ static void mt76x0_rf_init(struct mt76x02_dev *dev)
           E1: B0.R22<6:0>: xo_cxo<6:0>
           E2: B0.R21<0>: xo_cxo<0>, B0.R22<7:0>: xo_cxo<8:1>
         */
-       rf_wr(dev, MT_RF(0, 22),
-             min_t(u8, dev->cal.rx.freq_offset, 0xbf));
-       val = rf_rr(dev, MT_RF(0, 22));
-
-       /*
-          Reset the DAC (Set B0.R73<7>=1, then set B0.R73<7>=0, and then set B0.R73<7>) during power up.
+       mt76x0_rf_wr(dev, MT_RF(0, 22),
+                    min_t(u8, dev->cal.rx.freq_offset, 0xbf));
+       val = mt76x0_rf_rr(dev, MT_RF(0, 22));
+
+       /* Reset procedure DAC during power-up:
+        * - set B0.R73<7>
+        * - clear B0.R73<7>
+        * - set B0.R73<7>
         */
-       val = rf_rr(dev, MT_RF(0, 73));
-       val |= 0x80;
-       rf_wr(dev, MT_RF(0, 73), val);
-       val &= ~0x80;
-       rf_wr(dev, MT_RF(0, 73), val);
-       val |= 0x80;
-       rf_wr(dev, MT_RF(0, 73), val);
+       mt76x0_rf_set(dev, MT_RF(0, 73), BIT(7));
+       mt76x0_rf_clear(dev, MT_RF(0, 73), BIT(7));
+       mt76x0_rf_set(dev, MT_RF(0, 73), BIT(7));
 
-       /*
-          vcocal_en (initiate VCO calibration (reset after completion)) - It should be at the end of RF configuration.
-        */
-       rf_set(dev, MT_RF(0, 4), 0x80);
+       /* vcocal_en: initiate VCO calibration (reset after completion)) */
+       mt76x0_rf_set(dev, MT_RF(0, 4), 0x80);
 }
 
 void mt76x0_phy_init(struct mt76x02_dev *dev)
 {
        INIT_DELAYED_WORK(&dev->cal_work, mt76x0_phy_calibration_work);
 
-       mt76x0_rf_init(dev);
+       mt76x0_phy_ant_select(dev);
+       mt76x0_phy_rf_init(dev);
        mt76x02_phy_set_rxpath(dev);
        mt76x02_phy_set_txdac(dev);
 }
index 2880a43..9889132 100644 (file)
 #define MT_RF_BANK(offset) (offset >> 16)
 #define MT_RF_REG(offset) (offset & 0xff)
 
+#define MT_RF_VCO_BP_CLOSE_LOOP                BIT(3)
+#define MT_RF_VCO_BP_CLOSE_LOOP_MASK   GENMASK(3, 0)
+#define MT_RF_VCO_CAL_MASK             GENMASK(2, 0)
+#define MT_RF_START_TIME               0x3
+#define MT_RF_START_TIME_MASK          GENMASK(2, 0)
+#define MT_RF_SETTLE_TIME_MASK         GENMASK(6, 4)
+
+#define MT_RF_PLL_DEN_MASK             GENMASK(4, 0)
+#define MT_RF_PLL_K_MASK               GENMASK(4, 0)
+#define MT_RF_SDM_RESET_MASK           BIT(7)
+#define MT_RF_SDM_MASH_PRBS_MASK       GENMASK(6, 2)
+#define MT_RF_SDM_BP_MASK              BIT(1)
+#define MT_RF_ISI_ISO_MASK             GENMASK(7, 6)
+#define MT_RF_PFD_DLY_MASK             GENMASK(5, 4)
+#define MT_RF_CLK_SEL_MASK             GENMASK(3, 2)
+#define MT_RF_XO_DIV_MASK              GENMASK(1, 0)
+
 struct mt76x0_bbp_switch_item {
        u16 bw_band;
        struct mt76_reg_pair reg_pair;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/trace.c b/drivers/net/wireless/mediatek/mt76/mt76x0/trace.c
deleted file mode 100644 (file)
index 8abdd3c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation
- *
- * 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 <linux/module.h>
-
-#ifndef __CHECKER__
-#define CREATE_TRACE_POINTS
-#include "trace.h"
-
-#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/trace.h b/drivers/net/wireless/mediatek/mt76/mt76x0/trace.h
deleted file mode 100644 (file)
index 75d1d67..0000000
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation
- *
- * 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.
- */
-
-#if !defined(__MT76X0U_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
-#define __MT76X0U_TRACE_H
-
-#include <linux/tracepoint.h>
-#include "mt76x0.h"
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM mt76x0
-
-#define MAXNAME                32
-#define DEV_ENTRY      __array(char, wiphy_name, 32)
-#define DEV_ASSIGN     strlcpy(__entry->wiphy_name,                    \
-                               wiphy_name(dev->hw->wiphy), MAXNAME)
-#define DEV_PR_FMT     "%s "
-#define DEV_PR_ARG     __entry->wiphy_name
-
-#define REG_ENTRY      __field(u32, reg) __field(u32, val)
-#define REG_ASSIGN     __entry->reg = reg; __entry->val = val
-#define REG_PR_FMT     "%04x=%08x"
-#define REG_PR_ARG     __entry->reg, __entry->val
-
-DECLARE_EVENT_CLASS(dev_reg_evt,
-       TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val),
-       TP_ARGS(dev, reg, val),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               REG_ENTRY
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               REG_ASSIGN;
-       ),
-       TP_printk(
-               DEV_PR_FMT REG_PR_FMT,
-               DEV_PR_ARG, REG_PR_ARG
-       )
-);
-
-DEFINE_EVENT(dev_reg_evt, mt76x0_reg_read,
-       TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val),
-       TP_ARGS(dev, reg, val)
-);
-
-DEFINE_EVENT(dev_reg_evt, mt76x0_reg_write,
-       TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val),
-       TP_ARGS(dev, reg, val)
-);
-
-TRACE_EVENT(mt76x0_submit_urb,
-       TP_PROTO(struct mt76_dev *dev, struct urb *u),
-       TP_ARGS(dev, u),
-       TP_STRUCT__entry(
-               DEV_ENTRY __field(unsigned, pipe) __field(u32, len)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->pipe = u->pipe;
-               __entry->len = u->transfer_buffer_length;
-       ),
-       TP_printk(DEV_PR_FMT "p:%08x len:%u",
-                 DEV_PR_ARG, __entry->pipe, __entry->len)
-);
-
-#define trace_mt76x0_submit_urb_sync(__dev, __pipe, __len) ({  \
-       struct urb u;                                   \
-       u.pipe = __pipe;                                \
-       u.transfer_buffer_length = __len;               \
-       trace_mt76x0_submit_urb(__dev, &u);                     \
-})
-
-TRACE_EVENT(mt76x0_mcu_msg_send,
-       TP_PROTO(struct mt76_dev *dev,
-                struct sk_buff *skb, u32 csum, bool resp),
-       TP_ARGS(dev, skb, csum, resp),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field(u32, info)
-               __field(u32, csum)
-               __field(bool, resp)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->info = *(u32 *)skb->data;
-               __entry->csum = csum;
-               __entry->resp = resp;
-       ),
-       TP_printk(DEV_PR_FMT "i:%08x c:%08x r:%d",
-                 DEV_PR_ARG, __entry->info, __entry->csum, __entry->resp)
-);
-
-TRACE_EVENT(mt76x0_vend_req,
-       TP_PROTO(struct mt76_dev *dev, unsigned pipe, u8 req, u8 req_type,
-                u16 val, u16 offset, void *buf, size_t buflen, int ret),
-       TP_ARGS(dev, pipe, req, req_type, val, offset, buf, buflen, ret),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field(unsigned, pipe) __field(u8, req) __field(u8, req_type)
-               __field(u16, val) __field(u16, offset) __field(void*, buf)
-               __field(int, buflen) __field(int, ret)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->pipe = pipe;
-               __entry->req = req;
-               __entry->req_type = req_type;
-               __entry->val = val;
-               __entry->offset = offset;
-               __entry->buf = buf;
-               __entry->buflen = buflen;
-               __entry->ret = ret;
-       ),
-       TP_printk(DEV_PR_FMT
-                 "%d p:%08x req:%02hhx %02hhx val:%04hx %04hx buf:%d %d",
-                 DEV_PR_ARG, __entry->ret, __entry->pipe, __entry->req,
-                 __entry->req_type, __entry->val, __entry->offset,
-                 !!__entry->buf, __entry->buflen)
-);
-
-DECLARE_EVENT_CLASS(dev_rf_reg_evt,
-       TP_PROTO(struct mt76_dev *dev, u8 bank, u8 reg, u8 val),
-       TP_ARGS(dev, bank, reg, val),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field(u8, bank)
-               __field(u8, reg)
-               __field(u8, val)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               REG_ASSIGN;
-               __entry->bank = bank;
-       ),
-       TP_printk(
-               DEV_PR_FMT "%02hhx:%02hhx=%02hhx",
-               DEV_PR_ARG, __entry->bank, __entry->reg, __entry->val
-       )
-);
-
-DEFINE_EVENT(dev_rf_reg_evt, mt76x0_rf_read,
-       TP_PROTO(struct mt76_dev *dev, u8 bank, u8 reg, u8 val),
-       TP_ARGS(dev, bank, reg, val)
-);
-
-DEFINE_EVENT(dev_rf_reg_evt, mt76x0_rf_write,
-       TP_PROTO(struct mt76_dev *dev, u8 bank, u8 reg, u8 val),
-       TP_ARGS(dev, bank, reg, val)
-);
-
-DECLARE_EVENT_CLASS(dev_simple_evt,
-       TP_PROTO(struct mt76_dev *dev, u8 val),
-       TP_ARGS(dev, val),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field(u8, val)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->val = val;
-       ),
-       TP_printk(
-               DEV_PR_FMT "%02hhx", DEV_PR_ARG, __entry->val
-       )
-);
-
-TRACE_EVENT(mt76x0_rx,
-       TP_PROTO(struct mt76_dev *dev, struct mt76x02_rxwi *rxwi, u32 f),
-       TP_ARGS(dev, rxwi, f),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field_struct(struct mt76x02_rxwi, rxwi)
-               __field(u32, fce_info)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->rxwi = *rxwi;
-               __entry->fce_info = f;
-       ),
-       TP_printk(DEV_PR_FMT "rxi:%08x ctl:%08x", DEV_PR_ARG,
-                 le32_to_cpu(__entry->rxwi.rxinfo),
-                 le32_to_cpu(__entry->rxwi.ctl))
-);
-
-TRACE_EVENT(mt76x0_tx,
-       TP_PROTO(struct mt76_dev *dev, struct sk_buff *skb,
-                struct mt76x02_sta *sta, struct mt76x02_txwi *h),
-       TP_ARGS(dev, skb, sta, h),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field_struct(struct mt76x02_txwi, h)
-               __field(struct sk_buff *, skb)
-               __field(struct mt76x02_sta *, sta)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->h = *h;
-               __entry->skb = skb;
-               __entry->sta = sta;
-       ),
-       TP_printk(DEV_PR_FMT "skb:%p sta:%p  flg:%04hx rate:%04hx "
-                 "ack:%02hhx wcid:%02hhx len_ctl:%05hx", DEV_PR_ARG,
-                 __entry->skb, __entry->sta,
-                 le16_to_cpu(__entry->h.flags),
-                 le16_to_cpu(__entry->h.rate),
-                 __entry->h.ack_ctl, __entry->h.wcid,
-                 le16_to_cpu(__entry->h.len_ctl))
-);
-
-TRACE_EVENT(mt76x0_tx_dma_done,
-       TP_PROTO(struct mt76_dev *dev, struct sk_buff *skb),
-       TP_ARGS(dev, skb),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field(struct sk_buff *, skb)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->skb = skb;
-       ),
-       TP_printk(DEV_PR_FMT "%p", DEV_PR_ARG, __entry->skb)
-);
-
-TRACE_EVENT(mt76x0_tx_status_cleaned,
-       TP_PROTO(struct mt76_dev *dev, int cleaned),
-       TP_ARGS(dev, cleaned),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field(int, cleaned)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->cleaned = cleaned;
-       ),
-       TP_printk(DEV_PR_FMT "%d", DEV_PR_ARG, __entry->cleaned)
-);
-
-TRACE_EVENT(mt76x0_tx_status,
-       TP_PROTO(struct mt76_dev *dev, u32 stat1, u32 stat2),
-       TP_ARGS(dev, stat1, stat2),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field(u32, stat1)     __field(u32, stat2)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->stat1 = stat1;
-               __entry->stat2 = stat2;
-       ),
-       TP_printk(DEV_PR_FMT "%08x %08x",
-                 DEV_PR_ARG, __entry->stat1, __entry->stat2)
-);
-
-TRACE_EVENT(mt76x0_rx_dma_aggr,
-       TP_PROTO(struct mt76_dev *dev, int cnt, bool paged),
-       TP_ARGS(dev, cnt, paged),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field(u8, cnt)
-               __field(bool, paged)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->cnt = cnt;
-               __entry->paged = paged;
-       ),
-       TP_printk(DEV_PR_FMT "cnt:%d paged:%d",
-                 DEV_PR_ARG, __entry->cnt, __entry->paged)
-);
-
-DEFINE_EVENT(dev_simple_evt, mt76x0_set_key,
-       TP_PROTO(struct mt76_dev *dev, u8 val),
-       TP_ARGS(dev, val)
-);
-
-TRACE_EVENT(mt76x0_set_shared_key,
-       TP_PROTO(struct mt76_dev *dev, u8 vid, u8 key),
-       TP_ARGS(dev, vid, key),
-       TP_STRUCT__entry(
-               DEV_ENTRY
-               __field(u8, vid)
-               __field(u8, key)
-       ),
-       TP_fast_assign(
-               DEV_ASSIGN;
-               __entry->vid = vid;
-               __entry->key = key;
-       ),
-       TP_printk(DEV_PR_FMT "phy:%02hhx off:%02hhx",
-                 DEV_PR_ARG, __entry->vid, __entry->key)
-);
-
-#endif
-
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_FILE trace
-
-#include <trace/define_trace.h>
index a7fd36c..0e6b43b 100644 (file)
@@ -17,7 +17,6 @@
 
 #include "mt76x0.h"
 #include "mcu.h"
-#include "trace.h"
 #include "../mt76x02_usb.h"
 
 static struct usb_device_id mt76x0_device_table[] = {
@@ -117,6 +116,7 @@ static int mt76x0u_start(struct ieee80211_hw *hw)
        if (ret)
                goto out;
 
+       mt76x0_phy_calibrate(dev, true);
        ieee80211_queue_delayed_work(dev->mt76.hw, &dev->mac_work,
                                     MT_CALIBRATE_INTERVAL);
        ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work,
@@ -145,17 +145,17 @@ static const struct ieee80211_ops mt76x0u_ops = {
        .remove_interface = mt76x02_remove_interface,
        .config = mt76x0_config,
        .configure_filter = mt76x02_configure_filter,
-       .bss_info_changed = mt76x0_bss_info_changed,
-       .sta_add = mt76x02_sta_add,
-       .sta_remove = mt76x02_sta_remove,
+       .bss_info_changed = mt76x02_bss_info_changed,
+       .sta_state = mt76_sta_state,
        .set_key = mt76x02_set_key,
        .conf_tx = mt76x02_conf_tx,
-       .sw_scan_start = mt76x0_sw_scan,
-       .sw_scan_complete = mt76x0_sw_scan_complete,
+       .sw_scan_start = mt76x02_sw_scan,
+       .sw_scan_complete = mt76x02_sw_scan_complete,
        .ampdu_action = mt76x02_ampdu_action,
        .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update,
-       .set_rts_threshold = mt76x0_set_rts_threshold,
+       .set_rts_threshold = mt76x02_set_rts_threshold,
        .wake_tx_queue = mt76_wake_tx_queue,
+       .get_txpower = mt76x02_get_txpower,
 };
 
 static int mt76x0u_register_device(struct mt76x02_dev *dev)
@@ -218,6 +218,8 @@ static int mt76x0u_probe(struct usb_interface *usb_intf,
                .tx_complete_skb = mt76x02u_tx_complete_skb,
                .tx_status_data = mt76x02_tx_status_data,
                .rx_skb = mt76x02_queue_rx_skb,
+               .sta_add = mt76x02_sta_add,
+               .sta_remove = mt76x02_sta_remove,
        };
        struct usb_device *usb_dev = interface_to_usbdev(usb_intf);
        struct mt76x02_dev *dev;
@@ -337,6 +339,8 @@ err:
 }
 
 MODULE_DEVICE_TABLE(usb, mt76x0_device_table);
+MODULE_FIRMWARE(MT7610E_FIRMWARE);
+MODULE_FIRMWARE(MT7610U_FIRMWARE);
 MODULE_LICENSE("GPL");
 
 static struct usb_driver mt76x0_driver = {
index a9f14d5..9d75850 100644 (file)
@@ -22,7 +22,6 @@
 
 #define MCU_FW_URB_MAX_PAYLOAD         0x38f8
 #define MCU_FW_URB_SIZE                        (MCU_FW_URB_MAX_PAYLOAD + 12)
-#define MT7610U_FIRMWARE               "mediatek/mt7610u.bin"
 
 static int
 mt76x0u_upload_firmware(struct mt76x02_dev *dev,
@@ -75,6 +74,24 @@ out:
        return err;
 }
 
+static int mt76x0_get_firmware(struct mt76x02_dev *dev,
+                              const struct firmware **fw)
+{
+       int err;
+
+       /* try to load mt7610e fw if available
+        * otherwise fall back to mt7610u one
+        */
+       err = firmware_request_nowarn(fw, MT7610E_FIRMWARE, dev->mt76.dev);
+       if (err) {
+               dev_info(dev->mt76.dev, "%s not found, switching to %s",
+                        MT7610E_FIRMWARE, MT7610U_FIRMWARE);
+               return request_firmware(fw, MT7610U_FIRMWARE,
+                                       dev->mt76.dev);
+       }
+       return 0;
+}
+
 static int mt76x0u_load_firmware(struct mt76x02_dev *dev)
 {
        const struct firmware *fw;
@@ -88,7 +105,7 @@ static int mt76x0u_load_firmware(struct mt76x02_dev *dev)
        if (mt76x0_firmware_running(dev))
                return 0;
 
-       ret = request_firmware(&fw, MT7610U_FIRMWARE, dev->mt76.dev);
+       ret = mt76x0_get_firmware(dev, &fw);
        if (ret)
                return ret;
 
@@ -171,5 +188,3 @@ int mt76x0u_mcu_init(struct mt76x02_dev *dev)
 
        return 0;
 }
-
-MODULE_FIRMWARE(MT7610U_FIRMWARE);
index 47c42c6..6782665 100644 (file)
 #include "mt76x02_dfs.h"
 #include "mt76x02_dma.h"
 
-struct mt76x02_mac_stats {
-       u64 rx_stat[6];
-       u64 tx_stat[6];
-       u64 aggr_stat[2];
-       u64 aggr_n[32];
-       u64 zero_len_del[2];
-};
+#define MT_CALIBRATE_INTERVAL  HZ
 
 #define MT_MAX_CHAINS          2
 struct mt76x02_rx_freq_cal {
@@ -63,6 +57,10 @@ struct mt76x02_calibration {
        bool tssi_comp_pending;
        bool dpd_cal_done;
        bool channel_cal_done;
+       bool gain_init_done;
+
+       int tssi_target;
+       s8 tssi_dc;
 };
 
 struct mt76x02_dev {
@@ -71,7 +69,6 @@ struct mt76x02_dev {
        struct mac_address macaddr_list[8];
 
        struct mutex phy_mutex;
-       struct mutex mutex;
 
        u8 txdone_seq;
        DECLARE_KFIFO_PTR(txstatus_fifo, struct mt76x02_tx_status);
@@ -83,8 +80,6 @@ struct mt76x02_dev {
        struct delayed_work cal_work;
        struct delayed_work mac_work;
 
-       struct mt76x02_mac_stats stats;
-       atomic_t avg_ampdu_len;
        u32 aggr_stats[32];
 
        struct sk_buff *beacons[8];
@@ -110,14 +105,16 @@ struct mt76x02_dev {
 
 extern struct ieee80211_rate mt76x02_rates[12];
 
+void mt76x02_init_device(struct mt76x02_dev *dev);
 void mt76x02_configure_filter(struct ieee80211_hw *hw,
                             unsigned int changed_flags,
                             unsigned int *total_flags, u64 multicast);
-int mt76x02_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                  struct ieee80211_sta *sta);
-int mt76x02_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                     struct ieee80211_sta *sta);
+int mt76x02_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+                   struct ieee80211_sta *sta);
+void mt76x02_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta);
 
+void mt76x02_config_mac_addr_list(struct mt76x02_dev *dev);
 void mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif,
                      unsigned int idx);
 int mt76x02_add_interface(struct ieee80211_hw *hw,
@@ -140,9 +137,12 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev,
 s8 mt76x02_tx_get_txpwr_adj(struct mt76x02_dev *dev, s8 txpwr,
                            s8 max_txpwr_adj);
 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,
+                               s16 coverage_class);
+int mt76x02_set_rts_threshold(struct ieee80211_hw *hw, u32 val);
 int mt76x02_insert_hdr_pad(struct sk_buff *skb);
 void mt76x02_remove_hdr_pad(struct sk_buff *skb, int len);
-void mt76x02_tx_complete(struct mt76_dev *dev, struct sk_buff *skb);
 bool mt76x02_tx_status_data(struct mt76_dev *mdev, u8 *update);
 void mt76x02_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
                          struct sk_buff *skb);
@@ -154,12 +154,24 @@ int mt76x02_tx_prepare_skb(struct mt76_dev *mdev, void *txwi,
                           struct sk_buff *skb, struct mt76_queue *q,
                           struct mt76_wcid *wcid, struct ieee80211_sta *sta,
                           u32 *tx_info);
+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,
+                             struct ieee80211_bss_conf *info, u32 changed);
 
 extern const u16 mt76x02_beacon_offsets[16];
-void mt76x02_set_beacon_offsets(struct mt76x02_dev *dev);
+void mt76x02_init_beacon_config(struct mt76x02_dev *dev);
 void mt76x02_set_irq_mask(struct mt76x02_dev *dev, u32 clear, u32 set);
 void mt76x02_mac_start(struct mt76x02_dev *dev);
 
+void mt76x02_init_debugfs(struct mt76x02_dev *dev);
+
 static inline bool is_mt76x2(struct mt76x02_dev *dev)
 {
        return mt76_chip(&dev->mt76) == 0x7612 ||
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
new file mode 100644 (file)
index 0000000..a9d52ba
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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.
+ */
+
+#include <linux/debugfs.h>
+#include "mt76x02.h"
+
+static int
+mt76x02_ampdu_stat_read(struct seq_file *file, void *data)
+{
+       struct mt76x02_dev *dev = file->private;
+       int i, j;
+
+       for (i = 0; i < 4; i++) {
+               seq_puts(file, "Length: ");
+               for (j = 0; j < 8; j++)
+                       seq_printf(file, "%8d | ", i * 8 + j + 1);
+               seq_puts(file, "\n");
+               seq_puts(file, "Count:  ");
+               for (j = 0; j < 8; j++)
+                       seq_printf(file, "%8d | ", dev->aggr_stats[i * 8 + j]);
+               seq_puts(file, "\n");
+               seq_puts(file, "--------");
+               for (j = 0; j < 8; j++)
+                       seq_puts(file, "-----------");
+               seq_puts(file, "\n");
+       }
+
+       return 0;
+}
+
+static int
+mt76x02_ampdu_stat_open(struct inode *inode, struct file *f)
+{
+       return single_open(f, mt76x02_ampdu_stat_read, inode->i_private);
+}
+
+static int read_txpower(struct seq_file *file, void *data)
+{
+       struct mt76x02_dev *dev = dev_get_drvdata(file->private);
+
+       seq_printf(file, "Target power: %d\n", dev->target_power);
+
+       mt76_seq_puts_array(file, "Delta", dev->target_power_delta,
+                           ARRAY_SIZE(dev->target_power_delta));
+       return 0;
+}
+
+static const struct file_operations fops_ampdu_stat = {
+       .open = mt76x02_ampdu_stat_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static int
+mt76x02_dfs_stat_read(struct seq_file *file, void *data)
+{
+       struct mt76x02_dev *dev = file->private;
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+       int i;
+
+       seq_printf(file, "allocated sequences:\t%d\n",
+                  dfs_pd->seq_stats.seq_pool_len);
+       seq_printf(file, "used sequences:\t\t%d\n",
+                  dfs_pd->seq_stats.seq_len);
+       seq_puts(file, "\n");
+
+       for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
+               seq_printf(file, "engine: %d\n", i);
+               seq_printf(file, "  hw pattern detected:\t%d\n",
+                          dfs_pd->stats[i].hw_pattern);
+               seq_printf(file, "  hw pulse discarded:\t%d\n",
+                          dfs_pd->stats[i].hw_pulse_discarded);
+               seq_printf(file, "  sw pattern detected:\t%d\n",
+                          dfs_pd->stats[i].sw_pattern);
+       }
+
+       return 0;
+}
+
+static int
+mt76x02_dfs_stat_open(struct inode *inode, struct file *f)
+{
+       return single_open(f, mt76x02_dfs_stat_read, inode->i_private);
+}
+
+static const struct file_operations fops_dfs_stat = {
+       .open = mt76x02_dfs_stat_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static int read_agc(struct seq_file *file, void *data)
+{
+       struct mt76x02_dev *dev = dev_get_drvdata(file->private);
+
+       seq_printf(file, "avg_rssi: %d\n", dev->cal.avg_rssi_all);
+       seq_printf(file, "low_gain: %d\n", dev->cal.low_gain);
+       seq_printf(file, "false_cca: %d\n", dev->cal.false_cca);
+       seq_printf(file, "agc_gain_adjust: %d\n", dev->cal.agc_gain_adjust);
+
+       return 0;
+}
+
+void mt76x02_init_debugfs(struct mt76x02_dev *dev)
+{
+       struct dentry *dir;
+
+       dir = mt76_register_debugfs(&dev->mt76);
+       if (!dir)
+               return;
+
+       debugfs_create_u8("temperature", 0400, dir, &dev->cal.temp);
+       debugfs_create_bool("tpc", 0600, dir, &dev->enable_tpc);
+
+       debugfs_create_file("ampdu_stat", 0400, dir, dev, &fops_ampdu_stat);
+       debugfs_create_file("dfs_stats", 0400, dir, dev, &fops_dfs_stat);
+       debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir,
+                                   read_txpower);
+
+       debugfs_create_devm_seqfile(dev->mt76.dev, "agc", dir, read_agc);
+}
+EXPORT_SYMBOL_GPL(mt76x02_init_debugfs);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
new file mode 100644 (file)
index 0000000..054609c
--- /dev/null
@@ -0,0 +1,899 @@
+/*
+ * Copyright (C) 2016 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
+ *
+ * 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.
+ */
+
+#include "mt76x02.h"
+
+#define RADAR_SPEC(m, len, el, eh, wl, wh,             \
+                  w_tolerance, tl, th, t_tolerance,    \
+                  bl, bh, event_exp, power_jmp)        \
+{                                                      \
+       .mode = m,                                      \
+       .avg_len = len,                                 \
+       .e_low = el,                                    \
+       .e_high = eh,                                   \
+       .w_low = wl,                                    \
+       .w_high = wh,                                   \
+       .w_margin = w_tolerance,                        \
+       .t_low = tl,                                    \
+       .t_high = th,                                   \
+       .t_margin = t_tolerance,                        \
+       .b_low = bl,                                    \
+       .b_high = bh,                                   \
+       .event_expiration = event_exp,                  \
+       .pwr_jmp = power_jmp                            \
+}
+
+static const struct mt76x02_radar_specs etsi_radar_specs[] = {
+       /* 20MHz */
+       RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
+                  0x7fffffff, 0x155cc0, 0x19cc),
+       RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
+                  0x7fffffff, 0x155cc0, 0x19cc),
+       RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
+                  0x7fffffff, 0x155cc0, 0x19dd),
+       RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
+                  0x7fffffff, 0x2191c0, 0x15cc),
+       /* 40MHz */
+       RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
+                  0x7fffffff, 0x155cc0, 0x19cc),
+       RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
+                  0x7fffffff, 0x155cc0, 0x19cc),
+       RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
+                  0x7fffffff, 0x155cc0, 0x19dd),
+       RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
+                  0x7fffffff, 0x2191c0, 0x15cc),
+       /* 80MHz */
+       RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
+                  0x7fffffff, 0x155cc0, 0x19cc),
+       RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
+                  0x7fffffff, 0x155cc0, 0x19cc),
+       RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
+                  0x7fffffff, 0x155cc0, 0x19dd),
+       RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
+                  0x7fffffff, 0x2191c0, 0x15cc)
+};
+
+static const struct mt76x02_radar_specs fcc_radar_specs[] = {
+       /* 20MHz */
+       RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0,
+                  0x7fffffff, 0xfe808, 0x13dc),
+       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
+                  0x7fffffff, 0xfe808, 0x19dd),
+       RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
+                  0x7fffffff, 0xfe808, 0x12cc),
+       RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
+                  0x3938700, 0x57bcf00, 0x1289),
+       /* 40MHz */
+       RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0,
+                  0x7fffffff, 0xfe808, 0x13dc),
+       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
+                  0x7fffffff, 0xfe808, 0x19dd),
+       RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
+                  0x7fffffff, 0xfe808, 0x12cc),
+       RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
+                  0x3938700, 0x57bcf00, 0x1289),
+       /* 80MHz */
+       RADAR_SPEC(0, 8, 2, 14, 106, 150, 15, 2900, 80100, 15, 0,
+                  0x7fffffff, 0xfe808, 0x16cc),
+       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
+                  0x7fffffff, 0xfe808, 0x19dd),
+       RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
+                  0x7fffffff, 0xfe808, 0x12cc),
+       RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
+                  0x3938700, 0x57bcf00, 0x1289)
+};
+
+static const struct mt76x02_radar_specs jp_w56_radar_specs[] = {
+       /* 20MHz */
+       RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0,
+                  0x7fffffff, 0x14c080, 0x13dc),
+       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
+                  0x7fffffff, 0x14c080, 0x19dd),
+       RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
+                  0x7fffffff, 0x14c080, 0x12cc),
+       RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
+                  0x3938700, 0X57bcf00, 0x1289),
+       /* 40MHz */
+       RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0,
+                  0x7fffffff, 0x14c080, 0x13dc),
+       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
+                  0x7fffffff, 0x14c080, 0x19dd),
+       RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
+                  0x7fffffff, 0x14c080, 0x12cc),
+       RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
+                  0x3938700, 0X57bcf00, 0x1289),
+       /* 80MHz */
+       RADAR_SPEC(0, 8, 2, 9, 106, 150, 15, 2900, 80100, 15, 0,
+                  0x7fffffff, 0x14c080, 0x16cc),
+       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
+                  0x7fffffff, 0x14c080, 0x19dd),
+       RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
+                  0x7fffffff, 0x14c080, 0x12cc),
+       RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
+                  0x3938700, 0X57bcf00, 0x1289)
+};
+
+static const struct mt76x02_radar_specs jp_w53_radar_specs[] = {
+       /* 20MHz */
+       RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
+                  0x7fffffff, 0x14c080, 0x16cc),
+       { 0 },
+       RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
+                  0x7fffffff, 0x14c080, 0x16cc),
+       { 0 },
+       /* 40MHz */
+       RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
+                  0x7fffffff, 0x14c080, 0x16cc),
+       { 0 },
+       RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
+                  0x7fffffff, 0x14c080, 0x16cc),
+       { 0 },
+       /* 80MHz */
+       RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
+                  0x7fffffff, 0x14c080, 0x16cc),
+       { 0 },
+       RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
+                  0x7fffffff, 0x14c080, 0x16cc),
+       { 0 }
+};
+
+static void
+mt76x02_dfs_set_capture_mode_ctrl(struct mt76x02_dev *dev, u8 enable)
+{
+       u32 data;
+
+       data = (1 << 1) | enable;
+       mt76_wr(dev, MT_BBP(DFS, 36), data);
+}
+
+static void mt76x02_dfs_seq_pool_put(struct mt76x02_dev *dev,
+                                    struct mt76x02_dfs_sequence *seq)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+
+       list_add(&seq->head, &dfs_pd->seq_pool);
+
+       dfs_pd->seq_stats.seq_pool_len++;
+       dfs_pd->seq_stats.seq_len--;
+}
+
+static struct mt76x02_dfs_sequence *
+mt76x02_dfs_seq_pool_get(struct mt76x02_dev *dev)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+       struct mt76x02_dfs_sequence *seq;
+
+       if (list_empty(&dfs_pd->seq_pool)) {
+               seq = devm_kzalloc(dev->mt76.dev, sizeof(*seq), GFP_ATOMIC);
+       } else {
+               seq = list_first_entry(&dfs_pd->seq_pool,
+                                      struct mt76x02_dfs_sequence,
+                                      head);
+               list_del(&seq->head);
+               dfs_pd->seq_stats.seq_pool_len--;
+       }
+       if (seq)
+               dfs_pd->seq_stats.seq_len++;
+
+       return seq;
+}
+
+static int mt76x02_dfs_get_multiple(int val, int frac, int margin)
+{
+       int remainder, factor;
+
+       if (!frac)
+               return 0;
+
+       if (abs(val - frac) <= margin)
+               return 1;
+
+       factor = val / frac;
+       remainder = val % frac;
+
+       if (remainder > margin) {
+               if ((frac - remainder) <= margin)
+                       factor++;
+               else
+                       factor = 0;
+       }
+       return factor;
+}
+
+static void mt76x02_dfs_detector_reset(struct mt76x02_dev *dev)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+       struct mt76x02_dfs_sequence *seq, *tmp_seq;
+       int i;
+
+       /* reset hw detector */
+       mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
+
+       /* reset sw detector */
+       for (i = 0; i < ARRAY_SIZE(dfs_pd->event_rb); i++) {
+               dfs_pd->event_rb[i].h_rb = 0;
+               dfs_pd->event_rb[i].t_rb = 0;
+       }
+
+       list_for_each_entry_safe(seq, tmp_seq, &dfs_pd->sequences, head) {
+               list_del_init(&seq->head);
+               mt76x02_dfs_seq_pool_put(dev, seq);
+       }
+}
+
+static bool mt76x02_dfs_check_chirp(struct mt76x02_dev *dev)
+{
+       bool ret = false;
+       u32 current_ts, delta_ts;
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+
+       current_ts = mt76_rr(dev, MT_PBF_LIFE_TIMER);
+       delta_ts = current_ts - dfs_pd->chirp_pulse_ts;
+       dfs_pd->chirp_pulse_ts = current_ts;
+
+       /* 12 sec */
+       if (delta_ts <= (12 * (1 << 20))) {
+               if (++dfs_pd->chirp_pulse_cnt > 8)
+                       ret = true;
+       } else {
+               dfs_pd->chirp_pulse_cnt = 1;
+       }
+
+       return ret;
+}
+
+static void mt76x02_dfs_get_hw_pulse(struct mt76x02_dev *dev,
+                                    struct mt76x02_dfs_hw_pulse *pulse)
+{
+       u32 data;
+
+       /* select channel */
+       data = (MT_DFS_CH_EN << 16) | pulse->engine;
+       mt76_wr(dev, MT_BBP(DFS, 0), data);
+
+       /* reported period */
+       pulse->period = mt76_rr(dev, MT_BBP(DFS, 19));
+
+       /* reported width */
+       pulse->w1 = mt76_rr(dev, MT_BBP(DFS, 20));
+       pulse->w2 = mt76_rr(dev, MT_BBP(DFS, 23));
+
+       /* reported burst number */
+       pulse->burst = mt76_rr(dev, MT_BBP(DFS, 22));
+}
+
+static bool mt76x02_dfs_check_hw_pulse(struct mt76x02_dev *dev,
+                                      struct mt76x02_dfs_hw_pulse *pulse)
+{
+       bool ret = false;
+
+       if (!pulse->period || !pulse->w1)
+               return false;
+
+       switch (dev->dfs_pd.region) {
+       case NL80211_DFS_FCC:
+               if (pulse->engine > 3)
+                       break;
+
+               if (pulse->engine == 3) {
+                       ret = mt76x02_dfs_check_chirp(dev);
+                       break;
+               }
+
+               /* check short pulse*/
+               if (pulse->w1 < 120)
+                       ret = (pulse->period >= 2900 &&
+                              (pulse->period <= 4700 ||
+                               pulse->period >= 6400) &&
+                              (pulse->period <= 6800 ||
+                               pulse->period >= 10200) &&
+                              pulse->period <= 61600);
+               else if (pulse->w1 < 130) /* 120 - 130 */
+                       ret = (pulse->period >= 2900 &&
+                              pulse->period <= 61600);
+               else
+                       ret = (pulse->period >= 3500 &&
+                              pulse->period <= 10100);
+               break;
+       case NL80211_DFS_ETSI:
+               if (pulse->engine >= 3)
+                       break;
+
+               ret = (pulse->period >= 4900 &&
+                      (pulse->period <= 10200 ||
+                       pulse->period >= 12400) &&
+                      pulse->period <= 100100);
+               break;
+       case NL80211_DFS_JP:
+               if (dev->mt76.chandef.chan->center_freq >= 5250 &&
+                   dev->mt76.chandef.chan->center_freq <= 5350) {
+                       /* JPW53 */
+                       if (pulse->w1 <= 130)
+                               ret = (pulse->period >= 28360 &&
+                                      (pulse->period <= 28700 ||
+                                       pulse->period >= 76900) &&
+                                      pulse->period <= 76940);
+                       break;
+               }
+
+               if (pulse->engine > 3)
+                       break;
+
+               if (pulse->engine == 3) {
+                       ret = mt76x02_dfs_check_chirp(dev);
+                       break;
+               }
+
+               /* check short pulse*/
+               if (pulse->w1 < 120)
+                       ret = (pulse->period >= 2900 &&
+                              (pulse->period <= 4700 ||
+                               pulse->period >= 6400) &&
+                              (pulse->period <= 6800 ||
+                               pulse->period >= 27560) &&
+                              (pulse->period <= 27960 ||
+                               pulse->period >= 28360) &&
+                              (pulse->period <= 28700 ||
+                               pulse->period >= 79900) &&
+                              pulse->period <= 80100);
+               else if (pulse->w1 < 130) /* 120 - 130 */
+                       ret = (pulse->period >= 2900 &&
+                              (pulse->period <= 10100 ||
+                               pulse->period >= 27560) &&
+                              (pulse->period <= 27960 ||
+                               pulse->period >= 28360) &&
+                              (pulse->period <= 28700 ||
+                               pulse->period >= 79900) &&
+                              pulse->period <= 80100);
+               else
+                       ret = (pulse->period >= 3900 &&
+                              pulse->period <= 10100);
+               break;
+       case NL80211_DFS_UNSET:
+       default:
+               return false;
+       }
+
+       return ret;
+}
+
+static bool mt76x02_dfs_fetch_event(struct mt76x02_dev *dev,
+                                   struct mt76x02_dfs_event *event)
+{
+       u32 data;
+
+       /* 1st: DFS_R37[31]: 0 (engine 0) - 1 (engine 2)
+        * 2nd: DFS_R37[21:0]: pulse time
+        * 3rd: DFS_R37[11:0]: pulse width
+        * 3rd: DFS_R37[25:16]: phase
+        * 4th: DFS_R37[12:0]: current pwr
+        * 4th: DFS_R37[21:16]: pwr stable counter
+        *
+        * 1st: DFS_R37[31:0] set to 0xffffffff means no event detected
+        */
+       data = mt76_rr(dev, MT_BBP(DFS, 37));
+       if (!MT_DFS_CHECK_EVENT(data))
+               return false;
+
+       event->engine = MT_DFS_EVENT_ENGINE(data);
+       data = mt76_rr(dev, MT_BBP(DFS, 37));
+       event->ts = MT_DFS_EVENT_TIMESTAMP(data);
+       data = mt76_rr(dev, MT_BBP(DFS, 37));
+       event->width = MT_DFS_EVENT_WIDTH(data);
+
+       return true;
+}
+
+static bool mt76x02_dfs_check_event(struct mt76x02_dev *dev,
+                                   struct mt76x02_dfs_event *event)
+{
+       if (event->engine == 2) {
+               struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+               struct mt76x02_dfs_event_rb *event_buff = &dfs_pd->event_rb[1];
+               u16 last_event_idx;
+               u32 delta_ts;
+
+               last_event_idx = mt76_decr(event_buff->t_rb,
+                                          MT_DFS_EVENT_BUFLEN);
+               delta_ts = event->ts - event_buff->data[last_event_idx].ts;
+               if (delta_ts < MT_DFS_EVENT_TIME_MARGIN &&
+                   event_buff->data[last_event_idx].width >= 200)
+                       return false;
+       }
+       return true;
+}
+
+static void mt76x02_dfs_queue_event(struct mt76x02_dev *dev,
+                                   struct mt76x02_dfs_event *event)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+       struct mt76x02_dfs_event_rb *event_buff;
+
+       /* add radar event to ring buffer */
+       event_buff = event->engine == 2 ? &dfs_pd->event_rb[1]
+                                       : &dfs_pd->event_rb[0];
+       event_buff->data[event_buff->t_rb] = *event;
+       event_buff->data[event_buff->t_rb].fetch_ts = jiffies;
+
+       event_buff->t_rb = mt76_incr(event_buff->t_rb, MT_DFS_EVENT_BUFLEN);
+       if (event_buff->t_rb == event_buff->h_rb)
+               event_buff->h_rb = mt76_incr(event_buff->h_rb,
+                                            MT_DFS_EVENT_BUFLEN);
+}
+
+static int mt76x02_dfs_create_sequence(struct mt76x02_dev *dev,
+                                      struct mt76x02_dfs_event *event,
+                                      u16 cur_len)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+       struct mt76x02_dfs_sw_detector_params *sw_params;
+       u32 width_delta, with_sum, factor, cur_pri;
+       struct mt76x02_dfs_sequence seq, *seq_p;
+       struct mt76x02_dfs_event_rb *event_rb;
+       struct mt76x02_dfs_event *cur_event;
+       int i, j, end, pri;
+
+       event_rb = event->engine == 2 ? &dfs_pd->event_rb[1]
+                                     : &dfs_pd->event_rb[0];
+
+       i = mt76_decr(event_rb->t_rb, MT_DFS_EVENT_BUFLEN);
+       end = mt76_decr(event_rb->h_rb, MT_DFS_EVENT_BUFLEN);
+
+       while (i != end) {
+               cur_event = &event_rb->data[i];
+               with_sum = event->width + cur_event->width;
+
+               sw_params = &dfs_pd->sw_dpd_params;
+               switch (dev->dfs_pd.region) {
+               case NL80211_DFS_FCC:
+               case NL80211_DFS_JP:
+                       if (with_sum < 600)
+                               width_delta = 8;
+                       else
+                               width_delta = with_sum >> 3;
+                       break;
+               case NL80211_DFS_ETSI:
+                       if (event->engine == 2)
+                               width_delta = with_sum >> 6;
+                       else if (with_sum < 620)
+                               width_delta = 24;
+                       else
+                               width_delta = 8;
+                       break;
+               case NL80211_DFS_UNSET:
+               default:
+                       return -EINVAL;
+               }
+
+               pri = event->ts - cur_event->ts;
+               if (abs(event->width - cur_event->width) > width_delta ||
+                   pri < sw_params->min_pri)
+                       goto next;
+
+               if (pri > sw_params->max_pri)
+                       break;
+
+               seq.pri = event->ts - cur_event->ts;
+               seq.first_ts = cur_event->ts;
+               seq.last_ts = event->ts;
+               seq.engine = event->engine;
+               seq.count = 2;
+
+               j = mt76_decr(i, MT_DFS_EVENT_BUFLEN);
+               while (j != end) {
+                       cur_event = &event_rb->data[j];
+                       cur_pri = event->ts - cur_event->ts;
+                       factor = mt76x02_dfs_get_multiple(cur_pri, seq.pri,
+                                               sw_params->pri_margin);
+                       if (factor > 0) {
+                               seq.first_ts = cur_event->ts;
+                               seq.count++;
+                       }
+
+                       j = mt76_decr(j, MT_DFS_EVENT_BUFLEN);
+               }
+               if (seq.count <= cur_len)
+                       goto next;
+
+               seq_p = mt76x02_dfs_seq_pool_get(dev);
+               if (!seq_p)
+                       return -ENOMEM;
+
+               *seq_p = seq;
+               INIT_LIST_HEAD(&seq_p->head);
+               list_add(&seq_p->head, &dfs_pd->sequences);
+next:
+               i = mt76_decr(i, MT_DFS_EVENT_BUFLEN);
+       }
+       return 0;
+}
+
+static u16 mt76x02_dfs_add_event_to_sequence(struct mt76x02_dev *dev,
+                                            struct mt76x02_dfs_event *event)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+       struct mt76x02_dfs_sw_detector_params *sw_params;
+       struct mt76x02_dfs_sequence *seq, *tmp_seq;
+       u16 max_seq_len = 0;
+       u32 factor, pri;
+
+       sw_params = &dfs_pd->sw_dpd_params;
+       list_for_each_entry_safe(seq, tmp_seq, &dfs_pd->sequences, head) {
+               if (event->ts > seq->first_ts + MT_DFS_SEQUENCE_WINDOW) {
+                       list_del_init(&seq->head);
+                       mt76x02_dfs_seq_pool_put(dev, seq);
+                       continue;
+               }
+
+               if (event->engine != seq->engine)
+                       continue;
+
+               pri = event->ts - seq->last_ts;
+               factor = mt76x02_dfs_get_multiple(pri, seq->pri,
+                                                 sw_params->pri_margin);
+               if (factor > 0) {
+                       seq->last_ts = event->ts;
+                       seq->count++;
+                       max_seq_len = max_t(u16, max_seq_len, seq->count);
+               }
+       }
+       return max_seq_len;
+}
+
+static bool mt76x02_dfs_check_detection(struct mt76x02_dev *dev)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+       struct mt76x02_dfs_sequence *seq;
+
+       if (list_empty(&dfs_pd->sequences))
+               return false;
+
+       list_for_each_entry(seq, &dfs_pd->sequences, head) {
+               if (seq->count > MT_DFS_SEQUENCE_TH) {
+                       dfs_pd->stats[seq->engine].sw_pattern++;
+                       return true;
+               }
+       }
+       return false;
+}
+
+static void mt76x02_dfs_add_events(struct mt76x02_dev *dev)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+       struct mt76x02_dfs_event event;
+       int i, seq_len;
+
+       /* disable debug mode */
+       mt76x02_dfs_set_capture_mode_ctrl(dev, false);
+       for (i = 0; i < MT_DFS_EVENT_LOOP; i++) {
+               if (!mt76x02_dfs_fetch_event(dev, &event))
+                       break;
+
+               if (dfs_pd->last_event_ts > event.ts)
+                       mt76x02_dfs_detector_reset(dev);
+               dfs_pd->last_event_ts = event.ts;
+
+               if (!mt76x02_dfs_check_event(dev, &event))
+                       continue;
+
+               seq_len = mt76x02_dfs_add_event_to_sequence(dev, &event);
+               mt76x02_dfs_create_sequence(dev, &event, seq_len);
+
+               mt76x02_dfs_queue_event(dev, &event);
+       }
+       mt76x02_dfs_set_capture_mode_ctrl(dev, true);
+}
+
+static void mt76x02_dfs_check_event_window(struct mt76x02_dev *dev)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+       struct mt76x02_dfs_event_rb *event_buff;
+       struct mt76x02_dfs_event *event;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dfs_pd->event_rb); i++) {
+               event_buff = &dfs_pd->event_rb[i];
+
+               while (event_buff->h_rb != event_buff->t_rb) {
+                       event = &event_buff->data[event_buff->h_rb];
+
+                       /* sorted list */
+                       if (time_is_after_jiffies(event->fetch_ts +
+                                                 MT_DFS_EVENT_WINDOW))
+                               break;
+                       event_buff->h_rb = mt76_incr(event_buff->h_rb,
+                                                    MT_DFS_EVENT_BUFLEN);
+               }
+       }
+}
+
+static void mt76x02_dfs_tasklet(unsigned long arg)
+{
+       struct mt76x02_dev *dev = (struct mt76x02_dev *)arg;
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+       u32 engine_mask;
+       int i;
+
+       if (test_bit(MT76_SCANNING, &dev->mt76.state))
+               goto out;
+
+       if (time_is_before_jiffies(dfs_pd->last_sw_check +
+                                  MT_DFS_SW_TIMEOUT)) {
+               bool radar_detected;
+
+               dfs_pd->last_sw_check = jiffies;
+
+               mt76x02_dfs_add_events(dev);
+               radar_detected = mt76x02_dfs_check_detection(dev);
+               if (radar_detected) {
+                       /* sw detector rx radar pattern */
+                       ieee80211_radar_detected(dev->mt76.hw);
+                       mt76x02_dfs_detector_reset(dev);
+
+                       return;
+               }
+               mt76x02_dfs_check_event_window(dev);
+       }
+
+       engine_mask = mt76_rr(dev, MT_BBP(DFS, 1));
+       if (!(engine_mask & 0xf))
+               goto out;
+
+       for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
+               struct mt76x02_dfs_hw_pulse pulse;
+
+               if (!(engine_mask & (1 << i)))
+                       continue;
+
+               pulse.engine = i;
+               mt76x02_dfs_get_hw_pulse(dev, &pulse);
+
+               if (!mt76x02_dfs_check_hw_pulse(dev, &pulse)) {
+                       dfs_pd->stats[i].hw_pulse_discarded++;
+                       continue;
+               }
+
+               /* hw detector rx radar pattern */
+               dfs_pd->stats[i].hw_pattern++;
+               ieee80211_radar_detected(dev->mt76.hw);
+               mt76x02_dfs_detector_reset(dev);
+
+               return;
+       }
+
+       /* reset hw detector */
+       mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
+
+out:
+       mt76x02_irq_enable(dev, MT_INT_GPTIMER);
+}
+
+static void mt76x02_dfs_init_sw_detector(struct mt76x02_dev *dev)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+
+       switch (dev->dfs_pd.region) {
+       case NL80211_DFS_FCC:
+               dfs_pd->sw_dpd_params.max_pri = MT_DFS_FCC_MAX_PRI;
+               dfs_pd->sw_dpd_params.min_pri = MT_DFS_FCC_MIN_PRI;
+               dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN;
+               break;
+       case NL80211_DFS_ETSI:
+               dfs_pd->sw_dpd_params.max_pri = MT_DFS_ETSI_MAX_PRI;
+               dfs_pd->sw_dpd_params.min_pri = MT_DFS_ETSI_MIN_PRI;
+               dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN << 2;
+               break;
+       case NL80211_DFS_JP:
+               dfs_pd->sw_dpd_params.max_pri = MT_DFS_JP_MAX_PRI;
+               dfs_pd->sw_dpd_params.min_pri = MT_DFS_JP_MIN_PRI;
+               dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN;
+               break;
+       case NL80211_DFS_UNSET:
+       default:
+               break;
+       }
+}
+
+static void mt76x02_dfs_set_bbp_params(struct mt76x02_dev *dev)
+{
+       const struct mt76x02_radar_specs *radar_specs;
+       u8 i, shift;
+       u32 data;
+
+       switch (dev->mt76.chandef.width) {
+       case NL80211_CHAN_WIDTH_40:
+               shift = MT_DFS_NUM_ENGINES;
+               break;
+       case NL80211_CHAN_WIDTH_80:
+               shift = 2 * MT_DFS_NUM_ENGINES;
+               break;
+       default:
+               shift = 0;
+               break;
+       }
+
+       switch (dev->dfs_pd.region) {
+       case NL80211_DFS_FCC:
+               radar_specs = &fcc_radar_specs[shift];
+               break;
+       case NL80211_DFS_ETSI:
+               radar_specs = &etsi_radar_specs[shift];
+               break;
+       case NL80211_DFS_JP:
+               if (dev->mt76.chandef.chan->center_freq >= 5250 &&
+                   dev->mt76.chandef.chan->center_freq <= 5350)
+                       radar_specs = &jp_w53_radar_specs[shift];
+               else
+                       radar_specs = &jp_w56_radar_specs[shift];
+               break;
+       case NL80211_DFS_UNSET:
+       default:
+               return;
+       }
+
+       data = (MT_DFS_VGA_MASK << 16) |
+              (MT_DFS_PWR_GAIN_OFFSET << 12) |
+              (MT_DFS_PWR_DOWN_TIME << 8) |
+              (MT_DFS_SYM_ROUND << 4) |
+              (MT_DFS_DELTA_DELAY & 0xf);
+       mt76_wr(dev, MT_BBP(DFS, 2), data);
+
+       data = (MT_DFS_RX_PE_MASK << 16) | MT_DFS_PKT_END_MASK;
+       mt76_wr(dev, MT_BBP(DFS, 3), data);
+
+       for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
+               /* configure engine */
+               mt76_wr(dev, MT_BBP(DFS, 0), i);
+
+               /* detection mode + avg_len */
+               data = ((radar_specs[i].avg_len & 0x1ff) << 16) |
+                      (radar_specs[i].mode & 0xf);
+               mt76_wr(dev, MT_BBP(DFS, 4), data);
+
+               /* dfs energy */
+               data = ((radar_specs[i].e_high & 0x0fff) << 16) |
+                      (radar_specs[i].e_low & 0x0fff);
+               mt76_wr(dev, MT_BBP(DFS, 5), data);
+
+               /* dfs period */
+               mt76_wr(dev, MT_BBP(DFS, 7), radar_specs[i].t_low);
+               mt76_wr(dev, MT_BBP(DFS, 9), radar_specs[i].t_high);
+
+               /* dfs burst */
+               mt76_wr(dev, MT_BBP(DFS, 11), radar_specs[i].b_low);
+               mt76_wr(dev, MT_BBP(DFS, 13), radar_specs[i].b_high);
+
+               /* dfs width */
+               data = ((radar_specs[i].w_high & 0x0fff) << 16) |
+                      (radar_specs[i].w_low & 0x0fff);
+               mt76_wr(dev, MT_BBP(DFS, 14), data);
+
+               /* dfs margins */
+               data = (radar_specs[i].w_margin << 16) |
+                      radar_specs[i].t_margin;
+               mt76_wr(dev, MT_BBP(DFS, 15), data);
+
+               /* dfs event expiration */
+               mt76_wr(dev, MT_BBP(DFS, 17), radar_specs[i].event_expiration);
+
+               /* dfs pwr adj */
+               mt76_wr(dev, MT_BBP(DFS, 30), radar_specs[i].pwr_jmp);
+       }
+
+       /* reset status */
+       mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
+       mt76_wr(dev, MT_BBP(DFS, 36), 0x3);
+
+       /* enable detection*/
+       mt76_wr(dev, MT_BBP(DFS, 0), MT_DFS_CH_EN << 16);
+       mt76_wr(dev, MT_BBP(IBI, 11), 0x0c350001);
+}
+
+void mt76x02_phy_dfs_adjust_agc(struct mt76x02_dev *dev)
+{
+       u32 agc_r8, agc_r4, val_r8, val_r4, dfs_r31;
+
+       agc_r8 = mt76_rr(dev, MT_BBP(AGC, 8));
+       agc_r4 = mt76_rr(dev, MT_BBP(AGC, 4));
+
+       val_r8 = (agc_r8 & 0x00007e00) >> 9;
+       val_r4 = agc_r4 & ~0x1f000000;
+       val_r4 += (((val_r8 + 1) >> 1) << 24);
+       mt76_wr(dev, MT_BBP(AGC, 4), val_r4);
+
+       dfs_r31 = FIELD_GET(MT_BBP_AGC_LNA_HIGH_GAIN, val_r4);
+       dfs_r31 += val_r8;
+       dfs_r31 -= (agc_r8 & 0x00000038) >> 3;
+       dfs_r31 = (dfs_r31 << 16) | 0x00000307;
+       mt76_wr(dev, MT_BBP(DFS, 31), dfs_r31);
+
+       if (is_mt76x2(dev)) {
+               mt76_wr(dev, MT_BBP(DFS, 32), 0x00040071);
+       } else {
+               /* disable hw detector */
+               mt76_wr(dev, MT_BBP(DFS, 0), 0);
+               /* enable hw detector */
+               mt76_wr(dev, MT_BBP(DFS, 0), MT_DFS_CH_EN << 16);
+       }
+}
+EXPORT_SYMBOL_GPL(mt76x02_phy_dfs_adjust_agc);
+
+void mt76x02_dfs_init_params(struct mt76x02_dev *dev)
+{
+       struct cfg80211_chan_def *chandef = &dev->mt76.chandef;
+
+       if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) &&
+           dev->dfs_pd.region != NL80211_DFS_UNSET) {
+               mt76x02_dfs_init_sw_detector(dev);
+               mt76x02_dfs_set_bbp_params(dev);
+               /* enable debug mode */
+               mt76x02_dfs_set_capture_mode_ctrl(dev, true);
+
+               mt76x02_irq_enable(dev, MT_INT_GPTIMER);
+               mt76_rmw_field(dev, MT_INT_TIMER_EN,
+                              MT_INT_TIMER_EN_GP_TIMER_EN, 1);
+       } else {
+               /* disable hw detector */
+               mt76_wr(dev, MT_BBP(DFS, 0), 0);
+               /* clear detector status */
+               mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
+               if (mt76_chip(&dev->mt76) == 0x7610 ||
+                   mt76_chip(&dev->mt76) == 0x7630)
+                       mt76_wr(dev, MT_BBP(IBI, 11), 0xfde8081);
+               else
+                       mt76_wr(dev, MT_BBP(IBI, 11), 0);
+
+               mt76x02_irq_disable(dev, MT_INT_GPTIMER);
+               mt76_rmw_field(dev, MT_INT_TIMER_EN,
+                              MT_INT_TIMER_EN_GP_TIMER_EN, 0);
+       }
+}
+EXPORT_SYMBOL_GPL(mt76x02_dfs_init_params);
+
+void mt76x02_dfs_init_detector(struct mt76x02_dev *dev)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+
+       INIT_LIST_HEAD(&dfs_pd->sequences);
+       INIT_LIST_HEAD(&dfs_pd->seq_pool);
+       dfs_pd->region = NL80211_DFS_UNSET;
+       dfs_pd->last_sw_check = jiffies;
+       tasklet_init(&dfs_pd->dfs_tasklet, mt76x02_dfs_tasklet,
+                    (unsigned long)dev);
+}
+
+static void
+mt76x02_dfs_set_domain(struct mt76x02_dev *dev,
+                      enum nl80211_dfs_regions region)
+{
+       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
+
+       if (dfs_pd->region != region) {
+               tasklet_disable(&dfs_pd->dfs_tasklet);
+               dfs_pd->region = region;
+               mt76x02_dfs_init_params(dev);
+               tasklet_enable(&dfs_pd->dfs_tasklet);
+       }
+}
+
+void mt76x02_regd_notifier(struct wiphy *wiphy,
+                          struct regulatory_request *request)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct mt76x02_dev *dev = hw->priv;
+
+       mt76x02_dfs_set_domain(dev, request->dfs_region);
+}
index 7e177c9..70b394e 100644 (file)
@@ -137,4 +137,9 @@ struct mt76x02_dfs_pattern_detector {
        struct tasklet_struct dfs_tasklet;
 };
 
+void mt76x02_dfs_init_params(struct mt76x02_dev *dev);
+void mt76x02_dfs_init_detector(struct mt76x02_dev *dev);
+void mt76x02_regd_notifier(struct wiphy *wiphy,
+                          struct regulatory_request *request);
+void mt76x02_phy_dfs_adjust_agc(struct mt76x02_dev *dev);
 #endif /* __MT76x02_DFS_H */
index 9390de2..07f0496 100644 (file)
@@ -53,6 +53,18 @@ mt76x02_efuse_read(struct mt76x02_dev *dev, u16 addr, u8 *data,
        return 0;
 }
 
+int mt76x02_eeprom_copy(struct mt76x02_dev *dev,
+                       enum mt76x02_eeprom_field field,
+                       void *dest, int len)
+{
+       if (field + len > dev->mt76.eeprom.size)
+               return -1;
+
+       memcpy(dest, dev->mt76.eeprom.data + field, len);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt76x02_eeprom_copy);
+
 int mt76x02_get_efuse_data(struct mt76x02_dev *dev, u16 base, void *buf,
                           int len, enum mt76x02_eeprom_modes mode)
 {
index b3ec748..e3442bc 100644 (file)
@@ -25,6 +25,7 @@ enum mt76x02_eeprom_field {
        MT_EE_VERSION =                         0x002,
        MT_EE_MAC_ADDR =                        0x004,
        MT_EE_PCI_ID =                          0x00A,
+       MT_EE_ANTENNA =                         0x022,
        MT_EE_NIC_CONF_0 =                      0x034,
        MT_EE_NIC_CONF_1 =                      0x036,
        MT_EE_COUNTRY_REGION_5GHZ =             0x038,
@@ -55,6 +56,7 @@ enum mt76x02_eeprom_field {
 #define MT_TX_POWER_GROUP_SIZE_5G              5
 #define MT_TX_POWER_GROUPS_5G                  6
        MT_EE_TX_POWER_0_START_5G =             0x062,
+       MT_EE_TSSI_SLOPE_2G =                   0x06e,
 
        MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA =  0x074,
        MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE =      0x076,
@@ -85,6 +87,7 @@ enum mt76x02_eeprom_field {
        MT_EE_TSSI_BOUND5 =                     0x0dc,
        MT_EE_TX_POWER_BYRATE_BASE =            0x0de,
 
+       MT_EE_TSSI_SLOPE_5G =                   0x0f0,
        MT_EE_RF_TEMP_COMP_SLOPE_5G =           0x0f2,
        MT_EE_RF_TEMP_COMP_SLOPE_2G =           0x0f4,
 
@@ -104,6 +107,8 @@ enum mt76x02_eeprom_field {
        __MT_EE_MAX
 };
 
+#define MT_EE_ANTENNA_DUAL                     BIT(15)
+
 #define MT_EE_NIC_CONF_0_RX_PATH               GENMASK(3, 0)
 #define MT_EE_NIC_CONF_0_TX_PATH               GENMASK(7, 4)
 #define MT_EE_NIC_CONF_0_PA_TYPE               GENMASK(9, 8)
@@ -118,12 +123,9 @@ enum mt76x02_eeprom_field {
 #define MT_EE_NIC_CONF_1_LNA_EXT_5G            BIT(3)
 #define MT_EE_NIC_CONF_1_TX_ALC_EN             BIT(13)
 
-#define MT_EE_NIC_CONF_2_RX_STREAM             GENMASK(3, 0)
-#define MT_EE_NIC_CONF_2_TX_STREAM             GENMASK(7, 4)
-#define MT_EE_NIC_CONF_2_HW_ANTDIV             BIT(8)
+#define MT_EE_NIC_CONF_2_ANT_OPT               BIT(3)
+#define MT_EE_NIC_CONF_2_ANT_DIV               BIT(4)
 #define MT_EE_NIC_CONF_2_XTAL_OPTION           GENMASK(10, 9)
-#define MT_EE_NIC_CONF_2_TEMP_DISABLE          BIT(11)
-#define MT_EE_NIC_CONF_2_COEX_METHOD           GENMASK(15, 13)
 
 #define MT_EFUSE_USAGE_MAP_SIZE                        (MT_EE_USAGE_MAP_END - \
                                                 MT_EE_USAGE_MAP_START + 1)
@@ -188,5 +190,8 @@ u8 mt76x02_get_lna_gain(struct mt76x02_dev *dev,
                        s8 *lna_2g, s8 *lna_5g,
                        struct ieee80211_channel *chan);
 void mt76x02_eeprom_parse_hw_cap(struct mt76x02_dev *dev);
+int mt76x02_eeprom_copy(struct mt76x02_dev *dev,
+                       enum mt76x02_eeprom_field field,
+                       void *dest, int len);
 
 #endif /* __MT76x02_EEPROM_H */
index 10578e4..c08bf37 100644 (file)
@@ -18,7 +18,7 @@
 #include "mt76x02.h"
 #include "mt76x02_trace.h"
 
-enum mt76x02_cipher_type
+static enum mt76x02_cipher_type
 mt76x02_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data)
 {
        memset(key_data, 0, 32);
@@ -43,7 +43,6 @@ mt76x02_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data)
                return MT_CIPHER_NONE;
        }
 }
-EXPORT_SYMBOL_GPL(mt76x02_mac_get_key_info);
 
 int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx,
                                 u8 key_idx, struct ieee80211_key_conf *key)
@@ -95,7 +94,6 @@ int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(mt76x02_mac_wcid_set_key);
 
 void mt76x02_mac_wcid_setup(struct mt76x02_dev *dev, u8 idx,
                            u8 vif_idx, u8 *mac)
@@ -108,9 +106,6 @@ void mt76x02_mac_wcid_setup(struct mt76x02_dev *dev, u8 idx,
 
        mt76_wr(dev, MT_WCID_ATTR(idx), attr);
 
-       mt76_wr(dev, MT_WCID_TX_RATE(idx), 0);
-       mt76_wr(dev, MT_WCID_TX_RATE(idx) + 4, 0);
-
        if (idx >= 128)
                return;
 
@@ -130,31 +125,6 @@ void mt76x02_mac_wcid_set_drop(struct mt76x02_dev *dev, u8 idx, bool drop)
        if ((val & bit) != (bit * drop))
                mt76_wr(dev, MT_WCID_DROP(idx), (val & ~bit) | (bit * drop));
 }
-EXPORT_SYMBOL_GPL(mt76x02_mac_wcid_set_drop);
-
-void mt76x02_txq_init(struct mt76x02_dev *dev, struct ieee80211_txq *txq)
-{
-       struct mt76_txq *mtxq;
-
-       if (!txq)
-               return;
-
-       mtxq = (struct mt76_txq *) txq->drv_priv;
-       if (txq->sta) {
-               struct mt76x02_sta *sta;
-
-               sta = (struct mt76x02_sta *) txq->sta->drv_priv;
-               mtxq->wcid = &sta->wcid;
-       } else {
-               struct mt76x02_vif *mvif;
-
-               mvif = (struct mt76x02_vif *) txq->vif->drv_priv;
-               mtxq->wcid = &mvif->group_wcid;
-       }
-
-       mt76_txq_init(&dev->mt76, txq);
-}
-EXPORT_SYMBOL_GPL(mt76x02_txq_init);
 
 static __le16
 mt76x02_mac_tx_rate_val(struct mt76x02_dev *dev,
@@ -216,6 +186,14 @@ void mt76x02_mac_wcid_set_rate(struct mt76x02_dev *dev, struct mt76_wcid *wcid,
        spin_unlock_bh(&dev->mt76.lock);
 }
 
+void mt76x02_mac_set_short_preamble(struct mt76x02_dev *dev, bool enable)
+{
+       if (enable)
+               mt76_set(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT);
+       else
+               mt76_clear(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT);
+}
+
 bool mt76x02_mac_load_tx_status(struct mt76x02_dev *dev,
                                struct mt76x02_tx_status *stat)
 {
@@ -237,9 +215,10 @@ bool mt76x02_mac_load_tx_status(struct mt76x02_dev *dev,
        stat->retry = FIELD_GET(MT_TX_STAT_FIFO_EXT_RETRY, stat2);
        stat->pktid = FIELD_GET(MT_TX_STAT_FIFO_EXT_PKTID, stat2);
 
+       trace_mac_txstat_fetch(dev, stat);
+
        return true;
 }
-EXPORT_SYMBOL_GPL(mt76x02_mac_load_tx_status);
 
 static int
 mt76x02_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate,
@@ -319,8 +298,6 @@ void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi,
        else
                txwi->wcid = 0xff;
 
-       txwi->pktid = 1;
-
        if (wcid && wcid->sw_iv && key) {
                u64 pn = atomic64_inc_return(&key->tx_pn);
                ccmp_pn[0] = pn;
@@ -366,8 +343,6 @@ void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi,
                txwi->ack_ctl |= MT_TXWI_ACK_CTL_REQ;
        if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
                txwi->ack_ctl |= MT_TXWI_ACK_CTL_NSEQ;
-       if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
-               txwi->pktid |= MT_TXWI_PKTID_PROBE;
        if ((info->flags & IEEE80211_TX_CTL_AMPDU) && sta) {
                u8 ba_size = IEEE80211_MIN_AMPDU_BUF;
 
@@ -420,9 +395,6 @@ mt76x02_mac_fill_tx_status(struct mt76x02_dev *dev,
        info->status.ampdu_len = n_frames;
        info->status.ampdu_ack_len = st->success ? n_frames : 0;
 
-       if (st->pktid & MT_TXWI_PKTID_PROBE)
-               info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
-
        if (st->aggr)
                info->flags |= IEEE80211_TX_CTL_AMPDU |
                               IEEE80211_TX_STAT_AMPDU;
@@ -437,23 +409,40 @@ void mt76x02_send_tx_status(struct mt76x02_dev *dev,
                            struct mt76x02_tx_status *stat, u8 *update)
 {
        struct ieee80211_tx_info info = {};
-       struct ieee80211_sta *sta = NULL;
+       struct ieee80211_tx_status status = {
+               .info = &info
+       };
        struct mt76_wcid *wcid = NULL;
        struct mt76x02_sta *msta = NULL;
+       struct mt76_dev *mdev = &dev->mt76;
+       struct sk_buff_head list;
+
+       if (stat->pktid == MT_PACKET_ID_NO_ACK)
+               return;
 
        rcu_read_lock();
+       mt76_tx_status_lock(mdev, &list);
+
        if (stat->wcid < ARRAY_SIZE(dev->mt76.wcid))
                wcid = rcu_dereference(dev->mt76.wcid[stat->wcid]);
 
-       if (wcid) {
+       if (wcid && wcid->sta) {
                void *priv;
 
                priv = msta = container_of(wcid, struct mt76x02_sta, wcid);
-               sta = container_of(priv, struct ieee80211_sta,
-                                  drv_priv);
+               status.sta = container_of(priv, struct ieee80211_sta,
+                                         drv_priv);
+       }
+
+       if (wcid) {
+               if (stat->pktid)
+                       status.skb = mt76_tx_status_skb_get(mdev, wcid,
+                                                           stat->pktid, &list);
+               if (status.skb)
+                       status.info = IEEE80211_SKB_CB(status.skb);
        }
 
-       if (msta && stat->aggr) {
+       if (msta && stat->aggr && !status.skb) {
                u32 stat_val, stat_cache;
 
                stat_val = stat->rate;
@@ -467,25 +456,28 @@ void mt76x02_send_tx_status(struct mt76x02_dev *dev,
                        goto out;
                }
 
-               mt76x02_mac_fill_tx_status(dev, &info, &msta->status,
+               mt76x02_mac_fill_tx_status(dev, status.info, &msta->status,
                                           msta->n_frames);
 
                msta->status = *stat;
                msta->n_frames = 1;
                *update = 0;
        } else {
-               mt76x02_mac_fill_tx_status(dev, &info, stat, 1);
+               mt76x02_mac_fill_tx_status(dev, status.info, stat, 1);
                *update = 1;
        }
 
-       ieee80211_tx_status_noskb(dev->mt76.hw, sta, &info);
+       if (status.skb)
+               mt76_tx_status_skb_done(mdev, status.skb, &list);
+       else
+               ieee80211_tx_status_ext(mt76_hw(dev), &status);
 
 out:
+       mt76_tx_status_unlock(mdev, &list);
        rcu_read_unlock();
 }
-EXPORT_SYMBOL_GPL(mt76x02_send_tx_status);
 
-int
+static int
 mt76x02_mac_process_rate(struct mt76_rx_status *status, u16 rate)
 {
        u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate);
@@ -551,7 +543,6 @@ mt76x02_mac_process_rate(struct mt76_rx_status *status, u16 rate)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(mt76x02_mac_process_rate);
 
 void mt76x02_mac_setaddr(struct mt76x02_dev *dev, u8 *addr)
 {
@@ -695,8 +686,6 @@ void mt76x02_mac_poll_tx_status(struct mt76x02_dev *dev, bool irq)
                if (!ret)
                        break;
 
-               trace_mac_txstat_fetch(dev, &stat);
-
                if (!irq) {
                        mt76x02_send_tx_status(dev, &stat, &update);
                        continue;
@@ -705,33 +694,230 @@ void mt76x02_mac_poll_tx_status(struct mt76x02_dev *dev, bool irq)
                kfifo_put(&dev->txstatus_fifo, stat);
        }
 }
-EXPORT_SYMBOL_GPL(mt76x02_mac_poll_tx_status);
 
-static void
-mt76x02_mac_queue_txdone(struct mt76x02_dev *dev, struct sk_buff *skb,
-                        void *txwi_ptr)
+void mt76x02_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q,
+                            struct mt76_queue_entry *e, bool flush)
 {
-       struct mt76x02_tx_info *txi = mt76x02_skb_tx_info(skb);
-       struct mt76x02_txwi *txwi = txwi_ptr;
+       struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
+       struct mt76x02_txwi *txwi;
+
+       if (!e->txwi) {
+               dev_kfree_skb_any(e->skb);
+               return;
+       }
 
        mt76x02_mac_poll_tx_status(dev, false);
 
-       txi->tries = 0;
-       txi->jiffies = jiffies;
-       txi->wcid = txwi->wcid;
-       txi->pktid = txwi->pktid;
+       txwi = (struct mt76x02_txwi *) &e->txwi->txwi;
        trace_mac_txdone_add(dev, txwi->wcid, txwi->pktid);
-       mt76x02_tx_complete(&dev->mt76, skb);
+
+       mt76_tx_complete_skb(mdev, e->skb);
 }
+EXPORT_SYMBOL_GPL(mt76x02_tx_complete_skb);
 
-void mt76x02_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q,
-                            struct mt76_queue_entry *e, bool flush)
+void mt76x02_mac_set_tx_protection(struct mt76x02_dev *dev, u32 val)
+{
+       u32 data = 0;
+
+       if (val != ~0)
+               data = FIELD_PREP(MT_PROT_CFG_CTRL, 1) |
+                      MT_PROT_CFG_RTS_THRESH;
+
+       mt76_rmw_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH, val);
+
+       mt76_rmw(dev, MT_CCK_PROT_CFG,
+                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_update_channel(struct mt76_dev *mdev)
 {
        struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
+       struct mt76_channel_state *state;
+       u32 active, busy;
+
+       state = mt76_channel_state(&dev->mt76, dev->mt76.chandef.chan);
+
+       busy = mt76_rr(dev, MT_CH_BUSY);
+       active = busy + mt76_rr(dev, MT_CH_IDLE);
+
+       spin_lock_bh(&dev->mt76.cc_lock);
+       state->cc_busy += busy;
+       state->cc_active += active;
+       spin_unlock_bh(&dev->mt76.cc_lock);
+}
+EXPORT_SYMBOL_GPL(mt76x02_update_channel);
+
+static void mt76x02_check_mac_err(struct mt76x02_dev *dev)
+{
+       u32 val = mt76_rr(dev, 0x10f4);
+
+       if (!(val & BIT(29)) || !(val & (BIT(7) | BIT(5))))
+               return;
+
+       dev_err(dev->mt76.dev, "mac specific condition occurred\n");
+
+       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);
+}
+
+void mt76x02_mac_work(struct work_struct *work)
+{
+       struct mt76x02_dev *dev = container_of(work, struct mt76x02_dev,
+                                              mac_work.work);
+       int i, idx;
+
+       mt76x02_update_channel(&dev->mt76);
+       for (i = 0, idx = 0; i < 16; i++) {
+               u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i));
 
-       if (e->txwi)
-               mt76x02_mac_queue_txdone(dev, e->skb, &e->txwi->txwi);
+               dev->aggr_stats[idx++] += val & 0xffff;
+               dev->aggr_stats[idx++] += val >> 16;
+       }
+
+       /* XXX: check beacon stuck for ap mode */
+       if (!dev->beacon_mask)
+               mt76x02_check_mac_err(dev);
+
+       mt76_tx_status_check(&dev->mt76, NULL, false);
+
+       ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work,
+                                    MT_CALIBRATE_INTERVAL);
+}
+
+void mt76x02_mac_set_bssid(struct mt76x02_dev *dev, u8 idx, const u8 *addr)
+{
+       idx &= 7;
+       mt76_wr(dev, MT_MAC_APC_BSSID_L(idx), get_unaligned_le32(addr));
+       mt76_rmw_field(dev, MT_MAC_APC_BSSID_H(idx), MT_MAC_APC_BSSID_H_ADDR,
+                      get_unaligned_le16(addr + 4));
+}
+
+static int
+mt76x02_write_beacon(struct mt76x02_dev *dev, int offset, struct sk_buff *skb)
+{
+       int beacon_len = mt76x02_beacon_offsets[1] - mt76x02_beacon_offsets[0];
+       struct mt76x02_txwi txwi;
+
+       if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x02_txwi)))
+               return -ENOSPC;
+
+       mt76x02_mac_write_txwi(dev, &txwi, skb, NULL, NULL, skb->len);
+
+       mt76_wr_copy(dev, offset, &txwi, sizeof(txwi));
+       offset += sizeof(txwi);
+
+       mt76_wr_copy(dev, offset, skb->data, skb->len);
+       return 0;
+}
+
+static int
+__mt76x02_mac_set_beacon(struct mt76x02_dev *dev, u8 bcn_idx,
+                        struct sk_buff *skb)
+{
+       int beacon_len = mt76x02_beacon_offsets[1] - mt76x02_beacon_offsets[0];
+       int beacon_addr = mt76x02_beacon_offsets[bcn_idx];
+       int ret = 0;
+       int i;
+
+       /* Prevent corrupt transmissions during update */
+       mt76_set(dev, MT_BCN_BYPASS_MASK, BIT(bcn_idx));
+
+       if (skb) {
+               ret = mt76x02_write_beacon(dev, beacon_addr, skb);
+               if (!ret)
+                       dev->beacon_data_mask |= BIT(bcn_idx);
+       } else {
+               dev->beacon_data_mask &= ~BIT(bcn_idx);
+               for (i = 0; i < beacon_len; i += 4)
+                       mt76_wr(dev, beacon_addr + i, 0);
+       }
+
+       mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xff00 | ~dev->beacon_data_mask);
+
+       return ret;
+}
+
+int mt76x02_mac_set_beacon(struct mt76x02_dev *dev, u8 vif_idx,
+                          struct sk_buff *skb)
+{
+       bool force_update = false;
+       int bcn_idx = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dev->beacons); i++) {
+               if (vif_idx == i) {
+                       force_update = !!dev->beacons[i] ^ !!skb;
+
+                       if (dev->beacons[i])
+                               dev_kfree_skb(dev->beacons[i]);
+
+                       dev->beacons[i] = skb;
+                       __mt76x02_mac_set_beacon(dev, bcn_idx, skb);
+               } else if (force_update && dev->beacons[i]) {
+                       __mt76x02_mac_set_beacon(dev, bcn_idx,
+                                                dev->beacons[i]);
+               }
+
+               bcn_idx += !!dev->beacons[i];
+       }
+
+       for (i = bcn_idx; i < ARRAY_SIZE(dev->beacons); i++) {
+               if (!(dev->beacon_data_mask & BIT(i)))
+                       break;
+
+               __mt76x02_mac_set_beacon(dev, i, NULL);
+       }
+
+       mt76_rmw_field(dev, MT_MAC_BSSID_DW1, MT_MAC_BSSID_DW1_MBEACON_N,
+                      bcn_idx - 1);
+       return 0;
+}
+
+void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev,
+                                  u8 vif_idx, bool val)
+{
+       u8 old_mask = dev->beacon_mask;
+       bool en;
+       u32 reg;
+
+       if (val) {
+               dev->beacon_mask |= BIT(vif_idx);
+       } else {
+               dev->beacon_mask &= ~BIT(vif_idx);
+               mt76x02_mac_set_beacon(dev, vif_idx, NULL);
+       }
+
+       if (!!old_mask == !!dev->beacon_mask)
+               return;
+
+       en = dev->beacon_mask;
+
+       mt76_rmw_field(dev, MT_INT_TIMER_EN, MT_INT_TIMER_EN_PRE_TBTT_EN, en);
+       reg = MT_BEACON_TIME_CFG_BEACON_TX |
+             MT_BEACON_TIME_CFG_TBTT_EN |
+             MT_BEACON_TIME_CFG_TIMER_EN;
+       mt76_rmw(dev, MT_BEACON_TIME_CFG, reg, reg * en);
+
+       if (en)
+               mt76x02_irq_enable(dev, MT_INT_PRE_TBTT | MT_INT_TBTT);
        else
-               dev_kfree_skb_any(e->skb);
+               mt76x02_irq_disable(dev, MT_INT_PRE_TBTT | MT_INT_TBTT);
 }
-EXPORT_SYMBOL_GPL(mt76x02_tx_complete_skb);
index d99c187..4e59700 100644 (file)
@@ -37,18 +37,8 @@ struct mt76x02_tx_status {
 #define MT_MAX_VIFS            8
 
 struct mt76x02_vif {
+       struct mt76_wcid group_wcid; /* must be first */
        u8 idx;
-
-       struct mt76_wcid group_wcid;
-};
-
-struct mt76x02_tx_info {
-       unsigned long jiffies;
-       u8 tries;
-
-       u8 wcid;
-       u8 pktid;
-       u8 retry;
 };
 
 DECLARE_EWMA(signal, 10, 8);
@@ -153,8 +143,6 @@ enum mt76x2_phy_bandwidth {
 #define MT_TXWI_ACK_CTL_NSEQ           BIT(1)
 #define MT_TXWI_ACK_CTL_BA_WINDOW      GENMASK(7, 2)
 
-#define MT_TXWI_PKTID_PROBE            BIT(7)
-
 struct mt76x02_txwi {
        __le16 flags;
        __le16 rate;
@@ -190,18 +178,7 @@ static inline bool mt76x02_wait_for_mac(struct mt76_dev *dev)
        return false;
 }
 
-static inline struct mt76x02_tx_info *
-mt76x02_skb_tx_info(struct sk_buff *skb)
-{
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
-       return (void *)info->status.status_driver_data;
-}
-
-void mt76x02_txq_init(struct mt76x02_dev *dev, struct ieee80211_txq *txq);
-enum mt76x02_cipher_type
-mt76x02_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data);
-
+void mt76x02_mac_set_short_preamble(struct mt76x02_dev *dev, bool enable);
 int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx,
                                 u8 key_idx, struct ieee80211_key_conf *key);
 int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
@@ -217,8 +194,7 @@ 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);
-int
-mt76x02_mac_process_rate(struct mt76_rx_status *status, u16 rate);
+void mt76x02_mac_set_tx_protection(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,
@@ -226,4 +202,12 @@ void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi,
 void mt76x02_mac_poll_tx_status(struct mt76x02_dev *dev, bool irq);
 void mt76x02_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q,
                             struct mt76_queue_entry *e, bool flush);
+void mt76x02_update_channel(struct mt76_dev *mdev);
+void mt76x02_mac_work(struct work_struct *work);
+
+void mt76x02_mac_set_bssid(struct mt76x02_dev *dev, u8 idx, const u8 *addr);
+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);
 #endif
index 1b853bb..b7f4edb 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "mt76x02_mcu.h"
 
-struct sk_buff *mt76x02_mcu_msg_alloc(const void *data, int len)
+static struct sk_buff *mt76x02_mcu_msg_alloc(const void *data, int len)
 {
        struct sk_buff *skb;
 
@@ -32,7 +32,6 @@ struct sk_buff *mt76x02_mcu_msg_alloc(const void *data, int len)
 
        return skb;
 }
-EXPORT_SYMBOL_GPL(mt76x02_mcu_msg_alloc);
 
 static struct sk_buff *
 mt76x02_mcu_get_response(struct mt76x02_dev *dev, unsigned long expires)
@@ -80,16 +79,18 @@ mt76x02_tx_queue_mcu(struct mt76x02_dev *dev, enum mt76_txq_id qid,
        return 0;
 }
 
-int mt76x02_mcu_msg_send(struct mt76_dev *mdev, struct sk_buff *skb,
-                        int cmd, bool wait_resp)
+int mt76x02_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data,
+                        int len, bool wait_resp)
 {
        struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
        unsigned long expires = jiffies + HZ;
+       struct sk_buff *skb;
        int ret;
        u8 seq;
 
+       skb = mt76x02_mcu_msg_alloc(data, len);
        if (!skb)
-               return -EINVAL;
+               return -ENOMEM;
 
        mutex_lock(&mdev->mmio.mcu.mutex);
 
@@ -131,11 +132,9 @@ out:
 }
 EXPORT_SYMBOL_GPL(mt76x02_mcu_msg_send);
 
-int mt76x02_mcu_function_select(struct mt76x02_dev *dev,
-                               enum mcu_function func,
-                               u32 val, bool wait_resp)
+int mt76x02_mcu_function_select(struct mt76x02_dev *dev, enum mcu_function func,
+                               u32 val)
 {
-       struct sk_buff *skb;
        struct {
            __le32 id;
            __le32 value;
@@ -143,16 +142,17 @@ int mt76x02_mcu_function_select(struct mt76x02_dev *dev,
            .id = cpu_to_le32(func),
            .value = cpu_to_le32(val),
        };
+       bool wait = false;
 
-       skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
-       return mt76_mcu_send_msg(dev, skb, CMD_FUN_SET_OP, wait_resp);
+       if (func != Q_SELECT)
+               wait = true;
+
+       return mt76_mcu_send_msg(dev, CMD_FUN_SET_OP, &msg, sizeof(msg), wait);
 }
 EXPORT_SYMBOL_GPL(mt76x02_mcu_function_select);
 
-int mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on,
-                               bool wait_resp)
+int mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on)
 {
-       struct sk_buff *skb;
        struct {
                __le32 mode;
                __le32 level;
@@ -161,15 +161,12 @@ int mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on,
                .level = cpu_to_le32(0),
        };
 
-       skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
-       return mt76_mcu_send_msg(dev, skb, CMD_POWER_SAVING_OP, wait_resp);
+       return mt76_mcu_send_msg(dev, CMD_POWER_SAVING_OP, &msg, sizeof(msg), false);
 }
 EXPORT_SYMBOL_GPL(mt76x02_mcu_set_radio_state);
 
-int mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type,
-                         u32 param, bool wait)
+int mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type, u32 param)
 {
-       struct sk_buff *skb;
        struct {
                __le32 id;
                __le32 value;
@@ -177,17 +174,18 @@ int mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type,
                .id = cpu_to_le32(type),
                .value = cpu_to_le32(param),
        };
+       bool is_mt76x2e = mt76_is_mmio(dev) && is_mt76x2(dev);
        int ret;
 
-       if (wait)
+       if (is_mt76x2e)
                mt76_rmw(dev, MT_MCU_COM_REG0, BIT(31), 0);
 
-       skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
-       ret = mt76_mcu_send_msg(dev, skb, CMD_CALIBRATION_OP, true);
+       ret = mt76_mcu_send_msg(dev, CMD_CALIBRATION_OP, &msg, sizeof(msg),
+                               true);
        if (ret)
                return ret;
 
-       if (wait &&
+       if (is_mt76x2e &&
            WARN_ON(!mt76_poll_msec(dev, MT_MCU_COM_REG0,
                                    BIT(31), BIT(31), 100)))
                return -ETIMEDOUT;
index 2d8fd25..7e40041 100644 (file)
@@ -97,16 +97,12 @@ struct mt76x02_patch_header {
 };
 
 int mt76x02_mcu_cleanup(struct mt76x02_dev *dev);
-int mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type,
-                         u32 param, bool wait);
-struct sk_buff *mt76x02_mcu_msg_alloc(const void *data, int len);
-int mt76x02_mcu_msg_send(struct mt76_dev *mdev, struct sk_buff *skb,
-                        int cmd, bool wait_resp);
-int mt76x02_mcu_function_select(struct mt76x02_dev *dev,
-                               enum mcu_function func,
-                               u32 val, bool wait_resp);
-int mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on,
-                               bool wait_resp);
+int mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type, u32 param);
+int mt76x02_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data,
+                        int len, bool wait_resp);
+int mt76x02_mcu_function_select(struct mt76x02_dev *dev, enum mcu_function func,
+                               u32 val);
+int mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on);
 void mt76x02_set_ethtool_fwver(struct mt76x02_dev *dev,
                               const struct mt76x02_fw_header *h);
 
index 39f0920..6631541 100644 (file)
 #include "mt76x02.h"
 #include "mt76x02_trace.h"
 
+struct beacon_bc_data {
+       struct mt76x02_dev *dev;
+       struct sk_buff_head q;
+       struct sk_buff *tail[8];
+};
+
+static void
+mt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct mt76x02_dev *dev = (struct mt76x02_dev *)priv;
+       struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
+       struct sk_buff *skb = NULL;
+
+       if (!(dev->beacon_mask & BIT(mvif->idx)))
+               return;
+
+       skb = ieee80211_beacon_get(mt76_hw(dev), vif);
+       if (!skb)
+               return;
+
+       mt76x02_mac_set_beacon(dev, mvif->idx, skb);
+}
+
+static void
+mt76x02_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct beacon_bc_data *data = priv;
+       struct mt76x02_dev *dev = data->dev;
+       struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
+       struct ieee80211_tx_info *info;
+       struct sk_buff *skb;
+
+       if (!(dev->beacon_mask & BIT(mvif->idx)))
+               return;
+
+       skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif);
+       if (!skb)
+               return;
+
+       info = IEEE80211_SKB_CB(skb);
+       info->control.vif = vif;
+       info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
+       mt76_skb_set_moredata(skb, true);
+       __skb_queue_tail(&data->q, skb);
+       data->tail[mvif->idx] = skb;
+}
+
+static void
+mt76x02_resync_beacon_timer(struct mt76x02_dev *dev)
+{
+       u32 timer_val = dev->beacon_int << 4;
+
+       dev->tbtt_count++;
+
+       /*
+        * Beacon timer drifts by 1us every tick, the timer is configured
+        * in 1/16 TU (64us) units.
+        */
+       if (dev->tbtt_count < 62)
+               return;
+
+       if (dev->tbtt_count >= 64) {
+               dev->tbtt_count = 0;
+               return;
+       }
+
+       /*
+        * The updated beacon interval takes effect after two TBTT, because
+        * at this point the original interval has already been loaded into
+        * the next TBTT_TIMER value
+        */
+       if (dev->tbtt_count == 62)
+               timer_val -= 1;
+
+       mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
+                      MT_BEACON_TIME_CFG_INTVAL, timer_val);
+}
+
+static void mt76x02_pre_tbtt_tasklet(unsigned long arg)
+{
+       struct mt76x02_dev *dev = (struct mt76x02_dev *)arg;
+       struct mt76_queue *q = &dev->mt76.q_tx[MT_TXQ_PSD];
+       struct beacon_bc_data data = {};
+       struct sk_buff *skb;
+       int i, nframes;
+
+       mt76x02_resync_beacon_timer(dev);
+
+       data.dev = dev;
+       __skb_queue_head_init(&data.q);
+
+       ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
+               IEEE80211_IFACE_ITER_RESUME_ALL,
+               mt76x02_update_beacon_iter, dev);
+
+       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));
+
+       if (!nframes)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(data.tail); i++) {
+               if (!data.tail[i])
+                       continue;
+
+               mt76_skb_set_moredata(data.tail[i], false);
+       }
+
+       spin_lock_bh(&q->lock);
+       while ((skb = __skb_dequeue(&data.q)) != NULL) {
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+               struct ieee80211_vif *vif = info->control.vif;
+               struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
+
+               mt76_dma_tx_queue_skb(&dev->mt76, q, skb, &mvif->group_wcid,
+                                     NULL);
+       }
+       spin_unlock_bh(&q->lock);
+}
+
 static int
 mt76x02_init_tx_queue(struct mt76x02_dev *dev, struct mt76_queue *q,
                      int idx, int n_desc)
@@ -98,6 +222,9 @@ int mt76x02_dma_init(struct mt76x02_dev *dev)
                return -ENOMEM;
 
        tasklet_init(&dev->tx_tasklet, mt76x02_tx_tasklet, (unsigned long) dev);
+       tasklet_init(&dev->pre_tbtt_tasklet, mt76x02_pre_tbtt_tasklet,
+                    (unsigned long)dev);
+
        kfifo_init(&dev->txstatus_fifo, status_fifo, fifo_size);
 
        mt76_dma_attach(&dev->mt76);
@@ -225,7 +352,6 @@ static void mt76x02_dma_enable(struct mt76x02_dev *dev)
        mt76_clear(dev, MT_WPDMA_GLO_CFG,
                   MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE);
 }
-EXPORT_SYMBOL_GPL(mt76x02_dma_enable);
 
 void mt76x02_dma_cleanup(struct mt76x02_dev *dev)
 {
index 0f1d7b5..977a8e7 100644 (file)
@@ -254,5 +254,6 @@ void mt76x02_init_agc_gain(struct mt76x02_dev *dev)
        memcpy(dev->cal.agc_gain_cur, dev->cal.agc_gain_init,
               sizeof(dev->cal.agc_gain_cur));
        dev->cal.low_gain = -1;
+       dev->cal.gain_init_done = true;
 }
 EXPORT_SYMBOL_GPL(mt76x02_init_agc_gain);
index d3de088..4598cb2 100644 (file)
@@ -22,6 +22,7 @@
 void mt76x02_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
                struct sk_buff *skb)
 {
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct mt76x02_dev *dev = hw->priv;
        struct ieee80211_vif *vif = info->control.vif;
@@ -33,7 +34,8 @@ void mt76x02_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
                msta = (struct mt76x02_sta *)control->sta->drv_priv;
                wcid = &msta->wcid;
                /* sw encrypted frames */
-               if (!info->control.hw_key && wcid->hw_key_idx != 0xff)
+               if (!info->control.hw_key && wcid->hw_key_idx != 0xff &&
+                   ieee80211_has_protected(hdr->frame_control))
                        control->sta = NULL;
        }
 
@@ -110,7 +112,6 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev,
 
        return max_txpwr;
 }
-EXPORT_SYMBOL_GPL(mt76x02_tx_get_max_txpwr_adj);
 
 s8 mt76x02_tx_get_txpwr_adj(struct mt76x02_dev *dev, s8 txpwr, s8 max_txpwr_adj)
 {
@@ -125,7 +126,6 @@ s8 mt76x02_tx_get_txpwr_adj(struct mt76x02_dev *dev, s8 txpwr, s8 max_txpwr_adj)
        else
                return (txpwr < -16) ? 8 : (txpwr + 32) / 2;
 }
-EXPORT_SYMBOL_GPL(mt76x02_tx_get_txpwr_adj);
 
 void mt76x02_tx_set_txpwr_auto(struct mt76x02_dev *dev, s8 txpwr)
 {
@@ -140,21 +140,6 @@ void mt76x02_tx_set_txpwr_auto(struct mt76x02_dev *dev, s8 txpwr)
 }
 EXPORT_SYMBOL_GPL(mt76x02_tx_set_txpwr_auto);
 
-void mt76x02_tx_complete(struct mt76_dev *dev, struct sk_buff *skb)
-{
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
-       if (info->flags & IEEE80211_TX_CTL_AMPDU) {
-               ieee80211_free_txskb(dev->hw, skb);
-       } else {
-               ieee80211_tx_info_clear_status(info);
-               info->status.rates[0].idx = -1;
-               info->flags |= IEEE80211_TX_STAT_ACK;
-               ieee80211_tx_status(dev->hw, skb);
-       }
-}
-EXPORT_SYMBOL_GPL(mt76x02_tx_complete);
-
 bool mt76x02_tx_status_data(struct mt76_dev *mdev, u8 *update)
 {
        struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
@@ -169,14 +154,15 @@ bool mt76x02_tx_status_data(struct mt76_dev *mdev, u8 *update)
 }
 EXPORT_SYMBOL_GPL(mt76x02_tx_status_data);
 
-int mt76x02_tx_prepare_skb(struct mt76_dev *mdev, void *txwi,
+int mt76x02_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
                           struct sk_buff *skb, struct mt76_queue *q,
                           struct mt76_wcid *wcid, struct ieee80211_sta *sta,
                           u32 *tx_info)
 {
        struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct mt76x02_txwi *txwi = txwi_ptr;
        int qsel = MT_QSEL_EDCA;
+       int pid;
        int ret;
 
        if (q == &dev->mt76.q_tx[MT_TXQ_PSD] && wcid && wcid->idx < 128)
@@ -184,11 +170,14 @@ int mt76x02_tx_prepare_skb(struct mt76_dev *mdev, void *txwi,
 
        mt76x02_mac_write_txwi(dev, txwi, skb, wcid, sta, skb->len);
 
+       pid = mt76_tx_status_skb_add(mdev, wcid, skb);
+       txwi->pktid = pid;
+
        ret = mt76x02_insert_hdr_pad(skb);
        if (ret < 0)
                return ret;
 
-       if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
+       if (pid && pid != MT_PACKET_ID_NO_ACK)
                qsel = MT_QSEL_MGMT;
 
        *tx_info = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) |
index dc2226c..81970cf 100644 (file)
@@ -30,7 +30,7 @@ void mt76x02u_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q,
                              struct mt76_queue_entry *e, bool flush)
 {
        mt76x02u_remove_dma_hdr(e->skb);
-       mt76x02_tx_complete(mdev, e->skb);
+       mt76_tx_complete_skb(mdev, e->skb);
 }
 EXPORT_SYMBOL_GPL(mt76x02u_tx_complete_skb);
 
@@ -67,27 +67,6 @@ int mt76x02u_skb_dma_info(struct sk_buff *skb, int port, u32 flags)
        return 0;
 }
 
-static int
-mt76x02u_set_txinfo(struct sk_buff *skb, struct mt76_wcid *wcid, u8 ep)
-{
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       enum mt76_qsel qsel;
-       u32 flags;
-
-       if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) ||
-           ep == MT_EP_OUT_HCCA)
-               qsel = MT_QSEL_MGMT;
-       else
-               qsel = MT_QSEL_EDCA;
-
-       flags = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) |
-               MT_TXD_INFO_80211;
-       if (!wcid || wcid->hw_key_idx == 0xff || wcid->sw_iv)
-               flags |= MT_TXD_INFO_WIV;
-
-       return mt76x02u_skb_dma_info(skb, WLAN_PORT, flags);
-}
-
 int mt76x02u_tx_prepare_skb(struct mt76_dev *mdev, void *data,
                            struct sk_buff *skb, struct mt76_queue *q,
                            struct mt76_wcid *wcid, struct ieee80211_sta *sta,
@@ -95,13 +74,30 @@ int mt76x02u_tx_prepare_skb(struct mt76_dev *mdev, void *data,
 {
        struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
        struct mt76x02_txwi *txwi;
+       enum mt76_qsel qsel;
        int len = skb->len;
+       u32 flags;
+       int pid;
 
        mt76x02_insert_hdr_pad(skb);
 
        txwi = skb_push(skb, sizeof(struct mt76x02_txwi));
        mt76x02_mac_write_txwi(dev, txwi, skb, wcid, sta, len);
 
-       return mt76x02u_set_txinfo(skb, wcid, q2ep(q->hw_idx));
+       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)
+               qsel = MT_QSEL_MGMT;
+       else
+               qsel = MT_QSEL_EDCA;
+
+       flags = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) |
+               MT_TXD_INFO_80211;
+       if (!wcid || wcid->hw_key_idx == 0xff || wcid->sw_iv)
+               flags |= MT_TXD_INFO_WIV;
+
+       return mt76x02u_skb_dma_info(skb, WLAN_PORT, flags);
 }
 EXPORT_SYMBOL_GPL(mt76x02u_tx_prepare_skb);
index da299b8..6db789f 100644 (file)
@@ -129,9 +129,6 @@ __mt76x02u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb,
        u8 seq = 0;
        u32 info;
 
-       if (!skb)
-               return -EINVAL;
-
        if (test_bit(MT76_REMOVED, &dev->state))
                return 0;
 
@@ -162,12 +159,17 @@ __mt76x02u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb,
 }
 
 static int
-mt76x02u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb,
-                     int cmd, bool wait_resp)
+mt76x02u_mcu_send_msg(struct mt76_dev *dev, int cmd, const void *data,
+                     int len, bool wait_resp)
 {
        struct mt76_usb *usb = &dev->usb;
+       struct sk_buff *skb;
        int err;
 
+       skb = mt76x02u_mcu_msg_alloc(data, len);
+       if (!skb)
+               return -ENOMEM;
+
        mutex_lock(&usb->mcu.mutex);
        err = __mt76x02u_mcu_send_msg(dev, skb, cmd, wait_resp);
        mutex_unlock(&usb->mcu.mutex);
@@ -186,6 +188,7 @@ mt76x02u_mcu_wr_rp(struct mt76_dev *dev, u32 base,
 {
        const int CMD_RANDOM_WRITE = 12;
        const int max_vals_per_cmd = MT_INBAND_PACKET_MAX_LEN / 8;
+       struct mt76_usb *usb = &dev->usb;
        struct sk_buff *skb;
        int cnt, i, ret;
 
@@ -204,7 +207,9 @@ mt76x02u_mcu_wr_rp(struct mt76_dev *dev, u32 base,
                skb_put_le32(skb, data[i].value);
        }
 
-       ret = mt76x02u_mcu_send_msg(dev, skb, CMD_RANDOM_WRITE, cnt == n);
+       mutex_lock(&usb->mcu.mutex);
+       ret = __mt76x02u_mcu_send_msg(dev, skb, CMD_RANDOM_WRITE, cnt == n);
+       mutex_unlock(&usb->mcu.mutex);
        if (ret)
                return ret;
 
@@ -345,7 +350,6 @@ EXPORT_SYMBOL_GPL(mt76x02u_mcu_fw_send_data);
 void mt76x02u_init_mcu(struct mt76_dev *dev)
 {
        static const struct mt76_mcu_ops mt76x02u_mcu_ops = {
-               .mcu_msg_alloc = mt76x02u_mcu_msg_alloc,
                .mcu_send_msg = mt76x02u_mcu_send_msg,
                .mcu_wr_rp = mt76x02u_mcu_wr_rp,
                .mcu_rd_rp = mt76x02u_mcu_rd_rp,
index ca05332..38bd466 100644 (file)
@@ -47,6 +47,92 @@ struct ieee80211_rate mt76x02_rates[] = {
 };
 EXPORT_SYMBOL_GPL(mt76x02_rates);
 
+static const struct ieee80211_iface_limit mt76x02_if_limits[] = {
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_ADHOC)
+       }, {
+               .max = 8,
+               .types = BIT(NL80211_IFTYPE_STATION) |
+#ifdef CONFIG_MAC80211_MESH
+                        BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+                        BIT(NL80211_IFTYPE_AP)
+        },
+};
+
+static const struct ieee80211_iface_combination mt76x02_if_comb[] = {
+       {
+               .limits = mt76x02_if_limits,
+               .n_limits = ARRAY_SIZE(mt76x02_if_limits),
+               .max_interfaces = 8,
+               .num_different_channels = 1,
+               .beacon_int_infra_match = true,
+               .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                      BIT(NL80211_CHAN_WIDTH_20) |
+                                      BIT(NL80211_CHAN_WIDTH_40) |
+                                      BIT(NL80211_CHAN_WIDTH_80),
+       }
+};
+
+void mt76x02_init_device(struct mt76x02_dev *dev)
+{
+       struct ieee80211_hw *hw = mt76_hw(dev);
+       struct wiphy *wiphy = hw->wiphy;
+
+       INIT_DELAYED_WORK(&dev->mac_work, mt76x02_mac_work);
+
+       hw->queues = 4;
+       hw->max_rates = 1;
+       hw->max_report_rates = 7;
+       hw->max_rate_tries = 1;
+       hw->extra_tx_headroom = 2;
+
+       if (mt76_is_usb(dev)) {
+               hw->extra_tx_headroom += sizeof(struct mt76x02_txwi) +
+                                        MT_DMA_HDR_LEN;
+               wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+       } else {
+               mt76x02_dfs_init_detector(dev);
+
+               wiphy->reg_notifier = mt76x02_regd_notifier;
+               wiphy->iface_combinations = mt76x02_if_comb;
+               wiphy->n_iface_combinations = ARRAY_SIZE(mt76x02_if_comb);
+               wiphy->interface_modes =
+                       BIT(NL80211_IFTYPE_STATION) |
+                       BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+                       BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+                       BIT(NL80211_IFTYPE_ADHOC);
+
+               wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
+       }
+
+       hw->sta_data_size = sizeof(struct mt76x02_sta);
+       hw->vif_data_size = sizeof(struct mt76x02_vif);
+
+       ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES);
+       ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
+
+       dev->mt76.global_wcid.idx = 255;
+       dev->mt76.global_wcid.hw_key_idx = -1;
+       dev->slottime = 9;
+
+       if (is_mt76x2(dev)) {
+               dev->mt76.sband_2g.sband.ht_cap.cap |=
+                               IEEE80211_HT_CAP_LDPC_CODING;
+               dev->mt76.sband_5g.sband.ht_cap.cap |=
+                               IEEE80211_HT_CAP_LDPC_CODING;
+               dev->mt76.chainmask = 0x202;
+               dev->mt76.antenna_mask = 3;
+       } else {
+               dev->mt76.chainmask = 0x101;
+               dev->mt76.antenna_mask = 1;
+       }
+}
+EXPORT_SYMBOL_GPL(mt76x02_init_device);
+
 void mt76x02_configure_filter(struct ieee80211_hw *hw,
                              unsigned int changed_flags,
                              unsigned int *total_flags, u64 multicast)
@@ -81,23 +167,17 @@ void mt76x02_configure_filter(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL_GPL(mt76x02_configure_filter);
 
-int mt76x02_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+int mt76x02_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
                    struct ieee80211_sta *sta)
 {
-       struct mt76x02_dev *dev = hw->priv;
+       struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
        struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv;
        struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
-       int ret = 0;
        int idx = 0;
-       int i;
-
-       mutex_lock(&dev->mt76.mutex);
 
        idx = mt76_wcid_alloc(dev->mt76.wcid_mask, ARRAY_SIZE(dev->mt76.wcid));
-       if (idx < 0) {
-               ret = -ENOSPC;
-               goto out;
-       }
+       if (idx < 0)
+               return -ENOSPC;
 
        msta->vif = mvif;
        msta->wcid.sta = 1;
@@ -105,41 +185,25 @@ int mt76x02_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        msta->wcid.hw_key_idx = -1;
        mt76x02_mac_wcid_setup(dev, idx, mvif->idx, sta->addr);
        mt76x02_mac_wcid_set_drop(dev, idx, false);
-       for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
-               mt76x02_txq_init(dev, sta->txq[i]);
 
        if (vif->type == NL80211_IFTYPE_AP)
                set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags);
 
        ewma_signal_init(&msta->rssi);
 
-       rcu_assign_pointer(dev->mt76.wcid[idx], &msta->wcid);
-
-out:
-       mutex_unlock(&dev->mt76.mutex);
-
-       return ret;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(mt76x02_sta_add);
 
-int mt76x02_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                      struct ieee80211_sta *sta)
+void mt76x02_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta)
 {
-       struct mt76x02_dev *dev = hw->priv;
-       struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv;
-       int idx = msta->wcid.idx;
-       int i;
+       struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
+       struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
+       int idx = wcid->idx;
 
-       mutex_lock(&dev->mt76.mutex);
-       rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
-       for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
-               mt76_txq_remove(&dev->mt76, sta->txq[i]);
        mt76x02_mac_wcid_set_drop(dev, idx, true);
-       mt76_wcid_free(dev->mt76.wcid_mask, idx);
        mt76x02_mac_wcid_setup(dev, idx, 0, NULL);
-       mutex_unlock(&dev->mt76.mutex);
-
-       return 0;
 }
 EXPORT_SYMBOL_GPL(mt76x02_sta_remove);
 
@@ -147,11 +211,15 @@ void mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif,
                      unsigned int idx)
 {
        struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
+       struct mt76_txq *mtxq;
 
        mvif->idx = idx;
        mvif->group_wcid.idx = MT_VIF_WCID(idx);
        mvif->group_wcid.hw_key_idx = -1;
-       mt76x02_txq_init(dev, vif->txq);
+       mtxq = (struct mt76_txq *) vif->txq->drv_priv;
+       mtxq->wcid = &mvif->group_wcid;
+
+       mt76_txq_init(&dev->mt76, vif->txq);
 }
 EXPORT_SYMBOL_GPL(mt76x02_vif_init);
 
@@ -357,6 +425,51 @@ int mt76x02_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 }
 EXPORT_SYMBOL_GPL(mt76x02_conf_tx);
 
+void mt76x02_set_tx_ackto(struct mt76x02_dev *dev)
+{
+       u8 ackto, sifs, slottime = dev->slottime;
+
+       /* As defined by IEEE 802.11-2007 17.3.8.6 */
+       slottime += 3 * dev->coverage_class;
+       mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG,
+                      MT_BKOFF_SLOT_CFG_SLOTTIME, slottime);
+
+       sifs = mt76_get_field(dev, MT_XIFS_TIME_CFG,
+                             MT_XIFS_TIME_CFG_OFDM_SIFS);
+
+       ackto = slottime + sifs;
+       mt76_rmw_field(dev, MT_TX_TIMEOUT_CFG,
+                      MT_TX_TIMEOUT_CFG_ACKTO, ackto);
+}
+EXPORT_SYMBOL_GPL(mt76x02_set_tx_ackto);
+
+void mt76x02_set_coverage_class(struct ieee80211_hw *hw,
+                               s16 coverage_class)
+{
+       struct mt76x02_dev *dev = hw->priv;
+
+       mutex_lock(&dev->mt76.mutex);
+       dev->coverage_class = coverage_class;
+       mt76x02_set_tx_ackto(dev);
+       mutex_unlock(&dev->mt76.mutex);
+}
+EXPORT_SYMBOL_GPL(mt76x02_set_coverage_class);
+
+int mt76x02_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
+{
+       struct mt76x02_dev *dev = hw->priv;
+
+       if (val != ~0 && val > 0xffff)
+               return -EINVAL;
+
+       mutex_lock(&dev->mt76.mutex);
+       mt76x02_mac_set_tx_protection(dev, val);
+       mutex_unlock(&dev->mt76.mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt76x02_set_rts_threshold);
+
 void mt76x02_sta_rate_tbl_update(struct ieee80211_hw *hw,
                                struct ieee80211_vif *vif,
                                struct ieee80211_sta *sta)
@@ -405,6 +518,64 @@ void mt76x02_remove_hdr_pad(struct sk_buff *skb, int len)
 }
 EXPORT_SYMBOL_GPL(mt76x02_remove_hdr_pad);
 
+void mt76x02_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                    const u8 *mac)
+{
+       struct mt76x02_dev *dev = hw->priv;
+
+       if (mt76_is_mmio(dev))
+               tasklet_disable(&dev->pre_tbtt_tasklet);
+       set_bit(MT76_SCANNING, &dev->mt76.state);
+}
+EXPORT_SYMBOL_GPL(mt76x02_sw_scan);
+
+void mt76x02_sw_scan_complete(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif)
+{
+       struct mt76x02_dev *dev = hw->priv;
+
+       clear_bit(MT76_SCANNING, &dev->mt76.state);
+       if (mt76_is_mmio(dev))
+               tasklet_enable(&dev->pre_tbtt_tasklet);
+
+       if (dev->cal.gain_init_done) {
+               /* Restore AGC gain and resume calibration after scanning. */
+               dev->cal.low_gain = -1;
+               ieee80211_queue_delayed_work(hw, &dev->cal_work, 0);
+       }
+}
+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)
+{
+       struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
+       struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv;
+       int idx = msta->wcid.idx;
+
+       mt76_stop_tx_queues(&dev->mt76, sta, true);
+       mt76x02_mac_wcid_set_drop(dev, idx, ps);
+}
+EXPORT_SYMBOL_GPL(mt76x02_sta_ps);
+
 const u16 mt76x02_beacon_offsets[16] = {
        /* 1024 byte per beacon */
        0xc000,
@@ -425,9 +596,8 @@ const u16 mt76x02_beacon_offsets[16] = {
        0xc000,
        0xc000,
 };
-EXPORT_SYMBOL_GPL(mt76x02_beacon_offsets);
 
-void mt76x02_set_beacon_offsets(struct mt76x02_dev *dev)
+static void mt76x02_set_beacon_offsets(struct mt76x02_dev *dev)
 {
        u16 val, base = MT_BEACON_BASE;
        u32 regs[4] = {};
@@ -441,6 +611,98 @@ void mt76x02_set_beacon_offsets(struct mt76x02_dev *dev)
        for (i = 0; i < 4; i++)
                mt76_wr(dev, MT_BCN_OFFSET(i), regs[i]);
 }
-EXPORT_SYMBOL_GPL(mt76x02_set_beacon_offsets);
+
+void mt76x02_init_beacon_config(struct mt76x02_dev *dev)
+{
+       static const u8 null_addr[ETH_ALEN] = {};
+       int i;
+
+       mt76_wr(dev, MT_MAC_BSSID_DW0,
+               get_unaligned_le32(dev->mt76.macaddr));
+       mt76_wr(dev, MT_MAC_BSSID_DW1,
+               get_unaligned_le16(dev->mt76.macaddr + 4) |
+               FIELD_PREP(MT_MAC_BSSID_DW1_MBSS_MODE, 3) | /* 8 beacons */
+               MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT);
+
+       /* Fire a pre-TBTT interrupt 8 ms before TBTT */
+       mt76_rmw_field(dev, MT_INT_TIMER_CFG, MT_INT_TIMER_CFG_PRE_TBTT,
+                      8 << 4);
+       mt76_rmw_field(dev, MT_INT_TIMER_CFG, MT_INT_TIMER_CFG_GP_TIMER,
+                      MT_DFS_GP_INTERVAL);
+       mt76_wr(dev, MT_INT_TIMER_EN, 0);
+
+       mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xffff);
+
+       for (i = 0; i < 8; i++) {
+               mt76x02_mac_set_bssid(dev, i, null_addr);
+               mt76x02_mac_set_beacon(dev, i, NULL);
+       }
+       mt76x02_set_beacon_offsets(dev);
+}
+EXPORT_SYMBOL_GPL(mt76x02_init_beacon_config);
+
+void mt76x02_bss_info_changed(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_bss_conf *info,
+                             u32 changed)
+{
+       struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
+       struct mt76x02_dev *dev = hw->priv;
+
+       mutex_lock(&dev->mt76.mutex);
+
+       if (changed & BSS_CHANGED_BSSID)
+               mt76x02_mac_set_bssid(dev, mvif->idx, info->bssid);
+
+       if (changed & BSS_CHANGED_BEACON_ENABLED) {
+               tasklet_disable(&dev->pre_tbtt_tasklet);
+               mt76x02_mac_set_beacon_enable(dev, mvif->idx,
+                                             info->enable_beacon);
+               tasklet_enable(&dev->pre_tbtt_tasklet);
+       }
+
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
+                              MT_BEACON_TIME_CFG_INTVAL,
+                              info->beacon_int << 4);
+               dev->beacon_int = info->beacon_int;
+               dev->tbtt_count = 0;
+       }
+
+       if (changed & BSS_CHANGED_ERP_PREAMBLE)
+               mt76x02_mac_set_short_preamble(dev, info->use_short_preamble);
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               int slottime = info->use_short_slot ? 9 : 20;
+
+               dev->slottime = slottime;
+               mt76x02_set_tx_ackto(dev);
+       }
+
+       mutex_unlock(&dev->mt76.mutex);
+}
+EXPORT_SYMBOL_GPL(mt76x02_bss_info_changed);
+
+void mt76x02_config_mac_addr_list(struct mt76x02_dev *dev)
+{
+       struct ieee80211_hw *hw = mt76_hw(dev);
+       struct wiphy *wiphy = hw->wiphy;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dev->macaddr_list); i++) {
+               u8 *addr = dev->macaddr_list[i].addr;
+
+               memcpy(addr, dev->mt76.macaddr, ETH_ALEN);
+
+               if (!i)
+                       continue;
+
+               addr[0] |= BIT(1);
+               addr[0] ^= ((i - 1) << 2);
+       }
+       wiphy->addresses = dev->macaddr_list;
+       wiphy->n_addresses = ARRAY_SIZE(dev->macaddr_list);
+}
+EXPORT_SYMBOL_GPL(mt76x02_config_mac_addr_list);
 
 MODULE_LICENSE("Dual BSD/GPL");
index b71bb10..9297b85 100644 (file)
@@ -3,11 +3,11 @@ obj-$(CONFIG_MT76x2E) += mt76x2e.o
 obj-$(CONFIG_MT76x2U) += mt76x2u.o
 
 mt76x2-common-y := \
-       eeprom.o mac.o init.o phy.o debugfs.o mcu.o
+       eeprom.o mac.o init.o phy.o mcu.o
 
 mt76x2e-y := \
-       pci.o pci_main.o pci_init.o pci_tx.o \
-       pci_mac.o pci_mcu.o pci_phy.o pci_dfs.o
+       pci.o pci_main.o pci_init.o pci_mcu.o \
+       pci_phy.o
 
 mt76x2u-y := \
        usb.o usb_init.o usb_main.o usb_mac.o usb_mcu.o \
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x2/debugfs.c
deleted file mode 100644 (file)
index e8f8ccc..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
- *
- * 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.
- */
-
-#include <linux/debugfs.h>
-#include "mt76x2.h"
-
-static int
-mt76x2_ampdu_stat_read(struct seq_file *file, void *data)
-{
-       struct mt76x02_dev *dev = file->private;
-       int i, j;
-
-       for (i = 0; i < 4; i++) {
-               seq_puts(file, "Length: ");
-               for (j = 0; j < 8; j++)
-                       seq_printf(file, "%8d | ", i * 8 + j + 1);
-               seq_puts(file, "\n");
-               seq_puts(file, "Count:  ");
-               for (j = 0; j < 8; j++)
-                       seq_printf(file, "%8d | ", dev->aggr_stats[i * 8 + j]);
-               seq_puts(file, "\n");
-               seq_puts(file, "--------");
-               for (j = 0; j < 8; j++)
-                       seq_puts(file, "-----------");
-               seq_puts(file, "\n");
-       }
-
-       return 0;
-}
-
-static int
-mt76x2_ampdu_stat_open(struct inode *inode, struct file *f)
-{
-       return single_open(f, mt76x2_ampdu_stat_read, inode->i_private);
-}
-
-static int read_txpower(struct seq_file *file, void *data)
-{
-       struct mt76x02_dev *dev = dev_get_drvdata(file->private);
-
-       seq_printf(file, "Target power: %d\n", dev->target_power);
-
-       mt76_seq_puts_array(file, "Delta", dev->target_power_delta,
-                           ARRAY_SIZE(dev->target_power_delta));
-       return 0;
-}
-
-static const struct file_operations fops_ampdu_stat = {
-       .open = mt76x2_ampdu_stat_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
-
-static int
-mt76x2_dfs_stat_read(struct seq_file *file, void *data)
-{
-       struct mt76x02_dev *dev = file->private;
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-       int i;
-
-       seq_printf(file, "allocated sequences:\t%d\n",
-                  dfs_pd->seq_stats.seq_pool_len);
-       seq_printf(file, "used sequences:\t\t%d\n",
-                  dfs_pd->seq_stats.seq_len);
-       seq_puts(file, "\n");
-
-       for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
-               seq_printf(file, "engine: %d\n", i);
-               seq_printf(file, "  hw pattern detected:\t%d\n",
-                          dfs_pd->stats[i].hw_pattern);
-               seq_printf(file, "  hw pulse discarded:\t%d\n",
-                          dfs_pd->stats[i].hw_pulse_discarded);
-               seq_printf(file, "  sw pattern detected:\t%d\n",
-                          dfs_pd->stats[i].sw_pattern);
-       }
-
-       return 0;
-}
-
-static int
-mt76x2_dfs_stat_open(struct inode *inode, struct file *f)
-{
-       return single_open(f, mt76x2_dfs_stat_read, inode->i_private);
-}
-
-static const struct file_operations fops_dfs_stat = {
-       .open = mt76x2_dfs_stat_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
-
-static int read_agc(struct seq_file *file, void *data)
-{
-       struct mt76x02_dev *dev = dev_get_drvdata(file->private);
-
-       seq_printf(file, "avg_rssi: %d\n", dev->cal.avg_rssi_all);
-       seq_printf(file, "low_gain: %d\n", dev->cal.low_gain);
-       seq_printf(file, "false_cca: %d\n", dev->cal.false_cca);
-       seq_printf(file, "agc_gain_adjust: %d\n", dev->cal.agc_gain_adjust);
-
-       return 0;
-}
-
-void mt76x2_init_debugfs(struct mt76x02_dev *dev)
-{
-       struct dentry *dir;
-
-       dir = mt76_register_debugfs(&dev->mt76);
-       if (!dir)
-               return;
-
-       debugfs_create_u8("temperature", 0400, dir, &dev->cal.temp);
-       debugfs_create_bool("tpc", 0600, dir, &dev->enable_tpc);
-
-       debugfs_create_file("ampdu_stat", 0400, dir, dev, &fops_ampdu_stat);
-       debugfs_create_file("dfs_stats", 0400, dir, dev, &fops_dfs_stat);
-       debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir,
-                                   read_txpower);
-
-       debugfs_create_devm_seqfile(dev->mt76.dev, "agc", dir, read_agc);
-}
-EXPORT_SYMBOL_GPL(mt76x2_init_debugfs);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/dfs.h b/drivers/net/wireless/mediatek/mt76/mt76x2/dfs.h
deleted file mode 100644 (file)
index 3cb9d18..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2016 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
- *
- * 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.
- */
-
-#ifndef __DFS_H
-#define __DFS_H
-
-void mt76x2_dfs_init_params(struct mt76x02_dev *dev);
-void mt76x2_dfs_init_detector(struct mt76x02_dev *dev);
-void mt76x2_dfs_adjust_agc(struct mt76x02_dev *dev);
-void mt76x2_dfs_set_domain(struct mt76x02_dev *dev,
-                          enum nl80211_dfs_regions region);
-
-#endif /* __DFS_H */
index f39b622..6f69985 100644 (file)
 
 #define EE_FIELD(_name, _value) [MT_EE_##_name] = (_value) | 1
 
-static int
-mt76x2_eeprom_copy(struct mt76x02_dev *dev, enum mt76x02_eeprom_field field,
-                  void *dest, int len)
-{
-       if (field + len > dev->mt76.eeprom.size)
-               return -1;
-
-       memcpy(dest, dev->mt76.eeprom.data + field, len);
-       return 0;
-}
-
 static int
 mt76x2_eeprom_get_macaddr(struct mt76x02_dev *dev)
 {
@@ -378,7 +367,7 @@ mt76x2_get_power_info_2g(struct mt76x02_dev *dev,
        else
                delta_idx = 5;
 
-       mt76x2_eeprom_copy(dev, offset, data, sizeof(data));
+       mt76x02_eeprom_copy(dev, offset, data, sizeof(data));
 
        t->chain[chain].tssi_slope = data[0];
        t->chain[chain].tssi_offset = data[1];
@@ -429,7 +418,7 @@ mt76x2_get_power_info_5g(struct mt76x02_dev *dev,
        else
                delta_idx = 4;
 
-       mt76x2_eeprom_copy(dev, offset, data, sizeof(data));
+       mt76x02_eeprom_copy(dev, offset, data, sizeof(data));
 
        t->chain[chain].tssi_slope = data[0];
        t->chain[chain].tssi_offset = data[1];
index 3c73fde..54a9b5f 100644 (file)
@@ -158,38 +158,6 @@ void mt76_write_mac_initvals(struct mt76x02_dev *dev)
 }
 EXPORT_SYMBOL_GPL(mt76_write_mac_initvals);
 
-void mt76x2_init_device(struct mt76x02_dev *dev)
-{
-       struct ieee80211_hw *hw = mt76_hw(dev);
-
-       hw->queues = 4;
-       hw->max_rates = 1;
-       hw->max_report_rates = 7;
-       hw->max_rate_tries = 1;
-       hw->extra_tx_headroom = 2;
-       if (mt76_is_usb(dev))
-               hw->extra_tx_headroom += sizeof(struct mt76x02_txwi) +
-                                        MT_DMA_HDR_LEN;
-
-       hw->sta_data_size = sizeof(struct mt76x02_sta);
-       hw->vif_data_size = sizeof(struct mt76x02_vif);
-
-       ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES);
-       ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
-
-       dev->mt76.sband_2g.sband.ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
-       dev->mt76.sband_5g.sband.ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
-
-       dev->mt76.chainmask = 0x202;
-       dev->mt76.global_wcid.idx = 255;
-       dev->mt76.global_wcid.hw_key_idx = -1;
-       dev->slottime = 9;
-
-       /* init antenna configuration */
-       dev->mt76.antenna_mask = 3;
-}
-EXPORT_SYMBOL_GPL(mt76x2_init_device);
-
 void mt76x2_init_txpower(struct mt76x02_dev *dev,
                         struct ieee80211_supported_band *sband)
 {
index a31bd49..4c8e20b 100644 (file)
@@ -26,12 +26,5 @@ struct mt76x02_vif;
 int mt76x2_mac_start(struct mt76x02_dev *dev);
 void mt76x2_mac_stop(struct mt76x02_dev *dev, bool force);
 void mt76x2_mac_resume(struct mt76x02_dev *dev);
-void mt76x2_mac_set_bssid(struct mt76x02_dev *dev, u8 idx, const u8 *addr);
-
-int mt76x2_mac_set_beacon(struct mt76x02_dev *dev, u8 vif_idx,
-                         struct sk_buff *skb);
-void mt76x2_mac_set_beacon_enable(struct mt76x02_dev *dev, u8 vif_idx, bool val);
-
-void mt76x2_mac_work(struct work_struct *work);
 
 #endif
index 88bd62c..cd3e082 100644 (file)
@@ -26,7 +26,6 @@
 int mt76x2_mcu_set_channel(struct mt76x02_dev *dev, u8 channel, u8 bw,
                           u8 bw_index, bool scan)
 {
-       struct sk_buff *skb;
        struct {
                u8 idx;
                u8 scan;
@@ -45,21 +44,19 @@ int mt76x2_mcu_set_channel(struct mt76x02_dev *dev, u8 channel, u8 bw,
        };
 
        /* first set the channel without the extension channel info */
-       skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
-       mt76_mcu_send_msg(dev, skb, CMD_SWITCH_CHANNEL_OP, true);
+       mt76_mcu_send_msg(dev, CMD_SWITCH_CHANNEL_OP, &msg, sizeof(msg), true);
 
        usleep_range(5000, 10000);
 
        msg.ext_chan = 0xe0 + bw_index;
-       skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
-       return mt76_mcu_send_msg(dev, skb, CMD_SWITCH_CHANNEL_OP, true);
+       return mt76_mcu_send_msg(dev, CMD_SWITCH_CHANNEL_OP, &msg, sizeof(msg),
+                                true);
 }
 EXPORT_SYMBOL_GPL(mt76x2_mcu_set_channel);
 
 int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level,
                       u8 channel)
 {
-       struct sk_buff *skb;
        struct {
                u8 cr_mode;
                u8 temp;
@@ -80,15 +77,13 @@ int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level,
        msg.cfg = cpu_to_le32(val);
 
        /* first set the channel without the extension channel info */
-       skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
-       return mt76_mcu_send_msg(dev, skb, CMD_LOAD_CR, true);
+       return mt76_mcu_send_msg(dev, CMD_LOAD_CR, &msg, sizeof(msg), true);
 }
 EXPORT_SYMBOL_GPL(mt76x2_mcu_load_cr);
 
 int mt76x2_mcu_init_gain(struct mt76x02_dev *dev, u8 channel, u32 gain,
                         bool force)
 {
-       struct sk_buff *skb;
        struct {
                __le32 channel;
                __le32 gain_val;
@@ -100,15 +95,14 @@ int mt76x2_mcu_init_gain(struct mt76x02_dev *dev, u8 channel, u32 gain,
        if (force)
                msg.channel |= cpu_to_le32(BIT(31));
 
-       skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
-       return mt76_mcu_send_msg(dev, skb, CMD_INIT_GAIN_OP, true);
+       return mt76_mcu_send_msg(dev, CMD_INIT_GAIN_OP, &msg, sizeof(msg),
+                                true);
 }
 EXPORT_SYMBOL_GPL(mt76x2_mcu_init_gain);
 
 int mt76x2_mcu_tssi_comp(struct mt76x02_dev *dev,
                         struct mt76x2_tssi_comp *tssi_data)
 {
-       struct sk_buff *skb;
        struct {
                __le32 id;
                struct mt76x2_tssi_comp data;
@@ -117,7 +111,7 @@ int mt76x2_mcu_tssi_comp(struct mt76x02_dev *dev,
                .data = *tssi_data,
        };
 
-       skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
-       return mt76_mcu_send_msg(dev, skb, CMD_CALIBRATION_OP, true);
+       return mt76_mcu_send_msg(dev, CMD_CALIBRATION_OP, &msg, sizeof(msg),
+                                true);
 }
 EXPORT_SYMBOL_GPL(mt76x2_mcu_tssi_comp);
index ab93125..b259e4b 100644 (file)
 #define MT7662_ROM_PATCH       "mt7662_rom_patch.bin"
 #define MT7662_EEPROM_SIZE     512
 
-#define MT7662U_FIRMWARE       "mediatek/mt7662u.bin"
-#define MT7662U_ROM_PATCH      "mediatek/mt7662u_rom_patch.bin"
-
-#define MT_CALIBRATE_INTERVAL  HZ
-
 #include "../mt76x02.h"
 #include "mac.h"
-#include "dfs.h"
 
 static inline bool is_mt7612(struct mt76x02_dev *dev)
 {
@@ -57,15 +51,12 @@ extern const struct ieee80211_ops mt76x2_ops;
 
 struct mt76x02_dev *mt76x2_alloc_device(struct device *pdev);
 int mt76x2_register_device(struct mt76x02_dev *dev);
-void mt76x2_init_debugfs(struct mt76x02_dev *dev);
-void mt76x2_init_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);
-void mt76x2_set_tx_ackto(struct mt76x02_dev *dev);
 
 void mt76x2_phy_set_antenna(struct mt76x02_dev *dev);
 int mt76x2_phy_start(struct mt76x02_dev *dev);
@@ -82,24 +73,17 @@ int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level,
 
 void mt76x2_cleanup(struct mt76x02_dev *dev);
 
-void mt76x2_mac_set_tx_protection(struct mt76x02_dev *dev, u32 val);
-
-void mt76x2_pre_tbtt_tasklet(unsigned long arg);
-
-void mt76x2_sta_ps(struct mt76_dev *dev, struct ieee80211_sta *sta, bool ps);
-
-void mt76x2_update_channel(struct mt76_dev *mdev);
-
 void mt76x2_reset_wlan(struct mt76x02_dev *dev, bool enable);
 void mt76x2_init_txpower(struct mt76x02_dev *dev,
                         struct ieee80211_supported_band *sband);
 void mt76_write_mac_initvals(struct mt76x02_dev *dev);
 
-void mt76x2_phy_tssi_compensate(struct mt76x02_dev *dev, bool wait);
+void mt76x2_phy_tssi_compensate(struct mt76x02_dev *dev);
 void mt76x2_phy_set_txpower_regs(struct mt76x02_dev *dev,
                                 enum nl80211_band band);
 void mt76x2_configure_tx_delay(struct mt76x02_dev *dev,
                               enum nl80211_band band, u8 bw);
 void mt76x2_apply_gain_adj(struct mt76x02_dev *dev);
+void mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev);
 
 #endif
index 6e932b5..0b00754 100644 (file)
@@ -43,11 +43,8 @@ int mt76x2u_mac_stop(struct mt76x02_dev *dev);
 int mt76x2u_phy_set_channel(struct mt76x02_dev *dev,
                            struct cfg80211_chan_def *chandef);
 void mt76x2u_phy_calibrate(struct work_struct *work);
-void mt76x2u_phy_channel_calibrate(struct mt76x02_dev *dev);
 
 void mt76x2u_mcu_complete_urb(struct urb *urb);
-int mt76x2u_mcu_set_dynamic_vga(struct mt76x02_dev *dev, u8 channel, bool ap,
-                               bool ext, int rssi, u32 false_cca);
 int mt76x2u_mcu_init(struct mt76x02_dev *dev);
 int mt76x2u_mcu_fw_init(struct mt76x02_dev *dev);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_dfs.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_dfs.c
deleted file mode 100644 (file)
index b56feba..0000000
+++ /dev/null
@@ -1,878 +0,0 @@
-/*
- * Copyright (C) 2016 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
- *
- * 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.
- */
-
-#include "mt76x2.h"
-
-#define RADAR_SPEC(m, len, el, eh, wl, wh,             \
-                  w_tolerance, tl, th, t_tolerance,    \
-                  bl, bh, event_exp, power_jmp)        \
-{                                                      \
-       .mode = m,                                      \
-       .avg_len = len,                                 \
-       .e_low = el,                                    \
-       .e_high = eh,                                   \
-       .w_low = wl,                                    \
-       .w_high = wh,                                   \
-       .w_margin = w_tolerance,                        \
-       .t_low = tl,                                    \
-       .t_high = th,                                   \
-       .t_margin = t_tolerance,                        \
-       .b_low = bl,                                    \
-       .b_high = bh,                                   \
-       .event_expiration = event_exp,                  \
-       .pwr_jmp = power_jmp                            \
-}
-
-static const struct mt76x02_radar_specs etsi_radar_specs[] = {
-       /* 20MHz */
-       RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
-                  0x7fffffff, 0x155cc0, 0x19cc),
-       RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
-                  0x7fffffff, 0x155cc0, 0x19cc),
-       RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
-                  0x7fffffff, 0x155cc0, 0x19dd),
-       RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
-                  0x7fffffff, 0x2191c0, 0x15cc),
-       /* 40MHz */
-       RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
-                  0x7fffffff, 0x155cc0, 0x19cc),
-       RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
-                  0x7fffffff, 0x155cc0, 0x19cc),
-       RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
-                  0x7fffffff, 0x155cc0, 0x19dd),
-       RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
-                  0x7fffffff, 0x2191c0, 0x15cc),
-       /* 80MHz */
-       RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
-                  0x7fffffff, 0x155cc0, 0x19cc),
-       RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
-                  0x7fffffff, 0x155cc0, 0x19cc),
-       RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
-                  0x7fffffff, 0x155cc0, 0x19dd),
-       RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
-                  0x7fffffff, 0x2191c0, 0x15cc)
-};
-
-static const struct mt76x02_radar_specs fcc_radar_specs[] = {
-       /* 20MHz */
-       RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0,
-                  0x7fffffff, 0xfe808, 0x13dc),
-       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
-                  0x7fffffff, 0xfe808, 0x19dd),
-       RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
-                  0x7fffffff, 0xfe808, 0x12cc),
-       RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
-                  0x3938700, 0x57bcf00, 0x1289),
-       /* 40MHz */
-       RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0,
-                  0x7fffffff, 0xfe808, 0x13dc),
-       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
-                  0x7fffffff, 0xfe808, 0x19dd),
-       RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
-                  0x7fffffff, 0xfe808, 0x12cc),
-       RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
-                  0x3938700, 0x57bcf00, 0x1289),
-       /* 80MHz */
-       RADAR_SPEC(0, 8, 2, 14, 106, 150, 15, 2900, 80100, 15, 0,
-                  0x7fffffff, 0xfe808, 0x16cc),
-       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
-                  0x7fffffff, 0xfe808, 0x19dd),
-       RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
-                  0x7fffffff, 0xfe808, 0x12cc),
-       RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
-                  0x3938700, 0x57bcf00, 0x1289)
-};
-
-static const struct mt76x02_radar_specs jp_w56_radar_specs[] = {
-       /* 20MHz */
-       RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0,
-                  0x7fffffff, 0x14c080, 0x13dc),
-       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
-                  0x7fffffff, 0x14c080, 0x19dd),
-       RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
-                  0x7fffffff, 0x14c080, 0x12cc),
-       RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
-                  0x3938700, 0X57bcf00, 0x1289),
-       /* 40MHz */
-       RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0,
-                  0x7fffffff, 0x14c080, 0x13dc),
-       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
-                  0x7fffffff, 0x14c080, 0x19dd),
-       RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
-                  0x7fffffff, 0x14c080, 0x12cc),
-       RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
-                  0x3938700, 0X57bcf00, 0x1289),
-       /* 80MHz */
-       RADAR_SPEC(0, 8, 2, 9, 106, 150, 15, 2900, 80100, 15, 0,
-                  0x7fffffff, 0x14c080, 0x16cc),
-       RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
-                  0x7fffffff, 0x14c080, 0x19dd),
-       RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
-                  0x7fffffff, 0x14c080, 0x12cc),
-       RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
-                  0x3938700, 0X57bcf00, 0x1289)
-};
-
-static const struct mt76x02_radar_specs jp_w53_radar_specs[] = {
-       /* 20MHz */
-       RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
-                  0x7fffffff, 0x14c080, 0x16cc),
-       { 0 },
-       RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
-                  0x7fffffff, 0x14c080, 0x16cc),
-       { 0 },
-       /* 40MHz */
-       RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
-                  0x7fffffff, 0x14c080, 0x16cc),
-       { 0 },
-       RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
-                  0x7fffffff, 0x14c080, 0x16cc),
-       { 0 },
-       /* 80MHz */
-       RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
-                  0x7fffffff, 0x14c080, 0x16cc),
-       { 0 },
-       RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
-                  0x7fffffff, 0x14c080, 0x16cc),
-       { 0 }
-};
-
-static void
-mt76x2_dfs_set_capture_mode_ctrl(struct mt76x02_dev *dev,
-                                u8 enable)
-{
-       u32 data;
-
-       data = (1 << 1) | enable;
-       mt76_wr(dev, MT_BBP(DFS, 36), data);
-}
-
-static void mt76x2_dfs_seq_pool_put(struct mt76x02_dev *dev,
-                                   struct mt76x02_dfs_sequence *seq)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-
-       list_add(&seq->head, &dfs_pd->seq_pool);
-
-       dfs_pd->seq_stats.seq_pool_len++;
-       dfs_pd->seq_stats.seq_len--;
-}
-
-static struct mt76x02_dfs_sequence *
-mt76x2_dfs_seq_pool_get(struct mt76x02_dev *dev)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-       struct mt76x02_dfs_sequence *seq;
-
-       if (list_empty(&dfs_pd->seq_pool)) {
-               seq = devm_kzalloc(dev->mt76.dev, sizeof(*seq), GFP_ATOMIC);
-       } else {
-               seq = list_first_entry(&dfs_pd->seq_pool,
-                                      struct mt76x02_dfs_sequence,
-                                      head);
-               list_del(&seq->head);
-               dfs_pd->seq_stats.seq_pool_len--;
-       }
-       if (seq)
-               dfs_pd->seq_stats.seq_len++;
-
-       return seq;
-}
-
-static int mt76x2_dfs_get_multiple(int val, int frac, int margin)
-{
-       int remainder, factor;
-
-       if (!frac)
-               return 0;
-
-       if (abs(val - frac) <= margin)
-               return 1;
-
-       factor = val / frac;
-       remainder = val % frac;
-
-       if (remainder > margin) {
-               if ((frac - remainder) <= margin)
-                       factor++;
-               else
-                       factor = 0;
-       }
-       return factor;
-}
-
-static void mt76x2_dfs_detector_reset(struct mt76x02_dev *dev)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-       struct mt76x02_dfs_sequence *seq, *tmp_seq;
-       int i;
-
-       /* reset hw detector */
-       mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
-
-       /* reset sw detector */
-       for (i = 0; i < ARRAY_SIZE(dfs_pd->event_rb); i++) {
-               dfs_pd->event_rb[i].h_rb = 0;
-               dfs_pd->event_rb[i].t_rb = 0;
-       }
-
-       list_for_each_entry_safe(seq, tmp_seq, &dfs_pd->sequences, head) {
-               list_del_init(&seq->head);
-               mt76x2_dfs_seq_pool_put(dev, seq);
-       }
-}
-
-static bool mt76x2_dfs_check_chirp(struct mt76x02_dev *dev)
-{
-       bool ret = false;
-       u32 current_ts, delta_ts;
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-
-       current_ts = mt76_rr(dev, MT_PBF_LIFE_TIMER);
-       delta_ts = current_ts - dfs_pd->chirp_pulse_ts;
-       dfs_pd->chirp_pulse_ts = current_ts;
-
-       /* 12 sec */
-       if (delta_ts <= (12 * (1 << 20))) {
-               if (++dfs_pd->chirp_pulse_cnt > 8)
-                       ret = true;
-       } else {
-               dfs_pd->chirp_pulse_cnt = 1;
-       }
-
-       return ret;
-}
-
-static void mt76x2_dfs_get_hw_pulse(struct mt76x02_dev *dev,
-                                   struct mt76x02_dfs_hw_pulse *pulse)
-{
-       u32 data;
-
-       /* select channel */
-       data = (MT_DFS_CH_EN << 16) | pulse->engine;
-       mt76_wr(dev, MT_BBP(DFS, 0), data);
-
-       /* reported period */
-       pulse->period = mt76_rr(dev, MT_BBP(DFS, 19));
-
-       /* reported width */
-       pulse->w1 = mt76_rr(dev, MT_BBP(DFS, 20));
-       pulse->w2 = mt76_rr(dev, MT_BBP(DFS, 23));
-
-       /* reported burst number */
-       pulse->burst = mt76_rr(dev, MT_BBP(DFS, 22));
-}
-
-static bool mt76x2_dfs_check_hw_pulse(struct mt76x02_dev *dev,
-                                     struct mt76x02_dfs_hw_pulse *pulse)
-{
-       bool ret = false;
-
-       if (!pulse->period || !pulse->w1)
-               return false;
-
-       switch (dev->dfs_pd.region) {
-       case NL80211_DFS_FCC:
-               if (pulse->engine > 3)
-                       break;
-
-               if (pulse->engine == 3) {
-                       ret = mt76x2_dfs_check_chirp(dev);
-                       break;
-               }
-
-               /* check short pulse*/
-               if (pulse->w1 < 120)
-                       ret = (pulse->period >= 2900 &&
-                              (pulse->period <= 4700 ||
-                               pulse->period >= 6400) &&
-                              (pulse->period <= 6800 ||
-                               pulse->period >= 10200) &&
-                              pulse->period <= 61600);
-               else if (pulse->w1 < 130) /* 120 - 130 */
-                       ret = (pulse->period >= 2900 &&
-                              pulse->period <= 61600);
-               else
-                       ret = (pulse->period >= 3500 &&
-                              pulse->period <= 10100);
-               break;
-       case NL80211_DFS_ETSI:
-               if (pulse->engine >= 3)
-                       break;
-
-               ret = (pulse->period >= 4900 &&
-                      (pulse->period <= 10200 ||
-                       pulse->period >= 12400) &&
-                      pulse->period <= 100100);
-               break;
-       case NL80211_DFS_JP:
-               if (dev->mt76.chandef.chan->center_freq >= 5250 &&
-                   dev->mt76.chandef.chan->center_freq <= 5350) {
-                       /* JPW53 */
-                       if (pulse->w1 <= 130)
-                               ret = (pulse->period >= 28360 &&
-                                      (pulse->period <= 28700 ||
-                                       pulse->period >= 76900) &&
-                                      pulse->period <= 76940);
-                       break;
-               }
-
-               if (pulse->engine > 3)
-                       break;
-
-               if (pulse->engine == 3) {
-                       ret = mt76x2_dfs_check_chirp(dev);
-                       break;
-               }
-
-               /* check short pulse*/
-               if (pulse->w1 < 120)
-                       ret = (pulse->period >= 2900 &&
-                              (pulse->period <= 4700 ||
-                               pulse->period >= 6400) &&
-                              (pulse->period <= 6800 ||
-                               pulse->period >= 27560) &&
-                              (pulse->period <= 27960 ||
-                               pulse->period >= 28360) &&
-                              (pulse->period <= 28700 ||
-                               pulse->period >= 79900) &&
-                              pulse->period <= 80100);
-               else if (pulse->w1 < 130) /* 120 - 130 */
-                       ret = (pulse->period >= 2900 &&
-                              (pulse->period <= 10100 ||
-                               pulse->period >= 27560) &&
-                              (pulse->period <= 27960 ||
-                               pulse->period >= 28360) &&
-                              (pulse->period <= 28700 ||
-                               pulse->period >= 79900) &&
-                              pulse->period <= 80100);
-               else
-                       ret = (pulse->period >= 3900 &&
-                              pulse->period <= 10100);
-               break;
-       case NL80211_DFS_UNSET:
-       default:
-               return false;
-       }
-
-       return ret;
-}
-
-static bool mt76x2_dfs_fetch_event(struct mt76x02_dev *dev,
-                                  struct mt76x02_dfs_event *event)
-{
-       u32 data;
-
-       /* 1st: DFS_R37[31]: 0 (engine 0) - 1 (engine 2)
-        * 2nd: DFS_R37[21:0]: pulse time
-        * 3rd: DFS_R37[11:0]: pulse width
-        * 3rd: DFS_R37[25:16]: phase
-        * 4th: DFS_R37[12:0]: current pwr
-        * 4th: DFS_R37[21:16]: pwr stable counter
-        *
-        * 1st: DFS_R37[31:0] set to 0xffffffff means no event detected
-        */
-       data = mt76_rr(dev, MT_BBP(DFS, 37));
-       if (!MT_DFS_CHECK_EVENT(data))
-               return false;
-
-       event->engine = MT_DFS_EVENT_ENGINE(data);
-       data = mt76_rr(dev, MT_BBP(DFS, 37));
-       event->ts = MT_DFS_EVENT_TIMESTAMP(data);
-       data = mt76_rr(dev, MT_BBP(DFS, 37));
-       event->width = MT_DFS_EVENT_WIDTH(data);
-
-       return true;
-}
-
-static bool mt76x2_dfs_check_event(struct mt76x02_dev *dev,
-                                  struct mt76x02_dfs_event *event)
-{
-       if (event->engine == 2) {
-               struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-               struct mt76x02_dfs_event_rb *event_buff = &dfs_pd->event_rb[1];
-               u16 last_event_idx;
-               u32 delta_ts;
-
-               last_event_idx = mt76_decr(event_buff->t_rb,
-                                          MT_DFS_EVENT_BUFLEN);
-               delta_ts = event->ts - event_buff->data[last_event_idx].ts;
-               if (delta_ts < MT_DFS_EVENT_TIME_MARGIN &&
-                   event_buff->data[last_event_idx].width >= 200)
-                       return false;
-       }
-       return true;
-}
-
-static void mt76x2_dfs_queue_event(struct mt76x02_dev *dev,
-                                  struct mt76x02_dfs_event *event)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-       struct mt76x02_dfs_event_rb *event_buff;
-
-       /* add radar event to ring buffer */
-       event_buff = event->engine == 2 ? &dfs_pd->event_rb[1]
-                                       : &dfs_pd->event_rb[0];
-       event_buff->data[event_buff->t_rb] = *event;
-       event_buff->data[event_buff->t_rb].fetch_ts = jiffies;
-
-       event_buff->t_rb = mt76_incr(event_buff->t_rb, MT_DFS_EVENT_BUFLEN);
-       if (event_buff->t_rb == event_buff->h_rb)
-               event_buff->h_rb = mt76_incr(event_buff->h_rb,
-                                            MT_DFS_EVENT_BUFLEN);
-}
-
-static int mt76x2_dfs_create_sequence(struct mt76x02_dev *dev,
-                                     struct mt76x02_dfs_event *event,
-                                     u16 cur_len)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-       struct mt76x02_dfs_sw_detector_params *sw_params;
-       u32 width_delta, with_sum, factor, cur_pri;
-       struct mt76x02_dfs_sequence seq, *seq_p;
-       struct mt76x02_dfs_event_rb *event_rb;
-       struct mt76x02_dfs_event *cur_event;
-       int i, j, end, pri;
-
-       event_rb = event->engine == 2 ? &dfs_pd->event_rb[1]
-                                     : &dfs_pd->event_rb[0];
-
-       i = mt76_decr(event_rb->t_rb, MT_DFS_EVENT_BUFLEN);
-       end = mt76_decr(event_rb->h_rb, MT_DFS_EVENT_BUFLEN);
-
-       while (i != end) {
-               cur_event = &event_rb->data[i];
-               with_sum = event->width + cur_event->width;
-
-               sw_params = &dfs_pd->sw_dpd_params;
-               switch (dev->dfs_pd.region) {
-               case NL80211_DFS_FCC:
-               case NL80211_DFS_JP:
-                       if (with_sum < 600)
-                               width_delta = 8;
-                       else
-                               width_delta = with_sum >> 3;
-                       break;
-               case NL80211_DFS_ETSI:
-                       if (event->engine == 2)
-                               width_delta = with_sum >> 6;
-                       else if (with_sum < 620)
-                               width_delta = 24;
-                       else
-                               width_delta = 8;
-                       break;
-               case NL80211_DFS_UNSET:
-               default:
-                       return -EINVAL;
-               }
-
-               pri = event->ts - cur_event->ts;
-               if (abs(event->width - cur_event->width) > width_delta ||
-                   pri < sw_params->min_pri)
-                       goto next;
-
-               if (pri > sw_params->max_pri)
-                       break;
-
-               seq.pri = event->ts - cur_event->ts;
-               seq.first_ts = cur_event->ts;
-               seq.last_ts = event->ts;
-               seq.engine = event->engine;
-               seq.count = 2;
-
-               j = mt76_decr(i, MT_DFS_EVENT_BUFLEN);
-               while (j != end) {
-                       cur_event = &event_rb->data[j];
-                       cur_pri = event->ts - cur_event->ts;
-                       factor = mt76x2_dfs_get_multiple(cur_pri, seq.pri,
-                                               sw_params->pri_margin);
-                       if (factor > 0) {
-                               seq.first_ts = cur_event->ts;
-                               seq.count++;
-                       }
-
-                       j = mt76_decr(j, MT_DFS_EVENT_BUFLEN);
-               }
-               if (seq.count <= cur_len)
-                       goto next;
-
-               seq_p = mt76x2_dfs_seq_pool_get(dev);
-               if (!seq_p)
-                       return -ENOMEM;
-
-               *seq_p = seq;
-               INIT_LIST_HEAD(&seq_p->head);
-               list_add(&seq_p->head, &dfs_pd->sequences);
-next:
-               i = mt76_decr(i, MT_DFS_EVENT_BUFLEN);
-       }
-       return 0;
-}
-
-static u16 mt76x2_dfs_add_event_to_sequence(struct mt76x02_dev *dev,
-                                           struct mt76x02_dfs_event *event)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-       struct mt76x02_dfs_sw_detector_params *sw_params;
-       struct mt76x02_dfs_sequence *seq, *tmp_seq;
-       u16 max_seq_len = 0;
-       u32 factor, pri;
-
-       sw_params = &dfs_pd->sw_dpd_params;
-       list_for_each_entry_safe(seq, tmp_seq, &dfs_pd->sequences, head) {
-               if (event->ts > seq->first_ts + MT_DFS_SEQUENCE_WINDOW) {
-                       list_del_init(&seq->head);
-                       mt76x2_dfs_seq_pool_put(dev, seq);
-                       continue;
-               }
-
-               if (event->engine != seq->engine)
-                       continue;
-
-               pri = event->ts - seq->last_ts;
-               factor = mt76x2_dfs_get_multiple(pri, seq->pri,
-                                                sw_params->pri_margin);
-               if (factor > 0) {
-                       seq->last_ts = event->ts;
-                       seq->count++;
-                       max_seq_len = max_t(u16, max_seq_len, seq->count);
-               }
-       }
-       return max_seq_len;
-}
-
-static bool mt76x2_dfs_check_detection(struct mt76x02_dev *dev)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-       struct mt76x02_dfs_sequence *seq;
-
-       if (list_empty(&dfs_pd->sequences))
-               return false;
-
-       list_for_each_entry(seq, &dfs_pd->sequences, head) {
-               if (seq->count > MT_DFS_SEQUENCE_TH) {
-                       dfs_pd->stats[seq->engine].sw_pattern++;
-                       return true;
-               }
-       }
-       return false;
-}
-
-static void mt76x2_dfs_add_events(struct mt76x02_dev *dev)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-       struct mt76x02_dfs_event event;
-       int i, seq_len;
-
-       /* disable debug mode */
-       mt76x2_dfs_set_capture_mode_ctrl(dev, false);
-       for (i = 0; i < MT_DFS_EVENT_LOOP; i++) {
-               if (!mt76x2_dfs_fetch_event(dev, &event))
-                       break;
-
-               if (dfs_pd->last_event_ts > event.ts)
-                       mt76x2_dfs_detector_reset(dev);
-               dfs_pd->last_event_ts = event.ts;
-
-               if (!mt76x2_dfs_check_event(dev, &event))
-                       continue;
-
-               seq_len = mt76x2_dfs_add_event_to_sequence(dev, &event);
-               mt76x2_dfs_create_sequence(dev, &event, seq_len);
-
-               mt76x2_dfs_queue_event(dev, &event);
-       }
-       mt76x2_dfs_set_capture_mode_ctrl(dev, true);
-}
-
-static void mt76x2_dfs_check_event_window(struct mt76x02_dev *dev)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-       struct mt76x02_dfs_event_rb *event_buff;
-       struct mt76x02_dfs_event *event;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(dfs_pd->event_rb); i++) {
-               event_buff = &dfs_pd->event_rb[i];
-
-               while (event_buff->h_rb != event_buff->t_rb) {
-                       event = &event_buff->data[event_buff->h_rb];
-
-                       /* sorted list */
-                       if (time_is_after_jiffies(event->fetch_ts +
-                                                 MT_DFS_EVENT_WINDOW))
-                               break;
-                       event_buff->h_rb = mt76_incr(event_buff->h_rb,
-                                                    MT_DFS_EVENT_BUFLEN);
-               }
-       }
-}
-
-static void mt76x2_dfs_tasklet(unsigned long arg)
-{
-       struct mt76x02_dev *dev = (struct mt76x02_dev *)arg;
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-       u32 engine_mask;
-       int i;
-
-       if (test_bit(MT76_SCANNING, &dev->mt76.state))
-               goto out;
-
-       if (time_is_before_jiffies(dfs_pd->last_sw_check +
-                                  MT_DFS_SW_TIMEOUT)) {
-               bool radar_detected;
-
-               dfs_pd->last_sw_check = jiffies;
-
-               mt76x2_dfs_add_events(dev);
-               radar_detected = mt76x2_dfs_check_detection(dev);
-               if (radar_detected) {
-                       /* sw detector rx radar pattern */
-                       ieee80211_radar_detected(dev->mt76.hw);
-                       mt76x2_dfs_detector_reset(dev);
-
-                       return;
-               }
-               mt76x2_dfs_check_event_window(dev);
-       }
-
-       engine_mask = mt76_rr(dev, MT_BBP(DFS, 1));
-       if (!(engine_mask & 0xf))
-               goto out;
-
-       for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
-               struct mt76x02_dfs_hw_pulse pulse;
-
-               if (!(engine_mask & (1 << i)))
-                       continue;
-
-               pulse.engine = i;
-               mt76x2_dfs_get_hw_pulse(dev, &pulse);
-
-               if (!mt76x2_dfs_check_hw_pulse(dev, &pulse)) {
-                       dfs_pd->stats[i].hw_pulse_discarded++;
-                       continue;
-               }
-
-               /* hw detector rx radar pattern */
-               dfs_pd->stats[i].hw_pattern++;
-               ieee80211_radar_detected(dev->mt76.hw);
-               mt76x2_dfs_detector_reset(dev);
-
-               return;
-       }
-
-       /* reset hw detector */
-       mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
-
-out:
-       mt76x02_irq_enable(dev, MT_INT_GPTIMER);
-}
-
-static void mt76x2_dfs_init_sw_detector(struct mt76x02_dev *dev)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-
-       switch (dev->dfs_pd.region) {
-       case NL80211_DFS_FCC:
-               dfs_pd->sw_dpd_params.max_pri = MT_DFS_FCC_MAX_PRI;
-               dfs_pd->sw_dpd_params.min_pri = MT_DFS_FCC_MIN_PRI;
-               dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN;
-               break;
-       case NL80211_DFS_ETSI:
-               dfs_pd->sw_dpd_params.max_pri = MT_DFS_ETSI_MAX_PRI;
-               dfs_pd->sw_dpd_params.min_pri = MT_DFS_ETSI_MIN_PRI;
-               dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN << 2;
-               break;
-       case NL80211_DFS_JP:
-               dfs_pd->sw_dpd_params.max_pri = MT_DFS_JP_MAX_PRI;
-               dfs_pd->sw_dpd_params.min_pri = MT_DFS_JP_MIN_PRI;
-               dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN;
-               break;
-       case NL80211_DFS_UNSET:
-       default:
-               break;
-       }
-}
-
-static void mt76x2_dfs_set_bbp_params(struct mt76x02_dev *dev)
-{
-       const struct mt76x02_radar_specs *radar_specs;
-       u8 i, shift;
-       u32 data;
-
-       switch (dev->mt76.chandef.width) {
-       case NL80211_CHAN_WIDTH_40:
-               shift = MT_DFS_NUM_ENGINES;
-               break;
-       case NL80211_CHAN_WIDTH_80:
-               shift = 2 * MT_DFS_NUM_ENGINES;
-               break;
-       default:
-               shift = 0;
-               break;
-       }
-
-       switch (dev->dfs_pd.region) {
-       case NL80211_DFS_FCC:
-               radar_specs = &fcc_radar_specs[shift];
-               break;
-       case NL80211_DFS_ETSI:
-               radar_specs = &etsi_radar_specs[shift];
-               break;
-       case NL80211_DFS_JP:
-               if (dev->mt76.chandef.chan->center_freq >= 5250 &&
-                   dev->mt76.chandef.chan->center_freq <= 5350)
-                       radar_specs = &jp_w53_radar_specs[shift];
-               else
-                       radar_specs = &jp_w56_radar_specs[shift];
-               break;
-       case NL80211_DFS_UNSET:
-       default:
-               return;
-       }
-
-       data = (MT_DFS_VGA_MASK << 16) |
-              (MT_DFS_PWR_GAIN_OFFSET << 12) |
-              (MT_DFS_PWR_DOWN_TIME << 8) |
-              (MT_DFS_SYM_ROUND << 4) |
-              (MT_DFS_DELTA_DELAY & 0xf);
-       mt76_wr(dev, MT_BBP(DFS, 2), data);
-
-       data = (MT_DFS_RX_PE_MASK << 16) | MT_DFS_PKT_END_MASK;
-       mt76_wr(dev, MT_BBP(DFS, 3), data);
-
-       for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
-               /* configure engine */
-               mt76_wr(dev, MT_BBP(DFS, 0), i);
-
-               /* detection mode + avg_len */
-               data = ((radar_specs[i].avg_len & 0x1ff) << 16) |
-                      (radar_specs[i].mode & 0xf);
-               mt76_wr(dev, MT_BBP(DFS, 4), data);
-
-               /* dfs energy */
-               data = ((radar_specs[i].e_high & 0x0fff) << 16) |
-                      (radar_specs[i].e_low & 0x0fff);
-               mt76_wr(dev, MT_BBP(DFS, 5), data);
-
-               /* dfs period */
-               mt76_wr(dev, MT_BBP(DFS, 7), radar_specs[i].t_low);
-               mt76_wr(dev, MT_BBP(DFS, 9), radar_specs[i].t_high);
-
-               /* dfs burst */
-               mt76_wr(dev, MT_BBP(DFS, 11), radar_specs[i].b_low);
-               mt76_wr(dev, MT_BBP(DFS, 13), radar_specs[i].b_high);
-
-               /* dfs width */
-               data = ((radar_specs[i].w_high & 0x0fff) << 16) |
-                      (radar_specs[i].w_low & 0x0fff);
-               mt76_wr(dev, MT_BBP(DFS, 14), data);
-
-               /* dfs margins */
-               data = (radar_specs[i].w_margin << 16) |
-                      radar_specs[i].t_margin;
-               mt76_wr(dev, MT_BBP(DFS, 15), data);
-
-               /* dfs event expiration */
-               mt76_wr(dev, MT_BBP(DFS, 17), radar_specs[i].event_expiration);
-
-               /* dfs pwr adj */
-               mt76_wr(dev, MT_BBP(DFS, 30), radar_specs[i].pwr_jmp);
-       }
-
-       /* reset status */
-       mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
-       mt76_wr(dev, MT_BBP(DFS, 36), 0x3);
-
-       /* enable detection*/
-       mt76_wr(dev, MT_BBP(DFS, 0), MT_DFS_CH_EN << 16);
-       mt76_wr(dev, 0x212c, 0x0c350001);
-}
-
-void mt76x2_dfs_adjust_agc(struct mt76x02_dev *dev)
-{
-       u32 agc_r8, agc_r4, val_r8, val_r4, dfs_r31;
-
-       agc_r8 = mt76_rr(dev, MT_BBP(AGC, 8));
-       agc_r4 = mt76_rr(dev, MT_BBP(AGC, 4));
-
-       val_r8 = (agc_r8 & 0x00007e00) >> 9;
-       val_r4 = agc_r4 & ~0x1f000000;
-       val_r4 += (((val_r8 + 1) >> 1) << 24);
-       mt76_wr(dev, MT_BBP(AGC, 4), val_r4);
-
-       dfs_r31 = FIELD_GET(MT_BBP_AGC_LNA_HIGH_GAIN, val_r4);
-       dfs_r31 += val_r8;
-       dfs_r31 -= (agc_r8 & 0x00000038) >> 3;
-       dfs_r31 = (dfs_r31 << 16) | 0x00000307;
-       mt76_wr(dev, MT_BBP(DFS, 31), dfs_r31);
-
-       mt76_wr(dev, MT_BBP(DFS, 32), 0x00040071);
-}
-
-void mt76x2_dfs_init_params(struct mt76x02_dev *dev)
-{
-       struct cfg80211_chan_def *chandef = &dev->mt76.chandef;
-
-       if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) &&
-           dev->dfs_pd.region != NL80211_DFS_UNSET) {
-               mt76x2_dfs_init_sw_detector(dev);
-               mt76x2_dfs_set_bbp_params(dev);
-               /* enable debug mode */
-               mt76x2_dfs_set_capture_mode_ctrl(dev, true);
-
-               mt76x02_irq_enable(dev, MT_INT_GPTIMER);
-               mt76_rmw_field(dev, MT_INT_TIMER_EN,
-                              MT_INT_TIMER_EN_GP_TIMER_EN, 1);
-       } else {
-               /* disable hw detector */
-               mt76_wr(dev, MT_BBP(DFS, 0), 0);
-               /* clear detector status */
-               mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
-               mt76_wr(dev, 0x212c, 0);
-
-               mt76x02_irq_disable(dev, MT_INT_GPTIMER);
-               mt76_rmw_field(dev, MT_INT_TIMER_EN,
-                              MT_INT_TIMER_EN_GP_TIMER_EN, 0);
-       }
-}
-
-void mt76x2_dfs_init_detector(struct mt76x02_dev *dev)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-
-       INIT_LIST_HEAD(&dfs_pd->sequences);
-       INIT_LIST_HEAD(&dfs_pd->seq_pool);
-       dfs_pd->region = NL80211_DFS_UNSET;
-       dfs_pd->last_sw_check = jiffies;
-       tasklet_init(&dfs_pd->dfs_tasklet, mt76x2_dfs_tasklet,
-                    (unsigned long)dev);
-}
-
-void mt76x2_dfs_set_domain(struct mt76x02_dev *dev,
-                          enum nl80211_dfs_regions region)
-{
-       struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
-
-       if (dfs_pd->region != region) {
-               tasklet_disable(&dfs_pd->dfs_tasklet);
-               dfs_pd->region = region;
-               mt76x2_dfs_init_params(dev);
-               tasklet_enable(&dfs_pd->dfs_tasklet);
-       }
-}
-
index 3824290..7f4ea2d 100644 (file)
@@ -79,7 +79,6 @@ mt76x2_fixup_xtal(struct mt76x02_dev *dev)
 
 static int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
 {
-       static const u8 null_addr[ETH_ALEN] = {};
        const u8 *macaddr = dev->mt76.macaddr;
        u32 val;
        int i, k;
@@ -123,27 +122,18 @@ static int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
        mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(macaddr));
        mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(macaddr + 4));
 
-       mt76_wr(dev, MT_MAC_BSSID_DW0, get_unaligned_le32(macaddr));
-       mt76_wr(dev, MT_MAC_BSSID_DW1, get_unaligned_le16(macaddr + 4) |
-               FIELD_PREP(MT_MAC_BSSID_DW1_MBSS_MODE, 3) | /* 8 beacons */
-               MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT);
-
-       /* Fire a pre-TBTT interrupt 8 ms before TBTT */
-       mt76_rmw_field(dev, MT_INT_TIMER_CFG, MT_INT_TIMER_CFG_PRE_TBTT,
-                      8 << 4);
-       mt76_rmw_field(dev, MT_INT_TIMER_CFG, MT_INT_TIMER_CFG_GP_TIMER,
-                      MT_DFS_GP_INTERVAL);
-       mt76_wr(dev, MT_INT_TIMER_EN, 0);
-
-       mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xffff);
+       mt76x02_init_beacon_config(dev);
        if (!hard)
                return 0;
 
        for (i = 0; i < 256 / 32; i++)
                mt76_wr(dev, MT_WCID_DROP_BASE + i * 4, 0);
 
-       for (i = 0; i < 256; i++)
+       for (i = 0; i < 256; i++) {
                mt76x02_mac_wcid_setup(dev, i, 0, NULL);
+               mt76_wr(dev, MT_WCID_TX_RATE(i), 0);
+               mt76_wr(dev, MT_WCID_TX_RATE(i) + 4, 0);
+       }
 
        for (i = 0; i < MT_MAX_VIFS; i++)
                mt76x02_mac_wcid_setup(dev, MT_VIF_WCID(i), i, NULL);
@@ -152,11 +142,6 @@ static int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
                for (k = 0; k < 4; k++)
                        mt76x02_mac_shared_key_setup(dev, i, k, NULL);
 
-       for (i = 0; i < 8; i++) {
-               mt76x2_mac_set_bssid(dev, i, null_addr);
-               mt76x2_mac_set_beacon(dev, i, NULL);
-       }
-
        for (i = 0; i < 16; i++)
                mt76_rr(dev, MT_TX_STAT_FIFO);
 
@@ -168,9 +153,7 @@ static int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
                MT_CH_TIME_CFG_EIFS_AS_BUSY |
                FIELD_PREP(MT_CH_TIME_CFG_CH_TIMER_CLR, 1));
 
-       mt76x02_set_beacon_offsets(dev);
-
-       mt76x2_set_tx_ackto(dev);
+       mt76x02_set_tx_ackto(dev);
 
        return 0;
 }
@@ -277,30 +260,10 @@ mt76x2_power_on(struct mt76x02_dev *dev)
        mt76x2_power_on_rf(dev, 1);
 }
 
-void mt76x2_set_tx_ackto(struct mt76x02_dev *dev)
-{
-       u8 ackto, sifs, slottime = dev->slottime;
-
-       /* As defined by IEEE 802.11-2007 17.3.8.6 */
-       slottime += 3 * dev->coverage_class;
-       mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG,
-                      MT_BKOFF_SLOT_CFG_SLOTTIME, slottime);
-
-       sifs = mt76_get_field(dev, MT_XIFS_TIME_CFG,
-                             MT_XIFS_TIME_CFG_OFDM_SIFS);
-
-       ackto = slottime + sifs;
-       mt76_rmw_field(dev, MT_TX_TIMEOUT_CFG,
-                      MT_TX_TIMEOUT_CFG_ACKTO, ackto);
-}
-
 int mt76x2_init_hardware(struct mt76x02_dev *dev)
 {
        int ret;
 
-       tasklet_init(&dev->pre_tbtt_tasklet, mt76x2_pre_tbtt_tasklet,
-                    (unsigned long) dev);
-
        mt76x02_dma_disable(dev);
        mt76x2_reset_wlan(dev, true);
        mt76x2_power_on(dev);
@@ -337,7 +300,7 @@ void mt76x2_stop_hardware(struct mt76x02_dev *dev)
 {
        cancel_delayed_work_sync(&dev->cal_work);
        cancel_delayed_work_sync(&dev->mac_work);
-       mt76x02_mcu_set_radio_state(dev, false, true);
+       mt76x02_mcu_set_radio_state(dev, false);
        mt76x2_mac_stop(dev, false);
 }
 
@@ -354,12 +317,14 @@ struct mt76x02_dev *mt76x2_alloc_device(struct device *pdev)
 {
        static const struct mt76_driver_ops drv_ops = {
                .txwi_size = sizeof(struct mt76x02_txwi),
-               .update_survey = mt76x2_update_channel,
+               .update_survey = mt76x02_update_channel,
                .tx_prepare_skb = mt76x02_tx_prepare_skb,
                .tx_complete_skb = mt76x02_tx_complete_skb,
                .rx_skb = mt76x02_queue_rx_skb,
                .rx_poll_complete = mt76x02_rx_poll_complete,
-               .sta_ps = mt76x2_sta_ps,
+               .sta_ps = mt76x02_sta_ps,
+               .sta_add = mt76x02_sta_add,
+               .sta_remove = mt76x02_sta_remove,
        };
        struct mt76x02_dev *dev;
        struct mt76_dev *mdev;
@@ -375,43 +340,6 @@ struct mt76x02_dev *mt76x2_alloc_device(struct device *pdev)
        return dev;
 }
 
-static void mt76x2_regd_notifier(struct wiphy *wiphy,
-                                struct regulatory_request *request)
-{
-       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-       struct mt76x02_dev *dev = hw->priv;
-
-       mt76x2_dfs_set_domain(dev, request->dfs_region);
-}
-
-static const struct ieee80211_iface_limit if_limits[] = {
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_ADHOC)
-       }, {
-               .max = 8,
-               .types = BIT(NL80211_IFTYPE_STATION) |
-#ifdef CONFIG_MAC80211_MESH
-                        BIT(NL80211_IFTYPE_MESH_POINT) |
-#endif
-                        BIT(NL80211_IFTYPE_AP)
-        },
-};
-
-static const struct ieee80211_iface_combination if_comb[] = {
-       {
-               .limits = if_limits,
-               .n_limits = ARRAY_SIZE(if_limits),
-               .max_interfaces = 8,
-               .num_different_channels = 1,
-               .beacon_int_infra_match = true,
-               .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
-                                      BIT(NL80211_CHAN_WIDTH_20) |
-                                      BIT(NL80211_CHAN_WIDTH_40) |
-                                      BIT(NL80211_CHAN_WIDTH_80),
-       }
-};
-
 static void mt76x2_led_set_config(struct mt76_dev *mt76, u8 delay_on,
                                  u8 delay_off)
 {
@@ -462,60 +390,30 @@ static void mt76x2_led_set_brightness(struct led_classdev *led_cdev,
 
 int mt76x2_register_device(struct mt76x02_dev *dev)
 {
-       struct ieee80211_hw *hw = mt76_hw(dev);
-       struct wiphy *wiphy = hw->wiphy;
-       int i, ret;
+       int ret;
 
        INIT_DELAYED_WORK(&dev->cal_work, mt76x2_phy_calibrate);
-       INIT_DELAYED_WORK(&dev->mac_work, mt76x2_mac_work);
 
-       mt76x2_init_device(dev);
+       mt76x02_init_device(dev);
 
        ret = mt76x2_init_hardware(dev);
        if (ret)
                return ret;
 
-       for (i = 0; i < ARRAY_SIZE(dev->macaddr_list); i++) {
-               u8 *addr = dev->macaddr_list[i].addr;
-
-               memcpy(addr, dev->mt76.macaddr, ETH_ALEN);
-
-               if (!i)
-                       continue;
-
-               addr[0] |= BIT(1);
-               addr[0] ^= ((i - 1) << 2);
-       }
-       wiphy->addresses = dev->macaddr_list;
-       wiphy->n_addresses = ARRAY_SIZE(dev->macaddr_list);
-
-       wiphy->iface_combinations = if_comb;
-       wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
-
-       wiphy->reg_notifier = mt76x2_regd_notifier;
-
-       wiphy->interface_modes =
-               BIT(NL80211_IFTYPE_STATION) |
-               BIT(NL80211_IFTYPE_AP) |
-#ifdef CONFIG_MAC80211_MESH
-               BIT(NL80211_IFTYPE_MESH_POINT) |
-#endif
-               BIT(NL80211_IFTYPE_ADHOC);
-
-       wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
-
-       mt76x2_dfs_init_detector(dev);
+       mt76x02_config_mac_addr_list(dev);
 
        /* init led callbacks */
-       dev->mt76.led_cdev.brightness_set = mt76x2_led_set_brightness;
-       dev->mt76.led_cdev.blink_set = mt76x2_led_set_blink;
+       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)
                goto fail;
 
-       mt76x2_init_debugfs(dev);
+       mt76x02_init_debugfs(dev);
        mt76x2_init_txpower(dev, &dev->mt76.sband_2g.sband);
        mt76x2_init_txpower(dev, &dev->mt76.sband_5g.sband);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mac.c
deleted file mode 100644 (file)
index 4b331ed..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
- *
- * 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.
- */
-
-#include <linux/delay.h>
-#include "mt76x2.h"
-#include "mcu.h"
-#include "eeprom.h"
-
-void mt76x2_mac_set_bssid(struct mt76x02_dev *dev, u8 idx, const u8 *addr)
-{
-       idx &= 7;
-       mt76_wr(dev, MT_MAC_APC_BSSID_L(idx), get_unaligned_le32(addr));
-       mt76_rmw_field(dev, MT_MAC_APC_BSSID_H(idx), MT_MAC_APC_BSSID_H_ADDR,
-                      get_unaligned_le16(addr + 4));
-}
-
-static int
-mt76_write_beacon(struct mt76x02_dev *dev, int offset, struct sk_buff *skb)
-{
-       int beacon_len = mt76x02_beacon_offsets[1] - mt76x02_beacon_offsets[0];
-       struct mt76x02_txwi txwi;
-
-       if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x02_txwi)))
-               return -ENOSPC;
-
-       mt76x02_mac_write_txwi(dev, &txwi, skb, NULL, NULL, skb->len);
-
-       mt76_wr_copy(dev, offset, &txwi, sizeof(txwi));
-       offset += sizeof(txwi);
-
-       mt76_wr_copy(dev, offset, skb->data, skb->len);
-       return 0;
-}
-
-static int
-__mt76x2_mac_set_beacon(struct mt76x02_dev *dev, u8 bcn_idx, struct sk_buff *skb)
-{
-       int beacon_len = mt76x02_beacon_offsets[1] - mt76x02_beacon_offsets[0];
-       int beacon_addr = mt76x02_beacon_offsets[bcn_idx];
-       int ret = 0;
-       int i;
-
-       /* Prevent corrupt transmissions during update */
-       mt76_set(dev, MT_BCN_BYPASS_MASK, BIT(bcn_idx));
-
-       if (skb) {
-               ret = mt76_write_beacon(dev, beacon_addr, skb);
-               if (!ret)
-                       dev->beacon_data_mask |= BIT(bcn_idx);
-       } else {
-               dev->beacon_data_mask &= ~BIT(bcn_idx);
-               for (i = 0; i < beacon_len; i += 4)
-                       mt76_wr(dev, beacon_addr + i, 0);
-       }
-
-       mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xff00 | ~dev->beacon_data_mask);
-
-       return ret;
-}
-
-int mt76x2_mac_set_beacon(struct mt76x02_dev *dev, u8 vif_idx,
-                         struct sk_buff *skb)
-{
-       bool force_update = false;
-       int bcn_idx = 0;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(dev->beacons); i++) {
-               if (vif_idx == i) {
-                       force_update = !!dev->beacons[i] ^ !!skb;
-
-                       if (dev->beacons[i])
-                               dev_kfree_skb(dev->beacons[i]);
-
-                       dev->beacons[i] = skb;
-                       __mt76x2_mac_set_beacon(dev, bcn_idx, skb);
-               } else if (force_update && dev->beacons[i]) {
-                       __mt76x2_mac_set_beacon(dev, bcn_idx, dev->beacons[i]);
-               }
-
-               bcn_idx += !!dev->beacons[i];
-       }
-
-       for (i = bcn_idx; i < ARRAY_SIZE(dev->beacons); i++) {
-               if (!(dev->beacon_data_mask & BIT(i)))
-                       break;
-
-               __mt76x2_mac_set_beacon(dev, i, NULL);
-       }
-
-       mt76_rmw_field(dev, MT_MAC_BSSID_DW1, MT_MAC_BSSID_DW1_MBEACON_N,
-                      bcn_idx - 1);
-       return 0;
-}
-
-void mt76x2_mac_set_beacon_enable(struct mt76x02_dev *dev,
-                                 u8 vif_idx, bool val)
-{
-       u8 old_mask = dev->beacon_mask;
-       bool en;
-       u32 reg;
-
-       if (val) {
-               dev->beacon_mask |= BIT(vif_idx);
-       } else {
-               dev->beacon_mask &= ~BIT(vif_idx);
-               mt76x2_mac_set_beacon(dev, vif_idx, NULL);
-       }
-
-       if (!!old_mask == !!dev->beacon_mask)
-               return;
-
-       en = dev->beacon_mask;
-
-       mt76_rmw_field(dev, MT_INT_TIMER_EN, MT_INT_TIMER_EN_PRE_TBTT_EN, en);
-       reg = MT_BEACON_TIME_CFG_BEACON_TX |
-             MT_BEACON_TIME_CFG_TBTT_EN |
-             MT_BEACON_TIME_CFG_TIMER_EN;
-       mt76_rmw(dev, MT_BEACON_TIME_CFG, reg, reg * en);
-
-       if (en)
-               mt76x02_irq_enable(dev, MT_INT_PRE_TBTT | MT_INT_TBTT);
-       else
-               mt76x02_irq_disable(dev, MT_INT_PRE_TBTT | MT_INT_TBTT);
-}
-
-void mt76x2_update_channel(struct mt76_dev *mdev)
-{
-       struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
-       struct mt76_channel_state *state;
-       u32 active, busy;
-
-       state = mt76_channel_state(&dev->mt76, dev->mt76.chandef.chan);
-
-       busy = mt76_rr(dev, MT_CH_BUSY);
-       active = busy + mt76_rr(dev, MT_CH_IDLE);
-
-       spin_lock_bh(&dev->mt76.cc_lock);
-       state->cc_busy += busy;
-       state->cc_active += active;
-       spin_unlock_bh(&dev->mt76.cc_lock);
-}
-
-void mt76x2_mac_work(struct work_struct *work)
-{
-       struct mt76x02_dev *dev = container_of(work, struct mt76x02_dev,
-                                              mac_work.work);
-       int i, idx;
-
-       mt76x2_update_channel(&dev->mt76);
-       for (i = 0, idx = 0; i < 16; i++) {
-               u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i));
-
-               dev->aggr_stats[idx++] += val & 0xffff;
-               dev->aggr_stats[idx++] += val >> 16;
-       }
-
-       ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work,
-                                    MT_CALIBRATE_INTERVAL);
-}
-
-void mt76x2_mac_set_tx_protection(struct mt76x02_dev *dev, u32 val)
-{
-       u32 data = 0;
-
-       if (val != ~0)
-               data = FIELD_PREP(MT_PROT_CFG_CTRL, 1) |
-                      MT_PROT_CFG_RTS_THRESH;
-
-       mt76_rmw_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH, val);
-
-       mt76_rmw(dev, MT_CCK_PROT_CFG,
-                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);
-}
index 034a062..b54a323 100644 (file)
@@ -74,7 +74,7 @@ mt76x2_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef)
        mt76_rr(dev, MT_CH_IDLE);
        mt76_rr(dev, MT_CH_BUSY);
 
-       mt76x2_dfs_init_params(dev);
+       mt76x02_dfs_init_params(dev);
 
        mt76x2_mac_resume(dev);
        tasklet_enable(&dev->dfs_pd.dfs_tasklet);
@@ -127,103 +127,12 @@ mt76x2_config(struct ieee80211_hw *hw, u32 changed)
        return ret;
 }
 
-static void
-mt76x2_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                       struct ieee80211_bss_conf *info, u32 changed)
-{
-       struct mt76x02_dev *dev = hw->priv;
-       struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv;
-
-       mutex_lock(&dev->mt76.mutex);
-
-       if (changed & BSS_CHANGED_BSSID)
-               mt76x2_mac_set_bssid(dev, mvif->idx, info->bssid);
-
-       if (changed & BSS_CHANGED_BEACON_INT) {
-               mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
-                              MT_BEACON_TIME_CFG_INTVAL,
-                              info->beacon_int << 4);
-               dev->beacon_int = info->beacon_int;
-               dev->tbtt_count = 0;
-       }
-
-       if (changed & BSS_CHANGED_BEACON_ENABLED) {
-               tasklet_disable(&dev->pre_tbtt_tasklet);
-               mt76x2_mac_set_beacon_enable(dev, mvif->idx,
-                                            info->enable_beacon);
-               tasklet_enable(&dev->pre_tbtt_tasklet);
-       }
-
-       if (changed & BSS_CHANGED_ERP_SLOT) {
-               int slottime = info->use_short_slot ? 9 : 20;
-
-               dev->slottime = slottime;
-               mt76x2_set_tx_ackto(dev);
-       }
-
-       mutex_unlock(&dev->mt76.mutex);
-}
-
-void
-mt76x2_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps)
-{
-       struct mt76x02_sta *msta = (struct mt76x02_sta *) sta->drv_priv;
-       struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
-       int idx = msta->wcid.idx;
-
-       mt76_stop_tx_queues(&dev->mt76, sta, true);
-       mt76x02_mac_wcid_set_drop(dev, idx, ps);
-}
-
-static void
-mt76x2_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-              const u8 *mac)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       tasklet_disable(&dev->pre_tbtt_tasklet);
-       set_bit(MT76_SCANNING, &dev->mt76.state);
-}
-
-static void
-mt76x2_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       clear_bit(MT76_SCANNING, &dev->mt76.state);
-       tasklet_enable(&dev->pre_tbtt_tasklet);
-}
-
 static void
 mt76x2_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
             u32 queues, bool drop)
 {
 }
 
-static int
-mt76x2_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int *dbm)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       *dbm = dev->mt76.txpower_cur / 2;
-
-       /* convert from per-chain power to combined output on 2x2 devices */
-       *dbm += 3;
-
-       return 0;
-}
-
-static void mt76x2_set_coverage_class(struct ieee80211_hw *hw,
-                                     s16 coverage_class)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       mutex_lock(&dev->mt76.mutex);
-       dev->coverage_class = coverage_class;
-       mt76x2_set_tx_ackto(dev);
-       mutex_unlock(&dev->mt76.mutex);
-}
-
 static int
 mt76x2_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
 {
@@ -264,21 +173,6 @@ static int mt76x2_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant,
        return 0;
 }
 
-static int
-mt76x2_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       if (val != ~0 && val > 0xffff)
-               return -EINVAL;
-
-       mutex_lock(&dev->mutex);
-       mt76x2_mac_set_tx_protection(dev, val);
-       mutex_unlock(&dev->mutex);
-
-       return 0;
-}
-
 const struct ieee80211_ops mt76x2_ops = {
        .tx = mt76x02_tx,
        .start = mt76x2_start,
@@ -287,24 +181,23 @@ const struct ieee80211_ops mt76x2_ops = {
        .remove_interface = mt76x02_remove_interface,
        .config = mt76x2_config,
        .configure_filter = mt76x02_configure_filter,
-       .bss_info_changed = mt76x2_bss_info_changed,
-       .sta_add = mt76x02_sta_add,
-       .sta_remove = mt76x02_sta_remove,
+       .bss_info_changed = mt76x02_bss_info_changed,
+       .sta_state = mt76_sta_state,
        .set_key = mt76x02_set_key,
        .conf_tx = mt76x02_conf_tx,
-       .sw_scan_start = mt76x2_sw_scan,
-       .sw_scan_complete = mt76x2_sw_scan_complete,
+       .sw_scan_start = mt76x02_sw_scan,
+       .sw_scan_complete = mt76x02_sw_scan_complete,
        .flush = mt76x2_flush,
        .ampdu_action = mt76x02_ampdu_action,
-       .get_txpower = mt76x2_get_txpower,
+       .get_txpower = mt76x02_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,
-       .set_coverage_class = mt76x2_set_coverage_class,
+       .set_coverage_class = mt76x02_set_coverage_class,
        .get_survey = mt76_get_survey,
        .set_tim = mt76x2_set_tim,
        .set_antenna = mt76x2_set_antenna,
        .get_antenna = mt76x2_get_antenna,
-       .set_rts_threshold = mt76x2_set_rts_threshold,
+       .set_rts_threshold = mt76x02_set_rts_threshold,
 };
 
index d8fa9ba..03e24ae 100644 (file)
@@ -168,7 +168,6 @@ error:
 int mt76x2_mcu_init(struct mt76x02_dev *dev)
 {
        static const struct mt76_mcu_ops mt76x2_mcu_ops = {
-               .mcu_msg_alloc = mt76x02_mcu_msg_alloc,
                .mcu_send_msg = mt76x02_mcu_msg_send,
        };
        int ret;
@@ -183,6 +182,6 @@ int mt76x2_mcu_init(struct mt76x02_dev *dev)
        if (ret)
                return ret;
 
-       mt76x02_mcu_function_select(dev, Q_SELECT, 1, true);
+       mt76x02_mcu_function_select(dev, Q_SELECT, 1);
        return 0;
 }
index 5bda445..da7cd40 100644 (file)
@@ -38,7 +38,7 @@ mt76x2_phy_tssi_init_cal(struct mt76x02_dev *dev)
        if (mt76x02_ext_pa_enabled(dev, chan->band))
                flag |= BIT(8);
 
-       mt76x02_mcu_calibrate(dev, MCU_CAL_TSSI, flag, true);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_TSSI, flag);
        dev->cal.tssi_cal_done = true;
        return true;
 }
@@ -62,13 +62,13 @@ mt76x2_phy_channel_calibrate(struct mt76x02_dev *dev, bool mac_stopped)
                mt76x2_mac_stop(dev, false);
 
        if (is_5ghz)
-               mt76x02_mcu_calibrate(dev, MCU_CAL_LC, 0, true);
+               mt76x02_mcu_calibrate(dev, MCU_CAL_LC, 0);
 
-       mt76x02_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz, true);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz, true);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz, true);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0, true);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_TX_SHAPING, 0, true);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_TX_SHAPING, 0);
 
        if (!mac_stopped)
                mt76x2_mac_resume(dev);
@@ -124,96 +124,6 @@ void mt76x2_phy_set_antenna(struct mt76x02_dev *dev)
        mt76_wr(dev, MT_BBP(AGC, 0), val);
 }
 
-static void
-mt76x2_phy_set_gain_val(struct mt76x02_dev *dev)
-{
-       u32 val;
-       u8 gain_val[2];
-
-       gain_val[0] = dev->cal.agc_gain_cur[0] - dev->cal.agc_gain_adjust;
-       gain_val[1] = dev->cal.agc_gain_cur[1] - dev->cal.agc_gain_adjust;
-
-       if (dev->mt76.chandef.width >= NL80211_CHAN_WIDTH_40)
-               val = 0x1e42 << 16;
-       else
-               val = 0x1836 << 16;
-
-       val |= 0xf8;
-
-       mt76_wr(dev, MT_BBP(AGC, 8),
-               val | FIELD_PREP(MT_BBP_AGC_GAIN, gain_val[0]));
-       mt76_wr(dev, MT_BBP(AGC, 9),
-               val | FIELD_PREP(MT_BBP_AGC_GAIN, gain_val[1]));
-
-       if (dev->mt76.chandef.chan->flags & IEEE80211_CHAN_RADAR)
-               mt76x2_dfs_adjust_agc(dev);
-}
-
-static void
-mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev)
-{
-       u8 *gain = dev->cal.agc_gain_init;
-       u8 low_gain_delta, gain_delta;
-       bool gain_change;
-       int low_gain;
-       u32 val;
-
-       dev->cal.avg_rssi_all = mt76x02_phy_get_min_avg_rssi(dev);
-
-       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));
-
-       gain_change = (dev->cal.low_gain & 2) ^ (low_gain & 2);
-       dev->cal.low_gain = low_gain;
-
-       if (!gain_change) {
-               if (mt76x02_phy_adjust_vga_gain(dev))
-                       mt76x2_phy_set_gain_val(dev);
-               return;
-       }
-
-       if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80) {
-               mt76_wr(dev, MT_BBP(RXO, 14), 0x00560211);
-               val = mt76_rr(dev, MT_BBP(AGC, 26)) & ~0xf;
-               if (low_gain == 2)
-                       val |= 0x3;
-               else
-                       val |= 0x5;
-               mt76_wr(dev, MT_BBP(AGC, 26), val);
-       } else {
-               mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423);
-       }
-
-       if (mt76x2_has_ext_lna(dev))
-               low_gain_delta = 10;
-       else
-               low_gain_delta = 14;
-
-       if (low_gain == 2) {
-               mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a990);
-               mt76_wr(dev, MT_BBP(AGC, 35), 0x08080808);
-               mt76_wr(dev, MT_BBP(AGC, 37), 0x08080808);
-               gain_delta = low_gain_delta;
-               dev->cal.agc_gain_adjust = 0;
-       } else {
-               mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a991);
-               if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80)
-                       mt76_wr(dev, MT_BBP(AGC, 35), 0x10101014);
-               else
-                       mt76_wr(dev, MT_BBP(AGC, 35), 0x11111116);
-               mt76_wr(dev, MT_BBP(AGC, 37), 0x2121262C);
-               gain_delta = 0;
-               dev->cal.agc_gain_adjust = low_gain_delta;
-       }
-
-       dev->cal.agc_gain_cur[0] = gain[0] - gain_delta;
-       dev->cal.agc_gain_cur[1] = gain[1] - gain_delta;
-       mt76x2_phy_set_gain_val(dev);
-
-       /* clear false CCA counters */
-       mt76_rr(dev, MT_RX_STAT_1);
-}
-
 int mt76x2_phy_set_channel(struct mt76x02_dev *dev,
                           struct cfg80211_chan_def *chandef)
 {
@@ -313,14 +223,14 @@ int mt76x2_phy_set_channel(struct mt76x02_dev *dev,
                u8 val = mt76x02_eeprom_get(dev, MT_EE_BT_RCAL_RESULT);
 
                if (val != 0xff)
-                       mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0, true);
+                       mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0);
        }
 
-       mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel, true);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel);
 
        /* Rx LPF calibration */
        if (!dev->cal.init_cal_done)
-               mt76x02_mcu_calibrate(dev, MCU_CAL_RC, 0, true);
+               mt76x02_mcu_calibrate(dev, MCU_CAL_RC, 0);
 
        dev->cal.init_cal_done = true;
 
@@ -384,7 +294,7 @@ void mt76x2_phy_calibrate(struct work_struct *work)
 
        dev = container_of(work, struct mt76x02_dev, cal_work.work);
        mt76x2_phy_channel_calibrate(dev, false);
-       mt76x2_phy_tssi_compensate(dev, true);
+       mt76x2_phy_tssi_compensate(dev);
        mt76x2_phy_temp_compensate(dev);
        mt76x2_phy_update_channel_gain(dev);
        ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work,
@@ -395,7 +305,7 @@ int mt76x2_phy_start(struct mt76x02_dev *dev)
 {
        int ret;
 
-       ret = mt76x02_mcu_set_radio_state(dev, true, true);
+       ret = mt76x02_mcu_set_radio_state(dev, true);
        if (ret)
                return ret;
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_tx.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_tx.c
deleted file mode 100644 (file)
index 3a2ec86..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
- *
- * 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.
- */
-
-#include "mt76x2.h"
-
-struct beacon_bc_data {
-       struct mt76x02_dev *dev;
-       struct sk_buff_head q;
-       struct sk_buff *tail[8];
-};
-
-static void
-mt76x2_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct mt76x02_dev *dev = (struct mt76x02_dev *) priv;
-       struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv;
-       struct sk_buff *skb = NULL;
-
-       if (!(dev->beacon_mask & BIT(mvif->idx)))
-               return;
-
-       skb = ieee80211_beacon_get(mt76_hw(dev), vif);
-       if (!skb)
-               return;
-
-       mt76x2_mac_set_beacon(dev, mvif->idx, skb);
-}
-
-static void
-mt76x2_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct beacon_bc_data *data = priv;
-       struct mt76x02_dev *dev = data->dev;
-       struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv;
-       struct ieee80211_tx_info *info;
-       struct sk_buff *skb;
-
-       if (!(dev->beacon_mask & BIT(mvif->idx)))
-               return;
-
-       skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif);
-       if (!skb)
-               return;
-
-       info = IEEE80211_SKB_CB(skb);
-       info->control.vif = vif;
-       info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
-       mt76_skb_set_moredata(skb, true);
-       __skb_queue_tail(&data->q, skb);
-       data->tail[mvif->idx] = skb;
-}
-
-static void
-mt76x2_resync_beacon_timer(struct mt76x02_dev *dev)
-{
-       u32 timer_val = dev->beacon_int << 4;
-
-       dev->tbtt_count++;
-
-       /*
-        * Beacon timer drifts by 1us every tick, the timer is configured
-        * in 1/16 TU (64us) units.
-        */
-       if (dev->tbtt_count < 62)
-               return;
-
-       if (dev->tbtt_count >= 64) {
-               dev->tbtt_count = 0;
-               return;
-       }
-
-       /*
-        * The updated beacon interval takes effect after two TBTT, because
-        * at this point the original interval has already been loaded into
-        * the next TBTT_TIMER value
-        */
-       if (dev->tbtt_count == 62)
-               timer_val -= 1;
-
-       mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
-                      MT_BEACON_TIME_CFG_INTVAL, timer_val);
-}
-
-void mt76x2_pre_tbtt_tasklet(unsigned long arg)
-{
-       struct mt76x02_dev *dev = (struct mt76x02_dev *) arg;
-       struct mt76_queue *q = &dev->mt76.q_tx[MT_TXQ_PSD];
-       struct beacon_bc_data data = {};
-       struct sk_buff *skb;
-       int i, nframes;
-
-       mt76x2_resync_beacon_timer(dev);
-
-       data.dev = dev;
-       __skb_queue_head_init(&data.q);
-
-       ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
-               IEEE80211_IFACE_ITER_RESUME_ALL,
-               mt76x2_update_beacon_iter, dev);
-
-       do {
-               nframes = skb_queue_len(&data.q);
-               ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
-                       IEEE80211_IFACE_ITER_RESUME_ALL,
-                       mt76x2_add_buffered_bc, &data);
-       } while (nframes != skb_queue_len(&data.q));
-
-       if (!nframes)
-               return;
-
-       for (i = 0; i < ARRAY_SIZE(data.tail); i++) {
-               if (!data.tail[i])
-                       continue;
-
-               mt76_skb_set_moredata(data.tail[i], false);
-       }
-
-       spin_lock_bh(&q->lock);
-       while ((skb = __skb_dequeue(&data.q)) != NULL) {
-               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-               struct ieee80211_vif *vif = info->control.vif;
-               struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv;
-
-               mt76_dma_tx_queue_skb(&dev->mt76, q, skb, &mvif->group_wcid,
-                                     NULL);
-       }
-       spin_unlock_bh(&q->lock);
-}
-
index e9fff5b..c9634a7 100644 (file)
@@ -210,7 +210,7 @@ void mt76x2_configure_tx_delay(struct mt76x02_dev *dev,
 }
 EXPORT_SYMBOL_GPL(mt76x2_configure_tx_delay);
 
-void mt76x2_phy_tssi_compensate(struct mt76x02_dev *dev, bool wait)
+void mt76x2_phy_tssi_compensate(struct mt76x02_dev *dev)
 {
        struct ieee80211_channel *chan = dev->mt76.chandef.chan;
        struct mt76x2_tx_power_info txp;
@@ -245,8 +245,99 @@ void mt76x2_phy_tssi_compensate(struct mt76x02_dev *dev, bool wait)
                        return;
 
                usleep_range(10000, 20000);
-               mt76x02_mcu_calibrate(dev, MCU_CAL_DPD, chan->hw_value, wait);
+               mt76x02_mcu_calibrate(dev, MCU_CAL_DPD, chan->hw_value);
                dev->cal.dpd_cal_done = true;
        }
 }
 EXPORT_SYMBOL_GPL(mt76x2_phy_tssi_compensate);
+
+static void
+mt76x2_phy_set_gain_val(struct mt76x02_dev *dev)
+{
+       u32 val;
+       u8 gain_val[2];
+
+       gain_val[0] = dev->cal.agc_gain_cur[0] - dev->cal.agc_gain_adjust;
+       gain_val[1] = dev->cal.agc_gain_cur[1] - dev->cal.agc_gain_adjust;
+
+       if (dev->mt76.chandef.width >= NL80211_CHAN_WIDTH_40)
+               val = 0x1e42 << 16;
+       else
+               val = 0x1836 << 16;
+
+       val |= 0xf8;
+
+       mt76_wr(dev, MT_BBP(AGC, 8),
+               val | FIELD_PREP(MT_BBP_AGC_GAIN, gain_val[0]));
+       mt76_wr(dev, MT_BBP(AGC, 9),
+               val | FIELD_PREP(MT_BBP_AGC_GAIN, gain_val[1]));
+
+       if (dev->mt76.chandef.chan->flags & IEEE80211_CHAN_RADAR)
+               mt76x02_phy_dfs_adjust_agc(dev);
+}
+
+void mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev)
+{
+       u8 *gain = dev->cal.agc_gain_init;
+       u8 low_gain_delta, gain_delta;
+       bool gain_change;
+       int low_gain;
+       u32 val;
+
+       dev->cal.avg_rssi_all = mt76x02_phy_get_min_avg_rssi(dev);
+
+       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));
+
+       gain_change = dev->cal.low_gain < 0 ||
+                     (dev->cal.low_gain & 2) ^ (low_gain & 2);
+       dev->cal.low_gain = low_gain;
+
+       if (!gain_change) {
+               if (mt76x02_phy_adjust_vga_gain(dev))
+                       mt76x2_phy_set_gain_val(dev);
+               return;
+       }
+
+       if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80) {
+               mt76_wr(dev, MT_BBP(RXO, 14), 0x00560211);
+               val = mt76_rr(dev, MT_BBP(AGC, 26)) & ~0xf;
+               if (low_gain == 2)
+                       val |= 0x3;
+               else
+                       val |= 0x5;
+               mt76_wr(dev, MT_BBP(AGC, 26), val);
+       } else {
+               mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423);
+       }
+
+       if (mt76x2_has_ext_lna(dev))
+               low_gain_delta = 10;
+       else
+               low_gain_delta = 14;
+
+       if (low_gain == 2) {
+               mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a990);
+               mt76_wr(dev, MT_BBP(AGC, 35), 0x08080808);
+               mt76_wr(dev, MT_BBP(AGC, 37), 0x08080808);
+               gain_delta = low_gain_delta;
+               dev->cal.agc_gain_adjust = 0;
+       } else {
+               mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a991);
+               if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80)
+                       mt76_wr(dev, MT_BBP(AGC, 35), 0x10101014);
+               else
+                       mt76_wr(dev, MT_BBP(AGC, 35), 0x11111116);
+               mt76_wr(dev, MT_BBP(AGC, 37), 0x2121262C);
+               gain_delta = 0;
+               dev->cal.agc_gain_adjust = low_gain_delta;
+       }
+
+       dev->cal.agc_gain_cur[0] = gain[0] - gain_delta;
+       dev->cal.agc_gain_cur[1] = gain[1] - gain_delta;
+       mt76x2_phy_set_gain_val(dev);
+
+       /* clear false CCA counters */
+       mt76_rr(dev, MT_RX_STAT_1);
+}
+EXPORT_SYMBOL_GPL(mt76x2_phy_update_channel_gain);
index 57baf8d..4d1788e 100644 (file)
@@ -131,8 +131,8 @@ err:
 }
 
 MODULE_DEVICE_TABLE(usb, mt76x2u_device_table);
-MODULE_FIRMWARE(MT7662U_FIRMWARE);
-MODULE_FIRMWARE(MT7662U_ROM_PATCH);
+MODULE_FIRMWARE(MT7662_FIRMWARE);
+MODULE_FIRMWARE(MT7662_ROM_PATCH);
 
 static struct usb_driver mt76x2u_driver = {
        .name           = KBUILD_MODNAME,
index 13cce29..0be3784 100644 (file)
@@ -141,6 +141,8 @@ struct mt76x02_dev *mt76x2u_alloc_device(struct device *pdev)
                .tx_complete_skb = mt76x02u_tx_complete_skb,
                .tx_status_data = mt76x02_tx_status_data,
                .rx_skb = mt76x02_queue_rx_skb,
+               .sta_add = mt76x02_sta_add,
+               .sta_remove = mt76x02_sta_remove,
        };
        struct mt76x02_dev *dev;
        struct mt76_dev *mdev;
@@ -156,21 +158,9 @@ struct mt76x02_dev *mt76x2u_alloc_device(struct device *pdev)
        return dev;
 }
 
-static void mt76x2u_init_beacon_offsets(struct mt76x02_dev *dev)
-{
-       mt76_wr(dev, MT_BCN_OFFSET(0), 0x18100800);
-       mt76_wr(dev, MT_BCN_OFFSET(1), 0x38302820);
-       mt76_wr(dev, MT_BCN_OFFSET(2), 0x58504840);
-       mt76_wr(dev, MT_BCN_OFFSET(3), 0x78706860);
-}
-
 int mt76x2u_init_hardware(struct mt76x02_dev *dev)
 {
-       const struct mt76_wcid_addr addr = {
-               .macaddr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
-               .ba_mask = 0,
-       };
-       int i, err;
+       int i, k, err;
 
        mt76x2_reset_wlan(dev, true);
        mt76x2u_power_on(dev);
@@ -191,9 +181,6 @@ int mt76x2u_init_hardware(struct mt76x02_dev *dev)
        if (!mt76x02_wait_for_mac(&dev->mt76))
                return -ETIMEDOUT;
 
-       mt76_wr(dev, MT_HEADER_TRANS_CTRL_REG, 0);
-       mt76_wr(dev, MT_TSO_CTRL, 0);
-
        mt76x2u_init_dma(dev);
 
        err = mt76x2u_mcu_init(dev);
@@ -207,21 +194,18 @@ int mt76x2u_init_hardware(struct mt76x02_dev *dev)
        mt76x02_mac_setaddr(dev, dev->mt76.eeprom.data + MT_EE_MAC_ADDR);
        dev->mt76.rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG);
 
-       mt76x2u_init_beacon_offsets(dev);
-
        if (!mt76x02_wait_for_txrx_idle(&dev->mt76))
                return -ETIMEDOUT;
 
        /* reset wcid table */
-       for (i = 0; i < 254; i++)
-               mt76_wr_copy(dev, MT_WCID_ADDR(i), &addr,
-                            sizeof(struct mt76_wcid_addr));
+       for (i = 0; i < 256; i++)
+               mt76x02_mac_wcid_setup(dev, i, 0, NULL);
 
        /* reset shared key table and pairwise key table */
-       for (i = 0; i < 4; i++)
-               mt76_wr(dev, MT_SKEY_MODE_BASE_0 + 4 * i, 0);
-       for (i = 0; i < 256; i++)
-               mt76_wr(dev, MT_WCID_ATTR(i), 1);
+       for (i = 0; i < 16; i++) {
+               for (k = 0; k < 4; k++)
+                       mt76x02_mac_shared_key_setup(dev, i, k, NULL);
+       }
 
        mt76_clear(dev, MT_BEACON_TIME_CFG,
                   MT_BEACON_TIME_CFG_TIMER_EN |
@@ -245,11 +229,10 @@ int mt76x2u_init_hardware(struct mt76x02_dev *dev)
 int mt76x2u_register_device(struct mt76x02_dev *dev)
 {
        struct ieee80211_hw *hw = mt76_hw(dev);
-       struct wiphy *wiphy = hw->wiphy;
        int err;
 
        INIT_DELAYED_WORK(&dev->cal_work, mt76x2u_phy_calibrate);
-       mt76x2_init_device(dev);
+       mt76x02_init_device(dev);
 
        err = mt76x2u_init_eeprom(dev);
        if (err < 0)
@@ -267,8 +250,6 @@ int mt76x2u_register_device(struct mt76x02_dev *dev)
        if (err < 0)
                goto fail;
 
-       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
-
        err = mt76_register_device(&dev->mt76, true, mt76x02_rates,
                                   ARRAY_SIZE(mt76x02_rates));
        if (err)
@@ -282,7 +263,7 @@ int mt76x2u_register_device(struct mt76x02_dev *dev)
 
        set_bit(MT76_STATE_INITIALIZED, &dev->mt76.state);
 
-       mt76x2_init_debugfs(dev);
+       mt76x02_init_debugfs(dev);
        mt76x2_init_txpower(dev, &dev->mt76.sband_2g.sband);
        mt76x2_init_txpower(dev, &dev->mt76.sband_5g.sband);
 
@@ -297,12 +278,13 @@ void mt76x2u_stop_hw(struct mt76x02_dev *dev)
 {
        mt76u_stop_stat_wk(&dev->mt76);
        cancel_delayed_work_sync(&dev->cal_work);
+       cancel_delayed_work_sync(&dev->mac_work);
        mt76x2u_mac_stop(dev);
 }
 
 void mt76x2u_cleanup(struct mt76x02_dev *dev)
 {
-       mt76x02_mcu_set_radio_state(dev, false, false);
+       mt76x02_mcu_set_radio_state(dev, false);
        mt76x2u_stop_hw(dev);
        mt76u_queues_deinit(&dev->mt76);
        mt76u_mcu_deinit(&dev->mt76);
index 1971a1b..2b48cc5 100644 (file)
@@ -27,6 +27,8 @@ static int mt76x2u_start(struct ieee80211_hw *hw)
        if (ret)
                goto out;
 
+       ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work,
+                                    MT_CALIBRATE_INTERVAL);
        set_bit(MT76_STATE_RUNNING, &dev->mt76.state);
 
 out:
@@ -48,11 +50,12 @@ static int mt76x2u_add_interface(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif)
 {
        struct mt76x02_dev *dev = hw->priv;
+       unsigned int idx = 8;
 
        if (!ether_addr_equal(dev->mt76.macaddr, vif->addr))
                mt76x02_mac_setaddr(dev, vif->addr);
 
-       mt76x02_vif_init(dev, vif, 0);
+       mt76x02_vif_init(dev, vif, idx);
        return 0;
 }
 
@@ -81,29 +84,6 @@ mt76x2u_set_channel(struct mt76x02_dev *dev,
        return err;
 }
 
-static void
-mt76x2u_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                        struct ieee80211_bss_conf *info, u32 changed)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       mutex_lock(&dev->mt76.mutex);
-
-       if (changed & BSS_CHANGED_ASSOC) {
-               mt76x2u_phy_channel_calibrate(dev);
-               mt76x2_apply_gain_adj(dev);
-       }
-
-       if (changed & BSS_CHANGED_BSSID) {
-               mt76_wr(dev, MT_MAC_BSSID_DW0,
-                       get_unaligned_le32(info->bssid));
-               mt76_wr(dev, MT_MAC_BSSID_DW1,
-                       get_unaligned_le16(info->bssid + 4));
-       }
-
-       mutex_unlock(&dev->mt76.mutex);
-}
-
 static int
 mt76x2u_config(struct ieee80211_hw *hw, u32 changed)
 {
@@ -141,39 +121,22 @@ mt76x2u_config(struct ieee80211_hw *hw, u32 changed)
        return err;
 }
 
-static void
-mt76x2u_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-               const u8 *mac)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       set_bit(MT76_SCANNING, &dev->mt76.state);
-}
-
-static void
-mt76x2u_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
-{
-       struct mt76x02_dev *dev = hw->priv;
-
-       clear_bit(MT76_SCANNING, &dev->mt76.state);
-}
-
 const struct ieee80211_ops mt76x2u_ops = {
        .tx = mt76x02_tx,
        .start = mt76x2u_start,
        .stop = mt76x2u_stop,
        .add_interface = mt76x2u_add_interface,
        .remove_interface = mt76x02_remove_interface,
-       .sta_add = mt76x02_sta_add,
-       .sta_remove = mt76x02_sta_remove,
+       .sta_state = mt76_sta_state,
        .set_key = mt76x02_set_key,
        .ampdu_action = mt76x02_ampdu_action,
        .config = mt76x2u_config,
        .wake_tx_queue = mt76_wake_tx_queue,
-       .bss_info_changed = mt76x2u_bss_info_changed,
+       .bss_info_changed = mt76x02_bss_info_changed,
        .configure_filter = mt76x02_configure_filter,
        .conf_tx = mt76x02_conf_tx,
-       .sw_scan_start = mt76x2u_sw_scan,
-       .sw_scan_complete = mt76x2u_sw_scan_complete,
+       .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,
 };
index 3f1e558..45a95ee 100644 (file)
 #define MT76U_MCU_DLM_OFFSET           0x110000
 #define MT76U_MCU_ROM_PATCH_OFFSET     0x90000
 
-int mt76x2u_mcu_set_dynamic_vga(struct mt76x02_dev *dev, u8 channel, bool ap,
-                               bool ext, int rssi, u32 false_cca)
-{
-       struct {
-               __le32 channel;
-               __le32 rssi_val;
-               __le32 false_cca_val;
-       } __packed __aligned(4) msg = {
-               .rssi_val = cpu_to_le32(rssi),
-               .false_cca_val = cpu_to_le32(false_cca),
-       };
-       struct sk_buff *skb;
-       u32 val = channel;
-
-       if (ap)
-               val |= BIT(31);
-       if (ext)
-               val |= BIT(30);
-       msg.channel = cpu_to_le32(val);
-
-       skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
-       return mt76_mcu_send_msg(dev, skb, CMD_DYNC_VGA_OP, true);
-}
-
 static void mt76x2u_mcu_load_ivb(struct mt76x02_dev *dev)
 {
        mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE,
@@ -117,7 +93,7 @@ static int mt76x2u_mcu_load_rom_patch(struct mt76x02_dev *dev)
                return 0;
        }
 
-       err = request_firmware(&fw, MT7662U_ROM_PATCH, dev->mt76.dev);
+       err = request_firmware(&fw, MT7662_ROM_PATCH, dev->mt76.dev);
        if (err < 0)
                return err;
 
@@ -183,7 +159,7 @@ static int mt76x2u_mcu_load_firmware(struct mt76x02_dev *dev)
        int err, len, ilm_len, dlm_len;
        const struct firmware *fw;
 
-       err = request_firmware(&fw, MT7662U_FIRMWARE, dev->mt76.dev);
+       err = request_firmware(&fw, MT7662_FIRMWARE, dev->mt76.dev);
        if (err < 0)
                return err;
 
@@ -282,9 +258,9 @@ int mt76x2u_mcu_init(struct mt76x02_dev *dev)
 {
        int err;
 
-       err = mt76x02_mcu_function_select(dev, Q_SELECT, 1, false);
+       err = mt76x02_mcu_function_select(dev, Q_SELECT, 1);
        if (err < 0)
                return err;
 
-       return mt76x02_mcu_set_radio_state(dev, true, false);
+       return mt76x02_mcu_set_radio_state(dev, true);
 }
index ca96ba6..11d414d 100644 (file)
 #include "eeprom.h"
 #include "../mt76x02_phy.h"
 
-void mt76x2u_phy_channel_calibrate(struct mt76x02_dev *dev)
+static void
+mt76x2u_phy_channel_calibrate(struct mt76x02_dev *dev, bool mac_stopped)
 {
        struct ieee80211_channel *chan = dev->mt76.chandef.chan;
        bool is_5ghz = chan->band == NL80211_BAND_5GHZ;
 
+       if (dev->cal.channel_cal_done)
+               return;
+
        if (mt76x2_channel_silent(dev))
                return;
 
-       mt76x2u_mac_stop(dev);
+       if (!mac_stopped)
+               mt76x2u_mac_stop(dev);
 
        if (is_5ghz)
-               mt76x02_mcu_calibrate(dev, MCU_CAL_LC, 0, false);
-
-       mt76x02_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz, false);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz, false);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz, false);
-       mt76x02_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0, false);
-
-       mt76x2u_mac_resume(dev);
-}
+               mt76x02_mcu_calibrate(dev, MCU_CAL_LC, 0);
 
-static void
-mt76x2u_phy_update_channel_gain(struct mt76x02_dev *dev)
-{
-       u8 channel = dev->mt76.chandef.chan->hw_value;
-       int freq, freq1;
-       u32 false_cca;
-
-       freq = dev->mt76.chandef.chan->center_freq;
-       freq1 = dev->mt76.chandef.center_freq1;
+       mt76x02_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_TX_SHAPING, 0);
 
-       switch (dev->mt76.chandef.width) {
-       case NL80211_CHAN_WIDTH_80: {
-               int ch_group_index;
+       if (!mac_stopped)
+               mt76x2u_mac_resume(dev);
+       mt76x2_apply_gain_adj(dev);
 
-               ch_group_index = (freq - freq1 + 30) / 20;
-               if (WARN_ON(ch_group_index < 0 || ch_group_index > 3))
-                       ch_group_index = 0;
-               channel += 6 - ch_group_index * 4;
-               break;
-       }
-       case NL80211_CHAN_WIDTH_40:
-               if (freq1 > freq)
-                       channel += 2;
-               else
-                       channel -= 2;
-               break;
-       default:
-               break;
-       }
-
-       dev->cal.avg_rssi_all = mt76x02_phy_get_min_avg_rssi(dev);
-       false_cca = FIELD_GET(MT_RX_STAT_1_CCA_ERRORS,
-                             mt76_rr(dev, MT_RX_STAT_1));
-
-       mt76x2u_mcu_set_dynamic_vga(dev, channel, false, false,
-                                   dev->cal.avg_rssi_all, false_cca);
+       dev->cal.channel_cal_done = true;
 }
 
 void mt76x2u_phy_calibrate(struct work_struct *work)
@@ -82,8 +54,9 @@ void mt76x2u_phy_calibrate(struct work_struct *work)
        struct mt76x02_dev *dev;
 
        dev = container_of(work, struct mt76x02_dev, cal_work.work);
-       mt76x2_phy_tssi_compensate(dev, false);
-       mt76x2u_phy_update_channel_gain(dev);
+       mt76x2u_phy_channel_calibrate(dev, false);
+       mt76x2_phy_tssi_compensate(dev);
+       mt76x2_phy_update_channel_gain(dev);
 
        ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work,
                                     MT_CALIBRATE_INTERVAL);
@@ -180,14 +153,14 @@ int mt76x2u_phy_set_channel(struct mt76x02_dev *dev,
                u8 val = mt76x02_eeprom_get(dev, MT_EE_BT_RCAL_RESULT);
 
                if (val != 0xff)
-                       mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0, false);
+                       mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0);
        }
 
-       mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel, false);
+       mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel);
 
        /* Rx LPF calibration */
        if (!dev->cal.init_cal_done)
-               mt76x02_mcu_calibrate(dev, MCU_CAL_RC, 0, false);
+               mt76x02_mcu_calibrate(dev, MCU_CAL_RC, 0);
        dev->cal.init_cal_done = true;
 
        mt76_wr(dev, MT_BBP(AGC, 61), 0xff64a4e2);
@@ -202,6 +175,9 @@ int mt76x2u_phy_set_channel(struct mt76x02_dev *dev,
        if (scan)
                return 0;
 
+       mt76x2u_phy_channel_calibrate(dev, true);
+       mt76x02_init_agc_gain(dev);
+
        if (mt76x2_tssi_enabled(dev)) {
                /* init default values for temp compensation */
                mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP,
@@ -219,7 +195,7 @@ int mt76x2u_phy_set_channel(struct mt76x02_dev *dev,
                                flag |= BIT(0);
                        if (mt76x02_ext_pa_enabled(dev, chan->band))
                                flag |= BIT(8);
-                       mt76x02_mcu_calibrate(dev, MCU_CAL_TSSI, flag, false);
+                       mt76x02_mcu_calibrate(dev, MCU_CAL_TSSI, flag);
                        dev->cal.tssi_cal_done = true;
                }
        }
index 7cbce03..7b71105 100644 (file)
@@ -103,6 +103,157 @@ mt76_check_agg_ssn(struct mt76_txq *mtxq, struct sk_buff *skb)
        mtxq->agg_ssn = le16_to_cpu(hdr->seq_ctrl) + 0x10;
 }
 
+void
+mt76_tx_status_lock(struct mt76_dev *dev, struct sk_buff_head *list)
+                  __acquires(&dev->status_list.lock)
+{
+       __skb_queue_head_init(list);
+       spin_lock_bh(&dev->status_list.lock);
+       __acquire(&dev->status_list.lock);
+}
+EXPORT_SYMBOL_GPL(mt76_tx_status_lock);
+
+void
+mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
+                     __releases(&dev->status_list.unlock)
+{
+       struct sk_buff *skb;
+
+       spin_unlock_bh(&dev->status_list.lock);
+       __release(&dev->status_list.unlock);
+
+       while ((skb = __skb_dequeue(list)) != NULL)
+               ieee80211_tx_status(dev->hw, skb);
+}
+EXPORT_SYMBOL_GPL(mt76_tx_status_unlock);
+
+static void
+__mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags,
+                         struct sk_buff_head *list)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb);
+       u8 done = MT_TX_CB_DMA_DONE | MT_TX_CB_TXS_DONE;
+
+       flags |= cb->flags;
+       cb->flags = flags;
+
+       if ((flags & done) != done)
+               return;
+
+       __skb_unlink(skb, &dev->status_list);
+
+       /* Tx status can be unreliable. if it fails, mark the frame as ACKed */
+       if (flags & MT_TX_CB_TXS_FAILED) {
+               ieee80211_tx_info_clear_status(info);
+               info->status.rates[0].idx = -1;
+               info->flags |= IEEE80211_TX_STAT_ACK;
+       }
+
+       __skb_queue_tail(list, skb);
+}
+
+void
+mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb,
+                       struct sk_buff_head *list)
+{
+       __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_TXS_DONE, list);
+}
+EXPORT_SYMBOL_GPL(mt76_tx_status_skb_done);
+
+int
+mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid,
+                      struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb);
+       int pid;
+
+       if (!wcid)
+               return 0;
+
+       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;
+
+       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;
+
+       pid = wcid->packet_id;
+       cb->wcid = wcid->idx;
+       cb->pktid = pid;
+       cb->jiffies = jiffies;
+
+       __skb_queue_tail(&dev->status_list, skb);
+       spin_unlock_bh(&dev->status_list.lock);
+
+       return pid;
+}
+EXPORT_SYMBOL_GPL(mt76_tx_status_skb_add);
+
+struct sk_buff *
+mt76_tx_status_skb_get(struct mt76_dev *dev, struct mt76_wcid *wcid, int pktid,
+                      struct sk_buff_head *list)
+{
+       struct sk_buff *skb, *tmp;
+
+       if (pktid == MT_PACKET_ID_NO_ACK)
+               return NULL;
+
+       skb_queue_walk_safe(&dev->status_list, skb, tmp) {
+               struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb);
+
+               if (wcid && cb->wcid != wcid->idx)
+                       continue;
+
+               if (cb->pktid == pktid)
+                       return skb;
+
+               if (!pktid &&
+                   !time_after(jiffies, cb->jiffies + MT_TX_STATUS_SKB_TIMEOUT))
+                       continue;
+
+               __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_TXS_FAILED |
+                                                   MT_TX_CB_TXS_DONE, list);
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(mt76_tx_status_skb_get);
+
+void
+mt76_tx_status_check(struct mt76_dev *dev, struct mt76_wcid *wcid, bool flush)
+{
+       struct sk_buff_head list;
+
+       mt76_tx_status_lock(dev, &list);
+       mt76_tx_status_skb_get(dev, wcid, flush ? -1 : 0, &list);
+       mt76_tx_status_unlock(dev, &list);
+}
+EXPORT_SYMBOL_GPL(mt76_tx_status_check);
+
+void mt76_tx_complete_skb(struct mt76_dev *dev, struct sk_buff *skb)
+{
+       struct sk_buff_head list;
+
+       if (!skb->prev) {
+               ieee80211_free_txskb(dev->hw, skb);
+               return;
+       }
+
+       mt76_tx_status_lock(dev, &list);
+       __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_DMA_DONE, &list);
+       mt76_tx_status_unlock(dev, &list);
+}
+EXPORT_SYMBOL_GPL(mt76_tx_complete_skb);
+
 void
 mt76_tx(struct mt76_dev *dev, struct ieee80211_sta *sta,
        struct mt76_wcid *wcid, struct sk_buff *skb)
@@ -400,7 +551,12 @@ void mt76_stop_tx_queues(struct mt76_dev *dev, struct ieee80211_sta *sta,
 
        for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
                struct ieee80211_txq *txq = sta->txq[i];
-               struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
+               struct mt76_txq *mtxq;
+
+               if (!txq)
+                       continue;
+
+               mtxq = (struct mt76_txq *)txq->drv_priv;
 
                spin_lock_bh(&mtxq->hwq->lock);
                mtxq->send_bar = mtxq->aggr && send_bar;
@@ -439,7 +595,7 @@ void mt76_txq_remove(struct mt76_dev *dev, struct ieee80211_txq *txq)
 
        spin_lock_bh(&hwq->lock);
        if (!list_empty(&mtxq->list))
-               list_del(&mtxq->list);
+               list_del_init(&mtxq->list);
        spin_unlock_bh(&hwq->lock);
 
        while ((skb = skb_dequeue(&mtxq->retry_q)) != NULL)
index 5f0faf0..b061263 100644 (file)
@@ -100,7 +100,7 @@ static u32 __mt76u_rr(struct mt76_dev *dev, u32 addr)
        return data;
 }
 
-u32 mt76u_rr(struct mt76_dev *dev, u32 addr)
+static u32 mt76u_rr(struct mt76_dev *dev, u32 addr)
 {
        u32 ret;
 
@@ -110,7 +110,6 @@ u32 mt76u_rr(struct mt76_dev *dev, u32 addr)
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(mt76u_rr);
 
 /* should be called with usb_ctrl_mtx locked */
 static void __mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val)
@@ -136,13 +135,12 @@ static void __mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val)
        trace_usb_reg_wr(dev, addr, val);
 }
 
-void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val)
+static void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val)
 {
        mutex_lock(&dev->usb.usb_ctrl_mtx);
        __mt76u_wr(dev, addr, val);
        mutex_unlock(&dev->usb.usb_ctrl_mtx);
 }
-EXPORT_SYMBOL_GPL(mt76u_wr);
 
 static u32 mt76u_rmw(struct mt76_dev *dev, u32 addr,
                     u32 mask, u32 val)
@@ -356,6 +354,7 @@ int mt76u_submit_buf(struct mt76_dev *dev, int dir, int index,
 
        usb_fill_bulk_urb(buf->urb, udev, pipe, NULL, buf->len,
                          complete_fn, context);
+       trace_submit_urb(dev, buf->urb);
 
        return usb_submit_urb(buf->urb, gfp);
 }
@@ -442,6 +441,8 @@ static void mt76u_complete_rx(struct urb *urb)
        struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
        unsigned long flags;
 
+       trace_rx_urb(dev, urb);
+
        switch (urb->status) {
        case -ECONNRESET:
        case -ESHUTDOWN:
@@ -699,6 +700,7 @@ mt76u_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
        if (q->queued == q->ndesc)
                return -ENOSPC;
 
+       skb->prev = skb->next = NULL;
        err = dev->drv->tx_prepare_skb(dev, NULL, skb, q, wcid, sta, NULL);
        if (err < 0)
                return err;
@@ -728,6 +730,8 @@ static void mt76u_tx_kick(struct mt76_dev *dev, struct mt76_queue *q)
 
        while (q->first != q->tail) {
                buf = &q->entry[q->first].ubuf;
+
+               trace_submit_urb(dev, buf->urb);
                err = usb_submit_urb(buf->urb, GFP_ATOMIC);
                if (err < 0) {
                        if (err == -ENODEV)
index 52db701..b56c323 100644 (file)
 #define MAXNAME                32
 #define DEV_ENTRY   __array(char, wiphy_name, 32)
 #define DEV_ASSIGN  strlcpy(__entry->wiphy_name, wiphy_name(dev->hw->wiphy), MAXNAME)
-#define DEV_PR_FMT  "%s"
+#define DEV_PR_FMT  "%s "
 #define DEV_PR_ARG  __entry->wiphy_name
 
 #define REG_ENTRY      __field(u32, reg) __field(u32, val)
 #define REG_ASSIGN     __entry->reg = reg; __entry->val = val
-#define REG_PR_FMT     " %04x=%08x"
+#define REG_PR_FMT     "reg:0x%04x=0x%08x"
 #define REG_PR_ARG     __entry->reg, __entry->val
 
 DECLARE_EVENT_CLASS(dev_reg_evt,
@@ -61,6 +61,31 @@ DEFINE_EVENT(dev_reg_evt, usb_reg_wr,
        TP_ARGS(dev, reg, val)
 );
 
+DECLARE_EVENT_CLASS(urb_transfer,
+       TP_PROTO(struct mt76_dev *dev, struct urb *u),
+       TP_ARGS(dev, u),
+       TP_STRUCT__entry(
+               DEV_ENTRY __field(unsigned, pipe) __field(u32, len)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->pipe = u->pipe;
+               __entry->len = u->transfer_buffer_length;
+       ),
+       TP_printk(DEV_PR_FMT "p:%08x len:%u",
+                 DEV_PR_ARG, __entry->pipe, __entry->len)
+);
+
+DEFINE_EVENT(urb_transfer, submit_urb,
+       TP_PROTO(struct mt76_dev *dev, struct urb *u),
+       TP_ARGS(dev, u)
+);
+
+DEFINE_EVENT(urb_transfer, rx_urb,
+       TP_PROTO(struct mt76_dev *dev, struct urb *u),
+       TP_ARGS(dev, u)
+);
+
 #endif
 
 #undef TRACE_INCLUDE_PATH
index b8c12a5..6cf5202 100644 (file)
@@ -1,11 +1,11 @@
 config QTNFMAC
        tristate
-       depends on QTNFMAC_PEARL_PCIE
-       default m if QTNFMAC_PEARL_PCIE=m
-       default y if QTNFMAC_PEARL_PCIE=y
+       depends on QTNFMAC_PCIE
+       default m if QTNFMAC_PCIE=m
+       default y if QTNFMAC_PCIE=y
 
-config QTNFMAC_PEARL_PCIE
-       tristate "Quantenna QSR10g PCIe support"
+config QTNFMAC_PCIE
+       tristate "Quantenna QSR1000/QSR2000/QSR10g PCIe support"
        default n
        depends on PCI && CFG80211
        select QTNFMAC
@@ -13,7 +13,8 @@ config QTNFMAC_PEARL_PCIE
        select CRC32
        help
          This option adds support for wireless adapters based on Quantenna
-         802.11ac QSR10g (aka Pearl) FullMAC chipset running over PCIe.
+         802.11ac QSR10g (aka Pearl) and QSR1000/QSR2000 (aka Topaz)
+         FullMAC chipsets running over PCIe.
 
          If you choose to build it as a module, two modules will be built:
-         qtnfmac.ko and qtnfmac_pearl_pcie.ko.
+         qtnfmac.ko and qtnfmac_pcie.ko.
index 17cd7ad..40dffbd 100644 (file)
@@ -19,11 +19,12 @@ qtnfmac-objs += \
 
 #
 
-obj-$(CONFIG_QTNFMAC_PEARL_PCIE) += qtnfmac_pearl_pcie.o
+obj-$(CONFIG_QTNFMAC_PCIE) += qtnfmac_pcie.o
 
-qtnfmac_pearl_pcie-objs += \
+qtnfmac_pcie-objs += \
        shm_ipc.o \
        pcie/pcie.o \
-       pcie/pearl_pcie.o
+       pcie/pearl_pcie.o \
+       pcie/topaz_pcie.o
 
-qtnfmac_pearl_pcie-$(CONFIG_DEBUG_FS) += debug.o
+qtnfmac_pcie-$(CONFIG_DEBUG_FS) += debug.o
index bfdc1ad..659e764 100644 (file)
@@ -84,7 +84,7 @@ static int qtnf_cmd_send_with_reply(struct qtnf_bus *bus,
                                    size_t *var_resp_size)
 {
        struct qlink_cmd *cmd;
-       const struct qlink_resp *resp;
+       struct qlink_resp *resp = NULL;
        struct sk_buff *resp_skb = NULL;
        u16 cmd_id;
        u8 mac_id;
@@ -113,7 +113,12 @@ static int qtnf_cmd_send_with_reply(struct qtnf_bus *bus,
        if (ret)
                goto out;
 
-       resp = (const struct qlink_resp *)resp_skb->data;
+       if (WARN_ON(!resp_skb || !resp_skb->data)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       resp = (struct qlink_resp *)resp_skb->data;
        ret = qtnf_cmd_check_reply_header(resp, cmd_id, mac_id, vif_id,
                                          const_resp_size);
        if (ret)
@@ -686,7 +691,7 @@ int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac,
        struct sk_buff *cmd_skb, *resp_skb = NULL;
        struct qlink_cmd_get_sta_info *cmd;
        const struct qlink_resp_get_sta_info *resp;
-       size_t var_resp_len;
+       size_t var_resp_len = 0;
        int ret = 0;
 
        cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
@@ -1650,7 +1655,7 @@ int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac)
 {
        struct sk_buff *cmd_skb, *resp_skb = NULL;
        const struct qlink_resp_get_mac_info *resp;
-       size_t var_data_len;
+       size_t var_data_len = 0;
        int ret = 0;
 
        cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
@@ -1680,8 +1685,8 @@ int qtnf_cmd_get_hw_info(struct qtnf_bus *bus)
 {
        struct sk_buff *cmd_skb, *resp_skb = NULL;
        const struct qlink_resp_get_hw_info *resp;
+       size_t info_len = 0;
        int ret = 0;
-       size_t info_len;
 
        cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
                                            QLINK_CMD_GET_HW_INFO,
@@ -1709,9 +1714,9 @@ int qtnf_cmd_band_info_get(struct qtnf_wmac *mac,
                           struct ieee80211_supported_band *band)
 {
        struct sk_buff *cmd_skb, *resp_skb = NULL;
-       size_t info_len;
        struct qlink_cmd_band_info_get *cmd;
        struct qlink_resp_band_info_get *resp;
+       size_t info_len = 0;
        int ret = 0;
        u8 qband;
 
@@ -1764,8 +1769,8 @@ out:
 int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac)
 {
        struct sk_buff *cmd_skb, *resp_skb = NULL;
-       size_t response_size;
        struct qlink_resp_phy_params *resp;
+       size_t response_size = 0;
        int ret = 0;
 
        cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0,
@@ -2431,7 +2436,7 @@ int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel,
        struct sk_buff *cmd_skb, *resp_skb = NULL;
        struct qlink_cmd_get_chan_stats *cmd;
        struct qlink_resp_get_chan_stats *resp;
-       size_t var_data_len;
+       size_t var_data_len = 0;
        int ret = 0;
 
        cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
index 16795db..c3a32ef 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0+
 /* Copyright (c) 2018 Quantenna Communications, Inc. All rights reserved. */
 
+#include <linux/module.h>
 #include <linux/printk.h>
 #include <linux/pci.h>
 #include <linux/spinlock.h>
 #include "shm_ipc.h"
 #include "core.h"
 #include "debug.h"
-
-#undef pr_fmt
-#define pr_fmt(fmt)    "qtnf_pcie: %s: " fmt, __func__
+#include "util.h"
+#include "qtn_hw_ids.h"
 
 #define QTN_SYSCTL_BAR 0
 #define QTN_SHMEM_BAR  2
 #define QTN_DMA_BAR    3
 
+#define QTN_PCIE_MAX_FW_BUFSZ          (1 * 1024 * 1024)
+
+static bool use_msi = true;
+module_param(use_msi, bool, 0644);
+MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt");
+
+static unsigned int tx_bd_size_param;
+module_param(tx_bd_size_param, uint, 0644);
+MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size");
+
+static unsigned int rx_bd_size_param = 256;
+module_param(rx_bd_size_param, uint, 0644);
+MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size");
+
+static u8 flashboot = 1;
+module_param(flashboot, byte, 0644);
+MODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS");
+
+static unsigned int fw_blksize_param = QTN_PCIE_MAX_FW_BUFSZ;
+module_param(fw_blksize_param, uint, 0644);
+MODULE_PARM_DESC(fw_blksize_param, "firmware loading block size in bytes");
+
+#define DRV_NAME       "qtnfmac_pcie"
+
 int qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb)
 {
        struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
@@ -58,7 +82,7 @@ int qtnf_pcie_alloc_skb_array(struct qtnf_pcie_bus_priv *priv)
        return 0;
 }
 
-void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus)
+static void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus)
 {
        struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
        struct pci_dev *pdev = priv->pdev;
@@ -72,7 +96,7 @@ static int qtnf_dbg_mps_show(struct seq_file *s, void *data)
        struct qtnf_bus *bus = dev_get_drvdata(s->private);
        struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
 
-       seq_printf(s, "%d\n", priv->mps);
+       seq_printf(s, "%d\n", pcie_get_mps(priv->pdev));
 
        return 0;
 }
@@ -104,8 +128,7 @@ static int qtnf_dbg_shm_stats(struct seq_file *s, void *data)
        return 0;
 }
 
-void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
-                           const char *drv_name)
+void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success)
 {
        struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
        struct pci_dev *pdev = priv->pdev;
@@ -122,7 +145,7 @@ void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
        }
 
        if (boot_success) {
-               qtnf_debugfs_init(bus, drv_name);
+               qtnf_debugfs_init(bus, DRV_NAME);
                qtnf_debugfs_add_entry(bus, "mps", qtnf_dbg_mps_show);
                qtnf_debugfs_add_entry(bus, "msi_enabled", qtnf_dbg_msi_show);
                qtnf_debugfs_add_entry(bus, "shm_stats", qtnf_dbg_shm_stats);
@@ -133,9 +156,8 @@ void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
        put_device(&pdev->dev);
 }
 
-static void qtnf_tune_pcie_mps(struct qtnf_pcie_bus_priv *priv)
+static void qtnf_tune_pcie_mps(struct pci_dev *pdev)
 {
-       struct pci_dev *pdev = priv->pdev;
        struct pci_dev *parent;
        int mps_p, mps_o, mps_m, mps;
        int ret;
@@ -163,12 +185,10 @@ static void qtnf_tune_pcie_mps(struct qtnf_pcie_bus_priv *priv)
        if (ret) {
                pr_err("failed to set mps to %d, keep using current %d\n",
                       mps, mps_o);
-               priv->mps = mps_o;
                return;
        }
 
        pr_debug("set mps to %d (was %d, max %d)\n", mps, mps_o, mps_m);
-       priv->mps = mps;
 }
 
 static void qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv, bool use_msi)
@@ -194,20 +214,20 @@ static void qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv, bool use_msi)
        }
 }
 
-static void __iomem *qtnf_map_bar(struct qtnf_pcie_bus_priv *priv, u8 index)
+static void __iomem *qtnf_map_bar(struct pci_dev *pdev, u8 index)
 {
        void __iomem *vaddr;
        dma_addr_t busaddr;
        size_t len;
        int ret;
 
-       ret = pcim_iomap_regions(priv->pdev, 1 << index, "qtnfmac_pcie");
+       ret = pcim_iomap_regions(pdev, 1 << index, "qtnfmac_pcie");
        if (ret)
                return IOMEM_ERR_PTR(ret);
 
-       busaddr = pci_resource_start(priv->pdev, index);
-       len = pci_resource_len(priv->pdev, index);
-       vaddr = pcim_iomap_table(priv->pdev)[index];
+       busaddr = pci_resource_start(pdev, index);
+       len = pci_resource_len(pdev, index);
+       vaddr = pcim_iomap_table(pdev)[index];
        if (!vaddr)
                return IOMEM_ERR_PTR(-ENOMEM);
 
@@ -217,31 +237,6 @@ static void __iomem *qtnf_map_bar(struct qtnf_pcie_bus_priv *priv, u8 index)
        return vaddr;
 }
 
-static int qtnf_pcie_init_memory(struct qtnf_pcie_bus_priv *priv)
-{
-       int ret = -ENOMEM;
-
-       priv->sysctl_bar = qtnf_map_bar(priv, QTN_SYSCTL_BAR);
-       if (IS_ERR(priv->sysctl_bar)) {
-               pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR);
-               return ret;
-       }
-
-       priv->dmareg_bar = qtnf_map_bar(priv, QTN_DMA_BAR);
-       if (IS_ERR(priv->dmareg_bar)) {
-               pr_err("failed to map BAR%u\n", QTN_DMA_BAR);
-               return ret;
-       }
-
-       priv->epmem_bar = qtnf_map_bar(priv, QTN_SHMEM_BAR);
-       if (IS_ERR(priv->epmem_bar)) {
-               pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR);
-               return ret;
-       }
-
-       return 0;
-}
-
 static void qtnf_pcie_control_rx_callback(void *arg, const u8 __iomem *buf,
                                          size_t len)
 {
@@ -282,27 +277,83 @@ void qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv,
                          ipc_int, &rx_callback);
 }
 
-int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size,
-                   const struct qtnf_bus_ops *bus_ops, u64 dma_mask,
-                   bool use_msi)
+static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct qtnf_pcie_bus_priv *pcie_priv;
        struct qtnf_bus *bus;
+       void __iomem *sysctl_bar;
+       void __iomem *epmem_bar;
+       void __iomem *dmareg_bar;
+       unsigned int chipid;
        int ret;
 
-       bus = devm_kzalloc(&pdev->dev,
-                          sizeof(*bus) + priv_size, GFP_KERNEL);
+       if (!pci_is_pcie(pdev)) {
+               pr_err("device %s is not PCI Express\n", pci_name(pdev));
+               return -EIO;
+       }
+
+       qtnf_tune_pcie_mps(pdev);
+
+       ret = pcim_enable_device(pdev);
+       if (ret) {
+               pr_err("failed to init PCI device %x\n", pdev->device);
+               return ret;
+       }
+
+       pci_set_master(pdev);
+
+       sysctl_bar = qtnf_map_bar(pdev, QTN_SYSCTL_BAR);
+       if (IS_ERR(sysctl_bar)) {
+               pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR);
+               return ret;
+       }
+
+       dmareg_bar = qtnf_map_bar(pdev, QTN_DMA_BAR);
+       if (IS_ERR(dmareg_bar)) {
+               pr_err("failed to map BAR%u\n", QTN_DMA_BAR);
+               return ret;
+       }
+
+       epmem_bar = qtnf_map_bar(pdev, QTN_SHMEM_BAR);
+       if (IS_ERR(epmem_bar)) {
+               pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR);
+               return ret;
+       }
+
+       chipid = qtnf_chip_id_get(sysctl_bar);
+
+       pr_info("identified device: %s\n", qtnf_chipid_to_string(chipid));
+
+       switch (chipid) {
+       case QTN_CHIP_ID_PEARL:
+       case QTN_CHIP_ID_PEARL_B:
+       case QTN_CHIP_ID_PEARL_C:
+               bus = qtnf_pcie_pearl_alloc(pdev);
+               break;
+       case QTN_CHIP_ID_TOPAZ:
+               bus = qtnf_pcie_topaz_alloc(pdev);
+               break;
+       default:
+               pr_err("unsupported chip ID 0x%x\n", chipid);
+               return -ENOTSUPP;
+       }
+
        if (!bus)
                return -ENOMEM;
 
        pcie_priv = get_bus_priv(bus);
-
        pci_set_drvdata(pdev, bus);
-       bus->bus_ops = bus_ops;
        bus->dev = &pdev->dev;
        bus->fw_state = QTNF_FW_STATE_RESET;
        pcie_priv->pdev = pdev;
        pcie_priv->tx_stopped = 0;
+       pcie_priv->rx_bd_num = rx_bd_size_param;
+       pcie_priv->flashboot = flashboot;
+
+       if (fw_blksize_param > QTN_PCIE_MAX_FW_BUFSZ)
+               pcie_priv->fw_blksize =  QTN_PCIE_MAX_FW_BUFSZ;
+       else
+               pcie_priv->fw_blksize = fw_blksize_param;
 
        mutex_init(&bus->bus_lock);
        spin_lock_init(&pcie_priv->tx_lock);
@@ -317,53 +368,35 @@ int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size,
        pcie_priv->workqueue = create_singlethread_workqueue("QTNF_PCIE");
        if (!pcie_priv->workqueue) {
                pr_err("failed to alloc bus workqueue\n");
-               ret = -ENODEV;
-               goto err_init;
-       }
-
-       init_dummy_netdev(&bus->mux_dev);
-
-       if (!pci_is_pcie(pdev)) {
-               pr_err("device %s is not PCI Express\n", pci_name(pdev));
-               ret = -EIO;
-               goto err_base;
-       }
-
-       qtnf_tune_pcie_mps(pcie_priv);
-
-       ret = pcim_enable_device(pdev);
-       if (ret) {
-               pr_err("failed to init PCI device %x\n", pdev->device);
-               goto err_base;
-       } else {
-               pr_debug("successful init of PCI device %x\n", pdev->device);
+               return -ENODEV;
        }
 
-       ret = dma_set_mask_and_coherent(&pdev->dev, dma_mask);
+       ret = dma_set_mask_and_coherent(&pdev->dev,
+                                       pcie_priv->dma_mask_get_cb());
        if (ret) {
-               pr_err("PCIE DMA coherent mask init failed\n");
-               goto err_base;
+               pr_err("PCIE DMA coherent mask init failed 0x%llx\n",
+                      pcie_priv->dma_mask_get_cb());
+               goto error;
        }
 
-       pci_set_master(pdev);
+       init_dummy_netdev(&bus->mux_dev);
        qtnf_pcie_init_irq(pcie_priv, use_msi);
-
-       ret = qtnf_pcie_init_memory(pcie_priv);
-       if (ret < 0) {
-               pr_err("PCIE memory init failed\n");
-               goto err_base;
-       }
-
+       pcie_priv->sysctl_bar = sysctl_bar;
+       pcie_priv->dmareg_bar = dmareg_bar;
+       pcie_priv->epmem_bar = epmem_bar;
        pci_save_state(pdev);
 
+       ret = pcie_priv->probe_cb(bus, tx_bd_size_param);
+       if (ret)
+               goto error;
+
+       qtnf_pcie_bringup_fw_async(bus);
        return 0;
 
-err_base:
+error:
        flush_workqueue(pcie_priv->workqueue);
        destroy_workqueue(pcie_priv->workqueue);
-err_init:
        pci_set_drvdata(pdev, NULL);
-
        return ret;
 }
 
@@ -373,8 +406,17 @@ static void qtnf_pcie_free_shm_ipc(struct qtnf_pcie_bus_priv *priv)
        qtnf_shm_ipc_free(&priv->shm_ipc_ep_out);
 }
 
-void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv)
+static void qtnf_pcie_remove(struct pci_dev *dev)
 {
+       struct qtnf_pcie_bus_priv *priv;
+       struct qtnf_bus *bus;
+
+       bus = pci_get_drvdata(dev);
+       if (!bus)
+               return;
+
+       priv = get_bus_priv(bus);
+
        cancel_work_sync(&bus->fw_work);
 
        if (bus->fw_state == QTNF_FW_STATE_ACTIVE ||
@@ -388,5 +430,77 @@ void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv)
 
        qtnf_pcie_free_shm_ipc(priv);
        qtnf_debugfs_remove(bus);
+       priv->remove_cb(bus);
        pci_set_drvdata(priv->pdev, NULL);
 }
+
+#ifdef CONFIG_PM_SLEEP
+static int qtnf_pcie_suspend(struct device *dev)
+{
+       struct qtnf_pcie_bus_priv *priv;
+       struct qtnf_bus *bus;
+
+       bus = pci_get_drvdata(to_pci_dev(dev));
+       if (!bus)
+               return -EFAULT;
+
+       priv = get_bus_priv(bus);
+       return priv->suspend_cb(bus);
+}
+
+static int qtnf_pcie_resume(struct device *dev)
+{
+       struct qtnf_pcie_bus_priv *priv;
+       struct qtnf_bus *bus;
+
+       bus = pci_get_drvdata(to_pci_dev(dev));
+       if (!bus)
+               return -EFAULT;
+
+       priv = get_bus_priv(bus);
+       return priv->resume_cb(bus);
+}
+
+/* Power Management Hooks */
+static SIMPLE_DEV_PM_OPS(qtnf_pcie_pm_ops, qtnf_pcie_suspend,
+                        qtnf_pcie_resume);
+#endif
+
+static const struct pci_device_id qtnf_pcie_devid_table[] = {
+       {
+               PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QSR,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+       },
+       { },
+};
+
+MODULE_DEVICE_TABLE(pci, qtnf_pcie_devid_table);
+
+static struct pci_driver qtnf_pcie_drv_data = {
+       .name = DRV_NAME,
+       .id_table = qtnf_pcie_devid_table,
+       .probe = qtnf_pcie_probe,
+       .remove = qtnf_pcie_remove,
+#ifdef CONFIG_PM_SLEEP
+       .driver = {
+               .pm = &qtnf_pcie_pm_ops,
+       },
+#endif
+};
+
+static int __init qtnf_pcie_register(void)
+{
+       return pci_register_driver(&qtnf_pcie_drv_data);
+}
+
+static void __exit qtnf_pcie_exit(void)
+{
+       pci_unregister_driver(&qtnf_pcie_drv_data);
+}
+
+module_init(qtnf_pcie_register);
+module_exit(qtnf_pcie_exit);
+
+MODULE_AUTHOR("Quantenna Communications");
+MODULE_DESCRIPTION("Quantenna PCIe bus driver for 802.11 wireless LAN.");
+MODULE_LICENSE("GPL");
index 5c70fb4..bbc074e 100644 (file)
 struct qtnf_pcie_bus_priv {
        struct pci_dev *pdev;
 
+       int (*probe_cb)(struct qtnf_bus *bus, unsigned int tx_bd_size);
+       void (*remove_cb)(struct qtnf_bus *bus);
+       int (*suspend_cb)(struct qtnf_bus *bus);
+       int (*resume_cb)(struct qtnf_bus *bus);
+       u64 (*dma_mask_get_cb)(void);
+
        spinlock_t tx_reclaim_lock;
        spinlock_t tx_lock;
-       int mps;
 
        struct workqueue_struct *workqueue;
        struct tasklet_struct reclaim_tq;
@@ -43,6 +48,8 @@ struct qtnf_pcie_bus_priv {
        struct sk_buff **tx_skb;
        struct sk_buff **rx_skb;
 
+       unsigned int fw_blksize;
+
        u32 rx_bd_w_index;
        u32 rx_bd_r_index;
 
@@ -58,21 +65,18 @@ struct qtnf_pcie_bus_priv {
 
        u8 msi_enabled;
        u8 tx_stopped;
+       bool flashboot;
 };
 
 int qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb);
 int qtnf_pcie_alloc_skb_array(struct qtnf_pcie_bus_priv *priv);
-void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus);
-void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
-                           const char *drv_name);
+void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success);
 void qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv,
                            struct qtnf_shm_ipc_region __iomem *ipc_tx_reg,
                            struct qtnf_shm_ipc_region __iomem *ipc_rx_reg,
                            const struct qtnf_shm_ipc_int *ipc_int);
-int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size,
-                   const struct qtnf_bus_ops *bus_ops, u64 dma_mask,
-                   bool use_msi);
-void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv);
+struct qtnf_bus *qtnf_pcie_pearl_alloc(struct pci_dev *pdev);
+struct qtnf_bus *qtnf_pcie_topaz_alloc(struct pci_dev *pdev);
 
 static inline void qtnf_non_posted_write(u32 val, void __iomem *basereg)
 {
index 95c7b95..1f5facb 100644 (file)
@@ -2,7 +2,6 @@
 /* Copyright (c) 2018 Quantenna Communications */
 
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/firmware.h>
 #include <linux/pci.h>
 #include <linux/vmalloc.h>
 #include "shm_ipc.h"
 #include "debug.h"
 
-static bool use_msi = true;
-module_param(use_msi, bool, 0644);
-MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt");
-
-static unsigned int tx_bd_size_param = 32;
-module_param(tx_bd_size_param, uint, 0644);
-MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size, power of two");
-
-static unsigned int rx_bd_size_param = 256;
-module_param(rx_bd_size_param, uint, 0644);
-MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size, power of two");
-
-static u8 flashboot = 1;
-module_param(flashboot, byte, 0644);
-MODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS");
-
-#define DRV_NAME       "qtnfmac_pearl_pcie"
+#define PEARL_TX_BD_SIZE_DEFAULT       32
 
 struct qtnf_pearl_bda {
        __le16 bda_len;
@@ -415,30 +398,28 @@ static int pearl_hhbm_init(struct qtnf_pcie_pearl_state *ps)
        return 0;
 }
 
-static int qtnf_pcie_pearl_init_xfer(struct qtnf_pcie_pearl_state *ps)
+static int qtnf_pcie_pearl_init_xfer(struct qtnf_pcie_pearl_state *ps,
+                                    unsigned int tx_bd_size)
 {
        struct qtnf_pcie_bus_priv *priv = &ps->base;
        int ret;
        u32 val;
 
-       priv->tx_bd_num = tx_bd_size_param;
-       priv->rx_bd_num = rx_bd_size_param;
-       priv->rx_bd_w_index = 0;
-       priv->rx_bd_r_index = 0;
+       if (tx_bd_size == 0)
+               tx_bd_size = PEARL_TX_BD_SIZE_DEFAULT;
 
-       if (!priv->tx_bd_num || !is_power_of_2(priv->tx_bd_num)) {
-               pr_err("tx_bd_size_param %u is not power of two\n",
-                      priv->tx_bd_num);
-               return -EINVAL;
-       }
+       val = tx_bd_size * sizeof(struct qtnf_pearl_tx_bd);
 
-       val = priv->tx_bd_num * sizeof(struct qtnf_pearl_tx_bd);
-       if (val > PCIE_HHBM_MAX_SIZE) {
-               pr_err("tx_bd_size_param %u is too large\n",
-                      priv->tx_bd_num);
-               return -EINVAL;
+       if (!is_power_of_2(tx_bd_size) || val > PCIE_HHBM_MAX_SIZE) {
+               pr_warn("bad tx_bd_size value %u\n", tx_bd_size);
+               priv->tx_bd_num = PEARL_TX_BD_SIZE_DEFAULT;
+       } else {
+               priv->tx_bd_num = tx_bd_size;
        }
 
+       priv->rx_bd_w_index = 0;
+       priv->rx_bd_r_index = 0;
+
        if (!priv->rx_bd_num || !is_power_of_2(priv->rx_bd_num)) {
                pr_err("rx_bd_size_param %u is not power of two\n",
                       priv->rx_bd_num);
@@ -1006,7 +987,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
        const char *fwname = QTN_PCI_PEARL_FW_NAME;
        bool fw_boot_success = false;
 
-       if (flashboot) {
+       if (ps->base.flashboot) {
                state |= QTN_RC_FW_FLASHBOOT;
        } else {
                ret = request_firmware(&fw, fwname, &pdev->dev);
@@ -1022,7 +1003,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
                            QTN_FW_DL_TIMEOUT_MS)) {
                pr_err("card is not ready\n");
 
-               if (!flashboot)
+               if (!ps->base.flashboot)
                        release_firmware(fw);
 
                goto fw_load_exit;
@@ -1030,7 +1011,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
 
        qtnf_clear_state(&ps->bda->bda_ep_state, QTN_EP_FW_LOADRDY);
 
-       if (flashboot) {
+       if (ps->base.flashboot) {
                pr_info("booting firmware from flash\n");
 
        } else {
@@ -1061,7 +1042,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
        fw_boot_success = true;
 
 fw_load_exit:
-       qtnf_pcie_fw_boot_done(bus, fw_boot_success, DRV_NAME);
+       qtnf_pcie_fw_boot_done(bus, fw_boot_success);
 
        if (fw_boot_success) {
                qtnf_debugfs_add_entry(bus, "hdp_stats", qtnf_dbg_hdp_stats);
@@ -1077,74 +1058,34 @@ static void qtnf_pearl_reclaim_tasklet_fn(unsigned long data)
        qtnf_en_txdone_irq(ps);
 }
 
-static int qtnf_pearl_check_chip_id(struct qtnf_pcie_pearl_state *ps)
+static u64 qtnf_pearl_dma_mask_get(void)
 {
-       unsigned int chipid;
-
-       chipid = qtnf_chip_id_get(ps->base.sysctl_bar);
-
-       switch (chipid) {
-       case QTN_CHIP_ID_PEARL:
-       case QTN_CHIP_ID_PEARL_B:
-       case QTN_CHIP_ID_PEARL_C:
-               pr_info("chip ID is 0x%x\n", chipid);
-               break;
-       default:
-               pr_err("incorrect chip ID 0x%x\n", chipid);
-               return -ENODEV;
-       }
-
-       return 0;
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+       return DMA_BIT_MASK(64);
+#else
+       return DMA_BIT_MASK(32);
+#endif
 }
 
-static int qtnf_pcie_pearl_probe(struct pci_dev *pdev,
-                                const struct pci_device_id *id)
+static int qtnf_pcie_pearl_probe(struct qtnf_bus *bus, unsigned int tx_bd_size)
 {
        struct qtnf_shm_ipc_int ipc_int;
-       struct qtnf_pcie_pearl_state *ps;
-       struct qtnf_bus *bus;
+       struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus);
+       struct pci_dev *pdev = ps->base.pdev;
        int ret;
-       u64 dma_mask;
-
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
-       dma_mask = DMA_BIT_MASK(64);
-#else
-       dma_mask = DMA_BIT_MASK(32);
-#endif
-
-       ret = qtnf_pcie_probe(pdev, sizeof(*ps), &qtnf_pcie_pearl_bus_ops,
-                             dma_mask, use_msi);
-       if (ret)
-               return ret;
-
-       bus = pci_get_drvdata(pdev);
-       ps = get_bus_priv(bus);
 
+       bus->bus_ops = &qtnf_pcie_pearl_bus_ops;
        spin_lock_init(&ps->irq_lock);
-
-       tasklet_init(&ps->base.reclaim_tq, qtnf_pearl_reclaim_tasklet_fn,
-                    (unsigned long)ps);
-       netif_napi_add(&bus->mux_dev, &bus->mux_napi,
-                      qtnf_pcie_pearl_rx_poll, 10);
        INIT_WORK(&bus->fw_work, qtnf_pearl_fw_work_handler);
 
        ps->pcie_reg_base = ps->base.dmareg_bar;
        ps->bda = ps->base.epmem_bar;
        writel(ps->base.msi_enabled, &ps->bda->bda_rc_msi_enabled);
 
-       ipc_int.fn = qtnf_pcie_pearl_ipc_gen_ep_int;
-       ipc_int.arg = ps;
-       qtnf_pcie_init_shm_ipc(&ps->base, &ps->bda->bda_shm_reg1,
-                              &ps->bda->bda_shm_reg2, &ipc_int);
-
-       ret = qtnf_pearl_check_chip_id(ps);
-       if (ret)
-               goto error;
-
-       ret = qtnf_pcie_pearl_init_xfer(ps);
+       ret = qtnf_pcie_pearl_init_xfer(ps, tx_bd_size);
        if (ret) {
                pr_err("PCIE xfer init failed\n");
-               goto error;
+               return ret;
        }
 
        /* init default irq settings */
@@ -1155,95 +1096,63 @@ static int qtnf_pcie_pearl_probe(struct pci_dev *pdev,
 
        ret = devm_request_irq(&pdev->dev, pdev->irq,
                               &qtnf_pcie_pearl_interrupt, 0,
-                              "qtnf_pcie_irq", (void *)bus);
+                              "qtnf_pearl_irq", (void *)bus);
        if (ret) {
                pr_err("failed to request pcie irq %d\n", pdev->irq);
-               goto err_xfer;
+               qtnf_pearl_free_xfer_buffers(ps);
+               return ret;
        }
 
-       qtnf_pcie_bringup_fw_async(bus);
-
-       return 0;
+       tasklet_init(&ps->base.reclaim_tq, qtnf_pearl_reclaim_tasklet_fn,
+                    (unsigned long)ps);
+       netif_napi_add(&bus->mux_dev, &bus->mux_napi,
+                      qtnf_pcie_pearl_rx_poll, 10);
 
-err_xfer:
-       qtnf_pearl_free_xfer_buffers(ps);
-error:
-       qtnf_pcie_remove(bus, &ps->base);
+       ipc_int.fn = qtnf_pcie_pearl_ipc_gen_ep_int;
+       ipc_int.arg = ps;
+       qtnf_pcie_init_shm_ipc(&ps->base, &ps->bda->bda_shm_reg1,
+                              &ps->bda->bda_shm_reg2, &ipc_int);
 
-       return ret;
+       return 0;
 }
 
-static void qtnf_pcie_pearl_remove(struct pci_dev *pdev)
+static void qtnf_pcie_pearl_remove(struct qtnf_bus *bus)
 {
-       struct qtnf_pcie_pearl_state *ps;
-       struct qtnf_bus *bus;
-
-       bus = pci_get_drvdata(pdev);
-       if (!bus)
-               return;
-
-       ps = get_bus_priv(bus);
+       struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus);
 
-       qtnf_pcie_remove(bus, &ps->base);
        qtnf_pearl_reset_ep(ps);
        qtnf_pearl_free_xfer_buffers(ps);
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int qtnf_pcie_pearl_suspend(struct device *dev)
+static int qtnf_pcie_pearl_suspend(struct qtnf_bus *bus)
 {
        return -EOPNOTSUPP;
 }
 
-static int qtnf_pcie_pearl_resume(struct device *dev)
+static int qtnf_pcie_pearl_resume(struct qtnf_bus *bus)
 {
        return 0;
 }
-#endif /* CONFIG_PM_SLEEP */
-
-#ifdef CONFIG_PM_SLEEP
-/* Power Management Hooks */
-static SIMPLE_DEV_PM_OPS(qtnf_pcie_pearl_pm_ops, qtnf_pcie_pearl_suspend,
-                        qtnf_pcie_pearl_resume);
 #endif
 
-static const struct pci_device_id qtnf_pcie_devid_table[] = {
-       {
-               PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QTN_PEARL,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-       },
-       { },
-};
+struct qtnf_bus *qtnf_pcie_pearl_alloc(struct pci_dev *pdev)
+{
+       struct qtnf_bus *bus;
+       struct qtnf_pcie_pearl_state *ps;
 
-MODULE_DEVICE_TABLE(pci, qtnf_pcie_devid_table);
+       bus = devm_kzalloc(&pdev->dev, sizeof(*bus) + sizeof(*ps), GFP_KERNEL);
+       if (!bus)
+               return NULL;
 
-static struct pci_driver qtnf_pcie_pearl_drv_data = {
-       .name = DRV_NAME,
-       .id_table = qtnf_pcie_devid_table,
-       .probe = qtnf_pcie_pearl_probe,
-       .remove = qtnf_pcie_pearl_remove,
+       ps = get_bus_priv(bus);
+       ps->base.probe_cb = qtnf_pcie_pearl_probe;
+       ps->base.remove_cb = qtnf_pcie_pearl_remove;
+       ps->base.dma_mask_get_cb = qtnf_pearl_dma_mask_get;
 #ifdef CONFIG_PM_SLEEP
-       .driver = {
-               .pm = &qtnf_pcie_pearl_pm_ops,
-       },
+       ps->base.resume_cb = qtnf_pcie_pearl_resume;
+       ps->base.suspend_cb = qtnf_pcie_pearl_suspend;
 #endif
-};
-
-static int __init qtnf_pcie_pearl_register(void)
-{
-       pr_info("register Quantenna QSR10g FullMAC PCIE driver\n");
-       return pci_register_driver(&qtnf_pcie_pearl_drv_data);
-}
 
-static void __exit qtnf_pcie_pearl_exit(void)
-{
-       pr_info("unregister Quantenna QSR10g FullMAC PCIE driver\n");
-       pci_unregister_driver(&qtnf_pcie_pearl_drv_data);
+       return bus;
 }
-
-module_init(qtnf_pcie_pearl_register);
-module_exit(qtnf_pcie_pearl_exit);
-
-MODULE_AUTHOR("Quantenna Communications");
-MODULE_DESCRIPTION("Quantenna QSR10g PCIe bus driver for 802.11 wireless LAN.");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
new file mode 100644 (file)
index 0000000..598edb8
--- /dev/null
@@ -0,0 +1,1219 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2018 Quantenna Communications */
+
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/crc32.h>
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <linux/circ_buf.h>
+
+#include "pcie_priv.h"
+#include "topaz_pcie_regs.h"
+#include "topaz_pcie_ipc.h"
+#include "qtn_hw_ids.h"
+#include "core.h"
+#include "bus.h"
+#include "shm_ipc.h"
+#include "debug.h"
+
+#define TOPAZ_TX_BD_SIZE_DEFAULT       128
+
+struct qtnf_topaz_tx_bd {
+       __le32 addr;
+       __le32 info;
+} __packed;
+
+struct qtnf_topaz_rx_bd {
+       __le32 addr;
+       __le32 info;
+} __packed;
+
+struct qtnf_extra_bd_params {
+       __le32 param1;
+       __le32 param2;
+       __le32 param3;
+       __le32 param4;
+} __packed;
+
+#define QTNF_BD_PARAM_OFFSET(n)        offsetof(struct qtnf_extra_bd_params, param##n)
+
+struct vmac_pkt_info {
+       __le32 addr;
+       __le32 info;
+};
+
+struct qtnf_topaz_bda {
+       __le16  bda_len;
+       __le16  bda_version;
+       __le32  bda_bootstate;
+       __le32  bda_dma_mask;
+       __le32  bda_dma_offset;
+       __le32  bda_flags;
+       __le32  bda_img;
+       __le32  bda_img_size;
+       __le32  bda_ep2h_irqstatus;
+       __le32  bda_h2ep_irqstatus;
+       __le32  bda_msi_addr;
+       u8      reserved1[56];
+       __le32  bda_flashsz;
+       u8      bda_boardname[PCIE_BDA_NAMELEN];
+       __le32  bda_pci_pre_status;
+       __le32  bda_pci_endian;
+       __le32  bda_pci_post_status;
+       __le32  bda_h2ep_txd_budget;
+       __le32  bda_ep2h_txd_budget;
+       __le32  bda_rc_rx_bd_base;
+       __le32  bda_rc_rx_bd_num;
+       __le32  bda_rc_tx_bd_base;
+       __le32  bda_rc_tx_bd_num;
+       u8      bda_ep_link_state;
+       u8      bda_rc_link_state;
+       u8      bda_rc_msi_enabled;
+       u8      reserved2;
+       __le32  bda_ep_next_pkt;
+       struct vmac_pkt_info request[QTN_PCIE_RC_TX_QUEUE_LEN];
+       struct qtnf_shm_ipc_region bda_shm_reg1 __aligned(4096);
+       struct qtnf_shm_ipc_region bda_shm_reg2 __aligned(4096);
+} __packed;
+
+struct qtnf_pcie_topaz_state {
+       struct qtnf_pcie_bus_priv base;
+       struct qtnf_topaz_bda __iomem *bda;
+
+       dma_addr_t dma_msi_dummy;
+       u32 dma_msi_imwr;
+
+       struct qtnf_topaz_tx_bd *tx_bd_vbase;
+       struct qtnf_topaz_rx_bd *rx_bd_vbase;
+
+       __le32 __iomem *ep_next_rx_pkt;
+       __le32 __iomem *txqueue_wake;
+       __le32 __iomem *ep_pmstate;
+
+       unsigned long rx_pkt_count;
+};
+
+static void qtnf_deassert_intx(struct qtnf_pcie_topaz_state *ts)
+{
+       void __iomem *reg = ts->base.sysctl_bar + TOPAZ_PCIE_CFG0_OFFSET;
+       u32 cfg;
+
+       cfg = readl(reg);
+       cfg &= ~TOPAZ_ASSERT_INTX;
+       qtnf_non_posted_write(cfg, reg);
+}
+
+static inline int qtnf_topaz_intx_asserted(struct qtnf_pcie_topaz_state *ts)
+{
+       void __iomem *reg = ts->base.sysctl_bar + TOPAZ_PCIE_CFG0_OFFSET;
+       u32 cfg = readl(reg);
+
+       return !!(cfg & TOPAZ_ASSERT_INTX);
+}
+
+static void qtnf_topaz_reset_ep(struct qtnf_pcie_topaz_state *ts)
+{
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_RST_EP_IRQ),
+              TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+       msleep(QTN_EP_RESET_WAIT_MS);
+       pci_restore_state(ts->base.pdev);
+}
+
+static void setup_rx_irqs(struct qtnf_pcie_topaz_state *ts)
+{
+       void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
+
+       ts->dma_msi_imwr = readl(reg);
+}
+
+static void enable_rx_irqs(struct qtnf_pcie_topaz_state *ts)
+{
+       void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
+
+       qtnf_non_posted_write(ts->dma_msi_imwr, reg);
+}
+
+static void disable_rx_irqs(struct qtnf_pcie_topaz_state *ts)
+{
+       void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
+
+       qtnf_non_posted_write(QTN_HOST_LO32(ts->dma_msi_dummy), reg);
+}
+
+static void qtnf_topaz_ipc_gen_ep_int(void *arg)
+{
+       struct qtnf_pcie_topaz_state *ts = arg;
+
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_CTRL_IRQ),
+              TOPAZ_CTL_M2L_INT(ts->base.sysctl_bar));
+}
+
+static int qtnf_is_state(__le32 __iomem *reg, u32 state)
+{
+       u32 s = readl(reg);
+
+       return (s == state);
+}
+
+static void qtnf_set_state(__le32 __iomem *reg, u32 state)
+{
+       qtnf_non_posted_write(state, reg);
+}
+
+static int qtnf_poll_state(__le32 __iomem *reg, u32 state, u32 delay_in_ms)
+{
+       u32 timeout = 0;
+
+       while ((qtnf_is_state(reg, state) == 0)) {
+               usleep_range(1000, 1200);
+               if (++timeout > delay_in_ms)
+                       return -1;
+       }
+
+       return 0;
+}
+
+static int topaz_alloc_bd_table(struct qtnf_pcie_topaz_state *ts,
+                               struct qtnf_topaz_bda __iomem *bda)
+{
+       struct qtnf_extra_bd_params __iomem *extra_params;
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       dma_addr_t paddr;
+       void *vaddr;
+       int len;
+       int i;
+
+       /* bd table */
+
+       len = priv->tx_bd_num * sizeof(struct qtnf_topaz_tx_bd) +
+               priv->rx_bd_num * sizeof(struct qtnf_topaz_rx_bd) +
+                       sizeof(struct qtnf_extra_bd_params);
+
+       vaddr = dmam_alloc_coherent(&priv->pdev->dev, len, &paddr, GFP_KERNEL);
+       if (!vaddr)
+               return -ENOMEM;
+
+       memset(vaddr, 0, len);
+
+       /* tx bd */
+
+       ts->tx_bd_vbase = vaddr;
+       qtnf_non_posted_write(paddr, &bda->bda_rc_tx_bd_base);
+
+       for (i = 0; i < priv->tx_bd_num; i++)
+               ts->tx_bd_vbase[i].info |= cpu_to_le32(QTN_BD_EMPTY);
+
+       pr_debug("TX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr);
+
+       priv->tx_bd_r_index = 0;
+       priv->tx_bd_w_index = 0;
+
+       /* rx bd */
+
+       vaddr = ((struct qtnf_topaz_tx_bd *)vaddr) + priv->tx_bd_num;
+       paddr += priv->tx_bd_num * sizeof(struct qtnf_topaz_tx_bd);
+
+       ts->rx_bd_vbase = vaddr;
+       qtnf_non_posted_write(paddr, &bda->bda_rc_rx_bd_base);
+
+       pr_debug("RX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr);
+
+       /* extra shared params */
+
+       vaddr = ((struct qtnf_topaz_rx_bd *)vaddr) + priv->rx_bd_num;
+       paddr += priv->rx_bd_num * sizeof(struct qtnf_topaz_rx_bd);
+
+       extra_params = (struct qtnf_extra_bd_params __iomem *)vaddr;
+
+       ts->ep_next_rx_pkt = &extra_params->param1;
+       qtnf_non_posted_write(paddr + QTNF_BD_PARAM_OFFSET(1),
+                             &bda->bda_ep_next_pkt);
+       ts->txqueue_wake = &extra_params->param2;
+       ts->ep_pmstate = &extra_params->param3;
+       ts->dma_msi_dummy = paddr + QTNF_BD_PARAM_OFFSET(4);
+
+       return 0;
+}
+
+static int
+topaz_skb2rbd_attach(struct qtnf_pcie_topaz_state *ts, u16 index, u32 wrap)
+{
+       struct qtnf_topaz_rx_bd *rxbd = &ts->rx_bd_vbase[index];
+       struct sk_buff *skb;
+       dma_addr_t paddr;
+
+       skb = __netdev_alloc_skb_ip_align(NULL, SKB_BUF_SIZE, GFP_ATOMIC);
+       if (!skb) {
+               ts->base.rx_skb[index] = NULL;
+               return -ENOMEM;
+       }
+
+       ts->base.rx_skb[index] = skb;
+
+       paddr = pci_map_single(ts->base.pdev, skb->data,
+                              SKB_BUF_SIZE, PCI_DMA_FROMDEVICE);
+       if (pci_dma_mapping_error(ts->base.pdev, paddr)) {
+               pr_err("skb mapping error: %pad\n", &paddr);
+               return -ENOMEM;
+       }
+
+       rxbd->addr = cpu_to_le32(QTN_HOST_LO32(paddr));
+       rxbd->info = cpu_to_le32(QTN_BD_EMPTY | wrap);
+
+       ts->base.rx_bd_w_index = index;
+
+       return 0;
+}
+
+static int topaz_alloc_rx_buffers(struct qtnf_pcie_topaz_state *ts)
+{
+       u16 i;
+       int ret = 0;
+
+       memset(ts->rx_bd_vbase, 0x0,
+              ts->base.rx_bd_num * sizeof(struct qtnf_topaz_rx_bd));
+
+       for (i = 0; i < ts->base.rx_bd_num; i++) {
+               ret = topaz_skb2rbd_attach(ts, i, 0);
+               if (ret)
+                       break;
+       }
+
+       ts->rx_bd_vbase[ts->base.rx_bd_num - 1].info |=
+                                               cpu_to_le32(QTN_BD_WRAP);
+
+       return ret;
+}
+
+/* all rx/tx activity should have ceased before calling this function */
+static void qtnf_topaz_free_xfer_buffers(struct qtnf_pcie_topaz_state *ts)
+{
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       struct qtnf_topaz_rx_bd *rxbd;
+       struct qtnf_topaz_tx_bd *txbd;
+       struct sk_buff *skb;
+       dma_addr_t paddr;
+       int i;
+
+       /* free rx buffers */
+       for (i = 0; i < priv->rx_bd_num; i++) {
+               if (priv->rx_skb && priv->rx_skb[i]) {
+                       rxbd = &ts->rx_bd_vbase[i];
+                       skb = priv->rx_skb[i];
+                       paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(rxbd->addr));
+                       pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE,
+                                        PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb_any(skb);
+                       priv->rx_skb[i] = NULL;
+                       rxbd->addr = 0;
+                       rxbd->info = 0;
+               }
+       }
+
+       /* free tx buffers */
+       for (i = 0; i < priv->tx_bd_num; i++) {
+               if (priv->tx_skb && priv->tx_skb[i]) {
+                       txbd = &ts->tx_bd_vbase[i];
+                       skb = priv->tx_skb[i];
+                       paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(txbd->addr));
+                       pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE,
+                                        PCI_DMA_TODEVICE);
+                       dev_kfree_skb_any(skb);
+                       priv->tx_skb[i] = NULL;
+                       txbd->addr = 0;
+                       txbd->info = 0;
+               }
+       }
+}
+
+static int qtnf_pcie_topaz_init_xfer(struct qtnf_pcie_topaz_state *ts,
+                                    unsigned int tx_bd_size)
+{
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       int ret;
+
+       if (tx_bd_size == 0)
+               tx_bd_size = TOPAZ_TX_BD_SIZE_DEFAULT;
+
+       /* check TX BD queue max length according to struct qtnf_topaz_bda */
+       if (tx_bd_size > QTN_PCIE_RC_TX_QUEUE_LEN) {
+               pr_warn("TX BD queue cannot exceed %d\n",
+                       QTN_PCIE_RC_TX_QUEUE_LEN);
+               tx_bd_size = QTN_PCIE_RC_TX_QUEUE_LEN;
+       }
+
+       priv->tx_bd_num = tx_bd_size;
+       qtnf_non_posted_write(priv->tx_bd_num, &bda->bda_rc_tx_bd_num);
+       qtnf_non_posted_write(priv->rx_bd_num, &bda->bda_rc_rx_bd_num);
+
+       priv->rx_bd_w_index = 0;
+       priv->rx_bd_r_index = 0;
+
+       ret = qtnf_pcie_alloc_skb_array(priv);
+       if (ret) {
+               pr_err("failed to allocate skb array\n");
+               return ret;
+       }
+
+       ret = topaz_alloc_bd_table(ts, bda);
+       if (ret) {
+               pr_err("failed to allocate bd table\n");
+               return ret;
+       }
+
+       ret = topaz_alloc_rx_buffers(ts);
+       if (ret) {
+               pr_err("failed to allocate rx buffers\n");
+               return ret;
+       }
+
+       return ret;
+}
+
+static void qtnf_topaz_data_tx_reclaim(struct qtnf_pcie_topaz_state *ts)
+{
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       struct qtnf_topaz_tx_bd *txbd;
+       struct sk_buff *skb;
+       unsigned long flags;
+       dma_addr_t paddr;
+       u32 tx_done_index;
+       int count = 0;
+       int i;
+
+       spin_lock_irqsave(&priv->tx_reclaim_lock, flags);
+
+       tx_done_index = readl(ts->ep_next_rx_pkt);
+       i = priv->tx_bd_r_index;
+
+       if (CIRC_CNT(priv->tx_bd_w_index, tx_done_index, priv->tx_bd_num))
+               writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_DONE_IRQ),
+                      TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
+
+       while (CIRC_CNT(tx_done_index, i, priv->tx_bd_num)) {
+               skb = priv->tx_skb[i];
+
+               if (likely(skb)) {
+                       txbd = &ts->tx_bd_vbase[i];
+                       paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(txbd->addr));
+                       pci_unmap_single(priv->pdev, paddr, skb->len,
+                                        PCI_DMA_TODEVICE);
+
+                       if (skb->dev) {
+                               qtnf_update_tx_stats(skb->dev, skb);
+                               if (unlikely(priv->tx_stopped)) {
+                                       qtnf_wake_all_queues(skb->dev);
+                                       priv->tx_stopped = 0;
+                               }
+                       }
+
+                       dev_kfree_skb_any(skb);
+               }
+
+               priv->tx_skb[i] = NULL;
+               count++;
+
+               if (++i >= priv->tx_bd_num)
+                       i = 0;
+       }
+
+       priv->tx_reclaim_done += count;
+       priv->tx_reclaim_req++;
+       priv->tx_bd_r_index = i;
+
+       spin_unlock_irqrestore(&priv->tx_reclaim_lock, flags);
+}
+
+static void qtnf_try_stop_xmit(struct qtnf_bus *bus, struct net_device *ndev)
+{
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+
+       if (ndev) {
+               netif_tx_stop_all_queues(ndev);
+               ts->base.tx_stopped = 1;
+       }
+
+       writel(0x0, ts->txqueue_wake);
+
+       /* sync up tx queue status before generating interrupt */
+       dma_wmb();
+
+       /* send irq to card: tx stopped */
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_STOP_IRQ),
+              TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+
+       /* schedule reclaim attempt */
+       tasklet_hi_schedule(&ts->base.reclaim_tq);
+}
+
+static void qtnf_try_wake_xmit(struct qtnf_bus *bus, struct net_device *ndev)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+       int ready;
+
+       ready = readl(ts->txqueue_wake);
+       if (ready) {
+               netif_wake_queue(ndev);
+       } else {
+               /* re-send irq to card: tx stopped */
+               writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_STOP_IRQ),
+                      TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+       }
+}
+
+static int qtnf_tx_queue_ready(struct qtnf_pcie_topaz_state *ts)
+{
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+
+       if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
+                       priv->tx_bd_num)) {
+               qtnf_topaz_data_tx_reclaim(ts);
+
+               if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
+                               priv->tx_bd_num)) {
+                       priv->tx_full_count++;
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb)
+{
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       struct qtnf_topaz_tx_bd *txbd;
+       dma_addr_t skb_paddr;
+       unsigned long flags;
+       int ret = 0;
+       int len;
+       int i;
+
+       spin_lock_irqsave(&priv->tx_lock, flags);
+
+       if (!qtnf_tx_queue_ready(ts)) {
+               qtnf_try_stop_xmit(bus, skb->dev);
+               spin_unlock_irqrestore(&priv->tx_lock, flags);
+               return NETDEV_TX_BUSY;
+       }
+
+       i = priv->tx_bd_w_index;
+       priv->tx_skb[i] = skb;
+       len = skb->len;
+
+       skb_paddr = pci_map_single(priv->pdev, skb->data,
+                                  skb->len, PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(priv->pdev, skb_paddr)) {
+               ret = -ENOMEM;
+               goto tx_done;
+       }
+
+       txbd = &ts->tx_bd_vbase[i];
+       txbd->addr = cpu_to_le32(QTN_HOST_LO32(skb_paddr));
+
+       writel(QTN_HOST_LO32(skb_paddr), &bda->request[i].addr);
+       writel(len | QTN_PCIE_TX_VALID_PKT, &bda->request[i].info);
+
+       /* sync up descriptor updates before generating interrupt */
+       dma_wmb();
+
+       /* generate irq to card: tx done */
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_DONE_IRQ),
+              TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
+
+       if (++i >= priv->tx_bd_num)
+               i = 0;
+
+       priv->tx_bd_w_index = i;
+
+tx_done:
+       if (ret) {
+               if (skb->dev)
+                       skb->dev->stats.tx_dropped++;
+               dev_kfree_skb_any(skb);
+       }
+
+       priv->tx_done_count++;
+       spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+       qtnf_topaz_data_tx_reclaim(ts);
+
+       return NETDEV_TX_OK;
+}
+
+static irqreturn_t qtnf_pcie_topaz_interrupt(int irq, void *data)
+{
+       struct qtnf_bus *bus = (struct qtnf_bus *)data;
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+
+       if (!priv->msi_enabled && !qtnf_topaz_intx_asserted(ts))
+               return IRQ_NONE;
+
+       priv->pcie_irq_count++;
+
+       qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_in);
+       qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_out);
+
+       if (napi_schedule_prep(&bus->mux_napi)) {
+               disable_rx_irqs(ts);
+               __napi_schedule(&bus->mux_napi);
+       }
+
+       tasklet_hi_schedule(&priv->reclaim_tq);
+
+       if (!priv->msi_enabled)
+               qtnf_deassert_intx(ts);
+
+       return IRQ_HANDLED;
+}
+
+static int qtnf_rx_data_ready(struct qtnf_pcie_topaz_state *ts)
+{
+       u16 index = ts->base.rx_bd_r_index;
+       struct qtnf_topaz_rx_bd *rxbd;
+       u32 descw;
+
+       rxbd = &ts->rx_bd_vbase[index];
+       descw = le32_to_cpu(rxbd->info);
+
+       if (descw & QTN_BD_EMPTY)
+               return 0;
+
+       return 1;
+}
+
+static int qtnf_topaz_rx_poll(struct napi_struct *napi, int budget)
+{
+       struct qtnf_bus *bus = container_of(napi, struct qtnf_bus, mux_napi);
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       struct net_device *ndev = NULL;
+       struct sk_buff *skb = NULL;
+       int processed = 0;
+       struct qtnf_topaz_rx_bd *rxbd;
+       dma_addr_t skb_paddr;
+       int consume;
+       u32 descw;
+       u32 poffset;
+       u32 psize;
+       u16 r_idx;
+       u16 w_idx;
+       int ret;
+
+       while (processed < budget) {
+               if (!qtnf_rx_data_ready(ts))
+                       goto rx_out;
+
+               r_idx = priv->rx_bd_r_index;
+               rxbd = &ts->rx_bd_vbase[r_idx];
+               descw = le32_to_cpu(rxbd->info);
+
+               skb = priv->rx_skb[r_idx];
+               poffset = QTN_GET_OFFSET(descw);
+               psize = QTN_GET_LEN(descw);
+               consume = 1;
+
+               if (descw & QTN_BD_EMPTY) {
+                       pr_warn("skip invalid rxbd[%d]\n", r_idx);
+                       consume = 0;
+               }
+
+               if (!skb) {
+                       pr_warn("skip missing rx_skb[%d]\n", r_idx);
+                       consume = 0;
+               }
+
+               if (skb && (skb_tailroom(skb) <  psize)) {
+                       pr_err("skip packet with invalid length: %u > %u\n",
+                              psize, skb_tailroom(skb));
+                       consume = 0;
+               }
+
+               if (skb) {
+                       skb_paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(rxbd->addr));
+                       pci_unmap_single(priv->pdev, skb_paddr, SKB_BUF_SIZE,
+                                        PCI_DMA_FROMDEVICE);
+               }
+
+               if (consume) {
+                       skb_reserve(skb, poffset);
+                       skb_put(skb, psize);
+                       ndev = qtnf_classify_skb(bus, skb);
+                       if (likely(ndev)) {
+                               qtnf_update_rx_stats(ndev, skb);
+                               skb->protocol = eth_type_trans(skb, ndev);
+                               netif_receive_skb(skb);
+                       } else {
+                               pr_debug("drop untagged skb\n");
+                               bus->mux_dev.stats.rx_dropped++;
+                               dev_kfree_skb_any(skb);
+                       }
+               } else {
+                       if (skb) {
+                               bus->mux_dev.stats.rx_dropped++;
+                               dev_kfree_skb_any(skb);
+                       }
+               }
+
+               /* notify card about recv packets once per several packets */
+               if (((++ts->rx_pkt_count) & RX_DONE_INTR_MSK) == 0)
+                       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_RX_DONE_IRQ),
+                              TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
+
+               priv->rx_skb[r_idx] = NULL;
+               if (++r_idx >= priv->rx_bd_num)
+                       r_idx = 0;
+
+               priv->rx_bd_r_index = r_idx;
+
+               /* repalce processed buffer by a new one */
+               w_idx = priv->rx_bd_w_index;
+               while (CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index,
+                                 priv->rx_bd_num) > 0) {
+                       if (++w_idx >= priv->rx_bd_num)
+                               w_idx = 0;
+
+                       ret = topaz_skb2rbd_attach(ts, w_idx,
+                                                  descw & QTN_BD_WRAP);
+                       if (ret) {
+                               pr_err("failed to allocate new rx_skb[%d]\n",
+                                      w_idx);
+                               break;
+                       }
+               }
+
+               processed++;
+       }
+
+rx_out:
+       if (processed < budget) {
+               napi_complete(napi);
+               enable_rx_irqs(ts);
+       }
+
+       return processed;
+}
+
+static void
+qtnf_pcie_data_tx_timeout(struct qtnf_bus *bus, struct net_device *ndev)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+       qtnf_try_wake_xmit(bus, ndev);
+       tasklet_hi_schedule(&ts->base.reclaim_tq);
+}
+
+static void qtnf_pcie_data_rx_start(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+       napi_enable(&bus->mux_napi);
+       enable_rx_irqs(ts);
+}
+
+static void qtnf_pcie_data_rx_stop(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+       disable_rx_irqs(ts);
+       napi_disable(&bus->mux_napi);
+}
+
+static const struct qtnf_bus_ops qtnf_pcie_topaz_bus_ops = {
+       /* control path methods */
+       .control_tx     = qtnf_pcie_control_tx,
+
+       /* data path methods */
+       .data_tx                = qtnf_pcie_data_tx,
+       .data_tx_timeout        = qtnf_pcie_data_tx_timeout,
+       .data_rx_start          = qtnf_pcie_data_rx_start,
+       .data_rx_stop           = qtnf_pcie_data_rx_stop,
+};
+
+static int qtnf_dbg_irq_stats(struct seq_file *s, void *data)
+{
+       struct qtnf_bus *bus = dev_get_drvdata(s->private);
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+       seq_printf(s, "pcie_irq_count(%u)\n", ts->base.pcie_irq_count);
+
+       return 0;
+}
+
+static int qtnf_dbg_pkt_stats(struct seq_file *s, void *data)
+{
+       struct qtnf_bus *bus = dev_get_drvdata(s->private);
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       u32 tx_done_index = readl(ts->ep_next_rx_pkt);
+
+       seq_printf(s, "tx_full_count(%u)\n", priv->tx_full_count);
+       seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count);
+       seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done);
+       seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req);
+
+       seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index);
+       seq_printf(s, "tx_done_index(%u)\n", tx_done_index);
+       seq_printf(s, "tx_bd_w_index(%u)\n", priv->tx_bd_w_index);
+
+       seq_printf(s, "tx host queue len(%u)\n",
+                  CIRC_CNT(priv->tx_bd_w_index, priv->tx_bd_r_index,
+                           priv->tx_bd_num));
+       seq_printf(s, "tx reclaim queue len(%u)\n",
+                  CIRC_CNT(tx_done_index, priv->tx_bd_r_index,
+                           priv->tx_bd_num));
+       seq_printf(s, "tx card queue len(%u)\n",
+                  CIRC_CNT(priv->tx_bd_w_index, tx_done_index,
+                           priv->tx_bd_num));
+
+       seq_printf(s, "rx_bd_r_index(%u)\n", priv->rx_bd_r_index);
+       seq_printf(s, "rx_bd_w_index(%u)\n", priv->rx_bd_w_index);
+       seq_printf(s, "rx alloc queue len(%u)\n",
+                  CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index,
+                             priv->rx_bd_num));
+
+       return 0;
+}
+
+static void qtnf_reset_dma_offset(struct qtnf_pcie_topaz_state *ts)
+{
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       u32 offset = readl(&bda->bda_dma_offset);
+
+       if ((offset & PCIE_DMA_OFFSET_ERROR_MASK) != PCIE_DMA_OFFSET_ERROR)
+               return;
+
+       writel(0x0, &bda->bda_dma_offset);
+}
+
+static int qtnf_pcie_endian_detect(struct qtnf_pcie_topaz_state *ts)
+{
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       u32 timeout = 0;
+       u32 endian;
+       int ret = 0;
+
+       writel(QTN_PCI_ENDIAN_DETECT_DATA, &bda->bda_pci_endian);
+
+       /* flush endian modifications before status update */
+       dma_wmb();
+
+       writel(QTN_PCI_ENDIAN_VALID_STATUS, &bda->bda_pci_pre_status);
+
+       while (readl(&bda->bda_pci_post_status) !=
+              QTN_PCI_ENDIAN_VALID_STATUS) {
+               usleep_range(1000, 1200);
+               if (++timeout > QTN_FW_DL_TIMEOUT_MS) {
+                       pr_err("card endianness detection timed out\n");
+                       ret = -ETIMEDOUT;
+                       goto endian_out;
+               }
+       }
+
+       /* do not read before status is updated */
+       dma_rmb();
+
+       endian = readl(&bda->bda_pci_endian);
+       WARN(endian != QTN_PCI_LITTLE_ENDIAN,
+            "%s: unexpected card endianness", __func__);
+
+endian_out:
+       writel(0, &bda->bda_pci_pre_status);
+       writel(0, &bda->bda_pci_post_status);
+       writel(0, &bda->bda_pci_endian);
+
+       return ret;
+}
+
+static int qtnf_pre_init_ep(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       u32 flags;
+       int ret;
+
+       ret = qtnf_pcie_endian_detect(ts);
+       if (ret < 0) {
+               pr_err("failed to detect card endianness\n");
+               return ret;
+       }
+
+       writeb(ts->base.msi_enabled, &ts->bda->bda_rc_msi_enabled);
+       qtnf_reset_dma_offset(ts);
+
+       /* notify card about driver type and boot mode */
+       flags = readl(&bda->bda_flags) | QTN_BDA_HOST_QLINK_DRV;
+
+       if (ts->base.flashboot)
+               flags |= QTN_BDA_FLASH_BOOT;
+       else
+               flags &= ~QTN_BDA_FLASH_BOOT;
+
+       writel(flags, &bda->bda_flags);
+
+       qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_HOST_RDY);
+       if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_TARGET_RDY,
+                           QTN_FW_DL_TIMEOUT_MS)) {
+               pr_err("card is not ready to boot...\n");
+               return -ETIMEDOUT;
+       }
+
+       return ret;
+}
+
+static int qtnf_post_init_ep(struct qtnf_pcie_topaz_state *ts)
+{
+       struct pci_dev *pdev = ts->base.pdev;
+
+       setup_rx_irqs(ts);
+       disable_rx_irqs(ts);
+
+       if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_QLINK_DONE,
+                           QTN_FW_QLINK_TIMEOUT_MS))
+               return -ETIMEDOUT;
+
+       enable_irq(pdev->irq);
+       return 0;
+}
+
+static int
+qtnf_ep_fw_load(struct qtnf_pcie_topaz_state *ts, const u8 *fw, u32 fw_size)
+{
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       struct pci_dev *pdev = ts->base.pdev;
+       u32 remaining = fw_size;
+       u8 *curr = (u8 *)fw;
+       u32 blksize;
+       u32 nblocks;
+       u32 offset;
+       u32 count;
+       u32 size;
+       dma_addr_t paddr;
+       void *data;
+       int ret = 0;
+
+       pr_debug("FW upload started: fw_addr = 0x%p, size=%d\n", fw, fw_size);
+
+       blksize = ts->base.fw_blksize;
+
+       if (blksize < PAGE_SIZE)
+               blksize = PAGE_SIZE;
+
+       while (blksize >= PAGE_SIZE) {
+               pr_debug("allocating %u bytes to upload FW\n", blksize);
+               data = dma_alloc_coherent(&pdev->dev, blksize,
+                                         &paddr, GFP_KERNEL);
+               if (data)
+                       break;
+               blksize /= 2;
+       }
+
+       if (!data) {
+               pr_err("failed to allocate DMA buffer for FW upload\n");
+               ret = -ENOMEM;
+               goto fw_load_out;
+       }
+
+       nblocks = NBLOCKS(fw_size, blksize);
+       offset = readl(&bda->bda_dma_offset);
+
+       qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_HOST_LOAD);
+       if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_EP_RDY,
+                           QTN_FW_DL_TIMEOUT_MS)) {
+               pr_err("card is not ready to download FW\n");
+               ret = -ETIMEDOUT;
+               goto fw_load_map;
+       }
+
+       for (count = 0 ; count < nblocks; count++) {
+               size = (remaining > blksize) ? blksize : remaining;
+
+               memcpy(data, curr, size);
+               qtnf_non_posted_write(paddr + offset, &bda->bda_img);
+               qtnf_non_posted_write(size, &bda->bda_img_size);
+
+               pr_debug("chunk[%u] VA[0x%p] PA[%pad] sz[%u]\n",
+                        count, (void *)curr, &paddr, size);
+
+               qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_RDY);
+               if (qtnf_poll_state(&ts->bda->bda_bootstate,
+                                   QTN_BDA_FW_BLOCK_DONE,
+                                   QTN_FW_DL_TIMEOUT_MS)) {
+                       pr_err("confirmation for block #%d timed out\n", count);
+                       ret = -ETIMEDOUT;
+                       goto fw_load_map;
+               }
+
+               remaining = (remaining < size) ? remaining : (remaining - size);
+               curr += size;
+       }
+
+       /* upload completion mark: zero-sized block */
+       qtnf_non_posted_write(0, &bda->bda_img);
+       qtnf_non_posted_write(0, &bda->bda_img_size);
+
+       qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_RDY);
+       if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_DONE,
+                           QTN_FW_DL_TIMEOUT_MS)) {
+               pr_err("confirmation for the last block timed out\n");
+               ret = -ETIMEDOUT;
+               goto fw_load_map;
+       }
+
+       /* RC is done */
+       qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_END);
+       if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_LOAD_DONE,
+                           QTN_FW_DL_TIMEOUT_MS)) {
+               pr_err("confirmation for FW upload completion timed out\n");
+               ret = -ETIMEDOUT;
+               goto fw_load_map;
+       }
+
+       pr_debug("FW upload completed: totally sent %d blocks\n", count);
+
+fw_load_map:
+       dma_free_coherent(&pdev->dev, blksize, data, paddr);
+
+fw_load_out:
+       return ret;
+}
+
+static int qtnf_topaz_fw_upload(struct qtnf_pcie_topaz_state *ts,
+                               const char *fwname)
+{
+       const struct firmware *fw;
+       struct pci_dev *pdev = ts->base.pdev;
+       int ret;
+
+       if (qtnf_poll_state(&ts->bda->bda_bootstate,
+                           QTN_BDA_FW_LOAD_RDY,
+                           QTN_FW_DL_TIMEOUT_MS)) {
+               pr_err("%s: card is not ready\n", fwname);
+               return -1;
+       }
+
+       pr_info("starting firmware upload: %s\n", fwname);
+
+       ret = request_firmware(&fw, fwname, &pdev->dev);
+       if (ret < 0) {
+               pr_err("%s: request_firmware error %d\n", fwname, ret);
+               return -1;
+       }
+
+       ret = qtnf_ep_fw_load(ts, fw->data, fw->size);
+       release_firmware(fw);
+
+       if (ret)
+               pr_err("%s: FW upload error\n", fwname);
+
+       return ret;
+}
+
+static void qtnf_topaz_fw_work_handler(struct work_struct *work)
+{
+       struct qtnf_bus *bus = container_of(work, struct qtnf_bus, fw_work);
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+       int ret;
+       int bootloader_needed = readl(&ts->bda->bda_flags) & QTN_BDA_XMIT_UBOOT;
+
+       qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_TARGET_BOOT);
+
+       if (bootloader_needed) {
+               ret = qtnf_topaz_fw_upload(ts, QTN_PCI_TOPAZ_BOOTLD_NAME);
+               if (ret)
+                       goto fw_load_exit;
+
+               ret = qtnf_pre_init_ep(bus);
+               if (ret)
+                       goto fw_load_exit;
+
+               qtnf_set_state(&ts->bda->bda_bootstate,
+                              QTN_BDA_FW_TARGET_BOOT);
+       }
+
+       if (ts->base.flashboot) {
+               pr_info("booting firmware from flash\n");
+
+               ret = qtnf_poll_state(&ts->bda->bda_bootstate,
+                                     QTN_BDA_FW_FLASH_BOOT,
+                                     QTN_FW_DL_TIMEOUT_MS);
+               if (ret)
+                       goto fw_load_exit;
+       } else {
+               ret = qtnf_topaz_fw_upload(ts, QTN_PCI_TOPAZ_FW_NAME);
+               if (ret)
+                       goto fw_load_exit;
+
+               qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_START);
+               ret = qtnf_poll_state(&ts->bda->bda_bootstate,
+                                     QTN_BDA_FW_CONFIG,
+                                     QTN_FW_QLINK_TIMEOUT_MS);
+               if (ret) {
+                       pr_err("FW bringup timed out\n");
+                       goto fw_load_exit;
+               }
+
+               qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_RUN);
+               ret = qtnf_poll_state(&ts->bda->bda_bootstate,
+                                     QTN_BDA_FW_RUNNING,
+                                     QTN_FW_QLINK_TIMEOUT_MS);
+               if (ret) {
+                       pr_err("card bringup timed out\n");
+                       goto fw_load_exit;
+               }
+       }
+
+       pr_info("firmware is up and running\n");
+
+       ret = qtnf_post_init_ep(ts);
+       if (ret)
+               pr_err("FW runtime failure\n");
+
+fw_load_exit:
+       qtnf_pcie_fw_boot_done(bus, ret ? false : true);
+
+       if (ret == 0) {
+               qtnf_debugfs_add_entry(bus, "pkt_stats", qtnf_dbg_pkt_stats);
+               qtnf_debugfs_add_entry(bus, "irq_stats", qtnf_dbg_irq_stats);
+       }
+}
+
+static void qtnf_reclaim_tasklet_fn(unsigned long data)
+{
+       struct qtnf_pcie_topaz_state *ts = (void *)data;
+
+       qtnf_topaz_data_tx_reclaim(ts);
+}
+
+static u64 qtnf_topaz_dma_mask_get(void)
+{
+       return DMA_BIT_MASK(32);
+}
+
+static int qtnf_pcie_topaz_probe(struct qtnf_bus *bus, unsigned int tx_bd_num)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+       struct pci_dev *pdev = ts->base.pdev;
+       struct qtnf_shm_ipc_int ipc_int;
+       unsigned long irqflags;
+       int ret;
+
+       bus->bus_ops = &qtnf_pcie_topaz_bus_ops;
+       INIT_WORK(&bus->fw_work, qtnf_topaz_fw_work_handler);
+       ts->bda = ts->base.epmem_bar;
+
+       /* assign host msi irq before card init */
+       if (ts->base.msi_enabled)
+               irqflags = IRQF_NOBALANCING;
+       else
+               irqflags = IRQF_NOBALANCING | IRQF_SHARED;
+
+       ret = devm_request_irq(&pdev->dev, pdev->irq,
+                              &qtnf_pcie_topaz_interrupt,
+                              irqflags, "qtnf_topaz_irq", (void *)bus);
+       if (ret) {
+               pr_err("failed to request pcie irq %d\n", pdev->irq);
+               return ret;
+       }
+
+       disable_irq(pdev->irq);
+
+       ret = qtnf_pre_init_ep(bus);
+       if (ret) {
+               pr_err("failed to init card\n");
+               return ret;
+       }
+
+       ret = qtnf_pcie_topaz_init_xfer(ts, tx_bd_num);
+       if (ret) {
+               pr_err("PCIE xfer init failed\n");
+               return ret;
+       }
+
+       tasklet_init(&ts->base.reclaim_tq, qtnf_reclaim_tasklet_fn,
+                    (unsigned long)ts);
+       netif_napi_add(&bus->mux_dev, &bus->mux_napi,
+                      qtnf_topaz_rx_poll, 10);
+
+       ipc_int.fn = qtnf_topaz_ipc_gen_ep_int;
+       ipc_int.arg = ts;
+       qtnf_pcie_init_shm_ipc(&ts->base, &ts->bda->bda_shm_reg1,
+                              &ts->bda->bda_shm_reg2, &ipc_int);
+
+       return 0;
+}
+
+static void qtnf_pcie_topaz_remove(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+       qtnf_topaz_reset_ep(ts);
+       qtnf_topaz_free_xfer_buffers(ts);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qtnf_pcie_topaz_suspend(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+       struct pci_dev *pdev = ts->base.pdev;
+
+       writel((u32 __force)PCI_D3hot, ts->ep_pmstate);
+       dma_wmb();
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_PM_EP_IRQ),
+              TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+
+       pci_save_state(pdev);
+       pci_enable_wake(pdev, PCI_D3hot, 1);
+       pci_set_power_state(pdev, PCI_D3hot);
+
+       return 0;
+}
+
+static int qtnf_pcie_topaz_resume(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+       struct pci_dev *pdev = ts->base.pdev;
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       pci_enable_wake(pdev, PCI_D0, 0);
+
+       writel((u32 __force)PCI_D0, ts->ep_pmstate);
+       dma_wmb();
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_PM_EP_IRQ),
+              TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+
+       return 0;
+}
+#endif
+
+struct qtnf_bus *qtnf_pcie_topaz_alloc(struct pci_dev *pdev)
+{
+       struct qtnf_bus *bus;
+       struct qtnf_pcie_topaz_state *ts;
+
+       bus = devm_kzalloc(&pdev->dev, sizeof(*bus) + sizeof(*ts), GFP_KERNEL);
+       if (!bus)
+               return NULL;
+
+       ts = get_bus_priv(bus);
+       ts->base.probe_cb = qtnf_pcie_topaz_probe;
+       ts->base.remove_cb = qtnf_pcie_topaz_remove;
+       ts->base.dma_mask_get_cb = qtnf_topaz_dma_mask_get;
+#ifdef CONFIG_PM_SLEEP
+       ts->base.resume_cb = qtnf_pcie_topaz_resume;
+       ts->base.suspend_cb = qtnf_pcie_topaz_suspend;
+#endif
+
+       return bus;
+}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_ipc.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_ipc.h
new file mode 100644 (file)
index 0000000..eb30e9d
--- /dev/null
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2018 Quantenna Communications */
+
+#ifndef _QTN_FMAC_PCIE_IPC_H_
+#define _QTN_FMAC_PCIE_IPC_H_
+
+#include <linux/types.h>
+
+#include "shm_ipc_defs.h"
+
+/* EP/RC status and flags */
+#define QTN_BDA_PCIE_INIT              0x01
+#define QTN_BDA_PCIE_RDY               0x02
+#define QTN_BDA_FW_LOAD_RDY            0x03
+#define QTN_BDA_FW_LOAD_DONE           0x04
+#define QTN_BDA_FW_START               0x05
+#define QTN_BDA_FW_RUN                 0x06
+#define QTN_BDA_FW_HOST_RDY            0x07
+#define QTN_BDA_FW_TARGET_RDY          0x11
+#define QTN_BDA_FW_TARGET_BOOT         0x12
+#define QTN_BDA_FW_FLASH_BOOT          0x13
+#define QTN_BDA_FW_QLINK_DONE          0x14
+#define QTN_BDA_FW_HOST_LOAD           0x08
+#define QTN_BDA_FW_BLOCK_DONE          0x09
+#define QTN_BDA_FW_BLOCK_RDY           0x0A
+#define QTN_BDA_FW_EP_RDY              0x0B
+#define QTN_BDA_FW_BLOCK_END           0x0C
+#define QTN_BDA_FW_CONFIG              0x0D
+#define QTN_BDA_FW_RUNNING             0x0E
+#define QTN_BDA_PCIE_FAIL              0x82
+#define QTN_BDA_FW_LOAD_FAIL           0x85
+
+#define QTN_BDA_RCMODE                 BIT(1)
+#define QTN_BDA_MSI                    BIT(2)
+#define QTN_BDA_HOST_CALCMD            BIT(3)
+#define QTN_BDA_FLASH_PRESENT          BIT(4)
+#define QTN_BDA_FLASH_BOOT             BIT(5)
+#define QTN_BDA_XMIT_UBOOT             BIT(6)
+#define QTN_BDA_HOST_QLINK_DRV         BIT(7)
+#define QTN_BDA_TARGET_FBOOT_ERR       BIT(8)
+#define QTN_BDA_TARGET_FWLOAD_ERR      BIT(9)
+#define QTN_BDA_HOST_NOFW_ERR          BIT(12)
+#define QTN_BDA_HOST_MEMALLOC_ERR      BIT(13)
+#define QTN_BDA_HOST_MEMMAP_ERR                BIT(14)
+#define QTN_BDA_VER(x)                 (((x) >> 4) & 0xFF)
+#define QTN_BDA_ERROR_MASK             0xFF00
+
+/* registers and shmem address macros */
+#if BITS_PER_LONG == 64
+#define QTN_HOST_HI32(a)       ((u32)(((u64)a) >> 32))
+#define QTN_HOST_LO32(a)       ((u32)(((u64)a) & 0xffffffffUL))
+#define QTN_HOST_ADDR(h, l)    ((((u64)h) << 32) | ((u64)l))
+#elif BITS_PER_LONG == 32
+#define QTN_HOST_HI32(a)       0
+#define QTN_HOST_LO32(a)       ((u32)(((u32)a) & 0xffffffffUL))
+#define QTN_HOST_ADDR(h, l)    ((u32)l)
+#else
+#error Unexpected BITS_PER_LONG value
+#endif
+
+#define QTN_PCIE_BDA_VERSION           0x1001
+
+#define PCIE_BDA_NAMELEN               32
+
+#define QTN_PCIE_RC_TX_QUEUE_LEN       256
+#define QTN_PCIE_TX_VALID_PKT          0x80000000
+#define QTN_PCIE_PKT_LEN_MASK          0xffff
+
+#define QTN_BD_EMPTY           ((uint32_t)0x00000001)
+#define QTN_BD_WRAP            ((uint32_t)0x00000002)
+#define QTN_BD_MASK_LEN                ((uint32_t)0xFFFF0000)
+#define QTN_BD_MASK_OFFSET     ((uint32_t)0x0000FF00)
+
+#define QTN_GET_LEN(x)         (((x) >> 16) & 0xFFFF)
+#define QTN_GET_OFFSET(x)      (((x) >> 8) & 0xFF)
+#define QTN_SET_LEN(len)       (((len) & 0xFFFF) << 16)
+#define QTN_SET_OFFSET(of)     (((of) & 0xFF) << 8)
+
+#define RX_DONE_INTR_MSK       ((0x1 << 6) - 1)
+
+#define PCIE_DMA_OFFSET_ERROR          0xFFFF
+#define PCIE_DMA_OFFSET_ERROR_MASK     0xFFFF
+
+#define QTN_PCI_ENDIAN_DETECT_DATA     0x12345678
+#define QTN_PCI_ENDIAN_REVERSE_DATA    0x78563412
+#define QTN_PCI_ENDIAN_VALID_STATUS    0x3c3c3c3c
+#define QTN_PCI_ENDIAN_INVALID_STATUS  0
+#define QTN_PCI_LITTLE_ENDIAN          0
+#define QTN_PCI_BIG_ENDIAN             0xffffffff
+
+#define NBLOCKS(size, blksize)         \
+       ((size) / (blksize) + (((size) % (blksize) > 0) ? 1 : 0))
+
+#endif /* _QTN_FMAC_PCIE_IPC_H_ */
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_regs.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_regs.h
new file mode 100644 (file)
index 0000000..4782e1e
--- /dev/null
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2018 Quantenna Communications */
+
+#ifndef __TOPAZ_PCIE_H
+#define __TOPAZ_PCIE_H
+
+/* Topaz PCIe DMA registers */
+#define PCIE_DMA_WR_INTR_STATUS(base)          ((base) + 0x9bc)
+#define PCIE_DMA_WR_INTR_MASK(base)            ((base) + 0x9c4)
+#define PCIE_DMA_WR_INTR_CLR(base)             ((base) + 0x9c8)
+#define PCIE_DMA_WR_ERR_STATUS(base)           ((base) + 0x9cc)
+#define PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(base)   ((base) + 0x9D0)
+#define PCIE_DMA_WR_DONE_IMWR_ADDR_HIGH(base)  ((base) + 0x9d4)
+
+#define PCIE_DMA_RD_INTR_STATUS(base)          ((base) + 0x310)
+#define PCIE_DMA_RD_INTR_MASK(base)            ((base) + 0x319)
+#define PCIE_DMA_RD_INTR_CLR(base)             ((base) + 0x31c)
+#define PCIE_DMA_RD_ERR_STATUS_LOW(base)       ((base) + 0x324)
+#define PCIE_DMA_RD_ERR_STATUS_HIGH(base)      ((base) + 0x328)
+#define PCIE_DMA_RD_DONE_IMWR_ADDR_LOW(base)   ((base) + 0x33c)
+#define PCIE_DMA_RD_DONE_IMWR_ADDR_HIGH(base)  ((base) + 0x340)
+
+/* Topaz LHost IPC4 interrupt */
+#define TOPAZ_LH_IPC4_INT(base)                        ((base) + 0x13C)
+#define TOPAZ_LH_IPC4_INT_MASK(base)           ((base) + 0x140)
+
+#define TOPAZ_RC_TX_DONE_IRQ                   (0)
+#define TOPAZ_RC_RST_EP_IRQ                    (1)
+#define TOPAZ_RC_TX_STOP_IRQ                   (2)
+#define TOPAZ_RC_RX_DONE_IRQ                   (3)
+#define TOPAZ_RC_PM_EP_IRQ                     (4)
+
+/* Topaz LHost M2L interrupt */
+#define TOPAZ_CTL_M2L_INT(base)                        ((base) + 0x2C)
+#define TOPAZ_CTL_M2L_INT_MASK(base)           ((base) + 0x30)
+
+#define TOPAZ_RC_CTRL_IRQ                      (6)
+
+#define TOPAZ_IPC_IRQ_WORD(irq)                        (BIT(irq) | BIT(irq + 16))
+
+/* PCIe legacy INTx */
+#define TOPAZ_PCIE_CFG0_OFFSET (0x6C)
+#define TOPAZ_ASSERT_INTX      BIT(9)
+
+#endif /* __TOPAZ_PCIE_H */
index 1fe798a..40295a5 100644 (file)
@@ -23,7 +23,7 @@
 
 /* PCIE Device IDs */
 
-#define        PCIE_DEVICE_ID_QTN_PEARL        (0x0008)
+#define        PCIE_DEVICE_ID_QSR              (0x0008)
 
 #define QTN_REG_SYS_CTRL_CSR           0x14
 #define QTN_CHIP_ID_MASK               0xF0
@@ -35,6 +35,8 @@
 /* FW names */
 
 #define QTN_PCI_PEARL_FW_NAME          "qtn/fmac_qsr10g.img"
+#define QTN_PCI_TOPAZ_FW_NAME          "qtn/fmac_qsr1000.img"
+#define QTN_PCI_TOPAZ_BOOTLD_NAME      "qtn/uboot_qsr1000.img"
 
 static inline unsigned int qtnf_chip_id_get(const void __iomem *regs_base)
 {
index e745733..3bc96b2 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include "util.h"
+#include "qtn_hw_ids.h"
 
 void qtnf_sta_list_init(struct qtnf_sta_list *list)
 {
@@ -116,3 +117,20 @@ void qtnf_sta_list_free(struct qtnf_sta_list *list)
 
        INIT_LIST_HEAD(&list->head);
 }
+
+const char *qtnf_chipid_to_string(unsigned long chip_id)
+{
+       switch (chip_id) {
+       case QTN_CHIP_ID_TOPAZ:
+               return "Topaz";
+       case QTN_CHIP_ID_PEARL:
+               return "Pearl revA";
+       case QTN_CHIP_ID_PEARL_B:
+               return "Pearl revB";
+       case QTN_CHIP_ID_PEARL_C:
+               return "Pearl revC";
+       default:
+               return "unknown";
+       }
+}
+EXPORT_SYMBOL_GPL(qtnf_chipid_to_string);
index 0d4d92b..b8744ba 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/kernel.h>
 #include "core.h"
 
+const char *qtnf_chipid_to_string(unsigned long chip_id);
+
 void qtnf_sta_list_init(struct qtnf_sta_list *list);
 
 struct qtnf_sta_node *qtnf_sta_list_lookup(struct qtnf_sta_list *list,
index 0bc8b02..49a7327 100644 (file)
@@ -1302,7 +1302,7 @@ static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev,
                        break;
                case 2: /* Failure, excessive retries */
                        __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags);
-                       /* Don't break, this is a failed frame! */
+                       /* Fall through - this is a failed frame! */
                default: /* Failure */
                        __set_bit(TXDONE_FAILURE, &txdesc.flags);
                }
index 1ff5434..e8e7bfe 100644 (file)
@@ -1430,7 +1430,7 @@ static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev,
                        break;
                case 2: /* Failure, excessive retries */
                        __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags);
-                       /* Don't break, this is a failed frame! */
+                       /* Fall through - this is a failed frame! */
                default: /* Failure */
                        __set_bit(TXDONE_FAILURE, &txdesc.flags);
                }
index 9e7b893..0e95555 100644 (file)
@@ -2482,6 +2482,7 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
                switch (rt2x00dev->default_ant.tx_chain_num) {
                case 1:
                        rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1);
+                       /* fall through */
                case 2:
                        rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1);
                        break;
@@ -2490,6 +2491,7 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
                switch (rt2x00dev->default_ant.rx_chain_num) {
                case 1:
                        rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1);
+                       /* fall through */
                case 2:
                        rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1);
                        break;
@@ -9457,8 +9459,10 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        switch (rx_chains) {
        case 3:
                spec->ht.mcs.rx_mask[2] = 0xff;
+               /* fall through */
        case 2:
                spec->ht.mcs.rx_mask[1] = 0xff;
+               /* fall through */
        case 1:
                spec->ht.mcs.rx_mask[0] = 0xff;
                spec->ht.mcs.rx_mask[4] = 0x1; /* MCS32 */
index cb0e119..4c5de8f 100644 (file)
@@ -2226,7 +2226,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
                        break;
                case 6: /* Failure, excessive retries */
                        __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags);
-                       /* Don't break, this is a failed frame! */
+                       /* Fall through - this is a failed frame! */
                default: /* Failure */
                        __set_bit(TXDONE_FAILURE, &txdesc.flags);
                }
index 08c607c..33ad875 100644 (file)
@@ -889,8 +889,10 @@ static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev,
        switch (ccsindex = get_free_tx_ccs(local)) {
        case ECCSBUSY:
                pr_debug("ray_hw_xmit tx_ccs table busy\n");
+               /* fall through */
        case ECCSFULL:
                pr_debug("ray_hw_xmit No free tx ccs\n");
+               /* fall through */
        case ECARDGONE:
                netif_stop_queue(dev);
                return XMIT_NO_CCS;
index cec3778..1a2ea8b 100644 (file)
@@ -444,12 +444,13 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev)
                skb_queue_tail(&priv->rx_queue, skb);
                usb_anchor_urb(entry, &priv->anchored);
                ret = usb_submit_urb(entry, GFP_KERNEL);
-               usb_put_urb(entry);
                if (ret) {
                        skb_unlink(skb, &priv->rx_queue);
                        usb_unanchor_urb(entry);
+                       usb_put_urb(entry);
                        goto err;
                }
+               usb_put_urb(entry);
        }
        return ret;
 
index 56040b1..2bd4305 100644 (file)
@@ -1153,6 +1153,7 @@ void rtl8xxxu_gen1_config_channel(struct ieee80211_hw *hw)
        switch (hw->conf.chandef.width) {
        case NL80211_CHAN_WIDTH_20_NOHT:
                ht = false;
+               /* fall through */
        case NL80211_CHAN_WIDTH_20:
                opmode |= BW_OPMODE_20MHZ;
                rtl8xxxu_write8(priv, REG_BW_OPMODE, opmode);
@@ -1280,6 +1281,7 @@ void rtl8xxxu_gen2_config_channel(struct ieee80211_hw *hw)
        switch (hw->conf.chandef.width) {
        case NL80211_CHAN_WIDTH_20_NOHT:
                ht = false;
+               /* fall through */
        case NL80211_CHAN_WIDTH_20:
                rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_20;
                subchannel = 0;
@@ -1748,9 +1750,11 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv)
                case 3:
                        priv->ep_tx_low_queue = 1;
                        priv->ep_tx_count++;
+                       /* fall through */
                case 2:
                        priv->ep_tx_normal_queue = 1;
                        priv->ep_tx_count++;
+                       /* fall through */
                case 1:
                        priv->ep_tx_high_queue = 1;
                        priv->ep_tx_count++;
@@ -5688,6 +5692,7 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                break;
        case WLAN_CIPHER_SUITE_TKIP:
                key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+               break;
        default:
                return -EOPNOTSUPP;
        }
index f4122c8..ef9b502 100644 (file)
@@ -2289,6 +2289,7 @@ void rtl_c2hcmd_enqueue(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        if (rtl_c2h_fast_cmd(hw, skb)) {
                rtl_c2h_content_parsing(hw, skb);
+               kfree_skb(skb);
                return;
        }
 
index 6fbf884..d748aab 100644 (file)
@@ -292,11 +292,9 @@ bool halbtc_send_bt_mp_operation(struct btc_coexist *btcoexist, u8 op_code,
 static void halbtc_leave_lps(struct btc_coexist *btcoexist)
 {
        struct rtl_priv *rtlpriv;
-       struct rtl_ps_ctl *ppsc;
        bool ap_enable = false;
 
        rtlpriv = btcoexist->adapter;
-       ppsc = rtl_psc(rtlpriv);
 
        btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE,
                           &ap_enable);
@@ -315,11 +313,9 @@ static void halbtc_leave_lps(struct btc_coexist *btcoexist)
 static void halbtc_enter_lps(struct btc_coexist *btcoexist)
 {
        struct rtl_priv *rtlpriv;
-       struct rtl_ps_ctl *ppsc;
        bool ap_enable = false;
 
        rtlpriv = btcoexist->adapter;
-       ppsc = rtl_psc(rtlpriv);
 
        btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE,
                           &ap_enable);
index 4c1f8b0..14dcb08 100644 (file)
@@ -29,7 +29,6 @@
 #include "../stats.h"
 #include "reg.h"
 #include "def.h"
-#include "phy.h"
 #include "trx.h"
 #include "led.h"
 #include "dm.h"
index 85cedd0..75bfa9d 100644 (file)
@@ -173,7 +173,7 @@ static int _rtl92d_fw_init(struct ieee80211_hw *hw)
                         rtl_read_byte(rtlpriv, FW_MAC1_READY));
        }
        RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
-                "Polling FW ready fail!! REG_MCUFWDL:0x%08ul\n",
+                "Polling FW ready fail!! REG_MCUFWDL:0x%08x\n",
                 rtl_read_dword(rtlpriv, REG_MCUFWDL));
        return -1;
 }
index 5cf29f5..3f33278 100644 (file)
@@ -509,13 +509,10 @@ bool rtl8723e_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
        int i;
        bool rtstatus = true;
        u32 *radioa_array_table;
-       u32 *radiob_array_table;
-       u16 radioa_arraylen, radiob_arraylen;
+       u16 radioa_arraylen;
 
        radioa_arraylen = RTL8723ERADIOA_1TARRAYLENGTH;
        radioa_array_table = RTL8723E_RADIOA_1TARRAY;
-       radiob_arraylen = RTL8723E_RADIOB_1TARRAYLENGTH;
-       radiob_array_table = RTL8723E_RADIOB_1TARRAY;
 
        rtstatus = true;
 
index 61e8604..1bbee0b 100644 (file)
@@ -475,10 +475,6 @@ u32 RTL8723E_RADIOA_1TARRAY[RTL8723ERADIOA_1TARRAYLENGTH] = {
        0x000, 0x00030159,
 };
 
-u32 RTL8723E_RADIOB_1TARRAY[RTL8723E_RADIOB_1TARRAYLENGTH] = {
-       0x0,
-};
-
 u32 RTL8723EMAC_ARRAY[RTL8723E_MACARRAYLENGTH] = {
        0x420, 0x00000080,
        0x423, 0x00000000,
index 57a548c..a044f3c 100644 (file)
@@ -36,8 +36,6 @@ extern u32 RTL8723EPHY_REG_1TARRAY[RTL8723E_PHY_REG_1TARRAY_LENGTH];
 extern u32 RTL8723EPHY_REG_ARRAY_PG[RTL8723E_PHY_REG_ARRAY_PGLENGTH];
 #define RTL8723ERADIOA_1TARRAYLENGTH           282
 extern u32 RTL8723E_RADIOA_1TARRAY[RTL8723ERADIOA_1TARRAYLENGTH];
-#define RTL8723E_RADIOB_1TARRAYLENGTH          1
-extern u32 RTL8723E_RADIOB_1TARRAY[RTL8723E_RADIOB_1TARRAYLENGTH];
 #define RTL8723E_MACARRAYLENGTH                        172
 extern u32 RTL8723EMAC_ARRAY[RTL8723E_MACARRAYLENGTH];
 #define RTL8723E_AGCTAB_1TARRAYLENGTH          320
index 176deb2..a75451c 100644 (file)
@@ -394,6 +394,7 @@ static void _rtl8812ae_phy_set_rfe_reg_24g(struct ieee80211_hw *hw)
                        rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
                        break;
                }
+               /* fall through */
        case 0:
        case 2:
        default:
index d7960dd..0f2b7c6 100644 (file)
@@ -29,7 +29,6 @@
 #include "../stats.h"
 #include "reg.h"
 #include "def.h"
-#include "phy.h"
 #include "trx.h"
 #include "led.h"
 #include "dm.h"
@@ -307,14 +306,12 @@ static void translate_rx_signal_stuff(struct ieee80211_hw *hw,
        u8 *praddr;
        u8 *psaddr;
        __le16 fc;
-       u16 type;
        bool packet_matchbssid, packet_toself, packet_beacon;
 
        tmp_buf = skb->data + pstatus->rx_drvinfo_size + pstatus->rx_bufshift;
 
        hdr = (struct ieee80211_hdr *)tmp_buf;
        fc = hdr->frame_control;
-       type = WLAN_FC_GET_TYPE(hdr->frame_control);
        praddr = hdr->addr1;
        psaddr = ieee80211_get_SA(hdr);
        ether_addr_copy(pstatus->psaddr, psaddr);
index 612c211..449f6d2 100644 (file)
@@ -210,7 +210,7 @@ int rsi_init_sdio_slave_regs(struct rsi_hw *adapter)
        }
 
        /* This tells SDIO FIFO when to start read to host */
-       rsi_dbg(INIT_ZONE, "%s: Initialzing SDIO read start level\n", __func__);
+       rsi_dbg(INIT_ZONE, "%s: Initializing SDIO read start level\n", __func__);
        byte = 0x24;
 
        status = rsi_sdio_write_register(adapter,
@@ -223,7 +223,7 @@ int rsi_init_sdio_slave_regs(struct rsi_hw *adapter)
                return -1;
        }
 
-       rsi_dbg(INIT_ZONE, "%s: Initialzing FIFO ctrl registers\n", __func__);
+       rsi_dbg(INIT_ZONE, "%s: Initializing FIFO ctrl registers\n", __func__);
        byte = (128 - 32);
 
        status = rsi_sdio_write_register(adapter,
index 295cb1a..2231ba0 100644 (file)
@@ -289,19 +289,7 @@ static int cw1200_status_show(struct seq_file *seq, void *v)
        return 0;
 }
 
-static int cw1200_status_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, &cw1200_status_show,
-               inode->i_private);
-}
-
-static const struct file_operations fops_status = {
-       .open = cw1200_status_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-       .owner = THIS_MODULE,
-};
+DEFINE_SHOW_ATTRIBUTE(cw1200_status);
 
 static int cw1200_counters_show(struct seq_file *seq, void *v)
 {
@@ -345,19 +333,7 @@ static int cw1200_counters_show(struct seq_file *seq, void *v)
        return 0;
 }
 
-static int cw1200_counters_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, &cw1200_counters_show,
-               inode->i_private);
-}
-
-static const struct file_operations fops_counters = {
-       .open = cw1200_counters_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-       .owner = THIS_MODULE,
-};
+DEFINE_SHOW_ATTRIBUTE(cw1200_counters);
 
 static ssize_t cw1200_wsm_dumps(struct file *file,
        const char __user *user_buf, size_t count, loff_t *ppos)
@@ -399,11 +375,11 @@ int cw1200_debug_init(struct cw1200_common *priv)
                goto err;
 
        if (!debugfs_create_file("status", 0400, d->debugfs_phy,
-                                priv, &fops_status))
+                                priv, &cw1200_status_fops))
                goto err;
 
        if (!debugfs_create_file("counters", 0400, d->debugfs_phy,
-                                priv, &fops_counters))
+                                priv, &cw1200_counters_fops))
                goto err;
 
        if (!debugfs_create_file("wsm_dumps", 0200, d->debugfs_phy,
index 67213f1..0a9eac9 100644 (file)
@@ -78,6 +78,10 @@ int cw1200_hw_scan(struct ieee80211_hw *hw,
        if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
                return -EINVAL;
 
+       /* will be unlocked in cw1200_scan_work() */
+       down(&priv->scan.lock);
+       mutex_lock(&priv->conf_mutex);
+
        frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
                req->ie_len);
        if (!frame.skb)
@@ -86,19 +90,15 @@ int cw1200_hw_scan(struct ieee80211_hw *hw,
        if (req->ie_len)
                skb_put_data(frame.skb, req->ie, req->ie_len);
 
-       /* will be unlocked in cw1200_scan_work() */
-       down(&priv->scan.lock);
-       mutex_lock(&priv->conf_mutex);
-
        ret = wsm_set_template_frame(priv, &frame);
        if (!ret) {
                /* Host want to be the probe responder. */
                ret = wsm_set_probe_responder(priv, true);
        }
        if (ret) {
+               dev_kfree_skb(frame.skb);
                mutex_unlock(&priv->conf_mutex);
                up(&priv->scan.lock);
-               dev_kfree_skb(frame.skb);
                return ret;
        }
 
@@ -120,10 +120,9 @@ int cw1200_hw_scan(struct ieee80211_hw *hw,
                ++priv->scan.n_ssids;
        }
 
-       mutex_unlock(&priv->conf_mutex);
-
        if (frame.skb)
                dev_kfree_skb(frame.skb);
+       mutex_unlock(&priv->conf_mutex);
        queue_work(priv->workqueue, &priv->scan.work);
        return 0;
 }
index 38678e9..8dae92a 100644 (file)
@@ -1123,7 +1123,7 @@ int cw1200_setup_mac(struct cw1200_common *priv)
         *
         * NOTE2: RSSI based reports have been switched to RCPI, since
         * FW has a bug and RSSI reported values are not stable,
-        * what can leads to signal level oscilations in user-end applications
+        * what can lead to signal level oscilations in user-end applications
         */
        struct wsm_rcpi_rssi_threshold threshold = {
                .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
index 4c2154b..bd10165 100644 (file)
@@ -285,7 +285,7 @@ static int wl1271_probe(struct sdio_func *func,
        struct resource res[2];
        mmc_pm_flag_t mmcflags;
        int ret = -ENOMEM;
-       int irq, wakeirq;
+       int irq, wakeirq, num_irqs;
        const char *chip_family;
 
        /* We are only able to handle the wlan function */
@@ -353,12 +353,17 @@ static int wl1271_probe(struct sdio_func *func,
                       irqd_get_trigger_type(irq_get_irq_data(irq));
        res[0].name = "irq";
 
-       res[1].start = wakeirq;
-       res[1].flags = IORESOURCE_IRQ |
-                      irqd_get_trigger_type(irq_get_irq_data(wakeirq));
-       res[1].name = "wakeirq";
 
-       ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res));
+       if (wakeirq > 0) {
+               res[1].start = wakeirq;
+               res[1].flags = IORESOURCE_IRQ |
+                              irqd_get_trigger_type(irq_get_irq_data(wakeirq));
+               res[1].name = "wakeirq";
+               num_irqs = 2;
+       } else {
+               num_irqs = 1;
+       }
+       ret = platform_device_add_resources(glue->core, res, num_irqs);
        if (ret) {
                dev_err(glue->dev, "can't add resources\n");
                goto out_dev_put;
index dbe78d8..7f34ec0 100644 (file)
@@ -70,7 +70,7 @@ wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
 out:
        mutex_unlock(&wl->mutex);
 
-       return 0;
+       return ret;
 }
 
 static int
diff --git a/drivers/net/wireless/virt_wifi.c b/drivers/net/wireless/virt_wifi.c
new file mode 100644 (file)
index 0000000..64b2186
--- /dev/null
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0
+/* drivers/net/wireless/virt_wifi.c
+ *
+ * A fake implementation of cfg80211_ops that can be tacked on to an ethernet
+ * net_device to make it appear as a wireless connection.
+ *
+ * Copyright (C) 2018 Google, Inc.
+ *
+ * Author: schuffelen@google.com
+ */
+
+#include <net/cfg80211.h>
+#include <net/rtnetlink.h>
+#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 {
+       struct delayed_work scan_result;
+       struct cfg80211_scan_request *scan_request;
+       bool being_deleted;
+};
+
+static struct ieee80211_channel channel_2ghz = {
+       .band = NL80211_BAND_2GHZ,
+       .center_freq = 2432,
+       .hw_value = 2432,
+       .max_power = 20,
+};
+
+static struct ieee80211_rate bitrates_2ghz[] = {
+       { .bitrate = 10 },
+       { .bitrate = 20 },
+       { .bitrate = 55 },
+       { .bitrate = 110 },
+       { .bitrate = 60 },
+       { .bitrate = 120 },
+       { .bitrate = 240 },
+};
+
+static struct ieee80211_supported_band band_2ghz = {
+       .channels = &channel_2ghz,
+       .bitrates = bitrates_2ghz,
+       .band = NL80211_BAND_2GHZ,
+       .n_channels = 1,
+       .n_bitrates = ARRAY_SIZE(bitrates_2ghz),
+       .ht_cap = {
+               .ht_supported = true,
+               .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+                      IEEE80211_HT_CAP_GRN_FLD |
+                      IEEE80211_HT_CAP_SGI_20 |
+                      IEEE80211_HT_CAP_SGI_40 |
+                      IEEE80211_HT_CAP_DSSSCCK40,
+               .ampdu_factor = 0x3,
+               .ampdu_density = 0x6,
+               .mcs = {
+                       .rx_mask = {0xff, 0xff},
+                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               },
+       },
+};
+
+static struct ieee80211_channel channel_5ghz = {
+       .band = NL80211_BAND_5GHZ,
+       .center_freq = 5240,
+       .hw_value = 5240,
+       .max_power = 20,
+};
+
+static struct ieee80211_rate bitrates_5ghz[] = {
+       { .bitrate = 60 },
+       { .bitrate = 120 },
+       { .bitrate = 240 },
+};
+
+#define RX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 14)
+
+#define TX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \
+                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 14)
+
+static struct ieee80211_supported_band band_5ghz = {
+       .channels = &channel_5ghz,
+       .bitrates = bitrates_5ghz,
+       .band = NL80211_BAND_5GHZ,
+       .n_channels = 1,
+       .n_bitrates = ARRAY_SIZE(bitrates_5ghz),
+       .ht_cap = {
+               .ht_supported = true,
+               .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+                      IEEE80211_HT_CAP_GRN_FLD |
+                      IEEE80211_HT_CAP_SGI_20 |
+                      IEEE80211_HT_CAP_SGI_40 |
+                      IEEE80211_HT_CAP_DSSSCCK40,
+               .ampdu_factor = 0x3,
+               .ampdu_density = 0x6,
+               .mcs = {
+                       .rx_mask = {0xff, 0xff},
+                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               },
+       },
+       .vht_cap = {
+               .vht_supported = true,
+               .cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+                      IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
+                      IEEE80211_VHT_CAP_RXLDPC |
+                      IEEE80211_VHT_CAP_SHORT_GI_80 |
+                      IEEE80211_VHT_CAP_SHORT_GI_160 |
+                      IEEE80211_VHT_CAP_TXSTBC |
+                      IEEE80211_VHT_CAP_RXSTBC_1 |
+                      IEEE80211_VHT_CAP_RXSTBC_2 |
+                      IEEE80211_VHT_CAP_RXSTBC_3 |
+                      IEEE80211_VHT_CAP_RXSTBC_4 |
+                      IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+               .vht_mcs = {
+                       .rx_mcs_map = cpu_to_le16(RX_MCS_MAP),
+                       .tx_mcs_map = cpu_to_le16(TX_MCS_MAP),
+               }
+       },
+};
+
+/* Assigned at module init. Guaranteed locally-administered and unicast. */
+static u8 fake_router_bssid[ETH_ALEN] __ro_after_init = {};
+
+/* Called with the rtnl lock held. */
+static int virt_wifi_scan(struct wiphy *wiphy,
+                         struct cfg80211_scan_request *request)
+{
+       struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy);
+
+       wiphy_debug(wiphy, "scan\n");
+
+       if (priv->scan_request || priv->being_deleted)
+               return -EBUSY;
+
+       priv->scan_request = request;
+       schedule_delayed_work(&priv->scan_result, HZ * 2);
+
+       return 0;
+}
+
+/* Acquires and releases the rdev BSS lock. */
+static void virt_wifi_scan_result(struct work_struct *work)
+{
+       struct {
+               u8 tag;
+               u8 len;
+               u8 ssid[8];
+       } __packed ssid = {
+               .tag = WLAN_EID_SSID, .len = 8, .ssid = "VirtWifi",
+       };
+       struct cfg80211_bss *informed_bss;
+       struct virt_wifi_wiphy_priv *priv =
+               container_of(work, struct virt_wifi_wiphy_priv,
+                            scan_result.work);
+       struct wiphy *wiphy = priv_to_wiphy(priv);
+       struct cfg80211_scan_info scan_info = { .aborted = false };
+
+       informed_bss = cfg80211_inform_bss(wiphy, &channel_5ghz,
+                                          CFG80211_BSS_FTYPE_PRESP,
+                                          fake_router_bssid,
+                                          ktime_get_boot_ns(),
+                                          WLAN_CAPABILITY_ESS, 0,
+                                          (void *)&ssid, sizeof(ssid),
+                                          DBM_TO_MBM(-50), GFP_KERNEL);
+       cfg80211_put_bss(wiphy, informed_bss);
+
+       /* Schedules work which acquires and releases the rtnl lock. */
+       cfg80211_scan_done(priv->scan_request, &scan_info);
+       priv->scan_request = NULL;
+}
+
+/* May acquire and release the rdev BSS lock. */
+static void virt_wifi_cancel_scan(struct wiphy *wiphy)
+{
+       struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy);
+
+       cancel_delayed_work_sync(&priv->scan_result);
+       /* Clean up dangling callbacks if necessary. */
+       if (priv->scan_request) {
+               struct cfg80211_scan_info scan_info = { .aborted = true };
+               /* Schedules work which acquires and releases the rtnl lock. */
+               cfg80211_scan_done(priv->scan_request, &scan_info);
+               priv->scan_request = NULL;
+       }
+}
+
+struct virt_wifi_netdev_priv {
+       struct delayed_work connect;
+       struct net_device *lowerdev;
+       struct net_device *upperdev;
+       u32 tx_packets;
+       u32 tx_failed;
+       u8 connect_requested_bss[ETH_ALEN];
+       bool is_up;
+       bool is_connected;
+       bool being_deleted;
+};
+
+/* Called with the rtnl lock held. */
+static int virt_wifi_connect(struct wiphy *wiphy, struct net_device *netdev,
+                            struct cfg80211_connect_params *sme)
+{
+       struct virt_wifi_netdev_priv *priv = netdev_priv(netdev);
+       bool could_schedule;
+
+       if (priv->being_deleted || !priv->is_up)
+               return -EBUSY;
+
+       could_schedule = schedule_delayed_work(&priv->connect, HZ * 2);
+       if (!could_schedule)
+               return -EBUSY;
+
+       if (sme->bssid)
+               ether_addr_copy(priv->connect_requested_bss, sme->bssid);
+       else
+               eth_zero_addr(priv->connect_requested_bss);
+
+       wiphy_debug(wiphy, "connect\n");
+
+       return 0;
+}
+
+/* Acquires and releases the rdev event lock. */
+static void virt_wifi_connect_complete(struct work_struct *work)
+{
+       struct virt_wifi_netdev_priv *priv =
+               container_of(work, struct virt_wifi_netdev_priv, connect.work);
+       u8 *requested_bss = priv->connect_requested_bss;
+       bool has_addr = !is_zero_ether_addr(requested_bss);
+       bool right_addr = ether_addr_equal(requested_bss, fake_router_bssid);
+       u16 status = WLAN_STATUS_SUCCESS;
+
+       if (!priv->is_up || (has_addr && !right_addr))
+               status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+       else
+               priv->is_connected = true;
+
+       /* Schedules an event that acquires the rtnl lock. */
+       cfg80211_connect_result(priv->upperdev, requested_bss, NULL, 0, NULL, 0,
+                               status, GFP_KERNEL);
+       netif_carrier_on(priv->upperdev);
+}
+
+/* May acquire and release the rdev event lock. */
+static void virt_wifi_cancel_connect(struct net_device *netdev)
+{
+       struct virt_wifi_netdev_priv *priv = netdev_priv(netdev);
+
+       /* If there is work pending, clean up dangling callbacks. */
+       if (cancel_delayed_work_sync(&priv->connect)) {
+               /* Schedules an event that acquires the rtnl lock. */
+               cfg80211_connect_result(priv->upperdev,
+                                       priv->connect_requested_bss, NULL, 0,
+                                       NULL, 0,
+                                       WLAN_STATUS_UNSPECIFIED_FAILURE,
+                                       GFP_KERNEL);
+       }
+}
+
+/* Called with the rtnl lock held. Acquires the rdev event lock. */
+static int virt_wifi_disconnect(struct wiphy *wiphy, struct net_device *netdev,
+                               u16 reason_code)
+{
+       struct virt_wifi_netdev_priv *priv = netdev_priv(netdev);
+
+       if (priv->being_deleted)
+               return -EBUSY;
+
+       wiphy_debug(wiphy, "disconnect\n");
+       virt_wifi_cancel_connect(netdev);
+
+       cfg80211_disconnected(netdev, reason_code, NULL, 0, true, GFP_KERNEL);
+       priv->is_connected = false;
+       netif_carrier_off(netdev);
+
+       return 0;
+}
+
+/* Called with the rtnl lock held. */
+static int virt_wifi_get_station(struct wiphy *wiphy, struct net_device *dev,
+                                const u8 *mac, struct station_info *sinfo)
+{
+       struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+
+       wiphy_debug(wiphy, "get_station\n");
+
+       if (!priv->is_connected || !ether_addr_equal(mac, fake_router_bssid))
+               return -ENOENT;
+
+       sinfo->filled = BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
+               BIT_ULL(NL80211_STA_INFO_TX_FAILED) |
+               BIT_ULL(NL80211_STA_INFO_SIGNAL) |
+               BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+       sinfo->tx_packets = priv->tx_packets;
+       sinfo->tx_failed = priv->tx_failed;
+       /* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_ */
+       sinfo->signal = -50;
+       sinfo->txrate = (struct rate_info) {
+               .legacy = 10, /* units are 100kbit/s */
+       };
+       return 0;
+}
+
+/* Called with the rtnl lock held. */
+static int virt_wifi_dump_station(struct wiphy *wiphy, struct net_device *dev,
+                                 int idx, u8 *mac, struct station_info *sinfo)
+{
+       struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+
+       wiphy_debug(wiphy, "dump_station\n");
+
+       if (idx != 0 || !priv->is_connected)
+               return -ENOENT;
+
+       ether_addr_copy(mac, fake_router_bssid);
+       return virt_wifi_get_station(wiphy, dev, fake_router_bssid, sinfo);
+}
+
+static const struct cfg80211_ops virt_wifi_cfg80211_ops = {
+       .scan = virt_wifi_scan,
+
+       .connect = virt_wifi_connect,
+       .disconnect = virt_wifi_disconnect,
+
+       .get_station = virt_wifi_get_station,
+       .dump_station = virt_wifi_dump_station,
+};
+
+/* Acquires and releases the rtnl lock. */
+static struct wiphy *virt_wifi_make_wiphy(void)
+{
+       struct wiphy *wiphy;
+       struct virt_wifi_wiphy_priv *priv;
+       int err;
+
+       wiphy = wiphy_new(&virt_wifi_cfg80211_ops, sizeof(*priv));
+
+       if (!wiphy)
+               return NULL;
+
+       wiphy->max_scan_ssids = 4;
+       wiphy->max_scan_ie_len = 1000;
+       wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+       wiphy->bands[NL80211_BAND_2GHZ] = &band_2ghz;
+       wiphy->bands[NL80211_BAND_5GHZ] = &band_5ghz;
+       wiphy->bands[NL80211_BAND_60GHZ] = NULL;
+
+       wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
+       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
+       priv = wiphy_priv(wiphy);
+       priv->being_deleted = false;
+       priv->scan_request = NULL;
+       INIT_DELAYED_WORK(&priv->scan_result, virt_wifi_scan_result);
+
+       err = wiphy_register(wiphy);
+       if (err < 0) {
+               wiphy_free(wiphy);
+               return NULL;
+       }
+
+       return wiphy;
+}
+
+/* Acquires and releases the rtnl lock. */
+static void virt_wifi_destroy_wiphy(struct wiphy *wiphy)
+{
+       struct virt_wifi_wiphy_priv *priv;
+
+       WARN(!wiphy, "%s called with null wiphy", __func__);
+       if (!wiphy)
+               return;
+
+       priv = wiphy_priv(wiphy);
+       priv->being_deleted = true;
+       virt_wifi_cancel_scan(wiphy);
+
+       if (wiphy->registered)
+               wiphy_unregister(wiphy);
+       wiphy_free(wiphy);
+}
+
+/* Enters and exits a RCU-bh critical section. */
+static netdev_tx_t virt_wifi_start_xmit(struct sk_buff *skb,
+                                       struct net_device *dev)
+{
+       struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+
+       priv->tx_packets++;
+       if (!priv->is_connected) {
+               priv->tx_failed++;
+               return NET_XMIT_DROP;
+       }
+
+       skb->dev = priv->lowerdev;
+       return dev_queue_xmit(skb);
+}
+
+/* Called with rtnl lock held. */
+static int virt_wifi_net_device_open(struct net_device *dev)
+{
+       struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+
+       priv->is_up = true;
+       return 0;
+}
+
+/* Called with rtnl lock held. */
+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);
+       netif_carrier_off(dev);
+
+       return 0;
+}
+
+static const struct net_device_ops virt_wifi_ops = {
+       .ndo_start_xmit = virt_wifi_start_xmit,
+       .ndo_open = virt_wifi_net_device_open,
+       .ndo_stop = virt_wifi_net_device_stop,
+};
+
+/* Invoked as part of rtnl lock release. */
+static void virt_wifi_net_device_destructor(struct net_device *dev)
+{
+       /* Delayed past dellink to allow nl80211 to react to the device being
+        * deleted.
+        */
+       kfree(dev->ieee80211_ptr);
+       dev->ieee80211_ptr = NULL;
+       free_netdev(dev);
+}
+
+/* No lock interaction. */
+static void virt_wifi_setup(struct net_device *dev)
+{
+       ether_setup(dev);
+       dev->netdev_ops = &virt_wifi_ops;
+       dev->priv_destructor = virt_wifi_net_device_destructor;
+}
+
+/* Called in a RCU read critical section from netif_receive_skb */
+static rx_handler_result_t virt_wifi_rx_handler(struct sk_buff **pskb)
+{
+       struct sk_buff *skb = *pskb;
+       struct virt_wifi_netdev_priv *priv =
+               rcu_dereference(skb->dev->rx_handler_data);
+
+       if (!priv->is_connected)
+               return RX_HANDLER_PASS;
+
+       /* GFP_ATOMIC because this is a packet interrupt handler. */
+       skb = skb_share_check(skb, GFP_ATOMIC);
+       if (!skb) {
+               dev_err(&priv->upperdev->dev, "can't skb_share_check\n");
+               return RX_HANDLER_CONSUMED;
+       }
+
+       *pskb = skb;
+       skb->dev = priv->upperdev;
+       skb->pkt_type = PACKET_HOST;
+       return RX_HANDLER_ANOTHER;
+}
+
+/* Called with rtnl lock held. */
+static int virt_wifi_newlink(struct net *src_net, struct net_device *dev,
+                            struct nlattr *tb[], struct nlattr *data[],
+                            struct netlink_ext_ack *extack)
+{
+       struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+       int err;
+
+       if (!tb[IFLA_LINK])
+               return -EINVAL;
+
+       netif_carrier_off(dev);
+
+       priv->upperdev = dev;
+       priv->lowerdev = __dev_get_by_index(src_net,
+                                           nla_get_u32(tb[IFLA_LINK]));
+
+       if (!priv->lowerdev)
+               return -ENODEV;
+       if (!tb[IFLA_MTU])
+               dev->mtu = priv->lowerdev->mtu;
+       else if (dev->mtu > priv->lowerdev->mtu)
+               return -EINVAL;
+
+       err = netdev_rx_handler_register(priv->lowerdev, virt_wifi_rx_handler,
+                                        priv);
+       if (err) {
+               dev_err(&priv->lowerdev->dev,
+                       "can't netdev_rx_handler_register: %d\n", err);
+               return err;
+       }
+
+       eth_hw_addr_inherit(dev, priv->lowerdev);
+       netif_stacked_transfer_operstate(priv->lowerdev, dev);
+
+       SET_NETDEV_DEV(dev, &priv->lowerdev->dev);
+       dev->ieee80211_ptr = kzalloc(sizeof(*dev->ieee80211_ptr), GFP_KERNEL);
+
+       if (!dev->ieee80211_ptr)
+               goto remove_handler;
+
+       dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION;
+       dev->ieee80211_ptr->wiphy = common_wiphy;
+
+       err = register_netdevice(dev);
+       if (err) {
+               dev_err(&priv->lowerdev->dev, "can't register_netdevice: %d\n",
+                       err);
+               goto free_wireless_dev;
+       }
+
+       err = netdev_upper_dev_link(priv->lowerdev, dev, extack);
+       if (err) {
+               dev_err(&priv->lowerdev->dev, "can't netdev_upper_dev_link: %d\n",
+                       err);
+               goto unregister_netdev;
+       }
+
+       priv->being_deleted = false;
+       priv->is_connected = false;
+       priv->is_up = false;
+       INIT_DELAYED_WORK(&priv->connect, virt_wifi_connect_complete);
+
+       return 0;
+unregister_netdev:
+       unregister_netdevice(dev);
+free_wireless_dev:
+       kfree(dev->ieee80211_ptr);
+       dev->ieee80211_ptr = NULL;
+remove_handler:
+       netdev_rx_handler_unregister(priv->lowerdev);
+
+       return err;
+}
+
+/* Called with rtnl lock held. */
+static void virt_wifi_dellink(struct net_device *dev,
+                             struct list_head *head)
+{
+       struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+
+       if (dev->ieee80211_ptr)
+               virt_wifi_cancel_scan(dev->ieee80211_ptr->wiphy);
+
+       priv->being_deleted = true;
+       virt_wifi_cancel_connect(dev);
+       netif_carrier_off(dev);
+
+       netdev_rx_handler_unregister(priv->lowerdev);
+       netdev_upper_dev_unlink(priv->lowerdev, dev);
+
+       unregister_netdevice_queue(dev, head);
+
+       /* Deleting the wiphy is handled in the module destructor. */
+}
+
+static struct rtnl_link_ops virt_wifi_link_ops = {
+       .kind           = "virt_wifi",
+       .setup          = virt_wifi_setup,
+       .newlink        = virt_wifi_newlink,
+       .dellink        = virt_wifi_dellink,
+       .priv_size      = sizeof(struct virt_wifi_netdev_priv),
+};
+
+/* Acquires and releases the rtnl lock. */
+static int __init virt_wifi_init_module(void)
+{
+       int err;
+
+       /* Guaranteed to be locallly-administered and not multicast. */
+       eth_random_addr(fake_router_bssid);
+
+       common_wiphy = virt_wifi_make_wiphy();
+       if (!common_wiphy)
+               return -ENOMEM;
+
+       err = rtnl_link_register(&virt_wifi_link_ops);
+       if (err)
+               virt_wifi_destroy_wiphy(common_wiphy);
+
+       return err;
+}
+
+/* Acquires and releases the rtnl lock. */
+static void __exit virt_wifi_cleanup_module(void)
+{
+       /* Will delete any devices that depend on the wiphy. */
+       rtnl_link_unregister(&virt_wifi_link_ops);
+       virt_wifi_destroy_wiphy(common_wiphy);
+}
+
+module_init(virt_wifi_init_module);
+module_exit(virt_wifi_cleanup_module);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Cody Schuffelen <schuffelen@google.com>");
+MODULE_DESCRIPTION("Driver for a wireless wrapper of ethernet devices");
+MODULE_ALIAS_RTNL_LINK("virt_wifi");
index 2534038..22c70f1 100644 (file)
@@ -969,6 +969,7 @@ static int zd1201_set_mode(struct net_device *dev,
                         */
                        zd1201_join(zd, "\0-*#\0", 5);
                        /* Put port in pIBSS */
+                       /* Fall through */
                case 8: /* No pseudo-IBSS in wireless extensions (yet) */
                        porttype = ZD1201_PORTTYPE_PSEUDOIBSS;
                        break;
index fe1d522..2625740 100644 (file)
@@ -186,7 +186,7 @@ static const struct file_operations xenvif_dbg_io_ring_ops_fops = {
        .write = xenvif_write_io_ring,
 };
 
-static int xenvif_read_ctrl(struct seq_file *m, void *v)
+static int xenvif_ctrl_show(struct seq_file *m, void *v)
 {
        struct xenvif *vif = m->private;
 
@@ -194,19 +194,7 @@ static int xenvif_read_ctrl(struct seq_file *m, void *v)
 
        return 0;
 }
-
-static int xenvif_ctrl_open(struct inode *inode, struct file *filp)
-{
-       return single_open(filp, xenvif_read_ctrl, inode->i_private);
-}
-
-static const struct file_operations xenvif_dbg_ctrl_ops_fops = {
-       .owner = THIS_MODULE,
-       .open = xenvif_ctrl_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(xenvif_ctrl);
 
 static void xenvif_debugfs_addif(struct xenvif *vif)
 {
@@ -238,7 +226,7 @@ static void xenvif_debugfs_addif(struct xenvif *vif)
                                                    0400,
                                                    vif->xenvif_dbg_root,
                                                    vif,
-                                                   &xenvif_dbg_ctrl_ops_fops);
+                                                   &xenvif_ctrl_fops);
                        if (IS_ERR_OR_NULL(pfile))
                                pr_warn("Creation of ctrl file returned %ld!\n",
                                        PTR_ERR(pfile));
index a8303af..c914c24 100644 (file)
@@ -903,7 +903,7 @@ static RING_IDX xennet_fill_frags(struct netfront_queue *queue,
                if (skb_shinfo(skb)->nr_frags == MAX_SKB_FRAGS) {
                        unsigned int pull_to = NETFRONT_SKB_CB(skb)->pull_to;
 
-                       BUG_ON(pull_to <= skb_headlen(skb));
+                       BUG_ON(pull_to < skb_headlen(skb));
                        __pskb_pull_tail(skb, pull_to - skb_headlen(skb));
                }
                if (unlikely(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS)) {
index 182258f..d0c621b 100644 (file)
@@ -111,6 +111,8 @@ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
                struct nd_mapping *nd_mapping, resource_size_t *overlap);
 resource_size_t nd_blk_available_dpa(struct nd_region *nd_region);
 resource_size_t nd_region_available_dpa(struct nd_region *nd_region);
+int nd_region_conflict(struct nd_region *nd_region, resource_size_t start,
+               resource_size_t size);
 resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd,
                struct nd_label_id *label_id);
 int alias_dpa_busy(struct device *dev, void *data);
index 24c6409..6f22272 100644 (file)
@@ -649,14 +649,47 @@ static u64 phys_pmem_align_down(struct nd_pfn *nd_pfn, u64 phys)
                        ALIGN_DOWN(phys, nd_pfn->align));
 }
 
+/*
+ * Check if pmem collides with 'System RAM', or other regions when
+ * section aligned.  Trim it accordingly.
+ */
+static void trim_pfn_device(struct nd_pfn *nd_pfn, u32 *start_pad, u32 *end_trunc)
+{
+       struct nd_namespace_common *ndns = nd_pfn->ndns;
+       struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
+       struct nd_region *nd_region = to_nd_region(nd_pfn->dev.parent);
+       const resource_size_t start = nsio->res.start;
+       const resource_size_t end = start + resource_size(&nsio->res);
+       resource_size_t adjust, size;
+
+       *start_pad = 0;
+       *end_trunc = 0;
+
+       adjust = start - PHYS_SECTION_ALIGN_DOWN(start);
+       size = resource_size(&nsio->res) + adjust;
+       if (region_intersects(start - adjust, size, IORESOURCE_SYSTEM_RAM,
+                               IORES_DESC_NONE) == REGION_MIXED
+                       || nd_region_conflict(nd_region, start - adjust, size))
+               *start_pad = PHYS_SECTION_ALIGN_UP(start) - start;
+
+       /* Now check that end of the range does not collide. */
+       adjust = PHYS_SECTION_ALIGN_UP(end) - end;
+       size = resource_size(&nsio->res) + adjust;
+       if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM,
+                               IORES_DESC_NONE) == REGION_MIXED
+                       || !IS_ALIGNED(end, nd_pfn->align)
+                       || nd_region_conflict(nd_region, start, size + adjust))
+               *end_trunc = end - phys_pmem_align_down(nd_pfn, end);
+}
+
 static int nd_pfn_init(struct nd_pfn *nd_pfn)
 {
        u32 dax_label_reserve = is_nd_dax(&nd_pfn->dev) ? SZ_128K : 0;
        struct nd_namespace_common *ndns = nd_pfn->ndns;
-       u32 start_pad = 0, end_trunc = 0;
+       struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
        resource_size_t start, size;
-       struct nd_namespace_io *nsio;
        struct nd_region *nd_region;
+       u32 start_pad, end_trunc;
        struct nd_pfn_sb *pfn_sb;
        unsigned long npfns;
        phys_addr_t offset;
@@ -688,30 +721,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
 
        memset(pfn_sb, 0, sizeof(*pfn_sb));
 
-       /*
-        * Check if pmem collides with 'System RAM' when section aligned and
-        * trim it accordingly
-        */
-       nsio = to_nd_namespace_io(&ndns->dev);
-       start = PHYS_SECTION_ALIGN_DOWN(nsio->res.start);
-       size = resource_size(&nsio->res);
-       if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM,
-                               IORES_DESC_NONE) == REGION_MIXED) {
-               start = nsio->res.start;
-               start_pad = PHYS_SECTION_ALIGN_UP(start) - start;
-       }
-
-       start = nsio->res.start;
-       size = PHYS_SECTION_ALIGN_UP(start + size) - start;
-       if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM,
-                               IORES_DESC_NONE) == REGION_MIXED
-                       || !IS_ALIGNED(start + resource_size(&nsio->res),
-                               nd_pfn->align)) {
-               size = resource_size(&nsio->res);
-               end_trunc = start + size - phys_pmem_align_down(nd_pfn,
-                               start + size);
-       }
-
+       trim_pfn_device(nd_pfn, &start_pad, &end_trunc);
        if (start_pad + end_trunc)
                dev_info(&nd_pfn->dev, "%s alignment collision, truncate %d bytes\n",
                                dev_name(&ndns->dev), start_pad + end_trunc);
@@ -722,7 +732,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
         * implementation will limit the pfns advertised through
         * ->direct_access() to those that are included in the memmap.
         */
-       start += start_pad;
+       start = nsio->res.start + start_pad;
        size = resource_size(&nsio->res);
        npfns = PFN_SECTION_ALIGN_UP((size - start_pad - end_trunc - SZ_8K)
                        / PAGE_SIZE);
index 174a418..e7377f1 100644 (file)
@@ -1184,6 +1184,47 @@ int nvdimm_has_cache(struct nd_region *nd_region)
 }
 EXPORT_SYMBOL_GPL(nvdimm_has_cache);
 
+struct conflict_context {
+       struct nd_region *nd_region;
+       resource_size_t start, size;
+};
+
+static int region_conflict(struct device *dev, void *data)
+{
+       struct nd_region *nd_region;
+       struct conflict_context *ctx = data;
+       resource_size_t res_end, region_end, region_start;
+
+       if (!is_memory(dev))
+               return 0;
+
+       nd_region = to_nd_region(dev);
+       if (nd_region == ctx->nd_region)
+               return 0;
+
+       res_end = ctx->start + ctx->size;
+       region_start = nd_region->ndr_start;
+       region_end = region_start + nd_region->ndr_size;
+       if (ctx->start >= region_start && ctx->start < region_end)
+               return -EBUSY;
+       if (res_end > region_start && res_end <= region_end)
+               return -EBUSY;
+       return 0;
+}
+
+int nd_region_conflict(struct nd_region *nd_region, resource_size_t start,
+               resource_size_t size)
+{
+       struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev);
+       struct conflict_context ctx = {
+               .nd_region = nd_region,
+               .start = start,
+               .size = size,
+       };
+
+       return device_for_each_child(&nvdimm_bus->dev, &ctx, region_conflict);
+}
+
 void __exit nd_region_devs_exit(void)
 {
        ida_destroy(&region_ida);
index 559d567..9620121 100644 (file)
@@ -831,6 +831,8 @@ static int nvme_submit_user_cmd(struct request_queue *q,
 static void nvme_keep_alive_end_io(struct request *rq, blk_status_t status)
 {
        struct nvme_ctrl *ctrl = rq->end_io_data;
+       unsigned long flags;
+       bool startka = false;
 
        blk_mq_free_request(rq);
 
@@ -841,7 +843,13 @@ static void nvme_keep_alive_end_io(struct request *rq, blk_status_t status)
                return;
        }
 
-       schedule_delayed_work(&ctrl->ka_work, ctrl->kato * HZ);
+       spin_lock_irqsave(&ctrl->lock, flags);
+       if (ctrl->state == NVME_CTRL_LIVE ||
+           ctrl->state == NVME_CTRL_CONNECTING)
+               startka = true;
+       spin_unlock_irqrestore(&ctrl->lock, flags);
+       if (startka)
+               schedule_delayed_work(&ctrl->ka_work, ctrl->kato * HZ);
 }
 
 static int nvme_keep_alive(struct nvme_ctrl *ctrl)
@@ -3314,6 +3322,9 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
        struct nvme_ns *ns, *next;
        LIST_HEAD(ns_list);
 
+       /* prevent racing with ns scanning */
+       flush_work(&ctrl->scan_work);
+
        /*
         * The dead states indicates the controller was not gracefully
         * disconnected. In that case, we won't be able to flush any data while
@@ -3476,7 +3487,6 @@ void nvme_stop_ctrl(struct nvme_ctrl *ctrl)
        nvme_mpath_stop(ctrl);
        nvme_stop_keep_alive(ctrl);
        flush_work(&ctrl->async_event_work);
-       flush_work(&ctrl->scan_work);
        cancel_work_sync(&ctrl->fw_act_work);
        if (ctrl->ops->stop_ctrl)
                ctrl->ops->stop_ctrl(ctrl);
@@ -3585,7 +3595,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
 
        return 0;
 out_free_name:
-       kfree_const(dev->kobj.name);
+       kfree_const(ctrl->device->kobj.name);
 out_release_instance:
        ida_simple_remove(&nvme_instance_ida, ctrl->instance);
 out:
@@ -3607,7 +3617,7 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
        down_read(&ctrl->namespaces_rwsem);
 
        /* Forcibly unquiesce queues to avoid blocking dispatch */
-       if (ctrl->admin_q)
+       if (ctrl->admin_q && !blk_queue_dying(ctrl->admin_q))
                blk_mq_unquiesce_queue(ctrl->admin_q);
 
        list_for_each_entry(ns, &ctrl->namespaces, list)
index 0b70c8b..feb86b5 100644 (file)
@@ -152,6 +152,7 @@ struct nvme_fc_ctrl {
 
        bool                    ioq_live;
        bool                    assoc_active;
+       atomic_t                err_work_active;
        u64                     association_id;
 
        struct list_head        ctrl_list;      /* rport->ctrl_list */
@@ -160,6 +161,7 @@ struct nvme_fc_ctrl {
        struct blk_mq_tag_set   tag_set;
 
        struct delayed_work     connect_work;
+       struct work_struct      err_work;
 
        struct kref             ref;
        u32                     flags;
@@ -1531,6 +1533,10 @@ nvme_fc_abort_aen_ops(struct nvme_fc_ctrl *ctrl)
        struct nvme_fc_fcp_op *aen_op = ctrl->aen_ops;
        int i;
 
+       /* ensure we've initialized the ops once */
+       if (!(aen_op->flags & FCOP_FLAGS_AEN))
+               return;
+
        for (i = 0; i < NVME_NR_AEN_COMMANDS; i++, aen_op++)
                __nvme_fc_abort_op(ctrl, aen_op);
 }
@@ -1746,12 +1752,12 @@ nvme_fc_init_request(struct blk_mq_tag_set *set, struct request *rq,
        struct nvme_fc_queue *queue = &ctrl->queues[queue_idx];
        int res;
 
-       nvme_req(rq)->ctrl = &ctrl->ctrl;
        res = __nvme_fc_init_request(ctrl, queue, &op->op, rq, queue->rqcnt++);
        if (res)
                return res;
        op->op.fcp_req.first_sgl = &op->sgl[0];
        op->op.fcp_req.private = &op->priv[0];
+       nvme_req(rq)->ctrl = &ctrl->ctrl;
        return res;
 }
 
@@ -2049,7 +2055,25 @@ nvme_fc_nvme_ctrl_freed(struct nvme_ctrl *nctrl)
 static void
 nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg)
 {
-       /* only proceed if in LIVE state - e.g. on first error */
+       int active;
+
+       /*
+        * if an error (io timeout, etc) while (re)connecting,
+        * it's an error on creating the new association.
+        * Start the error recovery thread if it hasn't already
+        * been started. It is expected there could be multiple
+        * ios hitting this path before things are cleaned up.
+        */
+       if (ctrl->ctrl.state == NVME_CTRL_CONNECTING) {
+               active = atomic_xchg(&ctrl->err_work_active, 1);
+               if (!active && !schedule_work(&ctrl->err_work)) {
+                       atomic_set(&ctrl->err_work_active, 0);
+                       WARN_ON(1);
+               }
+               return;
+       }
+
+       /* Otherwise, only proceed if in LIVE state - e.g. on first error */
        if (ctrl->ctrl.state != NVME_CTRL_LIVE)
                return;
 
@@ -2814,6 +2838,7 @@ nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl)
 {
        struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
 
+       cancel_work_sync(&ctrl->err_work);
        cancel_delayed_work_sync(&ctrl->connect_work);
        /*
         * kill the association on the link side.  this will block
@@ -2866,23 +2891,30 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
 }
 
 static void
-nvme_fc_reset_ctrl_work(struct work_struct *work)
+__nvme_fc_terminate_io(struct nvme_fc_ctrl *ctrl)
 {
-       struct nvme_fc_ctrl *ctrl =
-               container_of(work, struct nvme_fc_ctrl, ctrl.reset_work);
-       int ret;
-
-       nvme_stop_ctrl(&ctrl->ctrl);
+       nvme_stop_keep_alive(&ctrl->ctrl);
 
        /* will block will waiting for io to terminate */
        nvme_fc_delete_association(ctrl);
 
-       if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) {
+       if (ctrl->ctrl.state != NVME_CTRL_CONNECTING &&
+           !nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING))
                dev_err(ctrl->ctrl.device,
                        "NVME-FC{%d}: error_recovery: Couldn't change state "
                        "to CONNECTING\n", ctrl->cnum);
-               return;
-       }
+}
+
+static void
+nvme_fc_reset_ctrl_work(struct work_struct *work)
+{
+       struct nvme_fc_ctrl *ctrl =
+               container_of(work, struct nvme_fc_ctrl, ctrl.reset_work);
+       int ret;
+
+       __nvme_fc_terminate_io(ctrl);
+
+       nvme_stop_ctrl(&ctrl->ctrl);
 
        if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE)
                ret = nvme_fc_create_association(ctrl);
@@ -2897,6 +2929,24 @@ nvme_fc_reset_ctrl_work(struct work_struct *work)
                        ctrl->cnum);
 }
 
+static void
+nvme_fc_connect_err_work(struct work_struct *work)
+{
+       struct nvme_fc_ctrl *ctrl =
+                       container_of(work, struct nvme_fc_ctrl, err_work);
+
+       __nvme_fc_terminate_io(ctrl);
+
+       atomic_set(&ctrl->err_work_active, 0);
+
+       /*
+        * Rescheduling the connection after recovering
+        * from the io error is left to the reconnect work
+        * item, which is what should have stalled waiting on
+        * the io that had the error that scheduled this work.
+        */
+}
+
 static const struct nvme_ctrl_ops nvme_fc_ctrl_ops = {
        .name                   = "fc",
        .module                 = THIS_MODULE,
@@ -3007,6 +3057,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
        ctrl->cnum = idx;
        ctrl->ioq_live = false;
        ctrl->assoc_active = false;
+       atomic_set(&ctrl->err_work_active, 0);
        init_waitqueue_head(&ctrl->ioabort_wait);
 
        get_device(ctrl->dev);
@@ -3014,6 +3065,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
 
        INIT_WORK(&ctrl->ctrl.reset_work, nvme_fc_reset_ctrl_work);
        INIT_DELAYED_WORK(&ctrl->connect_work, nvme_fc_connect_ctrl_work);
+       INIT_WORK(&ctrl->err_work, nvme_fc_connect_err_work);
        spin_lock_init(&ctrl->lock);
 
        /* io queue count */
@@ -3103,6 +3155,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
 fail_ctrl:
        nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING);
        cancel_work_sync(&ctrl->ctrl.reset_work);
+       cancel_work_sync(&ctrl->err_work);
        cancel_delayed_work_sync(&ctrl->connect_work);
 
        ctrl->ctrl.opts = NULL;
index cee79cb..081cbdc 100644 (file)
@@ -531,6 +531,9 @@ static inline void nvme_mpath_check_last_path(struct nvme_ns *ns)
 static inline int nvme_mpath_init(struct nvme_ctrl *ctrl,
                struct nvme_id_ctrl *id)
 {
+       if (ctrl->subsys->cmic & (1 << 3))
+               dev_warn(ctrl->device,
+"Please enable CONFIG_NVME_MULTIPATH for full support of multi-port devices.\n");
        return 0;
 }
 static inline void nvme_mpath_uninit(struct nvme_ctrl *ctrl)
index d181caf..ab6ec72 100644 (file)
@@ -184,6 +184,7 @@ static int nvme_rdma_alloc_qe(struct ib_device *ibdev, struct nvme_rdma_qe *qe,
        qe->dma = ib_dma_map_single(ibdev, qe->data, capsule_size, dir);
        if (ib_dma_mapping_error(ibdev, qe->dma)) {
                kfree(qe->data);
+               qe->data = NULL;
                return -ENOMEM;
        }
 
@@ -823,6 +824,7 @@ out_free_tagset:
 out_free_async_qe:
        nvme_rdma_free_qe(ctrl->device->dev, &ctrl->async_event_sqe,
                sizeof(struct nvme_command), DMA_TO_DEVICE);
+       ctrl->async_event_sqe.data = NULL;
 out_free_queue:
        nvme_rdma_free_queue(&ctrl->queues[0]);
        return error;
index 3f7971d..583086d 100644 (file)
@@ -529,6 +529,7 @@ static void nvmet_rdma_send_done(struct ib_cq *cq, struct ib_wc *wc)
 {
        struct nvmet_rdma_rsp *rsp =
                container_of(wc->wr_cqe, struct nvmet_rdma_rsp, send_cqe);
+       struct nvmet_rdma_queue *queue = cq->cq_context;
 
        nvmet_rdma_release_rsp(rsp);
 
@@ -536,7 +537,7 @@ static void nvmet_rdma_send_done(struct ib_cq *cq, struct ib_wc *wc)
                     wc->status != IB_WC_WR_FLUSH_ERR)) {
                pr_err("SEND for CQE 0x%p failed with status %s (%d).\n",
                        wc->wr_cqe, ib_wc_status_msg(wc->status), wc->status);
-               nvmet_rdma_error_comp(rsp->queue);
+               nvmet_rdma_error_comp(queue);
        }
 }
 
index 9b18ce9..27f67df 100644 (file)
@@ -44,6 +44,7 @@ struct nvmem_cell {
        int                     bytes;
        int                     bit_offset;
        int                     nbits;
+       struct device_node      *np;
        struct nvmem_device     *nvmem;
        struct list_head        node;
 };
@@ -298,6 +299,7 @@ static void nvmem_cell_drop(struct nvmem_cell *cell)
        mutex_lock(&nvmem_mutex);
        list_del(&cell->node);
        mutex_unlock(&nvmem_mutex);
+       of_node_put(cell->np);
        kfree(cell->name);
        kfree(cell);
 }
@@ -530,6 +532,7 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
                        return -ENOMEM;
 
                cell->nvmem = nvmem;
+               cell->np = of_node_get(child);
                cell->offset = be32_to_cpup(addr++);
                cell->bytes = be32_to_cpup(addr);
                cell->name = kasprintf(GFP_KERNEL, "%pOFn", child);
@@ -960,14 +963,13 @@ out:
 
 #if IS_ENABLED(CONFIG_OF)
 static struct nvmem_cell *
-nvmem_find_cell_by_index(struct nvmem_device *nvmem, int index)
+nvmem_find_cell_by_node(struct nvmem_device *nvmem, struct device_node *np)
 {
        struct nvmem_cell *cell = NULL;
-       int i = 0;
 
        mutex_lock(&nvmem_mutex);
        list_for_each_entry(cell, &nvmem->cells, node) {
-               if (index == i++)
+               if (np == cell->np)
                        break;
        }
        mutex_unlock(&nvmem_mutex);
@@ -1011,7 +1013,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
        if (IS_ERR(nvmem))
                return ERR_CAST(nvmem);
 
-       cell = nvmem_find_cell_by_index(nvmem, index);
+       cell = nvmem_find_cell_by_node(nvmem, cell_np);
        if (!cell) {
                __nvmem_device_put(nvmem);
                return ERR_PTR(-ENOENT);
index 53189d4..810ab0f 100644 (file)
@@ -81,42 +81,3 @@ const void *of_get_mac_address(struct device_node *np)
        return of_get_mac_addr(np, "address");
 }
 EXPORT_SYMBOL(of_get_mac_address);
-
-/**
- * Obtain the MAC address from an nvmem provider named 'mac-address' through
- * device tree.
- * On success, copies the new address into memory pointed to by addr and
- * returns 0. Returns a negative error code otherwise.
- * @np:                Device tree node containing the nvmem-cells phandle
- * @addr:      Pointer to receive the MAC address using ether_addr_copy()
- */
-int of_get_nvmem_mac_address(struct device_node *np, void *addr)
-{
-       struct nvmem_cell *cell;
-       const void *mac;
-       size_t len;
-       int ret;
-
-       cell = of_nvmem_cell_get(np, "mac-address");
-       if (IS_ERR(cell))
-               return PTR_ERR(cell);
-
-       mac = nvmem_cell_read(cell, &len);
-
-       nvmem_cell_put(cell);
-
-       if (IS_ERR(mac))
-               return PTR_ERR(mac);
-
-       if (len < ETH_ALEN || !is_valid_ether_addr(mac)) {
-               ret = -EINVAL;
-       } else {
-               ether_addr_copy(addr, mac);
-               ret = 0;
-       }
-
-       kfree(mac);
-
-       return ret;
-}
-EXPORT_SYMBOL(of_get_nvmem_mac_address);
index 5a4b479..38a0880 100644 (file)
@@ -579,10 +579,8 @@ int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
                 */
                count = of_count_phandle_with_args(dev->of_node,
                                                   "operating-points-v2", NULL);
-               if (count != 1)
-                       return -ENODEV;
-
-               index = 0;
+               if (count == 1)
+                       index = 0;
        }
 
        opp_table = dev_pm_opp_get_opp_table_indexed(dev, index);
index 9e5a9a3..1c69c40 100644 (file)
@@ -288,7 +288,10 @@ static int ti_opp_supply_set_opp(struct dev_pm_set_opp_data *data)
        int ret;
 
        vdd_uv = _get_optimal_vdd_voltage(dev, &opp_data,
-                                         new_supply_vbb->u_volt);
+                                         new_supply_vdd->u_volt);
+
+       if (new_supply_vdd->u_volt_min < vdd_uv)
+               new_supply_vdd->u_volt_min = vdd_uv;
 
        /* Scaling up? Scale voltage before frequency */
        if (freq > old_freq) {
@@ -414,7 +417,6 @@ static struct platform_driver ti_opp_supply_driver = {
        .probe = ti_opp_supply_probe,
        .driver = {
                   .name = "ti_opp_supply",
-                  .owner = THIS_MODULE,
                   .of_match_table = of_match_ptr(ti_opp_supply_of_match),
                   },
 };
index 2cbef2d..88af6bf 100644 (file)
@@ -81,8 +81,6 @@ struct imx6_pcie {
 #define PCIE_PL_PFLR_FORCE_LINK                        (1 << 15)
 #define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
 #define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
-#define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING        (1 << 29)
-#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP         (1 << 4)
 
 #define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
 #define PCIE_PHY_CTRL_DATA_LOC 0
@@ -711,12 +709,6 @@ static int imx6_pcie_host_init(struct pcie_port *pp)
        return 0;
 }
 
-static int imx6_pcie_link_up(struct dw_pcie *pci)
-{
-       return dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1) &
-                       PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
-}
-
 static const struct dw_pcie_host_ops imx6_pcie_host_ops = {
        .host_init = imx6_pcie_host_init,
 };
@@ -749,7 +741,7 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
 }
 
 static const struct dw_pcie_ops dw_pcie_ops = {
-       .link_up = imx6_pcie_link_up,
+       /* No special ops needed, but pcie-designware still expects this struct */
 };
 
 #ifdef CONFIG_PM_SLEEP
index 3724d3e..7aa9a82 100644 (file)
@@ -88,7 +88,7 @@ static void ls_pcie_disable_outbound_atus(struct ls_pcie *pcie)
        int i;
 
        for (i = 0; i < PCIE_IATU_NUM; i++)
-               dw_pcie_disable_atu(pcie->pci, DW_PCIE_REGION_OUTBOUND, i);
+               dw_pcie_disable_atu(pcie->pci, i, DW_PCIE_REGION_OUTBOUND);
 }
 
 static int ls1021_pcie_link_up(struct dw_pcie *pci)
index 1e7b022..de8635a 100644 (file)
@@ -440,7 +440,6 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
        tbl_offset = dw_pcie_readl_dbi(pci, reg);
        bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
        tbl_offset &= PCI_MSIX_TABLE_OFFSET;
-       tbl_offset >>= 3;
 
        reg = PCI_BASE_ADDRESS_0 + (4 * bir);
        bar_addr_upper = 0;
index 2a4aa64..921db6f 100644 (file)
@@ -793,15 +793,10 @@ static void pci_acpi_setup(struct device *dev)
 {
        struct pci_dev *pci_dev = to_pci_dev(dev);
        struct acpi_device *adev = ACPI_COMPANION(dev);
-       int node;
 
        if (!adev)
                return;
 
-       node = acpi_get_node(adev->handle);
-       if (node != NUMA_NO_NODE)
-               set_dev_node(dev, node);
-
        pci_acpi_optimize_delay(pci_dev, adev->handle);
 
        pci_acpi_add_pm_notifier(adev, pci_dev);
index d068f11..c9d8e3c 100644 (file)
@@ -5556,9 +5556,13 @@ enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev)
        u32 lnkcap2, lnkcap;
 
        /*
-        * PCIe r4.0 sec 7.5.3.18 recommends using the Supported Link
-        * Speeds Vector in Link Capabilities 2 when supported, falling
-        * back to Max Link Speed in Link Capabilities otherwise.
+        * Link Capabilities 2 was added in PCIe r3.0, sec 7.8.18.  The
+        * implementation note there recommends using the Supported Link
+        * Speeds Vector in Link Capabilities 2 when supported.
+        *
+        * Without Link Capabilities 2, i.e., prior to PCIe r3.0, software
+        * should use the Supported Link Speeds field in Link Capabilities,
+        * where only 2.5 GT/s and 5.0 GT/s speeds were defined.
         */
        pcie_capability_read_dword(dev, PCI_EXP_LNKCAP2, &lnkcap2);
        if (lnkcap2) { /* PCIe r3.0-compliant */
@@ -5574,16 +5578,10 @@ enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev)
        }
 
        pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
-       if (lnkcap) {
-               if (lnkcap & PCI_EXP_LNKCAP_SLS_16_0GB)
-                       return PCIE_SPEED_16_0GT;
-               else if (lnkcap & PCI_EXP_LNKCAP_SLS_8_0GB)
-                       return PCIE_SPEED_8_0GT;
-               else if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB)
-                       return PCIE_SPEED_5_0GT;
-               else if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB)
-                       return PCIE_SPEED_2_5GT;
-       }
+       if ((lnkcap & PCI_EXP_LNKCAP_SLS) == PCI_EXP_LNKCAP_SLS_5_0GB)
+               return PCIE_SPEED_5_0GT;
+       else if ((lnkcap & PCI_EXP_LNKCAP_SLS) == PCI_EXP_LNKCAP_SLS_2_5GB)
+               return PCIE_SPEED_2_5GT;
 
        return PCI_SPEED_UNKNOWN;
 }
index a90a919..fed29de 100644 (file)
@@ -1064,7 +1064,7 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
                .regs           = aer_regs,
        };
 
-       if (kfifo_in_spinlocked(&aer_recover_ring, &entry, sizeof(entry),
+       if (kfifo_in_spinlocked(&aer_recover_ring, &entry, 1,
                                 &aer_recover_ring_lock))
                schedule_work(&aer_recover_work);
        else
index dcb29cb..f78860c 100644 (file)
@@ -895,7 +895,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
        struct pcie_link_state *link;
        int blacklist = !!pcie_aspm_sanity_check(pdev);
 
-       if (!aspm_support_enabled || aspm_disabled)
+       if (!aspm_support_enabled)
                return;
 
        if (pdev->link_state)
index 9ce5311..6d4b44b 100644 (file)
@@ -231,6 +231,7 @@ static const struct qusb2_phy_cfg sdm845_phy_cfg = {
        .mask_core_ready = CORE_READY_STATUS,
        .has_pll_override = true,
        .autoresume_en    = BIT(0),
+       .update_tune1_with_efuse = true,
 };
 
 static const char * const qusb2_phy_vreg_names[] = {
@@ -402,10 +403,10 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
 
        /*
         * Read efuse register having TUNE2/1 parameter's high nibble.
-        * If efuse register shows value as 0x0, or if we fail to find
-        * a valid efuse register settings, then use default value
-        * as 0xB for high nibble that we have already set while
-        * configuring phy.
+        * If efuse register shows value as 0x0 (indicating value is not
+        * fused), or if we fail to find a valid efuse register setting,
+        * then use default value for high nibble that we have already
+        * set while configuring the phy.
         */
        val = nvmem_cell_read(qphy->cell, NULL);
        if (IS_ERR(val) || !val[0]) {
@@ -415,12 +416,13 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
 
        /* Fused TUNE1/2 value is the higher nibble only */
        if (cfg->update_tune1_with_efuse)
-               qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1],
-                             val[0] << 0x4);
+               qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1],
+                                val[0] << HSTX_TRIM_SHIFT,
+                                HSTX_TRIM_MASK);
        else
-               qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2],
-                             val[0] << 0x4);
-
+               qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2],
+                                val[0] << HSTX_TRIM_SHIFT,
+                                HSTX_TRIM_MASK);
 }
 
 static int qusb2_phy_set_mode(struct phy *phy, enum phy_mode mode)
index 467e814..9c85231 100644 (file)
@@ -26,7 +26,8 @@ config PHY_UNIPHIER_USB3
 
 config PHY_UNIPHIER_PCIE
        tristate "Uniphier PHY driver for PCIe controller"
-       depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
+       depends on ARCH_UNIPHIER || COMPILE_TEST
+       depends on OF && HAS_IOMEM
        default PCIE_UNIPHIER
        select GENERIC_PHY
        help
index 4ceb06f..4edeb4c 100644 (file)
@@ -830,7 +830,7 @@ static struct meson_bank meson_gxbb_periphs_banks[] = {
 
 static struct meson_bank meson_gxbb_aobus_banks[] = {
        /*   name    first      last       irq    pullen  pull    dir     out     in  */
-       BANK("AO",   GPIOAO_0,  GPIOAO_13, 0, 13, 0,  0,  0, 16,  0,  0,  0, 16,  1,  0),
+       BANK("AO",   GPIOAO_0,  GPIOAO_13, 0, 13, 0,  16, 0, 0,   0,  0,  0, 16,  1,  0),
 };
 
 static struct meson_pinctrl_data meson_gxbb_periphs_pinctrl_data = {
index 7dae1d7..158f618 100644 (file)
@@ -807,7 +807,7 @@ static struct meson_bank meson_gxl_periphs_banks[] = {
 
 static struct meson_bank meson_gxl_aobus_banks[] = {
        /*   name    first      last      irq   pullen  pull    dir     out     in  */
-       BANK("AO",   GPIOAO_0,  GPIOAO_9, 0, 9, 0,  0,  0, 16,  0,  0,  0, 16,  1,  0),
+       BANK("AO",   GPIOAO_0,  GPIOAO_9, 0, 9, 0,  16, 0, 0,   0,  0,  0, 16,  1,  0),
 };
 
 static struct meson_pinctrl_data meson_gxl_periphs_pinctrl_data = {
index f8b778a..ea87d73 100644 (file)
@@ -191,8 +191,9 @@ static int meson_pinconf_set(struct pinctrl_dev *pcdev, unsigned int pin,
                case PIN_CONFIG_BIAS_DISABLE:
                        dev_dbg(pc->dev, "pin %u: disable bias\n", pin);
 
-                       meson_calc_reg_and_bit(bank, pin, REG_PULL, &reg, &bit);
-                       ret = regmap_update_bits(pc->reg_pull, reg,
+                       meson_calc_reg_and_bit(bank, pin, REG_PULLEN, &reg,
+                                              &bit);
+                       ret = regmap_update_bits(pc->reg_pullen, reg,
                                                 BIT(bit), 0);
                        if (ret)
                                return ret;
index c6d7931..8646617 100644 (file)
@@ -1053,7 +1053,7 @@ static struct meson_bank meson8_cbus_banks[] = {
 
 static struct meson_bank meson8_aobus_banks[] = {
        /*   name    first     last         irq    pullen  pull    dir     out     in  */
-       BANK("AO",   GPIOAO_0, GPIO_TEST_N, 0, 13, 0,  0,  0, 16,  0,  0,  0, 16,  1,  0),
+       BANK("AO",   GPIOAO_0, GPIO_TEST_N, 0, 13, 0, 16,  0,  0,  0,  0,  0, 16,  1,  0),
 };
 
 static struct meson_pinctrl_data meson8_cbus_pinctrl_data = {
index bb2a309..647ad15 100644 (file)
@@ -906,7 +906,7 @@ static struct meson_bank meson8b_cbus_banks[] = {
 
 static struct meson_bank meson8b_aobus_banks[] = {
        /*   name    first     lastc        irq    pullen  pull    dir     out     in  */
-       BANK("AO",   GPIOAO_0, GPIO_TEST_N, 0, 13, 0,  0,  0, 16,  0,  0,  0, 16,  1,  0),
+       BANK("AO",   GPIOAO_0, GPIO_TEST_N, 0, 13, 0,  16, 0, 0,  0,  0,  0, 16,  1,  0),
 };
 
 static struct meson_pinctrl_data meson8b_cbus_pinctrl_data = {
index 6838b38..1bfb0ae 100644 (file)
@@ -33,7 +33,7 @@ enum {
        }
 
 
-#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9) \
+#define PINGROUP(id, _tile, f1, f2, f3, f4, f5, f6, f7, f8, f9)        \
        {                                               \
                .name = "gpio" #id,                     \
                .pins = gpio##id##_pins,                \
@@ -51,11 +51,12 @@ enum {
                        msm_mux_##f9                    \
                },                                      \
                .nfuncs = 10,                           \
-               .ctl_reg = base + REG_SIZE * id,        \
-               .io_reg = base + 0x4 + REG_SIZE * id,           \
-               .intr_cfg_reg = base + 0x8 + REG_SIZE * id,             \
-               .intr_status_reg = base + 0xc + REG_SIZE * id,  \
-               .intr_target_reg = base + 0x8 + REG_SIZE * id,  \
+               .ctl_reg = REG_SIZE * id,               \
+               .io_reg = 0x4 + REG_SIZE * id,          \
+               .intr_cfg_reg = 0x8 + REG_SIZE * id,    \
+               .intr_status_reg = 0xc + REG_SIZE * id, \
+               .intr_target_reg = 0x8 + REG_SIZE * id, \
+               .tile = _tile,                  \
                .mux_bit = 2,                   \
                .pull_bit = 0,                  \
                .drv_bit = 6,                   \
@@ -82,6 +83,7 @@ enum {
                .intr_cfg_reg = 0,                      \
                .intr_status_reg = 0,                   \
                .intr_target_reg = 0,                   \
+               .tile = NORTH,                          \
                .mux_bit = -1,                          \
                .pull_bit = pull,                       \
                .drv_bit = drv,                         \
@@ -1397,13 +1399,13 @@ static const struct msm_pingroup sdm660_groups[] = {
        PINGROUP(111, SOUTH, _, _, _, _, _, _, _, _, _),
        PINGROUP(112, SOUTH, _, _, _, _, _, _, _, _, _),
        PINGROUP(113, SOUTH, _, _, _, _, _, _, _, _, _),
-       SDC_QDSD_PINGROUP(sdc1_clk, 0x99a000, 13, 6),
-       SDC_QDSD_PINGROUP(sdc1_cmd, 0x99a000, 11, 3),
-       SDC_QDSD_PINGROUP(sdc1_data, 0x99a000, 9, 0),
-       SDC_QDSD_PINGROUP(sdc2_clk, 0x99b000, 14, 6),
-       SDC_QDSD_PINGROUP(sdc2_cmd, 0x99b000, 11, 3),
-       SDC_QDSD_PINGROUP(sdc2_data, 0x99b000, 9, 0),
-       SDC_QDSD_PINGROUP(sdc1_rclk, 0x99a000, 15, 0),
+       SDC_QDSD_PINGROUP(sdc1_clk, 0x9a000, 13, 6),
+       SDC_QDSD_PINGROUP(sdc1_cmd, 0x9a000, 11, 3),
+       SDC_QDSD_PINGROUP(sdc1_data, 0x9a000, 9, 0),
+       SDC_QDSD_PINGROUP(sdc2_clk, 0x9b000, 14, 6),
+       SDC_QDSD_PINGROUP(sdc2_cmd, 0x9b000, 11, 3),
+       SDC_QDSD_PINGROUP(sdc2_data, 0x9b000, 9, 0),
+       SDC_QDSD_PINGROUP(sdc1_rclk, 0x9a000, 15, 0),
 };
 
 static const struct msm_pinctrl_soc_data sdm660_pinctrl = {
index 6624499..4ada803 100644 (file)
@@ -568,7 +568,7 @@ static const struct sunxi_desc_pin sun8i_a83t_pins[] = {
        SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 11),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
                  SUNXI_FUNCTION(0x1, "gpio_out"),
-                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)),  /* PH_EINT11 */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 11)), /* PH_EINT11 */
 };
 
 static const struct sunxi_pinctrl_desc sun8i_a83t_pinctrl_data = {
index 40fda23..8a81eec 100644 (file)
@@ -252,8 +252,10 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
        ptp->dev = device_create_with_groups(ptp_class, parent, ptp->devid,
                                             ptp, ptp->pin_attr_groups,
                                             "ptp%d", ptp->index);
-       if (IS_ERR(ptp->dev))
+       if (IS_ERR(ptp->dev)) {
+               err = PTR_ERR(ptp->dev);
                goto no_device;
+       }
 
        /* Register a new PPS source. */
        if (info->pps) {
@@ -264,6 +266,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
                pps.owner = info->owner;
                ptp->pps_source = pps_register_source(&pps, PTP_PPS_DEFAULTS);
                if (!ptp->pps_source) {
+                       err = -EINVAL;
                        pr_err("failed to register pps source\n");
                        goto no_pps;
                }
index de21f62..183fc42 100644 (file)
@@ -214,6 +214,16 @@ static u64 rproc_virtio_get_features(struct virtio_device *vdev)
        return rsc->dfeatures;
 }
 
+static void rproc_transport_features(struct virtio_device *vdev)
+{
+       /*
+        * Packed ring isn't enabled on remoteproc for now,
+        * because remoteproc uses vring_new_virtqueue() which
+        * creates virtio rings on preallocated memory.
+        */
+       __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED);
+}
+
 static int rproc_virtio_finalize_features(struct virtio_device *vdev)
 {
        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
@@ -224,6 +234,9 @@ static int rproc_virtio_finalize_features(struct virtio_device *vdev)
        /* Give virtio_ring a chance to accept features */
        vring_transport_features(vdev);
 
+       /* Give virtio_rproc a chance to accept features. */
+       rproc_transport_features(vdev);
+
        /* Make sure we don't have any features > 32 bits! */
        BUG_ON((u32)vdev->features != vdev->features);
 
index e79f2a1..b9ec4a1 100644 (file)
@@ -50,8 +50,10 @@ static int __init rtc_hctosys(void)
        tv64.tv_sec = rtc_tm_to_time64(&tm);
 
 #if BITS_PER_LONG == 32
-       if (tv64.tv_sec > INT_MAX)
+       if (tv64.tv_sec > INT_MAX) {
+               err = -ERANGE;
                goto err_read;
+       }
 #endif
 
        err = do_settimeofday64(&tv64);
index df0c577..a5a19ff 100644 (file)
@@ -257,6 +257,7 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
        struct cmos_rtc *cmos = dev_get_drvdata(dev);
        unsigned char   rtc_control;
 
+       /* This not only a rtc_op, but also called directly */
        if (!is_valid_irq(cmos->irq))
                return -EIO;
 
@@ -452,6 +453,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
        unsigned char mon, mday, hrs, min, sec, rtc_control;
        int ret;
 
+       /* This not only a rtc_op, but also called directly */
        if (!is_valid_irq(cmos->irq))
                return -EIO;
 
@@ -516,9 +518,6 @@ static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled)
        struct cmos_rtc *cmos = dev_get_drvdata(dev);
        unsigned long   flags;
 
-       if (!is_valid_irq(cmos->irq))
-               return -EINVAL;
-
        spin_lock_irqsave(&rtc_lock, flags);
 
        if (enabled)
@@ -579,6 +578,12 @@ static const struct rtc_class_ops cmos_rtc_ops = {
        .alarm_irq_enable       = cmos_alarm_irq_enable,
 };
 
+static const struct rtc_class_ops cmos_rtc_ops_no_alarm = {
+       .read_time              = cmos_read_time,
+       .set_time               = cmos_set_time,
+       .proc                   = cmos_procfs,
+};
+
 /*----------------------------------------------------------------*/
 
 /*
@@ -855,9 +860,12 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
                        dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
                        goto cleanup1;
                }
+
+               cmos_rtc.rtc->ops = &cmos_rtc_ops;
+       } else {
+               cmos_rtc.rtc->ops = &cmos_rtc_ops_no_alarm;
        }
 
-       cmos_rtc.rtc->ops = &cmos_rtc_ops;
        cmos_rtc.rtc->nvram_old_abi = true;
        retval = rtc_register_device(cmos_rtc.rtc);
        if (retval)
index 2751dba..3e1abb4 100644 (file)
@@ -213,7 +213,7 @@ static int hid_rtc_read_time(struct device *dev, struct rtc_time *tm)
        /* get a report with all values through requesting one value */
        sensor_hub_input_attr_get_raw_value(time_state->common_attributes.hsdev,
                        HID_USAGE_SENSOR_TIME, hid_time_addresses[0],
-                       time_state->info[0].report_id, SENSOR_HUB_SYNC);
+                       time_state->info[0].report_id, SENSOR_HUB_SYNC, false);
        /* wait for all values (event) */
        ret = wait_for_completion_killable_timeout(
                        &time_state->comp_last_time, HZ*6);
index 9f99a09..7cb786d 100644 (file)
@@ -303,6 +303,9 @@ static int pcf2127_i2c_gather_write(void *context,
        memcpy(buf + 1, val, val_size);
 
        ret = i2c_master_send(client, buf, val_size + 1);
+
+       kfree(buf);
+
        if (ret != val_size + 1)
                return ret < 0 ? ret : -EIO;
 
index fd77e46..70a006b 100644 (file)
@@ -387,8 +387,10 @@ static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
                 * orb specified one of the unsupported formats, we defer
                 * checking for IDAWs in unsupported formats to here.
                 */
-               if ((!cp->orb.cmd.c64 || cp->orb.cmd.i2k) && ccw_is_idal(ccw))
+               if ((!cp->orb.cmd.c64 || cp->orb.cmd.i2k) && ccw_is_idal(ccw)) {
+                       kfree(p);
                        return -EOPNOTSUPP;
+               }
 
                if ((!ccw_is_chain(ccw)) && (!ccw_is_tic(ccw)))
                        break;
@@ -528,7 +530,7 @@ static int ccwchain_fetch_direct(struct ccwchain *chain,
 
        ret = pfn_array_alloc_pin(pat->pat_pa, cp->mdev, ccw->cda, ccw->count);
        if (ret < 0)
-               goto out_init;
+               goto out_unpin;
 
        /* Translate this direct ccw to a idal ccw. */
        idaws = kcalloc(ret, sizeof(*idaws), GFP_DMA | GFP_KERNEL);
index f47d16b..a10cec0 100644 (file)
@@ -22,7 +22,7 @@
 #include "vfio_ccw_private.h"
 
 struct workqueue_struct *vfio_ccw_work_q;
-struct kmem_cache *vfio_ccw_io_region;
+static struct kmem_cache *vfio_ccw_io_region;
 
 /*
  * Helpers
@@ -134,14 +134,14 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
        if (ret)
                goto out_free;
 
-       ret = vfio_ccw_mdev_reg(sch);
-       if (ret)
-               goto out_disable;
-
        INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
        atomic_set(&private->avail, 1);
        private->state = VFIO_CCW_STATE_STANDBY;
 
+       ret = vfio_ccw_mdev_reg(sch);
+       if (ret)
+               goto out_disable;
+
        return 0;
 
 out_disable:
index 048665e..9f5a201 100644 (file)
@@ -775,6 +775,8 @@ static int ap_device_probe(struct device *dev)
                drvres = ap_drv->flags & AP_DRIVER_FLAG_DEFAULT;
                if (!!devres != !!drvres)
                        return -ENODEV;
+               /* (re-)init queue's state machine */
+               ap_queue_reinit_state(to_ap_queue(dev));
        }
 
        /* Add queue/card to list of active queues/cards */
@@ -807,6 +809,8 @@ static int ap_device_remove(struct device *dev)
        struct ap_device *ap_dev = to_ap_dev(dev);
        struct ap_driver *ap_drv = ap_dev->drv;
 
+       if (is_queue_dev(dev))
+               ap_queue_remove(to_ap_queue(dev));
        if (ap_drv->remove)
                ap_drv->remove(ap_dev);
 
@@ -1444,10 +1448,6 @@ static void ap_scan_bus(struct work_struct *unused)
                        aq->ap_dev.device.parent = &ac->ap_dev.device;
                        dev_set_name(&aq->ap_dev.device,
                                     "%02x.%04x", id, dom);
-                       /* Start with a device reset */
-                       spin_lock_bh(&aq->lock);
-                       ap_wait(ap_sm_event(aq, AP_EVENT_POLL));
-                       spin_unlock_bh(&aq->lock);
                        /* Register device */
                        rc = device_register(&aq->ap_dev.device);
                        if (rc) {
index 3eed1b3..bfc66e4 100644 (file)
@@ -254,6 +254,7 @@ struct ap_queue *ap_queue_create(ap_qid_t qid, int device_type);
 void ap_queue_remove(struct ap_queue *aq);
 void ap_queue_suspend(struct ap_device *ap_dev);
 void ap_queue_resume(struct ap_device *ap_dev);
+void ap_queue_reinit_state(struct ap_queue *aq);
 
 struct ap_card *ap_card_create(int id, int queue_depth, int raw_device_type,
                               int comp_device_type, unsigned int functions);
index 66f7334..0aa4b3c 100644 (file)
@@ -718,5 +718,20 @@ void ap_queue_remove(struct ap_queue *aq)
 {
        ap_flush_queue(aq);
        del_timer_sync(&aq->timeout);
+
+       /* reset with zero, also clears irq registration */
+       spin_lock_bh(&aq->lock);
+       ap_zapq(aq->qid);
+       aq->state = AP_STATE_BORKED;
+       spin_unlock_bh(&aq->lock);
 }
 EXPORT_SYMBOL(ap_queue_remove);
+
+void ap_queue_reinit_state(struct ap_queue *aq)
+{
+       spin_lock_bh(&aq->lock);
+       aq->state = AP_STATE_RESET_START;
+       ap_wait(ap_sm_event(aq, AP_EVENT_POLL));
+       spin_unlock_bh(&aq->lock);
+}
+EXPORT_SYMBOL(ap_queue_reinit_state);
index 146f54f..c50f3e8 100644 (file)
@@ -196,7 +196,6 @@ static void zcrypt_cex2a_queue_remove(struct ap_device *ap_dev)
        struct ap_queue *aq = to_ap_queue(&ap_dev->device);
        struct zcrypt_queue *zq = aq->private;
 
-       ap_queue_remove(aq);
        if (zq)
                zcrypt_queue_unregister(zq);
 }
index 546f676..35c7c66 100644 (file)
@@ -251,7 +251,6 @@ static void zcrypt_cex2c_queue_remove(struct ap_device *ap_dev)
        struct ap_queue *aq = to_ap_queue(&ap_dev->device);
        struct zcrypt_queue *zq = aq->private;
 
-       ap_queue_remove(aq);
        if (zq)
                zcrypt_queue_unregister(zq);
 }
index f9d4c6c..582ffa7 100644 (file)
@@ -275,7 +275,6 @@ static void zcrypt_cex4_queue_remove(struct ap_device *ap_dev)
        struct ap_queue *aq = to_ap_queue(&ap_dev->device);
        struct zcrypt_queue *zq = aq->private;
 
-       ap_queue_remove(aq);
        if (zq)
                zcrypt_queue_unregister(zq);
 }
index f96ec68..dcbf5c8 100644 (file)
@@ -415,9 +415,9 @@ static irqreturn_t ism_handle_irq(int irq, void *data)
                        break;
 
                clear_bit_inv(bit, bv);
+               ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET] = 0;
                barrier();
                smcd_handle_irq(ism->smcd, bit + ISM_DMB_BIT_OFFSET);
-               ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET] = 0;
        }
 
        if (ism->sba->e) {
index ae97246..e63e031 100644 (file)
@@ -4513,8 +4513,8 @@ static int qeth_snmp_command_cb(struct qeth_card *card,
 {
        struct qeth_ipa_cmd *cmd;
        struct qeth_arp_query_info *qinfo;
-       struct qeth_snmp_cmd *snmp;
        unsigned char *data;
+       void *snmp_data;
        __u16 data_len;
 
        QETH_CARD_TEXT(card, 3, "snpcmdcb");
@@ -4522,7 +4522,6 @@ static int qeth_snmp_command_cb(struct qeth_card *card,
        cmd = (struct qeth_ipa_cmd *) sdata;
        data = (unsigned char *)((char *)cmd - reply->offset);
        qinfo = (struct qeth_arp_query_info *) reply->param;
-       snmp = &cmd->data.setadapterparms.data.snmp;
 
        if (cmd->hdr.return_code) {
                QETH_CARD_TEXT_(card, 4, "scer1%x", cmd->hdr.return_code);
@@ -4535,10 +4534,15 @@ static int qeth_snmp_command_cb(struct qeth_card *card,
                return 0;
        }
        data_len = *((__u16 *)QETH_IPA_PDU_LEN_PDU1(data));
-       if (cmd->data.setadapterparms.hdr.seq_no == 1)
-               data_len -= (__u16)((char *)&snmp->data - (char *)cmd);
-       else
-               data_len -= (__u16)((char *)&snmp->request - (char *)cmd);
+       if (cmd->data.setadapterparms.hdr.seq_no == 1) {
+               snmp_data = &cmd->data.setadapterparms.data.snmp;
+               data_len -= offsetof(struct qeth_ipa_cmd,
+                                    data.setadapterparms.data.snmp);
+       } else {
+               snmp_data = &cmd->data.setadapterparms.data.snmp.request;
+               data_len -= offsetof(struct qeth_ipa_cmd,
+                                    data.setadapterparms.data.snmp.request);
+       }
 
        /* check if there is enough room in userspace */
        if ((qinfo->udata_len - qinfo->udata_offset) < data_len) {
@@ -4551,16 +4555,9 @@ static int qeth_snmp_command_cb(struct qeth_card *card,
        QETH_CARD_TEXT_(card, 4, "sseqn%i",
                cmd->data.setadapterparms.hdr.seq_no);
        /*copy entries to user buffer*/
-       if (cmd->data.setadapterparms.hdr.seq_no == 1) {
-               memcpy(qinfo->udata + qinfo->udata_offset,
-                      (char *)snmp,
-                      data_len + offsetof(struct qeth_snmp_cmd, data));
-               qinfo->udata_offset += offsetof(struct qeth_snmp_cmd, data);
-       } else {
-               memcpy(qinfo->udata + qinfo->udata_offset,
-                      (char *)&snmp->request, data_len);
-       }
+       memcpy(qinfo->udata + qinfo->udata_offset, snmp_data, data_len);
        qinfo->udata_offset += data_len;
+
        /* check if all replies received ... */
                QETH_CARD_TEXT_(card, 4, "srtot%i",
                               cmd->data.setadapterparms.hdr.used_total);
index 2836231..f108d4b 100644 (file)
@@ -1007,7 +1007,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
                        qeth_l2_set_rx_mode(card->dev);
                } else {
                        rtnl_lock();
-                       dev_open(card->dev);
+                       dev_open(card->dev, NULL);
                        rtnl_unlock();
                }
        }
index eca68da..42a7cdc 100644 (file)
@@ -2417,7 +2417,7 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
                        __qeth_l3_open(card->dev);
                        qeth_l3_set_rx_mode(card->dev);
                } else {
-                       dev_open(card->dev);
+                       dev_open(card->dev, NULL);
                }
                rtnl_unlock();
        }
index 97b6f19..fc9dbad 100644 (file)
@@ -56,6 +56,7 @@ struct virtio_ccw_device {
        unsigned int revision; /* Transport revision */
        wait_queue_head_t wait_q;
        spinlock_t lock;
+       struct mutex io_lock; /* Serializes I/O requests */
        struct list_head virtqueues;
        unsigned long indicators;
        unsigned long indicators2;
@@ -296,6 +297,7 @@ static int ccw_io_helper(struct virtio_ccw_device *vcdev,
        unsigned long flags;
        int flag = intparm & VIRTIO_CCW_INTPARM_MASK;
 
+       mutex_lock(&vcdev->io_lock);
        do {
                spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags);
                ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0);
@@ -308,7 +310,9 @@ static int ccw_io_helper(struct virtio_ccw_device *vcdev,
                cpu_relax();
        } while (ret == -EBUSY);
        wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0);
-       return ret ? ret : vcdev->err;
+       ret = ret ? ret : vcdev->err;
+       mutex_unlock(&vcdev->io_lock);
+       return ret;
 }
 
 static void virtio_ccw_drop_indicator(struct virtio_ccw_device *vcdev,
@@ -765,6 +769,17 @@ out_free:
        return rc;
 }
 
+static void ccw_transport_features(struct virtio_device *vdev)
+{
+       /*
+        * Packed ring isn't enabled on virtio_ccw for now,
+        * because virtio_ccw uses some legacy accessors,
+        * e.g. virtqueue_get_avail() and virtqueue_get_used()
+        * which aren't available in packed ring currently.
+        */
+       __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED);
+}
+
 static int virtio_ccw_finalize_features(struct virtio_device *vdev)
 {
        struct virtio_ccw_device *vcdev = to_vc_device(vdev);
@@ -791,6 +806,9 @@ static int virtio_ccw_finalize_features(struct virtio_device *vdev)
        /* Give virtio_ring a chance to accept features. */
        vring_transport_features(vdev);
 
+       /* Give virtio_ccw a chance to accept features. */
+       ccw_transport_features(vdev);
+
        features->index = 0;
        features->features = cpu_to_le32((u32)vdev->features);
        /* Write the first half of the feature bits to the host. */
@@ -828,6 +846,7 @@ static void virtio_ccw_get_config(struct virtio_device *vdev,
        int ret;
        struct ccw1 *ccw;
        void *config_area;
+       unsigned long flags;
 
        ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
        if (!ccw)
@@ -846,11 +865,13 @@ static void virtio_ccw_get_config(struct virtio_device *vdev,
        if (ret)
                goto out_free;
 
+       spin_lock_irqsave(&vcdev->lock, flags);
        memcpy(vcdev->config, config_area, offset + len);
-       if (buf)
-               memcpy(buf, &vcdev->config[offset], len);
        if (vcdev->config_ready < offset + len)
                vcdev->config_ready = offset + len;
+       spin_unlock_irqrestore(&vcdev->lock, flags);
+       if (buf)
+               memcpy(buf, config_area + offset, len);
 
 out_free:
        kfree(config_area);
@@ -864,6 +885,7 @@ static void virtio_ccw_set_config(struct virtio_device *vdev,
        struct virtio_ccw_device *vcdev = to_vc_device(vdev);
        struct ccw1 *ccw;
        void *config_area;
+       unsigned long flags;
 
        ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
        if (!ccw)
@@ -876,9 +898,11 @@ static void virtio_ccw_set_config(struct virtio_device *vdev,
        /* Make sure we don't overwrite fields. */
        if (vcdev->config_ready < offset)
                virtio_ccw_get_config(vdev, 0, NULL, offset);
+       spin_lock_irqsave(&vcdev->lock, flags);
        memcpy(&vcdev->config[offset], buf, len);
        /* Write the config area to the host. */
        memcpy(config_area, vcdev->config, sizeof(vcdev->config));
+       spin_unlock_irqrestore(&vcdev->lock, flags);
        ccw->cmd_code = CCW_CMD_WRITE_CONF;
        ccw->flags = 0;
        ccw->count = offset + len;
@@ -1247,6 +1271,7 @@ static int virtio_ccw_online(struct ccw_device *cdev)
        init_waitqueue_head(&vcdev->wait_q);
        INIT_LIST_HEAD(&vcdev->virtqueues);
        spin_lock_init(&vcdev->lock);
+       mutex_init(&vcdev->io_lock);
 
        spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
        dev_set_drvdata(&cdev->dev, vcdev);
index 5c8ed73..a36e4cf 100644 (file)
@@ -220,6 +220,7 @@ static int d7s_probe(struct platform_device *op)
        dev_set_drvdata(&op->dev, p);
        d7s_device = p;
        err = 0;
+       of_node_put(opts);
 
 out:
        return err;
index 56e962a..b848192 100644 (file)
@@ -910,8 +910,10 @@ static void envctrl_init_i2c_child(struct device_node *dp,
                        for (len = 0; len < PCF8584_MAX_CHANNELS; ++len) {
                                pchild->mon_type[len] = ENVCTRL_NOMON;
                        }
+                       of_node_put(root_node);
                        return;
                }
+               of_node_put(root_node);
        }
 
        /* Get the monitor channels. */
index f07444d..640cd1b 100644 (file)
@@ -578,6 +578,7 @@ config SCSI_MYRB
 config SCSI_MYRS
        tristate "Mylex DAC960/DAC1100 PCI RAID Controller (SCSI Interface)"
        depends on PCI
+       depends on !CPU_BIG_ENDIAN || COMPILE_TEST
        select RAID_ATTRS
        help
          This driver adds support for the Mylex DAC960, AcceleRAID, and
index 8429c85..01c23d2 100644 (file)
@@ -1198,7 +1198,7 @@ static bool NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
 
 out:
        if (!hostdata->selecting)
-               return NULL;
+               return false;
        hostdata->selecting = NULL;
        return ret;
 }
index cd160f2..bcd30e2 100644 (file)
@@ -2364,7 +2364,7 @@ static int _bnx2fc_create(struct net_device *netdev,
        if (!interface) {
                printk(KERN_ERR PFX "bnx2fc_interface_create failed\n");
                rc = -ENOMEM;
-               goto ifput_err;
+               goto netdev_err;
        }
 
        if (is_vlan_dev(netdev)) {
index 064ef57..907dd87 100644 (file)
@@ -1767,8 +1767,7 @@ static int init_act_open(struct cxgbi_sock *csk)
                csk->mtu = dst_mtu(csk->dst);
        cxgb4_best_mtu(lldi->mtus, csk->mtu, &csk->mss_idx);
        csk->tx_chan = cxgb4_port_chan(ndev);
-       csk->smac_idx = cxgb4_tp_smt_idx(lldi->adapter_type,
-                                        cxgb4_port_viid(ndev));
+       csk->smac_idx = ((struct port_info *)netdev_priv(ndev))->smt_idx;
        step = lldi->ntxq / lldi->nchan;
        csk->txq_idx = cxgb4_port_idx(ndev) * step;
        step = lldi->nrxq / lldi->nchan;
index f0e457e..8df822a 100644 (file)
@@ -904,11 +904,9 @@ static void start_delivery_v1_hw(struct hisi_sas_dq *dq)
 {
        struct hisi_hba *hisi_hba = dq->hisi_hba;
        struct hisi_sas_slot *s, *s1, *s2 = NULL;
-       struct list_head *dq_list;
        int dlvry_queue = dq->id;
        int wp;
 
-       dq_list = &dq->list;
        list_for_each_entry_safe(s, s1, &dq->list, delivery) {
                if (!s->ready)
                        break;
index cc36b64..77a85ea 100644 (file)
@@ -1670,11 +1670,9 @@ static void start_delivery_v2_hw(struct hisi_sas_dq *dq)
 {
        struct hisi_hba *hisi_hba = dq->hisi_hba;
        struct hisi_sas_slot *s, *s1, *s2 = NULL;
-       struct list_head *dq_list;
        int dlvry_queue = dq->id;
        int wp;
 
-       dq_list = &dq->list;
        list_for_each_entry_safe(s, s1, &dq->list, delivery) {
                if (!s->ready)
                        break;
index bd4ce38..a369450 100644 (file)
@@ -886,11 +886,9 @@ static void start_delivery_v3_hw(struct hisi_sas_dq *dq)
 {
        struct hisi_hba *hisi_hba = dq->hisi_hba;
        struct hisi_sas_slot *s, *s1, *s2 = NULL;
-       struct list_head *dq_list;
        int dlvry_queue = dq->id;
        int wp;
 
-       dq_list = &dq->list;
        list_for_each_entry_safe(s, s1, &dq->list, delivery) {
                if (!s->ready)
                        break;
index 93c66eb..f78d2e5 100644 (file)
@@ -2416,8 +2416,8 @@ int iscsi_eh_session_reset(struct scsi_cmnd *sc)
 failed:
                ISCSI_DBG_EH(session,
                             "failing session reset: Could not log back into "
-                            "%s, %s [age %d]\n", session->targetname,
-                            conn->persistent_address, session->age);
+                            "%s [age %d]\n", session->targetname,
+                            session->age);
                spin_unlock_bh(&session->frwd_lock);
                mutex_unlock(&session->eh_mutex);
                return FAILED;
index 0c8005b..34d311a 100644 (file)
@@ -698,6 +698,8 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size)
                rport = lpfc_ndlp_get_nrport(ndlp);
                if (rport)
                        nrport = rport->remoteport;
+               else
+                       nrport = NULL;
                spin_unlock(&phba->hbalock);
                if (!nrport)
                        continue;
index 20fa678..68d62d5 100644 (file)
@@ -167,7 +167,11 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
                       sizeof(phba->wwpn));
        }
 
-       phba->sli3_options = 0x0;
+       /*
+        * Clear all option bits except LPFC_SLI3_BG_ENABLED,
+        * which was already set in lpfc_get_cfgparam()
+        */
+       phba->sli3_options &= (uint32_t)LPFC_SLI3_BG_ENABLED;
 
        /* Setup and issue mailbox READ REV command */
        lpfc_read_rev(phba, pmb);
index 783a154..b9e5cd7 100644 (file)
@@ -4965,7 +4965,6 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
                phba->sli3_options &= ~(LPFC_SLI3_NPIV_ENABLED |
                                        LPFC_SLI3_HBQ_ENABLED |
                                        LPFC_SLI3_CRP_ENABLED |
-                                       LPFC_SLI3_BG_ENABLED |
                                        LPFC_SLI3_DSS_ENABLED);
                if (rc != MBX_SUCCESS) {
                        lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
index aeb282f..0642f2d 100644 (file)
@@ -1049,7 +1049,8 @@ static int myrb_get_hba_config(struct myrb_hba *cb)
                enquiry2->fw.firmware_type = '0';
                enquiry2->fw.turn_id = 0;
        }
-       sprintf(cb->fw_version, "%d.%02d-%c-%02d",
+       snprintf(cb->fw_version, sizeof(cb->fw_version),
+               "%d.%02d-%c-%02d",
                enquiry2->fw.major_version,
                enquiry2->fw.minor_version,
                enquiry2->fw.firmware_type,
index 0264a2e..b8d54ef 100644 (file)
@@ -163,9 +163,12 @@ static unsigned char myrs_get_ctlr_info(struct myrs_hba *cs)
        dma_addr_t ctlr_info_addr;
        union myrs_sgl *sgl;
        unsigned char status;
-       struct myrs_ctlr_info old;
+       unsigned short ldev_present, ldev_critical, ldev_offline;
+
+       ldev_present = cs->ctlr_info->ldev_present;
+       ldev_critical = cs->ctlr_info->ldev_critical;
+       ldev_offline = cs->ctlr_info->ldev_offline;
 
-       memcpy(&old, cs->ctlr_info, sizeof(struct myrs_ctlr_info));
        ctlr_info_addr = dma_map_single(&cs->pdev->dev, cs->ctlr_info,
                                        sizeof(struct myrs_ctlr_info),
                                        DMA_FROM_DEVICE);
@@ -198,9 +201,9 @@ static unsigned char myrs_get_ctlr_info(struct myrs_hba *cs)
                    cs->ctlr_info->rbld_active +
                    cs->ctlr_info->exp_active != 0)
                        cs->needs_update = true;
-               if (cs->ctlr_info->ldev_present != old.ldev_present ||
-                   cs->ctlr_info->ldev_critical != old.ldev_critical ||
-                   cs->ctlr_info->ldev_offline != old.ldev_offline)
+               if (cs->ctlr_info->ldev_present != ldev_present ||
+                   cs->ctlr_info->ldev_critical != ldev_critical ||
+                   cs->ctlr_info->ldev_offline != ldev_offline)
                        shost_printk(KERN_INFO, cs->host,
                                     "Logical drive count changes (%d/%d/%d)\n",
                                     cs->ctlr_info->ldev_critical,
index 6fe20c2..eb59c79 100644 (file)
@@ -4763,6 +4763,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
        fcport->loop_id = FC_NO_LOOP_ID;
        qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
        fcport->supported_classes = FC_COS_UNSPECIFIED;
+       fcport->fp_speed = PORT_SPEED_UNKNOWN;
 
        fcport->ct_desc.ct_sns = dma_alloc_coherent(&vha->hw->pdev->dev,
                sizeof(struct ct_sns_pkt), &fcport->ct_desc.ct_sns_dma,
index 518f151..d0ecc72 100644 (file)
@@ -67,7 +67,7 @@ module_param(ql2xplogiabsentdevice, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(ql2xplogiabsentdevice,
                "Option to enable PLOGI to devices that are not present after "
                "a Fabric scan.  This is needed for several broken switches. "
-               "Default is 0 - no PLOGI. 1 - perfom PLOGI.");
+               "Default is 0 - no PLOGI. 1 - perform PLOGI.");
 
 int ql2xloginretrycount = 0;
 module_param(ql2xloginretrycount, int, S_IRUGO);
@@ -1749,7 +1749,7 @@ qla2x00_loop_reset(scsi_qla_host_t *vha)
 static void
 __qla2x00_abort_all_cmds(struct qla_qpair *qp, int res)
 {
-       int cnt;
+       int cnt, status;
        unsigned long flags;
        srb_t *sp;
        scsi_qla_host_t *vha = qp->vha;
@@ -1799,10 +1799,16 @@ __qla2x00_abort_all_cmds(struct qla_qpair *qp, int res)
                                        if (!sp_get(sp)) {
                                                spin_unlock_irqrestore
                                                        (qp->qp_lock_ptr, flags);
-                                               qla2xxx_eh_abort(
+                                               status = qla2xxx_eh_abort(
                                                        GET_CMD_SP(sp));
                                                spin_lock_irqsave
                                                        (qp->qp_lock_ptr, flags);
+                                               /*
+                                                * Get rid of extra reference caused
+                                                * by early exit from qla2xxx_eh_abort
+                                                */
+                                               if (status == FAST_IO_FAIL)
+                                                       atomic_dec(&sp->ref_count);
                                        }
                                }
                                sp->done(sp, res);
@@ -4880,10 +4886,10 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e)
                        fcport->d_id = e->u.new_sess.id;
                        fcport->flags |= FCF_FABRIC_DEVICE;
                        fcport->fw_login_state = DSC_LS_PLOGI_PEND;
-                       if (e->u.new_sess.fc4_type & FS_FC4TYPE_FCP)
+                       if (e->u.new_sess.fc4_type == FS_FC4TYPE_FCP)
                                fcport->fc4_type = FC4_TYPE_FCP_SCSI;
 
-                       if (e->u.new_sess.fc4_type & FS_FC4TYPE_NVME) {
+                       if (e->u.new_sess.fc4_type == FS_FC4TYPE_NVME) {
                                fcport->fc4_type = FC4_TYPE_OTHER;
                                fcport->fc4f_nvme = FC4_TYPE_NVME;
                        }
index c7fccbb..fa6e0c3 100644 (file)
@@ -697,6 +697,12 @@ static bool scsi_end_request(struct request *req, blk_status_t error,
                 */
                scsi_mq_uninit_cmd(cmd);
 
+               /*
+                * queue is still alive, so grab the ref for preventing it
+                * from being cleaned up during running queue.
+                */
+               percpu_ref_get(&q->q_usage_counter);
+
                __blk_mq_end_request(req, error);
 
                if (scsi_target(sdev)->single_lun ||
@@ -704,6 +710,8 @@ static bool scsi_end_request(struct request *req, blk_status_t error,
                        kblockd_schedule_work(&sdev->requeue_work);
                else
                        blk_mq_run_hw_queues(q, true);
+
+               percpu_ref_put(&q->q_usage_counter);
        } else {
                unsigned long flags;
 
index f03dc03..8f88348 100644 (file)
@@ -446,7 +446,6 @@ struct storvsc_device {
 
        bool     destroy;
        bool     drain_notify;
-       bool     open_sub_channel;
        atomic_t num_outstanding_req;
        struct Scsi_Host *host;
 
@@ -636,33 +635,38 @@ get_in_err:
 static void handle_sc_creation(struct vmbus_channel *new_sc)
 {
        struct hv_device *device = new_sc->primary_channel->device_obj;
+       struct device *dev = &device->device;
        struct storvsc_device *stor_device;
        struct vmstorage_channel_properties props;
+       int ret;
 
        stor_device = get_out_stor_device(device);
        if (!stor_device)
                return;
 
-       if (stor_device->open_sub_channel == false)
-               return;
-
        memset(&props, 0, sizeof(struct vmstorage_channel_properties));
 
-       vmbus_open(new_sc,
-                  storvsc_ringbuffer_size,
-                  storvsc_ringbuffer_size,
-                  (void *)&props,
-                  sizeof(struct vmstorage_channel_properties),
-                  storvsc_on_channel_callback, new_sc);
+       ret = vmbus_open(new_sc,
+                        storvsc_ringbuffer_size,
+                        storvsc_ringbuffer_size,
+                        (void *)&props,
+                        sizeof(struct vmstorage_channel_properties),
+                        storvsc_on_channel_callback, new_sc);
 
-       if (new_sc->state == CHANNEL_OPENED_STATE) {
-               stor_device->stor_chns[new_sc->target_cpu] = new_sc;
-               cpumask_set_cpu(new_sc->target_cpu, &stor_device->alloced_cpus);
+       /* In case vmbus_open() fails, we don't use the sub-channel. */
+       if (ret != 0) {
+               dev_err(dev, "Failed to open sub-channel: err=%d\n", ret);
+               return;
        }
+
+       /* Add the sub-channel to the array of available channels. */
+       stor_device->stor_chns[new_sc->target_cpu] = new_sc;
+       cpumask_set_cpu(new_sc->target_cpu, &stor_device->alloced_cpus);
 }
 
 static void  handle_multichannel_storage(struct hv_device *device, int max_chns)
 {
+       struct device *dev = &device->device;
        struct storvsc_device *stor_device;
        int num_cpus = num_online_cpus();
        int num_sc;
@@ -679,21 +683,11 @@ static void  handle_multichannel_storage(struct hv_device *device, int max_chns)
        request = &stor_device->init_request;
        vstor_packet = &request->vstor_packet;
 
-       stor_device->open_sub_channel = true;
        /*
         * Establish a handler for dealing with subchannels.
         */
        vmbus_set_sc_create_callback(device->channel, handle_sc_creation);
 
-       /*
-        * Check to see if sub-channels have already been created. This
-        * can happen when this driver is re-loaded after unloading.
-        */
-
-       if (vmbus_are_subchannels_present(device->channel))
-               return;
-
-       stor_device->open_sub_channel = false;
        /*
         * Request the host to create sub-channels.
         */
@@ -710,23 +704,29 @@ static void  handle_multichannel_storage(struct hv_device *device, int max_chns)
                               VM_PKT_DATA_INBAND,
                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
 
-       if (ret != 0)
+       if (ret != 0) {
+               dev_err(dev, "Failed to create sub-channel: err=%d\n", ret);
                return;
+       }
 
        t = wait_for_completion_timeout(&request->wait_event, 10*HZ);
-       if (t == 0)
+       if (t == 0) {
+               dev_err(dev, "Failed to create sub-channel: timed out\n");
                return;
+       }
 
        if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
-           vstor_packet->status != 0)
+           vstor_packet->status != 0) {
+               dev_err(dev, "Failed to create sub-channel: op=%d, sts=%d\n",
+                       vstor_packet->operation, vstor_packet->status);
                return;
+       }
 
        /*
-        * Now that we created the sub-channels, invoke the check; this
-        * may trigger the callback.
+        * We need to do nothing here, because vmbus_process_offer()
+        * invokes channel->sc_creation_callback, which will open and use
+        * the sub-channel(s).
         */
-       stor_device->open_sub_channel = true;
-       vmbus_are_subchannels_present(device->channel);
 }
 
 static void cache_wwn(struct storvsc_device *stor_device,
@@ -1794,7 +1794,6 @@ static int storvsc_probe(struct hv_device *device,
        }
 
        stor_device->destroy = false;
-       stor_device->open_sub_channel = false;
        init_waitqueue_head(&stor_device->waiting_to_drain);
        stor_device->device = device;
        stor_device->host = host;
index 46df707..452e19f 100644 (file)
@@ -20,6 +20,7 @@
 #include "unipro.h"
 #include "ufs-hisi.h"
 #include "ufshci.h"
+#include "ufs_quirks.h"
 
 static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
 {
@@ -390,6 +391,14 @@ static void ufs_hisi_set_dev_cap(struct ufs_hisi_dev_params *hisi_param)
 
 static void ufs_hisi_pwr_change_pre_change(struct ufs_hba *hba)
 {
+       if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_VS_DEBUGSAVECONFIGTIME) {
+               pr_info("ufs flash device must set VS_DebugSaveConfigTime 0x10\n");
+               /* VS_DebugSaveConfigTime */
+               ufshcd_dme_set(hba, UIC_ARG_MIB(0xD0A0), 0x10);
+               /* sync length */
+               ufshcd_dme_set(hba, UIC_ARG_MIB(0x1556), 0x48);
+       }
+
        /* update */
        ufshcd_dme_set(hba, UIC_ARG_MIB(0x15A8), 0x1);
        /* PA_TxSkip */
index 71f73d1..5d2dfdb 100644 (file)
@@ -131,4 +131,10 @@ struct ufs_dev_fix {
  */
 #define UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME        (1 << 8)
 
+/*
+ * Some UFS devices require VS_DebugSaveConfigTime is 0x10,
+ * enabling this quirk ensure this.
+ */
+#define UFS_DEVICE_QUIRK_HOST_VS_DEBUGSAVECONFIGTIME   (1 << 9)
+
 #endif /* UFS_QUIRKS_H_ */
index 23d7cca..f1c57cd 100644 (file)
@@ -231,6 +231,8 @@ static struct ufs_dev_fix ufs_fixups[] = {
        UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
        UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL,
                UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME),
+       UFS_FIX(UFS_VENDOR_SKHYNIX, "hB8aL1" /*H28U62301AMR*/,
+               UFS_DEVICE_QUIRK_HOST_VS_DEBUGSAVECONFIGTIME),
 
        END_FIX
 };
@@ -8099,13 +8101,6 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
                err = -ENOMEM;
                goto out_error;
        }
-
-       /*
-        * Do not use blk-mq at this time because blk-mq does not support
-        * runtime pm.
-        */
-       host->use_blk_mq = false;
-
        hba = shost_priv(host);
        hba->host = host;
        hba->dev = dev;
index 6e49102..0d6b2a8 100644 (file)
@@ -1202,8 +1202,6 @@ static void pvscsi_shutdown_intr(struct pvscsi_adapter *adapter)
 
 static void pvscsi_release_resources(struct pvscsi_adapter *adapter)
 {
-       pvscsi_shutdown_intr(adapter);
-
        if (adapter->workqueue)
                destroy_workqueue(adapter->workqueue);
 
@@ -1534,6 +1532,7 @@ static int pvscsi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 out_reset_adapter:
        ll_adapter_reset(adapter);
 out_release_resources:
+       pvscsi_shutdown_intr(adapter);
        pvscsi_release_resources(adapter);
        scsi_host_put(host);
 out_disable_device:
@@ -1542,6 +1541,7 @@ out_disable_device:
        return error;
 
 out_release_resources_and_disable:
+       pvscsi_shutdown_intr(adapter);
        pvscsi_release_resources(adapter);
        goto out_disable_device;
 }
index 7218fb9..1382a8d 100644 (file)
@@ -777,9 +777,6 @@ static int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl,
        u8 la = txn->la;
        bool usr_msg = false;
 
-       if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
-               return -EPROTONOSUPPORT;
-
        if (txn->mt == SLIM_MSG_MT_CORE &&
                (txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
                 txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW))
index 4399d18..9be4108 100644 (file)
 #define SLIM_MSG_MC_NEXT_REMOVE_CHANNEL          0x58
 #define SLIM_MSG_MC_RECONFIGURE_NOW              0x5F
 
-/*
- * Clock pause flag to indicate that the reconfig message
- * corresponds to clock pause sequence
- */
-#define SLIM_MSG_CLK_PAUSE_SEQ_FLG             (1U << 8)
-
 /* Clock pause values per SLIMbus spec */
 #define SLIM_CLK_FAST                          0
 #define SLIM_CLK_CONST_PHASE                   1
index 321a926..ec0837f 100644 (file)
@@ -601,3 +601,71 @@ struct dpaa2_dq *dpaa2_io_store_next(struct dpaa2_io_store *s, int *is_last)
        return ret;
 }
 EXPORT_SYMBOL_GPL(dpaa2_io_store_next);
+
+/**
+ * dpaa2_io_query_fq_count() - Get the frame and byte count for a given fq.
+ * @d: the given DPIO object.
+ * @fqid: the id of frame queue to be queried.
+ * @fcnt: the queried frame count.
+ * @bcnt: the queried byte count.
+ *
+ * Knowing the FQ count at run-time can be useful in debugging situations.
+ * The instantaneous frame- and byte-count are hereby returned.
+ *
+ * Return 0 for a successful query, and negative error code if query fails.
+ */
+int dpaa2_io_query_fq_count(struct dpaa2_io *d, u32 fqid,
+                           u32 *fcnt, u32 *bcnt)
+{
+       struct qbman_fq_query_np_rslt state;
+       struct qbman_swp *swp;
+       unsigned long irqflags;
+       int ret;
+
+       d = service_select(d);
+       if (!d)
+               return -ENODEV;
+
+       swp = d->swp;
+       spin_lock_irqsave(&d->lock_mgmt_cmd, irqflags);
+       ret = qbman_fq_query_state(swp, fqid, &state);
+       spin_unlock_irqrestore(&d->lock_mgmt_cmd, irqflags);
+       if (ret)
+               return ret;
+       *fcnt = qbman_fq_state_frame_count(&state);
+       *bcnt = qbman_fq_state_byte_count(&state);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(dpaa2_io_query_fq_count);
+
+/**
+ * dpaa2_io_query_bp_count() - Query the number of buffers currently in a
+ * buffer pool.
+ * @d: the given DPIO object.
+ * @bpid: the index of buffer pool to be queried.
+ * @num: the queried number of buffers in the buffer pool.
+ *
+ * Return 0 for a successful query, and negative error code if query fails.
+ */
+int dpaa2_io_query_bp_count(struct dpaa2_io *d, u16 bpid, u32 *num)
+{
+       struct qbman_bp_query_rslt state;
+       struct qbman_swp *swp;
+       unsigned long irqflags;
+       int ret;
+
+       d = service_select(d);
+       if (!d)
+               return -ENODEV;
+
+       swp = d->swp;
+       spin_lock_irqsave(&d->lock_mgmt_cmd, irqflags);
+       ret = qbman_bp_query(swp, bpid, &state);
+       spin_unlock_irqrestore(&d->lock_mgmt_cmd, irqflags);
+       if (ret)
+               return ret;
+       *num = qbman_bp_info_num_free_bufs(&state);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(dpaa2_io_query_bp_count);
index cf1d448..0bddb85 100644 (file)
@@ -1003,3 +1003,99 @@ int qbman_swp_CDAN_set(struct qbman_swp *s, u16 channelid,
 
        return 0;
 }
+
+#define QBMAN_RESPONSE_VERB_MASK       0x7f
+#define QBMAN_FQ_QUERY_NP              0x45
+#define QBMAN_BP_QUERY                 0x32
+
+struct qbman_fq_query_desc {
+       u8 verb;
+       u8 reserved[3];
+       __le32 fqid;
+       u8 reserved2[56];
+};
+
+int qbman_fq_query_state(struct qbman_swp *s, u32 fqid,
+                        struct qbman_fq_query_np_rslt *r)
+{
+       struct qbman_fq_query_desc *p;
+       void *resp;
+
+       p = (struct qbman_fq_query_desc *)qbman_swp_mc_start(s);
+       if (!p)
+               return -EBUSY;
+
+       /* FQID is a 24 bit value */
+       p->fqid = cpu_to_le32(fqid & 0x00FFFFFF);
+       resp = qbman_swp_mc_complete(s, p, QBMAN_FQ_QUERY_NP);
+       if (!resp) {
+               pr_err("qbman: Query FQID %d NP fields failed, no response\n",
+                      fqid);
+               return -EIO;
+       }
+       *r = *(struct qbman_fq_query_np_rslt *)resp;
+       /* Decode the outcome */
+       WARN_ON((r->verb & QBMAN_RESPONSE_VERB_MASK) != QBMAN_FQ_QUERY_NP);
+
+       /* Determine success or failure */
+       if (r->rslt != QBMAN_MC_RSLT_OK) {
+               pr_err("Query NP fields of FQID 0x%x failed, code=0x%02x\n",
+                      p->fqid, r->rslt);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+u32 qbman_fq_state_frame_count(const struct qbman_fq_query_np_rslt *r)
+{
+       return (le32_to_cpu(r->frm_cnt) & 0x00FFFFFF);
+}
+
+u32 qbman_fq_state_byte_count(const struct qbman_fq_query_np_rslt *r)
+{
+       return le32_to_cpu(r->byte_cnt);
+}
+
+struct qbman_bp_query_desc {
+       u8 verb;
+       u8 reserved;
+       __le16 bpid;
+       u8 reserved2[60];
+};
+
+int qbman_bp_query(struct qbman_swp *s, u16 bpid,
+                  struct qbman_bp_query_rslt *r)
+{
+       struct qbman_bp_query_desc *p;
+       void *resp;
+
+       p = (struct qbman_bp_query_desc *)qbman_swp_mc_start(s);
+       if (!p)
+               return -EBUSY;
+
+       p->bpid = cpu_to_le16(bpid);
+       resp = qbman_swp_mc_complete(s, p, QBMAN_BP_QUERY);
+       if (!resp) {
+               pr_err("qbman: Query BPID %d fields failed, no response\n",
+                      bpid);
+               return -EIO;
+       }
+       *r = *(struct qbman_bp_query_rslt *)resp;
+       /* Decode the outcome */
+       WARN_ON((r->verb & QBMAN_RESPONSE_VERB_MASK) != QBMAN_BP_QUERY);
+
+       /* Determine success or failure */
+       if (r->rslt != QBMAN_MC_RSLT_OK) {
+               pr_err("Query fields of BPID 0x%x failed, code=0x%02x\n",
+                      bpid, r->rslt);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+u32 qbman_bp_info_num_free_bufs(struct qbman_bp_query_rslt *a)
+{
+       return le32_to_cpu(a->fill);
+}
index 89d1dd9..fa35fc1 100644 (file)
@@ -441,4 +441,62 @@ static inline void *qbman_swp_mc_complete(struct qbman_swp *swp, void *cmd,
        return cmd;
 }
 
+/* Query APIs */
+struct qbman_fq_query_np_rslt {
+       u8 verb;
+       u8 rslt;
+       u8 st1;
+       u8 st2;
+       u8 reserved[2];
+       __le16 od1_sfdr;
+       __le16 od2_sfdr;
+       __le16 od3_sfdr;
+       __le16 ra1_sfdr;
+       __le16 ra2_sfdr;
+       __le32 pfdr_hptr;
+       __le32 pfdr_tptr;
+       __le32 frm_cnt;
+       __le32 byte_cnt;
+       __le16 ics_surp;
+       u8 is;
+       u8 reserved2[29];
+};
+
+int qbman_fq_query_state(struct qbman_swp *s, u32 fqid,
+                        struct qbman_fq_query_np_rslt *r);
+u32 qbman_fq_state_frame_count(const struct qbman_fq_query_np_rslt *r);
+u32 qbman_fq_state_byte_count(const struct qbman_fq_query_np_rslt *r);
+
+struct qbman_bp_query_rslt {
+       u8 verb;
+       u8 rslt;
+       u8 reserved[4];
+       u8 bdi;
+       u8 state;
+       __le32 fill;
+       __le32 hdotr;
+       __le16 swdet;
+       __le16 swdxt;
+       __le16 hwdet;
+       __le16 hwdxt;
+       __le16 swset;
+       __le16 swsxt;
+       __le16 vbpid;
+       __le16 icid;
+       __le64 bpscn_addr;
+       __le64 bpscn_ctx;
+       __le16 hw_targ;
+       u8 dbe;
+       u8 reserved2;
+       u8 sdcnt;
+       u8 hdcnt;
+       u8 sscnt;
+       u8 reserved3[9];
+};
+
+int qbman_bp_query(struct qbman_swp *s, u16 bpid,
+                  struct qbman_bp_query_rslt *r);
+
+u32 qbman_bp_info_num_free_bufs(struct qbman_bp_query_rslt *a);
+
 #endif /* __FSL_QBMAN_PORTAL_H */
index 5ce2471..52c153c 100644 (file)
@@ -36,6 +36,8 @@
 #define MAX_IRQNAME    16      /* big enough for "QMan portal %d" */
 #define QMAN_POLL_LIMIT 32
 #define QMAN_PIRQ_DQRR_ITHRESH 12
+#define QMAN_DQRR_IT_MAX 15
+#define QMAN_ITP_MAX 0xFFF
 #define QMAN_PIRQ_MR_ITHRESH 4
 #define QMAN_PIRQ_IPERIOD 100
 
@@ -727,9 +729,15 @@ static inline void qm_dqrr_vdqcr_set(struct qm_portal *portal, u32 vdqcr)
        qm_out(portal, QM_REG_DQRR_VDQCR, vdqcr);
 }
 
-static inline void qm_dqrr_set_ithresh(struct qm_portal *portal, u8 ithresh)
+static inline int qm_dqrr_set_ithresh(struct qm_portal *portal, u8 ithresh)
 {
+
+       if (ithresh > QMAN_DQRR_IT_MAX)
+               return -EINVAL;
+
        qm_out(portal, QM_REG_DQRR_ITR, ithresh);
+
+       return 0;
 }
 
 /* --- MR API --- */
@@ -1012,20 +1020,27 @@ static inline void put_affine_portal(void)
 
 static struct workqueue_struct *qm_portal_wq;
 
-void qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh)
+int qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh)
 {
+       int res;
+
        if (!portal)
-               return;
+               return -EINVAL;
+
+       res = qm_dqrr_set_ithresh(&portal->p, ithresh);
+       if (res)
+               return res;
 
-       qm_dqrr_set_ithresh(&portal->p, ithresh);
        portal->p.dqrr.ithresh = ithresh;
+
+       return 0;
 }
 EXPORT_SYMBOL(qman_dqrr_set_ithresh);
 
 void qman_dqrr_get_ithresh(struct qman_portal *portal, u8 *ithresh)
 {
        if (portal && ithresh)
-               *ithresh = portal->p.dqrr.ithresh;
+               *ithresh = qm_in(&portal->p, QM_REG_DQRR_ITR);
 }
 EXPORT_SYMBOL(qman_dqrr_get_ithresh);
 
@@ -1036,10 +1051,14 @@ void qman_portal_get_iperiod(struct qman_portal *portal, u32 *iperiod)
 }
 EXPORT_SYMBOL(qman_portal_get_iperiod);
 
-void qman_portal_set_iperiod(struct qman_portal *portal, u32 iperiod)
+int qman_portal_set_iperiod(struct qman_portal *portal, u32 iperiod)
 {
-       if (portal)
-               qm_out(&portal->p, QM_REG_ITPR, iperiod);
+       if (!portal || iperiod > QMAN_ITP_MAX)
+               return -EINVAL;
+
+       qm_out(&portal->p, QM_REG_ITPR, iperiod);
+
+       return 0;
 }
 EXPORT_SYMBOL(qman_portal_set_iperiod);
 
index 3dc3162..0c2867d 100644 (file)
@@ -522,11 +522,11 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
                mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, len);
                mtk_spi_setup_packet(master);
 
-               cnt = len / 4;
+               cnt = mdata->xfer_len / 4;
                iowrite32_rep(mdata->base + SPI_TX_DATA_REG,
                                trans->tx_buf + mdata->num_xfered, cnt);
 
-               remainder = len % 4;
+               remainder = mdata->xfer_len % 4;
                if (remainder > 0) {
                        reg_val = 0;
                        memcpy(&reg_val,
index f024c3f..2fd8881 100644 (file)
@@ -1540,13 +1540,26 @@ static int omap2_mcspi_remove(struct platform_device *pdev)
 /* work with hotplug and coldplug */
 MODULE_ALIAS("platform:omap2_mcspi");
 
-#ifdef CONFIG_SUSPEND
-static int omap2_mcspi_suspend_noirq(struct device *dev)
+static int __maybe_unused omap2_mcspi_suspend(struct device *dev)
 {
-       return pinctrl_pm_select_sleep_state(dev);
+       struct spi_master *master = dev_get_drvdata(dev);
+       struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
+       int error;
+
+       error = pinctrl_pm_select_sleep_state(dev);
+       if (error)
+               dev_warn(mcspi->dev, "%s: failed to set pins: %i\n",
+                        __func__, error);
+
+       error = spi_master_suspend(master);
+       if (error)
+               dev_warn(mcspi->dev, "%s: master suspend failed: %i\n",
+                        __func__, error);
+
+       return pm_runtime_force_suspend(dev);
 }
 
-static int omap2_mcspi_resume_noirq(struct device *dev)
+static int __maybe_unused omap2_mcspi_resume(struct device *dev)
 {
        struct spi_master *master = dev_get_drvdata(dev);
        struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
@@ -1557,17 +1570,17 @@ static int omap2_mcspi_resume_noirq(struct device *dev)
                dev_warn(mcspi->dev, "%s: failed to set pins: %i\n",
                         __func__, error);
 
-       return 0;
-}
+       error = spi_master_resume(master);
+       if (error)
+               dev_warn(mcspi->dev, "%s: master resume failed: %i\n",
+                        __func__, error);
 
-#else
-#define omap2_mcspi_suspend_noirq      NULL
-#define omap2_mcspi_resume_noirq       NULL
-#endif
+       return pm_runtime_force_resume(dev);
+}
 
 static const struct dev_pm_ops omap2_mcspi_pm_ops = {
-       .suspend_noirq = omap2_mcspi_suspend_noirq,
-       .resume_noirq = omap2_mcspi_resume_noirq,
+       SET_SYSTEM_SLEEP_PM_OPS(omap2_mcspi_suspend,
+                               omap2_mcspi_resume)
        .runtime_resume = omap_mcspi_runtime_resume,
 };
 
index e90b177..09a9400 100644 (file)
@@ -1005,35 +1005,38 @@ enum i8254_mode {
  * and INSN_DEVICE_CONFIG_GET_ROUTES.
  */
 #define NI_NAMES_BASE  0x8000u
+
+#define _TERM_N(base, n, x)    ((base) + ((x) & ((n) - 1)))
+
 /*
  * not necessarily all allowed 64 PFIs are valid--certainly not for all devices
  */
-#define NI_PFI(x)      (NI_NAMES_BASE        + ((x) & 0x3f))
+#define NI_PFI(x)              _TERM_N(NI_NAMES_BASE, 64, x)
 /* 8 trigger lines by standard, Some devices cannot talk to all eight. */
-#define TRIGGER_LINE(x)        (NI_PFI(-1)       + 1 + ((x) & 0x7))
+#define TRIGGER_LINE(x)                _TERM_N(NI_PFI(-1) + 1, 8, x)
 /* 4 RTSI shared MUXes to route signals to/from TRIGGER_LINES on NI hardware */
-#define NI_RTSI_BRD(x) (TRIGGER_LINE(-1) + 1 + ((x) & 0x3))
+#define NI_RTSI_BRD(x)         _TERM_N(TRIGGER_LINE(-1) + 1, 4, x)
 
 /* *** Counter/timer names : 8 counters max *** */
-#define NI_COUNTER_NAMES_BASE  (NI_RTSI_BRD(-1)  + 1)
-#define NI_MAX_COUNTERS               7
-#define NI_CtrSource(x)               (NI_COUNTER_NAMES_BASE + ((x) & NI_MAX_COUNTERS))
+#define NI_MAX_COUNTERS                8
+#define NI_COUNTER_NAMES_BASE  (NI_RTSI_BRD(-1)  + 1)
+#define NI_CtrSource(x)              _TERM_N(NI_COUNTER_NAMES_BASE, NI_MAX_COUNTERS, x)
 /* Gate, Aux, A,B,Z are all treated, at times as gates */
-#define NI_GATES_NAMES_BASE    (NI_CtrSource(-1) + 1)
-#define NI_CtrGate(x)         (NI_GATES_NAMES_BASE   + ((x) & NI_MAX_COUNTERS))
-#define NI_CtrAux(x)          (NI_CtrGate(-1)   + 1  + ((x) & NI_MAX_COUNTERS))
-#define NI_CtrA(x)            (NI_CtrAux(-1)    + 1  + ((x) & NI_MAX_COUNTERS))
-#define NI_CtrB(x)            (NI_CtrA(-1)      + 1  + ((x) & NI_MAX_COUNTERS))
-#define NI_CtrZ(x)            (NI_CtrB(-1)      + 1  + ((x) & NI_MAX_COUNTERS))
-#define NI_GATES_NAMES_MAX     NI_CtrZ(-1)
-#define NI_CtrArmStartTrigger(x) (NI_CtrZ(-1)    + 1  + ((x) & NI_MAX_COUNTERS))
+#define NI_GATES_NAMES_BASE    (NI_CtrSource(-1) + 1)
+#define NI_CtrGate(x)          _TERM_N(NI_GATES_NAMES_BASE, NI_MAX_COUNTERS, x)
+#define NI_CtrAux(x)           _TERM_N(NI_CtrGate(-1)  + 1, NI_MAX_COUNTERS, x)
+#define NI_CtrA(x)             _TERM_N(NI_CtrAux(-1)   + 1, NI_MAX_COUNTERS, x)
+#define NI_CtrB(x)             _TERM_N(NI_CtrA(-1)     + 1, NI_MAX_COUNTERS, x)
+#define NI_CtrZ(x)             _TERM_N(NI_CtrB(-1)     + 1, NI_MAX_COUNTERS, x)
+#define NI_GATES_NAMES_MAX     NI_CtrZ(-1)
+#define NI_CtrArmStartTrigger(x) _TERM_N(NI_CtrZ(-1)    + 1, NI_MAX_COUNTERS, x)
 #define NI_CtrInternalOutput(x) \
-                    (NI_CtrArmStartTrigger(-1)  + 1  + ((x) & NI_MAX_COUNTERS))
+                     _TERM_N(NI_CtrArmStartTrigger(-1) + 1, NI_MAX_COUNTERS, x)
 /** external pin(s) labeled conveniently as Ctr<i>Out. */
-#define NI_CtrOut(x)  (NI_CtrInternalOutput(-1)  + 1  + ((x) & NI_MAX_COUNTERS))
+#define NI_CtrOut(x)   _TERM_N(NI_CtrInternalOutput(-1) + 1, NI_MAX_COUNTERS, x)
 /** For Buffered sampling of ctr -- x series capability. */
-#define NI_CtrSampleClock(x)   (NI_CtrOut(-1)   + 1  + ((x) & NI_MAX_COUNTERS))
-#define NI_COUNTER_NAMES_MAX   NI_CtrSampleClock(-1)
+#define NI_CtrSampleClock(x)   _TERM_N(NI_CtrOut(-1)   + 1, NI_MAX_COUNTERS, x)
+#define NI_COUNTER_NAMES_MAX   NI_CtrSampleClock(-1)
 
 enum ni_common_signal_names {
        /* PXI_Star: this is a non-NI-specific signal */
index 2d1e032..5edf59a 100644 (file)
@@ -2843,7 +2843,8 @@ static int ni_ao_insn_config(struct comedi_device *dev,
                return ni_ao_arm(dev, s);
        case INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS:
                /* we don't care about actual channels */
-               data[1] = board->ao_speed;
+               /* data[3] : chanlist_len */
+               data[1] = board->ao_speed * data[3];
                data[2] = 0;
                return 0;
        default:
index 7a7ca67..daabace 100644 (file)
@@ -719,9 +719,6 @@ static int port_vlans_add(struct net_device *netdev,
        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
        int vid, err = 0;
 
-       if (netif_is_bridge_master(vlan->obj.orig_dev))
-               return -EOPNOTSUPP;
-
        if (switchdev_trans_ph_prepare(trans))
                return 0;
 
@@ -930,8 +927,6 @@ static int swdev_port_obj_del(struct net_device *netdev,
 static const struct switchdev_ops ethsw_port_switchdev_ops = {
        .switchdev_port_attr_get        = swdev_port_attr_get,
        .switchdev_port_attr_set        = swdev_port_attr_set,
-       .switchdev_port_obj_add         = swdev_port_obj_add,
-       .switchdev_port_obj_del         = swdev_port_obj_del,
 };
 
 /* For the moment, only flood setting needs to be updated */
@@ -972,6 +967,11 @@ static int port_bridge_leave(struct net_device *netdev)
        return err;
 }
 
+static bool ethsw_port_dev_check(const struct net_device *netdev)
+{
+       return netdev->netdev_ops == &ethsw_port_ops;
+}
+
 static int port_netdevice_event(struct notifier_block *unused,
                                unsigned long event, void *ptr)
 {
@@ -980,7 +980,7 @@ static int port_netdevice_event(struct notifier_block *unused,
        struct net_device *upper_dev;
        int err = 0;
 
-       if (netdev->netdev_ops != &ethsw_port_ops)
+       if (!ethsw_port_dev_check(netdev))
                return NOTIFY_DONE;
 
        /* Handle just upper dev link/unlink for the moment */
@@ -1083,10 +1083,51 @@ err_addr_alloc:
        return NOTIFY_BAD;
 }
 
+static int
+ethsw_switchdev_port_obj_event(unsigned long event, struct net_device *netdev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info)
+{
+       int err = -EOPNOTSUPP;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD:
+               err = swdev_port_obj_add(netdev, port_obj_info->obj,
+                                        port_obj_info->trans);
+               break;
+       case SWITCHDEV_PORT_OBJ_DEL:
+               err = swdev_port_obj_del(netdev, port_obj_info->obj);
+               break;
+       }
+
+       port_obj_info->handled = true;
+       return notifier_from_errno(err);
+}
+
+static int port_switchdev_blocking_event(struct notifier_block *unused,
+                                        unsigned long event, void *ptr)
+{
+       struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+
+       if (!ethsw_port_dev_check(dev))
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD: /* fall through */
+       case SWITCHDEV_PORT_OBJ_DEL:
+               return ethsw_switchdev_port_obj_event(event, dev, ptr);
+       }
+
+       return NOTIFY_DONE;
+}
+
 static struct notifier_block port_switchdev_nb = {
        .notifier_call = port_switchdev_event,
 };
 
+static struct notifier_block port_switchdev_blocking_nb = {
+       .notifier_call = port_switchdev_blocking_event,
+};
+
 static int ethsw_register_notifier(struct device *dev)
 {
        int err;
@@ -1103,8 +1144,16 @@ static int ethsw_register_notifier(struct device *dev)
                goto err_switchdev_nb;
        }
 
+       err = register_switchdev_blocking_notifier(&port_switchdev_blocking_nb);
+       if (err) {
+               dev_err(dev, "Failed to register switchdev blocking notifier\n");
+               goto err_switchdev_blocking_nb;
+       }
+
        return 0;
 
+err_switchdev_blocking_nb:
+       unregister_switchdev_notifier(&port_switchdev_nb);
 err_switchdev_nb:
        unregister_netdevice_notifier(&port_nb);
        return err;
@@ -1123,7 +1172,7 @@ static int ethsw_open(struct ethsw_core *ethsw)
 
        for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
                port_priv = ethsw->ports[i];
-               err = dev_open(port_priv->netdev);
+               err = dev_open(port_priv->netdev, NULL);
                if (err) {
                        netdev_err(port_priv->netdev, "dev_open err %d\n", err);
                        return err;
@@ -1291,8 +1340,15 @@ static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port)
 
 static void ethsw_unregister_notifier(struct device *dev)
 {
+       struct notifier_block *nb;
        int err;
 
+       nb = &port_switchdev_blocking_nb;
+       err = unregister_switchdev_blocking_notifier(nb);
+       if (err)
+               dev_err(dev,
+                       "Failed to unregister switchdev blocking notifier (%d)\n", err);
+
        err = unregister_switchdev_notifier(&port_switchdev_nb);
        if (err)
                dev_err(dev,
index a53231b..e3425bf 100644 (file)
@@ -310,6 +310,7 @@ static int ipipeif_hw_setup(struct v4l2_subdev *sd)
                        ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2);
                        break;
                }
+               /* fall through */
 
        case IPIPEIF_SDRAM_YUV:
                /* Set clock divider */
index a7a34e8..3252efa 100644 (file)
@@ -3,6 +3,7 @@ config VIDEO_SUNXI_CEDRUS
        depends on VIDEO_DEV && VIDEO_V4L2 && MEDIA_CONTROLLER
        depends on HAS_DMA
        depends on OF
+       depends on MEDIA_CONTROLLER_REQUEST_API
        select SUNXI_SRAM
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
index ec277ec..a951b3f 100644 (file)
@@ -5,3 +5,8 @@ Before this stateless decoder driver can leave the staging area:
 * Userspace support for the Request API needs to be reviewed;
 * Another stateless decoder driver should be submitted;
 * At least one stateless encoder driver should be submitted.
+* When queueing a request containing references to I frames, the
+  refcount of the memory for those I frames needs to be incremented
+  and decremented when the request is completed. This will likely
+  require some help from vb2. The driver should fail the request
+  if the memory/buffer is gone.
index 8255845..c912c70 100644 (file)
@@ -108,17 +108,6 @@ static int cedrus_request_validate(struct media_request *req)
        unsigned int count;
        unsigned int i;
 
-       count = vb2_request_buffer_cnt(req);
-       if (!count) {
-               v4l2_info(&ctx->dev->v4l2_dev,
-                         "No buffer was provided with the request\n");
-               return -ENOENT;
-       } else if (count > 1) {
-               v4l2_info(&ctx->dev->v4l2_dev,
-                         "More than one buffer was provided with the request\n");
-               return -EINVAL;
-       }
-
        list_for_each_entry(obj, &req->objects, list) {
                struct vb2_buffer *vb;
 
@@ -133,6 +122,17 @@ static int cedrus_request_validate(struct media_request *req)
        if (!ctx)
                return -ENOENT;
 
+       count = vb2_request_buffer_cnt(req);
+       if (!count) {
+               v4l2_info(&ctx->dev->v4l2_dev,
+                         "No buffer was provided with the request\n");
+               return -ENOENT;
+       } else if (count > 1) {
+               v4l2_info(&ctx->dev->v4l2_dev,
+                         "More than one buffer was provided with the request\n");
+               return -EINVAL;
+       }
+
        parent_hdl = &ctx->hdl;
 
        hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
@@ -253,7 +253,7 @@ static const struct v4l2_m2m_ops cedrus_m2m_ops = {
 
 static const struct media_device_ops cedrus_m2m_media_ops = {
        .req_validate   = cedrus_request_validate,
-       .req_queue      = vb2_m2m_request_queue,
+       .req_queue      = v4l2_m2m_request_queue,
 };
 
 static int cedrus_probe(struct platform_device *pdev)
index 32adbcb..07520a2 100644 (file)
@@ -255,10 +255,10 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
 
        res = platform_get_resource(dev->pdev, IORESOURCE_MEM, 0);
        dev->base = devm_ioremap_resource(dev->dev, res);
-       if (!dev->base) {
+       if (IS_ERR(dev->base)) {
                v4l2_err(&dev->v4l2_dev, "Failed to map registers\n");
 
-               ret = -ENOMEM;
+               ret = PTR_ERR(dev->base);
                goto err_sram;
        }
 
index 6a18cf7..18936cd 100644 (file)
@@ -351,7 +351,7 @@ static ssize_t set_datatype_show(struct device *dev,
 
        for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) {
                if (c->cfg.data_type & ch_data_type[i].most_ch_data_type)
-                       return snprintf(buf, PAGE_SIZE, ch_data_type[i].name);
+                       return snprintf(buf, PAGE_SIZE, "%s", ch_data_type[i].name);
        }
        return snprintf(buf, PAGE_SIZE, "unconfigured\n");
 }
index df6ebf4..5831f81 100644 (file)
@@ -335,6 +335,8 @@ static int mtk_hsdma_start_transfer(struct mtk_hsdam_engine *hsdma,
        /* tx desc */
        src = sg->src_addr;
        for (i = 0; i < chan->desc->num_sgs; i++) {
+               tx_desc = &chan->tx_ring[chan->tx_idx];
+
                if (len > HSDMA_MAX_PLEN)
                        tlen = HSDMA_MAX_PLEN;
                else
@@ -344,7 +346,6 @@ static int mtk_hsdma_start_transfer(struct mtk_hsdam_engine *hsdma,
                        tx_desc->addr1 = src;
                        tx_desc->flags |= HSDMA_DESC_PLEN1(tlen);
                } else {
-                       tx_desc = &chan->tx_ring[chan->tx_idx];
                        tx_desc->addr0 = src;
                        tx_desc->flags = HSDMA_DESC_PLEN0(tlen);
 
index b8566ed..aa98fbb 100644 (file)
@@ -82,7 +82,7 @@ static int rt2880_pinctrl_dt_node_to_map(struct pinctrl_dev *pctrldev,
        struct property *prop;
        const char *function_name, *group_name;
        int ret;
-       int ngroups;
+       int ngroups = 0;
        unsigned int reserved_maps = 0;
 
        for_each_node_with_property(np_config, "group")
index 9d156ef..4d473f0 100644 (file)
@@ -146,7 +146,7 @@ void r8712_report_sec_ie(struct _adapter *adapter, u8 authmode, u8 *sec_ie)
                p = buff;
                p += sprintf(p, "ASSOCINFO(ReqIEs=");
                len = sec_ie[1] + 2;
-               len =  (len < IW_CUSTOM_MAX) ? len : IW_CUSTOM_MAX - 1;
+               len =  (len < IW_CUSTOM_MAX) ? len : IW_CUSTOM_MAX;
                for (i = 0; i < len; i++)
                        p += sprintf(p, "%02x", sec_ie[i]);
                p += sprintf(p, ")");
index a737400..986a1d5 100644 (file)
@@ -1346,7 +1346,7 @@ sint r8712_restruct_sec_ie(struct _adapter *adapter, u8 *in_ie,
                     u8 *out_ie, uint in_len)
 {
        u8 authmode = 0, match;
-       u8 sec_ie[255], uncst_oui[4], bkup_ie[255];
+       u8 sec_ie[IW_CUSTOM_MAX], uncst_oui[4], bkup_ie[255];
        u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01};
        uint ielength, cnt, remove_cnt;
        int iEntry;
index 69c7abc..8445d51 100644 (file)
@@ -1565,7 +1565,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame)
        if (pstat->aid > 0) {
                DBG_871X("  old AID %d\n", pstat->aid);
        } else {
-               for (pstat->aid = 1; pstat->aid < NUM_STA; pstat->aid++)
+               for (pstat->aid = 1; pstat->aid <= NUM_STA; pstat->aid++)
                        if (pstapriv->sta_aid[pstat->aid - 1] == NULL)
                                break;
 
index 8507794..85aba8a 100644 (file)
@@ -109,12 +109,12 @@ static void update_recvframe_phyinfo(union recv_frame *precvframe,
        rx_bssid = get_hdr_bssid(wlanhdr);
        pkt_info.bssid_match = ((!IsFrameTypeCtrl(wlanhdr)) &&
                                !pattrib->icv_err && !pattrib->crc_err &&
-                               !ether_addr_equal(rx_bssid, my_bssid));
+                               ether_addr_equal(rx_bssid, my_bssid));
 
        rx_ra = get_ra(wlanhdr);
        my_hwaddr = myid(&padapter->eeprompriv);
        pkt_info.to_self = pkt_info.bssid_match &&
-               !ether_addr_equal(rx_ra, my_hwaddr);
+               ether_addr_equal(rx_ra, my_hwaddr);
 
 
        pkt_info.is_beacon = pkt_info.bssid_match &&
index af22347..db553f2 100644 (file)
@@ -1277,7 +1277,7 @@ static int cfg80211_rtw_get_station(struct wiphy *wiphy,
 
                sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
                sinfo->tx_packets = psta->sta_stats.tx_pkts;
-
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
        }
 
        /* for Ad-Hoc/AP mode */
index 28bfdbd..b8631ba 100644 (file)
@@ -2289,7 +2289,7 @@ static int rtw_wx_read32(struct net_device *dev,
 exit:
        kfree(ptmp);
 
-       return 0;
+       return ret;
 }
 
 static int rtw_wx_write32(struct net_device *dev,
index 3647b8f..5eeb4b9 100644 (file)
@@ -2095,7 +2095,7 @@ static int visornic_resume(struct visor_device *dev,
        mod_timer(&devdata->irq_poll_timer, msecs_to_jiffies(2));
 
        rtnl_lock();
-       dev_open(netdev);
+       dev_open(netdev, NULL);
        rtnl_unlock();
 
        complete_func(dev, 0);
index ea78937..45de21c 100644 (file)
@@ -1795,6 +1795,7 @@ vchiq_compat_ioctl_await_completion(struct file *file,
        struct vchiq_await_completion32 args32;
        struct vchiq_completion_data32 completion32;
        unsigned int *msgbufcount32;
+       unsigned int msgbufcount_native;
        compat_uptr_t msgbuf32;
        void *msgbuf;
        void **msgbufptr;
@@ -1906,7 +1907,11 @@ vchiq_compat_ioctl_await_completion(struct file *file,
                         sizeof(completion32)))
                return -EFAULT;
 
-       args32.msgbufcount--;
+       if (get_user(msgbufcount_native, &args->msgbufcount))
+               return -EFAULT;
+
+       if (!msgbufcount_native)
+               args32.msgbufcount--;
 
        msgbufcount32 =
                &((struct vchiq_await_completion32 __user *)arg)->msgbufcount;
index 71888b9..0376d73 100644 (file)
@@ -932,8 +932,8 @@ cxgbit_offload_init(struct cxgbit_sock *csk, int iptype, __u8 *peer_ip,
                        goto out;
                csk->mtu = ndev->mtu;
                csk->tx_chan = cxgb4_port_chan(ndev);
-               csk->smac_idx = cxgb4_tp_smt_idx(cdev->lldi.adapter_type,
-                                                cxgb4_port_viid(ndev));
+               csk->smac_idx =
+                              ((struct port_info *)netdev_priv(ndev))->smt_idx;
                step = cdev->lldi.ntxq /
                        cdev->lldi.nchan;
                csk->txq_idx = cxgb4_port_idx(ndev) * step;
@@ -968,8 +968,8 @@ cxgbit_offload_init(struct cxgbit_sock *csk, int iptype, __u8 *peer_ip,
                port_id = cxgb4_port_idx(ndev);
                csk->mtu = dst_mtu(dst);
                csk->tx_chan = cxgb4_port_chan(ndev);
-               csk->smac_idx = cxgb4_tp_smt_idx(cdev->lldi.adapter_type,
-                                                cxgb4_port_viid(ndev));
+               csk->smac_idx =
+                              ((struct port_info *)netdev_priv(ndev))->smt_idx;
                step = cdev->lldi.ntxq /
                        cdev->lldi.nports;
                csk->txq_idx = (port_id * step) +
index e31e4fc..2cfd61d 100644 (file)
@@ -1778,7 +1778,7 @@ EXPORT_SYMBOL(target_submit_tmr);
 void transport_generic_request_failure(struct se_cmd *cmd,
                sense_reason_t sense_reason)
 {
-       int ret = 0;
+       int ret = 0, post_ret;
 
        pr_debug("-----[ Storage Engine Exception; sense_reason %d\n",
                 sense_reason);
@@ -1790,7 +1790,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
        transport_complete_task_attr(cmd);
 
        if (cmd->transport_complete_callback)
-               cmd->transport_complete_callback(cmd, false, NULL);
+               cmd->transport_complete_callback(cmd, false, &post_ret);
 
        if (transport_check_aborted_status(cmd, 1))
                return;
index 92f67d4..d7105d0 100644 (file)
@@ -357,7 +357,7 @@ static int armada_get_temp_legacy(struct thermal_zone_device *thermal,
        int ret;
 
        /* Valid check */
-       if (armada_is_valid(priv)) {
+       if (!armada_is_valid(priv)) {
                dev_err(priv->dev,
                        "Temperature sensor reading not valid\n");
                return -EIO;
@@ -395,7 +395,7 @@ unlock_mutex:
        return ret;
 }
 
-static struct thermal_zone_of_device_ops of_ops = {
+static const struct thermal_zone_of_device_ops of_ops = {
        .get_temp = armada_get_temp,
 };
 
@@ -526,23 +526,21 @@ static int armada_thermal_probe_legacy(struct platform_device *pdev,
 
        /* First memory region points towards the status register */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -EIO;
-
-       /*
-        * Edit the resource start address and length to map over all the
-        * registers, instead of pointing at them one by one.
-        */
-       res->start -= data->syscon_status_off;
-       res->end = res->start + max(data->syscon_status_off,
-                                   max(data->syscon_control0_off,
-                                       data->syscon_control1_off)) +
-                  sizeof(unsigned int) - 1;
-
        base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(base))
                return PTR_ERR(base);
 
+       /*
+        * Fix up from the old individual DT register specification to
+        * cover all the registers.  We do this by adjusting the ioremap()
+        * result, which should be fine as ioremap() deals with pages.
+        * However, validate that we do not cross a page boundary while
+        * making this adjustment.
+        */
+       if (((unsigned long)base & ~PAGE_MASK) < data->syscon_status_off)
+               return -EINVAL;
+       base -= data->syscon_status_off;
+
        priv->syscon = devm_regmap_init_mmio(&pdev->dev, base,
                                             &armada_thermal_regmap_config);
        if (IS_ERR(priv->syscon))
index 23ad4f9..b9d90f0 100644 (file)
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Driver for Broadcom BCM2835 SoC temperature sensor
  *
  * Copyright (C) 2016 Martin Sperl
- *
- * 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.
  */
 
 #include <linux/clk.h>
index 1919f91..e8b1570 100644 (file)
@@ -299,7 +299,7 @@ static int brcmstb_set_trips(void *data, int low, int high)
        return 0;
 }
 
-static struct thermal_zone_of_device_ops of_ops = {
+static const struct thermal_zone_of_device_ops of_ops = {
        .get_temp       = brcmstb_get_temp,
        .set_trips      = brcmstb_set_trips,
 };
index c4111a9..2d26ae8 100644 (file)
@@ -424,7 +424,7 @@ static int hi3660_thermal_probe(struct hisi_thermal_data *data)
        struct platform_device *pdev = data->pdev;
        struct device *dev = &pdev->dev;
 
-       data->nr_sensors = 2;
+       data->nr_sensors = 1;
 
        data->sensor = devm_kzalloc(dev, sizeof(*data->sensor) *
                                    data->nr_sensors, GFP_KERNEL);
@@ -589,7 +589,7 @@ static int hisi_thermal_probe(struct platform_device *pdev)
                        return ret;
                }
 
-               ret = platform_get_irq_byname(pdev, sensor->irq_name);
+               ret = platform_get_irq(pdev, 0);
                if (ret < 0)
                        return ret;
 
index 47623da..bbd73c5 100644 (file)
@@ -241,8 +241,8 @@ static int stm_thermal_read_factory_settings(struct stm_thermal_sensor *sensor)
                sensor->t0 = TS1_T0_VAL1;
 
        /* Retrieve fmt0 and put it on Hz */
-       sensor->fmt0 = ADJUST * readl_relaxed(sensor->base + DTS_T0VALR1_OFFSET)
-                                             & TS1_FMT0_MASK;
+       sensor->fmt0 = ADJUST * (readl_relaxed(sensor->base +
+                                DTS_T0VALR1_OFFSET) & TS1_FMT0_MASK);
 
        /* Retrieve ramp coefficient */
        sensor->ramp_coeff = readl_relaxed(sensor->base + DTS_RAMPVALR_OFFSET) &
@@ -532,6 +532,10 @@ static int stm_thermal_prepare(struct stm_thermal_sensor *sensor)
        if (ret)
                return ret;
 
+       ret = stm_thermal_read_factory_settings(sensor);
+       if (ret)
+               goto thermal_unprepare;
+
        ret = stm_thermal_calibration(sensor);
        if (ret)
                goto thermal_unprepare;
@@ -636,10 +640,6 @@ static int stm_thermal_probe(struct platform_device *pdev)
        /* Populate sensor */
        sensor->base = base;
 
-       ret = stm_thermal_read_factory_settings(sensor);
-       if (ret)
-               return ret;
-
        sensor->clk = devm_clk_get(&pdev->dev, "pclk");
        if (IS_ERR(sensor->clk)) {
                dev_err(&pdev->dev, "%s: failed to fetch PCLK clock\n",
index 52ff854..cd96994 100644 (file)
@@ -863,6 +863,30 @@ static ssize_t key_store(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR(key, 0600, key_show, key_store);
 
+static void nvm_authenticate_start(struct tb_switch *sw)
+{
+       struct pci_dev *root_port;
+
+       /*
+        * During host router NVM upgrade we should not allow root port to
+        * go into D3cold because some root ports cannot trigger PME
+        * itself. To be on the safe side keep the root port in D0 during
+        * the whole upgrade process.
+        */
+       root_port = pci_find_pcie_root_port(sw->tb->nhi->pdev);
+       if (root_port)
+               pm_runtime_get_noresume(&root_port->dev);
+}
+
+static void nvm_authenticate_complete(struct tb_switch *sw)
+{
+       struct pci_dev *root_port;
+
+       root_port = pci_find_pcie_root_port(sw->tb->nhi->pdev);
+       if (root_port)
+               pm_runtime_put(&root_port->dev);
+}
+
 static ssize_t nvm_authenticate_show(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
@@ -912,10 +936,18 @@ static ssize_t nvm_authenticate_store(struct device *dev,
 
                sw->nvm->authenticating = true;
 
-               if (!tb_route(sw))
+               if (!tb_route(sw)) {
+                       /*
+                        * Keep root port from suspending as long as the
+                        * NVM upgrade process is running.
+                        */
+                       nvm_authenticate_start(sw);
                        ret = nvm_authenticate_host(sw);
-               else
+                       if (ret)
+                               nvm_authenticate_complete(sw);
+               } else {
                        ret = nvm_authenticate_device(sw);
+               }
                pm_runtime_mark_last_busy(&sw->dev);
                pm_runtime_put_autosuspend(&sw->dev);
        }
@@ -1334,6 +1366,10 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
        if (ret <= 0)
                return ret;
 
+       /* Now we can allow root port to suspend again */
+       if (!tb_route(sw))
+               nvm_authenticate_complete(sw);
+
        if (status) {
                tb_sw_info(sw, "switch flash authentication failed\n");
                tb_switch_set_uuid(sw);
index dd5e1ce..c3f933d 100644 (file)
@@ -213,17 +213,17 @@ static int mtk8250_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, data);
 
-       pm_runtime_enable(&pdev->dev);
-       if (!pm_runtime_enabled(&pdev->dev)) {
-               err = mtk8250_runtime_resume(&pdev->dev);
-               if (err)
-                       return err;
-       }
+       err = mtk8250_runtime_resume(&pdev->dev);
+       if (err)
+               return err;
 
        data->line = serial8250_register_8250_port(&uart);
        if (data->line < 0)
                return data->line;
 
+       pm_runtime_set_active(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+
        return 0;
 }
 
@@ -234,13 +234,11 @@ static int mtk8250_remove(struct platform_device *pdev)
        pm_runtime_get_sync(&pdev->dev);
 
        serial8250_unregister_port(data->line);
+       mtk8250_runtime_suspend(&pdev->dev);
 
        pm_runtime_disable(&pdev->dev);
        pm_runtime_put_noidle(&pdev->dev);
 
-       if (!pm_runtime_status_suspended(&pdev->dev))
-               mtk8250_runtime_suspend(&pdev->dev);
-
        return 0;
 }
 
index f776b3e..3f779d2 100644 (file)
@@ -552,30 +552,11 @@ static unsigned int serial_icr_read(struct uart_8250_port *up, int offset)
  */
 static void serial8250_clear_fifos(struct uart_8250_port *p)
 {
-       unsigned char fcr;
-       unsigned char clr_mask = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
-
        if (p->capabilities & UART_CAP_FIFO) {
-               /*
-                * Make sure to avoid changing FCR[7:3] and ENABLE_FIFO bits.
-                * In case ENABLE_FIFO is not set, there is nothing to flush
-                * so just return. Furthermore, on certain implementations of
-                * the 8250 core, the FCR[7:3] bits may only be changed under
-                * specific conditions and changing them if those conditions
-                * are not met can have nasty side effects. One such core is
-                * the 8250-omap present in TI AM335x.
-                */
-               fcr = serial_in(p, UART_FCR);
-
-               /* FIFO is not enabled, there's nothing to clear. */
-               if (!(fcr & UART_FCR_ENABLE_FIFO))
-                       return;
-
-               fcr |= clr_mask;
-               serial_out(p, UART_FCR, fcr);
-
-               fcr &= ~clr_mask;
-               serial_out(p, UART_FCR, fcr);
+               serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO);
+               serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO |
+                              UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+               serial_out(p, UART_FCR, 0);
        }
 }
 
@@ -1467,7 +1448,7 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p)
         * Enable previously disabled RX interrupts.
         */
        if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) {
-               serial8250_clear_fifos(p);
+               serial8250_clear_and_reinit_fifos(p);
 
                p->ier |= UART_IER_RLSI | UART_IER_RDI;
                serial_port_out(&p->port, UART_IER, p->ier);
index baeeeae..6fb312e 100644 (file)
@@ -233,7 +233,7 @@ static void kgdboc_put_char(u8 chr)
 static int param_set_kgdboc_var(const char *kmessage,
                                const struct kernel_param *kp)
 {
-       int len = strlen(kmessage);
+       size_t len = strlen(kmessage);
 
        if (len >= MAX_CONFIG_LEN) {
                pr_err("config string too long\n");
@@ -254,7 +254,7 @@ static int param_set_kgdboc_var(const char *kmessage,
 
        strcpy(config, kmessage);
        /* Chop out \n char as a result of echo */
-       if (config[len - 1] == '\n')
+       if (len && config[len - 1] == '\n')
                config[len - 1] = '\0';
 
        if (configured == 1)
index 70a4ea4..9903765 100644 (file)
@@ -112,6 +112,7 @@ void sunserial_console_termios(struct console *con, struct device_node *uart_dp)
                mode = of_get_property(dp, mode_prop, NULL);
                if (!mode)
                        mode = "9600,8,n,1,-";
+               of_node_put(dp);
        }
 
        cflag = CREAD | HUPCL | CLOCAL;
index ee80dfb..687250e 100644 (file)
@@ -1373,7 +1373,13 @@ err_release_lock:
        return ERR_PTR(retval);
 }
 
-static void tty_free_termios(struct tty_struct *tty)
+/**
+ * tty_save_termios() - save tty termios data in driver table
+ * @tty: tty whose termios data to save
+ *
+ * Locking: Caller guarantees serialisation with tty_init_termios().
+ */
+void tty_save_termios(struct tty_struct *tty)
 {
        struct ktermios *tp;
        int idx = tty->index;
@@ -1392,6 +1398,7 @@ static void tty_free_termios(struct tty_struct *tty)
        }
        *tp = tty->termios;
 }
+EXPORT_SYMBOL_GPL(tty_save_termios);
 
 /**
  *     tty_flush_works         -       flush all works of a tty/pty pair
@@ -1491,7 +1498,7 @@ static void release_tty(struct tty_struct *tty, int idx)
        WARN_ON(!mutex_is_locked(&tty_mutex));
        if (tty->ops->shutdown)
                tty->ops->shutdown(tty);
-       tty_free_termios(tty);
+       tty_save_termios(tty);
        tty_driver_remove_tty(tty->driver, tty);
        tty->port->itty = NULL;
        if (tty->link)
index cb60750..044c3cb 100644 (file)
@@ -633,7 +633,8 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty,
        if (tty_port_close_start(port, tty, filp) == 0)
                return;
        tty_port_shutdown(port, tty);
-       set_bit(TTY_IO_ERROR, &tty->flags);
+       if (!port->console)
+               set_bit(TTY_IO_ERROR, &tty->flags);
        tty_port_close_end(port, tty);
        tty_port_tty_set(port, NULL);
 }
index 8564466..0a357db 100644 (file)
@@ -961,6 +961,8 @@ int __uio_register_device(struct module *owner,
        if (ret)
                goto err_uio_dev_add_attributes;
 
+       info->uio_dev = idev;
+
        if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) {
                /*
                 * Note that we deliberately don't use devm_request_irq
@@ -972,11 +974,12 @@ int __uio_register_device(struct module *owner,
                 */
                ret = request_irq(info->irq, uio_interrupt,
                                  info->irq_flags, info->name, idev);
-               if (ret)
+               if (ret) {
+                       info->uio_dev = NULL;
                        goto err_request_irq;
+               }
        }
 
-       info->uio_dev = idev;
        return 0;
 
 err_request_irq:
index c2493d0..3c5169e 100644 (file)
@@ -204,9 +204,11 @@ hv_uio_open(struct uio_info *info, struct inode *inode)
        if (atomic_inc_return(&pdata->refcnt) != 1)
                return 0;
 
+       vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind);
+       vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel);
+
        ret = vmbus_connect_ring(dev->channel,
                                 hv_uio_channel_cb, dev->channel);
-
        if (ret == 0)
                dev->channel->inbound.ring_buffer->interrupt_mask = 1;
        else
@@ -334,9 +336,6 @@ hv_uio_probe(struct hv_device *dev,
                goto fail_close;
        }
 
-       vmbus_set_chn_rescind_callback(channel, hv_uio_rescind);
-       vmbus_set_sc_create_callback(channel, hv_uio_new_channel);
-
        ret = sysfs_create_bin_file(&channel->kobj, &ring_buffer_bin_attr);
        if (ret)
                dev_notice(&dev->device,
index 47d75c2..1b68fed 100644 (file)
@@ -1696,6 +1696,9 @@ static const struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
        .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
        },
+       { USB_DEVICE(0x0572, 0x1349), /* Hiro (Conexant) USB MODEM H50228 */
+       .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+       },
        { USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
        .driver_info = QUIRK_CONTROL_LINE_STATE, },
        { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
index c6077d5..f76b2e0 100644 (file)
@@ -2251,7 +2251,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
                /* descriptor may appear anywhere in config */
                err = __usb_get_extra_descriptor(udev->rawdescriptors[0],
                                le16_to_cpu(udev->config[0].desc.wTotalLength),
-                               USB_DT_OTG, (void **) &desc);
+                               USB_DT_OTG, (void **) &desc, sizeof(*desc));
                if (err || !(desc->bmAttributes & USB_OTG_HNP))
                        return 0;
 
@@ -2794,6 +2794,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
        int i, status;
        u16 portchange, portstatus;
        struct usb_port *port_dev = hub->ports[port1 - 1];
+       int reset_recovery_time;
 
        if (!hub_is_superspeed(hub->hdev)) {
                if (warm) {
@@ -2849,7 +2850,9 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                                        USB_PORT_FEAT_C_BH_PORT_RESET);
                        usb_clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_PORT_LINK_STATE);
-                       usb_clear_port_feature(hub->hdev, port1,
+
+                       if (udev)
+                               usb_clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_CONNECTION);
 
                        /*
@@ -2885,11 +2888,18 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
 
 done:
        if (status == 0) {
-               /* TRSTRCY = 10 ms; plus some extra */
                if (port_dev->quirks & USB_PORT_QUIRK_FAST_ENUM)
                        usleep_range(10000, 12000);
-               else
-                       msleep(10 + 40);
+               else {
+                       /* TRSTRCY = 10 ms; plus some extra */
+                       reset_recovery_time = 10 + 40;
+
+                       /* Hub needs extra delay after resetting its port. */
+                       if (hub->hdev->quirks & USB_QUIRK_HUB_SLOW_RESET)
+                               reset_recovery_time += 100;
+
+                       msleep(reset_recovery_time);
+               }
 
                if (udev) {
                        struct usb_hcd *hcd = bus_to_hcd(udev->bus);
@@ -5153,7 +5163,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 /* Handle notifying userspace about hub over-current events */
 static void port_over_current_notify(struct usb_port *port_dev)
 {
-       static char *envp[] = { NULL, NULL, NULL };
+       char *envp[3];
        struct device *hub_dev;
        char *port_dev_path;
 
@@ -5177,6 +5187,7 @@ static void port_over_current_notify(struct usb_port *port_dev)
        if (!envp[1])
                goto exit;
 
+       envp[2] = NULL;
        kobject_uevent_env(&hub_dev->kobj, KOBJ_CHANGE, envp);
 
        kfree(envp[1]);
index 178d6c6..514c521 100644 (file)
@@ -128,6 +128,9 @@ static int quirks_param_set(const char *val, const struct kernel_param *kp)
                        case 'n':
                                flags |= USB_QUIRK_DELAY_CTRL_MSG;
                                break;
+                       case 'o':
+                               flags |= USB_QUIRK_HUB_SLOW_RESET;
+                               break;
                        /* Ignore unrecognized flag characters */
                        }
                }
@@ -206,6 +209,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* Microsoft LifeCam-VX700 v2.0 */
        { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
 
+       /* Cherry Stream G230 2.0 (G85-231) and 3.0 (G85-232) */
+       { USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME },
+
        /* Logitech HD Pro Webcams C920, C920-C, C925e and C930e */
        { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
        { USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -327,6 +333,10 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* Midiman M-Audio Keystation 88es */
        { USB_DEVICE(0x0763, 0x0192), .driver_info = USB_QUIRK_RESET_RESUME },
 
+       /* SanDisk Ultra Fit and Ultra Flair */
+       { USB_DEVICE(0x0781, 0x5583), .driver_info = USB_QUIRK_NO_LPM },
+       { USB_DEVICE(0x0781, 0x5591), .driver_info = USB_QUIRK_NO_LPM },
+
        /* M-Systems Flash Disk Pioneers */
        { USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
 
@@ -380,6 +390,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x1a0a, 0x0200), .driver_info =
                        USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
 
+       /* Terminus Technology Inc. Hub */
+       { USB_DEVICE(0x1a40, 0x0101), .driver_info = USB_QUIRK_HUB_SLOW_RESET },
+
        /* Corsair K70 RGB */
        { USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT },
 
@@ -391,6 +404,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT |
          USB_QUIRK_DELAY_CTRL_MSG },
 
+       /* Corsair K70 LUX RGB */
+       { USB_DEVICE(0x1b1c, 0x1b33), .driver_info = USB_QUIRK_DELAY_INIT },
+
        /* Corsair K70 LUX */
        { USB_DEVICE(0x1b1c, 0x1b36), .driver_info = USB_QUIRK_DELAY_INIT },
 
@@ -411,6 +427,11 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x2040, 0x7200), .driver_info =
                        USB_QUIRK_CONFIG_INTF_STRINGS },
 
+       /* Raydium Touchscreen */
+       { USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM },
+
+       { USB_DEVICE(0x2386, 0x3119), .driver_info = USB_QUIRK_NO_LPM },
+
        /* DJI CineSSD */
        { USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM },
 
index 79d8bd7..4ebfbd7 100644 (file)
@@ -832,14 +832,14 @@ EXPORT_SYMBOL_GPL(usb_get_current_frame_number);
  */
 
 int __usb_get_extra_descriptor(char *buffer, unsigned size,
-                              unsigned char type, void **ptr)
+                              unsigned char type, void **ptr, size_t minsize)
 {
        struct usb_descriptor_header *header;
 
        while (size >= sizeof(struct usb_descriptor_header)) {
                header = (struct usb_descriptor_header *)buffer;
 
-               if (header->bLength < 2) {
+               if (header->bLength < 2 || header->bLength > size) {
                        printk(KERN_ERR
                                "%s: bogus descriptor, type %d length %d\n",
                                usbcore_name,
@@ -848,7 +848,7 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size,
                        return -1;
                }
 
-               if (header->bDescriptorType == type) {
+               if (header->bDescriptorType == type && header->bLength >= minsize) {
                        *ptr = header;
                        return 0;
                }
index d257c54..7afc108 100644 (file)
@@ -120,6 +120,7 @@ static int dwc2_pci_probe(struct pci_dev *pci,
        dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
        if (!dwc2) {
                dev_err(dev, "couldn't allocate dwc2 device\n");
+               ret = -ENOMEM;
                goto err;
        }
 
index becfbb8..2f2048a 100644 (file)
@@ -1499,6 +1499,7 @@ static int dwc3_probe(struct platform_device *pdev)
 
 err5:
        dwc3_event_buffers_cleanup(dwc);
+       dwc3_ulpi_exit(dwc);
 
 err4:
        dwc3_free_scratch_buffers(dwc);
index 1286076..8427958 100644 (file)
@@ -283,8 +283,10 @@ err:
 static void dwc3_pci_remove(struct pci_dev *pci)
 {
        struct dwc3_pci         *dwc = pci_get_drvdata(pci);
+       struct pci_dev          *pdev = dwc->pci;
 
-       gpiod_remove_lookup_table(&platform_bytcr_gpios);
+       if (pdev->device == PCI_DEVICE_ID_INTEL_BYT)
+               gpiod_remove_lookup_table(&platform_bytcr_gpios);
 #ifdef CONFIG_PM
        cancel_work_sync(&dwc->wakeup_work);
 #endif
index 679c12e..9f92ee0 100644 (file)
@@ -1081,7 +1081,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
                        /* Now prepare one extra TRB to align transfer size */
                        trb = &dep->trb_pool[dep->trb_enqueue];
                        __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
-                                       maxp - rem, false, 0,
+                                       maxp - rem, false, 1,
                                        req->request.stream_id,
                                        req->request.short_not_ok,
                                        req->request.no_interrupt);
@@ -1125,7 +1125,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
                /* Now prepare one extra TRB to align transfer size */
                trb = &dep->trb_pool[dep->trb_enqueue];
                __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
-                               false, 0, req->request.stream_id,
+                               false, 1, req->request.stream_id,
                                req->request.short_not_ok,
                                req->request.no_interrupt);
        } else if (req->request.zero && req->request.length &&
@@ -1141,7 +1141,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
                /* Now prepare one extra TRB to handle ZLP */
                trb = &dep->trb_pool[dep->trb_enqueue];
                __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
-                               false, 0, req->request.stream_id,
+                               false, 1, req->request.stream_id,
                                req->request.short_not_ok,
                                req->request.no_interrupt);
        } else {
@@ -1470,9 +1470,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
                unsigned transfer_in_flight;
                unsigned started;
 
-               if (dep->flags & DWC3_EP_STALL)
-                       return 0;
-
                if (dep->number > 1)
                        trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
                else
@@ -1494,8 +1491,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
                else
                        dep->flags |= DWC3_EP_STALL;
        } else {
-               if (!(dep->flags & DWC3_EP_STALL))
-                       return 0;
 
                ret = dwc3_send_clear_stall_ep_cmd(dep);
                if (ret)
@@ -2259,7 +2254,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
         * with one TRB pending in the ring. We need to manually clear HWO bit
         * from that TRB.
         */
-       if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
+       if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
                trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
                return 1;
        }
index 3ada83d..31e8bf3 100644 (file)
@@ -215,7 +215,6 @@ struct ffs_io_data {
 
        struct mm_struct *mm;
        struct work_struct work;
-       struct work_struct cancellation_work;
 
        struct usb_ep *ep;
        struct usb_request *req;
@@ -1073,31 +1072,22 @@ ffs_epfile_open(struct inode *inode, struct file *file)
        return 0;
 }
 
-static void ffs_aio_cancel_worker(struct work_struct *work)
-{
-       struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
-                                                  cancellation_work);
-
-       ENTER();
-
-       usb_ep_dequeue(io_data->ep, io_data->req);
-}
-
 static int ffs_aio_cancel(struct kiocb *kiocb)
 {
        struct ffs_io_data *io_data = kiocb->private;
-       struct ffs_data *ffs = io_data->ffs;
+       struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
        int value;
 
        ENTER();
 
-       if (likely(io_data && io_data->ep && io_data->req)) {
-               INIT_WORK(&io_data->cancellation_work, ffs_aio_cancel_worker);
-               queue_work(ffs->io_completion_wq, &io_data->cancellation_work);
-               value = -EINPROGRESS;
-       } else {
+       spin_lock_irq(&epfile->ffs->eps_lock);
+
+       if (likely(io_data && io_data->ep && io_data->req))
+               value = usb_ep_dequeue(io_data->ep, io_data->req);
+       else
                value = -EINVAL;
-       }
+
+       spin_unlock_irq(&epfile->ffs->eps_lock);
 
        return value;
 }
index 1000d86..737bd77 100644 (file)
@@ -401,12 +401,12 @@ done:
 static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
 {
        struct usb_request      *req;
-       struct usb_request      *tmp;
        unsigned long           flags;
 
        /* fill unused rxq slots with some skb */
        spin_lock_irqsave(&dev->req_lock, flags);
-       list_for_each_entry_safe(req, tmp, &dev->rx_reqs, list) {
+       while (!list_empty(&dev->rx_reqs)) {
+               req = list_first_entry(&dev->rx_reqs, struct usb_request, list);
                list_del_init(&req->list);
                spin_unlock_irqrestore(&dev->req_lock, flags);
 
@@ -879,7 +879,7 @@ int gether_register_netdev(struct net_device *net)
        sa.sa_family = net->type;
        memcpy(sa.sa_data, dev->dev_mac, ETH_ALEN);
        rtnl_lock();
-       status = dev_set_mac_address(net, &sa);
+       status = dev_set_mac_address(net, &sa, NULL);
        rtnl_unlock();
        if (status)
                pr_warn("cannot set self ethernet address: %d\n", status);
@@ -1125,7 +1125,6 @@ void gether_disconnect(struct gether *link)
 {
        struct eth_dev          *dev = link->ioport;
        struct usb_request      *req;
-       struct usb_request      *tmp;
 
        WARN_ON(!dev);
        if (!dev)
@@ -1142,7 +1141,8 @@ void gether_disconnect(struct gether *link)
         */
        usb_ep_disable(link->in_ep);
        spin_lock(&dev->req_lock);
-       list_for_each_entry_safe(req, tmp, &dev->tx_reqs, list) {
+       while (!list_empty(&dev->tx_reqs)) {
+               req = list_first_entry(&dev->tx_reqs, struct usb_request, list);
                list_del(&req->list);
 
                spin_unlock(&dev->req_lock);
@@ -1154,7 +1154,8 @@ void gether_disconnect(struct gether *link)
 
        usb_ep_disable(link->out_ep);
        spin_lock(&dev->req_lock);
-       list_for_each_entry_safe(req, tmp, &dev->rx_reqs, list) {
+       while (!list_empty(&dev->rx_reqs)) {
+               req = list_first_entry(&dev->rx_reqs, struct usb_request, list);
                list_del(&req->list);
 
                spin_unlock(&dev->req_lock);
index 3a16431..fcf13ef 100644 (file)
@@ -2033,6 +2033,7 @@ static inline int machine_without_vbus_sense(void)
 {
        return machine_is_omap_innovator()
                || machine_is_omap_osk()
+               || machine_is_omap_palmte()
                || machine_is_sx1()
                /* No known omap7xx boards with vbus sense */
                || cpu_is_omap7xx();
@@ -2041,7 +2042,7 @@ static inline int machine_without_vbus_sense(void)
 static int omap_udc_start(struct usb_gadget *g,
                struct usb_gadget_driver *driver)
 {
-       int             status = -ENODEV;
+       int             status;
        struct omap_ep  *ep;
        unsigned long   flags;
 
@@ -2079,6 +2080,7 @@ static int omap_udc_start(struct usb_gadget *g,
                        goto done;
                }
        } else {
+               status = 0;
                if (can_pullup(udc))
                        pullup_enable(udc);
                else
@@ -2593,9 +2595,22 @@ omap_ep_setup(char *name, u8 addr, u8 type,
 
 static void omap_udc_release(struct device *dev)
 {
-       complete(udc->done);
+       pullup_disable(udc);
+       if (!IS_ERR_OR_NULL(udc->transceiver)) {
+               usb_put_phy(udc->transceiver);
+               udc->transceiver = NULL;
+       }
+       omap_writew(0, UDC_SYSCON1);
+       remove_proc_file();
+       if (udc->dc_clk) {
+               if (udc->clk_requested)
+                       omap_udc_enable_clock(0);
+               clk_put(udc->hhc_clk);
+               clk_put(udc->dc_clk);
+       }
+       if (udc->done)
+               complete(udc->done);
        kfree(udc);
-       udc = NULL;
 }
 
 static int
@@ -2627,6 +2642,7 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
        udc->gadget.speed = USB_SPEED_UNKNOWN;
        udc->gadget.max_speed = USB_SPEED_FULL;
        udc->gadget.name = driver_name;
+       udc->gadget.quirk_ep_out_aligned_size = 1;
        udc->transceiver = xceiv;
 
        /* ep0 is special; put it right after the SETUP buffer */
@@ -2867,8 +2883,8 @@ bad_on_1710:
                udc->clr_halt = UDC_RESET_EP;
 
        /* USB general purpose IRQ:  ep0, state changes, dma, etc */
-       status = request_irq(pdev->resource[1].start, omap_udc_irq,
-                       0, driver_name, udc);
+       status = devm_request_irq(&pdev->dev, pdev->resource[1].start,
+                                 omap_udc_irq, 0, driver_name, udc);
        if (status != 0) {
                ERR("can't get irq %d, err %d\n",
                        (int) pdev->resource[1].start, status);
@@ -2876,20 +2892,20 @@ bad_on_1710:
        }
 
        /* USB "non-iso" IRQ (PIO for all but ep0) */
-       status = request_irq(pdev->resource[2].start, omap_udc_pio_irq,
-                       0, "omap_udc pio", udc);
+       status = devm_request_irq(&pdev->dev, pdev->resource[2].start,
+                                 omap_udc_pio_irq, 0, "omap_udc pio", udc);
        if (status != 0) {
                ERR("can't get irq %d, err %d\n",
                        (int) pdev->resource[2].start, status);
-               goto cleanup2;
+               goto cleanup1;
        }
 #ifdef USE_ISO
-       status = request_irq(pdev->resource[3].start, omap_udc_iso_irq,
-                       0, "omap_udc iso", udc);
+       status = devm_request_irq(&pdev->dev, pdev->resource[3].start,
+                                 omap_udc_iso_irq, 0, "omap_udc iso", udc);
        if (status != 0) {
                ERR("can't get irq %d, err %d\n",
                        (int) pdev->resource[3].start, status);
-               goto cleanup3;
+               goto cleanup1;
        }
 #endif
        if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
@@ -2900,23 +2916,8 @@ bad_on_1710:
        }
 
        create_proc_file();
-       status = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget,
-                       omap_udc_release);
-       if (status)
-               goto cleanup4;
-
-       return 0;
-
-cleanup4:
-       remove_proc_file();
-
-#ifdef USE_ISO
-cleanup3:
-       free_irq(pdev->resource[2].start, udc);
-#endif
-
-cleanup2:
-       free_irq(pdev->resource[1].start, udc);
+       return usb_add_gadget_udc_release(&pdev->dev, &udc->gadget,
+                                         omap_udc_release);
 
 cleanup1:
        kfree(udc);
@@ -2943,42 +2944,15 @@ static int omap_udc_remove(struct platform_device *pdev)
 {
        DECLARE_COMPLETION_ONSTACK(done);
 
-       if (!udc)
-               return -ENODEV;
-
-       usb_del_gadget_udc(&udc->gadget);
-       if (udc->driver)
-               return -EBUSY;
-
        udc->done = &done;
 
-       pullup_disable(udc);
-       if (!IS_ERR_OR_NULL(udc->transceiver)) {
-               usb_put_phy(udc->transceiver);
-               udc->transceiver = NULL;
-       }
-       omap_writew(0, UDC_SYSCON1);
-
-       remove_proc_file();
-
-#ifdef USE_ISO
-       free_irq(pdev->resource[3].start, udc);
-#endif
-       free_irq(pdev->resource[2].start, udc);
-       free_irq(pdev->resource[1].start, udc);
+       usb_del_gadget_udc(&udc->gadget);
 
-       if (udc->dc_clk) {
-               if (udc->clk_requested)
-                       omap_udc_enable_clock(0);
-               clk_put(udc->hhc_clk);
-               clk_put(udc->dc_clk);
-       }
+       wait_for_completion(&done);
 
        release_mem_region(pdev->resource[0].start,
                        pdev->resource[0].end - pdev->resource[0].start + 1);
 
-       wait_for_completion(&done);
-
        return 0;
 }
 
index 684d6f0..09a8ebd 100644 (file)
@@ -640,7 +640,7 @@ static int hwahc_security_create(struct hwahc *hwahc)
        top = itr + itr_size;
        result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index],
                        le16_to_cpu(usb_dev->actconfig->desc.wTotalLength),
-                       USB_DT_SECURITY, (void **) &secd);
+                       USB_DT_SECURITY, (void **) &secd, sizeof(*secd));
        if (result == -1) {
                dev_warn(dev, "BUG? WUSB host has no security descriptors\n");
                return 0;
index 27f0016..3c4abb5 100644 (file)
@@ -325,14 +325,16 @@ static int xhci_histb_remove(struct platform_device *dev)
        struct xhci_hcd_histb *histb = platform_get_drvdata(dev);
        struct usb_hcd *hcd = histb->hcd;
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       struct usb_hcd *shared_hcd = xhci->shared_hcd;
 
        xhci->xhc_state |= XHCI_STATE_REMOVING;
 
-       usb_remove_hcd(xhci->shared_hcd);
+       usb_remove_hcd(shared_hcd);
+       xhci->shared_hcd = NULL;
        device_wakeup_disable(&dev->dev);
 
        usb_remove_hcd(hcd);
-       usb_put_hcd(xhci->shared_hcd);
+       usb_put_hcd(shared_hcd);
 
        xhci_histb_host_disable(histb);
        usb_put_hcd(hcd);
index 12eea73..01b5818 100644 (file)
@@ -876,7 +876,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
                        status |= USB_PORT_STAT_SUSPEND;
        }
        if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME &&
-               !DEV_SUPERSPEED_ANY(raw_port_status)) {
+               !DEV_SUPERSPEED_ANY(raw_port_status) && hcd->speed < HCD_USB3) {
                if ((raw_port_status & PORT_RESET) ||
                                !(raw_port_status & PORT_PE))
                        return 0xffffffff;
@@ -921,7 +921,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
                        time_left = wait_for_completion_timeout(
                                        &bus_state->rexit_done[wIndex],
                                        msecs_to_jiffies(
-                                               XHCI_MAX_REXIT_TIMEOUT));
+                                               XHCI_MAX_REXIT_TIMEOUT_MS));
                        spin_lock_irqsave(&xhci->lock, flags);
 
                        if (time_left) {
@@ -935,7 +935,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
                        } else {
                                int port_status = readl(port->addr);
                                xhci_warn(xhci, "Port resume took longer than %i msec, port status = 0x%x\n",
-                                               XHCI_MAX_REXIT_TIMEOUT,
+                                               XHCI_MAX_REXIT_TIMEOUT_MS,
                                                port_status);
                                status |= USB_PORT_STAT_SUSPEND;
                                clear_bit(wIndex, &bus_state->rexit_ports);
@@ -1474,15 +1474,18 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
        unsigned long flags;
        struct xhci_hub *rhub;
        struct xhci_port **ports;
+       u32 portsc_buf[USB_MAXCHILDREN];
+       bool wake_enabled;
 
        rhub = xhci_get_rhub(hcd);
        ports = rhub->ports;
        max_ports = rhub->num_ports;
        bus_state = &xhci->bus_state[hcd_index(hcd)];
+       wake_enabled = hcd->self.root_hub->do_remote_wakeup;
 
        spin_lock_irqsave(&xhci->lock, flags);
 
-       if (hcd->self.root_hub->do_remote_wakeup) {
+       if (wake_enabled) {
                if (bus_state->resuming_ports ||        /* USB2 */
                    bus_state->port_remote_wakeup) {    /* USB3 */
                        spin_unlock_irqrestore(&xhci->lock, flags);
@@ -1490,26 +1493,37 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
                        return -EBUSY;
                }
        }
-
-       port_index = max_ports;
+       /*
+        * Prepare ports for suspend, but don't write anything before all ports
+        * are checked and we know bus suspend can proceed
+        */
        bus_state->bus_suspended = 0;
+       port_index = max_ports;
        while (port_index--) {
-               /* suspend the port if the port is not suspended */
                u32 t1, t2;
-               int slot_id;
 
                t1 = readl(ports[port_index]->addr);
                t2 = xhci_port_state_to_neutral(t1);
+               portsc_buf[port_index] = 0;
 
-               if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
-                       xhci_dbg(xhci, "port %d not suspended\n", port_index);
-                       slot_id = xhci_find_slot_id_by_port(hcd, xhci,
-                                       port_index + 1);
-                       if (slot_id) {
+               /* Bail out if a USB3 port has a new device in link training */
+               if ((hcd->speed >= HCD_USB3) &&
+                   (t1 & PORT_PLS_MASK) == XDEV_POLLING) {
+                       bus_state->bus_suspended = 0;
+                       spin_unlock_irqrestore(&xhci->lock, flags);
+                       xhci_dbg(xhci, "Bus suspend bailout, port in polling\n");
+                       return -EBUSY;
+               }
+
+               /* suspend ports in U0, or bail out for new connect changes */
+               if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) {
+                       if ((t1 & PORT_CSC) && wake_enabled) {
+                               bus_state->bus_suspended = 0;
                                spin_unlock_irqrestore(&xhci->lock, flags);
-                               xhci_stop_device(xhci, slot_id, 1);
-                               spin_lock_irqsave(&xhci->lock, flags);
+                               xhci_dbg(xhci, "Bus suspend bailout, port connect change\n");
+                               return -EBUSY;
                        }
+                       xhci_dbg(xhci, "port %d not suspended\n", port_index);
                        t2 &= ~PORT_PLS_MASK;
                        t2 |= PORT_LINK_STROBE | XDEV_U3;
                        set_bit(port_index, &bus_state->bus_suspended);
@@ -1518,7 +1532,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
                 * including the USB 3.0 roothub, but only if CONFIG_PM
                 * is enabled, so also enable remote wake here.
                 */
-               if (hcd->self.root_hub->do_remote_wakeup) {
+               if (wake_enabled) {
                        if (t1 & PORT_CONNECT) {
                                t2 |= PORT_WKOC_E | PORT_WKDISC_E;
                                t2 &= ~PORT_WKCONN_E;
@@ -1538,7 +1552,26 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
 
                t1 = xhci_port_state_to_neutral(t1);
                if (t1 != t2)
-                       writel(t2, ports[port_index]->addr);
+                       portsc_buf[port_index] = t2;
+       }
+
+       /* write port settings, stopping and suspending ports if needed */
+       port_index = max_ports;
+       while (port_index--) {
+               if (!portsc_buf[port_index])
+                       continue;
+               if (test_bit(port_index, &bus_state->bus_suspended)) {
+                       int slot_id;
+
+                       slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+                                                           port_index + 1);
+                       if (slot_id) {
+                               spin_unlock_irqrestore(&xhci->lock, flags);
+                               xhci_stop_device(xhci, slot_id, 1);
+                               spin_lock_irqsave(&xhci->lock, flags);
+                       }
+               }
+               writel(portsc_buf[port_index], ports[port_index]->addr);
        }
        hcd->state = HC_STATE_SUSPENDED;
        bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
index 71d0d33..60987c7 100644 (file)
@@ -590,12 +590,14 @@ static int xhci_mtk_remove(struct platform_device *dev)
        struct xhci_hcd_mtk *mtk = platform_get_drvdata(dev);
        struct usb_hcd  *hcd = mtk->hcd;
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       struct usb_hcd  *shared_hcd = xhci->shared_hcd;
 
-       usb_remove_hcd(xhci->shared_hcd);
+       usb_remove_hcd(shared_hcd);
+       xhci->shared_hcd = NULL;
        device_init_wakeup(&dev->dev, false);
 
        usb_remove_hcd(hcd);
-       usb_put_hcd(xhci->shared_hcd);
+       usb_put_hcd(shared_hcd);
        usb_put_hcd(hcd);
        xhci_mtk_sch_exit(mtk);
        xhci_mtk_clks_disable(mtk);
index 01c5705..a9ec705 100644 (file)
@@ -139,6 +139,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
                 pdev->device == 0x43bb))
                xhci->quirks |= XHCI_SUSPEND_DELAY;
 
+       if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+           (pdev->device == 0x15e0 || pdev->device == 0x15e1))
+               xhci->quirks |= XHCI_SNPS_BROKEN_SUSPEND;
+
        if (pdev->vendor == PCI_VENDOR_ID_AMD)
                xhci->quirks |= XHCI_TRUST_TX_LENGTH;
 
@@ -248,6 +252,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
        if (pdev->vendor == PCI_VENDOR_ID_TI && pdev->device == 0x8241)
                xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_7;
 
+       if ((pdev->vendor == PCI_VENDOR_ID_BROADCOM ||
+            pdev->vendor == PCI_VENDOR_ID_CAVIUM) &&
+            pdev->device == 0x9026)
+               xhci->quirks |= XHCI_RESET_PLL_ON_DISCONNECT;
+
        if (xhci->quirks & XHCI_RESET_ON_RESUME)
                xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
                                "QUIRK: Resetting on resume");
@@ -380,6 +389,7 @@ static void xhci_pci_remove(struct pci_dev *dev)
        if (xhci->shared_hcd) {
                usb_remove_hcd(xhci->shared_hcd);
                usb_put_hcd(xhci->shared_hcd);
+               xhci->shared_hcd = NULL;
        }
 
        /* Workaround for spurious wakeups at shutdown with HSW */
index 32b5574..ef09cb0 100644 (file)
@@ -362,14 +362,16 @@ static int xhci_plat_remove(struct platform_device *dev)
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        struct clk *clk = xhci->clk;
        struct clk *reg_clk = xhci->reg_clk;
+       struct usb_hcd *shared_hcd = xhci->shared_hcd;
 
        xhci->xhc_state |= XHCI_STATE_REMOVING;
 
-       usb_remove_hcd(xhci->shared_hcd);
+       usb_remove_hcd(shared_hcd);
+       xhci->shared_hcd = NULL;
        usb_phy_shutdown(hcd->usb_phy);
 
        usb_remove_hcd(hcd);
-       usb_put_hcd(xhci->shared_hcd);
+       usb_put_hcd(shared_hcd);
 
        clk_disable_unprepare(clk);
        clk_disable_unprepare(reg_clk);
index a8d92c9..6575058 100644 (file)
@@ -1521,6 +1521,35 @@ static void handle_device_notification(struct xhci_hcd *xhci,
                usb_wakeup_notification(udev->parent, udev->portnum);
 }
 
+/*
+ * Quirk hanlder for errata seen on Cavium ThunderX2 processor XHCI
+ * Controller.
+ * As per ThunderX2errata-129 USB 2 device may come up as USB 1
+ * If a connection to a USB 1 device is followed by another connection
+ * to a USB 2 device.
+ *
+ * Reset the PHY after the USB device is disconnected if device speed
+ * is less than HCD_USB3.
+ * Retry the reset sequence max of 4 times checking the PLL lock status.
+ *
+ */
+static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci)
+{
+       struct usb_hcd *hcd = xhci_to_hcd(xhci);
+       u32 pll_lock_check;
+       u32 retry_count = 4;
+
+       do {
+               /* Assert PHY reset */
+               writel(0x6F, hcd->regs + 0x1048);
+               udelay(10);
+               /* De-assert the PHY reset */
+               writel(0x7F, hcd->regs + 0x1048);
+               udelay(200);
+               pll_lock_check = readl(hcd->regs + 0x1070);
+       } while (!(pll_lock_check & 0x1) && --retry_count);
+}
+
 static void handle_port_status(struct xhci_hcd *xhci,
                union xhci_trb *event)
 {
@@ -1556,6 +1585,13 @@ static void handle_port_status(struct xhci_hcd *xhci,
                goto cleanup;
        }
 
+       /* We might get interrupts after shared_hcd is removed */
+       if (port->rhub == &xhci->usb3_rhub && xhci->shared_hcd == NULL) {
+               xhci_dbg(xhci, "ignore port event for removed USB3 hcd\n");
+               bogus_port_status = true;
+               goto cleanup;
+       }
+
        hcd = port->rhub->hcd;
        bus_state = &xhci->bus_state[hcd_index(hcd)];
        hcd_portnum = port->hcd_portnum;
@@ -1639,7 +1675,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
         * RExit to a disconnect state).  If so, let the the driver know it's
         * out of the RExit state.
         */
-       if (!DEV_SUPERSPEED_ANY(portsc) &&
+       if (!DEV_SUPERSPEED_ANY(portsc) && hcd->speed < HCD_USB3 &&
                        test_and_clear_bit(hcd_portnum,
                                &bus_state->rexit_ports)) {
                complete(&bus_state->rexit_done[hcd_portnum]);
@@ -1647,8 +1683,12 @@ static void handle_port_status(struct xhci_hcd *xhci,
                goto cleanup;
        }
 
-       if (hcd->speed < HCD_USB3)
+       if (hcd->speed < HCD_USB3) {
                xhci_test_and_clear_bit(xhci, port, PORT_PLC);
+               if ((xhci->quirks & XHCI_RESET_PLL_ON_DISCONNECT) &&
+                   (portsc & PORT_CSC) && !(portsc & PORT_CONNECT))
+                       xhci_cavium_reset_phy_quirk(xhci);
+       }
 
 cleanup:
        /* Update event ring dequeue pointer before dropping the lock */
@@ -2266,6 +2306,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
                        goto cleanup;
                case COMP_RING_UNDERRUN:
                case COMP_RING_OVERRUN:
+               case COMP_STOPPED_LENGTH_INVALID:
                        goto cleanup;
                default:
                        xhci_err(xhci, "ERROR Transfer event for unknown stream ring slot %u ep %u\n",
index 6b5db34..938ff06 100644 (file)
@@ -1303,6 +1303,7 @@ static int tegra_xusb_remove(struct platform_device *pdev)
 
        usb_remove_hcd(xhci->shared_hcd);
        usb_put_hcd(xhci->shared_hcd);
+       xhci->shared_hcd = NULL;
        usb_remove_hcd(tegra->hcd);
        usb_put_hcd(tegra->hcd);
 
index 0420eef..dae3be1 100644 (file)
@@ -719,8 +719,6 @@ static void xhci_stop(struct usb_hcd *hcd)
 
        /* Only halt host and free memory after both hcds are removed */
        if (!usb_hcd_is_primary_hcd(hcd)) {
-               /* usb core will free this hcd shortly, unset pointer */
-               xhci->shared_hcd = NULL;
                mutex_unlock(&xhci->mutex);
                return;
        }
@@ -970,6 +968,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
        unsigned int            delay = XHCI_MAX_HALT_USEC;
        struct usb_hcd          *hcd = xhci_to_hcd(xhci);
        u32                     command;
+       u32                     res;
 
        if (!hcd->state)
                return 0;
@@ -1023,11 +1022,28 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
        command = readl(&xhci->op_regs->command);
        command |= CMD_CSS;
        writel(command, &xhci->op_regs->command);
+       xhci->broken_suspend = 0;
        if (xhci_handshake(&xhci->op_regs->status,
                                STS_SAVE, 0, 10 * 1000)) {
-               xhci_warn(xhci, "WARN: xHC save state timeout\n");
-               spin_unlock_irq(&xhci->lock);
-               return -ETIMEDOUT;
+       /*
+        * AMD SNPS xHC 3.0 occasionally does not clear the
+        * SSS bit of USBSTS and when driver tries to poll
+        * to see if the xHC clears BIT(8) which never happens
+        * and driver assumes that controller is not responding
+        * and times out. To workaround this, its good to check
+        * if SRE and HCE bits are not set (as per xhci
+        * Section 5.4.2) and bypass the timeout.
+        */
+               res = readl(&xhci->op_regs->status);
+               if ((xhci->quirks & XHCI_SNPS_BROKEN_SUSPEND) &&
+                   (((res & STS_SRE) == 0) &&
+                               ((res & STS_HCE) == 0))) {
+                       xhci->broken_suspend = 1;
+               } else {
+                       xhci_warn(xhci, "WARN: xHC save state timeout\n");
+                       spin_unlock_irq(&xhci->lock);
+                       return -ETIMEDOUT;
+               }
        }
        spin_unlock_irq(&xhci->lock);
 
@@ -1080,7 +1096,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
        set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
 
        spin_lock_irq(&xhci->lock);
-       if (xhci->quirks & XHCI_RESET_ON_RESUME)
+       if ((xhci->quirks & XHCI_RESET_ON_RESUME) || xhci->broken_suspend)
                hibernated = true;
 
        if (!hibernated) {
@@ -4498,6 +4514,14 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
 {
        unsigned long long timeout_ns;
 
+       /* Prevent U1 if service interval is shorter than U1 exit latency */
+       if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) {
+               if (xhci_service_interval_to_ns(desc) <= udev->u1_params.mel) {
+                       dev_dbg(&udev->dev, "Disable U1, ESIT shorter than exit latency\n");
+                       return USB3_LPM_DISABLED;
+               }
+       }
+
        if (xhci->quirks & XHCI_INTEL_HOST)
                timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
        else
@@ -4554,6 +4578,14 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
 {
        unsigned long long timeout_ns;
 
+       /* Prevent U2 if service interval is shorter than U2 exit latency */
+       if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) {
+               if (xhci_service_interval_to_ns(desc) <= udev->u2_params.mel) {
+                       dev_dbg(&udev->dev, "Disable U2, ESIT shorter than exit latency\n");
+                       return USB3_LPM_DISABLED;
+               }
+       }
+
        if (xhci->quirks & XHCI_INTEL_HOST)
                timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
        else
index bf0b369..011dd45 100644 (file)
@@ -1680,7 +1680,7 @@ struct xhci_bus_state {
  * It can take up to 20 ms to transition from RExit to U0 on the
  * Intel Lynx Point LP xHCI host.
  */
-#define        XHCI_MAX_REXIT_TIMEOUT  (20 * 1000)
+#define        XHCI_MAX_REXIT_TIMEOUT_MS       20
 
 static inline unsigned int hcd_index(struct usb_hcd *hcd)
 {
@@ -1849,6 +1849,8 @@ struct xhci_hcd {
 #define XHCI_INTEL_USB_ROLE_SW BIT_ULL(31)
 #define XHCI_ZERO_64B_REGS     BIT_ULL(32)
 #define XHCI_DEFAULT_PM_RUNTIME_ALLOW  BIT_ULL(33)
+#define XHCI_RESET_PLL_ON_DISCONNECT   BIT_ULL(34)
+#define XHCI_SNPS_BROKEN_SUSPEND    BIT_ULL(35)
 
        unsigned int            num_active_eps;
        unsigned int            limit_active_eps;
@@ -1861,6 +1863,8 @@ struct xhci_hcd {
        unsigned                sw_lpm_support:1;
        /* support xHCI 1.0 spec USB2 hardware LPM */
        unsigned                hw_lpm_support:1;
+       /* Broken Suspend flag for SNPS Suspend resume issue */
+       unsigned                broken_suspend:1;
        /* cached usb2 extened protocol capabilites */
        u32                     *ext_caps;
        unsigned int            num_ext_caps;
index bd539f3..39ca31b 100644 (file)
@@ -50,6 +50,8 @@ static const struct usb_device_id appledisplay_table[] = {
        { APPLEDISPLAY_DEVICE(0x9219) },
        { APPLEDISPLAY_DEVICE(0x921c) },
        { APPLEDISPLAY_DEVICE(0x921d) },
+       { APPLEDISPLAY_DEVICE(0x9222) },
+       { APPLEDISPLAY_DEVICE(0x9226) },
        { APPLEDISPLAY_DEVICE(0x9236) },
 
        /* Terminating entry */
index 1794058..7d28930 100644 (file)
@@ -101,7 +101,6 @@ static int usb_console_setup(struct console *co, char *options)
                cflag |= PARENB;
                break;
        }
-       co->cflag = cflag;
 
        /*
         * no need to check the index here: if the index is wrong, console
@@ -164,6 +163,7 @@ static int usb_console_setup(struct console *co, char *options)
                        serial->type->set_termios(tty, port, &dummy);
 
                        tty_port_tty_set(&port->port, NULL);
+                       tty_save_termios(tty);
                        tty_kref_put(tty);
                }
                tty_port_set_initialized(&port->port, 1);
index e24ff16..1ce27f3 100644 (file)
@@ -1164,6 +1164,10 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1213, 0xff) },
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1214),
          .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
+       { USB_DEVICE(TELIT_VENDOR_ID, 0x1900),                          /* Telit LN940 (QMI) */
+         .driver_info = NCTRL(0) | RSVD(1) },
+       { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1901, 0xff),    /* Telit LN940 (MBIM) */
+         .driver_info = NCTRL(0) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff),
          .driver_info = RSVD(1) },
@@ -1328,6 +1332,7 @@ static const struct usb_device_id option_ids[] = {
          .driver_info = RSVD(4) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0414, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0417, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_INTERFACE_CLASS(ZTE_VENDOR_ID, 0x0602, 0xff) },    /* GosunCn ZTE WeLink ME3630 (MBIM mode) */
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
          .driver_info = RSVD(4) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
@@ -1531,6 +1536,7 @@ static const struct usb_device_id option_ids[] = {
          .driver_info = RSVD(2) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff),  /* Telewell TW-LTE 4G v2 */
          .driver_info = RSVD(2) },
+       { USB_DEVICE_INTERFACE_CLASS(ZTE_VENDOR_ID, 0x1476, 0xff) },    /* GosunCn ZTE WeLink ME3630 (ECM/NCM mode) */
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1533, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1534, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1535, 0xff, 0xff, 0xff) },
@@ -1758,6 +1764,7 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) },
        { USB_DEVICE(ALINK_VENDOR_ID, SIMCOM_PRODUCT_SIM7100E),
          .driver_info = RSVD(5) | RSVD(6) },
+       { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) },   /* Simcom SIM7500/SIM7600 MBIM mode */
        { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200),
          .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) },
        { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D),
@@ -1940,7 +1947,14 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD200, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_6802, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD300, 0xff, 0xff, 0xff) },
-       { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 0xff, 0xff, 0xff) }, /* HP lt2523 (Novatel E371) */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 0xff, 0xff, 0xff) },    /* HP lt2523 (Novatel E371) */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x10) },    /* HP lt4132 (Huawei ME906s-158) */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x12) },
+       { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x13) },
+       { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x14) },
+       { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x1b) },
+       { USB_DEVICE(0x1508, 0x1001),                                           /* Fibocom NL668 */
+         .driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
        { } /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, option_ids);
index d17cd95..6b2140f 100644 (file)
@@ -27,4 +27,14 @@ UNUSUAL_DEV(0x0bda, 0x0159, 0x0000, 0x9999,
                "USB Card Reader",
                USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0),
 
+UNUSUAL_DEV(0x0bda, 0x0177, 0x0000, 0x9999,
+               "Realtek",
+               "USB Card Reader",
+               USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0),
+
+UNUSUAL_DEV(0x0bda, 0x0184, 0x0000, 0x9999,
+               "Realtek",
+               "USB Card Reader",
+               USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0),
+
 #endif  /* defined(CONFIG_USB_STORAGE_REALTEK) || ... */
index ab11b2b..02b699a 100644 (file)
@@ -141,6 +141,10 @@ struct vhost_net {
        unsigned tx_zcopy_err;
        /* Flush in progress. Protected by tx vq lock. */
        bool tx_flush;
+       /* Private page frag */
+       struct page_frag page_frag;
+       /* Refcount bias of page frag */
+       int refcnt_bias;
 };
 
 static unsigned vhost_net_zcopy_mask __read_mostly;
@@ -513,7 +517,13 @@ static void vhost_net_busy_poll(struct vhost_net *net,
        struct socket *sock;
        struct vhost_virtqueue *vq = poll_rx ? tvq : rvq;
 
-       mutex_lock_nested(&vq->mutex, poll_rx ? VHOST_NET_VQ_TX: VHOST_NET_VQ_RX);
+       /* Try to hold the vq mutex of the paired virtqueue. We can't
+        * use mutex_lock() here since we could not guarantee a
+        * consistenet lock ordering.
+        */
+       if (!mutex_trylock(&vq->mutex))
+               return;
+
        vhost_disable_notify(&net->dev, vq);
        sock = rvq->private_data;
 
@@ -637,14 +647,53 @@ static bool tx_can_batch(struct vhost_virtqueue *vq, size_t total_len)
               !vhost_vq_avail_empty(vq->dev, vq);
 }
 
+#define SKB_FRAG_PAGE_ORDER     get_order(32768)
+
+static bool vhost_net_page_frag_refill(struct vhost_net *net, unsigned int sz,
+                                      struct page_frag *pfrag, gfp_t gfp)
+{
+       if (pfrag->page) {
+               if (pfrag->offset + sz <= pfrag->size)
+                       return true;
+               __page_frag_cache_drain(pfrag->page, net->refcnt_bias);
+       }
+
+       pfrag->offset = 0;
+       net->refcnt_bias = 0;
+       if (SKB_FRAG_PAGE_ORDER) {
+               /* Avoid direct reclaim but allow kswapd to wake */
+               pfrag->page = alloc_pages((gfp & ~__GFP_DIRECT_RECLAIM) |
+                                         __GFP_COMP | __GFP_NOWARN |
+                                         __GFP_NORETRY,
+                                         SKB_FRAG_PAGE_ORDER);
+               if (likely(pfrag->page)) {
+                       pfrag->size = PAGE_SIZE << SKB_FRAG_PAGE_ORDER;
+                       goto done;
+               }
+       }
+       pfrag->page = alloc_page(gfp);
+       if (likely(pfrag->page)) {
+               pfrag->size = PAGE_SIZE;
+               goto done;
+       }
+       return false;
+
+done:
+       net->refcnt_bias = USHRT_MAX;
+       page_ref_add(pfrag->page, USHRT_MAX - 1);
+       return true;
+}
+
 #define VHOST_NET_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD)
 
 static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
                               struct iov_iter *from)
 {
        struct vhost_virtqueue *vq = &nvq->vq;
+       struct vhost_net *net = container_of(vq->dev, struct vhost_net,
+                                            dev);
        struct socket *sock = vq->private_data;
-       struct page_frag *alloc_frag = &current->task_frag;
+       struct page_frag *alloc_frag = &net->page_frag;
        struct virtio_net_hdr *gso;
        struct xdp_buff *xdp = &nvq->xdp[nvq->batched_xdp];
        struct tun_xdp_hdr *hdr;
@@ -665,7 +714,8 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
 
        buflen += SKB_DATA_ALIGN(len + pad);
        alloc_frag->offset = ALIGN((u64)alloc_frag->offset, SMP_CACHE_BYTES);
-       if (unlikely(!skb_page_frag_refill(buflen, alloc_frag, GFP_KERNEL)))
+       if (unlikely(!vhost_net_page_frag_refill(net, buflen,
+                                                alloc_frag, GFP_KERNEL)))
                return -ENOMEM;
 
        buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset;
@@ -703,7 +753,7 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
        xdp->data_end = xdp->data + len;
        hdr->buflen = buflen;
 
-       get_page(alloc_frag->page);
+       --net->refcnt_bias;
        alloc_frag->offset += buflen;
 
        ++nvq->batched_xdp;
@@ -1292,6 +1342,8 @@ static int vhost_net_open(struct inode *inode, struct file *f)
        vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, EPOLLIN, dev);
 
        f->private_data = n;
+       n->page_frag.page = NULL;
+       n->refcnt_bias = 0;
 
        return 0;
 }
@@ -1366,6 +1418,8 @@ static int vhost_net_release(struct inode *inode, struct file *f)
        kfree(n->vqs[VHOST_NET_VQ_RX].rxq.queue);
        kfree(n->vqs[VHOST_NET_VQ_TX].xdp);
        kfree(n->dev.vqs);
+       if (n->page_frag.page)
+               __page_frag_cache_drain(n->page_frag.page, n->refcnt_bias);
        kvfree(n);
        return 0;
 }
index 3a5f81a..55e5aa6 100644 (file)
@@ -295,11 +295,8 @@ static void vhost_vq_meta_reset(struct vhost_dev *d)
 {
        int i;
 
-       for (i = 0; i < d->nvqs; ++i) {
-               mutex_lock(&d->vqs[i]->mutex);
+       for (i = 0; i < d->nvqs; ++i)
                __vhost_vq_meta_reset(d->vqs[i]);
-               mutex_unlock(&d->vqs[i]->mutex);
-       }
 }
 
 static void vhost_vq_reset(struct vhost_dev *dev,
@@ -895,6 +892,20 @@ static inline void __user *__vhost_get_user(struct vhost_virtqueue *vq,
 #define vhost_get_used(vq, x, ptr) \
        vhost_get_user(vq, x, ptr, VHOST_ADDR_USED)
 
+static void vhost_dev_lock_vqs(struct vhost_dev *d)
+{
+       int i = 0;
+       for (i = 0; i < d->nvqs; ++i)
+               mutex_lock_nested(&d->vqs[i]->mutex, i);
+}
+
+static void vhost_dev_unlock_vqs(struct vhost_dev *d)
+{
+       int i = 0;
+       for (i = 0; i < d->nvqs; ++i)
+               mutex_unlock(&d->vqs[i]->mutex);
+}
+
 static int vhost_new_umem_range(struct vhost_umem *umem,
                                u64 start, u64 size, u64 end,
                                u64 userspace_addr, int perm)
@@ -944,10 +955,7 @@ static void vhost_iotlb_notify_vq(struct vhost_dev *d,
                if (msg->iova <= vq_msg->iova &&
                    msg->iova + msg->size - 1 >= vq_msg->iova &&
                    vq_msg->type == VHOST_IOTLB_MISS) {
-                       mutex_lock(&node->vq->mutex);
                        vhost_poll_queue(&node->vq->poll);
-                       mutex_unlock(&node->vq->mutex);
-
                        list_del(&node->node);
                        kfree(node);
                }
@@ -979,6 +987,7 @@ static int vhost_process_iotlb_msg(struct vhost_dev *dev,
        int ret = 0;
 
        mutex_lock(&dev->mutex);
+       vhost_dev_lock_vqs(dev);
        switch (msg->type) {
        case VHOST_IOTLB_UPDATE:
                if (!dev->iotlb) {
@@ -1012,6 +1021,7 @@ static int vhost_process_iotlb_msg(struct vhost_dev *dev,
                break;
        }
 
+       vhost_dev_unlock_vqs(dev);
        mutex_unlock(&dev->mutex);
 
        return ret;
@@ -2223,6 +2233,8 @@ int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads,
                return -EFAULT;
        }
        if (unlikely(vq->log_used)) {
+               /* Make sure used idx is seen before log. */
+               smp_wmb();
                /* Log used index update. */
                log_write(vq->log_base,
                          vq->log_addr + offsetof(struct vring_used, idx),
index 34bc3ab..98ed5be 100644 (file)
@@ -15,6 +15,7 @@
 #include <net/sock.h>
 #include <linux/virtio_vsock.h>
 #include <linux/vhost.h>
+#include <linux/hashtable.h>
 
 #include <net/af_vsock.h>
 #include "vhost.h"
@@ -27,14 +28,14 @@ enum {
 
 /* Used to track all the vhost_vsock instances on the system. */
 static DEFINE_SPINLOCK(vhost_vsock_lock);
-static LIST_HEAD(vhost_vsock_list);
+static DEFINE_READ_MOSTLY_HASHTABLE(vhost_vsock_hash, 8);
 
 struct vhost_vsock {
        struct vhost_dev dev;
        struct vhost_virtqueue vqs[2];
 
-       /* Link to global vhost_vsock_list, protected by vhost_vsock_lock */
-       struct list_head list;
+       /* Link to global vhost_vsock_hash, writes use vhost_vsock_lock */
+       struct hlist_node hash;
 
        struct vhost_work send_pkt_work;
        spinlock_t send_pkt_list_lock;
@@ -50,11 +51,14 @@ static u32 vhost_transport_get_local_cid(void)
        return VHOST_VSOCK_DEFAULT_HOST_CID;
 }
 
-static struct vhost_vsock *__vhost_vsock_get(u32 guest_cid)
+/* Callers that dereference the return value must hold vhost_vsock_lock or the
+ * RCU read lock.
+ */
+static struct vhost_vsock *vhost_vsock_get(u32 guest_cid)
 {
        struct vhost_vsock *vsock;
 
-       list_for_each_entry(vsock, &vhost_vsock_list, list) {
+       hash_for_each_possible_rcu(vhost_vsock_hash, vsock, hash, guest_cid) {
                u32 other_cid = vsock->guest_cid;
 
                /* Skip instances that have no CID yet */
@@ -69,17 +73,6 @@ static struct vhost_vsock *__vhost_vsock_get(u32 guest_cid)
        return NULL;
 }
 
-static struct vhost_vsock *vhost_vsock_get(u32 guest_cid)
-{
-       struct vhost_vsock *vsock;
-
-       spin_lock_bh(&vhost_vsock_lock);
-       vsock = __vhost_vsock_get(guest_cid);
-       spin_unlock_bh(&vhost_vsock_lock);
-
-       return vsock;
-}
-
 static void
 vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
                            struct vhost_virtqueue *vq)
@@ -210,9 +203,12 @@ vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt)
        struct vhost_vsock *vsock;
        int len = pkt->len;
 
+       rcu_read_lock();
+
        /* Find the vhost_vsock according to guest context id  */
        vsock = vhost_vsock_get(le64_to_cpu(pkt->hdr.dst_cid));
        if (!vsock) {
+               rcu_read_unlock();
                virtio_transport_free_pkt(pkt);
                return -ENODEV;
        }
@@ -225,6 +221,8 @@ vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt)
        spin_unlock_bh(&vsock->send_pkt_list_lock);
 
        vhost_work_queue(&vsock->dev, &vsock->send_pkt_work);
+
+       rcu_read_unlock();
        return len;
 }
 
@@ -234,12 +232,15 @@ vhost_transport_cancel_pkt(struct vsock_sock *vsk)
        struct vhost_vsock *vsock;
        struct virtio_vsock_pkt *pkt, *n;
        int cnt = 0;
+       int ret = -ENODEV;
        LIST_HEAD(freeme);
 
+       rcu_read_lock();
+
        /* Find the vhost_vsock according to guest context id  */
        vsock = vhost_vsock_get(vsk->remote_addr.svm_cid);
        if (!vsock)
-               return -ENODEV;
+               goto out;
 
        spin_lock_bh(&vsock->send_pkt_list_lock);
        list_for_each_entry_safe(pkt, n, &vsock->send_pkt_list, list) {
@@ -265,7 +266,10 @@ vhost_transport_cancel_pkt(struct vsock_sock *vsk)
                        vhost_poll_queue(&tx_vq->poll);
        }
 
-       return 0;
+       ret = 0;
+out:
+       rcu_read_unlock();
+       return ret;
 }
 
 static struct virtio_vsock_pkt *
@@ -533,10 +537,6 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
        spin_lock_init(&vsock->send_pkt_list_lock);
        INIT_LIST_HEAD(&vsock->send_pkt_list);
        vhost_work_init(&vsock->send_pkt_work, vhost_transport_send_pkt_work);
-
-       spin_lock_bh(&vhost_vsock_lock);
-       list_add_tail(&vsock->list, &vhost_vsock_list);
-       spin_unlock_bh(&vhost_vsock_lock);
        return 0;
 
 out:
@@ -563,13 +563,21 @@ static void vhost_vsock_reset_orphans(struct sock *sk)
         * executing.
         */
 
-       if (!vhost_vsock_get(vsk->remote_addr.svm_cid)) {
-               sock_set_flag(sk, SOCK_DONE);
-               vsk->peer_shutdown = SHUTDOWN_MASK;
-               sk->sk_state = SS_UNCONNECTED;
-               sk->sk_err = ECONNRESET;
-               sk->sk_error_report(sk);
-       }
+       /* If the peer is still valid, no need to reset connection */
+       if (vhost_vsock_get(vsk->remote_addr.svm_cid))
+               return;
+
+       /* If the close timeout is pending, let it expire.  This avoids races
+        * with the timeout callback.
+        */
+       if (vsk->close_work_scheduled)
+               return;
+
+       sock_set_flag(sk, SOCK_DONE);
+       vsk->peer_shutdown = SHUTDOWN_MASK;
+       sk->sk_state = SS_UNCONNECTED;
+       sk->sk_err = ECONNRESET;
+       sk->sk_error_report(sk);
 }
 
 static int vhost_vsock_dev_release(struct inode *inode, struct file *file)
@@ -577,9 +585,13 @@ static int vhost_vsock_dev_release(struct inode *inode, struct file *file)
        struct vhost_vsock *vsock = file->private_data;
 
        spin_lock_bh(&vhost_vsock_lock);
-       list_del(&vsock->list);
+       if (vsock->guest_cid)
+               hash_del_rcu(&vsock->hash);
        spin_unlock_bh(&vhost_vsock_lock);
 
+       /* Wait for other CPUs to finish using vsock */
+       synchronize_rcu();
+
        /* Iterating over all connections for all CIDs to find orphans is
         * inefficient.  Room for improvement here. */
        vsock_for_each_connected_socket(vhost_vsock_reset_orphans);
@@ -620,12 +632,17 @@ static int vhost_vsock_set_cid(struct vhost_vsock *vsock, u64 guest_cid)
 
        /* Refuse if CID is already in use */
        spin_lock_bh(&vhost_vsock_lock);
-       other = __vhost_vsock_get(guest_cid);
+       other = vhost_vsock_get(guest_cid);
        if (other && other != vsock) {
                spin_unlock_bh(&vhost_vsock_lock);
                return -EADDRINUSE;
        }
+
+       if (vsock->guest_cid)
+               hash_del_rcu(&vsock->hash);
+
        vsock->guest_cid = guest_cid;
+       hash_add_rcu(vhost_vsock_hash, &vsock->hash, guest_cid);
        spin_unlock_bh(&vhost_vsock_lock);
 
        return 0;
index 678b270..f9ef067 100644 (file)
@@ -562,7 +562,30 @@ static int pwm_backlight_probe(struct platform_device *pdev)
                goto err_alloc;
        }
 
-       if (!data->levels) {
+       if (data->levels) {
+               /*
+                * For the DT case, only when brightness levels is defined
+                * data->levels is filled. For the non-DT case, data->levels
+                * can come from platform data, however is not usual.
+                */
+               for (i = 0; i <= data->max_brightness; i++) {
+                       if (data->levels[i] > pb->scale)
+                               pb->scale = data->levels[i];
+
+                       pb->levels = data->levels;
+               }
+       } else if (!data->max_brightness) {
+               /*
+                * If no brightness levels are provided and max_brightness is
+                * not set, use the default brightness table. For the DT case,
+                * max_brightness is set to 0 when brightness levels is not
+                * specified. For the non-DT case, max_brightness is usually
+                * set to some value.
+                */
+
+               /* Get the PWM period (in nanoseconds) */
+               pwm_get_state(pb->pwm, &state);
+
                ret = pwm_backlight_brightness_default(&pdev->dev, data,
                                                       state.period);
                if (ret < 0) {
@@ -570,13 +593,19 @@ static int pwm_backlight_probe(struct platform_device *pdev)
                                "failed to setup default brightness table\n");
                        goto err_alloc;
                }
-       }
 
-       for (i = 0; i <= data->max_brightness; i++) {
-               if (data->levels[i] > pb->scale)
-                       pb->scale = data->levels[i];
+               for (i = 0; i <= data->max_brightness; i++) {
+                       if (data->levels[i] > pb->scale)
+                               pb->scale = data->levels[i];
 
-               pb->levels = data->levels;
+                       pb->levels = data->levels;
+               }
+       } else {
+               /*
+                * That only happens for the non-DT case, where platform data
+                * sets the max_brightness value.
+                */
+               pb->scale = data->max_brightness;
        }
 
        pb->lth_brightness = data->lth_brightness * (state.period / pb->scale);
index 814b395..cd7e755 100644 (file)
        } while (0)
 #define END_USE(_vq) \
        do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; } while(0)
+#define LAST_ADD_TIME_UPDATE(_vq)                              \
+       do {                                                    \
+               ktime_t now = ktime_get();                      \
+                                                               \
+               /* No kick or get, with .1 second between?  Warn. */ \
+               if ((_vq)->last_add_time_valid)                 \
+                       WARN_ON(ktime_to_ms(ktime_sub(now,      \
+                               (_vq)->last_add_time)) > 100);  \
+               (_vq)->last_add_time = now;                     \
+               (_vq)->last_add_time_valid = true;              \
+       } while (0)
+#define LAST_ADD_TIME_CHECK(_vq)                               \
+       do {                                                    \
+               if ((_vq)->last_add_time_valid) {               \
+                       WARN_ON(ktime_to_ms(ktime_sub(ktime_get(), \
+                                     (_vq)->last_add_time)) > 100); \
+               }                                               \
+       } while (0)
+#define LAST_ADD_TIME_INVALID(_vq)                             \
+       ((_vq)->last_add_time_valid = false)
 #else
 #define BAD_RING(_vq, fmt, args...)                            \
        do {                                                    \
        } while (0)
 #define START_USE(vq)
 #define END_USE(vq)
+#define LAST_ADD_TIME_UPDATE(vq)
+#define LAST_ADD_TIME_CHECK(vq)
+#define LAST_ADD_TIME_INVALID(vq)
 #endif
 
-struct vring_desc_state {
+struct vring_desc_state_split {
        void *data;                     /* Data for callback. */
        struct vring_desc *indir_desc;  /* Indirect descriptor, if any. */
 };
 
+struct vring_desc_state_packed {
+       void *data;                     /* Data for callback. */
+       struct vring_packed_desc *indir_desc; /* Indirect descriptor, if any. */
+       u16 num;                        /* Descriptor list length. */
+       u16 next;                       /* The next desc state in a list. */
+       u16 last;                       /* The last desc state in a list. */
+};
+
+struct vring_desc_extra_packed {
+       dma_addr_t addr;                /* Buffer DMA addr. */
+       u32 len;                        /* Buffer length. */
+       u16 flags;                      /* Descriptor flags. */
+};
+
 struct vring_virtqueue {
        struct virtqueue vq;
 
-       /* Actual memory layout for this queue */
-       struct vring vring;
+       /* Is this a packed ring? */
+       bool packed_ring;
+
+       /* Is DMA API used? */
+       bool use_dma_api;
 
        /* Can we use weak barriers? */
        bool weak_barriers;
@@ -86,19 +126,70 @@ struct vring_virtqueue {
        /* Last used index we've seen. */
        u16 last_used_idx;
 
-       /* Last written value to avail->flags */
-       u16 avail_flags_shadow;
+       union {
+               /* Available for split ring */
+               struct {
+                       /* Actual memory layout for this queue. */
+                       struct vring vring;
+
+                       /* Last written value to avail->flags */
+                       u16 avail_flags_shadow;
+
+                       /*
+                        * Last written value to avail->idx in
+                        * guest byte order.
+                        */
+                       u16 avail_idx_shadow;
+
+                       /* Per-descriptor state. */
+                       struct vring_desc_state_split *desc_state;
 
-       /* Last written value to avail->idx in guest byte order */
-       u16 avail_idx_shadow;
+                       /* DMA address and size information */
+                       dma_addr_t queue_dma_addr;
+                       size_t queue_size_in_bytes;
+               } split;
+
+               /* Available for packed ring */
+               struct {
+                       /* Actual memory layout for this queue. */
+                       struct vring_packed vring;
+
+                       /* Driver ring wrap counter. */
+                       bool avail_wrap_counter;
+
+                       /* Device ring wrap counter. */
+                       bool used_wrap_counter;
+
+                       /* Avail used flags. */
+                       u16 avail_used_flags;
+
+                       /* Index of the next avail descriptor. */
+                       u16 next_avail_idx;
+
+                       /*
+                        * Last written value to driver->flags in
+                        * guest byte order.
+                        */
+                       u16 event_flags_shadow;
+
+                       /* Per-descriptor state. */
+                       struct vring_desc_state_packed *desc_state;
+                       struct vring_desc_extra_packed *desc_extra;
+
+                       /* DMA address and size information */
+                       dma_addr_t ring_dma_addr;
+                       dma_addr_t driver_event_dma_addr;
+                       dma_addr_t device_event_dma_addr;
+                       size_t ring_size_in_bytes;
+                       size_t event_size_in_bytes;
+               } packed;
+       };
 
        /* How to notify other side. FIXME: commonalize hcalls! */
        bool (*notify)(struct virtqueue *vq);
 
        /* DMA, allocation, and size information */
        bool we_own_ring;
-       size_t queue_size_in_bytes;
-       dma_addr_t queue_dma_addr;
 
 #ifdef DEBUG
        /* They're supposed to lock for us. */
@@ -108,13 +199,27 @@ struct vring_virtqueue {
        bool last_add_time_valid;
        ktime_t last_add_time;
 #endif
-
-       /* Per-descriptor state. */
-       struct vring_desc_state desc_state[];
 };
 
+
+/*
+ * Helpers.
+ */
+
 #define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq)
 
+static inline bool virtqueue_use_indirect(struct virtqueue *_vq,
+                                         unsigned int total_sg)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       /*
+        * If the host supports indirect descriptor tables, and we have multiple
+        * buffers, then go indirect. FIXME: tune this threshold
+        */
+       return (vq->indirect && total_sg > 1 && vq->vq.num_free);
+}
+
 /*
  * Modern virtio devices have feature bits to specify whether they need a
  * quirk and bypass the IOMMU. If not there, just use the DMA API.
@@ -161,6 +266,48 @@ static bool vring_use_dma_api(struct virtio_device *vdev)
        return false;
 }
 
+static void *vring_alloc_queue(struct virtio_device *vdev, size_t size,
+                             dma_addr_t *dma_handle, gfp_t flag)
+{
+       if (vring_use_dma_api(vdev)) {
+               return dma_alloc_coherent(vdev->dev.parent, size,
+                                         dma_handle, flag);
+       } else {
+               void *queue = alloc_pages_exact(PAGE_ALIGN(size), flag);
+
+               if (queue) {
+                       phys_addr_t phys_addr = virt_to_phys(queue);
+                       *dma_handle = (dma_addr_t)phys_addr;
+
+                       /*
+                        * Sanity check: make sure we dind't truncate
+                        * the address.  The only arches I can find that
+                        * have 64-bit phys_addr_t but 32-bit dma_addr_t
+                        * are certain non-highmem MIPS and x86
+                        * configurations, but these configurations
+                        * should never allocate physical pages above 32
+                        * bits, so this is fine.  Just in case, throw a
+                        * warning and abort if we end up with an
+                        * unrepresentable address.
+                        */
+                       if (WARN_ON_ONCE(*dma_handle != phys_addr)) {
+                               free_pages_exact(queue, PAGE_ALIGN(size));
+                               return NULL;
+                       }
+               }
+               return queue;
+       }
+}
+
+static void vring_free_queue(struct virtio_device *vdev, size_t size,
+                            void *queue, dma_addr_t dma_handle)
+{
+       if (vring_use_dma_api(vdev))
+               dma_free_coherent(vdev->dev.parent, size, queue, dma_handle);
+       else
+               free_pages_exact(queue, PAGE_ALIGN(size));
+}
+
 /*
  * The DMA ops on various arches are rather gnarly right now, and
  * making all of the arch DMA ops work on the vring device itself
@@ -176,7 +323,7 @@ static dma_addr_t vring_map_one_sg(const struct vring_virtqueue *vq,
                                   struct scatterlist *sg,
                                   enum dma_data_direction direction)
 {
-       if (!vring_use_dma_api(vq->vq.vdev))
+       if (!vq->use_dma_api)
                return (dma_addr_t)sg_phys(sg);
 
        /*
@@ -193,19 +340,33 @@ static dma_addr_t vring_map_single(const struct vring_virtqueue *vq,
                                   void *cpu_addr, size_t size,
                                   enum dma_data_direction direction)
 {
-       if (!vring_use_dma_api(vq->vq.vdev))
+       if (!vq->use_dma_api)
                return (dma_addr_t)virt_to_phys(cpu_addr);
 
        return dma_map_single(vring_dma_dev(vq),
                              cpu_addr, size, direction);
 }
 
-static void vring_unmap_one(const struct vring_virtqueue *vq,
-                           struct vring_desc *desc)
+static int vring_mapping_error(const struct vring_virtqueue *vq,
+                              dma_addr_t addr)
+{
+       if (!vq->use_dma_api)
+               return 0;
+
+       return dma_mapping_error(vring_dma_dev(vq), addr);
+}
+
+
+/*
+ * Split ring specific functions - *_split().
+ */
+
+static void vring_unmap_one_split(const struct vring_virtqueue *vq,
+                                 struct vring_desc *desc)
 {
        u16 flags;
 
-       if (!vring_use_dma_api(vq->vq.vdev))
+       if (!vq->use_dma_api)
                return;
 
        flags = virtio16_to_cpu(vq->vq.vdev, desc->flags);
@@ -225,17 +386,9 @@ static void vring_unmap_one(const struct vring_virtqueue *vq,
        }
 }
 
-static int vring_mapping_error(const struct vring_virtqueue *vq,
-                              dma_addr_t addr)
-{
-       if (!vring_use_dma_api(vq->vq.vdev))
-               return 0;
-
-       return dma_mapping_error(vring_dma_dev(vq), addr);
-}
-
-static struct vring_desc *alloc_indirect(struct virtqueue *_vq,
-                                        unsigned int total_sg, gfp_t gfp)
+static struct vring_desc *alloc_indirect_split(struct virtqueue *_vq,
+                                              unsigned int total_sg,
+                                              gfp_t gfp)
 {
        struct vring_desc *desc;
        unsigned int i;
@@ -256,14 +409,14 @@ static struct vring_desc *alloc_indirect(struct virtqueue *_vq,
        return desc;
 }
 
-static inline int virtqueue_add(struct virtqueue *_vq,
-                               struct scatterlist *sgs[],
-                               unsigned int total_sg,
-                               unsigned int out_sgs,
-                               unsigned int in_sgs,
-                               void *data,
-                               void *ctx,
-                               gfp_t gfp)
+static inline int virtqueue_add_split(struct virtqueue *_vq,
+                                     struct scatterlist *sgs[],
+                                     unsigned int total_sg,
+                                     unsigned int out_sgs,
+                                     unsigned int in_sgs,
+                                     void *data,
+                                     void *ctx,
+                                     gfp_t gfp)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
        struct scatterlist *sg;
@@ -282,30 +435,17 @@ static inline int virtqueue_add(struct virtqueue *_vq,
                return -EIO;
        }
 
-#ifdef DEBUG
-       {
-               ktime_t now = ktime_get();
-
-               /* No kick or get, with .1 second between?  Warn. */
-               if (vq->last_add_time_valid)
-                       WARN_ON(ktime_to_ms(ktime_sub(now, vq->last_add_time))
-                                           > 100);
-               vq->last_add_time = now;
-               vq->last_add_time_valid = true;
-       }
-#endif
+       LAST_ADD_TIME_UPDATE(vq);
 
        BUG_ON(total_sg == 0);
 
        head = vq->free_head;
 
-       /* If the host supports indirect descriptor tables, and we have multiple
-        * buffers, then go indirect. FIXME: tune this threshold */
-       if (vq->indirect && total_sg > 1 && vq->vq.num_free)
-               desc = alloc_indirect(_vq, total_sg, gfp);
+       if (virtqueue_use_indirect(_vq, total_sg))
+               desc = alloc_indirect_split(_vq, total_sg, gfp);
        else {
                desc = NULL;
-               WARN_ON_ONCE(total_sg > vq->vring.num && !vq->indirect);
+               WARN_ON_ONCE(total_sg > vq->split.vring.num && !vq->indirect);
        }
 
        if (desc) {
@@ -316,7 +456,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
                descs_used = 1;
        } else {
                indirect = false;
-               desc = vq->vring.desc;
+               desc = vq->split.vring.desc;
                i = head;
                descs_used = total_sg;
        }
@@ -372,10 +512,13 @@ static inline int virtqueue_add(struct virtqueue *_vq,
                if (vring_mapping_error(vq, addr))
                        goto unmap_release;
 
-               vq->vring.desc[head].flags = cpu_to_virtio16(_vq->vdev, VRING_DESC_F_INDIRECT);
-               vq->vring.desc[head].addr = cpu_to_virtio64(_vq->vdev, addr);
+               vq->split.vring.desc[head].flags = cpu_to_virtio16(_vq->vdev,
+                               VRING_DESC_F_INDIRECT);
+               vq->split.vring.desc[head].addr = cpu_to_virtio64(_vq->vdev,
+                               addr);
 
-               vq->vring.desc[head].len = cpu_to_virtio32(_vq->vdev, total_sg * sizeof(struct vring_desc));
+               vq->split.vring.desc[head].len = cpu_to_virtio32(_vq->vdev,
+                               total_sg * sizeof(struct vring_desc));
        }
 
        /* We're using some buffers from the free list. */
@@ -383,27 +526,29 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 
        /* Update free pointer */
        if (indirect)
-               vq->free_head = virtio16_to_cpu(_vq->vdev, vq->vring.desc[head].next);
+               vq->free_head = virtio16_to_cpu(_vq->vdev,
+                                       vq->split.vring.desc[head].next);
        else
                vq->free_head = i;
 
        /* Store token and indirect buffer state. */
-       vq->desc_state[head].data = data;
+       vq->split.desc_state[head].data = data;
        if (indirect)
-               vq->desc_state[head].indir_desc = desc;
+               vq->split.desc_state[head].indir_desc = desc;
        else
-               vq->desc_state[head].indir_desc = ctx;
+               vq->split.desc_state[head].indir_desc = ctx;
 
        /* Put entry in available array (but don't update avail->idx until they
         * do sync). */
-       avail = vq->avail_idx_shadow & (vq->vring.num - 1);
-       vq->vring.avail->ring[avail] = cpu_to_virtio16(_vq->vdev, head);
+       avail = vq->split.avail_idx_shadow & (vq->split.vring.num - 1);
+       vq->split.vring.avail->ring[avail] = cpu_to_virtio16(_vq->vdev, head);
 
        /* Descriptors and available array need to be set before we expose the
         * new available array entries. */
        virtio_wmb(vq->weak_barriers);
-       vq->avail_idx_shadow++;
-       vq->vring.avail->idx = cpu_to_virtio16(_vq->vdev, vq->avail_idx_shadow);
+       vq->split.avail_idx_shadow++;
+       vq->split.vring.avail->idx = cpu_to_virtio16(_vq->vdev,
+                                               vq->split.avail_idx_shadow);
        vq->num_added++;
 
        pr_debug("Added buffer head %i to %p\n", head, vq);
@@ -423,8 +568,8 @@ unmap_release:
        for (n = 0; n < total_sg; n++) {
                if (i == err_idx)
                        break;
-               vring_unmap_one(vq, &desc[i]);
-               i = virtio16_to_cpu(_vq->vdev, vq->vring.desc[i].next);
+               vring_unmap_one_split(vq, &desc[i]);
+               i = virtio16_to_cpu(_vq->vdev, vq->split.vring.desc[i].next);
        }
 
        if (indirect)
@@ -434,6 +579,1122 @@ unmap_release:
        return -EIO;
 }
 
+static bool virtqueue_kick_prepare_split(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 new, old;
+       bool needs_kick;
+
+       START_USE(vq);
+       /* We need to expose available array entries before checking avail
+        * event. */
+       virtio_mb(vq->weak_barriers);
+
+       old = vq->split.avail_idx_shadow - vq->num_added;
+       new = vq->split.avail_idx_shadow;
+       vq->num_added = 0;
+
+       LAST_ADD_TIME_CHECK(vq);
+       LAST_ADD_TIME_INVALID(vq);
+
+       if (vq->event) {
+               needs_kick = vring_need_event(virtio16_to_cpu(_vq->vdev,
+                                       vring_avail_event(&vq->split.vring)),
+                                             new, old);
+       } else {
+               needs_kick = !(vq->split.vring.used->flags &
+                                       cpu_to_virtio16(_vq->vdev,
+                                               VRING_USED_F_NO_NOTIFY));
+       }
+       END_USE(vq);
+       return needs_kick;
+}
+
+static void detach_buf_split(struct vring_virtqueue *vq, unsigned int head,
+                            void **ctx)
+{
+       unsigned int i, j;
+       __virtio16 nextflag = cpu_to_virtio16(vq->vq.vdev, VRING_DESC_F_NEXT);
+
+       /* Clear data ptr. */
+       vq->split.desc_state[head].data = NULL;
+
+       /* Put back on free list: unmap first-level descriptors and find end */
+       i = head;
+
+       while (vq->split.vring.desc[i].flags & nextflag) {
+               vring_unmap_one_split(vq, &vq->split.vring.desc[i]);
+               i = virtio16_to_cpu(vq->vq.vdev, vq->split.vring.desc[i].next);
+               vq->vq.num_free++;
+       }
+
+       vring_unmap_one_split(vq, &vq->split.vring.desc[i]);
+       vq->split.vring.desc[i].next = cpu_to_virtio16(vq->vq.vdev,
+                                               vq->free_head);
+       vq->free_head = head;
+
+       /* Plus final descriptor */
+       vq->vq.num_free++;
+
+       if (vq->indirect) {
+               struct vring_desc *indir_desc =
+                               vq->split.desc_state[head].indir_desc;
+               u32 len;
+
+               /* Free the indirect table, if any, now that it's unmapped. */
+               if (!indir_desc)
+                       return;
+
+               len = virtio32_to_cpu(vq->vq.vdev,
+                               vq->split.vring.desc[head].len);
+
+               BUG_ON(!(vq->split.vring.desc[head].flags &
+                        cpu_to_virtio16(vq->vq.vdev, VRING_DESC_F_INDIRECT)));
+               BUG_ON(len == 0 || len % sizeof(struct vring_desc));
+
+               for (j = 0; j < len / sizeof(struct vring_desc); j++)
+                       vring_unmap_one_split(vq, &indir_desc[j]);
+
+               kfree(indir_desc);
+               vq->split.desc_state[head].indir_desc = NULL;
+       } else if (ctx) {
+               *ctx = vq->split.desc_state[head].indir_desc;
+       }
+}
+
+static inline bool more_used_split(const struct vring_virtqueue *vq)
+{
+       return vq->last_used_idx != virtio16_to_cpu(vq->vq.vdev,
+                       vq->split.vring.used->idx);
+}
+
+static void *virtqueue_get_buf_ctx_split(struct virtqueue *_vq,
+                                        unsigned int *len,
+                                        void **ctx)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       void *ret;
+       unsigned int i;
+       u16 last_used;
+
+       START_USE(vq);
+
+       if (unlikely(vq->broken)) {
+               END_USE(vq);
+               return NULL;
+       }
+
+       if (!more_used_split(vq)) {
+               pr_debug("No more buffers in queue\n");
+               END_USE(vq);
+               return NULL;
+       }
+
+       /* Only get used array entries after they have been exposed by host. */
+       virtio_rmb(vq->weak_barriers);
+
+       last_used = (vq->last_used_idx & (vq->split.vring.num - 1));
+       i = virtio32_to_cpu(_vq->vdev,
+                       vq->split.vring.used->ring[last_used].id);
+       *len = virtio32_to_cpu(_vq->vdev,
+                       vq->split.vring.used->ring[last_used].len);
+
+       if (unlikely(i >= vq->split.vring.num)) {
+               BAD_RING(vq, "id %u out of range\n", i);
+               return NULL;
+       }
+       if (unlikely(!vq->split.desc_state[i].data)) {
+               BAD_RING(vq, "id %u is not a head!\n", i);
+               return NULL;
+       }
+
+       /* detach_buf_split clears data, so grab it now. */
+       ret = vq->split.desc_state[i].data;
+       detach_buf_split(vq, i, ctx);
+       vq->last_used_idx++;
+       /* If we expect an interrupt for the next entry, tell host
+        * by writing event index and flush out the write before
+        * the read in the next get_buf call. */
+       if (!(vq->split.avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT))
+               virtio_store_mb(vq->weak_barriers,
+                               &vring_used_event(&vq->split.vring),
+                               cpu_to_virtio16(_vq->vdev, vq->last_used_idx));
+
+       LAST_ADD_TIME_INVALID(vq);
+
+       END_USE(vq);
+       return ret;
+}
+
+static void virtqueue_disable_cb_split(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       if (!(vq->split.avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT)) {
+               vq->split.avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
+               if (!vq->event)
+                       vq->split.vring.avail->flags =
+                               cpu_to_virtio16(_vq->vdev,
+                                               vq->split.avail_flags_shadow);
+       }
+}
+
+static unsigned virtqueue_enable_cb_prepare_split(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 last_used_idx;
+
+       START_USE(vq);
+
+       /* We optimistically turn back on interrupts, then check if there was
+        * more to do. */
+       /* Depending on the VIRTIO_RING_F_EVENT_IDX feature, we need to
+        * either clear the flags bit or point the event index at the next
+        * entry. Always do both to keep code simple. */
+       if (vq->split.avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
+               vq->split.avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
+               if (!vq->event)
+                       vq->split.vring.avail->flags =
+                               cpu_to_virtio16(_vq->vdev,
+                                               vq->split.avail_flags_shadow);
+       }
+       vring_used_event(&vq->split.vring) = cpu_to_virtio16(_vq->vdev,
+                       last_used_idx = vq->last_used_idx);
+       END_USE(vq);
+       return last_used_idx;
+}
+
+static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       return (u16)last_used_idx != virtio16_to_cpu(_vq->vdev,
+                       vq->split.vring.used->idx);
+}
+
+static bool virtqueue_enable_cb_delayed_split(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 bufs;
+
+       START_USE(vq);
+
+       /* We optimistically turn back on interrupts, then check if there was
+        * more to do. */
+       /* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
+        * either clear the flags bit or point the event index at the next
+        * entry. Always update the event index to keep code simple. */
+       if (vq->split.avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
+               vq->split.avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
+               if (!vq->event)
+                       vq->split.vring.avail->flags =
+                               cpu_to_virtio16(_vq->vdev,
+                                               vq->split.avail_flags_shadow);
+       }
+       /* TODO: tune this threshold */
+       bufs = (u16)(vq->split.avail_idx_shadow - vq->last_used_idx) * 3 / 4;
+
+       virtio_store_mb(vq->weak_barriers,
+                       &vring_used_event(&vq->split.vring),
+                       cpu_to_virtio16(_vq->vdev, vq->last_used_idx + bufs));
+
+       if (unlikely((u16)(virtio16_to_cpu(_vq->vdev, vq->split.vring.used->idx)
+                                       - vq->last_used_idx) > bufs)) {
+               END_USE(vq);
+               return false;
+       }
+
+       END_USE(vq);
+       return true;
+}
+
+static void *virtqueue_detach_unused_buf_split(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       unsigned int i;
+       void *buf;
+
+       START_USE(vq);
+
+       for (i = 0; i < vq->split.vring.num; i++) {
+               if (!vq->split.desc_state[i].data)
+                       continue;
+               /* detach_buf_split clears data, so grab it now. */
+               buf = vq->split.desc_state[i].data;
+               detach_buf_split(vq, i, NULL);
+               vq->split.avail_idx_shadow--;
+               vq->split.vring.avail->idx = cpu_to_virtio16(_vq->vdev,
+                               vq->split.avail_idx_shadow);
+               END_USE(vq);
+               return buf;
+       }
+       /* That should have freed everything. */
+       BUG_ON(vq->vq.num_free != vq->split.vring.num);
+
+       END_USE(vq);
+       return NULL;
+}
+
+static struct virtqueue *vring_create_virtqueue_split(
+       unsigned int index,
+       unsigned int num,
+       unsigned int vring_align,
+       struct virtio_device *vdev,
+       bool weak_barriers,
+       bool may_reduce_num,
+       bool context,
+       bool (*notify)(struct virtqueue *),
+       void (*callback)(struct virtqueue *),
+       const char *name)
+{
+       struct virtqueue *vq;
+       void *queue = NULL;
+       dma_addr_t dma_addr;
+       size_t queue_size_in_bytes;
+       struct vring vring;
+
+       /* We assume num is a power of 2. */
+       if (num & (num - 1)) {
+               dev_warn(&vdev->dev, "Bad virtqueue length %u\n", num);
+               return NULL;
+       }
+
+       /* TODO: allocate each queue chunk individually */
+       for (; num && vring_size(num, vring_align) > PAGE_SIZE; num /= 2) {
+               queue = vring_alloc_queue(vdev, vring_size(num, vring_align),
+                                         &dma_addr,
+                                         GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO);
+               if (queue)
+                       break;
+       }
+
+       if (!num)
+               return NULL;
+
+       if (!queue) {
+               /* Try to get a single page. You are my only hope! */
+               queue = vring_alloc_queue(vdev, vring_size(num, vring_align),
+                                         &dma_addr, GFP_KERNEL|__GFP_ZERO);
+       }
+       if (!queue)
+               return NULL;
+
+       queue_size_in_bytes = vring_size(num, vring_align);
+       vring_init(&vring, num, queue, vring_align);
+
+       vq = __vring_new_virtqueue(index, vring, vdev, weak_barriers, context,
+                                  notify, callback, name);
+       if (!vq) {
+               vring_free_queue(vdev, queue_size_in_bytes, queue,
+                                dma_addr);
+               return NULL;
+       }
+
+       to_vvq(vq)->split.queue_dma_addr = dma_addr;
+       to_vvq(vq)->split.queue_size_in_bytes = queue_size_in_bytes;
+       to_vvq(vq)->we_own_ring = true;
+
+       return vq;
+}
+
+
+/*
+ * Packed ring specific functions - *_packed().
+ */
+
+static void vring_unmap_state_packed(const struct vring_virtqueue *vq,
+                                    struct vring_desc_extra_packed *state)
+{
+       u16 flags;
+
+       if (!vq->use_dma_api)
+               return;
+
+       flags = state->flags;
+
+       if (flags & VRING_DESC_F_INDIRECT) {
+               dma_unmap_single(vring_dma_dev(vq),
+                                state->addr, state->len,
+                                (flags & VRING_DESC_F_WRITE) ?
+                                DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       } else {
+               dma_unmap_page(vring_dma_dev(vq),
+                              state->addr, state->len,
+                              (flags & VRING_DESC_F_WRITE) ?
+                              DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       }
+}
+
+static void vring_unmap_desc_packed(const struct vring_virtqueue *vq,
+                                  struct vring_packed_desc *desc)
+{
+       u16 flags;
+
+       if (!vq->use_dma_api)
+               return;
+
+       flags = le16_to_cpu(desc->flags);
+
+       if (flags & VRING_DESC_F_INDIRECT) {
+               dma_unmap_single(vring_dma_dev(vq),
+                                le64_to_cpu(desc->addr),
+                                le32_to_cpu(desc->len),
+                                (flags & VRING_DESC_F_WRITE) ?
+                                DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       } else {
+               dma_unmap_page(vring_dma_dev(vq),
+                              le64_to_cpu(desc->addr),
+                              le32_to_cpu(desc->len),
+                              (flags & VRING_DESC_F_WRITE) ?
+                              DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       }
+}
+
+static struct vring_packed_desc *alloc_indirect_packed(unsigned int total_sg,
+                                                      gfp_t gfp)
+{
+       struct vring_packed_desc *desc;
+
+       /*
+        * We require lowmem mappings for the descriptors because
+        * otherwise virt_to_phys will give us bogus addresses in the
+        * virtqueue.
+        */
+       gfp &= ~__GFP_HIGHMEM;
+
+       desc = kmalloc_array(total_sg, sizeof(struct vring_packed_desc), gfp);
+
+       return desc;
+}
+
+static int virtqueue_add_indirect_packed(struct vring_virtqueue *vq,
+                                      struct scatterlist *sgs[],
+                                      unsigned int total_sg,
+                                      unsigned int out_sgs,
+                                      unsigned int in_sgs,
+                                      void *data,
+                                      gfp_t gfp)
+{
+       struct vring_packed_desc *desc;
+       struct scatterlist *sg;
+       unsigned int i, n, err_idx;
+       u16 head, id;
+       dma_addr_t addr;
+
+       head = vq->packed.next_avail_idx;
+       desc = alloc_indirect_packed(total_sg, gfp);
+
+       if (unlikely(vq->vq.num_free < 1)) {
+               pr_debug("Can't add buf len 1 - avail = 0\n");
+               END_USE(vq);
+               return -ENOSPC;
+       }
+
+       i = 0;
+       id = vq->free_head;
+       BUG_ON(id == vq->packed.vring.num);
+
+       for (n = 0; n < out_sgs + in_sgs; n++) {
+               for (sg = sgs[n]; sg; sg = sg_next(sg)) {
+                       addr = vring_map_one_sg(vq, sg, n < out_sgs ?
+                                       DMA_TO_DEVICE : DMA_FROM_DEVICE);
+                       if (vring_mapping_error(vq, addr))
+                               goto unmap_release;
+
+                       desc[i].flags = cpu_to_le16(n < out_sgs ?
+                                               0 : VRING_DESC_F_WRITE);
+                       desc[i].addr = cpu_to_le64(addr);
+                       desc[i].len = cpu_to_le32(sg->length);
+                       i++;
+               }
+       }
+
+       /* Now that the indirect table is filled in, map it. */
+       addr = vring_map_single(vq, desc,
+                       total_sg * sizeof(struct vring_packed_desc),
+                       DMA_TO_DEVICE);
+       if (vring_mapping_error(vq, addr))
+               goto unmap_release;
+
+       vq->packed.vring.desc[head].addr = cpu_to_le64(addr);
+       vq->packed.vring.desc[head].len = cpu_to_le32(total_sg *
+                               sizeof(struct vring_packed_desc));
+       vq->packed.vring.desc[head].id = cpu_to_le16(id);
+
+       if (vq->use_dma_api) {
+               vq->packed.desc_extra[id].addr = addr;
+               vq->packed.desc_extra[id].len = total_sg *
+                               sizeof(struct vring_packed_desc);
+               vq->packed.desc_extra[id].flags = VRING_DESC_F_INDIRECT |
+                                                 vq->packed.avail_used_flags;
+       }
+
+       /*
+        * A driver MUST NOT make the first descriptor in the list
+        * available before all subsequent descriptors comprising
+        * the list are made available.
+        */
+       virtio_wmb(vq->weak_barriers);
+       vq->packed.vring.desc[head].flags = cpu_to_le16(VRING_DESC_F_INDIRECT |
+                                               vq->packed.avail_used_flags);
+
+       /* We're using some buffers from the free list. */
+       vq->vq.num_free -= 1;
+
+       /* Update free pointer */
+       n = head + 1;
+       if (n >= vq->packed.vring.num) {
+               n = 0;
+               vq->packed.avail_wrap_counter ^= 1;
+               vq->packed.avail_used_flags ^=
+                               1 << VRING_PACKED_DESC_F_AVAIL |
+                               1 << VRING_PACKED_DESC_F_USED;
+       }
+       vq->packed.next_avail_idx = n;
+       vq->free_head = vq->packed.desc_state[id].next;
+
+       /* Store token and indirect buffer state. */
+       vq->packed.desc_state[id].num = 1;
+       vq->packed.desc_state[id].data = data;
+       vq->packed.desc_state[id].indir_desc = desc;
+       vq->packed.desc_state[id].last = id;
+
+       vq->num_added += 1;
+
+       pr_debug("Added buffer head %i to %p\n", head, vq);
+       END_USE(vq);
+
+       return 0;
+
+unmap_release:
+       err_idx = i;
+
+       for (i = 0; i < err_idx; i++)
+               vring_unmap_desc_packed(vq, &desc[i]);
+
+       kfree(desc);
+
+       END_USE(vq);
+       return -EIO;
+}
+
+static inline int virtqueue_add_packed(struct virtqueue *_vq,
+                                      struct scatterlist *sgs[],
+                                      unsigned int total_sg,
+                                      unsigned int out_sgs,
+                                      unsigned int in_sgs,
+                                      void *data,
+                                      void *ctx,
+                                      gfp_t gfp)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       struct vring_packed_desc *desc;
+       struct scatterlist *sg;
+       unsigned int i, n, c, descs_used, err_idx;
+       __le16 uninitialized_var(head_flags), flags;
+       u16 head, id, uninitialized_var(prev), curr, avail_used_flags;
+
+       START_USE(vq);
+
+       BUG_ON(data == NULL);
+       BUG_ON(ctx && vq->indirect);
+
+       if (unlikely(vq->broken)) {
+               END_USE(vq);
+               return -EIO;
+       }
+
+       LAST_ADD_TIME_UPDATE(vq);
+
+       BUG_ON(total_sg == 0);
+
+       if (virtqueue_use_indirect(_vq, total_sg))
+               return virtqueue_add_indirect_packed(vq, sgs, total_sg,
+                               out_sgs, in_sgs, data, gfp);
+
+       head = vq->packed.next_avail_idx;
+       avail_used_flags = vq->packed.avail_used_flags;
+
+       WARN_ON_ONCE(total_sg > vq->packed.vring.num && !vq->indirect);
+
+       desc = vq->packed.vring.desc;
+       i = head;
+       descs_used = total_sg;
+
+       if (unlikely(vq->vq.num_free < descs_used)) {
+               pr_debug("Can't add buf len %i - avail = %i\n",
+                        descs_used, vq->vq.num_free);
+               END_USE(vq);
+               return -ENOSPC;
+       }
+
+       id = vq->free_head;
+       BUG_ON(id == vq->packed.vring.num);
+
+       curr = id;
+       c = 0;
+       for (n = 0; n < out_sgs + in_sgs; n++) {
+               for (sg = sgs[n]; sg; sg = sg_next(sg)) {
+                       dma_addr_t addr = vring_map_one_sg(vq, sg, n < out_sgs ?
+                                       DMA_TO_DEVICE : DMA_FROM_DEVICE);
+                       if (vring_mapping_error(vq, addr))
+                               goto unmap_release;
+
+                       flags = cpu_to_le16(vq->packed.avail_used_flags |
+                                   (++c == total_sg ? 0 : VRING_DESC_F_NEXT) |
+                                   (n < out_sgs ? 0 : VRING_DESC_F_WRITE));
+                       if (i == head)
+                               head_flags = flags;
+                       else
+                               desc[i].flags = flags;
+
+                       desc[i].addr = cpu_to_le64(addr);
+                       desc[i].len = cpu_to_le32(sg->length);
+                       desc[i].id = cpu_to_le16(id);
+
+                       if (unlikely(vq->use_dma_api)) {
+                               vq->packed.desc_extra[curr].addr = addr;
+                               vq->packed.desc_extra[curr].len = sg->length;
+                               vq->packed.desc_extra[curr].flags =
+                                       le16_to_cpu(flags);
+                       }
+                       prev = curr;
+                       curr = vq->packed.desc_state[curr].next;
+
+                       if ((unlikely(++i >= vq->packed.vring.num))) {
+                               i = 0;
+                               vq->packed.avail_used_flags ^=
+                                       1 << VRING_PACKED_DESC_F_AVAIL |
+                                       1 << VRING_PACKED_DESC_F_USED;
+                       }
+               }
+       }
+
+       if (i < head)
+               vq->packed.avail_wrap_counter ^= 1;
+
+       /* We're using some buffers from the free list. */
+       vq->vq.num_free -= descs_used;
+
+       /* Update free pointer */
+       vq->packed.next_avail_idx = i;
+       vq->free_head = curr;
+
+       /* Store token. */
+       vq->packed.desc_state[id].num = descs_used;
+       vq->packed.desc_state[id].data = data;
+       vq->packed.desc_state[id].indir_desc = ctx;
+       vq->packed.desc_state[id].last = prev;
+
+       /*
+        * A driver MUST NOT make the first descriptor in the list
+        * available before all subsequent descriptors comprising
+        * the list are made available.
+        */
+       virtio_wmb(vq->weak_barriers);
+       vq->packed.vring.desc[head].flags = head_flags;
+       vq->num_added += descs_used;
+
+       pr_debug("Added buffer head %i to %p\n", head, vq);
+       END_USE(vq);
+
+       return 0;
+
+unmap_release:
+       err_idx = i;
+       i = head;
+
+       vq->packed.avail_used_flags = avail_used_flags;
+
+       for (n = 0; n < total_sg; n++) {
+               if (i == err_idx)
+                       break;
+               vring_unmap_desc_packed(vq, &desc[i]);
+               i++;
+               if (i >= vq->packed.vring.num)
+                       i = 0;
+       }
+
+       END_USE(vq);
+       return -EIO;
+}
+
+static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 new, old, off_wrap, flags, wrap_counter, event_idx;
+       bool needs_kick;
+       union {
+               struct {
+                       __le16 off_wrap;
+                       __le16 flags;
+               };
+               u32 u32;
+       } snapshot;
+
+       START_USE(vq);
+
+       /*
+        * We need to expose the new flags value before checking notification
+        * suppressions.
+        */
+       virtio_mb(vq->weak_barriers);
+
+       old = vq->packed.next_avail_idx - vq->num_added;
+       new = vq->packed.next_avail_idx;
+       vq->num_added = 0;
+
+       snapshot.u32 = *(u32 *)vq->packed.vring.device;
+       flags = le16_to_cpu(snapshot.flags);
+
+       LAST_ADD_TIME_CHECK(vq);
+       LAST_ADD_TIME_INVALID(vq);
+
+       if (flags != VRING_PACKED_EVENT_FLAG_DESC) {
+               needs_kick = (flags != VRING_PACKED_EVENT_FLAG_DISABLE);
+               goto out;
+       }
+
+       off_wrap = le16_to_cpu(snapshot.off_wrap);
+
+       wrap_counter = off_wrap >> VRING_PACKED_EVENT_F_WRAP_CTR;
+       event_idx = off_wrap & ~(1 << VRING_PACKED_EVENT_F_WRAP_CTR);
+       if (wrap_counter != vq->packed.avail_wrap_counter)
+               event_idx -= vq->packed.vring.num;
+
+       needs_kick = vring_need_event(event_idx, new, old);
+out:
+       END_USE(vq);
+       return needs_kick;
+}
+
+static void detach_buf_packed(struct vring_virtqueue *vq,
+                             unsigned int id, void **ctx)
+{
+       struct vring_desc_state_packed *state = NULL;
+       struct vring_packed_desc *desc;
+       unsigned int i, curr;
+
+       state = &vq->packed.desc_state[id];
+
+       /* Clear data ptr. */
+       state->data = NULL;
+
+       vq->packed.desc_state[state->last].next = vq->free_head;
+       vq->free_head = id;
+       vq->vq.num_free += state->num;
+
+       if (unlikely(vq->use_dma_api)) {
+               curr = id;
+               for (i = 0; i < state->num; i++) {
+                       vring_unmap_state_packed(vq,
+                               &vq->packed.desc_extra[curr]);
+                       curr = vq->packed.desc_state[curr].next;
+               }
+       }
+
+       if (vq->indirect) {
+               u32 len;
+
+               /* Free the indirect table, if any, now that it's unmapped. */
+               desc = state->indir_desc;
+               if (!desc)
+                       return;
+
+               if (vq->use_dma_api) {
+                       len = vq->packed.desc_extra[id].len;
+                       for (i = 0; i < len / sizeof(struct vring_packed_desc);
+                                       i++)
+                               vring_unmap_desc_packed(vq, &desc[i]);
+               }
+               kfree(desc);
+               state->indir_desc = NULL;
+       } else if (ctx) {
+               *ctx = state->indir_desc;
+       }
+}
+
+static inline bool is_used_desc_packed(const struct vring_virtqueue *vq,
+                                      u16 idx, bool used_wrap_counter)
+{
+       bool avail, used;
+       u16 flags;
+
+       flags = le16_to_cpu(vq->packed.vring.desc[idx].flags);
+       avail = !!(flags & (1 << VRING_PACKED_DESC_F_AVAIL));
+       used = !!(flags & (1 << VRING_PACKED_DESC_F_USED));
+
+       return avail == used && used == used_wrap_counter;
+}
+
+static inline bool more_used_packed(const struct vring_virtqueue *vq)
+{
+       return is_used_desc_packed(vq, vq->last_used_idx,
+                       vq->packed.used_wrap_counter);
+}
+
+static void *virtqueue_get_buf_ctx_packed(struct virtqueue *_vq,
+                                         unsigned int *len,
+                                         void **ctx)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 last_used, id;
+       void *ret;
+
+       START_USE(vq);
+
+       if (unlikely(vq->broken)) {
+               END_USE(vq);
+               return NULL;
+       }
+
+       if (!more_used_packed(vq)) {
+               pr_debug("No more buffers in queue\n");
+               END_USE(vq);
+               return NULL;
+       }
+
+       /* Only get used elements after they have been exposed by host. */
+       virtio_rmb(vq->weak_barriers);
+
+       last_used = vq->last_used_idx;
+       id = le16_to_cpu(vq->packed.vring.desc[last_used].id);
+       *len = le32_to_cpu(vq->packed.vring.desc[last_used].len);
+
+       if (unlikely(id >= vq->packed.vring.num)) {
+               BAD_RING(vq, "id %u out of range\n", id);
+               return NULL;
+       }
+       if (unlikely(!vq->packed.desc_state[id].data)) {
+               BAD_RING(vq, "id %u is not a head!\n", id);
+               return NULL;
+       }
+
+       /* detach_buf_packed clears data, so grab it now. */
+       ret = vq->packed.desc_state[id].data;
+       detach_buf_packed(vq, id, ctx);
+
+       vq->last_used_idx += vq->packed.desc_state[id].num;
+       if (unlikely(vq->last_used_idx >= vq->packed.vring.num)) {
+               vq->last_used_idx -= vq->packed.vring.num;
+               vq->packed.used_wrap_counter ^= 1;
+       }
+
+       /*
+        * If we expect an interrupt for the next entry, tell host
+        * by writing event index and flush out the write before
+        * the read in the next get_buf call.
+        */
+       if (vq->packed.event_flags_shadow == VRING_PACKED_EVENT_FLAG_DESC)
+               virtio_store_mb(vq->weak_barriers,
+                               &vq->packed.vring.driver->off_wrap,
+                               cpu_to_le16(vq->last_used_idx |
+                                       (vq->packed.used_wrap_counter <<
+                                        VRING_PACKED_EVENT_F_WRAP_CTR)));
+
+       LAST_ADD_TIME_INVALID(vq);
+
+       END_USE(vq);
+       return ret;
+}
+
+static void virtqueue_disable_cb_packed(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       if (vq->packed.event_flags_shadow != VRING_PACKED_EVENT_FLAG_DISABLE) {
+               vq->packed.event_flags_shadow = VRING_PACKED_EVENT_FLAG_DISABLE;
+               vq->packed.vring.driver->flags =
+                       cpu_to_le16(vq->packed.event_flags_shadow);
+       }
+}
+
+static unsigned virtqueue_enable_cb_prepare_packed(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       START_USE(vq);
+
+       /*
+        * We optimistically turn back on interrupts, then check if there was
+        * more to do.
+        */
+
+       if (vq->event) {
+               vq->packed.vring.driver->off_wrap =
+                       cpu_to_le16(vq->last_used_idx |
+                               (vq->packed.used_wrap_counter <<
+                                VRING_PACKED_EVENT_F_WRAP_CTR));
+               /*
+                * We need to update event offset and event wrap
+                * counter first before updating event flags.
+                */
+               virtio_wmb(vq->weak_barriers);
+       }
+
+       if (vq->packed.event_flags_shadow == VRING_PACKED_EVENT_FLAG_DISABLE) {
+               vq->packed.event_flags_shadow = vq->event ?
+                               VRING_PACKED_EVENT_FLAG_DESC :
+                               VRING_PACKED_EVENT_FLAG_ENABLE;
+               vq->packed.vring.driver->flags =
+                               cpu_to_le16(vq->packed.event_flags_shadow);
+       }
+
+       END_USE(vq);
+       return vq->last_used_idx | ((u16)vq->packed.used_wrap_counter <<
+                       VRING_PACKED_EVENT_F_WRAP_CTR);
+}
+
+static bool virtqueue_poll_packed(struct virtqueue *_vq, u16 off_wrap)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       bool wrap_counter;
+       u16 used_idx;
+
+       wrap_counter = off_wrap >> VRING_PACKED_EVENT_F_WRAP_CTR;
+       used_idx = off_wrap & ~(1 << VRING_PACKED_EVENT_F_WRAP_CTR);
+
+       return is_used_desc_packed(vq, used_idx, wrap_counter);
+}
+
+static bool virtqueue_enable_cb_delayed_packed(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 used_idx, wrap_counter;
+       u16 bufs;
+
+       START_USE(vq);
+
+       /*
+        * We optimistically turn back on interrupts, then check if there was
+        * more to do.
+        */
+
+       if (vq->event) {
+               /* TODO: tune this threshold */
+               bufs = (vq->packed.vring.num - vq->vq.num_free) * 3 / 4;
+               wrap_counter = vq->packed.used_wrap_counter;
+
+               used_idx = vq->last_used_idx + bufs;
+               if (used_idx >= vq->packed.vring.num) {
+                       used_idx -= vq->packed.vring.num;
+                       wrap_counter ^= 1;
+               }
+
+               vq->packed.vring.driver->off_wrap = cpu_to_le16(used_idx |
+                       (wrap_counter << VRING_PACKED_EVENT_F_WRAP_CTR));
+
+               /*
+                * We need to update event offset and event wrap
+                * counter first before updating event flags.
+                */
+               virtio_wmb(vq->weak_barriers);
+       } else {
+               used_idx = vq->last_used_idx;
+               wrap_counter = vq->packed.used_wrap_counter;
+       }
+
+       if (vq->packed.event_flags_shadow == VRING_PACKED_EVENT_FLAG_DISABLE) {
+               vq->packed.event_flags_shadow = vq->event ?
+                               VRING_PACKED_EVENT_FLAG_DESC :
+                               VRING_PACKED_EVENT_FLAG_ENABLE;
+               vq->packed.vring.driver->flags =
+                               cpu_to_le16(vq->packed.event_flags_shadow);
+       }
+
+       /*
+        * We need to update event suppression structure first
+        * before re-checking for more used buffers.
+        */
+       virtio_mb(vq->weak_barriers);
+
+       if (is_used_desc_packed(vq, used_idx, wrap_counter)) {
+               END_USE(vq);
+               return false;
+       }
+
+       END_USE(vq);
+       return true;
+}
+
+static void *virtqueue_detach_unused_buf_packed(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       unsigned int i;
+       void *buf;
+
+       START_USE(vq);
+
+       for (i = 0; i < vq->packed.vring.num; i++) {
+               if (!vq->packed.desc_state[i].data)
+                       continue;
+               /* detach_buf clears data, so grab it now. */
+               buf = vq->packed.desc_state[i].data;
+               detach_buf_packed(vq, i, NULL);
+               END_USE(vq);
+               return buf;
+       }
+       /* That should have freed everything. */
+       BUG_ON(vq->vq.num_free != vq->packed.vring.num);
+
+       END_USE(vq);
+       return NULL;
+}
+
+static struct virtqueue *vring_create_virtqueue_packed(
+       unsigned int index,
+       unsigned int num,
+       unsigned int vring_align,
+       struct virtio_device *vdev,
+       bool weak_barriers,
+       bool may_reduce_num,
+       bool context,
+       bool (*notify)(struct virtqueue *),
+       void (*callback)(struct virtqueue *),
+       const char *name)
+{
+       struct vring_virtqueue *vq;
+       struct vring_packed_desc *ring;
+       struct vring_packed_desc_event *driver, *device;
+       dma_addr_t ring_dma_addr, driver_event_dma_addr, device_event_dma_addr;
+       size_t ring_size_in_bytes, event_size_in_bytes;
+       unsigned int i;
+
+       ring_size_in_bytes = num * sizeof(struct vring_packed_desc);
+
+       ring = vring_alloc_queue(vdev, ring_size_in_bytes,
+                                &ring_dma_addr,
+                                GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO);
+       if (!ring)
+               goto err_ring;
+
+       event_size_in_bytes = sizeof(struct vring_packed_desc_event);
+
+       driver = vring_alloc_queue(vdev, event_size_in_bytes,
+                                  &driver_event_dma_addr,
+                                  GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO);
+       if (!driver)
+               goto err_driver;
+
+       device = vring_alloc_queue(vdev, event_size_in_bytes,
+                                  &device_event_dma_addr,
+                                  GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO);
+       if (!device)
+               goto err_device;
+
+       vq = kmalloc(sizeof(*vq), GFP_KERNEL);
+       if (!vq)
+               goto err_vq;
+
+       vq->vq.callback = callback;
+       vq->vq.vdev = vdev;
+       vq->vq.name = name;
+       vq->vq.num_free = num;
+       vq->vq.index = index;
+       vq->we_own_ring = true;
+       vq->notify = notify;
+       vq->weak_barriers = weak_barriers;
+       vq->broken = false;
+       vq->last_used_idx = 0;
+       vq->num_added = 0;
+       vq->packed_ring = true;
+       vq->use_dma_api = vring_use_dma_api(vdev);
+       list_add_tail(&vq->vq.list, &vdev->vqs);
+#ifdef DEBUG
+       vq->in_use = false;
+       vq->last_add_time_valid = false;
+#endif
+
+       vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) &&
+               !context;
+       vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX);
+
+       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;
+
+       vq->packed.ring_size_in_bytes = ring_size_in_bytes;
+       vq->packed.event_size_in_bytes = event_size_in_bytes;
+
+       vq->packed.vring.num = num;
+       vq->packed.vring.desc = ring;
+       vq->packed.vring.driver = driver;
+       vq->packed.vring.device = device;
+
+       vq->packed.next_avail_idx = 0;
+       vq->packed.avail_wrap_counter = 1;
+       vq->packed.used_wrap_counter = 1;
+       vq->packed.event_flags_shadow = 0;
+       vq->packed.avail_used_flags = 1 << VRING_PACKED_DESC_F_AVAIL;
+
+       vq->packed.desc_state = kmalloc_array(num,
+                       sizeof(struct vring_desc_state_packed),
+                       GFP_KERNEL);
+       if (!vq->packed.desc_state)
+               goto err_desc_state;
+
+       memset(vq->packed.desc_state, 0,
+               num * sizeof(struct vring_desc_state_packed));
+
+       /* Put everything in free lists. */
+       vq->free_head = 0;
+       for (i = 0; i < num-1; i++)
+               vq->packed.desc_state[i].next = i + 1;
+
+       vq->packed.desc_extra = kmalloc_array(num,
+                       sizeof(struct vring_desc_extra_packed),
+                       GFP_KERNEL);
+       if (!vq->packed.desc_extra)
+               goto err_desc_extra;
+
+       memset(vq->packed.desc_extra, 0,
+               num * sizeof(struct vring_desc_extra_packed));
+
+       /* No callback?  Tell other side not to bother us. */
+       if (!callback) {
+               vq->packed.event_flags_shadow = VRING_PACKED_EVENT_FLAG_DISABLE;
+               vq->packed.vring.driver->flags =
+                       cpu_to_le16(vq->packed.event_flags_shadow);
+       }
+
+       return &vq->vq;
+
+err_desc_extra:
+       kfree(vq->packed.desc_state);
+err_desc_state:
+       kfree(vq);
+err_vq:
+       vring_free_queue(vdev, event_size_in_bytes, device, ring_dma_addr);
+err_device:
+       vring_free_queue(vdev, event_size_in_bytes, driver, ring_dma_addr);
+err_driver:
+       vring_free_queue(vdev, ring_size_in_bytes, ring, ring_dma_addr);
+err_ring:
+       return NULL;
+}
+
+
+/*
+ * Generic functions and exported symbols.
+ */
+
+static inline int virtqueue_add(struct virtqueue *_vq,
+                               struct scatterlist *sgs[],
+                               unsigned int total_sg,
+                               unsigned int out_sgs,
+                               unsigned int in_sgs,
+                               void *data,
+                               void *ctx,
+                               gfp_t gfp)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       return vq->packed_ring ? virtqueue_add_packed(_vq, sgs, total_sg,
+                                       out_sgs, in_sgs, data, ctx, gfp) :
+                                virtqueue_add_split(_vq, sgs, total_sg,
+                                       out_sgs, in_sgs, data, ctx, gfp);
+}
+
 /**
  * virtqueue_add_sgs - expose buffers to other end
  * @vq: the struct virtqueue we're talking about.
@@ -460,6 +1721,7 @@ int virtqueue_add_sgs(struct virtqueue *_vq,
        /* Count them first. */
        for (i = 0; i < out_sgs + in_sgs; i++) {
                struct scatterlist *sg;
+
                for (sg = sgs[i]; sg; sg = sg_next(sg))
                        total_sg++;
        }
@@ -550,34 +1812,9 @@ EXPORT_SYMBOL_GPL(virtqueue_add_inbuf_ctx);
 bool virtqueue_kick_prepare(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-       u16 new, old;
-       bool needs_kick;
-
-       START_USE(vq);
-       /* We need to expose available array entries before checking avail
-        * event. */
-       virtio_mb(vq->weak_barriers);
 
-       old = vq->avail_idx_shadow - vq->num_added;
-       new = vq->avail_idx_shadow;
-       vq->num_added = 0;
-
-#ifdef DEBUG
-       if (vq->last_add_time_valid) {
-               WARN_ON(ktime_to_ms(ktime_sub(ktime_get(),
-                                             vq->last_add_time)) > 100);
-       }
-       vq->last_add_time_valid = false;
-#endif
-
-       if (vq->event) {
-               needs_kick = vring_need_event(virtio16_to_cpu(_vq->vdev, vring_avail_event(&vq->vring)),
-                                             new, old);
-       } else {
-               needs_kick = !(vq->vring.used->flags & cpu_to_virtio16(_vq->vdev, VRING_USED_F_NO_NOTIFY));
-       }
-       END_USE(vq);
-       return needs_kick;
+       return vq->packed_ring ? virtqueue_kick_prepare_packed(_vq) :
+                                virtqueue_kick_prepare_split(_vq);
 }
 EXPORT_SYMBOL_GPL(virtqueue_kick_prepare);
 
@@ -625,60 +1862,6 @@ bool virtqueue_kick(struct virtqueue *vq)
 }
 EXPORT_SYMBOL_GPL(virtqueue_kick);
 
-static void detach_buf(struct vring_virtqueue *vq, unsigned int head,
-                      void **ctx)
-{
-       unsigned int i, j;
-       __virtio16 nextflag = cpu_to_virtio16(vq->vq.vdev, VRING_DESC_F_NEXT);
-
-       /* Clear data ptr. */
-       vq->desc_state[head].data = NULL;
-
-       /* Put back on free list: unmap first-level descriptors and find end */
-       i = head;
-
-       while (vq->vring.desc[i].flags & nextflag) {
-               vring_unmap_one(vq, &vq->vring.desc[i]);
-               i = virtio16_to_cpu(vq->vq.vdev, vq->vring.desc[i].next);
-               vq->vq.num_free++;
-       }
-
-       vring_unmap_one(vq, &vq->vring.desc[i]);
-       vq->vring.desc[i].next = cpu_to_virtio16(vq->vq.vdev, vq->free_head);
-       vq->free_head = head;
-
-       /* Plus final descriptor */
-       vq->vq.num_free++;
-
-       if (vq->indirect) {
-               struct vring_desc *indir_desc = vq->desc_state[head].indir_desc;
-               u32 len;
-
-               /* Free the indirect table, if any, now that it's unmapped. */
-               if (!indir_desc)
-                       return;
-
-               len = virtio32_to_cpu(vq->vq.vdev, vq->vring.desc[head].len);
-
-               BUG_ON(!(vq->vring.desc[head].flags &
-                        cpu_to_virtio16(vq->vq.vdev, VRING_DESC_F_INDIRECT)));
-               BUG_ON(len == 0 || len % sizeof(struct vring_desc));
-
-               for (j = 0; j < len / sizeof(struct vring_desc); j++)
-                       vring_unmap_one(vq, &indir_desc[j]);
-
-               kfree(indir_desc);
-               vq->desc_state[head].indir_desc = NULL;
-       } else if (ctx) {
-               *ctx = vq->desc_state[head].indir_desc;
-       }
-}
-
-static inline bool more_used(const struct vring_virtqueue *vq)
-{
-       return vq->last_used_idx != virtio16_to_cpu(vq->vq.vdev, vq->vring.used->idx);
-}
-
 /**
  * virtqueue_get_buf - get the next used buffer
  * @vq: the struct virtqueue we're talking about.
@@ -699,57 +1882,9 @@ void *virtqueue_get_buf_ctx(struct virtqueue *_vq, unsigned int *len,
                            void **ctx)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-       void *ret;
-       unsigned int i;
-       u16 last_used;
-
-       START_USE(vq);
-
-       if (unlikely(vq->broken)) {
-               END_USE(vq);
-               return NULL;
-       }
-
-       if (!more_used(vq)) {
-               pr_debug("No more buffers in queue\n");
-               END_USE(vq);
-               return NULL;
-       }
-
-       /* Only get used array entries after they have been exposed by host. */
-       virtio_rmb(vq->weak_barriers);
-
-       last_used = (vq->last_used_idx & (vq->vring.num - 1));
-       i = virtio32_to_cpu(_vq->vdev, vq->vring.used->ring[last_used].id);
-       *len = virtio32_to_cpu(_vq->vdev, vq->vring.used->ring[last_used].len);
-
-       if (unlikely(i >= vq->vring.num)) {
-               BAD_RING(vq, "id %u out of range\n", i);
-               return NULL;
-       }
-       if (unlikely(!vq->desc_state[i].data)) {
-               BAD_RING(vq, "id %u is not a head!\n", i);
-               return NULL;
-       }
-
-       /* detach_buf clears data, so grab it now. */
-       ret = vq->desc_state[i].data;
-       detach_buf(vq, i, ctx);
-       vq->last_used_idx++;
-       /* If we expect an interrupt for the next entry, tell host
-        * by writing event index and flush out the write before
-        * the read in the next get_buf call. */
-       if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT))
-               virtio_store_mb(vq->weak_barriers,
-                               &vring_used_event(&vq->vring),
-                               cpu_to_virtio16(_vq->vdev, vq->last_used_idx));
-
-#ifdef DEBUG
-       vq->last_add_time_valid = false;
-#endif
 
-       END_USE(vq);
-       return ret;
+       return vq->packed_ring ? virtqueue_get_buf_ctx_packed(_vq, len, ctx) :
+                                virtqueue_get_buf_ctx_split(_vq, len, ctx);
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_buf_ctx);
 
@@ -771,12 +1906,10 @@ void virtqueue_disable_cb(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
 
-       if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT)) {
-               vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
-               if (!vq->event)
-                       vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
-       }
-
+       if (vq->packed_ring)
+               virtqueue_disable_cb_packed(_vq);
+       else
+               virtqueue_disable_cb_split(_vq);
 }
 EXPORT_SYMBOL_GPL(virtqueue_disable_cb);
 
@@ -795,23 +1928,9 @@ EXPORT_SYMBOL_GPL(virtqueue_disable_cb);
 unsigned virtqueue_enable_cb_prepare(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-       u16 last_used_idx;
-
-       START_USE(vq);
 
-       /* We optimistically turn back on interrupts, then check if there was
-        * more to do. */
-       /* Depending on the VIRTIO_RING_F_EVENT_IDX feature, we need to
-        * either clear the flags bit or point the event index at the next
-        * entry. Always do both to keep code simple. */
-       if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
-               vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
-               if (!vq->event)
-                       vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
-       }
-       vring_used_event(&vq->vring) = cpu_to_virtio16(_vq->vdev, last_used_idx = vq->last_used_idx);
-       END_USE(vq);
-       return last_used_idx;
+       return vq->packed_ring ? virtqueue_enable_cb_prepare_packed(_vq) :
+                                virtqueue_enable_cb_prepare_split(_vq);
 }
 EXPORT_SYMBOL_GPL(virtqueue_enable_cb_prepare);
 
@@ -829,7 +1948,8 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx)
        struct vring_virtqueue *vq = to_vvq(_vq);
 
        virtio_mb(vq->weak_barriers);
-       return (u16)last_used_idx != virtio16_to_cpu(_vq->vdev, vq->vring.used->idx);
+       return vq->packed_ring ? virtqueue_poll_packed(_vq, last_used_idx) :
+                                virtqueue_poll_split(_vq, last_used_idx);
 }
 EXPORT_SYMBOL_GPL(virtqueue_poll);
 
@@ -847,6 +1967,7 @@ EXPORT_SYMBOL_GPL(virtqueue_poll);
 bool virtqueue_enable_cb(struct virtqueue *_vq)
 {
        unsigned last_used_idx = virtqueue_enable_cb_prepare(_vq);
+
        return !virtqueue_poll(_vq, last_used_idx);
 }
 EXPORT_SYMBOL_GPL(virtqueue_enable_cb);
@@ -867,34 +1988,9 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb);
 bool virtqueue_enable_cb_delayed(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-       u16 bufs;
-
-       START_USE(vq);
-
-       /* We optimistically turn back on interrupts, then check if there was
-        * more to do. */
-       /* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
-        * either clear the flags bit or point the event index at the next
-        * entry. Always update the event index to keep code simple. */
-       if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
-               vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
-               if (!vq->event)
-                       vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
-       }
-       /* TODO: tune this threshold */
-       bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) * 3 / 4;
-
-       virtio_store_mb(vq->weak_barriers,
-                       &vring_used_event(&vq->vring),
-                       cpu_to_virtio16(_vq->vdev, vq->last_used_idx + bufs));
-
-       if (unlikely((u16)(virtio16_to_cpu(_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) {
-               END_USE(vq);
-               return false;
-       }
 
-       END_USE(vq);
-       return true;
+       return vq->packed_ring ? virtqueue_enable_cb_delayed_packed(_vq) :
+                                virtqueue_enable_cb_delayed_split(_vq);
 }
 EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed);
 
@@ -909,30 +2005,17 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed);
 void *virtqueue_detach_unused_buf(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-       unsigned int i;
-       void *buf;
-
-       START_USE(vq);
-
-       for (i = 0; i < vq->vring.num; i++) {
-               if (!vq->desc_state[i].data)
-                       continue;
-               /* detach_buf clears data, so grab it now. */
-               buf = vq->desc_state[i].data;
-               detach_buf(vq, i, NULL);
-               vq->avail_idx_shadow--;
-               vq->vring.avail->idx = cpu_to_virtio16(_vq->vdev, vq->avail_idx_shadow);
-               END_USE(vq);
-               return buf;
-       }
-       /* That should have freed everything. */
-       BUG_ON(vq->vq.num_free != vq->vring.num);
 
-       END_USE(vq);
-       return NULL;
+       return vq->packed_ring ? virtqueue_detach_unused_buf_packed(_vq) :
+                                virtqueue_detach_unused_buf_split(_vq);
 }
 EXPORT_SYMBOL_GPL(virtqueue_detach_unused_buf);
 
+static inline bool more_used(const struct vring_virtqueue *vq)
+{
+       return vq->packed_ring ? more_used_packed(vq) : more_used_split(vq);
+}
+
 irqreturn_t vring_interrupt(int irq, void *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
@@ -953,6 +2036,7 @@ irqreturn_t vring_interrupt(int irq, void *_vq)
 }
 EXPORT_SYMBOL_GPL(vring_interrupt);
 
+/* Only available for split ring */
 struct virtqueue *__vring_new_virtqueue(unsigned int index,
                                        struct vring vring,
                                        struct virtio_device *vdev,
@@ -965,27 +2049,26 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index,
        unsigned int i;
        struct vring_virtqueue *vq;
 
-       vq = kmalloc(sizeof(*vq) + vring.num * sizeof(struct vring_desc_state),
-                    GFP_KERNEL);
+       if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED))
+               return NULL;
+
+       vq = kmalloc(sizeof(*vq), GFP_KERNEL);
        if (!vq)
                return NULL;
 
-       vq->vring = vring;
+       vq->packed_ring = false;
        vq->vq.callback = callback;
        vq->vq.vdev = vdev;
        vq->vq.name = name;
        vq->vq.num_free = vring.num;
        vq->vq.index = index;
        vq->we_own_ring = false;
-       vq->queue_dma_addr = 0;
-       vq->queue_size_in_bytes = 0;
        vq->notify = notify;
        vq->weak_barriers = weak_barriers;
        vq->broken = false;
        vq->last_used_idx = 0;
-       vq->avail_flags_shadow = 0;
-       vq->avail_idx_shadow = 0;
        vq->num_added = 0;
+       vq->use_dma_api = vring_use_dma_api(vdev);
        list_add_tail(&vq->vq.list, &vdev->vqs);
 #ifdef DEBUG
        vq->in_use = false;
@@ -996,65 +2079,39 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index,
                !context;
        vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX);
 
+       vq->split.queue_dma_addr = 0;
+       vq->split.queue_size_in_bytes = 0;
+
+       vq->split.vring = vring;
+       vq->split.avail_flags_shadow = 0;
+       vq->split.avail_idx_shadow = 0;
+
        /* No callback?  Tell other side not to bother us. */
        if (!callback) {
-               vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
+               vq->split.avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
                if (!vq->event)
-                       vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow);
+                       vq->split.vring.avail->flags = cpu_to_virtio16(vdev,
+                                       vq->split.avail_flags_shadow);
+       }
+
+       vq->split.desc_state = kmalloc_array(vring.num,
+                       sizeof(struct vring_desc_state_split), GFP_KERNEL);
+       if (!vq->split.desc_state) {
+               kfree(vq);
+               return NULL;
        }
 
        /* Put everything in free lists. */
        vq->free_head = 0;
        for (i = 0; i < vring.num-1; i++)
-               vq->vring.desc[i].next = cpu_to_virtio16(vdev, i + 1);
-       memset(vq->desc_state, 0, vring.num * sizeof(struct vring_desc_state));
+               vq->split.vring.desc[i].next = cpu_to_virtio16(vdev, i + 1);
+       memset(vq->split.desc_state, 0, vring.num *
+                       sizeof(struct vring_desc_state_split));
 
        return &vq->vq;
 }
 EXPORT_SYMBOL_GPL(__vring_new_virtqueue);
 
-static void *vring_alloc_queue(struct virtio_device *vdev, size_t size,
-                             dma_addr_t *dma_handle, gfp_t flag)
-{
-       if (vring_use_dma_api(vdev)) {
-               return dma_alloc_coherent(vdev->dev.parent, size,
-                                         dma_handle, flag);
-       } else {
-               void *queue = alloc_pages_exact(PAGE_ALIGN(size), flag);
-               if (queue) {
-                       phys_addr_t phys_addr = virt_to_phys(queue);
-                       *dma_handle = (dma_addr_t)phys_addr;
-
-                       /*
-                        * Sanity check: make sure we dind't truncate
-                        * the address.  The only arches I can find that
-                        * have 64-bit phys_addr_t but 32-bit dma_addr_t
-                        * are certain non-highmem MIPS and x86
-                        * configurations, but these configurations
-                        * should never allocate physical pages above 32
-                        * bits, so this is fine.  Just in case, throw a
-                        * warning and abort if we end up with an
-                        * unrepresentable address.
-                        */
-                       if (WARN_ON_ONCE(*dma_handle != phys_addr)) {
-                               free_pages_exact(queue, PAGE_ALIGN(size));
-                               return NULL;
-                       }
-               }
-               return queue;
-       }
-}
-
-static void vring_free_queue(struct virtio_device *vdev, size_t size,
-                            void *queue, dma_addr_t dma_handle)
-{
-       if (vring_use_dma_api(vdev)) {
-               dma_free_coherent(vdev->dev.parent, size, queue, dma_handle);
-       } else {
-               free_pages_exact(queue, PAGE_ALIGN(size));
-       }
-}
-
 struct virtqueue *vring_create_virtqueue(
        unsigned int index,
        unsigned int num,
@@ -1067,57 +2124,19 @@ struct virtqueue *vring_create_virtqueue(
        void (*callback)(struct virtqueue *),
        const char *name)
 {
-       struct virtqueue *vq;
-       void *queue = NULL;
-       dma_addr_t dma_addr;
-       size_t queue_size_in_bytes;
-       struct vring vring;
-
-       /* We assume num is a power of 2. */
-       if (num & (num - 1)) {
-               dev_warn(&vdev->dev, "Bad virtqueue length %u\n", num);
-               return NULL;
-       }
-
-       /* TODO: allocate each queue chunk individually */
-       for (; num && vring_size(num, vring_align) > PAGE_SIZE; num /= 2) {
-               queue = vring_alloc_queue(vdev, vring_size(num, vring_align),
-                                         &dma_addr,
-                                         GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO);
-               if (queue)
-                       break;
-       }
 
-       if (!num)
-               return NULL;
-
-       if (!queue) {
-               /* Try to get a single page. You are my only hope! */
-               queue = vring_alloc_queue(vdev, vring_size(num, vring_align),
-                                         &dma_addr, GFP_KERNEL|__GFP_ZERO);
-       }
-       if (!queue)
-               return NULL;
-
-       queue_size_in_bytes = vring_size(num, vring_align);
-       vring_init(&vring, num, queue, vring_align);
-
-       vq = __vring_new_virtqueue(index, vring, vdev, weak_barriers, context,
-                                  notify, callback, name);
-       if (!vq) {
-               vring_free_queue(vdev, queue_size_in_bytes, queue,
-                                dma_addr);
-               return NULL;
-       }
-
-       to_vvq(vq)->queue_dma_addr = dma_addr;
-       to_vvq(vq)->queue_size_in_bytes = queue_size_in_bytes;
-       to_vvq(vq)->we_own_ring = true;
+       if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED))
+               return vring_create_virtqueue_packed(index, num, vring_align,
+                               vdev, weak_barriers, may_reduce_num,
+                               context, notify, callback, name);
 
-       return vq;
+       return vring_create_virtqueue_split(index, num, vring_align,
+                       vdev, weak_barriers, may_reduce_num,
+                       context, notify, callback, name);
 }
 EXPORT_SYMBOL_GPL(vring_create_virtqueue);
 
+/* Only available for split ring */
 struct virtqueue *vring_new_virtqueue(unsigned int index,
                                      unsigned int num,
                                      unsigned int vring_align,
@@ -1130,6 +2149,10 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
                                      const char *name)
 {
        struct vring vring;
+
+       if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED))
+               return NULL;
+
        vring_init(&vring, num, pages, vring_align);
        return __vring_new_virtqueue(index, vring, vdev, weak_barriers, context,
                                     notify, callback, name);
@@ -1141,8 +2164,32 @@ void vring_del_virtqueue(struct virtqueue *_vq)
        struct vring_virtqueue *vq = to_vvq(_vq);
 
        if (vq->we_own_ring) {
-               vring_free_queue(vq->vq.vdev, vq->queue_size_in_bytes,
-                                vq->vring.desc, vq->queue_dma_addr);
+               if (vq->packed_ring) {
+                       vring_free_queue(vq->vq.vdev,
+                                        vq->packed.ring_size_in_bytes,
+                                        vq->packed.vring.desc,
+                                        vq->packed.ring_dma_addr);
+
+                       vring_free_queue(vq->vq.vdev,
+                                        vq->packed.event_size_in_bytes,
+                                        vq->packed.vring.driver,
+                                        vq->packed.driver_event_dma_addr);
+
+                       vring_free_queue(vq->vq.vdev,
+                                        vq->packed.event_size_in_bytes,
+                                        vq->packed.vring.device,
+                                        vq->packed.device_event_dma_addr);
+
+                       kfree(vq->packed.desc_state);
+                       kfree(vq->packed.desc_extra);
+               } else {
+                       vring_free_queue(vq->vq.vdev,
+                                        vq->split.queue_size_in_bytes,
+                                        vq->split.vring.desc,
+                                        vq->split.queue_dma_addr);
+
+                       kfree(vq->split.desc_state);
+               }
        }
        list_del(&_vq->list);
        kfree(vq);
@@ -1164,6 +2211,8 @@ void vring_transport_features(struct virtio_device *vdev)
                        break;
                case VIRTIO_F_IOMMU_PLATFORM:
                        break;
+               case VIRTIO_F_RING_PACKED:
+                       break;
                default:
                        /* We don't understand this bit. */
                        __virtio_clear_bit(vdev, i);
@@ -1184,7 +2233,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)
 
        struct vring_virtqueue *vq = to_vvq(_vq);
 
-       return vq->vring.num;
+       return vq->packed_ring ? vq->packed.vring.num : vq->split.vring.num;
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_vring_size);
 
@@ -1217,7 +2266,10 @@ dma_addr_t virtqueue_get_desc_addr(struct virtqueue *_vq)
 
        BUG_ON(!vq->we_own_ring);
 
-       return vq->queue_dma_addr;
+       if (vq->packed_ring)
+               return vq->packed.ring_dma_addr;
+
+       return vq->split.queue_dma_addr;
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_desc_addr);
 
@@ -1227,8 +2279,11 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)
 
        BUG_ON(!vq->we_own_ring);
 
-       return vq->queue_dma_addr +
-               ((char *)vq->vring.avail - (char *)vq->vring.desc);
+       if (vq->packed_ring)
+               return vq->packed.driver_event_dma_addr;
+
+       return vq->split.queue_dma_addr +
+               ((char *)vq->split.vring.avail - (char *)vq->split.vring.desc);
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_avail_addr);
 
@@ -1238,14 +2293,18 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)
 
        BUG_ON(!vq->we_own_ring);
 
-       return vq->queue_dma_addr +
-               ((char *)vq->vring.used - (char *)vq->vring.desc);
+       if (vq->packed_ring)
+               return vq->packed.device_event_dma_addr;
+
+       return vq->split.queue_dma_addr +
+               ((char *)vq->split.vring.used - (char *)vq->split.vring.desc);
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_used_addr);
 
+/* Only available for split ring */
 const struct vring *virtqueue_get_vring(struct virtqueue *vq)
 {
-       return &to_vvq(vq)->vring;
+       return &to_vvq(vq)->split.vring;
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_vring);
 
index fdfc64f..221b733 100644 (file)
@@ -251,25 +251,10 @@ static void release_memory_resource(struct resource *resource)
        kfree(resource);
 }
 
-/*
- * Host memory not allocated to dom0. We can use this range for hotplug-based
- * ballooning.
- *
- * It's a type-less resource. Setting IORESOURCE_MEM will make resource
- * management algorithms (arch_remove_reservations()) look into guest e820,
- * which we don't want.
- */
-static struct resource hostmem_resource = {
-       .name   = "Host RAM",
-};
-
-void __attribute__((weak)) __init arch_xen_balloon_init(struct resource *res)
-{}
-
 static struct resource *additional_memory_resource(phys_addr_t size)
 {
-       struct resource *res, *res_hostmem;
-       int ret = -ENOMEM;
+       struct resource *res;
+       int ret;
 
        res = kzalloc(sizeof(*res), GFP_KERNEL);
        if (!res)
@@ -278,42 +263,13 @@ static struct resource *additional_memory_resource(phys_addr_t size)
        res->name = "System RAM";
        res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
 
-       res_hostmem = kzalloc(sizeof(*res), GFP_KERNEL);
-       if (res_hostmem) {
-               /* Try to grab a range from hostmem */
-               res_hostmem->name = "Host memory";
-               ret = allocate_resource(&hostmem_resource, res_hostmem,
-                                       size, 0, -1,
-                                       PAGES_PER_SECTION * PAGE_SIZE, NULL, NULL);
-       }
-
-       if (!ret) {
-               /*
-                * Insert this resource into iomem. Because hostmem_resource
-                * tracks portion of guest e820 marked as UNUSABLE noone else
-                * should try to use it.
-                */
-               res->start = res_hostmem->start;
-               res->end = res_hostmem->end;
-               ret = insert_resource(&iomem_resource, res);
-               if (ret < 0) {
-                       pr_err("Can't insert iomem_resource [%llx - %llx]\n",
-                               res->start, res->end);
-                       release_memory_resource(res_hostmem);
-                       res_hostmem = NULL;
-                       res->start = res->end = 0;
-               }
-       }
-
-       if (ret) {
-               ret = allocate_resource(&iomem_resource, res,
-                                       size, 0, -1,
-                                       PAGES_PER_SECTION * PAGE_SIZE, NULL, NULL);
-               if (ret < 0) {
-                       pr_err("Cannot allocate new System RAM resource\n");
-                       kfree(res);
-                       return NULL;
-               }
+       ret = allocate_resource(&iomem_resource, res,
+                               size, 0, -1,
+                               PAGES_PER_SECTION * PAGE_SIZE, NULL, NULL);
+       if (ret < 0) {
+               pr_err("Cannot allocate new System RAM resource\n");
+               kfree(res);
+               return NULL;
        }
 
 #ifdef CONFIG_SPARSEMEM
@@ -325,7 +281,6 @@ static struct resource *additional_memory_resource(phys_addr_t size)
                        pr_err("New System RAM resource outside addressable RAM (%lu > %lu)\n",
                               pfn, limit);
                        release_memory_resource(res);
-                       release_memory_resource(res_hostmem);
                        return NULL;
                }
        }
@@ -750,8 +705,6 @@ static int __init balloon_init(void)
        set_online_page_callback(&xen_online_page);
        register_memory_notifier(&xen_memory_nb);
        register_sysctl_table(xen_root);
-
-       arch_xen_balloon_init(&hostmem_resource);
 #endif
 
 #ifdef CONFIG_XEN_PV
index 2f11ca7..77224d8 100644 (file)
@@ -385,8 +385,8 @@ static int create_active(struct sock_mapping *map, int *evtchn)
 out_error:
        if (*evtchn >= 0)
                xenbus_free_evtchn(pvcalls_front_dev, *evtchn);
-       kfree(map->active.data.in);
-       kfree(map->active.ring);
+       free_pages((unsigned long)map->active.data.in, PVCALLS_RING_ORDER);
+       free_page((unsigned long)map->active.ring);
        return ret;
 }
 
index 23f1387..e7df65d 100644 (file)
@@ -36,6 +36,7 @@
 #include <asm/xen/hypervisor.h>
 
 #include <xen/xen.h>
+#include <xen/xen-ops.h>
 #include <xen/page.h>
 #include <xen/interface/xen.h>
 #include <xen/interface/memory.h>
index 43dea3b..8a2562e 100644 (file)
@@ -1075,8 +1075,6 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
        if (fc->ac.error < 0)
                return;
 
-       d_drop(new_dentry);
-
        inode = afs_iget(fc->vnode->vfs_inode.i_sb, fc->key,
                         newfid, newstatus, newcb, fc->cbi);
        if (IS_ERR(inode)) {
@@ -1090,7 +1088,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
        vnode = AFS_FS_I(inode);
        set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
        afs_vnode_commit_status(fc, vnode, 0);
-       d_add(new_dentry, inode);
+       d_instantiate(new_dentry, inode);
 }
 
 /*
index d049cb4..fde6b4d 100644 (file)
@@ -61,8 +61,11 @@ void afs_fileserver_probe_result(struct afs_call *call)
                afs_io_error(call, afs_io_error_fs_probe_fail);
                goto out;
        case -ECONNRESET: /* Responded, but call expired. */
+       case -ERFKILL:
+       case -EADDRNOTAVAIL:
        case -ENETUNREACH:
        case -EHOSTUNREACH:
+       case -EHOSTDOWN:
        case -ECONNREFUSED:
        case -ETIMEDOUT:
        case -ETIME:
@@ -132,12 +135,14 @@ out:
 static int afs_do_probe_fileserver(struct afs_net *net,
                                   struct afs_server *server,
                                   struct key *key,
-                                  unsigned int server_index)
+                                  unsigned int server_index,
+                                  struct afs_error *_e)
 {
        struct afs_addr_cursor ac = {
                .index = 0,
        };
-       int ret;
+       bool in_progress = false;
+       int err;
 
        _enter("%pU", &server->uuid);
 
@@ -151,15 +156,17 @@ static int afs_do_probe_fileserver(struct afs_net *net,
        server->probe.rtt = UINT_MAX;
 
        for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) {
-               ret = afs_fs_get_capabilities(net, server, &ac, key, server_index,
+               err = afs_fs_get_capabilities(net, server, &ac, key, server_index,
                                              true);
-               if (ret != -EINPROGRESS) {
-                       afs_fs_probe_done(server);
-                       return ret;
-               }
+               if (err == -EINPROGRESS)
+                       in_progress = true;
+               else
+                       afs_prioritise_error(_e, err, ac.abort_code);
        }
 
-       return 0;
+       if (!in_progress)
+               afs_fs_probe_done(server);
+       return in_progress;
 }
 
 /*
@@ -169,21 +176,23 @@ int afs_probe_fileservers(struct afs_net *net, struct key *key,
                          struct afs_server_list *list)
 {
        struct afs_server *server;
-       int i, ret;
+       struct afs_error e;
+       bool in_progress = false;
+       int i;
 
+       e.error = 0;
+       e.responded = false;
        for (i = 0; i < list->nr_servers; i++) {
                server = list->servers[i].server;
                if (test_bit(AFS_SERVER_FL_PROBED, &server->flags))
                        continue;
 
-               if (!test_and_set_bit_lock(AFS_SERVER_FL_PROBING, &server->flags)) {
-                       ret = afs_do_probe_fileserver(net, server, key, i);
-                       if (ret)
-                               return ret;
-               }
+               if (!test_and_set_bit_lock(AFS_SERVER_FL_PROBING, &server->flags) &&
+                   afs_do_probe_fileserver(net, server, key, i, &e))
+                       in_progress = true;
        }
 
-       return 0;
+       return in_progress ? 0 : e.error;
 }
 
 /*
index 4c6d8e1..6b17d36 100644 (file)
@@ -382,7 +382,7 @@ void afs_zap_data(struct afs_vnode *vnode)
 int afs_validate(struct afs_vnode *vnode, struct key *key)
 {
        time64_t now = ktime_get_real_seconds();
-       bool valid = false;
+       bool valid;
        int ret;
 
        _enter("{v={%llx:%llu} fl=%lx},%x",
@@ -402,15 +402,21 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
                        vnode->cb_v_break = vnode->volume->cb_v_break;
                        valid = false;
                } else if (vnode->status.type == AFS_FTYPE_DIR &&
-                          test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) &&
-                          vnode->cb_expires_at - 10 > now) {
-                       valid = true;
-               } else if (!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) &&
-                          vnode->cb_expires_at - 10 > now) {
+                          (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) ||
+                           vnode->cb_expires_at - 10 <= now)) {
+                       valid = false;
+               } else if (test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) ||
+                          vnode->cb_expires_at - 10 <= now) {
+                       valid = false;
+               } else {
                        valid = true;
                }
        } else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
                valid = true;
+       } else {
+               vnode->cb_s_break = vnode->cb_interest->server->cb_s_break;
+               vnode->cb_v_break = vnode->volume->cb_v_break;
+               valid = false;
        }
 
        read_sequnlock_excl(&vnode->cb_lock);
index 5da3b09..8871b9e 100644 (file)
@@ -695,6 +695,14 @@ struct afs_interface {
        unsigned        mtu;            /* MTU of interface */
 };
 
+/*
+ * Error prioritisation and accumulation.
+ */
+struct afs_error {
+       short   error;                  /* Accumulated error */
+       bool    responded;              /* T if server responded */
+};
+
 /*
  * Cursor for iterating over a server's address list.
  */
@@ -1015,6 +1023,7 @@ static inline void __afs_stat(atomic_t *s)
  * misc.c
  */
 extern int afs_abort_to_error(u32);
+extern void afs_prioritise_error(struct afs_error *, int, u32);
 
 /*
  * mntpt.c
index 700a5fa..bbb1fd5 100644 (file)
@@ -118,3 +118,55 @@ int afs_abort_to_error(u32 abort_code)
        default:                return -EREMOTEIO;
        }
 }
+
+/*
+ * Select the error to report from a set of errors.
+ */
+void afs_prioritise_error(struct afs_error *e, int error, u32 abort_code)
+{
+       switch (error) {
+       case 0:
+               return;
+       default:
+               if (e->error == -ETIMEDOUT ||
+                   e->error == -ETIME)
+                       return;
+       case -ETIMEDOUT:
+       case -ETIME:
+               if (e->error == -ENOMEM ||
+                   e->error == -ENONET)
+                       return;
+       case -ENOMEM:
+       case -ENONET:
+               if (e->error == -ERFKILL)
+                       return;
+       case -ERFKILL:
+               if (e->error == -EADDRNOTAVAIL)
+                       return;
+       case -EADDRNOTAVAIL:
+               if (e->error == -ENETUNREACH)
+                       return;
+       case -ENETUNREACH:
+               if (e->error == -EHOSTUNREACH)
+                       return;
+       case -EHOSTUNREACH:
+               if (e->error == -EHOSTDOWN)
+                       return;
+       case -EHOSTDOWN:
+               if (e->error == -ECONNREFUSED)
+                       return;
+       case -ECONNREFUSED:
+               if (e->error == -ECONNRESET)
+                       return;
+       case -ECONNRESET: /* Responded, but call expired. */
+               if (e->responded)
+                       return;
+               e->error = error;
+               return;
+
+       case -ECONNABORTED:
+               e->responded = true;
+               e->error = afs_abort_to_error(abort_code);
+               return;
+       }
+}
index 0050425..c3ae324 100644 (file)
@@ -136,7 +136,8 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
        struct afs_addr_list *alist;
        struct afs_server *server;
        struct afs_vnode *vnode = fc->vnode;
-       u32 rtt, abort_code;
+       struct afs_error e;
+       u32 rtt;
        int error = fc->ac.error, i;
 
        _enter("%lx[%d],%lx[%d],%d,%d",
@@ -306,8 +307,11 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
                if (fc->error != -EDESTADDRREQ)
                        goto iterate_address;
                /* Fall through */
+       case -ERFKILL:
+       case -EADDRNOTAVAIL:
        case -ENETUNREACH:
        case -EHOSTUNREACH:
+       case -EHOSTDOWN:
        case -ECONNREFUSED:
                _debug("no conn");
                fc->error = error;
@@ -446,50 +450,15 @@ no_more_servers:
        if (fc->flags & AFS_FS_CURSOR_VBUSY)
                goto restart_from_beginning;
 
-       abort_code = 0;
-       error = -EDESTADDRREQ;
+       e.error = -EDESTADDRREQ;
+       e.responded = false;
        for (i = 0; i < fc->server_list->nr_servers; i++) {
                struct afs_server *s = fc->server_list->servers[i].server;
-               int probe_error = READ_ONCE(s->probe.error);
 
-               switch (probe_error) {
-               case 0:
-                       continue;
-               default:
-                       if (error == -ETIMEDOUT ||
-                           error == -ETIME)
-                               continue;
-               case -ETIMEDOUT:
-               case -ETIME:
-                       if (error == -ENOMEM ||
-                           error == -ENONET)
-                               continue;
-               case -ENOMEM:
-               case -ENONET:
-                       if (error == -ENETUNREACH)
-                               continue;
-               case -ENETUNREACH:
-                       if (error == -EHOSTUNREACH)
-                               continue;
-               case -EHOSTUNREACH:
-                       if (error == -ECONNREFUSED)
-                               continue;
-               case -ECONNREFUSED:
-                       if (error == -ECONNRESET)
-                               continue;
-               case -ECONNRESET: /* Responded, but call expired. */
-                       if (error == -ECONNABORTED)
-                               continue;
-               case -ECONNABORTED:
-                       abort_code = s->probe.abort_code;
-                       error = probe_error;
-                       continue;
-               }
+               afs_prioritise_error(&e, READ_ONCE(s->probe.error),
+                                    s->probe.abort_code);
        }
 
-       if (error == -ECONNABORTED)
-               error = afs_abort_to_error(abort_code);
-
 failed_set_error:
        fc->error = error;
 failed:
@@ -553,8 +522,11 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
                _leave(" = f [abort]");
                return false;
 
+       case -ERFKILL:
+       case -EADDRNOTAVAIL:
        case -ENETUNREACH:
        case -EHOSTUNREACH:
+       case -EHOSTDOWN:
        case -ECONNREFUSED:
        case -ETIMEDOUT:
        case -ETIME:
@@ -633,6 +605,7 @@ int afs_end_vnode_operation(struct afs_fs_cursor *fc)
        struct afs_net *net = afs_v2net(fc->vnode);
 
        if (fc->error == -EDESTADDRREQ ||
+           fc->error == -EADDRNOTAVAIL ||
            fc->error == -ENETUNREACH ||
            fc->error == -EHOSTUNREACH)
                afs_dump_edestaddrreq(fc);
index 5997088..a7b4486 100644 (file)
@@ -576,6 +576,7 @@ static long afs_wait_for_call_to_complete(struct afs_call *call,
 {
        signed long rtt2, timeout;
        long ret;
+       bool stalled = false;
        u64 rtt;
        u32 life, last_life;
 
@@ -609,12 +610,20 @@ static long afs_wait_for_call_to_complete(struct afs_call *call,
 
                life = rxrpc_kernel_check_life(call->net->socket, call->rxcall);
                if (timeout == 0 &&
-                   life == last_life && signal_pending(current))
+                   life == last_life && signal_pending(current)) {
+                       if (stalled)
                                break;
+                       __set_current_state(TASK_RUNNING);
+                       rxrpc_kernel_probe_life(call->net->socket, call->rxcall);
+                       timeout = rtt2;
+                       stalled = true;
+                       continue;
+               }
 
                if (life != last_life) {
                        timeout = rtt2;
                        last_life = life;
+                       stalled = false;
                }
 
                timeout = schedule_timeout(timeout);
index c0f616b..f0b0329 100644 (file)
@@ -61,8 +61,11 @@ void afs_vlserver_probe_result(struct afs_call *call)
                afs_io_error(call, afs_io_error_vl_probe_fail);
                goto out;
        case -ECONNRESET: /* Responded, but call expired. */
+       case -ERFKILL:
+       case -EADDRNOTAVAIL:
        case -ENETUNREACH:
        case -EHOSTUNREACH:
+       case -EHOSTDOWN:
        case -ECONNREFUSED:
        case -ETIMEDOUT:
        case -ETIME:
@@ -129,15 +132,17 @@ out:
  * Probe all of a vlserver's addresses to find out the best route and to
  * query its capabilities.
  */
-static int afs_do_probe_vlserver(struct afs_net *net,
-                                struct afs_vlserver *server,
-                                struct key *key,
-                                unsigned int server_index)
+static bool afs_do_probe_vlserver(struct afs_net *net,
+                                 struct afs_vlserver *server,
+                                 struct key *key,
+                                 unsigned int server_index,
+                                 struct afs_error *_e)
 {
        struct afs_addr_cursor ac = {
                .index = 0,
        };
-       int ret;
+       bool in_progress = false;
+       int err;
 
        _enter("%s", server->name);
 
@@ -151,15 +156,17 @@ static int afs_do_probe_vlserver(struct afs_net *net,
        server->probe.rtt = UINT_MAX;
 
        for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) {
-               ret = afs_vl_get_capabilities(net, &ac, key, server,
+               err = afs_vl_get_capabilities(net, &ac, key, server,
                                              server_index, true);
-               if (ret != -EINPROGRESS) {
-                       afs_vl_probe_done(server);
-                       return ret;
-               }
+               if (err == -EINPROGRESS)
+                       in_progress = true;
+               else
+                       afs_prioritise_error(_e, err, ac.abort_code);
        }
 
-       return 0;
+       if (!in_progress)
+               afs_vl_probe_done(server);
+       return in_progress;
 }
 
 /*
@@ -169,21 +176,23 @@ int afs_send_vl_probes(struct afs_net *net, struct key *key,
                       struct afs_vlserver_list *vllist)
 {
        struct afs_vlserver *server;
-       int i, ret;
+       struct afs_error e;
+       bool in_progress = false;
+       int i;
 
+       e.error = 0;
+       e.responded = false;
        for (i = 0; i < vllist->nr_servers; i++) {
                server = vllist->servers[i].server;
                if (test_bit(AFS_VLSERVER_FL_PROBED, &server->flags))
                        continue;
 
-               if (!test_and_set_bit_lock(AFS_VLSERVER_FL_PROBING, &server->flags)) {
-                       ret = afs_do_probe_vlserver(net, server, key, i);
-                       if (ret)
-                               return ret;
-               }
+               if (!test_and_set_bit_lock(AFS_VLSERVER_FL_PROBING, &server->flags) &&
+                   afs_do_probe_vlserver(net, server, key, i, &e))
+                       in_progress = true;
        }
 
-       return 0;
+       return in_progress ? 0 : e.error;
 }
 
 /*
index b64a284..7adde83 100644 (file)
@@ -71,8 +71,9 @@ bool afs_select_vlserver(struct afs_vl_cursor *vc)
 {
        struct afs_addr_list *alist;
        struct afs_vlserver *vlserver;
+       struct afs_error e;
        u32 rtt;
-       int error = vc->ac.error, abort_code, i;
+       int error = vc->ac.error, i;
 
        _enter("%lx[%d],%lx[%d],%d,%d",
               vc->untried, vc->index,
@@ -119,8 +120,11 @@ bool afs_select_vlserver(struct afs_vl_cursor *vc)
                        goto failed;
                }
 
+       case -ERFKILL:
+       case -EADDRNOTAVAIL:
        case -ENETUNREACH:
        case -EHOSTUNREACH:
+       case -EHOSTDOWN:
        case -ECONNREFUSED:
        case -ETIMEDOUT:
        case -ETIME:
@@ -235,50 +239,15 @@ no_more_servers:
        if (vc->flags & AFS_VL_CURSOR_RETRY)
                goto restart_from_beginning;
 
-       abort_code = 0;
-       error = -EDESTADDRREQ;
+       e.error = -EDESTADDRREQ;
+       e.responded = false;
        for (i = 0; i < vc->server_list->nr_servers; i++) {
                struct afs_vlserver *s = vc->server_list->servers[i].server;
-               int probe_error = READ_ONCE(s->probe.error);
 
-               switch (probe_error) {
-               case 0:
-                       continue;
-               default:
-                       if (error == -ETIMEDOUT ||
-                           error == -ETIME)
-                               continue;
-               case -ETIMEDOUT:
-               case -ETIME:
-                       if (error == -ENOMEM ||
-                           error == -ENONET)
-                               continue;
-               case -ENOMEM:
-               case -ENONET:
-                       if (error == -ENETUNREACH)
-                               continue;
-               case -ENETUNREACH:
-                       if (error == -EHOSTUNREACH)
-                               continue;
-               case -EHOSTUNREACH:
-                       if (error == -ECONNREFUSED)
-                               continue;
-               case -ECONNREFUSED:
-                       if (error == -ECONNRESET)
-                               continue;
-               case -ECONNRESET: /* Responded, but call expired. */
-                       if (error == -ECONNABORTED)
-                               continue;
-               case -ECONNABORTED:
-                       abort_code = s->probe.abort_code;
-                       error = probe_error;
-                       continue;
-               }
+               afs_prioritise_error(&e, READ_ONCE(s->probe.error),
+                                    s->probe.abort_code);
        }
 
-       if (error == -ECONNABORTED)
-               error = afs_abort_to_error(abort_code);
-
 failed_set_error:
        vc->error = error;
 failed:
@@ -341,6 +310,7 @@ int afs_end_vlserver_operation(struct afs_vl_cursor *vc)
        struct afs_net *net = vc->cell->net;
 
        if (vc->error == -EDESTADDRREQ ||
+           vc->error == -EADDRNOTAVAIL ||
            vc->error == -ENETUNREACH ||
            vc->error == -EHOSTUNREACH)
                afs_vl_dump_edestaddrreq(vc);
index 301e631..aac9659 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -45,6 +45,7 @@
 
 #include <asm/kmap_types.h>
 #include <linux/uaccess.h>
+#include <linux/nospec.h>
 
 #include "internal.h"
 
@@ -1038,6 +1039,7 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id)
        if (!table || id >= table->nr)
                goto out;
 
+       id = array_index_nospec(id, table->nr);
        ctx = rcu_dereference(table->table[id]);
        if (ctx && ctx->user_id == ctx_id) {
                if (percpu_ref_tryget_live(&ctx->users))
@@ -1436,6 +1438,7 @@ static int aio_prep_rw(struct kiocb *req, struct iocb *iocb)
                ret = ioprio_check_cap(iocb->aio_reqprio);
                if (ret) {
                        pr_debug("aio ioprio check cap error: %d\n", ret);
+                       fput(req->ki_filp);
                        return ret;
                }
 
index 3f0b6d1..6d77671 100644 (file)
@@ -477,9 +477,9 @@ static int btree_read_extent_buffer_pages(struct btrfs_fs_info *fs_info,
        int mirror_num = 0;
        int failed_mirror = 0;
 
-       clear_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);
        io_tree = &BTRFS_I(fs_info->btree_inode)->io_tree;
        while (1) {
+               clear_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);
                ret = read_extent_buffer_pages(io_tree, eb, WAIT_COMPLETE,
                                               mirror_num);
                if (!ret) {
@@ -493,15 +493,6 @@ static int btree_read_extent_buffer_pages(struct btrfs_fs_info *fs_info,
                                break;
                }
 
-               /*
-                * This buffer's crc is fine, but its contents are corrupted, so
-                * there is no reason to read the other copies, they won't be
-                * any less wrong.
-                */
-               if (test_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags) ||
-                   ret == -EUCLEAN)
-                       break;
-
                num_copies = btrfs_num_copies(fs_info,
                                              eb->start, eb->len);
                if (num_copies == 1)
index a3c22e1..58e93bc 100644 (file)
@@ -2088,6 +2088,30 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
 
        atomic_inc(&root->log_batch);
 
+       /*
+        * Before we acquired the inode's lock, someone may have dirtied more
+        * pages in the target range. We need to make sure that writeback for
+        * any such pages does not start while we are logging the inode, because
+        * if it does, any of the following might happen when we are not doing a
+        * full inode sync:
+        *
+        * 1) We log an extent after its writeback finishes but before its
+        *    checksums are added to the csum tree, leading to -EIO errors
+        *    when attempting to read the extent after a log replay.
+        *
+        * 2) We can end up logging an extent before its writeback finishes.
+        *    Therefore after the log replay we will have a file extent item
+        *    pointing to an unwritten extent (and no data checksums as well).
+        *
+        * So trigger writeback for any eventual new dirty pages and then we
+        * wait for all ordered extents to complete below.
+        */
+       ret = start_ordered_ops(inode, start, end);
+       if (ret) {
+               inode_unlock(inode);
+               goto out;
+       }
+
        /*
         * We have to do this here to avoid the priority inversion of waiting on
         * IO of a lower priority task while holding a transaciton open.
index 45868fd..f70825a 100644 (file)
@@ -2659,7 +2659,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
        int i;
        u64 *i_qgroups;
        struct btrfs_fs_info *fs_info = trans->fs_info;
-       struct btrfs_root *quota_root = fs_info->quota_root;
+       struct btrfs_root *quota_root;
        struct btrfs_qgroup *srcgroup;
        struct btrfs_qgroup *dstgroup;
        u32 level_size = 0;
@@ -2669,6 +2669,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
        if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
                goto out;
 
+       quota_root = fs_info->quota_root;
        if (!quota_root) {
                ret = -EINVAL;
                goto out;
index 924116f..a3f75b8 100644 (file)
@@ -3959,6 +3959,7 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
 restart:
                if (update_backref_cache(trans, &rc->backref_cache)) {
                        btrfs_end_transaction(trans);
+                       trans = NULL;
                        continue;
                }
 
index 094cc14..5be83b5 100644 (file)
@@ -3340,7 +3340,8 @@ static void free_pending_move(struct send_ctx *sctx, struct pending_dir_move *m)
        kfree(m);
 }
 
-static void tail_append_pending_moves(struct pending_dir_move *moves,
+static void tail_append_pending_moves(struct send_ctx *sctx,
+                                     struct pending_dir_move *moves,
                                      struct list_head *stack)
 {
        if (list_empty(&moves->list)) {
@@ -3351,6 +3352,10 @@ static void tail_append_pending_moves(struct pending_dir_move *moves,
                list_add_tail(&moves->list, stack);
                list_splice_tail(&list, stack);
        }
+       if (!RB_EMPTY_NODE(&moves->node)) {
+               rb_erase(&moves->node, &sctx->pending_dir_moves);
+               RB_CLEAR_NODE(&moves->node);
+       }
 }
 
 static int apply_children_dir_moves(struct send_ctx *sctx)
@@ -3365,7 +3370,7 @@ static int apply_children_dir_moves(struct send_ctx *sctx)
                return 0;
 
        INIT_LIST_HEAD(&stack);
-       tail_append_pending_moves(pm, &stack);
+       tail_append_pending_moves(sctx, pm, &stack);
 
        while (!list_empty(&stack)) {
                pm = list_first_entry(&stack, struct pending_dir_move, list);
@@ -3376,7 +3381,7 @@ static int apply_children_dir_moves(struct send_ctx *sctx)
                        goto out;
                pm = get_pending_dir_moves(sctx, parent_ino);
                if (pm)
-                       tail_append_pending_moves(pm, &stack);
+                       tail_append_pending_moves(sctx, pm, &stack);
        }
        return 0;
 
index cbc9d0d..645fc81 100644 (file)
@@ -2237,6 +2237,7 @@ static long btrfs_control_ioctl(struct file *file, unsigned int cmd,
        vol = memdup_user((void __user *)arg, sizeof(*vol));
        if (IS_ERR(vol))
                return PTR_ERR(vol);
+       vol->name[BTRFS_PATH_NAME_MAX] = '\0';
 
        switch (cmd) {
        case BTRFS_IOC_SCAN_DEV:
index efcf89a..1a4e2b1 100644 (file)
@@ -389,13 +389,11 @@ static int check_block_group_item(struct btrfs_fs_info *fs_info,
 
        /*
         * Here we don't really care about alignment since extent allocator can
-        * handle it.  We care more about the size, as if one block group is
-        * larger than maximum size, it's must be some obvious corruption.
+        * handle it.  We care more about the size.
         */
-       if (key->offset > BTRFS_MAX_DATA_CHUNK_SIZE || key->offset == 0) {
+       if (key->offset == 0) {
                block_group_err(fs_info, leaf, slot,
-                       "invalid block group size, have %llu expect (0, %llu]",
-                               key->offset, BTRFS_MAX_DATA_CHUNK_SIZE);
+                               "invalid block group size 0");
                return -EUCLEAN;
        }
 
index 95983c7..1645fcf 100644 (file)
@@ -244,11 +244,13 @@ wait_for_old_object:
 
        ASSERT(!test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags));
 
-       cache->cache.ops->put_object(&xobject->fscache, cachefiles_obj_put_wait_retry);
+       cache->cache.ops->put_object(&xobject->fscache,
+               (enum fscache_obj_ref_trace)cachefiles_obj_put_wait_retry);
        goto try_again;
 
 requeue:
-       cache->cache.ops->put_object(&xobject->fscache, cachefiles_obj_put_wait_timeo);
+       cache->cache.ops->put_object(&xobject->fscache,
+               (enum fscache_obj_ref_trace)cachefiles_obj_put_wait_timeo);
        _leave(" = -ETIMEDOUT");
        return -ETIMEDOUT;
 }
@@ -336,7 +338,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
 try_again:
        /* first step is to make up a grave dentry in the graveyard */
        sprintf(nbuffer, "%08x%08x",
-               (uint32_t) get_seconds(),
+               (uint32_t) ktime_get_real_seconds(),
                (uint32_t) atomic_inc_return(&cache->gravecounter));
 
        /* do the multiway lock magic */
index 40f7595..8a57740 100644 (file)
@@ -535,7 +535,10 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
                                            netpage->index, cachefiles_gfp);
                if (ret < 0) {
                        if (ret == -EEXIST) {
+                               put_page(backpage);
+                               backpage = NULL;
                                put_page(netpage);
+                               netpage = NULL;
                                fscache_retrieval_complete(op, 1);
                                continue;
                        }
@@ -608,7 +611,10 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
                                            netpage->index, cachefiles_gfp);
                if (ret < 0) {
                        if (ret == -EEXIST) {
+                               put_page(backpage);
+                               backpage = NULL;
                                put_page(netpage);
+                               netpage = NULL;
                                fscache_retrieval_complete(op, 1);
                                continue;
                        }
@@ -962,11 +968,8 @@ void cachefiles_uncache_page(struct fscache_object *_object, struct page *page)
        __releases(&object->fscache.cookie->lock)
 {
        struct cachefiles_object *object;
-       struct cachefiles_cache *cache;
 
        object = container_of(_object, struct cachefiles_object, fscache);
-       cache = container_of(object->fscache.cache,
-                            struct cachefiles_cache, cache);
 
        _enter("%p,{%lu}", object, page->index);
 
index 0a29a00..511e6c6 100644 (file)
@@ -135,7 +135,8 @@ int cachefiles_update_object_xattr(struct cachefiles_object *object,
        struct dentry *dentry = object->dentry;
        int ret;
 
-       ASSERT(dentry);
+       if (!dentry)
+               return -ESTALE;
 
        _enter("%p,#%d", object, auxdata->len);
 
index b5ecd6f..4e9a7cc 100644 (file)
@@ -563,8 +563,8 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root)
                seq_puts(m, ",noacl");
 #endif
 
-       if (fsopt->flags & CEPH_MOUNT_OPT_NOCOPYFROM)
-               seq_puts(m, ",nocopyfrom");
+       if ((fsopt->flags & CEPH_MOUNT_OPT_NOCOPYFROM) == 0)
+               seq_puts(m, ",copyfrom");
 
        if (fsopt->mds_namespace)
                seq_show_option(m, "mds_namespace", fsopt->mds_namespace);
index c005a54..79a265b 100644 (file)
@@ -42,7 +42,9 @@
 #define CEPH_MOUNT_OPT_NOQUOTADF       (1<<13) /* no root dir quota in statfs */
 #define CEPH_MOUNT_OPT_NOCOPYFROM      (1<<14) /* don't use RADOS 'copy-from' op */
 
-#define CEPH_MOUNT_OPT_DEFAULT    CEPH_MOUNT_OPT_DCACHE
+#define CEPH_MOUNT_OPT_DEFAULT                 \
+       (CEPH_MOUNT_OPT_DCACHE |                \
+        CEPH_MOUNT_OPT_NOCOPYFROM)
 
 #define ceph_set_mount_opt(fsc, opt) \
        (fsc)->mount_options->flags |= CEPH_MOUNT_OPT_##opt;
index abcd78e..85dadb9 100644 (file)
@@ -133,7 +133,7 @@ config CIFS_XATTR
 
 config CIFS_POSIX
         bool "CIFS POSIX Extensions"
-        depends on CIFS_XATTR
+        depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY && CIFS_XATTR
         help
           Enabling this option will cause the cifs client to attempt to
          negotiate a newer dialect with servers, such as Samba 3.0.5
index 3713d22..907e85d 100644 (file)
@@ -174,7 +174,7 @@ cifs_bp_rename_retry:
 
                cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath);
                memcpy(full_path+dfsplen+1, cifs_sb->prepath, pplen-1);
-               full_path[dfsplen] = '\\';
+               full_path[dfsplen] = dirsep;
                for (i = 0; i < pplen-1; i++)
                        if (full_path[dfsplen+1+i] == '/')
                                full_path[dfsplen+1+i] = CIFS_DIR_SEP(cifs_sb);
index 74c33d5..c9bc56b 100644 (file)
@@ -2541,14 +2541,13 @@ static int
 cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
        struct cifs_aio_ctx *ctx)
 {
-       int wait_retry = 0;
        unsigned int wsize, credits;
        int rc;
        struct TCP_Server_Info *server =
                tlink_tcon(wdata->cfile->tlink)->ses->server;
 
        /*
-        * Try to resend this wdata, waiting for credits up to 3 seconds.
+        * Wait for credits to resend this wdata.
         * Note: we are attempting to resend the whole wdata not in segments
         */
        do {
@@ -2556,19 +2555,13 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
                        server, wdata->bytes, &wsize, &credits);
 
                if (rc)
-                       break;
+                       goto out;
 
                if (wsize < wdata->bytes) {
                        add_credits_and_wake_if(server, credits, 0);
                        msleep(1000);
-                       wait_retry++;
                }
-       } while (wsize < wdata->bytes && wait_retry < 3);
-
-       if (wsize < wdata->bytes) {
-               rc = -EBUSY;
-               goto out;
-       }
+       } while (wsize < wdata->bytes);
 
        rc = -EAGAIN;
        while (rc == -EAGAIN) {
@@ -3234,14 +3227,13 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
                        struct list_head *rdata_list,
                        struct cifs_aio_ctx *ctx)
 {
-       int wait_retry = 0;
        unsigned int rsize, credits;
        int rc;
        struct TCP_Server_Info *server =
                tlink_tcon(rdata->cfile->tlink)->ses->server;
 
        /*
-        * Try to resend this rdata, waiting for credits up to 3 seconds.
+        * Wait for credits to resend this rdata.
         * Note: we are attempting to resend the whole rdata not in segments
         */
        do {
@@ -3249,24 +3241,13 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
                                                &rsize, &credits);
 
                if (rc)
-                       break;
+                       goto out;
 
                if (rsize < rdata->bytes) {
                        add_credits_and_wake_if(server, credits, 0);
                        msleep(1000);
-                       wait_retry++;
                }
-       } while (rsize < rdata->bytes && wait_retry < 3);
-
-       /*
-        * If we can't find enough credits to send this rdata
-        * release the rdata and return failure, this will pass
-        * whatever I/O amount we have finished to VFS.
-        */
-       if (rsize < rdata->bytes) {
-               rc = -EBUSY;
-               goto out;
-       }
+       } while (rsize < rdata->bytes);
 
        rc = -EAGAIN;
        while (rc == -EAGAIN) {
index 616e36e..48132ec 100644 (file)
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -98,12 +98,6 @@ static void *dax_make_entry(pfn_t pfn, unsigned long flags)
        return xa_mk_value(flags | (pfn_t_to_pfn(pfn) << DAX_SHIFT));
 }
 
-static void *dax_make_page_entry(struct page *page)
-{
-       pfn_t pfn = page_to_pfn_t(page);
-       return dax_make_entry(pfn, PageHead(page) ? DAX_PMD : 0);
-}
-
 static bool dax_is_locked(void *entry)
 {
        return xa_to_value(entry) & DAX_LOCKED;
@@ -116,12 +110,12 @@ static unsigned int dax_entry_order(void *entry)
        return 0;
 }
 
-static int dax_is_pmd_entry(void *entry)
+static unsigned long dax_is_pmd_entry(void *entry)
 {
        return xa_to_value(entry) & DAX_PMD;
 }
 
-static int dax_is_pte_entry(void *entry)
+static bool dax_is_pte_entry(void *entry)
 {
        return !(xa_to_value(entry) & DAX_PMD);
 }
@@ -222,9 +216,8 @@ static void *get_unlocked_entry(struct xa_state *xas)
        ewait.wait.func = wake_exceptional_entry_func;
 
        for (;;) {
-               entry = xas_load(xas);
-               if (!entry || xa_is_internal(entry) ||
-                               WARN_ON_ONCE(!xa_is_value(entry)) ||
+               entry = xas_find_conflict(xas);
+               if (!entry || WARN_ON_ONCE(!xa_is_value(entry)) ||
                                !dax_is_locked(entry))
                        return entry;
 
@@ -239,6 +232,34 @@ static void *get_unlocked_entry(struct xa_state *xas)
        }
 }
 
+/*
+ * The only thing keeping the address space around is the i_pages lock
+ * (it's cycled in clear_inode() after removing the entries from i_pages)
+ * After we call xas_unlock_irq(), we cannot touch xas->xa.
+ */
+static void wait_entry_unlocked(struct xa_state *xas, void *entry)
+{
+       struct wait_exceptional_entry_queue ewait;
+       wait_queue_head_t *wq;
+
+       init_wait(&ewait.wait);
+       ewait.wait.func = wake_exceptional_entry_func;
+
+       wq = dax_entry_waitqueue(xas, entry, &ewait.key);
+       prepare_to_wait_exclusive(wq, &ewait.wait, TASK_UNINTERRUPTIBLE);
+       xas_unlock_irq(xas);
+       schedule();
+       finish_wait(wq, &ewait.wait);
+
+       /*
+        * Entry lock waits are exclusive. Wake up the next waiter since
+        * we aren't sure we will acquire the entry lock and thus wake
+        * the next waiter up on unlock.
+        */
+       if (waitqueue_active(wq))
+               __wake_up(wq, TASK_NORMAL, 1, &ewait.key);
+}
+
 static void put_unlocked_entry(struct xa_state *xas, void *entry)
 {
        /* If we were the only waiter woken, wake the next one */
@@ -255,6 +276,7 @@ static void dax_unlock_entry(struct xa_state *xas, void *entry)
 {
        void *old;
 
+       BUG_ON(dax_is_locked(entry));
        xas_reset(xas);
        xas_lock_irq(xas);
        old = xas_store(xas, entry);
@@ -352,16 +374,27 @@ static struct page *dax_busy_page(void *entry)
        return NULL;
 }
 
-bool dax_lock_mapping_entry(struct page *page)
+/*
+ * dax_lock_mapping_entry - Lock the DAX entry corresponding to a page
+ * @page: The page whose entry we want to lock
+ *
+ * Context: Process context.
+ * Return: A cookie to pass to dax_unlock_page() or 0 if the entry could
+ * not be locked.
+ */
+dax_entry_t dax_lock_page(struct page *page)
 {
        XA_STATE(xas, NULL, 0);
        void *entry;
 
+       /* Ensure page->mapping isn't freed while we look at it */
+       rcu_read_lock();
        for (;;) {
                struct address_space *mapping = READ_ONCE(page->mapping);
 
-               if (!dax_mapping(mapping))
-                       return false;
+               entry = NULL;
+               if (!mapping || !dax_mapping(mapping))
+                       break;
 
                /*
                 * In the device-dax case there's no need to lock, a
@@ -370,8 +403,9 @@ bool dax_lock_mapping_entry(struct page *page)
                 * otherwise we would not have a valid pfn_to_page()
                 * translation.
                 */
+               entry = (void *)~0UL;
                if (S_ISCHR(mapping->host->i_mode))
-                       return true;
+                       break;
 
                xas.xa = &mapping->i_pages;
                xas_lock_irq(&xas);
@@ -382,20 +416,20 @@ bool dax_lock_mapping_entry(struct page *page)
                xas_set(&xas, page->index);
                entry = xas_load(&xas);
                if (dax_is_locked(entry)) {
-                       entry = get_unlocked_entry(&xas);
-                       /* Did the page move while we slept? */
-                       if (dax_to_pfn(entry) != page_to_pfn(page)) {
-                               xas_unlock_irq(&xas);
-                               continue;
-                       }
+                       rcu_read_unlock();
+                       wait_entry_unlocked(&xas, entry);
+                       rcu_read_lock();
+                       continue;
                }
                dax_lock_entry(&xas, entry);
                xas_unlock_irq(&xas);
-               return true;
+               break;
        }
+       rcu_read_unlock();
+       return (dax_entry_t)entry;
 }
 
-void dax_unlock_mapping_entry(struct page *page)
+void dax_unlock_page(struct page *page, dax_entry_t cookie)
 {
        struct address_space *mapping = page->mapping;
        XA_STATE(xas, &mapping->i_pages, page->index);
@@ -403,7 +437,7 @@ void dax_unlock_mapping_entry(struct page *page)
        if (S_ISCHR(mapping->host->i_mode))
                return;
 
-       dax_unlock_entry(&xas, dax_make_page_entry(page));
+       dax_unlock_entry(&xas, (void *)cookie);
 }
 
 /*
@@ -445,11 +479,9 @@ static void *grab_mapping_entry(struct xa_state *xas,
 retry:
        xas_lock_irq(xas);
        entry = get_unlocked_entry(xas);
-       if (xa_is_internal(entry))
-               goto fallback;
 
        if (entry) {
-               if (WARN_ON_ONCE(!xa_is_value(entry))) {
+               if (!xa_is_value(entry)) {
                        xas_set_err(xas, EIO);
                        goto out_unlock;
                }
@@ -1628,8 +1660,7 @@ dax_insert_pfn_mkwrite(struct vm_fault *vmf, pfn_t pfn, unsigned int order)
        /* Did we race with someone splitting entry or so? */
        if (!entry ||
            (order == 0 && !dax_is_pte_entry(entry)) ||
-           (order == PMD_ORDER && (xa_is_internal(entry) ||
-                                   !dax_is_pmd_entry(entry)))) {
+           (order == PMD_ORDER && !dax_is_pmd_entry(entry))) {
                put_unlocked_entry(&xas, entry);
                xas_unlock_irq(&xas);
                trace_dax_insert_pfn_mkwrite_no_entry(mapping->host, vmf,
index 722d17c..41a0e97 100644 (file)
@@ -325,8 +325,8 @@ static ssize_t dio_complete(struct dio *dio, ssize_t ret, unsigned int flags)
                 */
                dio->iocb->ki_pos += transferred;
 
-               if (dio->op == REQ_OP_WRITE)
-                       ret = generic_write_sync(dio->iocb,  transferred);
+               if (ret > 0 && dio->op == REQ_OP_WRITE)
+                       ret = generic_write_sync(dio->iocb, ret);
                dio->iocb->ki_complete(dio->iocb, ret, 0);
        }
 
index 645158d..c69927b 100644 (file)
@@ -77,7 +77,7 @@ static bool dentry_connected(struct dentry *dentry)
                struct dentry *parent = dget_parent(dentry);
 
                dput(dentry);
-               if (IS_ROOT(dentry)) {
+               if (dentry == parent) {
                        dput(parent);
                        return false;
                }
@@ -147,6 +147,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
        tmp = lookup_one_len_unlocked(nbuf, parent, strlen(nbuf));
        if (IS_ERR(tmp)) {
                dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp));
+               err = PTR_ERR(tmp);
                goto out_err;
        }
        if (tmp != dentry) {
index cb91baa..eb11502 100644 (file)
@@ -892,6 +892,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
        if (sb->s_magic != EXT2_SUPER_MAGIC)
                goto cantfind_ext2;
 
+       opts.s_mount_opt = 0;
        /* Set defaults before we parse the mount options */
        def_mount_opts = le32_to_cpu(es->s_default_mount_opts);
        if (def_mount_opts & EXT2_DEFM_DEBUG)
index 62d9a65..dd8f10d 100644 (file)
@@ -612,9 +612,9 @@ skip_replace:
        }
 
 cleanup:
-       brelse(bh);
        if (!(bh && header == HDR(bh)))
                kfree(header);
+       brelse(bh);
        up_write(&EXT2_I(inode)->xattr_sem);
 
        return error;
index 9edc920..6d9cb17 100644 (file)
@@ -730,6 +730,9 @@ static const struct fscache_state *fscache_drop_object(struct fscache_object *ob
 
        if (awaken)
                wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING);
+       if (test_and_clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags))
+               wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP);
+
 
        /* Prevent a race with our last child, which has to signal EV_CLEARED
         * before dropping our spinlock.
index ae813e6..a5e516a 100644 (file)
@@ -165,9 +165,13 @@ static bool fuse_block_alloc(struct fuse_conn *fc, bool for_background)
 
 static void fuse_drop_waiting(struct fuse_conn *fc)
 {
-       if (fc->connected) {
-               atomic_dec(&fc->num_waiting);
-       } else if (atomic_dec_and_test(&fc->num_waiting)) {
+       /*
+        * lockess check of fc->connected is okay, because atomic_dec_and_test()
+        * provides a memory barrier mached with the one in fuse_wait_aborted()
+        * to ensure no wake-up is missed.
+        */
+       if (atomic_dec_and_test(&fc->num_waiting) &&
+           !READ_ONCE(fc->connected)) {
                /* wake up aborters */
                wake_up_all(&fc->blocked_waitq);
        }
@@ -1768,8 +1772,10 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
        req->in.args[1].size = total_len;
 
        err = fuse_request_send_notify_reply(fc, req, outarg->notify_unique);
-       if (err)
+       if (err) {
                fuse_retrieve_end(fc, req);
+               fuse_put_request(fc, req);
+       }
 
        return err;
 }
@@ -2219,6 +2225,8 @@ EXPORT_SYMBOL_GPL(fuse_abort_conn);
 
 void fuse_wait_aborted(struct fuse_conn *fc)
 {
+       /* matches implicit memory barrier in fuse_drop_waiting() */
+       smp_mb();
        wait_event(fc->blocked_waitq, atomic_read(&fc->num_waiting) == 0);
 }
 
index 47395b0..e909678 100644 (file)
@@ -1119,8 +1119,10 @@ static int fuse_permission(struct inode *inode, int mask)
        if (fc->default_permissions ||
            ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) {
                struct fuse_inode *fi = get_fuse_inode(inode);
+               u32 perm_mask = STATX_MODE | STATX_UID | STATX_GID;
 
-               if (time_before64(fi->i_time, get_jiffies_64())) {
+               if (perm_mask & READ_ONCE(fi->inval_mask) ||
+                   time_before64(fi->i_time, get_jiffies_64())) {
                        refreshed = true;
 
                        err = fuse_perm_getattr(inode, mask);
@@ -1241,7 +1243,7 @@ static int fuse_dir_open(struct inode *inode, struct file *file)
 
 static int fuse_dir_release(struct inode *inode, struct file *file)
 {
-       fuse_release_common(file, FUSE_RELEASEDIR);
+       fuse_release_common(file, true);
 
        return 0;
 }
@@ -1249,7 +1251,25 @@ static int fuse_dir_release(struct inode *inode, struct file *file)
 static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end,
                          int datasync)
 {
-       return fuse_fsync_common(file, start, end, datasync, 1);
+       struct inode *inode = file->f_mapping->host;
+       struct fuse_conn *fc = get_fuse_conn(inode);
+       int err;
+
+       if (is_bad_inode(inode))
+               return -EIO;
+
+       if (fc->no_fsyncdir)
+               return 0;
+
+       inode_lock(inode);
+       err = fuse_fsync_common(file, start, end, datasync, FUSE_FSYNCDIR);
+       if (err == -ENOSYS) {
+               fc->no_fsyncdir = 1;
+               err = 0;
+       }
+       inode_unlock(inode);
+
+       return err;
 }
 
 static long fuse_dir_ioctl(struct file *file, unsigned int cmd,
index cc2121b..ffaffe1 100644 (file)
@@ -89,12 +89,12 @@ static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req)
        iput(req->misc.release.inode);
 }
 
-static void fuse_file_put(struct fuse_file *ff, bool sync)
+static void fuse_file_put(struct fuse_file *ff, bool sync, bool isdir)
 {
        if (refcount_dec_and_test(&ff->count)) {
                struct fuse_req *req = ff->reserved_req;
 
-               if (ff->fc->no_open) {
+               if (ff->fc->no_open && !isdir) {
                        /*
                         * Drop the release request when client does not
                         * implement 'open'
@@ -247,10 +247,11 @@ static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode)
        req->in.args[0].value = inarg;
 }
 
-void fuse_release_common(struct file *file, int opcode)
+void fuse_release_common(struct file *file, bool isdir)
 {
        struct fuse_file *ff = file->private_data;
        struct fuse_req *req = ff->reserved_req;
+       int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE;
 
        fuse_prepare_release(ff, file->f_flags, opcode);
 
@@ -272,7 +273,7 @@ void fuse_release_common(struct file *file, int opcode)
         * synchronous RELEASE is allowed (and desirable) in this case
         * because the server can be trusted not to screw up.
         */
-       fuse_file_put(ff, ff->fc->destroy_req != NULL);
+       fuse_file_put(ff, ff->fc->destroy_req != NULL, isdir);
 }
 
 static int fuse_open(struct inode *inode, struct file *file)
@@ -288,7 +289,7 @@ static int fuse_release(struct inode *inode, struct file *file)
        if (fc->writeback_cache)
                write_inode_now(inode, 1);
 
-       fuse_release_common(file, FUSE_RELEASE);
+       fuse_release_common(file, false);
 
        /* return value is ignored by VFS */
        return 0;
@@ -302,7 +303,7 @@ void fuse_sync_release(struct fuse_file *ff, int flags)
         * iput(NULL) is a no-op and since the refcount is 1 and everything's
         * synchronous, we are fine with not doing igrab() here"
         */
-       fuse_file_put(ff, true);
+       fuse_file_put(ff, true, false);
 }
 EXPORT_SYMBOL_GPL(fuse_sync_release);
 
@@ -441,13 +442,30 @@ static int fuse_flush(struct file *file, fl_owner_t id)
 }
 
 int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
-                     int datasync, int isdir)
+                     int datasync, int opcode)
 {
        struct inode *inode = file->f_mapping->host;
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_file *ff = file->private_data;
        FUSE_ARGS(args);
        struct fuse_fsync_in inarg;
+
+       memset(&inarg, 0, sizeof(inarg));
+       inarg.fh = ff->fh;
+       inarg.fsync_flags = datasync ? 1 : 0;
+       args.in.h.opcode = opcode;
+       args.in.h.nodeid = get_node_id(inode);
+       args.in.numargs = 1;
+       args.in.args[0].size = sizeof(inarg);
+       args.in.args[0].value = &inarg;
+       return fuse_simple_request(fc, &args);
+}
+
+static int fuse_fsync(struct file *file, loff_t start, loff_t end,
+                     int datasync)
+{
+       struct inode *inode = file->f_mapping->host;
+       struct fuse_conn *fc = get_fuse_conn(inode);
        int err;
 
        if (is_bad_inode(inode))
@@ -479,34 +497,18 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
        if (err)
                goto out;
 
-       if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
+       if (fc->no_fsync)
                goto out;
 
-       memset(&inarg, 0, sizeof(inarg));
-       inarg.fh = ff->fh;
-       inarg.fsync_flags = datasync ? 1 : 0;
-       args.in.h.opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC;
-       args.in.h.nodeid = get_node_id(inode);
-       args.in.numargs = 1;
-       args.in.args[0].size = sizeof(inarg);
-       args.in.args[0].value = &inarg;
-       err = fuse_simple_request(fc, &args);
+       err = fuse_fsync_common(file, start, end, datasync, FUSE_FSYNC);
        if (err == -ENOSYS) {
-               if (isdir)
-                       fc->no_fsyncdir = 1;
-               else
-                       fc->no_fsync = 1;
+               fc->no_fsync = 1;
                err = 0;
        }
 out:
        inode_unlock(inode);
-       return err;
-}
 
-static int fuse_fsync(struct file *file, loff_t start, loff_t end,
-                     int datasync)
-{
-       return fuse_fsync_common(file, start, end, datasync, 0);
+       return err;
 }
 
 void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos,
@@ -807,7 +809,7 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
                put_page(page);
        }
        if (req->ff)
-               fuse_file_put(req->ff, false);
+               fuse_file_put(req->ff, false, false);
 }
 
 static void fuse_send_readpages(struct fuse_req *req, struct file *file)
@@ -1460,7 +1462,7 @@ static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req)
                __free_page(req->pages[i]);
 
        if (req->ff)
-               fuse_file_put(req->ff, false);
+               fuse_file_put(req->ff, false, false);
 }
 
 static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req)
@@ -1619,7 +1621,7 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
        ff = __fuse_write_file_get(fc, fi);
        err = fuse_flush_times(inode, ff);
        if (ff)
-               fuse_file_put(ff, 0);
+               fuse_file_put(ff, false, false);
 
        return err;
 }
@@ -1940,7 +1942,7 @@ static int fuse_writepages(struct address_space *mapping,
                err = 0;
        }
        if (data.ff)
-               fuse_file_put(data.ff, false);
+               fuse_file_put(data.ff, false, false);
 
        kfree(data.orig_pages);
 out:
@@ -2924,10 +2926,12 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
        }
 
        if (io->async) {
+               bool blocking = io->blocking;
+
                fuse_aio_complete(io, ret < 0 ? ret : 0, -1);
 
                /* we have a non-extending, async request, so return */
-               if (!io->blocking)
+               if (!blocking)
                        return -EIOCBQUEUED;
 
                wait_for_completion(&wait);
index e9f712e..2f2c92e 100644 (file)
@@ -822,13 +822,13 @@ void fuse_sync_release(struct fuse_file *ff, int flags);
 /**
  * Send RELEASE or RELEASEDIR request
  */
-void fuse_release_common(struct file *file, int opcode);
+void fuse_release_common(struct file *file, bool isdir);
 
 /**
  * Send FSYNC or FSYNCDIR request
  */
 int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
-                     int datasync, int isdir);
+                     int datasync, int opcode);
 
 /**
  * Notify poll wakeup
index 0b94b23..568abed 100644 (file)
@@ -115,7 +115,7 @@ static void fuse_i_callback(struct rcu_head *head)
 static void fuse_destroy_inode(struct inode *inode)
 {
        struct fuse_inode *fi = get_fuse_inode(inode);
-       if (S_ISREG(inode->i_mode)) {
+       if (S_ISREG(inode->i_mode) && !is_bad_inode(inode)) {
                WARN_ON(!list_empty(&fi->write_files));
                WARN_ON(!list_empty(&fi->queued_writes));
        }
@@ -1068,6 +1068,7 @@ void fuse_dev_free(struct fuse_dev *fud)
 
                fuse_conn_put(fc);
        }
+       kfree(fud->pq.processing);
        kfree(fud);
 }
 EXPORT_SYMBOL_GPL(fuse_dev_free);
index a683d9b..9a4a15d 100644 (file)
@@ -826,7 +826,7 @@ static int gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length,
        ret = gfs2_meta_inode_buffer(ip, &dibh);
        if (ret)
                goto unlock;
-       iomap->private = dibh;
+       mp->mp_bh[0] = dibh;
 
        if (gfs2_is_stuffed(ip)) {
                if (flags & IOMAP_WRITE) {
@@ -863,9 +863,6 @@ unstuff:
        len = lblock_stop - lblock + 1;
        iomap->length = len << inode->i_blkbits;
 
-       get_bh(dibh);
-       mp->mp_bh[0] = dibh;
-
        height = ip->i_height;
        while ((lblock + 1) * sdp->sd_sb.sb_bsize > sdp->sd_heightsize[height])
                height++;
@@ -898,8 +895,6 @@ out:
        iomap->bdev = inode->i_sb->s_bdev;
 unlock:
        up_read(&ip->i_rw_mutex);
-       if (ret && dibh)
-               brelse(dibh);
        return ret;
 
 do_alloc:
@@ -980,9 +975,9 @@ static void gfs2_iomap_journaled_page_done(struct inode *inode, loff_t pos,
 
 static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
                                  loff_t length, unsigned flags,
-                                 struct iomap *iomap)
+                                 struct iomap *iomap,
+                                 struct metapath *mp)
 {
-       struct metapath mp = { .mp_aheight = 1, };
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_sbd *sdp = GFS2_SB(inode);
        unsigned int data_blocks = 0, ind_blocks = 0, rblocks;
@@ -996,9 +991,9 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
        unstuff = gfs2_is_stuffed(ip) &&
                  pos + length > gfs2_max_stuffed_size(ip);
 
-       ret = gfs2_iomap_get(inode, pos, length, flags, iomap, &mp);
+       ret = gfs2_iomap_get(inode, pos, length, flags, iomap, mp);
        if (ret)
-               goto out_release;
+               goto out_unlock;
 
        alloc_required = unstuff || iomap->type == IOMAP_HOLE;
 
@@ -1013,7 +1008,7 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
 
                ret = gfs2_quota_lock_check(ip, &ap);
                if (ret)
-                       goto out_release;
+                       goto out_unlock;
 
                ret = gfs2_inplace_reserve(ip, &ap);
                if (ret)
@@ -1038,17 +1033,15 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
                ret = gfs2_unstuff_dinode(ip, NULL);
                if (ret)
                        goto out_trans_end;
-               release_metapath(&mp);
-               brelse(iomap->private);
-               iomap->private = NULL;
+               release_metapath(mp);
                ret = gfs2_iomap_get(inode, iomap->offset, iomap->length,
-                                    flags, iomap, &mp);
+                                    flags, iomap, mp);
                if (ret)
                        goto out_trans_end;
        }
 
        if (iomap->type == IOMAP_HOLE) {
-               ret = gfs2_iomap_alloc(inode, iomap, flags, &mp);
+               ret = gfs2_iomap_alloc(inode, iomap, flags, mp);
                if (ret) {
                        gfs2_trans_end(sdp);
                        gfs2_inplace_release(ip);
@@ -1056,7 +1049,6 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
                        goto out_qunlock;
                }
        }
-       release_metapath(&mp);
        if (!gfs2_is_stuffed(ip) && gfs2_is_jdata(ip))
                iomap->page_done = gfs2_iomap_journaled_page_done;
        return 0;
@@ -1069,10 +1061,7 @@ out_trans_fail:
 out_qunlock:
        if (alloc_required)
                gfs2_quota_unlock(ip);
-out_release:
-       if (iomap->private)
-               brelse(iomap->private);
-       release_metapath(&mp);
+out_unlock:
        gfs2_write_unlock(inode);
        return ret;
 }
@@ -1088,10 +1077,10 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
 
        trace_gfs2_iomap_start(ip, pos, length, flags);
        if ((flags & IOMAP_WRITE) && !(flags & IOMAP_DIRECT)) {
-               ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap);
+               ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp);
        } else {
                ret = gfs2_iomap_get(inode, pos, length, flags, iomap, &mp);
-               release_metapath(&mp);
+
                /*
                 * Silently fall back to buffered I/O for stuffed files or if
                 * we've hot a hole (see gfs2_file_direct_write).
@@ -1100,6 +1089,11 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
                    iomap->type != IOMAP_MAPPED)
                        ret = -ENOTBLK;
        }
+       if (!ret) {
+               get_bh(mp.mp_bh[0]);
+               iomap->private = mp.mp_bh[0];
+       }
+       release_metapath(&mp);
        trace_gfs2_iomap_end(ip, iomap, ret);
        return ret;
 }
@@ -1908,10 +1902,16 @@ static int punch_hole(struct gfs2_inode *ip, u64 offset, u64 length)
                        if (ret < 0)
                                goto out;
 
-                       /* issue read-ahead on metadata */
-                       if (mp.mp_aheight > 1) {
-                               for (; ret > 1; ret--) {
-                                       metapointer_range(&mp, mp.mp_aheight - ret,
+                       /* On the first pass, issue read-ahead on metadata. */
+                       if (mp.mp_aheight > 1 && strip_h == ip->i_height - 1) {
+                               unsigned int height = mp.mp_aheight - 1;
+
+                               /* No read-ahead for data blocks. */
+                               if (mp.mp_aheight - 1 == strip_h)
+                                       height--;
+
+                               for (; height >= mp.mp_aheight - ret; height--) {
+                                       metapointer_range(&mp, height,
                                                          start_list, start_aligned,
                                                          end_list, end_aligned,
                                                          &start, &end);
index ffe3032..b08a530 100644 (file)
@@ -733,6 +733,7 @@ void gfs2_clear_rgrpd(struct gfs2_sbd *sdp)
 
                if (gl) {
                        glock_clear_object(gl, rgd);
+                       gfs2_rgrp_brelse(rgd);
                        gfs2_glock_put(gl);
                }
 
@@ -1174,7 +1175,7 @@ static u32 count_unlinked(struct gfs2_rgrpd *rgd)
  * @rgd: the struct gfs2_rgrpd describing the RG to read in
  *
  * Read in all of a Resource Group's header and bitmap blocks.
- * Caller must eventually call gfs2_rgrp_relse() to free the bitmaps.
+ * Caller must eventually call gfs2_rgrp_brelse() to free the bitmaps.
  *
  * Returns: errno
  */
index 98b96ff..19017d2 100644 (file)
@@ -338,13 +338,14 @@ void hfs_bmap_free(struct hfs_bnode *node)
 
                nidx -= len * 8;
                i = node->next;
-               hfs_bnode_put(node);
                if (!i) {
                        /* panic */;
                        pr_crit("unable to free bnode %u. bmap not found!\n",
                                node->this);
+                       hfs_bnode_put(node);
                        return;
                }
+               hfs_bnode_put(node);
                node = hfs_bnode_find(tree, i);
                if (IS_ERR(node))
                        return;
index 236efe5..66774f4 100644 (file)
@@ -466,14 +466,15 @@ void hfs_bmap_free(struct hfs_bnode *node)
 
                nidx -= len * 8;
                i = node->next;
-               hfs_bnode_put(node);
                if (!i) {
                        /* panic */;
                        pr_crit("unable to free bnode %u. "
                                        "bmap not found!\n",
                                node->this);
+                       hfs_bnode_put(node);
                        return;
                }
+               hfs_bnode_put(node);
                node = hfs_bnode_find(tree, i);
                if (IS_ERR(node))
                        return;
index 9e198f0..35d2108 100644 (file)
@@ -730,8 +730,11 @@ static enum lru_status inode_lru_isolate(struct list_head *item,
                return LRU_REMOVED;
        }
 
-       /* recently referenced inodes get one more pass */
-       if (inode->i_state & I_REFERENCED) {
+       /*
+        * Recently referenced inodes and inodes with many attached pages
+        * get one more pass.
+        */
+       if (inode->i_state & I_REFERENCED || inode->i_data.nrpages > 1) {
                inode->i_state &= ~I_REFERENCED;
                spin_unlock(&inode->i_lock);
                return LRU_ROTATE;
index 64ce240..d6bc98a 100644 (file)
@@ -142,13 +142,14 @@ static void
 iomap_adjust_read_range(struct inode *inode, struct iomap_page *iop,
                loff_t *pos, loff_t length, unsigned *offp, unsigned *lenp)
 {
+       loff_t orig_pos = *pos;
+       loff_t isize = i_size_read(inode);
        unsigned block_bits = inode->i_blkbits;
        unsigned block_size = (1 << block_bits);
        unsigned poff = offset_in_page(*pos);
        unsigned plen = min_t(loff_t, PAGE_SIZE - poff, length);
        unsigned first = poff >> block_bits;
        unsigned last = (poff + plen - 1) >> block_bits;
-       unsigned end = offset_in_page(i_size_read(inode)) >> block_bits;
 
        /*
         * If the block size is smaller than the page size we need to check the
@@ -183,8 +184,12 @@ iomap_adjust_read_range(struct inode *inode, struct iomap_page *iop,
         * handle both halves separately so that we properly zero data in the
         * page cache for blocks that are entirely outside of i_size.
         */
-       if (first <= end && last > end)
-               plen -= (last - end) * block_size;
+       if (orig_pos <= isize && orig_pos + length > isize) {
+               unsigned end = offset_in_page(isize - 1) >> block_bits;
+
+               if (first <= end && last > end)
+                       plen -= (last - end) * block_size;
+       }
 
        *offp = poff;
        *lenp = plen;
@@ -1580,7 +1585,7 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length,
        struct bio *bio;
        bool need_zeroout = false;
        bool use_fua = false;
-       int nr_pages, ret;
+       int nr_pages, ret = 0;
        size_t copied = 0;
 
        if ((pos | length | align) & ((1 << blkbits) - 1))
@@ -1596,12 +1601,13 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length,
 
        if (iomap->flags & IOMAP_F_NEW) {
                need_zeroout = true;
-       } else {
+       } else if (iomap->type == IOMAP_MAPPED) {
                /*
-                * Use a FUA write if we need datasync semantics, this
-                * is a pure data IO that doesn't require any metadata
-                * updates and the underlying device supports FUA. This
-                * allows us to avoid cache flushes on IO completion.
+                * Use a FUA write if we need datasync semantics, this is a pure
+                * data IO that doesn't require any metadata updates (including
+                * after IO completion such as unwritten extent conversion) and
+                * the underlying device supports FUA. This allows us to avoid
+                * cache flushes on IO completion.
                 */
                if (!(iomap->flags & (IOMAP_F_SHARED|IOMAP_F_DIRTY)) &&
                    (dio->flags & IOMAP_DIO_WRITE_FUA) &&
@@ -1644,8 +1650,14 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length,
 
                ret = bio_iov_iter_get_pages(bio, &iter);
                if (unlikely(ret)) {
+                       /*
+                        * We have to stop part way through an IO. We must fall
+                        * through to the sub-block tail zeroing here, otherwise
+                        * this short IO may expose stale data in the tail of
+                        * the block we haven't written data to.
+                        */
                        bio_put(bio);
-                       return copied ? copied : ret;
+                       goto zero_tail;
                }
 
                n = bio->bi_iter.bi_size;
@@ -1676,13 +1688,21 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length,
                dio->submit.cookie = submit_bio(bio);
        } while (nr_pages);
 
-       if (need_zeroout) {
+       /*
+        * We need to zeroout the tail of a sub-block write if the extent type
+        * requires zeroing or the write extends beyond EOF. If we don't zero
+        * the block tail in the latter case, we can expose stale data via mmap
+        * reads of the EOF block.
+        */
+zero_tail:
+       if (need_zeroout ||
+           ((dio->flags & IOMAP_DIO_WRITE) && pos >= i_size_read(inode))) {
                /* zero out from the end of the write to the end of the block */
                pad = pos & (fs_block_size - 1);
                if (pad)
                        iomap_dio_zero(dio, iomap, pos, fs_block_size - pad);
        }
-       return copied;
+       return copied ? copied : ret;
 }
 
 static loff_t
index 74f6429..a7f9126 100644 (file)
@@ -695,9 +695,6 @@ static struct mountpoint *lookup_mountpoint(struct dentry *dentry)
 
        hlist_for_each_entry(mp, chain, m_hash) {
                if (mp->m_dentry == dentry) {
-                       /* might be worth a WARN_ON() */
-                       if (d_unlinked(dentry))
-                               return ERR_PTR(-ENOENT);
                        mp->m_count++;
                        return mp;
                }
@@ -711,6 +708,9 @@ static struct mountpoint *get_mountpoint(struct dentry *dentry)
        int ret;
 
        if (d_mountpoint(dentry)) {
+               /* might be worth a WARN_ON() */
+               if (d_unlinked(dentry))
+                       return ERR_PTR(-ENOENT);
 mountpoint:
                read_seqlock_excl(&mount_lock);
                mp = lookup_mountpoint(dentry);
index fa515d5..3159673 100644 (file)
@@ -66,7 +66,7 @@ __be32 nfs4_callback_getattr(void *argp, void *resp,
 out_iput:
        rcu_read_unlock();
        trace_nfs4_cb_getattr(cps->clp, &args->fh, inode, -ntohl(res->status));
-       iput(inode);
+       nfs_iput_and_deactive(inode);
 out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status));
        return res->status;
@@ -108,7 +108,7 @@ __be32 nfs4_callback_recall(void *argp, void *resp,
        }
        trace_nfs4_cb_recall(cps->clp, &args->fh, inode,
                        &args->stateid, -ntohl(res));
-       iput(inode);
+       nfs_iput_and_deactive(inode);
 out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
        return res;
@@ -686,20 +686,24 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
 {
        struct cb_offloadargs *args = data;
        struct nfs_server *server;
-       struct nfs4_copy_state *copy;
+       struct nfs4_copy_state *copy, *tmp_copy;
        bool found = false;
 
+       copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
+       if (!copy)
+               return htonl(NFS4ERR_SERVERFAULT);
+
        spin_lock(&cps->clp->cl_lock);
        rcu_read_lock();
        list_for_each_entry_rcu(server, &cps->clp->cl_superblocks,
                                client_link) {
-               list_for_each_entry(copy, &server->ss_copies, copies) {
+               list_for_each_entry(tmp_copy, &server->ss_copies, copies) {
                        if (memcmp(args->coa_stateid.other,
-                                       copy->stateid.other,
+                                       tmp_copy->stateid.other,
                                        sizeof(args->coa_stateid.other)))
                                continue;
-                       nfs4_copy_cb_args(copy, args);
-                       complete(&copy->completion);
+                       nfs4_copy_cb_args(tmp_copy, args);
+                       complete(&tmp_copy->completion);
                        found = true;
                        goto out;
                }
@@ -707,15 +711,11 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
 out:
        rcu_read_unlock();
        if (!found) {
-               copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
-               if (!copy) {
-                       spin_unlock(&cps->clp->cl_lock);
-                       return htonl(NFS4ERR_SERVERFAULT);
-               }
                memcpy(&copy->stateid, &args->coa_stateid, NFS4_STATEID_SIZE);
                nfs4_copy_cb_args(copy, args);
                list_add_tail(&copy->copies, &cps->clp->pending_cb_stateids);
-       }
+       } else
+               kfree(copy);
        spin_unlock(&cps->clp->cl_lock);
 
        return 0;
index 07b8395..6ec2f78 100644 (file)
@@ -850,16 +850,23 @@ nfs_delegation_find_inode_server(struct nfs_server *server,
                                 const struct nfs_fh *fhandle)
 {
        struct nfs_delegation *delegation;
-       struct inode *res = NULL;
+       struct inode *freeme, *res = NULL;
 
        list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
                spin_lock(&delegation->lock);
                if (delegation->inode != NULL &&
                    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
-                       res = igrab(delegation->inode);
+                       freeme = igrab(delegation->inode);
+                       if (freeme && nfs_sb_active(freeme->i_sb))
+                               res = freeme;
                        spin_unlock(&delegation->lock);
                        if (res != NULL)
                                return res;
+                       if (freeme) {
+                               rcu_read_unlock();
+                               iput(freeme);
+                               rcu_read_lock();
+                       }
                        return ERR_PTR(-EAGAIN);
                }
                spin_unlock(&delegation->lock);
index aa12c30..33824a0 100644 (file)
@@ -98,8 +98,11 @@ struct nfs_direct_req {
        struct pnfs_ds_commit_info ds_cinfo;    /* Storage for cinfo */
        struct work_struct      work;
        int                     flags;
+       /* for write */
 #define NFS_ODIRECT_DO_COMMIT          (1)     /* an unstable reply was received */
 #define NFS_ODIRECT_RESCHED_WRITES     (2)     /* write verification failed */
+       /* for read */
+#define NFS_ODIRECT_SHOULD_DIRTY       (3)     /* dirty user-space page after read */
        struct nfs_writeverf    verf;           /* unstable write verifier */
 };
 
@@ -412,7 +415,8 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr)
                struct nfs_page *req = nfs_list_entry(hdr->pages.next);
                struct page *page = req->wb_page;
 
-               if (!PageCompound(page) && bytes < hdr->good_bytes)
+               if (!PageCompound(page) && bytes < hdr->good_bytes &&
+                   (dreq->flags == NFS_ODIRECT_SHOULD_DIRTY))
                        set_page_dirty(page);
                bytes += req->wb_bytes;
                nfs_list_remove_request(req);
@@ -587,6 +591,9 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter)
        if (!is_sync_kiocb(iocb))
                dreq->iocb = iocb;
 
+       if (iter_is_iovec(iter))
+               dreq->flags = NFS_ODIRECT_SHOULD_DIRTY;
+
        nfs_start_io_direct(inode);
 
        NFS_I(inode)->read_io += count;
index 86bcba4..310d750 100644 (file)
@@ -1361,12 +1361,7 @@ static void ff_layout_read_prepare_v4(struct rpc_task *task, void *data)
                                task))
                return;
 
-       if (ff_layout_read_prepare_common(task, hdr))
-               return;
-
-       if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context,
-                       hdr->args.lock_context, FMODE_READ) == -EIO)
-               rpc_exit(task, -EIO); /* lost lock, terminate I/O */
+       ff_layout_read_prepare_common(task, hdr);
 }
 
 static void ff_layout_read_call_done(struct rpc_task *task, void *data)
@@ -1542,12 +1537,7 @@ static void ff_layout_write_prepare_v4(struct rpc_task *task, void *data)
                                task))
                return;
 
-       if (ff_layout_write_prepare_common(task, hdr))
-               return;
-
-       if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context,
-                       hdr->args.lock_context, FMODE_WRITE) == -EIO)
-               rpc_exit(task, -EIO); /* lost lock, terminate I/O */
+       ff_layout_write_prepare_common(task, hdr);
 }
 
 static void ff_layout_write_call_done(struct rpc_task *task, void *data)
@@ -1742,6 +1732,11 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr)
        fh = nfs4_ff_layout_select_ds_fh(lseg, idx);
        if (fh)
                hdr->args.fh = fh;
+
+       if (vers == 4 &&
+               !nfs4_ff_layout_select_ds_stateid(lseg, idx, &hdr->args.stateid))
+               goto out_failed;
+
        /*
         * Note that if we ever decide to split across DSes,
         * then we may need to handle dense-like offsets.
@@ -1804,6 +1799,10 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
        if (fh)
                hdr->args.fh = fh;
 
+       if (vers == 4 &&
+               !nfs4_ff_layout_select_ds_stateid(lseg, idx, &hdr->args.stateid))
+               goto out_failed;
+
        /*
         * Note that if we ever decide to split across DSes,
         * then we may need to handle dense-like offsets.
index 4117983..de50a34 100644 (file)
@@ -215,6 +215,10 @@ unsigned int ff_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo,
                unsigned int maxnum);
 struct nfs_fh *
 nfs4_ff_layout_select_ds_fh(struct pnfs_layout_segment *lseg, u32 mirror_idx);
+int
+nfs4_ff_layout_select_ds_stateid(struct pnfs_layout_segment *lseg,
+                               u32 mirror_idx,
+                               nfs4_stateid *stateid);
 
 struct nfs4_pnfs_ds *
 nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx,
index 74d8d53..d233473 100644 (file)
@@ -370,6 +370,25 @@ out:
        return fh;
 }
 
+int
+nfs4_ff_layout_select_ds_stateid(struct pnfs_layout_segment *lseg,
+                               u32 mirror_idx,
+                               nfs4_stateid *stateid)
+{
+       struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, mirror_idx);
+
+       if (!ff_layout_mirror_valid(lseg, mirror, false)) {
+               pr_err_ratelimited("NFS: %s: No data server for mirror offset index %d\n",
+                       __func__, mirror_idx);
+               goto out;
+       }
+
+       nfs4_stateid_copy(stateid, &mirror->stateid);
+       return 1;
+out:
+       return 0;
+}
+
 /**
  * nfs4_ff_layout_prepare_ds - prepare a DS connection for an RPC call
  * @lseg: the layout segment we're operating on
index ac5b784..fed06fd 100644 (file)
@@ -137,31 +137,32 @@ static int handle_async_copy(struct nfs42_copy_res *res,
                             struct file *dst,
                             nfs4_stateid *src_stateid)
 {
-       struct nfs4_copy_state *copy;
+       struct nfs4_copy_state *copy, *tmp_copy;
        int status = NFS4_OK;
        bool found_pending = false;
        struct nfs_open_context *ctx = nfs_file_open_context(dst);
 
+       copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
+       if (!copy)
+               return -ENOMEM;
+
        spin_lock(&server->nfs_client->cl_lock);
-       list_for_each_entry(copy, &server->nfs_client->pending_cb_stateids,
+       list_for_each_entry(tmp_copy, &server->nfs_client->pending_cb_stateids,
                                copies) {
-               if (memcmp(&res->write_res.stateid, &copy->stateid,
+               if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
                                NFS4_STATEID_SIZE))
                        continue;
                found_pending = true;
-               list_del(&copy->copies);
+               list_del(&tmp_copy->copies);
                break;
        }
        if (found_pending) {
                spin_unlock(&server->nfs_client->cl_lock);
+               kfree(copy);
+               copy = tmp_copy;
                goto out;
        }
 
-       copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
-       if (!copy) {
-               spin_unlock(&server->nfs_client->cl_lock);
-               return -ENOMEM;
-       }
        memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
        init_completion(&copy->completion);
        copy->parent_state = ctx->state;
index 8d59c96..1b994b5 100644 (file)
@@ -41,6 +41,8 @@ enum nfs4_client_state {
        NFS4CLNT_MOVED,
        NFS4CLNT_LEASE_MOVED,
        NFS4CLNT_DELEGATION_EXPIRED,
+       NFS4CLNT_RUN_MANAGER,
+       NFS4CLNT_DELEGRETURN_RUNNING,
 };
 
 #define NFS4_RENEW_TIMEOUT             0x01
index 62ae0fd..d8decf2 100644 (file)
@@ -1210,6 +1210,7 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
        struct task_struct *task;
        char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
 
+       set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
        if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
                return;
        __module_get(THIS_MODULE);
@@ -2503,6 +2504,7 @@ static void nfs4_state_manager(struct nfs_client *clp)
 
        /* Ensure exclusive access to NFSv4 state */
        do {
+               clear_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
                if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
                        section = "purge state";
                        status = nfs4_purge_lease(clp);
@@ -2593,19 +2595,24 @@ static void nfs4_state_manager(struct nfs_client *clp)
                }
 
                nfs4_end_drain_session(clp);
-               if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
-                       nfs_client_return_marked_delegations(clp);
-                       continue;
+               nfs4_clear_state_manager_bit(clp);
+
+               if (!test_and_set_bit(NFS4CLNT_DELEGRETURN_RUNNING, &clp->cl_state)) {
+                       if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
+                               nfs_client_return_marked_delegations(clp);
+                               set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
+                       }
+                       clear_bit(NFS4CLNT_DELEGRETURN_RUNNING, &clp->cl_state);
                }
 
-               nfs4_clear_state_manager_bit(clp);
                /* Did we race with an attempt to give us more work? */
-               if (clp->cl_state == 0)
-                       break;
+               if (!test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state))
+                       return;
                if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
-                       break;
-       } while (refcount_read(&clp->cl_count) > 1);
-       return;
+                       return;
+       } while (refcount_read(&clp->cl_count) > 1 && !signalled());
+       goto out_drain;
+
 out_error:
        if (strlen(section))
                section_sep = ": ";
@@ -2613,6 +2620,7 @@ out_error:
                        " with error %d\n", section_sep, section,
                        clp->cl_hostname, -status);
        ssleep(1);
+out_drain:
        nfs4_end_drain_session(clp);
        nfs4_clear_state_manager_bit(clp);
 }
index edff074..d505990 100644 (file)
@@ -1038,6 +1038,9 @@ nfsd4_verify_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 {
        __be32 status;
 
+       if (!cstate->save_fh.fh_dentry)
+               return nfserr_nofilehandle;
+
        status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
                                            src_stateid, RD_STATE, src, NULL);
        if (status) {
index de99db5..f2129a5 100644 (file)
@@ -266,9 +266,7 @@ void nilfs_btnode_abort_change_key(struct address_space *btnc,
                return;
 
        if (nbh == NULL) {      /* blocksize == pagesize */
-               xa_lock_irq(&btnc->i_pages);
-               __xa_erase(&btnc->i_pages, newkey);
-               xa_unlock_irq(&btnc->i_pages);
+               xa_erase_irq(&btnc->i_pages, newkey);
                unlock_page(ctxt->bh->b_page);
        } else
                brelse(nbh);
index 5769cf3..e08a664 100644 (file)
@@ -115,12 +115,12 @@ static bool fanotify_should_send_event(struct fsnotify_iter_info *iter_info,
                        continue;
                mark = iter_info->marks[type];
                /*
-                * if the event is for a child and this inode doesn't care about
-                * events on the child, don't send it!
+                * If the event is for a child and this mark doesn't care about
+                * events on a child, don't send it!
                 */
-               if (type == FSNOTIFY_OBJ_TYPE_INODE &&
-                   (event_mask & FS_EVENT_ON_CHILD) &&
-                   !(mark->mask & FS_EVENT_ON_CHILD))
+               if (event_mask & FS_EVENT_ON_CHILD &&
+                   (type != FSNOTIFY_OBJ_TYPE_INODE ||
+                    !(mark->mask & FS_EVENT_ON_CHILD)))
                        continue;
 
                marks_mask |= mark->mask;
index 2172ba5..d2c3490 100644 (file)
@@ -167,9 +167,9 @@ int __fsnotify_parent(const struct path *path, struct dentry *dentry, __u32 mask
        parent = dget_parent(dentry);
        p_inode = parent->d_inode;
 
-       if (unlikely(!fsnotify_inode_watches_children(p_inode)))
+       if (unlikely(!fsnotify_inode_watches_children(p_inode))) {
                __fsnotify_update_child_dentry_flags(p_inode);
-       else if (p_inode->i_fsnotify_mask & mask) {
+       } else if (p_inode->i_fsnotify_mask & mask & ALL_FSNOTIFY_EVENTS) {
                struct name_snapshot name;
 
                /* we are notifying a parent so come up with the new mask which
@@ -339,6 +339,9 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,
                sb = mnt->mnt.mnt_sb;
                mnt_or_sb_mask = mnt->mnt_fsnotify_mask | sb->s_fsnotify_mask;
        }
+       /* An event "on child" is not intended for a mount/sb mark */
+       if (mask & FS_EVENT_ON_CHILD)
+               mnt_or_sb_mask = 0;
 
        /*
         * Optimization: srcu_read_lock() has a memory barrier which can
index da578ad..eb1ce30 100644 (file)
@@ -2411,8 +2411,16 @@ static int ocfs2_dio_end_io(struct kiocb *iocb,
        /* this io's submitter should not have unlocked this before we could */
        BUG_ON(!ocfs2_iocb_is_rw_locked(iocb));
 
-       if (bytes > 0 && private)
-               ret = ocfs2_dio_end_io_write(inode, private, offset, bytes);
+       if (bytes <= 0)
+               mlog_ratelimited(ML_ERROR, "Direct IO failed, bytes = %lld",
+                                (long long)bytes);
+       if (private) {
+               if (bytes > 0)
+                       ret = ocfs2_dio_end_io_write(inode, private, offset,
+                                                    bytes);
+               else
+                       ocfs2_dio_free_write_ctx(inode, private);
+       }
 
        ocfs2_iocb_clear_rw_locked(iocb);
 
index 308ea0e..a396096 100644 (file)
@@ -178,6 +178,15 @@ do {                                                                       \
                              ##__VA_ARGS__);                           \
 } while (0)
 
+#define mlog_ratelimited(mask, fmt, ...)                               \
+do {                                                                   \
+       static DEFINE_RATELIMIT_STATE(_rs,                              \
+                                     DEFAULT_RATELIMIT_INTERVAL,       \
+                                     DEFAULT_RATELIMIT_BURST);         \
+       if (__ratelimit(&_rs))                                          \
+               mlog(mask, fmt, ##__VA_ARGS__);                         \
+} while (0)
+
 #define mlog_errno(st) ({                                              \
        int _st = (st);                                                 \
        if (_st != -ERESTARTSYS && _st != -EINTR &&                     \
index 9f88188..4bf8d58 100644 (file)
@@ -125,10 +125,10 @@ check_err:
 
 check_gen:
        if (handle->ih_generation != inode->i_generation) {
-               iput(inode);
                trace_ocfs2_get_dentry_generation((unsigned long long)blkno,
                                                  handle->ih_generation,
                                                  inode->i_generation);
+               iput(inode);
                result = ERR_PTR(-ESTALE);
                goto bail;
        }
index 3f1685d..1565dd8 100644 (file)
@@ -157,18 +157,14 @@ out:
 }
 
 /*
- * lock allocators, and reserving appropriate number of bits for
- * meta blocks and data clusters.
- *
- * in some cases, we don't need to reserve clusters, just let data_ac
- * be NULL.
+ * lock allocator, and reserve appropriate number of bits for
+ * meta blocks.
  */
-static int ocfs2_lock_allocators_move_extents(struct inode *inode,
+static int ocfs2_lock_meta_allocator_move_extents(struct inode *inode,
                                        struct ocfs2_extent_tree *et,
                                        u32 clusters_to_move,
                                        u32 extents_to_split,
                                        struct ocfs2_alloc_context **meta_ac,
-                                       struct ocfs2_alloc_context **data_ac,
                                        int extra_blocks,
                                        int *credits)
 {
@@ -193,13 +189,6 @@ static int ocfs2_lock_allocators_move_extents(struct inode *inode,
                goto out;
        }
 
-       if (data_ac) {
-               ret = ocfs2_reserve_clusters(osb, clusters_to_move, data_ac);
-               if (ret) {
-                       mlog_errno(ret);
-                       goto out;
-               }
-       }
 
        *credits += ocfs2_calc_extend_credits(osb->sb, et->et_root_el);
 
@@ -259,10 +248,10 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
                }
        }
 
-       ret = ocfs2_lock_allocators_move_extents(inode, &context->et, *len, 1,
-                                                &context->meta_ac,
-                                                &context->data_ac,
-                                                extra_blocks, &credits);
+       ret = ocfs2_lock_meta_allocator_move_extents(inode, &context->et,
+                                               *len, 1,
+                                               &context->meta_ac,
+                                               extra_blocks, &credits);
        if (ret) {
                mlog_errno(ret);
                goto out;
@@ -285,6 +274,21 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
                }
        }
 
+       /*
+        * Make sure ocfs2_reserve_cluster is called after
+        * __ocfs2_flush_truncate_log, otherwise, dead lock may happen.
+        *
+        * If ocfs2_reserve_cluster is called
+        * before __ocfs2_flush_truncate_log, dead lock on global bitmap
+        * may happen.
+        *
+        */
+       ret = ocfs2_reserve_clusters(osb, *len, &context->data_ac);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_unlock_mutex;
+       }
+
        handle = ocfs2_start_trans(osb, credits);
        if (IS_ERR(handle)) {
                ret = PTR_ERR(handle);
@@ -617,9 +621,10 @@ static int ocfs2_move_extent(struct ocfs2_move_extents_context *context,
                }
        }
 
-       ret = ocfs2_lock_allocators_move_extents(inode, &context->et, len, 1,
-                                                &context->meta_ac,
-                                                NULL, extra_blocks, &credits);
+       ret = ocfs2_lock_meta_allocator_move_extents(inode, &context->et,
+                                               len, 1,
+                                               &context->meta_ac,
+                                               extra_blocks, &credits);
        if (ret) {
                mlog_errno(ret);
                goto out;
index c628914..82c129b 100644 (file)
@@ -651,6 +651,18 @@ static int ovl_symlink(struct inode *dir, struct dentry *dentry,
        return ovl_create_object(dentry, S_IFLNK, 0, link);
 }
 
+static int ovl_set_link_redirect(struct dentry *dentry)
+{
+       const struct cred *old_cred;
+       int err;
+
+       old_cred = ovl_override_creds(dentry->d_sb);
+       err = ovl_set_redirect(dentry, false);
+       revert_creds(old_cred);
+
+       return err;
+}
+
 static int ovl_link(struct dentry *old, struct inode *newdir,
                    struct dentry *new)
 {
@@ -670,7 +682,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
                goto out_drop_write;
 
        if (ovl_is_metacopy_dentry(old)) {
-               err = ovl_set_redirect(old, false);
+               err = ovl_set_link_redirect(old);
                if (err)
                        goto out_drop_write;
        }
index 8fa37cd..54e5d17 100644 (file)
@@ -754,9 +754,8 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
                goto out;
        }
 
-       /* Otherwise, get a connected non-upper dir or disconnected non-dir */
-       if (d_is_dir(origin.dentry) &&
-           (origin.dentry->d_flags & DCACHE_DISCONNECTED)) {
+       /* Find origin.dentry again with ovl_acceptable() layer check */
+       if (d_is_dir(origin.dentry)) {
                dput(origin.dentry);
                origin.dentry = NULL;
                err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack);
@@ -769,6 +768,7 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
                        goto out_err;
        }
 
+       /* Get a connected non-upper dir or disconnected non-dir */
        dentry = ovl_get_dentry(sb, NULL, &origin, index);
 
 out:
index 6bcc9de..3b7ed5d 100644 (file)
@@ -286,22 +286,13 @@ int ovl_permission(struct inode *inode, int mask)
        if (err)
                return err;
 
-       /* No need to do any access on underlying for special files */
-       if (special_file(realinode->i_mode))
-               return 0;
-
-       /* No need to access underlying for execute */
-       mask &= ~MAY_EXEC;
-       if ((mask & (MAY_READ | MAY_WRITE)) == 0)
-               return 0;
-
-       /* Lower files get copied up, so turn write access into read */
-       if (!upperinode && mask & MAY_WRITE) {
+       old_cred = ovl_override_creds(inode->i_sb);
+       if (!upperinode &&
+           !special_file(realinode->i_mode) && mask & MAY_WRITE) {
                mask &= ~(MAY_WRITE | MAY_APPEND);
+               /* Make sure mounter can read file for copy up later */
                mask |= MAY_READ;
        }
-
-       old_cred = ovl_override_creds(inode->i_sb);
        err = inode_permission(realinode, mask);
        revert_creds(old_cred);
 
index ffcff65..e02a903 100644 (file)
@@ -816,17 +816,14 @@ static int ramoops_probe(struct platform_device *pdev)
 
        cxt->pstore.data = cxt;
        /*
-        * Console can handle any buffer size, so prefer LOG_LINE_MAX. If we
-        * have to handle dumps, we must have at least record_size buffer. And
-        * for ftrace, bufsize is irrelevant (if bufsize is 0, buf will be
-        * ZERO_SIZE_PTR).
+        * Since bufsize is only used for dmesg crash dumps, it
+        * must match the size of the dprz record (after PRZ header
+        * and ECC bytes have been accounted for).
         */
-       if (cxt->console_size)
-               cxt->pstore.bufsize = 1024; /* LOG_LINE_MAX */
-       cxt->pstore.bufsize = max(cxt->record_size, cxt->pstore.bufsize);
-       cxt->pstore.buf = kmalloc(cxt->pstore.bufsize, GFP_KERNEL);
+       cxt->pstore.bufsize = cxt->dprzs[0]->buffer_size;
+       cxt->pstore.buf = kzalloc(cxt->pstore.bufsize, GFP_KERNEL);
        if (!cxt->pstore.buf) {
-               pr_err("cannot allocate pstore buffer\n");
+               pr_err("cannot allocate pstore crash dump buffer\n");
                err = -ENOMEM;
                goto fail_clear;
        }
index bfcb4ce..58f3053 100644 (file)
@@ -1956,7 +1956,7 @@ loff_t do_clone_file_range(struct file *file_in, loff_t pos_in,
        struct inode *inode_out = file_inode(file_out);
        loff_t ret;
 
-       WARN_ON_ONCE(remap_flags);
+       WARN_ON_ONCE(remap_flags & REMAP_FILE_DEDUP);
 
        if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
                return -EISDIR;
@@ -2094,17 +2094,18 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
        off = same->src_offset;
        len = same->src_length;
 
-       ret = -EISDIR;
        if (S_ISDIR(src->i_mode))
-               goto out;
+               return -EISDIR;
 
-       ret = -EINVAL;
        if (!S_ISREG(src->i_mode))
-               goto out;
+               return -EINVAL;
+
+       if (!file->f_op->remap_file_range)
+               return -EOPNOTSUPP;
 
        ret = remap_verify_area(file, off, len, false);
        if (ret < 0)
-               goto out;
+               return ret;
        ret = 0;
 
        if (off + len > i_size_read(src))
@@ -2147,10 +2148,8 @@ next_fdput:
                fdput(dst_fd);
 next_loop:
                if (fatal_signal_pending(current))
-                       goto out;
+                       break;
        }
-
-out:
        return ret;
 }
 EXPORT_SYMBOL(vfs_dedupe_file_range);
index 3553f19..de2ede0 100644 (file)
@@ -945,11 +945,16 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
        sd->flags &= ~SPLICE_F_NONBLOCK;
        more = sd->flags & SPLICE_F_MORE;
 
+       WARN_ON_ONCE(pipe->nrbufs != 0);
+
        while (len) {
                size_t read_len;
                loff_t pos = sd->pos, prev_pos = pos;
 
-               ret = do_splice_to(in, &pos, pipe, len, flags);
+               /* Don't try to read more the pipe has space for. */
+               read_len = min_t(size_t, len,
+                                (pipe->buffers - pipe->nrbufs) << PAGE_SHIFT);
+               ret = do_splice_to(in, &pos, pipe, read_len, flags);
                if (unlikely(ret <= 0))
                        goto out_release;
 
index 499a20a..273736f 100644 (file)
@@ -275,7 +275,7 @@ static int __sysv_write_inode(struct inode *inode, int wait)
                 }
         }
        brelse(bh);
-       return 0;
+       return err;
 }
 
 int sysv_write_inode(struct inode *inode, struct writeback_control *wbc)
index 8f2f56d..e3d684e 100644 (file)
@@ -827,16 +827,20 @@ static int udf_load_pvoldesc(struct super_block *sb, sector_t block)
 
 
        ret = udf_dstrCS0toChar(sb, outstr, 31, pvoldesc->volIdent, 32);
-       if (ret < 0)
-               goto out_bh;
-
-       strncpy(UDF_SB(sb)->s_volume_ident, outstr, ret);
+       if (ret < 0) {
+               strcpy(UDF_SB(sb)->s_volume_ident, "InvalidName");
+               pr_warn("incorrect volume identification, setting to "
+                       "'InvalidName'\n");
+       } else {
+               strncpy(UDF_SB(sb)->s_volume_ident, outstr, ret);
+       }
        udf_debug("volIdent[] = '%s'\n", UDF_SB(sb)->s_volume_ident);
 
        ret = udf_dstrCS0toChar(sb, outstr, 127, pvoldesc->volSetIdent, 128);
-       if (ret < 0)
+       if (ret < 0) {
+               ret = 0;
                goto out_bh;
-
+       }
        outstr[ret] = 0;
        udf_debug("volSetIdent[] = '%s'\n", outstr);
 
index 4523479..5fcfa96 100644 (file)
@@ -351,6 +351,11 @@ try_again:
        return u_len;
 }
 
+/*
+ * Convert CS0 dstring to output charset. Warning: This function may truncate
+ * input string if it is too long as it is used for informational strings only
+ * and it is better to truncate the string than to refuse mounting a media.
+ */
 int udf_dstrCS0toChar(struct super_block *sb, uint8_t *utf_o, int o_len,
                      const uint8_t *ocu_i, int i_len)
 {
@@ -359,9 +364,12 @@ int udf_dstrCS0toChar(struct super_block *sb, uint8_t *utf_o, int o_len,
        if (i_len > 0) {
                s_len = ocu_i[i_len - 1];
                if (s_len >= i_len) {
-                       pr_err("incorrect dstring lengths (%d/%d)\n",
-                              s_len, i_len);
-                       return -EINVAL;
+                       pr_warn("incorrect dstring lengths (%d/%d),"
+                               " truncating\n", s_len, i_len);
+                       s_len = i_len - 1;
+                       /* 2-byte encoding? Need to round properly... */
+                       if (ocu_i[0] == 16)
+                               s_len -= (s_len - 1) & 2;
                }
        }
 
index 356d2b8..7a85e60 100644 (file)
@@ -1361,6 +1361,19 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
                ret = -EINVAL;
                if (!vma_can_userfault(cur))
                        goto out_unlock;
+
+               /*
+                * UFFDIO_COPY will fill file holes even without
+                * PROT_WRITE. This check enforces that if this is a
+                * MAP_SHARED, the process has write permission to the backing
+                * file. If VM_MAYWRITE is set it also enforces that on a
+                * MAP_SHARED vma: there is no F_WRITE_SEAL and no further
+                * F_WRITE_SEAL can be taken until the vma is destroyed.
+                */
+               ret = -EPERM;
+               if (unlikely(!(cur->vm_flags & VM_MAYWRITE)))
+                       goto out_unlock;
+
                /*
                 * If this vma contains ending address, and huge pages
                 * check alignment.
@@ -1406,6 +1419,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
                BUG_ON(!vma_can_userfault(vma));
                BUG_ON(vma->vm_userfaultfd_ctx.ctx &&
                       vma->vm_userfaultfd_ctx.ctx != ctx);
+               WARN_ON(!(vma->vm_flags & VM_MAYWRITE));
 
                /*
                 * Nothing to do: this vma is already registered into this
@@ -1560,6 +1574,8 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
                if (!vma->vm_userfaultfd_ctx.ctx)
                        goto skip;
 
+               WARN_ON(!(vma->vm_flags & VM_MAYWRITE));
+
                if (vma->vm_start > start)
                        start = vma->vm_start;
                vma_end = min(end, vma->vm_end);
index 74d7228..19e921d 100644 (file)
@@ -1694,10 +1694,13 @@ xfs_bmap_add_extent_delay_real(
        case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
                /*
                 * Filling in all of a previously delayed allocation extent.
-                * The right neighbor is contiguous, the left is not.
+                * The right neighbor is contiguous, the left is not. Take care
+                * with delay -> unwritten extent allocation here because the
+                * delalloc record we are overwriting is always written.
                 */
                PREV.br_startblock = new->br_startblock;
                PREV.br_blockcount += RIGHT.br_blockcount;
+               PREV.br_state = new->br_state;
 
                xfs_iext_next(ifp, &bma->icur);
                xfs_iext_remove(bma->ip, &bma->icur, state);
index 34c6d7b..bbdae2b 100644 (file)
@@ -330,7 +330,7 @@ xfs_btree_sblock_verify_crc(
 
        if (xfs_sb_version_hascrc(&mp->m_sb)) {
                if (!xfs_log_check_lsn(mp, be64_to_cpu(block->bb_u.s.bb_lsn)))
-                       return __this_address;
+                       return false;
                return xfs_buf_verify_cksum(bp, XFS_BTREE_SBLOCK_CRC_OFF);
        }
 
index 86c5020..7fbf8af 100644 (file)
@@ -538,15 +538,18 @@ xfs_inobt_rec_check_count(
 
 static xfs_extlen_t
 xfs_inobt_max_size(
-       struct xfs_mount        *mp)
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno)
 {
+       xfs_agblock_t           agblocks = xfs_ag_block_count(mp, agno);
+
        /* Bail out if we're uninitialized, which can happen in mkfs. */
        if (mp->m_inobt_mxr[0] == 0)
                return 0;
 
        return xfs_btree_calc_size(mp->m_inobt_mnr,
-               (uint64_t)mp->m_sb.sb_agblocks * mp->m_sb.sb_inopblock /
-                               XFS_INODES_PER_CHUNK);
+                               (uint64_t)agblocks * mp->m_sb.sb_inopblock /
+                                       XFS_INODES_PER_CHUNK);
 }
 
 static int
@@ -594,7 +597,7 @@ xfs_finobt_calc_reserves(
        if (error)
                return error;
 
-       *ask += xfs_inobt_max_size(mp);
+       *ask += xfs_inobt_max_size(mp, agno);
        *used += tree_len;
        return 0;
 }
index 5d263df..1ee8c55 100644 (file)
@@ -1042,7 +1042,7 @@ out_trans_cancel:
        goto out_unlock;
 }
 
-static int
+int
 xfs_flush_unmap_range(
        struct xfs_inode        *ip,
        xfs_off_t               offset,
@@ -1126,9 +1126,9 @@ xfs_free_file_space(
         * page could be mmap'd and iomap_zero_range doesn't do that for us.
         * Writeback of the eof page will do this, albeit clumsily.
         */
-       if (offset + len >= XFS_ISIZE(ip) && ((offset + len) & PAGE_MASK)) {
+       if (offset + len >= XFS_ISIZE(ip) && offset_in_page(offset + len) > 0) {
                error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping,
-                               (offset + len) & ~PAGE_MASK, LLONG_MAX);
+                               round_down(offset + len, PAGE_SIZE), LLONG_MAX);
        }
 
        return error;
@@ -1195,13 +1195,7 @@ xfs_prepare_shift(
         * Writeback and invalidate cache for the remainder of the file as we're
         * about to shift down every extent from offset to EOF.
         */
-       error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping, offset, -1);
-       if (error)
-               return error;
-       error = invalidate_inode_pages2_range(VFS_I(ip)->i_mapping,
-                                       offset >> PAGE_SHIFT, -1);
-       if (error)
-               return error;
+       error = xfs_flush_unmap_range(ip, offset, XFS_ISIZE(ip));
 
        /*
         * Clean out anything hanging around in the cow fork now that
index 87363d1..7a78229 100644 (file)
@@ -80,4 +80,7 @@ int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
                          int whichfork, xfs_extnum_t *nextents,
                          xfs_filblks_t *count);
 
+int    xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
+                             xfs_off_t len);
+
 #endif /* __XFS_BMAP_UTIL_H__ */
index 12d8455..010db5f 100644 (file)
@@ -1233,9 +1233,23 @@ xfs_buf_iodone(
 }
 
 /*
- * Requeue a failed buffer for writeback
+ * Requeue a failed buffer for writeback.
  *
- * Return true if the buffer has been re-queued properly, false otherwise
+ * We clear the log item failed state here as well, but we have to be careful
+ * about reference counts because the only active reference counts on the buffer
+ * may be the failed log items. Hence if we clear the log item failed state
+ * before queuing the buffer for IO we can release all active references to
+ * the buffer and free it, leading to use after free problems in
+ * xfs_buf_delwri_queue. It makes no difference to the buffer or log items which
+ * order we process them in - the buffer is locked, and we own the buffer list
+ * so nothing on them is going to change while we are performing this action.
+ *
+ * Hence we can safely queue the buffer for IO before we clear the failed log
+ * item state, therefore  always having an active reference to the buffer and
+ * avoiding the transient zero-reference state that leads to use-after-free.
+ *
+ * Return true if the buffer was added to the buffer list, false if it was
+ * already on the buffer list.
  */
 bool
 xfs_buf_resubmit_failed_buffers(
@@ -1243,16 +1257,16 @@ xfs_buf_resubmit_failed_buffers(
        struct list_head        *buffer_list)
 {
        struct xfs_log_item     *lip;
+       bool                    ret;
+
+       ret = xfs_buf_delwri_queue(bp, buffer_list);
 
        /*
-        * Clear XFS_LI_FAILED flag from all items before resubmit
-        *
-        * XFS_LI_FAILED set/clear is protected by ail_lock, caller  this
+        * XFS_LI_FAILED set/clear is protected by ail_lock, caller of this
         * function already have it acquired
         */
        list_for_each_entry(lip, &bp->b_li_list, li_bio_list)
                xfs_clear_li_failed(lip);
 
-       /* Add this buffer back to the delayed write list */
-       return xfs_buf_delwri_queue(bp, buffer_list);
+       return ret;
 }
index 53c9ab8..e474250 100644 (file)
@@ -920,7 +920,7 @@ out_unlock:
 }
 
 
-loff_t
+STATIC loff_t
 xfs_file_remap_range(
        struct file             *file_in,
        loff_t                  pos_in,
index 73a1d77..3091e4b 100644 (file)
@@ -40,7 +40,7 @@ xfs_fill_statvfs_from_dquot(
                statp->f_files = limit;
                statp->f_ffree =
                        (statp->f_files > dqp->q_res_icount) ?
-                        (statp->f_ffree - dqp->q_res_icount) : 0;
+                        (statp->f_files - dqp->q_res_icount) : 0;
        }
 }
 
index ecdb086..322a852 100644 (file)
@@ -296,6 +296,7 @@ xfs_reflink_reserve_cow(
        if (error)
                return error;
 
+       xfs_trim_extent(imap, got.br_startoff, got.br_blockcount);
        trace_xfs_reflink_cow_alloc(ip, &got);
        return 0;
 }
@@ -1351,10 +1352,19 @@ xfs_reflink_remap_prep(
        if (ret)
                goto out_unlock;
 
-       /* Zap any page cache for the destination file's range. */
-       truncate_inode_pages_range(&inode_out->i_data,
-                       round_down(pos_out, PAGE_SIZE),
-                       round_up(pos_out + *len, PAGE_SIZE) - 1);
+       /*
+        * If pos_out > EOF, we may have dirtied blocks between EOF and
+        * pos_out. In that case, we need to extend the flush and unmap to cover
+        * from EOF to the end of the copy length.
+        */
+       if (pos_out > XFS_ISIZE(dest)) {
+               loff_t  flen = *len + (pos_out - XFS_ISIZE(dest));
+               ret = xfs_flush_unmap_range(dest, XFS_ISIZE(dest), flen);
+       } else {
+               ret = xfs_flush_unmap_range(dest, pos_out, *len);
+       }
+       if (ret)
+               goto out_unlock;
 
        return 1;
 out_unlock:
index 3043e5e..8a6532a 100644 (file)
@@ -280,7 +280,10 @@ DECLARE_EVENT_CLASS(xfs_buf_class,
        ),
        TP_fast_assign(
                __entry->dev = bp->b_target->bt_dev;
-               __entry->bno = bp->b_bn;
+               if (bp->b_bn == XFS_BUF_DADDR_NULL)
+                       __entry->bno = bp->b_maps[0].bm_bn;
+               else
+                       __entry->bno = bp->b_bn;
                __entry->nblks = bp->b_length;
                __entry->hold = atomic_read(&bp->b_hold);
                __entry->pincount = atomic_read(&bp->b_pin_count);
index 827e4d3..8cc7b09 100644 (file)
@@ -16,6 +16,7 @@
 #define __ASM_GENERIC_FIXMAP_H
 
 #include <linux/bug.h>
+#include <linux/mm_types.h>
 
 #define __fix_to_virt(x)       (FIXADDR_TOP - ((x) << PAGE_SHIFT))
 #define __virt_to_fix(x)       ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT)
index b248805..7605b59 100644 (file)
@@ -171,7 +171,7 @@ struct virtchnl_msg {
 
 VIRTCHNL_CHECK_STRUCT_LEN(20, virtchnl_msg);
 
-/* Message descriptions and data structures.*/
+/* Message descriptions and data structures. */
 
 /* VIRTCHNL_OP_VERSION
  * VF posts its version number to the PF. PF responds with its version number
@@ -342,6 +342,8 @@ struct virtchnl_vsi_queue_config_info {
        struct virtchnl_queue_pair_info qpair[1];
 };
 
+VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_vsi_queue_config_info);
+
 /* VIRTCHNL_OP_REQUEST_QUEUES
  * VF sends this message to request the PF to allocate additional queues to
  * this VF.  Each VF gets a guaranteed number of queues on init but asking for
@@ -357,8 +359,6 @@ struct virtchnl_vf_res_request {
        u16 num_queue_pairs;
 };
 
-VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_vsi_queue_config_info);
-
 /* VIRTCHNL_OP_CONFIG_IRQ_MAP
  * VF uses this message to map vectors to queues.
  * The rxq_map and txq_map fields are bitmaps used to indicate which queues
@@ -819,8 +819,8 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
                if (msglen >= valid_len) {
                        struct virtchnl_tc_info *vti =
                                (struct virtchnl_tc_info *)msg;
-                       valid_len += vti->num_tc *
-                               sizeof(struct virtchnl_channel_info);
+                       valid_len += (vti->num_tc - 1) *
+                                    sizeof(struct virtchnl_channel_info);
                        if (vti->num_tc == 0)
                                err_msg_format = true;
                }
index 33014ae..e734f16 100644 (file)
@@ -23,6 +23,7 @@ struct bpf_prog;
 struct bpf_map;
 struct sock;
 struct seq_file;
+struct btf;
 struct btf_type;
 
 /* map is generic key/value storage optionally accesible by eBPF programs */
@@ -52,6 +53,7 @@ struct bpf_map_ops {
        void (*map_seq_show_elem)(struct bpf_map *map, void *key,
                                  struct seq_file *m);
        int (*map_check_btf)(const struct bpf_map *map,
+                            const struct btf *btf,
                             const struct btf_type *key_type,
                             const struct btf_type *value_type);
 };
@@ -126,6 +128,7 @@ static inline bool bpf_map_support_seq_show(const struct bpf_map *map)
 }
 
 int map_check_no_btf(const struct bpf_map *map,
+                    const struct btf *btf,
                     const struct btf_type *key_type,
                     const struct btf_type *value_type);
 
@@ -268,15 +271,18 @@ struct bpf_prog_offload_ops {
        int (*insn_hook)(struct bpf_verifier_env *env,
                         int insn_idx, int prev_insn_idx);
        int (*finalize)(struct bpf_verifier_env *env);
+       int (*prepare)(struct bpf_prog *prog);
+       int (*translate)(struct bpf_prog *prog);
+       void (*destroy)(struct bpf_prog *prog);
 };
 
 struct bpf_prog_offload {
        struct bpf_prog         *prog;
        struct net_device       *netdev;
+       struct bpf_offload_dev  *offdev;
        void                    *dev_priv;
        struct list_head        offloads;
        bool                    dev_state;
-       const struct bpf_prog_offload_ops *dev_ops;
        void                    *jited_image;
        u32                     jited_len;
 };
@@ -293,9 +299,11 @@ struct bpf_prog_aux {
        atomic_t refcnt;
        u32 used_map_cnt;
        u32 max_ctx_offset;
+       u32 max_pkt_offset;
        u32 stack_depth;
        u32 id;
-       u32 func_cnt;
+       u32 func_cnt; /* used by non-func prog as the number of func progs */
+       u32 func_idx; /* 0 for non-func prog, the index in func array for func prog */
        bool offload_requested;
        struct bpf_prog **func;
        void *jit_data; /* JIT specific data. arch dependent */
@@ -312,6 +320,30 @@ struct bpf_prog_aux {
        void *security;
 #endif
        struct bpf_prog_offload *offload;
+       struct btf *btf;
+       struct bpf_func_info *func_info;
+       /* bpf_line_info loaded from userspace.  linfo->insn_off
+        * has the xlated insn offset.
+        * Both the main and sub prog share the same linfo.
+        * The subprog can access its first linfo by
+        * using the linfo_idx.
+        */
+       struct bpf_line_info *linfo;
+       /* jited_linfo is the jited addr of the linfo.  It has a
+        * one to one mapping to linfo:
+        * jited_linfo[i] is the jited addr for the linfo[i]->insn_off.
+        * Both the main and sub prog share the same jited_linfo.
+        * The subprog can access its first jited_linfo by
+        * using the linfo_idx.
+        */
+       void **jited_linfo;
+       u32 func_info_cnt;
+       u32 nr_linfo;
+       /* subprog can use linfo_idx to access its first linfo and
+        * jited_linfo.
+        * main prog always has linfo_idx == 0
+        */
+       u32 linfo_idx;
        union {
                struct work_struct work;
                struct rcu_head rcu;
@@ -523,7 +555,8 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size)
 }
 
 /* verify correctness of eBPF program */
-int bpf_check(struct bpf_prog **fp, union bpf_attr *attr);
+int bpf_check(struct bpf_prog **fp, union bpf_attr *attr,
+             union bpf_attr __user *uattr);
 void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
 
 /* Map specifics */
@@ -691,7 +724,8 @@ 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(void);
+struct bpf_offload_dev *
+bpf_offload_dev_create(const struct bpf_prog_offload_ops *ops);
 void bpf_offload_dev_destroy(struct bpf_offload_dev *offdev);
 int bpf_offload_dev_netdev_register(struct bpf_offload_dev *offdev,
                                    struct net_device *netdev);
index d93e897..c233efc 100644 (file)
@@ -38,6 +38,7 @@ enum bpf_reg_liveness {
        REG_LIVE_NONE = 0, /* reg hasn't been read or written this branch */
        REG_LIVE_READ, /* reg was read, so we're sensitive to initial value */
        REG_LIVE_WRITTEN, /* reg was written first, screening off later reads */
+       REG_LIVE_DONE = 4, /* liveness won't be updating this register anymore */
 };
 
 struct bpf_reg_state {
@@ -203,6 +204,7 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
 
 struct bpf_subprog_info {
        u32 start; /* insn idx of function entry point */
+       u32 linfo_idx; /* The idx to the main_prog->aux->linfo */
        u16 stack_depth; /* max. stack depth used by this function */
 };
 
@@ -223,6 +225,7 @@ struct bpf_verifier_env {
        bool allow_ptr_leaks;
        bool seen_direct_write;
        struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */
+       const struct bpf_line_info *prev_linfo;
        struct bpf_verifier_log log;
        struct bpf_subprog_info subprog_info[BPF_MAX_SUBPROGS + 1];
        u32 subprog_cnt;
@@ -245,7 +248,7 @@ static inline struct bpf_reg_state *cur_regs(struct bpf_verifier_env *env)
        return cur_func(env)->regs;
 }
 
-int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env);
+int bpf_prog_offload_verifier_prep(struct bpf_prog *prog);
 int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
                                 int insn_idx, int prev_insn_idx);
 int bpf_prog_offload_finalize(struct bpf_verifier_env *env);
index e076c46..12502e2 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/types.h>
 
 struct btf;
+struct btf_member;
 struct btf_type;
 union bpf_attr;
 
@@ -46,5 +47,24 @@ void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
                       struct seq_file *m);
 int btf_get_fd_by_id(u32 id);
 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);
+
+#ifdef CONFIG_BPF_SYSCALL
+const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id);
+const char *btf_name_by_offset(const struct btf *btf, u32 offset);
+#else
+static inline const struct btf_type *btf_type_by_id(const struct btf *btf,
+                                                   u32 type_id)
+{
+       return NULL;
+}
+static inline const char *btf_name_by_offset(const struct btf *btf,
+                                            u32 offset)
+{
+       return NULL;
+}
+#endif
 
 #endif
index a83e1f6..f01623a 100644 (file)
@@ -169,6 +169,7 @@ void can_change_state(struct net_device *dev, struct can_frame *cf,
 
 void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
                      unsigned int idx);
+struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr);
 unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx);
 void can_free_echo_skb(struct net_device *dev, unsigned int idx);
 
index cb31683..8268811 100644 (file)
@@ -41,7 +41,12 @@ int can_rx_offload_add_timestamp(struct net_device *dev, struct can_rx_offload *
 int can_rx_offload_add_fifo(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight);
 int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, u64 reg);
 int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload);
-int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_buff *skb);
+int can_rx_offload_queue_sorted(struct can_rx_offload *offload,
+                               struct sk_buff *skb, u32 timestamp);
+unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
+                                        unsigned int idx, u32 timestamp);
+int can_rx_offload_queue_tail(struct can_rx_offload *offload,
+                             struct sk_buff *skb);
 void can_rx_offload_reset(struct can_rx_offload *offload);
 void can_rx_offload_del(struct can_rx_offload *offload);
 void can_rx_offload_enable(struct can_rx_offload *offload);
index cf68ca4..3d656f5 100644 (file)
 
 #include <linux/types.h>
 
+#define CORDIC_ANGLE_GEN       39797
+#define CORDIC_PRECISION_SHIFT 16
+#define CORDIC_NUM_ITER        (CORDIC_PRECISION_SHIFT + 2)
+
+#define CORDIC_FIXED(X)        ((s32)((X) << CORDIC_PRECISION_SHIFT))
+#define CORDIC_FLOAT(X)        (((X) >= 0) \
+               ? ((((X) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1) \
+               : -((((-(X)) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1))
+
 /**
  * struct cordic_iq - i/q coordinate.
  *
index 450b28d..0dd316a 100644 (file)
@@ -7,6 +7,8 @@
 #include <linux/radix-tree.h>
 #include <asm/pgtable.h>
 
+typedef unsigned long dax_entry_t;
+
 struct iomap_ops;
 struct dax_device;
 struct dax_operations {
@@ -88,8 +90,8 @@ int dax_writeback_mapping_range(struct address_space *mapping,
                struct block_device *bdev, struct writeback_control *wbc);
 
 struct page *dax_layout_busy_page(struct address_space *mapping);
-bool dax_lock_mapping_entry(struct page *page);
-void dax_unlock_mapping_entry(struct page *page);
+dax_entry_t dax_lock_page(struct page *page);
+void dax_unlock_page(struct page *page, dax_entry_t cookie);
 #else
 static inline bool bdev_dax_supported(struct block_device *bdev,
                int blocksize)
@@ -122,14 +124,14 @@ static inline int dax_writeback_mapping_range(struct address_space *mapping,
        return -EOPNOTSUPP;
 }
 
-static inline bool dax_lock_mapping_entry(struct page *page)
+static inline dax_entry_t dax_lock_page(struct page *page)
 {
        if (IS_DAX(page->mapping->host))
-               return true;
-       return false;
+               return ~0UL;
+       return 0;
 }
 
-static inline void dax_unlock_mapping_entry(struct page *page)
+static inline void dax_unlock_page(struct page *page, dax_entry_t cookie)
 {
 }
 #endif
index bd73e7a..9e66bfe 100644 (file)
@@ -5,7 +5,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/mem_encrypt.h>
 
-#define DIRECT_MAPPING_ERROR           0
+#define DIRECT_MAPPING_ERROR           (~(dma_addr_t)0)
 
 #ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
 #include <asm/dma-direct.h>
index 845174e..100ce4a 100644 (file)
@@ -1167,6 +1167,8 @@ static inline bool efi_enabled(int feature)
 extern void efi_reboot(enum reboot_mode reboot_mode, const char *__unused);
 
 extern bool efi_is_table_address(unsigned long phys_addr);
+
+extern int efi_apply_persistent_mem_reservations(void);
 #else
 static inline bool efi_enabled(int feature)
 {
@@ -1185,6 +1187,11 @@ static inline bool efi_is_table_address(unsigned long phys_addr)
 {
        return false;
 }
+
+static inline int efi_apply_persistent_mem_reservations(void)
+{
+       return 0;
+}
 #endif
 
 extern int efi_status_to_err(efi_status_t status);
index 572e11b..2c0af7b 100644 (file)
@@ -32,6 +32,7 @@
 struct device;
 int eth_platform_get_mac_address(struct device *dev, u8 *mac_addr);
 unsigned char *arch_get_platform_mac_address(void);
+int nvmem_get_mac_address(struct device *dev, void *addrbuf);
 u32 eth_get_headlen(void *data, unsigned int max_len);
 __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
 extern const struct header_ops eth_header_ops;
index de629b7..8c8544b 100644 (file)
@@ -449,6 +449,13 @@ struct sock_reuseport;
        offsetof(TYPE, MEMBER) ... offsetofend(TYPE, MEMBER) - 1
 #define bpf_ctx_range_till(TYPE, MEMBER1, MEMBER2)                             \
        offsetof(TYPE, MEMBER1) ... offsetofend(TYPE, MEMBER2) - 1
+#if BITS_PER_LONG == 64
+# define bpf_ctx_range_ptr(TYPE, MEMBER)                                       \
+       offsetof(TYPE, MEMBER) ... offsetofend(TYPE, MEMBER) - 1
+#else
+# define bpf_ctx_range_ptr(TYPE, MEMBER)                                       \
+       offsetof(TYPE, MEMBER) ... offsetof(TYPE, MEMBER) + 8 - 1
+#endif /* BITS_PER_LONG == 64 */
 
 #define bpf_target_off(TYPE, MEMBER, SIZE, PTR_SIZE)                           \
        ({                                                                      \
@@ -668,24 +675,10 @@ static inline u32 bpf_ctx_off_adjust_machine(u32 size)
        return size;
 }
 
-static inline bool bpf_ctx_narrow_align_ok(u32 off, u32 size_access,
-                                          u32 size_default)
-{
-       size_default = bpf_ctx_off_adjust_machine(size_default);
-       size_access  = bpf_ctx_off_adjust_machine(size_access);
-
-#ifdef __LITTLE_ENDIAN
-       return (off & (size_default - 1)) == 0;
-#else
-       return (off & (size_default - 1)) + size_access == size_default;
-#endif
-}
-
 static inline bool
 bpf_ctx_narrow_access_ok(u32 off, u32 size, u32 size_default)
 {
-       return bpf_ctx_narrow_align_ok(off, size, size_default) &&
-              size <= size_default && (size & (size - 1)) == 0;
+       return size <= size_default && (size & (size - 1)) == 0;
 }
 
 #define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0]))
@@ -732,6 +725,13 @@ void bpf_prog_free(struct bpf_prog *fp);
 
 bool bpf_opcode_in_insntable(u8 code);
 
+void bpf_prog_free_linfo(struct bpf_prog *prog);
+void bpf_prog_fill_jited_linfo(struct bpf_prog *prog,
+                              const u32 *insn_to_jit_off);
+int bpf_prog_alloc_jited_linfo(struct bpf_prog *prog);
+void bpf_prog_free_jited_linfo(struct bpf_prog *prog);
+void bpf_prog_free_unused_jited_linfo(struct bpf_prog *prog);
+
 struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags);
 struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
                                  gfp_t gfp_extra_flags);
@@ -854,7 +854,7 @@ bpf_run_sk_reuseport(struct sock_reuseport *reuse, struct sock *sk,
 extern int bpf_jit_enable;
 extern int bpf_jit_harden;
 extern int bpf_jit_kallsyms;
-extern int bpf_jit_limit;
+extern long bpf_jit_limit;
 
 typedef void (*bpf_jit_fill_hole_t)(void *area, unsigned int size);
 
@@ -866,6 +866,10 @@ void bpf_jit_binary_free(struct bpf_binary_header *hdr);
 
 void bpf_jit_free(struct bpf_prog *fp);
 
+int bpf_jit_get_func_addr(const struct bpf_prog *prog,
+                         const struct bpf_insn *insn, bool extra_pass,
+                         u64 *func_addr, bool *func_addr_fixed);
+
 struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *fp);
 void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other);
 
index 34cf0fd..610815e 100644 (file)
@@ -196,8 +196,7 @@ static inline void fscache_enqueue_retrieval(struct fscache_retrieval *op)
 static inline void fscache_retrieval_complete(struct fscache_retrieval *op,
                                              int n_pages)
 {
-       atomic_sub(n_pages, &op->n_pages);
-       if (atomic_read(&op->n_pages) <= 0)
+       if (atomic_sub_return_relaxed(n_pages, &op->n_pages) <= 0)
                fscache_op_complete(&op->op, false);
 }
 
index a397907..dd16e82 100644 (file)
@@ -777,8 +777,8 @@ struct ftrace_ret_stack {
 extern void return_to_handler(void);
 
 extern int
-ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth,
-                        unsigned long frame_pointer, unsigned long *retp);
+function_graph_enter(unsigned long ret, unsigned long func,
+                    unsigned long frame_pointer, unsigned long *retp);
 
 unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
                                    unsigned long ret, unsigned long *retp);
index 76f8db0..0705164 100644 (file)
@@ -510,18 +510,22 @@ alloc_pages(gfp_t gfp_mask, unsigned int order)
 }
 extern struct page *alloc_pages_vma(gfp_t gfp_mask, int order,
                        struct vm_area_struct *vma, unsigned long addr,
-                       int node);
+                       int node, bool hugepage);
+#define alloc_hugepage_vma(gfp_mask, vma, addr, order) \
+       alloc_pages_vma(gfp_mask, order, vma, addr, numa_node_id(), true)
 #else
 #define alloc_pages(gfp_mask, order) \
                alloc_pages_node(numa_node_id(), gfp_mask, order)
-#define alloc_pages_vma(gfp_mask, order, vma, addr, node)\
+#define alloc_pages_vma(gfp_mask, order, vma, addr, node, false)\
+       alloc_pages(gfp_mask, order)
+#define alloc_hugepage_vma(gfp_mask, vma, addr, order) \
        alloc_pages(gfp_mask, order)
 #endif
 #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
 #define alloc_page_vma(gfp_mask, vma, addr)                    \
-       alloc_pages_vma(gfp_mask, 0, vma, addr, numa_node_id())
+       alloc_pages_vma(gfp_mask, 0, vma, addr, numa_node_id(), false)
 #define alloc_page_vma_node(gfp_mask, vma, addr, node)         \
-       alloc_pages_vma(gfp_mask, 0, vma, addr, node)
+       alloc_pages_vma(gfp_mask, 0, vma, addr, node, false)
 
 extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
 extern unsigned long get_zeroed_page(gfp_t gfp_mask);
index 331dc37..dc12f5c 100644 (file)
@@ -177,6 +177,7 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
 * @attr_usage_id:      Attribute usage id as per spec
 * @report_id:  Report id to look for
 * @flag:      Synchronous or asynchronous read
+* @is_signed:   If true then fields < 32 bits will be sign-extended
 *
 * Issues a synchronous or asynchronous read request for an input attribute.
 * Returns data upto 32 bits.
@@ -190,7 +191,8 @@ enum sensor_hub_read_flags {
 int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
                                        u32 usage_id,
                                        u32 attr_usage_id, u32 report_id,
-                                       enum sensor_hub_read_flags flag
+                                       enum sensor_hub_read_flags flag,
+                                       bool is_signed
 );
 
 /**
index 387c70d..a355d61 100644 (file)
@@ -1139,34 +1139,6 @@ static inline u32 hid_report_len(struct hid_report *report)
 int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
                int interrupt);
 
-
-/**
- * struct hid_scroll_counter - Utility class for processing high-resolution
- *                             scroll events.
- * @dev: the input device for which events should be reported.
- * @microns_per_hi_res_unit: the amount moved by the user's finger for each
- *                           high-resolution unit reported by the mouse, in
- *                           microns.
- * @resolution_multiplier: the wheel's resolution in high-resolution mode as a
- *                         multiple of its lower resolution. For example, if
- *                         moving the wheel by one "notch" would result in a
- *                         value of 1 in low-resolution mode but 8 in
- *                         high-resolution, the multiplier is 8.
- * @remainder: counts the number of high-resolution units moved since the last
- *             low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should
- *             only be used by class methods.
- */
-struct hid_scroll_counter {
-       struct input_dev *dev;
-       int microns_per_hi_res_unit;
-       int resolution_multiplier;
-
-       int remainder;
-};
-
-void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
-                                     int hi_res_value);
-
 /* HID quirks API */
 unsigned long hid_lookup_quirk(const struct hid_device *hdev);
 int hid_quirks_init(char **quirks_param, __u16 bus, int count);
index b3e2436..14131b6 100644 (file)
@@ -905,6 +905,13 @@ struct vmbus_channel {
 
        bool probe_done;
 
+       /*
+        * We must offload the handling of the primary/sub channels
+        * from the single-threaded vmbus_connection.work_queue to
+        * two different workqueue, otherwise we can block
+        * vmbus_connection.work_queue and hang: see vmbus_process_offer().
+        */
+       struct work_struct add_channel_work;
 };
 
 static inline bool is_hvsock_channel(const struct vmbus_channel *c)
index 0ef67f8..3b04e72 100644 (file)
@@ -812,6 +812,8 @@ enum mesh_config_capab_flags {
        IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL       = 0x40,
 };
 
+#define IEEE80211_MESHCONF_FORM_CONNECTED_TO_GATE 0x1
+
 /**
  * mesh channel switch parameters element's flag indicator
  *
@@ -1617,7 +1619,7 @@ struct ieee80211_he_mcs_nss_supp {
  * struct ieee80211_he_operation - HE capabilities element
  *
  * This structure is the "HE operation element" fields as
- * described in P802.11ax_D2.0 section 9.4.2.238
+ * described in P802.11ax_D3.0 section 9.4.2.238
  */
 struct ieee80211_he_operation {
        __le32 he_oper_params;
@@ -2009,17 +2011,17 @@ ieee80211_he_ppe_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
 }
 
 /* HE Operation defines */
-#define IEEE80211_HE_OPERATION_BSS_COLOR_MASK                  0x0000003f
-#define IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK           0x000001c0
-#define IEEE80211_HE_OPERATION_DFLT_PE_DURATION_OFFSET         6
-#define IEEE80211_HE_OPERATION_TWT_REQUIRED                    0x00000200
-#define IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK              0x000ffc00
-#define IEEE80211_HE_OPERATION_RTS_THRESHOLD_OFFSET            10
-#define IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR               0x00100000
-#define IEEE80211_HE_OPERATION_VHT_OPER_INFO                   0x00200000
-#define IEEE80211_HE_OPERATION_MULTI_BSSID_AP                  0x10000000
-#define IEEE80211_HE_OPERATION_TX_BSSID_INDICATOR              0x20000000
-#define IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED              0x40000000
+#define IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK           0x00000003
+#define IEEE80211_HE_OPERATION_TWT_REQUIRED                    0x00000008
+#define IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK              0x00003ff0
+#define IEEE80211_HE_OPERATION_RTS_THRESHOLD_OFFSET            4
+#define IEEE80211_HE_OPERATION_VHT_OPER_INFO                   0x00004000
+#define IEEE80211_HE_OPERATION_CO_LOCATED_BSS                  0x00008000
+#define IEEE80211_HE_OPERATION_ER_SU_DISABLE                   0x00010000
+#define IEEE80211_HE_OPERATION_BSS_COLOR_MASK                  0x3f000000
+#define IEEE80211_HE_OPERATION_BSS_COLOR_OFFSET                24
+#define IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR               0x40000000
+#define IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED              0x80000000
 
 /*
  * ieee80211_he_oper_size - calculate 802.11ax HE Operations IE size
@@ -2044,7 +2046,7 @@ ieee80211_he_oper_size(const u8 *he_oper_ie)
        he_oper_params = le32_to_cpu(he_oper->he_oper_params);
        if (he_oper_params & IEEE80211_HE_OPERATION_VHT_OPER_INFO)
                oper_len += 3;
-       if (he_oper_params & IEEE80211_HE_OPERATION_MULTI_BSSID_AP)
+       if (he_oper_params & IEEE80211_HE_OPERATION_CO_LOCATED_BSS)
                oper_len++;
 
        /* Add the first byte (extension ID) to the total length */
@@ -2685,6 +2687,10 @@ enum ieee80211_tdls_actioncode {
  */
 #define WLAN_EXT_CAPA9_FTM_INITIATOR   BIT(7)
 
+/* Defines support for TWT Requester and TWT Responder */
+#define WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT  BIT(5)
+#define WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT  BIT(6)
+
 /* TDLS specific payload type in the LLC/SNAP header */
 #define WLAN_TDLS_SNAP_RFTYPE  0x2
 
index c20c7e1..627b788 100644 (file)
@@ -119,6 +119,8 @@ static inline int br_vlan_get_info(const struct net_device *dev, u16 vid,
 struct net_device *br_fdb_find_port(const struct net_device *br_dev,
                                    const unsigned char *addr,
                                    __u16 vid);
+void br_fdb_clear_offload(const struct net_device *dev, u16 vid);
+bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag);
 #else
 static inline struct net_device *
 br_fdb_find_port(const struct net_device *br_dev,
@@ -127,6 +129,16 @@ br_fdb_find_port(const struct net_device *br_dev,
 {
        return NULL;
 }
+
+static inline void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
+{
+}
+
+static inline bool
+br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
+{
+       return false;
+}
 #endif
 
 #endif
index 1be5230..4cca4da 100644 (file)
@@ -65,8 +65,7 @@ static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb)
 
 #define VLAN_PRIO_MASK         0xe000 /* Priority Code Point */
 #define VLAN_PRIO_SHIFT                13
-#define VLAN_CFI_MASK          0x1000 /* Canonical Format Indicator */
-#define VLAN_TAG_PRESENT       VLAN_CFI_MASK
+#define VLAN_CFI_MASK          0x1000 /* Canonical Format Indicator / Drop Eligible Indicator */
 #define VLAN_VID_MASK          0x0fff /* VLAN Identifier */
 #define VLAN_N_VID             4096
 
@@ -78,9 +77,10 @@ static inline bool is_vlan_dev(const struct net_device *dev)
         return dev->priv_flags & IFF_802_1Q_VLAN;
 }
 
-#define skb_vlan_tag_present(__skb)    ((__skb)->vlan_tci & VLAN_TAG_PRESENT)
-#define skb_vlan_tag_get(__skb)                ((__skb)->vlan_tci & ~VLAN_TAG_PRESENT)
+#define skb_vlan_tag_present(__skb)    ((__skb)->vlan_present)
+#define skb_vlan_tag_get(__skb)                ((__skb)->vlan_tci)
 #define skb_vlan_tag_get_id(__skb)     ((__skb)->vlan_tci & VLAN_VID_MASK)
+#define skb_vlan_tag_get_cfi(__skb)    (!!((__skb)->vlan_tci & VLAN_CFI_MASK))
 #define skb_vlan_tag_get_prio(__skb)   (((__skb)->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT)
 
 static inline int vlan_get_rx_ctag_filter_info(struct net_device *dev)
@@ -480,7 +480,7 @@ static inline struct sk_buff *vlan_insert_tag_set_proto(struct sk_buff *skb,
  */
 static inline void __vlan_hwaccel_clear_tag(struct sk_buff *skb)
 {
-       skb->vlan_tci = 0;
+       skb->vlan_present = 0;
 }
 
 /**
@@ -492,6 +492,7 @@ static inline void __vlan_hwaccel_clear_tag(struct sk_buff *skb)
  */
 static inline void __vlan_hwaccel_copy_tag(struct sk_buff *dst, const struct sk_buff *src)
 {
+       dst->vlan_present = src->vlan_present;
        dst->vlan_proto = src->vlan_proto;
        dst->vlan_tci = src->vlan_tci;
 }
@@ -526,7 +527,8 @@ static inline void __vlan_hwaccel_put_tag(struct sk_buff *skb,
                                          __be16 vlan_proto, u16 vlan_tci)
 {
        skb->vlan_proto = vlan_proto;
-       skb->vlan_tci = VLAN_TAG_PRESENT | vlan_tci;
+       skb->vlan_tci = vlan_tci;
+       skb->vlan_present = 1;
 }
 
 /**
diff --git a/include/linux/indirect_call_wrapper.h b/include/linux/indirect_call_wrapper.h
new file mode 100644 (file)
index 0000000..00d7e8e
--- /dev/null
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_INDIRECT_CALL_WRAPPER_H
+#define _LINUX_INDIRECT_CALL_WRAPPER_H
+
+#ifdef CONFIG_RETPOLINE
+
+/*
+ * INDIRECT_CALL_$NR - wrapper for indirect calls with $NR known builtin
+ *  @f: function pointer
+ *  @f$NR: builtin functions names, up to $NR of them
+ *  @__VA_ARGS__: arguments for @f
+ *
+ * Avoid retpoline overhead for known builtin, checking @f vs each of them and
+ * eventually invoking directly the builtin function. The functions are check
+ * in the given order. Fallback to the indirect call.
+ */
+#define INDIRECT_CALL_1(f, f1, ...)                                    \
+       ({                                                              \
+               likely(f == f1) ? f1(__VA_ARGS__) : f(__VA_ARGS__);     \
+       })
+#define INDIRECT_CALL_2(f, f2, f1, ...)                                        \
+       ({                                                              \
+               likely(f == f2) ? f2(__VA_ARGS__) :                     \
+                                 INDIRECT_CALL_1(f, f1, __VA_ARGS__);  \
+       })
+
+#define INDIRECT_CALLABLE_DECLARE(f)   f
+#define INDIRECT_CALLABLE_SCOPE
+
+#else
+#define INDIRECT_CALL_1(f, f1, ...) f(__VA_ARGS__)
+#define INDIRECT_CALL_2(f, f2, f1, ...) f(__VA_ARGS__)
+#define INDIRECT_CALLABLE_DECLARE(f)
+#define INDIRECT_CALLABLE_SCOPE                static
+#endif
+
+/*
+ * We can use INDIRECT_CALL_$NR for ipv6 related functions only if ipv6 is
+ * builtin, this macro simplify dealing with indirect calls with only ipv4/ipv6
+ * alternatives
+ */
+#if IS_BUILTIN(CONFIG_IPV6)
+#define INDIRECT_CALL_INET(f, f2, f1, ...) \
+       INDIRECT_CALL_2(f, f2, f1, __VA_ARGS__)
+#elif IS_ENABLED(CONFIG_INET)
+#define INDIRECT_CALL_INET(f, f2, f1, ...) INDIRECT_CALL_1(f, f1, __VA_ARGS__)
+#else
+#define INDIRECT_CALL_INET(f, f2, f1, ...) f(__VA_ARGS__)
+#endif
+
+#endif
index 22443d7..a99c588 100644 (file)
@@ -57,6 +57,15 @@ static inline void linkmode_clear_bit(int nr, volatile unsigned long *addr)
        __clear_bit(nr, addr);
 }
 
+static inline void linkmode_mod_bit(int nr, volatile unsigned long *addr,
+                                   int set)
+{
+       if (set)
+               linkmode_set_bit(nr, addr);
+       else
+               linkmode_clear_bit(nr, addr);
+}
+
 static inline void linkmode_change_bit(int nr, volatile unsigned long *addr)
 {
        __change_bit(nr, addr);
index bac395f..5228c62 100644 (file)
@@ -139,8 +139,6 @@ struct mempolicy *mpol_shared_policy_lookup(struct shared_policy *sp,
 struct mempolicy *get_task_policy(struct task_struct *p);
 struct mempolicy *__get_vma_policy(struct vm_area_struct *vma,
                unsigned long addr);
-struct mempolicy *get_vma_policy(struct vm_area_struct *vma,
-                                               unsigned long addr);
 bool vma_policy_mof(struct vm_area_struct *vma);
 
 extern void numa_default_policy(void);
index fb7ae4a..6fee8b1 100644 (file)
@@ -288,22 +288,22 @@ static inline u32 mii_stat1000_to_ethtool_lpa_t(u32 lpa)
 }
 
 /**
- * mii_stat1000_to_linkmode_lpa_t
+ * mii_stat1000_mod_linkmode_lpa_t
  * @advertising: target the linkmode advertisement settings
  * @adv: value of the MII_STAT1000 register
  *
  * A small helper function that translates MII_STAT1000 bits, when in
- * 1000Base-T mode, to linkmode advertisement settings.
+ * 1000Base-T mode, to linkmode advertisement settings. Other bits in
+ * advertising are not changes.
  */
-static inline void mii_stat1000_to_linkmode_lpa_t(unsigned long *advertising,
-                                                 u32 lpa)
+static inline void mii_stat1000_mod_linkmode_lpa_t(unsigned long *advertising,
+                                                  u32 lpa)
 {
-       if (lpa & LPA_1000HALF)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-                                advertising);
-       if (lpa & LPA_1000FULL)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-                                advertising);
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                        advertising, lpa & LPA_1000HALF);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                        advertising, lpa & LPA_1000FULL);
 }
 
 /**
@@ -372,35 +372,51 @@ static inline u32 mii_lpa_to_ethtool_lpa_x(u32 lpa)
        return result | mii_adv_to_ethtool_adv_x(lpa);
 }
 
+/**
+ * mii_adv_mod_linkmode_adv_t
+ * @advertising:pointer to destination link mode.
+ * @adv: value of the MII_ADVERTISE register
+ *
+ * A small helper function that translates MII_ADVERTISE bits to
+ * linkmode advertisement settings. Leaves other bits unchanged.
+ */
+static inline void mii_adv_mod_linkmode_adv_t(unsigned long *advertising,
+                                             u32 adv)
+{
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+                        advertising, adv & ADVERTISE_10HALF);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+                        advertising, adv & ADVERTISE_10FULL);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+                        advertising, adv & ADVERTISE_100HALF);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+                        advertising, adv & ADVERTISE_100FULL);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising,
+                        adv & ADVERTISE_PAUSE_CAP);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                        advertising, adv & ADVERTISE_PAUSE_ASYM);
+}
+
 /**
  * mii_adv_to_linkmode_adv_t
  * @advertising:pointer to destination link mode.
  * @adv: value of the MII_ADVERTISE register
  *
  * A small helper function that translates MII_ADVERTISE bits
- * to linkmode advertisement settings.
+ * to linkmode advertisement settings. Clears the old value
+ * of advertising.
  */
 static inline void mii_adv_to_linkmode_adv_t(unsigned long *advertising,
                                             u32 adv)
 {
        linkmode_zero(advertising);
 
-       if (adv & ADVERTISE_10HALF)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
-                                advertising);
-       if (adv & ADVERTISE_10FULL)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
-                                advertising);
-       if (adv & ADVERTISE_100HALF)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
-                                advertising);
-       if (adv & ADVERTISE_100FULL)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
-                                advertising);
-       if (adv & ADVERTISE_PAUSE_CAP)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising);
-       if (adv & ADVERTISE_PAUSE_ASYM)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertising);
+       mii_adv_mod_linkmode_adv_t(advertising, adv);
 }
 
 /**
@@ -408,16 +424,35 @@ static inline void mii_adv_to_linkmode_adv_t(unsigned long *advertising,
  * @adv: value of the MII_LPA register
  *
  * A small helper function that translates MII_LPA bits, when in
- * 1000Base-T mode, to linkmode LP advertisement settings.
+ * 1000Base-T mode, to linkmode LP advertisement settings. Clears the
+ * old value of advertising
  */
 static inline void mii_lpa_to_linkmode_lpa_t(unsigned long *lp_advertising,
                                             u32 lpa)
 {
+       mii_adv_to_linkmode_adv_t(lp_advertising, lpa);
+
        if (lpa & LPA_LPACK)
                linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
                                 lp_advertising);
 
-       mii_adv_to_linkmode_adv_t(lp_advertising, lpa);
+}
+
+/**
+ * mii_lpa_mod_linkmode_lpa_t
+ * @adv: value of the MII_LPA register
+ *
+ * A small helper function that translates MII_LPA bits, when in
+ * 1000Base-T mode, to linkmode LP advertisement settings. Leaves
+ * other bits unchanged.
+ */
+static inline void mii_lpa_mod_linkmode_lpa_t(unsigned long *lp_advertising,
+                                             u32 lpa)
+{
+       mii_adv_mod_linkmode_adv_t(lp_advertising, lpa);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                        lp_advertising, lpa & LPA_LPACK);
 }
 
 /**
index dca6ab4..36e412c 100644 (file)
@@ -226,6 +226,7 @@ enum {
        MLX4_DEV_CAP_FLAG2_SL_TO_VL_CHANGE_EVENT = 1ULL << 37,
        MLX4_DEV_CAP_FLAG2_USER_MAC_EN          = 1ULL << 38,
        MLX4_DEV_CAP_FLAG2_DRIVER_VERSION_TO_FW = 1ULL << 39,
+       MLX4_DEV_CAP_FLAG2_SW_CQ_INIT           = 1ULL << 40,
 };
 
 enum {
@@ -1136,7 +1137,8 @@ void mlx4_free_hwq_res(struct mlx4_dev *mdev, struct mlx4_hwq_resources *wqres,
 
 int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, struct mlx4_mtt *mtt,
                  struct mlx4_uar *uar, u64 db_rec, struct mlx4_cq *cq,
-                 unsigned vector, int collapsed, int timestamp_en);
+                 unsigned int vector, int collapsed, int timestamp_en,
+                 void *buf_addr, bool user_cq);
 void mlx4_cq_free(struct mlx4_dev *dev, struct mlx4_cq *cq);
 int mlx4_qp_reserve_range(struct mlx4_dev *dev, int cnt, int align,
                          int *base, u8 flags, u8 usage);
index 31a7505..612c8c2 100644 (file)
@@ -60,7 +60,7 @@ struct mlx5_core_cq {
        } tasklet_ctx;
        int                     reset_notify_added;
        struct list_head        reset_notify;
-       struct mlx5_eq          *eq;
+       struct mlx5_eq_comp     *eq;
        u16 uid;
 };
 
@@ -125,9 +125,9 @@ struct mlx5_cq_modify_params {
 };
 
 enum {
-       CQE_SIZE_64 = 0,
-       CQE_SIZE_128 = 1,
-       CQE_SIZE_128_PAD = 2,
+       CQE_STRIDE_64 = 0,
+       CQE_STRIDE_128 = 1,
+       CQE_STRIDE_128_PAD = 2,
 };
 
 #define MLX5_MAX_CQ_PERIOD (BIT(__mlx5_bit_sz(cqc, cq_period)) - 1)
@@ -135,8 +135,8 @@ enum {
 
 static inline int cqe_sz_to_mlx_sz(u8 size, int padding_128_en)
 {
-       return padding_128_en ? CQE_SIZE_128_PAD :
-                               size == 64 ? CQE_SIZE_64 : CQE_SIZE_128;
+       return padding_128_en ? CQE_STRIDE_128_PAD :
+                               size == 64 ? CQE_STRIDE_64 : CQE_STRIDE_128;
 }
 
 static inline void mlx5_cq_set_ci(struct mlx5_core_cq *cq)
index b4c0457..4674b9e 100644 (file)
@@ -212,6 +212,13 @@ enum {
        MLX5_PFAULT_SUBTYPE_RDMA = 1,
 };
 
+enum wqe_page_fault_type {
+       MLX5_WQE_PF_TYPE_RMP = 0,
+       MLX5_WQE_PF_TYPE_REQ_SEND_OR_WRITE = 1,
+       MLX5_WQE_PF_TYPE_RESP = 2,
+       MLX5_WQE_PF_TYPE_REQ_READ_OR_ATOMIC = 3,
+};
+
 enum {
        MLX5_PERM_LOCAL_READ    = 1 << 2,
        MLX5_PERM_LOCAL_WRITE   = 1 << 3,
@@ -294,9 +301,15 @@ enum {
        MLX5_EVENT_QUEUE_TYPE_DCT = 6,
 };
 
+/* mlx5 components can subscribe to any one of these events via
+ * mlx5_eq_notifier_register API.
+ */
 enum mlx5_event {
+       /* Special value to subscribe to any event */
+       MLX5_EVENT_TYPE_NOTIFY_ANY         = 0x0,
+       /* HW events enum start: comp events are not subscribable */
        MLX5_EVENT_TYPE_COMP               = 0x0,
-
+       /* HW Async events enum start: subscribable events */
        MLX5_EVENT_TYPE_PATH_MIG           = 0x01,
        MLX5_EVENT_TYPE_COMM_EST           = 0x02,
        MLX5_EVENT_TYPE_SQ_DRAINED         = 0x03,
@@ -317,6 +330,7 @@ enum mlx5_event {
        MLX5_EVENT_TYPE_TEMP_WARN_EVENT    = 0x17,
        MLX5_EVENT_TYPE_REMOTE_CONFIG      = 0x19,
        MLX5_EVENT_TYPE_GENERAL_EVENT      = 0x22,
+       MLX5_EVENT_TYPE_MONITOR_COUNTER    = 0x24,
        MLX5_EVENT_TYPE_PPS_EVENT          = 0x25,
 
        MLX5_EVENT_TYPE_DB_BF_CONGESTION   = 0x1a,
@@ -334,6 +348,8 @@ enum mlx5_event {
        MLX5_EVENT_TYPE_FPGA_QP_ERROR      = 0x21,
 
        MLX5_EVENT_TYPE_DEVICE_TRACER      = 0x26,
+
+       MLX5_EVENT_TYPE_MAX                = MLX5_EVENT_TYPE_DEVICE_TRACER + 1,
 };
 
 enum {
@@ -766,6 +782,11 @@ static inline u8 mlx5_get_cqe_format(struct mlx5_cqe64 *cqe)
        return (cqe->op_own >> 2) & 0x3;
 }
 
+static inline u8 get_cqe_opcode(struct mlx5_cqe64 *cqe)
+{
+       return cqe->op_own >> 4;
+}
+
 static inline u8 get_cqe_lro_tcppsh(struct mlx5_cqe64 *cqe)
 {
        return (cqe->lro_tcppsh_abort_dupack >> 6) & 1;
index aa5963b..4d16ba0 100644 (file)
 #include <linux/mempool.h>
 #include <linux/interrupt.h>
 #include <linux/idr.h>
+#include <linux/notifier.h>
 
 #include <linux/mlx5/device.h>
 #include <linux/mlx5/doorbell.h>
-#include <linux/mlx5/srq.h>
+#include <linux/mlx5/eq.h>
 #include <linux/timecounter.h>
 #include <linux/ptp_clock_kernel.h>
 
@@ -84,18 +85,6 @@ enum {
        MLX5_MAX_PORTS  = 2,
 };
 
-enum {
-       MLX5_EQ_VEC_PAGES        = 0,
-       MLX5_EQ_VEC_CMD          = 1,
-       MLX5_EQ_VEC_ASYNC        = 2,
-       MLX5_EQ_VEC_PFAULT       = 3,
-       MLX5_EQ_VEC_COMP_BASE,
-};
-
-enum {
-       MLX5_MAX_IRQ_NAME       = 32
-};
-
 enum {
        MLX5_ATOMIC_MODE_OFFSET = 16,
        MLX5_ATOMIC_MODE_IB_COMP = 1,
@@ -205,16 +194,7 @@ struct mlx5_rsc_debug {
 };
 
 enum mlx5_dev_event {
-       MLX5_DEV_EVENT_SYS_ERROR,
-       MLX5_DEV_EVENT_PORT_UP,
-       MLX5_DEV_EVENT_PORT_DOWN,
-       MLX5_DEV_EVENT_PORT_INITIALIZED,
-       MLX5_DEV_EVENT_LID_CHANGE,
-       MLX5_DEV_EVENT_PKEY_CHANGE,
-       MLX5_DEV_EVENT_GUID_CHANGE,
-       MLX5_DEV_EVENT_CLIENT_REREG,
-       MLX5_DEV_EVENT_PPS,
-       MLX5_DEV_EVENT_DELAY_DROP_TIMEOUT,
+       MLX5_DEV_EVENT_SYS_ERROR = 128, /* 0 - 127 are FW events */
 };
 
 enum mlx5_port_status {
@@ -222,14 +202,6 @@ enum mlx5_port_status {
        MLX5_PORT_DOWN      = 2,
 };
 
-enum mlx5_eq_type {
-       MLX5_EQ_TYPE_COMP,
-       MLX5_EQ_TYPE_ASYNC,
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       MLX5_EQ_TYPE_PF,
-#endif
-};
-
 struct mlx5_bfreg_info {
        u32                    *sys_pages;
        int                     num_low_latency_bfregs;
@@ -297,6 +269,8 @@ struct mlx5_cmd_stats {
 };
 
 struct mlx5_cmd {
+       struct mlx5_nb    nb;
+
        void           *cmd_alloc_buf;
        dma_addr_t      alloc_dma;
        int             alloc_size;
@@ -366,51 +340,6 @@ struct mlx5_frag_buf_ctrl {
        u8                      log_frag_strides;
 };
 
-struct mlx5_eq_tasklet {
-       struct list_head list;
-       struct list_head process_list;
-       struct tasklet_struct task;
-       /* lock on completion tasklet list */
-       spinlock_t lock;
-};
-
-struct mlx5_eq_pagefault {
-       struct work_struct       work;
-       /* Pagefaults lock */
-       spinlock_t               lock;
-       struct workqueue_struct *wq;
-       mempool_t               *pool;
-};
-
-struct mlx5_cq_table {
-       /* protect radix tree */
-       spinlock_t              lock;
-       struct radix_tree_root  tree;
-};
-
-struct mlx5_eq {
-       struct mlx5_core_dev   *dev;
-       struct mlx5_cq_table    cq_table;
-       __be32 __iomem         *doorbell;
-       u32                     cons_index;
-       struct mlx5_frag_buf    buf;
-       int                     size;
-       unsigned int            irqn;
-       u8                      eqn;
-       int                     nent;
-       u64                     mask;
-       struct list_head        list;
-       int                     index;
-       struct mlx5_rsc_debug   *dbg;
-       enum mlx5_eq_type       type;
-       union {
-               struct mlx5_eq_tasklet   tasklet_ctx;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-               struct mlx5_eq_pagefault pf_ctx;
-#endif
-       };
-};
-
 struct mlx5_core_psv {
        u32     psv_idx;
        struct psv_layout {
@@ -463,36 +392,6 @@ struct mlx5_core_rsc_common {
        struct completion       free;
 };
 
-struct mlx5_core_srq {
-       struct mlx5_core_rsc_common     common; /* must be first */
-       u32             srqn;
-       int             max;
-       size_t          max_gs;
-       size_t          max_avail_gather;
-       int             wqe_shift;
-       void (*event)   (struct mlx5_core_srq *, enum mlx5_event);
-
-       atomic_t                refcount;
-       struct completion       free;
-       u16             uid;
-};
-
-struct mlx5_eq_table {
-       void __iomem           *update_ci;
-       void __iomem           *update_arm_ci;
-       struct list_head        comp_eqs_list;
-       struct mlx5_eq          pages_eq;
-       struct mlx5_eq          async_eq;
-       struct mlx5_eq          cmd_eq;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       struct mlx5_eq          pfault_eq;
-#endif
-       int                     num_comp_vectors;
-       /* protect EQs list
-        */
-       spinlock_t              lock;
-};
-
 struct mlx5_uars_page {
        void __iomem           *map;
        bool                    wc;
@@ -542,13 +441,8 @@ struct mlx5_core_health {
 };
 
 struct mlx5_qp_table {
-       /* protect radix tree
-        */
-       spinlock_t              lock;
-       struct radix_tree_root  tree;
-};
+       struct notifier_block   nb;
 
-struct mlx5_srq_table {
        /* protect radix tree
         */
        spinlock_t              lock;
@@ -575,11 +469,6 @@ struct mlx5_core_sriov {
        int                     enabled_vfs;
 };
 
-struct mlx5_irq_info {
-       cpumask_var_t mask;
-       char name[MLX5_MAX_IRQ_NAME];
-};
-
 struct mlx5_fc_stats {
        spinlock_t counters_idr_lock; /* protects counters_idr */
        struct idr counters_idr;
@@ -593,10 +482,12 @@ struct mlx5_fc_stats {
        unsigned long sampling_interval; /* jiffies */
 };
 
+struct mlx5_events;
 struct mlx5_mpfs;
 struct mlx5_eswitch;
 struct mlx5_lag;
-struct mlx5_pagefault;
+struct mlx5_devcom;
+struct mlx5_eq_table;
 
 struct mlx5_rate_limit {
        u32                     rate;
@@ -619,37 +510,12 @@ struct mlx5_rl_table {
        struct mlx5_rl_entry   *rl_entry;
 };
 
-enum port_module_event_status_type {
-       MLX5_MODULE_STATUS_PLUGGED   = 0x1,
-       MLX5_MODULE_STATUS_UNPLUGGED = 0x2,
-       MLX5_MODULE_STATUS_ERROR     = 0x3,
-       MLX5_MODULE_STATUS_NUM       = 0x3,
-};
-
-enum  port_module_event_error_type {
-       MLX5_MODULE_EVENT_ERROR_POWER_BUDGET_EXCEEDED,
-       MLX5_MODULE_EVENT_ERROR_LONG_RANGE_FOR_NON_MLNX_CABLE_MODULE,
-       MLX5_MODULE_EVENT_ERROR_BUS_STUCK,
-       MLX5_MODULE_EVENT_ERROR_NO_EEPROM_RETRY_TIMEOUT,
-       MLX5_MODULE_EVENT_ERROR_ENFORCE_PART_NUMBER_LIST,
-       MLX5_MODULE_EVENT_ERROR_UNKNOWN_IDENTIFIER,
-       MLX5_MODULE_EVENT_ERROR_HIGH_TEMPERATURE,
-       MLX5_MODULE_EVENT_ERROR_BAD_CABLE,
-       MLX5_MODULE_EVENT_ERROR_UNKNOWN,
-       MLX5_MODULE_EVENT_ERROR_NUM,
-};
-
-struct mlx5_port_module_event_stats {
-       u64 status_counters[MLX5_MODULE_STATUS_NUM];
-       u64 error_counters[MLX5_MODULE_EVENT_ERROR_NUM];
-};
-
 struct mlx5_priv {
        char                    name[MLX5_MAX_NAME_LEN];
-       struct mlx5_eq_table    eq_table;
-       struct mlx5_irq_info    *irq_info;
+       struct mlx5_eq_table    *eq_table;
 
        /* pages stuff */
+       struct mlx5_nb          pg_nb;
        struct workqueue_struct *pg_wq;
        struct rb_root          page_root;
        int                     fw_pages;
@@ -659,8 +525,6 @@ struct mlx5_priv {
 
        struct mlx5_core_health health;
 
-       struct mlx5_srq_table   srq_table;
-
        /* start: qp staff */
        struct mlx5_qp_table    qp_table;
        struct dentry          *qp_debugfs;
@@ -690,28 +554,18 @@ struct mlx5_priv {
        struct list_head        dev_list;
        struct list_head        ctx_list;
        spinlock_t              ctx_lock;
-
-       struct list_head        waiting_events_list;
-       bool                    is_accum_events;
+       struct mlx5_events      *events;
 
        struct mlx5_flow_steering *steering;
        struct mlx5_mpfs        *mpfs;
        struct mlx5_eswitch     *eswitch;
        struct mlx5_core_sriov  sriov;
        struct mlx5_lag         *lag;
+       struct mlx5_devcom      *devcom;
        unsigned long           pci_dev_data;
        struct mlx5_fc_stats            fc_stats;
        struct mlx5_rl_table            rl_table;
 
-       struct mlx5_port_module_event_stats  pme_stats;
-
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
-       void                  (*pfault)(struct mlx5_core_dev *dev,
-                                       void *context,
-                                       struct mlx5_pagefault *pfault);
-       void                   *pfault_ctx;
-       struct srcu_struct      pfault_srcu;
-#endif
        struct mlx5_bfreg_data          bfregs;
        struct mlx5_uars_page          *uar;
 };
@@ -736,44 +590,6 @@ enum mlx5_pagefault_type_flags {
        MLX5_PFAULT_RDMA      = 1 << 2,
 };
 
-/* Contains the details of a pagefault. */
-struct mlx5_pagefault {
-       u32                     bytes_committed;
-       u32                     token;
-       u8                      event_subtype;
-       u8                      type;
-       union {
-               /* Initiator or send message responder pagefault details. */
-               struct {
-                       /* Received packet size, only valid for responders. */
-                       u32     packet_size;
-                       /*
-                        * Number of resource holding WQE, depends on type.
-                        */
-                       u32     wq_num;
-                       /*
-                        * WQE index. Refers to either the send queue or
-                        * receive queue, according to event_subtype.
-                        */
-                       u16     wqe_index;
-               } wqe;
-               /* RDMA responder pagefault details */
-               struct {
-                       u32     r_key;
-                       /*
-                        * Received packet size, minimal size page fault
-                        * resolution required for forward progress.
-                        */
-                       u32     packet_size;
-                       u32     rdma_op_len;
-                       u64     rdma_va;
-               } rdma;
-       };
-
-       struct mlx5_eq         *eq;
-       struct work_struct      work;
-};
-
 struct mlx5_td {
        struct list_head tirs_list;
        u32              tdn;
@@ -803,6 +619,8 @@ struct mlx5_pps {
 };
 
 struct mlx5_clock {
+       struct mlx5_core_dev      *mdev;
+       struct mlx5_nb             pps_nb;
        seqlock_t                  lock;
        struct cyclecounter        cycles;
        struct timecounter         tc;
@@ -810,7 +628,6 @@ struct mlx5_clock {
        u32                        nominal_c_mult;
        unsigned long              overflow_period;
        struct delayed_work        overflow_work;
-       struct mlx5_core_dev      *mdev;
        struct ptp_clock          *ptp;
        struct ptp_clock_info      ptp_info;
        struct mlx5_pps            pps_info;
@@ -843,9 +660,6 @@ struct mlx5_core_dev {
        /* sync interface state */
        struct mutex            intf_state_mutex;
        unsigned long           intf_state;
-       void                    (*event) (struct mlx5_core_dev *dev,
-                                         enum mlx5_dev_event event,
-                                         unsigned long param);
        struct mlx5_priv        priv;
        struct mlx5_profile     *profile;
        atomic_t                num_qps;
@@ -858,9 +672,6 @@ struct mlx5_core_dev {
        } roce;
 #ifdef CONFIG_MLX5_FPGA
        struct mlx5_fpga_device *fpga;
-#endif
-#ifdef CONFIG_RFS_ACCEL
-       struct cpu_rmap         *rmap;
 #endif
        struct mlx5_clock        clock;
        struct mlx5_ib_clock_info  *clock_info;
@@ -1070,13 +881,6 @@ struct mlx5_cmd_mailbox *mlx5_alloc_cmd_mailbox_chain(struct mlx5_core_dev *dev,
                                                      gfp_t flags, int npages);
 void mlx5_free_cmd_mailbox_chain(struct mlx5_core_dev *dev,
                                 struct mlx5_cmd_mailbox *head);
-int mlx5_core_create_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                        struct mlx5_srq_attr *in);
-int mlx5_core_destroy_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq);
-int mlx5_core_query_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                       struct mlx5_srq_attr *out);
-int mlx5_core_arm_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
-                     u16 lwm, int is_srq);
 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,
@@ -1095,9 +899,9 @@ 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);
-void mlx5_pagealloc_init(struct mlx5_core_dev *dev);
+int mlx5_pagealloc_init(struct mlx5_core_dev *dev);
 void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev);
-int mlx5_pagealloc_start(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);
@@ -1108,9 +912,6 @@ void mlx5_unregister_debugfs(void);
 
 void mlx5_fill_page_array(struct mlx5_frag_buf *buf, __be64 *pas);
 void mlx5_fill_page_frag_array(struct mlx5_frag_buf *frag_buf, __be64 *pas);
-void mlx5_rsc_event(struct mlx5_core_dev *dev, u32 rsn, int event_type);
-void mlx5_srq_event(struct mlx5_core_dev *dev, u32 srqn, int event_type);
-struct mlx5_core_srq *mlx5_core_get_srq(struct mlx5_core_dev *dev, u32 srqn);
 int mlx5_vector2eqn(struct mlx5_core_dev *dev, int vector, int *eqn,
                    unsigned int *irqn);
 int mlx5_core_attach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn);
@@ -1155,6 +956,9 @@ int mlx5_alloc_bfreg(struct mlx5_core_dev *mdev, struct mlx5_sq_bfreg *bfreg,
                     bool map_wc, bool fast_path);
 void mlx5_free_bfreg(struct mlx5_core_dev *mdev, struct mlx5_sq_bfreg *bfreg);
 
+unsigned int mlx5_comp_vectors_count(struct mlx5_core_dev *dev);
+struct cpumask *
+mlx5_comp_irq_get_affinity_mask(struct mlx5_core_dev *dev, int vector);
 unsigned int mlx5_core_reserved_gids_count(struct mlx5_core_dev *dev);
 int mlx5_core_roce_gid_set(struct mlx5_core_dev *dev, unsigned int index,
                           u8 roce_version, u8 roce_l3_type, const u8 *gid,
@@ -1202,23 +1006,21 @@ struct mlx5_interface {
        void                    (*remove)(struct mlx5_core_dev *dev, void *context);
        int                     (*attach)(struct mlx5_core_dev *dev, void *context);
        void                    (*detach)(struct mlx5_core_dev *dev, void *context);
-       void                    (*event)(struct mlx5_core_dev *dev, void *context,
-                                        enum mlx5_dev_event event, unsigned long param);
-       void                    (*pfault)(struct mlx5_core_dev *dev,
-                                         void *context,
-                                         struct mlx5_pagefault *pfault);
-       void *                  (*get_dev)(void *context);
        int                     protocol;
        struct list_head        list;
 };
 
-void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol);
 int mlx5_register_interface(struct mlx5_interface *intf);
 void mlx5_unregister_interface(struct mlx5_interface *intf);
+int mlx5_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb);
+int mlx5_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb);
+
 int mlx5_core_query_vendor_id(struct mlx5_core_dev *mdev, u32 *vendor_id);
 
 int mlx5_cmd_create_vport_lag(struct mlx5_core_dev *dev);
 int mlx5_cmd_destroy_vport_lag(struct mlx5_core_dev *dev);
+bool mlx5_lag_is_roce(struct mlx5_core_dev *dev);
+bool mlx5_lag_is_sriov(struct mlx5_core_dev *dev);
 bool mlx5_lag_is_active(struct mlx5_core_dev *dev);
 struct net_device *mlx5_lag_get_roce_netdev(struct mlx5_core_dev *dev);
 int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,
@@ -1306,10 +1108,4 @@ enum {
        MLX5_TRIGGERED_CMD_COMP = (u64)1 << 32,
 };
 
-static inline const struct cpumask *
-mlx5_get_vector_affinity_hint(struct mlx5_core_dev *dev, int vector)
-{
-       return dev->priv.irq_info[vector].mask;
-}
-
 #endif /* MLX5_DRIVER_H */
diff --git a/include/linux/mlx5/eq.h b/include/linux/mlx5/eq.h
new file mode 100644 (file)
index 0000000..00045cc
--- /dev/null
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2018 Mellanox Technologies. */
+
+#ifndef MLX5_CORE_EQ_H
+#define MLX5_CORE_EQ_H
+
+enum {
+       MLX5_EQ_PAGEREQ_IDX        = 0,
+       MLX5_EQ_CMD_IDX            = 1,
+       MLX5_EQ_ASYNC_IDX          = 2,
+       /* reserved to be used by mlx5_core ulps (mlx5e/mlx5_ib) */
+       MLX5_EQ_PFAULT_IDX         = 3,
+       MLX5_EQ_MAX_ASYNC_EQS,
+       /* completion eqs vector indices start here */
+       MLX5_EQ_VEC_COMP_BASE = MLX5_EQ_MAX_ASYNC_EQS,
+};
+
+#define MLX5_NUM_CMD_EQE   (32)
+#define MLX5_NUM_ASYNC_EQE (0x1000)
+#define MLX5_NUM_SPARE_EQE (0x80)
+
+struct mlx5_eq;
+struct mlx5_core_dev;
+
+struct mlx5_eq_param {
+       u8             index;
+       int            nent;
+       u64            mask;
+       void          *context;
+       irq_handler_t  handler;
+};
+
+struct mlx5_eq *
+mlx5_eq_create_generic(struct mlx5_core_dev *dev, const char *name,
+                      struct mlx5_eq_param *param);
+int
+mlx5_eq_destroy_generic(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
+
+struct mlx5_eqe *mlx5_eq_get_eqe(struct mlx5_eq *eq, u32 cc);
+void mlx5_eq_update_ci(struct mlx5_eq *eq, u32 cc, bool arm);
+
+/* The HCA will think the queue has overflowed if we
+ * don't tell it we've been processing events.  We
+ * create EQs with MLX5_NUM_SPARE_EQE extra entries,
+ * so we must update our consumer index at
+ * least that often.
+ *
+ * mlx5_eq_update_cc must be called on every EQE @EQ irq handler
+ */
+static inline u32 mlx5_eq_update_cc(struct mlx5_eq *eq, u32 cc)
+{
+       if (unlikely(cc >= MLX5_NUM_SPARE_EQE)) {
+               mlx5_eq_update_ci(eq, cc, 0);
+               cc = 0;
+       }
+       return cc;
+}
+
+struct mlx5_nb {
+       struct notifier_block nb;
+       u8 event_type;
+};
+
+#define mlx5_nb_cof(ptr, type, member) \
+       (container_of(container_of(ptr, struct mlx5_nb, nb), type, member))
+
+#define MLX5_NB_INIT(name, handler, event) do {              \
+       (name)->nb.notifier_call = handler;                  \
+       (name)->event_type = MLX5_EVENT_TYPE_##event;        \
+} while (0)
+
+#endif /* MLX5_CORE_EQ_H */
index 5660f07..9df51da 100644 (file)
@@ -86,6 +86,11 @@ struct mlx5_flow_spec {
        u32  match_value[MLX5_ST_SZ_DW(fte_match_param)];
 };
 
+enum {
+       MLX5_FLOW_DEST_VPORT_VHCA_ID      = BIT(0),
+       MLX5_FLOW_DEST_VPORT_REFORMAT_ID  = BIT(1),
+};
+
 struct mlx5_flow_destination {
        enum mlx5_flow_destination_type type;
        union {
@@ -96,7 +101,8 @@ struct mlx5_flow_destination {
                struct {
                        u16             num;
                        u16             vhca_id;
-                       bool            vhca_id_valid;
+                       u32             reformat_id;
+                       u8              flags;
                } vport;
        };
 };
index dbff9ff..821b751 100644 (file)
@@ -161,6 +161,8 @@ enum {
        MLX5_CMD_OP_ALLOC_Q_COUNTER               = 0x771,
        MLX5_CMD_OP_DEALLOC_Q_COUNTER             = 0x772,
        MLX5_CMD_OP_QUERY_Q_COUNTER               = 0x773,
+       MLX5_CMD_OP_SET_MONITOR_COUNTER           = 0x774,
+       MLX5_CMD_OP_ARM_MONITOR_COUNTER           = 0x775,
        MLX5_CMD_OP_SET_PP_RATE_LIMIT             = 0x780,
        MLX5_CMD_OP_QUERY_RATE_LIMIT              = 0x781,
        MLX5_CMD_OP_CREATE_SCHEDULING_ELEMENT      = 0x782,
@@ -349,7 +351,7 @@ struct mlx5_ifc_flow_table_prop_layout_bits {
        u8         reformat_l3_tunnel_to_l2[0x1];
        u8         reformat_l2_to_l3_tunnel[0x1];
        u8         reformat_and_modify_action[0x1];
-       u8         reserved_at_14[0xb];
+       u8         reserved_at_15[0xb];
        u8         reserved_at_20[0x2];
        u8         log_max_ft_size[0x6];
        u8         log_max_modify_header_context[0x8];
@@ -421,6 +423,16 @@ struct mlx5_ifc_fte_match_set_lyr_2_4_bits {
        union mlx5_ifc_ipv6_layout_ipv4_layout_auto_bits dst_ipv4_dst_ipv6;
 };
 
+struct mlx5_ifc_nvgre_key_bits {
+       u8 hi[0x18];
+       u8 lo[0x8];
+};
+
+union mlx5_ifc_gre_key_bits {
+       struct mlx5_ifc_nvgre_key_bits nvgre;
+       u8 key[0x20];
+};
+
 struct mlx5_ifc_fte_match_set_misc_bits {
        u8         reserved_at_0[0x8];
        u8         source_sqn[0x18];
@@ -442,8 +454,7 @@ struct mlx5_ifc_fte_match_set_misc_bits {
        u8         reserved_at_64[0xc];
        u8         gre_protocol[0x10];
 
-       u8         gre_key_h[0x18];
-       u8         gre_key_l[0x8];
+       union mlx5_ifc_gre_key_bits gre_key;
 
        u8         vxlan_vni[0x18];
        u8         reserved_at_b8[0x8];
@@ -582,11 +593,13 @@ struct mlx5_ifc_flow_table_nic_cap_bits {
 };
 
 struct mlx5_ifc_flow_table_eswitch_cap_bits {
-       u8      reserved_at_0[0x1c];
-       u8      fdb_multi_path_to_table[0x1];
-       u8      reserved_at_1d[0x1];
+       u8      reserved_at_0[0x1a];
        u8      multi_fdb_encap[0x1];
-       u8      reserved_at_1e[0x1e1];
+       u8      reserved_at_1b[0x1];
+       u8      fdb_multi_path_to_table[0x1];
+       u8      reserved_at_1d[0x3];
+
+       u8      reserved_at_20[0x1e0];
 
        struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_nic_esw_fdb;
 
@@ -597,20 +610,28 @@ struct mlx5_ifc_flow_table_eswitch_cap_bits {
        u8      reserved_at_800[0x7800];
 };
 
+enum {
+       MLX5_COUNTER_SOURCE_ESWITCH = 0x0,
+       MLX5_COUNTER_FLOW_ESWITCH   = 0x1,
+};
+
 struct mlx5_ifc_e_switch_cap_bits {
        u8         vport_svlan_strip[0x1];
        u8         vport_cvlan_strip[0x1];
        u8         vport_svlan_insert[0x1];
        u8         vport_cvlan_insert_if_not_exist[0x1];
        u8         vport_cvlan_insert_overwrite[0x1];
-       u8         reserved_at_5[0x18];
+       u8         reserved_at_5[0x17];
+       u8         counter_eswitch_affinity[0x1];
        u8         merged_eswitch[0x1];
        u8         nic_vport_node_guid_modify[0x1];
        u8         nic_vport_port_guid_modify[0x1];
 
        u8         vxlan_encap_decap[0x1];
        u8         nvgre_encap_decap[0x1];
-       u8         reserved_at_22[0x9];
+       u8         reserved_at_22[0x1];
+       u8         log_max_fdb_encap_uplink[0x5];
+       u8         reserved_at_21[0x3];
        u8         log_max_packet_reformat_context[0x5];
        u8         reserved_2b[0x6];
        u8         max_encap_header_size[0xa];
@@ -829,7 +850,7 @@ struct mlx5_ifc_vector_calc_cap_bits {
        struct mlx5_ifc_calc_op calc2;
        struct mlx5_ifc_calc_op calc3;
 
-       u8         reserved_at_e0[0x720];
+       u8         reserved_at_c0[0x720];
 };
 
 enum {
@@ -883,6 +904,10 @@ enum {
        MLX5_CAP_UMR_FENCE_NONE         = 0x2,
 };
 
+enum {
+       MLX5_UCTX_CAP_RAW_TX = 1UL << 0,
+};
+
 struct mlx5_ifc_cmd_hca_cap_bits {
        u8         reserved_at_0[0x30];
        u8         vhca_id[0x10];
@@ -1043,7 +1068,8 @@ struct mlx5_ifc_cmd_hca_cap_bits {
        u8         vector_calc[0x1];
        u8         umr_ptr_rlky[0x1];
        u8         imaicl[0x1];
-       u8         reserved_at_232[0x4];
+       u8         qp_packet_based[0x1];
+       u8         reserved_at_233[0x3];
        u8         qkv[0x1];
        u8         pkv[0x1];
        u8         set_deth_sqpn[0x1];
@@ -1193,7 +1219,19 @@ struct mlx5_ifc_cmd_hca_cap_bits {
        u8         num_vhca_ports[0x8];
        u8         reserved_at_618[0x6];
        u8         sw_owner_id[0x1];
-       u8         reserved_at_61f[0x1e1];
+       u8         reserved_at_61f[0x1];
+
+       u8         max_num_of_monitor_counters[0x10];
+       u8         num_ppcnt_monitor_counters[0x10];
+
+       u8         reserved_at_640[0x10];
+       u8         num_q_monitor_counters[0x10];
+
+       u8         reserved_at_660[0x40];
+
+       u8         uctx_cap[0x20];
+
+       u8         reserved_at_6c0[0x140];
 };
 
 enum mlx5_flow_destination_type {
@@ -1209,8 +1247,10 @@ enum mlx5_flow_destination_type {
 struct mlx5_ifc_dest_format_struct_bits {
        u8         destination_type[0x8];
        u8         destination_id[0x18];
+
        u8         destination_eswitch_owner_vhca_id_valid[0x1];
-       u8         reserved_at_21[0xf];
+       u8         packet_reformat[0x1];
+       u8         reserved_at_22[0xe];
        u8         destination_eswitch_owner_vhca_id[0x10];
 };
 
@@ -1220,6 +1260,14 @@ struct mlx5_ifc_flow_counter_list_bits {
        u8         reserved_at_20[0x20];
 };
 
+struct mlx5_ifc_extended_dest_format_bits {
+       struct mlx5_ifc_dest_format_struct_bits destination_entry;
+
+       u8         packet_reformat_id[0x20];
+
+       u8         reserved_at_60[0x20];
+};
+
 union mlx5_ifc_dest_format_struct_flow_counter_list_auto_bits {
        struct mlx5_ifc_dest_format_struct_bits dest_format_struct;
        struct mlx5_ifc_flow_counter_list_bits flow_counter_list;
@@ -2249,7 +2297,8 @@ struct mlx5_ifc_qpc_bits {
        u8         st[0x8];
        u8         reserved_at_10[0x3];
        u8         pm_state[0x2];
-       u8         reserved_at_15[0x3];
+       u8         reserved_at_15[0x1];
+       u8         req_e2e_credit_mode[0x2];
        u8         offload_type[0x4];
        u8         end_padding_mode[0x2];
        u8         reserved_at_1e[0x2];
@@ -2440,7 +2489,8 @@ struct mlx5_ifc_flow_context_bits {
        u8         reserved_at_60[0x10];
        u8         action[0x10];
 
-       u8         reserved_at_80[0x8];
+       u8         extended_destination[0x1];
+       u8         reserved_at_80[0x7];
        u8         destination_list_size[0x18];
 
        u8         reserved_at_a0[0x8];
@@ -2473,14 +2523,15 @@ struct mlx5_ifc_xrc_srqc_bits {
 
        u8         wq_signature[0x1];
        u8         cont_srq[0x1];
-       u8         dbr_umem_valid[0x1];
+       u8         reserved_at_22[0x1];
        u8         rlky[0x1];
        u8         basic_cyclic_rcv_wqe[0x1];
        u8         log_rq_stride[0x3];
        u8         xrcd[0x18];
 
        u8         page_offset[0x6];
-       u8         reserved_at_46[0x2];
+       u8         reserved_at_46[0x1];
+       u8         dbr_umem_valid[0x1];
        u8         cqn[0x18];
 
        u8         reserved_at_60[0x20];
@@ -3795,6 +3846,83 @@ enum {
        MLX5_VPORT_STATE_OP_MOD_ESW_VPORT   = 0x1,
 };
 
+struct mlx5_ifc_arm_monitor_counter_in_bits {
+       u8         opcode[0x10];
+       u8         uid[0x10];
+
+       u8         reserved_at_20[0x10];
+       u8         op_mod[0x10];
+
+       u8         reserved_at_40[0x20];
+
+       u8         reserved_at_60[0x20];
+};
+
+struct mlx5_ifc_arm_monitor_counter_out_bits {
+       u8         status[0x8];
+       u8         reserved_at_8[0x18];
+
+       u8         syndrome[0x20];
+
+       u8         reserved_at_40[0x40];
+};
+
+enum {
+       MLX5_QUERY_MONITOR_CNT_TYPE_PPCNT     = 0x0,
+       MLX5_QUERY_MONITOR_CNT_TYPE_Q_COUNTER = 0x1,
+};
+
+enum mlx5_monitor_counter_ppcnt {
+       MLX5_QUERY_MONITOR_PPCNT_IN_RANGE_LENGTH_ERRORS      = 0x0,
+       MLX5_QUERY_MONITOR_PPCNT_OUT_OF_RANGE_LENGTH_FIELD   = 0x1,
+       MLX5_QUERY_MONITOR_PPCNT_FRAME_TOO_LONG_ERRORS       = 0x2,
+       MLX5_QUERY_MONITOR_PPCNT_FRAME_CHECK_SEQUENCE_ERRORS = 0x3,
+       MLX5_QUERY_MONITOR_PPCNT_ALIGNMENT_ERRORS            = 0x4,
+       MLX5_QUERY_MONITOR_PPCNT_IF_OUT_DISCARDS             = 0x5,
+};
+
+enum {
+       MLX5_QUERY_MONITOR_Q_COUNTER_RX_OUT_OF_BUFFER     = 0x4,
+};
+
+struct mlx5_ifc_monitor_counter_output_bits {
+       u8         reserved_at_0[0x4];
+       u8         type[0x4];
+       u8         reserved_at_8[0x8];
+       u8         counter[0x10];
+
+       u8         counter_group_id[0x20];
+};
+
+#define MLX5_CMD_SET_MONITOR_NUM_PPCNT_COUNTER_SET1 (6)
+#define MLX5_CMD_SET_MONITOR_NUM_Q_COUNTERS_SET1    (1)
+#define MLX5_CMD_SET_MONITOR_NUM_COUNTER (MLX5_CMD_SET_MONITOR_NUM_PPCNT_COUNTER_SET1 +\
+                                         MLX5_CMD_SET_MONITOR_NUM_Q_COUNTERS_SET1)
+
+struct mlx5_ifc_set_monitor_counter_in_bits {
+       u8         opcode[0x10];
+       u8         uid[0x10];
+
+       u8         reserved_at_20[0x10];
+       u8         op_mod[0x10];
+
+       u8         reserved_at_40[0x10];
+       u8         num_of_counters[0x10];
+
+       u8         reserved_at_60[0x20];
+
+       struct mlx5_ifc_monitor_counter_output_bits monitor_counter[MLX5_CMD_SET_MONITOR_NUM_COUNTER];
+};
+
+struct mlx5_ifc_set_monitor_counter_out_bits {
+       u8         status[0x8];
+       u8         reserved_at_8[0x18];
+
+       u8         syndrome[0x20];
+
+       u8         reserved_at_40[0x40];
+};
+
 struct mlx5_ifc_query_vport_state_in_bits {
        u8         opcode[0x10];
        u8         reserved_at_10[0x10];
@@ -4660,7 +4788,7 @@ enum {
        MLX5_QUERY_FLOW_GROUP_OUT_MATCH_CRITERIA_ENABLE_OUTER_HEADERS    = 0x0,
        MLX5_QUERY_FLOW_GROUP_OUT_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS  = 0x1,
        MLX5_QUERY_FLOW_GROUP_OUT_MATCH_CRITERIA_ENABLE_INNER_HEADERS    = 0x2,
-       MLX5_QUERY_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS_2 = 0X3,
+       MLX5_QUERY_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS_2 = 0x3,
 };
 
 struct mlx5_ifc_query_flow_group_out_bits {
@@ -5566,7 +5694,7 @@ struct mlx5_ifc_modify_nic_vport_context_out_bits {
 struct mlx5_ifc_modify_nic_vport_field_select_bits {
        u8         reserved_at_0[0x12];
        u8         affiliation[0x1];
-       u8         reserved_at_e[0x1];
+       u8         reserved_at_13[0x1];
        u8         disable_uc_local_lb[0x1];
        u8         disable_mc_local_lb[0x1];
        u8         node_guid[0x1];
@@ -6689,9 +6817,12 @@ struct mlx5_ifc_create_xrc_srq_in_bits {
 
        struct mlx5_ifc_xrc_srqc_bits xrc_srq_context_entry;
 
-       u8         reserved_at_280[0x40];
+       u8         reserved_at_280[0x60];
+
        u8         xrc_srq_umem_valid[0x1];
-       u8         reserved_at_2c1[0x5bf];
+       u8         reserved_at_2e1[0x1f];
+
+       u8         reserved_at_300[0x580];
 
        u8         pas[0][0x40];
 };
@@ -8160,7 +8291,9 @@ struct mlx5_ifc_pcam_regs_5000_to_507f_bits {
        u8         port_access_reg_cap_mask_31_to_13[0x13];
        u8         pbmc[0x1];
        u8         pptb[0x1];
-       u8         port_access_reg_cap_mask_10_to_0[0xb];
+       u8         port_access_reg_cap_mask_10_to_09[0x2];
+       u8         ppcnt[0x1];
+       u8         port_access_reg_cap_mask_07_to_00[0x8];
 };
 
 struct mlx5_ifc_pcam_reg_bits {
@@ -9024,7 +9157,7 @@ struct mlx5_ifc_dcbx_param_bits {
        u8         dcbx_cee_cap[0x1];
        u8         dcbx_ieee_cap[0x1];
        u8         dcbx_standby_cap[0x1];
-       u8         reserved_at_0[0x5];
+       u8         reserved_at_3[0x5];
        u8         port_number[0x8];
        u8         reserved_at_10[0xa];
        u8         max_application_table_size[6];
@@ -9272,7 +9405,9 @@ struct mlx5_ifc_umem_bits {
 struct mlx5_ifc_uctx_bits {
        u8         modify_field_select[0x40];
 
-       u8         reserved_at_40[0x1c0];
+       u8         cap[0x20];
+
+       u8         reserved_at_60[0x1a0];
 };
 
 struct mlx5_ifc_create_umem_in_bits {
index 34aed60..bf4bc01 100644 (file)
@@ -107,9 +107,6 @@ enum mlx5e_connector_type {
 
 #define MLX5E_PROT_MASK(link_mode) (1 << link_mode)
 
-#define PORT_MODULE_EVENT_MODULE_STATUS_MASK 0xF
-#define PORT_MODULE_EVENT_ERROR_TYPE_MASK         0xF
-
 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);
index fbe322c..b26ea90 100644 (file)
@@ -596,6 +596,11 @@ int mlx5_core_dealloc_q_counter(struct mlx5_core_dev *dev, u16 counter_id);
 int mlx5_core_query_q_counter(struct mlx5_core_dev *dev, u16 counter_id,
                              int reset, void *out, int out_size);
 
+struct mlx5_core_rsc_common *mlx5_core_res_hold(struct mlx5_core_dev *dev,
+                                               int res_num,
+                                               enum mlx5_res_type res_type);
+void mlx5_core_res_put(struct mlx5_core_rsc_common *res);
+
 static inline const char *mlx5_qp_type_str(int type)
 {
        switch (type) {
diff --git a/include/linux/mlx5/srq.h b/include/linux/mlx5/srq.h
deleted file mode 100644 (file)
index 1b1f3c2..0000000
+++ /dev/null
@@ -1,72 +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.
- */
-
-#ifndef MLX5_SRQ_H
-#define MLX5_SRQ_H
-
-#include <linux/mlx5/driver.h>
-
-enum {
-       MLX5_SRQ_FLAG_ERR    = (1 << 0),
-       MLX5_SRQ_FLAG_WQ_SIG = (1 << 1),
-       MLX5_SRQ_FLAG_RNDV   = (1 << 2),
-};
-
-struct mlx5_srq_attr {
-       u32 type;
-       u32 flags;
-       u32 log_size;
-       u32 wqe_shift;
-       u32 log_page_size;
-       u32 wqe_cnt;
-       u32 srqn;
-       u32 xrcd;
-       u32 page_offset;
-       u32 cqn;
-       u32 pd;
-       u32 lwm;
-       u32 user_index;
-       u64 db_record;
-       __be64 *pas;
-       u32 tm_log_list_size;
-       u32 tm_next_tag;
-       u32 tm_hw_phase_cnt;
-       u32 tm_sw_phase_cnt;
-       u16 uid;
-};
-
-struct mlx5_core_dev;
-
-void mlx5_init_srq_table(struct mlx5_core_dev *dev);
-void mlx5_cleanup_srq_table(struct mlx5_core_dev *dev);
-
-#endif /* MLX5_SRQ_H */
index 7f5ca2c..a261d55 100644 (file)
@@ -58,17 +58,6 @@ int mlx5_core_create_tis(struct mlx5_core_dev *dev, u32 *in, int inlen,
 int mlx5_core_modify_tis(struct mlx5_core_dev *dev, u32 tisn, u32 *in,
                         int inlen);
 void mlx5_core_destroy_tis(struct mlx5_core_dev *dev, u32 tisn);
-int mlx5_core_create_rmp(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                        u32 *rmpn);
-int mlx5_core_modify_rmp(struct mlx5_core_dev *dev, u32 *in, int inlen);
-int mlx5_core_destroy_rmp(struct mlx5_core_dev *dev, u32 rmpn);
-int mlx5_core_query_rmp(struct mlx5_core_dev *dev, u32 rmpn, u32 *out);
-int mlx5_core_arm_rmp(struct mlx5_core_dev *dev, u32 rmpn, u16 lwm);
-int mlx5_core_create_xsrq(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                         u32 *rmpn);
-int mlx5_core_destroy_xsrq(struct mlx5_core_dev *dev, u32 rmpn);
-int mlx5_core_arm_xsrq(struct mlx5_core_dev *dev, u32 rmpn, u16 lwm);
-
 int mlx5_core_create_rqt(struct mlx5_core_dev *dev, u32 *in, int inlen,
                         u32 *rqtn);
 int mlx5_core_modify_rqt(struct mlx5_core_dev *dev, u32 rqtn, u32 *in,
index 5ed8f62..2c471a2 100644 (file)
@@ -206,6 +206,11 @@ struct page {
 #endif
 } _struct_page_alignment;
 
+/*
+ * Used for sizing the vmemmap region on some architectures
+ */
+#define STRUCT_PAGE_MAX_SHIFT  (order_base_2(sizeof(struct page)))
+
 #define PAGE_FRAG_CACHE_MAX_SIZE       __ALIGN_MASK(32768, ~PAGE_MASK)
 #define PAGE_FRAG_CACHE_MAX_ORDER      get_order(PAGE_FRAG_CACHE_MAX_SIZE)
 
index 4224902..4332199 100644 (file)
@@ -42,6 +42,7 @@
 #define SDIO_DEVICE_ID_BROADCOM_4354           0x4354
 #define SDIO_DEVICE_ID_BROADCOM_4356           0x4356
 #define SDIO_DEVICE_ID_CYPRESS_4373            0x4373
+#define SDIO_DEVICE_ID_CYPRESS_43012           43012
 
 #define SDIO_VENDOR_ID_INTEL                   0x0089
 #define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX     0x1402
index 847705a..db023a9 100644 (file)
@@ -783,6 +783,12 @@ void memory_present(int nid, unsigned long start, unsigned long end);
 static inline void memory_present(int nid, unsigned long start, unsigned long end) {}
 #endif
 
+#if defined(CONFIG_SPARSEMEM)
+void memblocks_present(void);
+#else
+static inline void memblocks_present(void) {}
+#endif
+
 #ifdef CONFIG_HAVE_MEMORYLESS_NODES
 int local_memory_node(int node_id);
 #else
index 01797cb..a0dcc9b 100644 (file)
@@ -565,7 +565,7 @@ struct platform_device_id {
 /**
  * struct mdio_device_id - identifies PHY devices on an MDIO/MII bus
  * @phy_id: The result of
- *     (mdio_read(&MII_PHYSID1) << 16 | mdio_read(&PHYSID2)) & @phy_id_mask
+ *     (mdio_read(&MII_PHYSID1) << 16 | mdio_read(&MII_PHYSID2)) & @phy_id_mask
  *     for this PHY type
  * @phy_id_mask: Defines the significant bits of @phy_id.  A value of 0
  *     is used to terminate an array of struct mdio_device_id.
index fce6b43..5f147dd 100644 (file)
@@ -432,6 +432,10 @@ struct module {
        unsigned int num_tracepoints;
        tracepoint_ptr_t *tracepoints_ptrs;
 #endif
+#ifdef CONFIG_BPF_EVENTS
+       unsigned int num_bpf_raw_events;
+       struct bpf_raw_event_map *bpf_raw_events;
+#endif
 #ifdef HAVE_JUMP_LABEL
        struct jump_entry *jump_entries;
        unsigned int num_jump_entries;
index c79e859..fd45838 100644 (file)
@@ -406,6 +406,8 @@ static inline void net_dim(struct net_dim *dim,
                }
                /* fall through */
        case NET_DIM_START_MEASURE:
+               net_dim_sample(end_sample.event_ctr, end_sample.pkt_ctr, end_sample.byte_ctr,
+                              &dim->start_sample);
                dim->state = NET_DIM_MEASURE_IN_PROGRESS;
                break;
        case NET_DIM_APPLY_NEW_PROFILE:
index 487fa5e..1377d08 100644 (file)
@@ -845,6 +845,8 @@ enum tc_setup_type {
        TC_SETUP_QDISC_PRIO,
        TC_SETUP_QDISC_MQ,
        TC_SETUP_QDISC_ETF,
+       TC_SETUP_ROOT_QDISC,
+       TC_SETUP_QDISC_GRED,
 };
 
 /* These structures hold the attributes of bpf state that are being passed
@@ -863,9 +865,6 @@ enum bpf_netdev_command {
        XDP_QUERY_PROG,
        XDP_QUERY_PROG_HW,
        /* BPF program for offload callbacks, invoked at program load time. */
-       BPF_OFFLOAD_VERIFIER_PREP,
-       BPF_OFFLOAD_TRANSLATE,
-       BPF_OFFLOAD_DESTROY,
        BPF_OFFLOAD_MAP_ALLOC,
        BPF_OFFLOAD_MAP_FREE,
        XDP_QUERY_XSK_UMEM,
@@ -891,15 +890,6 @@ struct netdev_bpf {
                        /* flags with which program was installed */
                        u32 prog_flags;
                };
-               /* BPF_OFFLOAD_VERIFIER_PREP */
-               struct {
-                       struct bpf_prog *prog;
-                       const struct bpf_prog_offload_ops *ops; /* callee set */
-               } verifier;
-               /* BPF_OFFLOAD_TRANSLATE, BPF_OFFLOAD_DESTROY */
-               struct {
-                       struct bpf_prog *prog;
-               } offload;
                /* BPF_OFFLOAD_MAP_ALLOC, BPF_OFFLOAD_MAP_FREE */
                struct {
                        struct bpf_offloaded_map *offmap;
@@ -1175,7 +1165,7 @@ struct dev_ifalias {
  *     entries to skb and update idx with the number of entries.
  *
  * int (*ndo_bridge_setlink)(struct net_device *dev, struct nlmsghdr *nlh,
- *                          u16 flags)
+ *                          u16 flags, struct netlink_ext_ack *extack)
  * int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq,
  *                          struct net_device *dev, u32 filter_mask,
  *                          int nlflags)
@@ -1397,10 +1387,16 @@ struct net_device_ops {
                                                struct net_device *dev,
                                                struct net_device *filter_dev,
                                                int *idx);
-
+       int                     (*ndo_fdb_get)(struct sk_buff *skb,
+                                              struct nlattr *tb[],
+                                              struct net_device *dev,
+                                              const unsigned char *addr,
+                                              u16 vid, u32 portid, u32 seq,
+                                              struct netlink_ext_ack *extack);
        int                     (*ndo_bridge_setlink)(struct net_device *dev,
                                                      struct nlmsghdr *nlh,
-                                                     u16 flags);
+                                                     u16 flags,
+                                                     struct netlink_ext_ack *extack);
        int                     (*ndo_bridge_getlink)(struct sk_buff *skb,
                                                      u32 pid, u32 seq,
                                                      struct net_device *dev,
@@ -2388,13 +2384,13 @@ struct pcpu_sw_netstats {
        u64     tx_packets;
        u64     tx_bytes;
        struct u64_stats_sync   syncp;
-};
+} __aligned(4 * sizeof(u64));
 
 struct pcpu_lstats {
        u64 packets;
        u64 bytes;
        struct u64_stats_sync syncp;
-};
+} __aligned(2 * sizeof(u64));
 
 #define __netdev_alloc_pcpu_stats(type, gfp)                           \
 ({                                                                     \
@@ -2459,7 +2455,8 @@ enum netdev_cmd {
        NETDEV_REGISTER,
        NETDEV_UNREGISTER,
        NETDEV_CHANGEMTU,       /* notify after mtu change happened */
-       NETDEV_CHANGEADDR,
+       NETDEV_CHANGEADDR,      /* notify after the address change */
+       NETDEV_PRE_CHANGEADDR,  /* notify before the address change */
        NETDEV_GOING_DOWN,
        NETDEV_CHANGENAME,
        NETDEV_FEAT_CHANGE,
@@ -2521,6 +2518,11 @@ struct netdev_notifier_changelowerstate_info {
        void *lower_state_info; /* is lower dev state */
 };
 
+struct netdev_notifier_pre_changeaddr_info {
+       struct netdev_notifier_info info; /* must be first */
+       const unsigned char *dev_addr;
+};
+
 static inline void netdev_notifier_info_init(struct netdev_notifier_info *info,
                                             struct net_device *dev)
 {
@@ -2615,7 +2617,7 @@ struct net_device *dev_get_by_name(struct net *net, const char *name);
 struct net_device *dev_get_by_name_rcu(struct net *net, const char *name);
 struct net_device *__dev_get_by_name(struct net *net, const char *name);
 int dev_alloc_name(struct net_device *dev, const char *name);
-int dev_open(struct net_device *dev);
+int dev_open(struct net_device *dev, struct netlink_ext_ack *extack);
 void dev_close(struct net_device *dev);
 void dev_close_many(struct list_head *head, bool unlink);
 void dev_disable_lro(struct net_device *dev);
@@ -3224,6 +3226,14 @@ static inline void netdev_sent_queue(struct net_device *dev, unsigned int bytes)
        netdev_tx_sent_queue(netdev_get_tx_queue(dev, 0), bytes);
 }
 
+static inline bool __netdev_sent_queue(struct net_device *dev,
+                                      unsigned int bytes,
+                                      bool xmit_more)
+{
+       return __netdev_tx_sent_queue(netdev_get_tx_queue(dev, 0), bytes,
+                                     xmit_more);
+}
+
 static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue,
                                             unsigned int pkts, unsigned int bytes)
 {
@@ -3613,8 +3623,10 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
 int dev_ifconf(struct net *net, struct ifconf *, int);
 int dev_ethtool(struct net *net, struct ifreq *);
 unsigned int dev_get_flags(const struct net_device *);
-int __dev_change_flags(struct net_device *, unsigned int flags);
-int dev_change_flags(struct net_device *, unsigned int);
+int __dev_change_flags(struct net_device *dev, unsigned int flags,
+                      struct netlink_ext_ack *extack);
+int dev_change_flags(struct net_device *dev, unsigned int flags,
+                    struct netlink_ext_ack *extack);
 void __dev_notify_flags(struct net_device *, unsigned int old_flags,
                        unsigned int gchanges);
 int dev_change_name(struct net_device *, const char *);
@@ -3627,7 +3639,10 @@ int dev_set_mtu_ext(struct net_device *dev, int mtu,
 int dev_set_mtu(struct net_device *, int);
 int dev_change_tx_queue_len(struct net_device *, unsigned long);
 void dev_set_group(struct net_device *, int);
-int dev_set_mac_address(struct net_device *, struct sockaddr *);
+int dev_pre_changeaddr_notify(struct net_device *dev, const char *addr,
+                             struct netlink_ext_ack *extack);
+int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa,
+                       struct netlink_ext_ack *extack);
 int dev_change_carrier(struct net_device *, bool new_carrier);
 int dev_get_phys_port_id(struct net_device *dev,
                         struct netdev_phys_item_id *ppid);
@@ -4342,9 +4357,10 @@ static inline bool can_checksum_protocol(netdev_features_t features,
 }
 
 #ifdef CONFIG_BUG
-void netdev_rx_csum_fault(struct net_device *dev);
+void netdev_rx_csum_fault(struct net_device *dev, struct sk_buff *skb);
 #else
-static inline void netdev_rx_csum_fault(struct net_device *dev)
+static inline void netdev_rx_csum_fault(struct net_device *dev,
+                                       struct sk_buff *skb)
 {
 }
 #endif
@@ -4370,7 +4386,7 @@ static inline netdev_tx_t netdev_start_xmit(struct sk_buff *skb, struct net_devi
                                            struct netdev_queue *txq, bool more)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
-       int rc;
+       netdev_tx_t rc;
 
        rc = __netdev_start_xmit(ops, skb, dev, more);
        if (rc == NETDEV_TX_OK)
index f76efa5..6989e2e 100644 (file)
@@ -21,6 +21,19 @@ struct nf_ct_gre_keymap {
        struct nf_conntrack_tuple tuple;
 };
 
+enum grep_conntrack {
+       GRE_CT_UNREPLIED,
+       GRE_CT_REPLIED,
+       GRE_CT_MAX
+};
+
+struct netns_proto_gre {
+       struct nf_proto_net     nf;
+       rwlock_t                keymap_lock;
+       struct list_head        keymap_list;
+       unsigned int            gre_timeouts[GRE_CT_MAX];
+};
+
 /* add new tuple->key_reply pair to keymap */
 int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
                         struct nf_conntrack_tuple *t);
index 4a520d3..cf09ab3 100644 (file)
@@ -62,18 +62,6 @@ static inline bool lockdep_nfnl_is_held(__u8 subsys_id)
 }
 #endif /* CONFIG_PROVE_LOCKING */
 
-/*
- * nfnl_dereference - fetch RCU pointer when updates are prevented by subsys mutex
- *
- * @p: The pointer to read, prior to dereferencing
- * @ss: The nfnetlink subsystem ID
- *
- * Return the value of the specified RCU-protected pointer, but omit
- * the READ_ONCE(), because caller holds the NFNL subsystem mutex.
- */
-#define nfnl_dereference(p, ss)                                        \
-       rcu_dereference_protected(p, lockdep_nfnl_is_held(ss))
-
 #define MODULE_ALIAS_NFNL_SUBSYS(subsys) \
        MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
 
index fa06865..5f2614d 100644 (file)
@@ -17,43 +17,58 @@ static inline void br_drop_fake_rtable(struct sk_buff *skb)
                skb_dst_drop(skb);
 }
 
+static inline struct nf_bridge_info *
+nf_bridge_info_get(const struct sk_buff *skb)
+{
+       return skb_ext_find(skb, SKB_EXT_BRIDGE_NF);
+}
+
+static inline bool nf_bridge_info_exists(const struct sk_buff *skb)
+{
+       return skb_ext_exist(skb, SKB_EXT_BRIDGE_NF);
+}
+
 static inline int nf_bridge_get_physinif(const struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge;
+       const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
 
-       if (skb->nf_bridge == NULL)
+       if (!nf_bridge)
                return 0;
 
-       nf_bridge = skb->nf_bridge;
        return nf_bridge->physindev ? nf_bridge->physindev->ifindex : 0;
 }
 
 static inline int nf_bridge_get_physoutif(const struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge;
+       const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
 
-       if (skb->nf_bridge == NULL)
+       if (!nf_bridge)
                return 0;
 
-       nf_bridge = skb->nf_bridge;
        return nf_bridge->physoutdev ? nf_bridge->physoutdev->ifindex : 0;
 }
 
 static inline struct net_device *
 nf_bridge_get_physindev(const struct sk_buff *skb)
 {
-       return skb->nf_bridge ? skb->nf_bridge->physindev : NULL;
+       const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
+
+       return nf_bridge ? nf_bridge->physindev : NULL;
 }
 
 static inline struct net_device *
 nf_bridge_get_physoutdev(const struct sk_buff *skb)
 {
-       return skb->nf_bridge ? skb->nf_bridge->physoutdev : NULL;
+       const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
+
+       return nf_bridge ? nf_bridge->physoutdev : NULL;
 }
 
 static inline bool nf_bridge_in_prerouting(const struct sk_buff *skb)
 {
-       return skb->nf_bridge && skb->nf_bridge->in_prerouting;
+       const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
+
+       return nf_bridge && nf_bridge->in_prerouting;
 }
 #else
 #define br_drop_fake_rtable(skb)               do { } while (0)
index 4da90a6..4e8add2 100644 (file)
@@ -34,8 +34,8 @@ struct netlink_skb_parms {
 #define NETLINK_CREDS(skb)     (&NETLINK_CB((skb)).creds)
 
 
-extern void netlink_table_grab(void);
-extern void netlink_table_ungrab(void);
+void netlink_table_grab(void);
+void netlink_table_ungrab(void);
 
 #define NL_CFG_F_NONROOT_RECV  (1 << 0)
 #define NL_CFG_F_NONROOT_SEND  (1 << 1)
@@ -51,7 +51,7 @@ struct netlink_kernel_cfg {
        bool            (*compare)(struct net *net, struct sock *sk);
 };
 
-extern struct sock *__netlink_kernel_create(struct net *net, int unit,
+struct sock *__netlink_kernel_create(struct net *net, int unit,
                                            struct module *module,
                                            struct netlink_kernel_cfg *cfg);
 static inline struct sock *
@@ -110,24 +110,33 @@ struct netlink_ext_ack {
        }                                               \
 } while (0)
 
-extern void netlink_kernel_release(struct sock *sk);
-extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups);
-extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
-extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group);
-extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
-                       const struct netlink_ext_ack *extack);
-extern int netlink_has_listeners(struct sock *sk, unsigned int group);
-
-extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
-extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,
-                            __u32 group, gfp_t allocation);
-extern int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb,
-       __u32 portid, __u32 group, gfp_t allocation,
-       int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data),
-       void *filter_data);
-extern int netlink_set_err(struct sock *ssk, __u32 portid, __u32 group, int code);
-extern int netlink_register_notifier(struct notifier_block *nb);
-extern int netlink_unregister_notifier(struct notifier_block *nb);
+static inline void nl_set_extack_cookie_u64(struct netlink_ext_ack *extack,
+                                           u64 cookie)
+{
+       u64 __cookie = cookie;
+
+       memcpy(extack->cookie, &__cookie, sizeof(__cookie));
+       extack->cookie_len = sizeof(__cookie);
+}
+
+void netlink_kernel_release(struct sock *sk);
+int __netlink_change_ngroups(struct sock *sk, unsigned int groups);
+int netlink_change_ngroups(struct sock *sk, unsigned int groups);
+void __netlink_clear_multicast_users(struct sock *sk, unsigned int group);
+void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
+                const struct netlink_ext_ack *extack);
+int netlink_has_listeners(struct sock *sk, unsigned int group);
+
+int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
+int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,
+                     __u32 group, gfp_t allocation);
+int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb,
+                              __u32 portid, __u32 group, gfp_t allocation,
+                              int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data),
+                              void *filter_data);
+int netlink_set_err(struct sock *ssk, __u32 portid, __u32 group, int code);
+int netlink_register_notifier(struct notifier_block *nb);
+int netlink_unregister_notifier(struct notifier_block *nb);
 
 /* finegrained unicast helpers: */
 struct sock *netlink_getsockbyfilp(struct file *filp);
@@ -203,7 +212,7 @@ struct netlink_dump_control {
        u16 min_dump_alloc;
 };
 
-extern int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
+int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
                                const struct nlmsghdr *nlh,
                                struct netlink_dump_control *control);
 static inline int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
@@ -222,8 +231,8 @@ struct netlink_tap {
        struct list_head list;
 };
 
-extern int netlink_add_tap(struct netlink_tap *nt);
-extern int netlink_remove_tap(struct netlink_tap *nt);
+int netlink_add_tap(struct netlink_tap *nt);
+int netlink_remove_tap(struct netlink_tap *nt);
 
 bool __netlink_ns_capable(const struct netlink_skb_parms *nsp,
                          struct user_namespace *ns, int cap);
diff --git a/include/linux/objagg.h b/include/linux/objagg.h
new file mode 100644 (file)
index 0000000..34f38c1
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#ifndef _OBJAGG_H
+#define _OBJAGG_H
+
+struct objagg_ops {
+       size_t obj_size;
+       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_destroy)(void *priv, void *root_priv);
+};
+
+struct objagg;
+struct objagg_obj;
+
+const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj);
+const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj);
+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);
+void objagg_destroy(struct objagg *objagg);
+
+struct objagg_obj_stats {
+       unsigned int user_count;
+       unsigned int delta_user_count; /* includes delta object users */
+};
+
+struct objagg_obj_stats_info {
+       struct objagg_obj_stats stats;
+       struct objagg_obj *objagg_obj; /* associated object */
+       bool is_root;
+};
+
+struct objagg_stats {
+       unsigned int stats_info_count;
+       struct objagg_obj_stats_info stats_info[];
+};
+
+const struct objagg_stats *objagg_stats_get(struct objagg *objagg);
+void objagg_stats_put(const struct objagg_stats *objagg_stats);
+
+#endif
index 90d81ee..9cd72aa 100644 (file)
@@ -13,7 +13,6 @@
 struct net_device;
 extern int of_get_phy_mode(struct device_node *np);
 extern const void *of_get_mac_address(struct device_node *np);
-extern int of_get_nvmem_mac_address(struct device_node *np, void *addr);
 extern struct net_device *of_find_net_device_by_node(struct device_node *np);
 #else
 static inline int of_get_phy_mode(struct device_node *np)
@@ -26,11 +25,6 @@ static inline const void *of_get_mac_address(struct device_node *np)
        return NULL;
 }
 
-static inline int of_get_nvmem_mac_address(struct device_node *np, void *addr)
-{
-       return -ENODEV;
-}
-
 static inline struct net_device *of_find_net_device_by_node(struct device_node *np)
 {
        return NULL;
index 8f92724..da039f2 100644 (file)
@@ -319,12 +319,12 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
 enum phy_state {
        PHY_DOWN = 0,
        PHY_READY,
+       PHY_HALTED,
        PHY_UP,
        PHY_RUNNING,
        PHY_NOLINK,
        PHY_FORCING,
        PHY_CHANGELINK,
-       PHY_HALTED,
        PHY_RESUMING
 };
 
@@ -669,6 +669,28 @@ phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
 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;
+}
+
+/**
+ * phy_is_started - Convenience function to check whether PHY is started
+ * @phydev: The phy_device struct
+ */
+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;
+}
+
 void phy_resolve_aneg_linkmode(struct phy_device *phydev);
 
 /**
index ee54453..9525567 100644 (file)
@@ -13,6 +13,7 @@ struct fixed_phy_status {
 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);
@@ -47,6 +48,10 @@ static inline int fixed_phy_set_link_update(struct phy_device *phydev,
 {
        return -ENODEV;
 }
+static inline int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier)
+{
+       return -EINVAL;
+}
 #endif /* CONFIG_FIXED_PHY */
 
 #endif /* __PHY_FIXED_H */
index f92a47e..a93841b 100644 (file)
@@ -17,6 +17,8 @@
 #define __DAVINCI_GPIO_PLATFORM_H
 
 struct davinci_gpio_platform_data {
+       bool    no_auto_base;
+       u32     base;
        u32     ngpio;
        u32     gpio_unbanked;
 };
diff --git a/include/linux/platform_data/mdio-gpio.h b/include/linux/platform_data/mdio-gpio.h
new file mode 100644 (file)
index 0000000..13874fa
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MDIO-GPIO bus platform data structure
+ */
+
+#ifndef __LINUX_MDIO_GPIO_PDATA_H
+#define __LINUX_MDIO_GPIO_PDATA_H
+
+struct mdio_gpio_platform_data {
+       u32 phy_mask;
+       u32 phy_ignore_ta_mask;
+};
+
+#endif /* __LINUX_MDIO_GPIO_PDATA_H */
index 8e0725a..7006008 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _LINUX_PSI_H
 #define _LINUX_PSI_H
 
+#include <linux/jump_label.h>
 #include <linux/psi_types.h>
 #include <linux/sched.h>
 
@@ -9,7 +10,7 @@ struct css_set;
 
 #ifdef CONFIG_PSI
 
-extern bool psi_disabled;
+extern struct static_key_false psi_disabled;
 
 void psi_init(void);
 
index a15bc4d..30fcec3 100644 (file)
@@ -90,7 +90,10 @@ struct pstore_record {
  *
  * @buf_lock:  spinlock to serialize access to @buf
  * @buf:       preallocated crash dump buffer
- * @bufsize:   size of @buf available for crash dump writes
+ * @bufsize:   size of @buf available for crash dump bytes (must match
+ *             smallest number of bytes available for writing to a
+ *             backend entry, since compressed bytes don't take kindly
+ *             to being truncated)
  *
  * @read_mutex:        serializes @open, @read, @close, and @erase callbacks
  * @flags:     bitfield of frontends the backend can accept writes for
index 6c2ffed..de20ede 100644 (file)
@@ -64,15 +64,12 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead);
 #define PTRACE_MODE_NOAUDIT    0x04
 #define PTRACE_MODE_FSCREDS    0x08
 #define PTRACE_MODE_REALCREDS  0x10
-#define PTRACE_MODE_SCHED      0x20
-#define PTRACE_MODE_IBPB       0x40
 
 /* shorthands for READ/ATTACH and FSCREDS/REALCREDS combinations */
 #define PTRACE_MODE_READ_FSCREDS (PTRACE_MODE_READ | PTRACE_MODE_FSCREDS)
 #define PTRACE_MODE_READ_REALCREDS (PTRACE_MODE_READ | PTRACE_MODE_REALCREDS)
 #define PTRACE_MODE_ATTACH_FSCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS)
 #define PTRACE_MODE_ATTACH_REALCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_REALCREDS)
-#define PTRACE_MODE_SPEC_IBPB (PTRACE_MODE_ATTACH_REALCREDS | PTRACE_MODE_IBPB)
 
 /**
  * ptrace_may_access - check whether the caller is permitted to access
@@ -90,20 +87,6 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead);
  */
 extern bool ptrace_may_access(struct task_struct *task, unsigned int mode);
 
-/**
- * ptrace_may_access - check whether the caller is permitted to access
- * a target task.
- * @task: target task
- * @mode: selects type of access and caller credentials
- *
- * Returns true on success, false on denial.
- *
- * Similar to ptrace_may_access(). Only to be called from context switch
- * code. Does not call into audit and the regular LSM hooks due to locking
- * constraints.
- */
-extern bool ptrace_may_access_sched(struct task_struct *task, unsigned int mode);
-
 static inline int ptrace_reparented(struct task_struct *child)
 {
        return !same_thread_group(child->real_parent, child->parent);
index a47321a..91c536a 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/slab.h>
 #include <linux/qed/common_hsi.h>
 #include <linux/qed/qed_chain.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
 
 enum dcbx_protocol_type {
        DCBX_PROTOCOL_ISCSI,
@@ -448,11 +449,24 @@ struct qed_mfw_tlv_iscsi {
        bool tx_bytes_set;
 };
 
+enum qed_db_rec_width {
+       DB_REC_WIDTH_32B,
+       DB_REC_WIDTH_64B,
+};
+
+enum qed_db_rec_space {
+       DB_REC_KERNEL,
+       DB_REC_USER,
+};
+
 #define DIRECT_REG_WR(reg_addr, val) writel((u32)val, \
                                            (void __iomem *)(reg_addr))
 
 #define DIRECT_REG_RD(reg_addr) readl((void __iomem *)(reg_addr))
 
+#define DIRECT_REG_WR64(reg_addr, val) writeq((u32)val,        \
+                                             (void __iomem *)(reg_addr))
+
 #define QED_COALESCE_MAX 0x1FF
 #define QED_DEFAULT_RX_USECS 12
 #define QED_DEFAULT_TX_USECS 48
@@ -1015,6 +1029,33 @@ struct qed_common_ops {
  */
        int (*set_led)(struct qed_dev *cdev,
                       enum qed_led_mode mode);
+/**
+ * @brief db_recovery_add - add doorbell information to the doorbell
+ * recovery mechanism.
+ *
+ * @param cdev
+ * @param db_addr - doorbell address
+ * @param db_data - address of where db_data is stored
+ * @param db_is_32b - doorbell is 32b pr 64b
+ * @param db_is_user - doorbell recovery addresses are user or kernel space
+ */
+       int (*db_recovery_add)(struct qed_dev *cdev,
+                              void __iomem *db_addr,
+                              void *db_data,
+                              enum qed_db_rec_width db_width,
+                              enum qed_db_rec_space db_space);
+
+/**
+ * @brief db_recovery_del - remove doorbell information from the doorbell
+ * recovery mechanism. db_data serves as key (db_addr is not unique).
+ *
+ * @param cdev
+ * @param db_addr - doorbell address
+ * @param db_data - address where db_data is stored. Serves as key for the
+ *                 entry to delete.
+ */
+       int (*db_recovery_del)(struct qed_dev *cdev,
+                              void __iomem *db_addr, void *db_data);
 
 /**
  * @brief update_drv_state - API to inform the change in the driver state.
index eb71110..20f9c6a 100644 (file)
@@ -75,8 +75,19 @@ struct bucket_table {
        struct rhash_head __rcu *buckets[] ____cacheline_aligned_in_smp;
 };
 
+/*
+ * NULLS_MARKER() expects a hash value with the low
+ * bits mostly likely to be significant, and it discards
+ * the msb.
+ * We git it an address, in which the bottom 2 bits are
+ * always 0, and the msb might be significant.
+ * So we shift the address down one bit to align with
+ * expectations and avoid losing a significant bit.
+ */
+#define        RHT_NULLS_MARKER(ptr)   \
+       ((void *)NULLS_MARKER(((unsigned long) (ptr)) >> 1))
 #define INIT_RHT_NULLS_HEAD(ptr)       \
-       ((ptr) = (typeof(ptr)) NULLS_MARKER(0))
+       ((ptr) = RHT_NULLS_MARKER(&(ptr)))
 
 static inline bool rht_is_a_nulls(const struct rhash_head *ptr)
 {
@@ -471,6 +482,7 @@ static inline struct rhash_head *__rhashtable_lookup(
                .ht = ht,
                .key = key,
        };
+       struct rhash_head __rcu * const *head;
        struct bucket_table *tbl;
        struct rhash_head *he;
        unsigned int hash;
@@ -478,13 +490,19 @@ static inline struct rhash_head *__rhashtable_lookup(
        tbl = rht_dereference_rcu(ht->tbl, ht);
 restart:
        hash = rht_key_hashfn(ht, tbl, key, params);
-       rht_for_each_rcu(he, tbl, hash) {
-               if (params.obj_cmpfn ?
-                   params.obj_cmpfn(&arg, rht_obj(ht, he)) :
-                   rhashtable_compare(&arg, rht_obj(ht, he)))
-                       continue;
-               return he;
-       }
+       head = rht_bucket(tbl, hash);
+       do {
+               rht_for_each_rcu_continue(he, *head, tbl, hash) {
+                       if (params.obj_cmpfn ?
+                           params.obj_cmpfn(&arg, rht_obj(ht, he)) :
+                           rhashtable_compare(&arg, rht_obj(ht, he)))
+                               continue;
+                       return he;
+               }
+               /* An object might have been moved to a different hash chain,
+                * while we walk along it - better check and retry.
+                */
+       } while (he != RHT_NULLS_MARKER(head));
 
        /* Ensure we see any new tables. */
        smp_rmb();
index a51c13c..291a9bd 100644 (file)
@@ -1116,6 +1116,7 @@ struct task_struct {
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
        /* Index of current stored address in ret_stack: */
        int                             curr_ret_stack;
+       int                             curr_ret_depth;
 
        /* Stack of return addresses for return function tracing: */
        struct ftrace_ret_stack         *ret_stack;
@@ -1453,6 +1454,8 @@ static inline bool is_percpu_thread(void)
 #define PFA_SPREAD_SLAB                        2       /* Spread some slab caches over cpuset */
 #define PFA_SPEC_SSB_DISABLE           3       /* Speculative Store Bypass disabled */
 #define PFA_SPEC_SSB_FORCE_DISABLE     4       /* Speculative Store Bypass force disabled*/
+#define PFA_SPEC_IB_DISABLE            5       /* Indirect branch speculation restricted */
+#define PFA_SPEC_IB_FORCE_DISABLE      6       /* Indirect branch speculation permanently restricted */
 
 #define TASK_PFA_TEST(name, func)                                      \
        static inline bool task_##func(struct task_struct *p)           \
@@ -1484,6 +1487,13 @@ TASK_PFA_CLEAR(SPEC_SSB_DISABLE, spec_ssb_disable)
 TASK_PFA_TEST(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable)
 TASK_PFA_SET(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable)
 
+TASK_PFA_TEST(SPEC_IB_DISABLE, spec_ib_disable)
+TASK_PFA_SET(SPEC_IB_DISABLE, spec_ib_disable)
+TASK_PFA_CLEAR(SPEC_IB_DISABLE, spec_ib_disable)
+
+TASK_PFA_TEST(SPEC_IB_FORCE_DISABLE, spec_ib_force_disable)
+TASK_PFA_SET(SPEC_IB_FORCE_DISABLE, spec_ib_force_disable)
+
 static inline void
 current_restore_flags(unsigned long orig_flags, unsigned long flags)
 {
diff --git a/include/linux/sched/smt.h b/include/linux/sched/smt.h
new file mode 100644 (file)
index 0000000..59d3736
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_SCHED_SMT_H
+#define _LINUX_SCHED_SMT_H
+
+#include <linux/static_key.h>
+
+#ifdef CONFIG_SCHED_SMT
+extern struct static_key_false sched_smt_present;
+
+static __always_inline bool sched_smt_active(void)
+{
+       return static_branch_likely(&sched_smt_present);
+}
+#else
+static inline bool sched_smt_active(void) { return false; }
+#endif
+
+void arch_smt_update(void);
+
+#endif
index d37518e..d9d9de3 100644 (file)
@@ -224,7 +224,7 @@ struct sfp_eeprom_ext {
  *
  * See the SFF-8472 specification and related documents for the definition
  * of these structure members. This can be obtained from
- * ftp://ftp.seagate.com/sff
+ * https://www.snia.org/technology-communities/sff/specifications
  */
 struct sfp_eeprom_id {
        struct sfp_eeprom_base base;
index 7dcfb55..3f741b0 100644 (file)
@@ -245,6 +245,7 @@ struct iov_iter;
 struct napi_struct;
 struct bpf_prog;
 union bpf_attr;
+struct skb_ext;
 
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 struct nf_conntrack {
@@ -254,7 +255,6 @@ struct nf_conntrack {
 
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 struct nf_bridge_info {
-       refcount_t              use;
        enum {
                BRNF_PROTO_UNCHANGED,
                BRNF_PROTO_8021Q,
@@ -481,10 +481,11 @@ static inline void sock_zerocopy_get(struct ubuf_info *uarg)
 }
 
 void sock_zerocopy_put(struct ubuf_info *uarg);
-void sock_zerocopy_put_abort(struct ubuf_info *uarg);
+void sock_zerocopy_put_abort(struct ubuf_info *uarg, bool have_uref);
 
 void sock_zerocopy_callback(struct ubuf_info *uarg, bool success);
 
+int skb_zerocopy_iter_dgram(struct sk_buff *skb, struct msghdr *msg, int len);
 int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,
                             struct msghdr *msg, int len,
                             struct ubuf_info *uarg);
@@ -615,6 +616,8 @@ typedef unsigned char *sk_buff_data_t;
  *     @pkt_type: Packet class
  *     @fclone: skbuff clone status
  *     @ipvs_property: skbuff is owned by ipvs
+ *     @offload_fwd_mark: Packet was L2-forwarded in hardware
+ *     @offload_l3_fwd_mark: Packet was L3-forwarded in hardware
  *     @tc_skip_classify: do not classify packet. set by IFB device
  *     @tc_at_ingress: used within tc_classify to distinguish in/egress
  *     @tc_redirected: packet was redirected by a tc action
@@ -633,6 +636,7 @@ typedef unsigned char *sk_buff_data_t;
  *     @queue_mapping: Queue mapping for multiqueue devices
  *     @xmit_more: More SKBs are pending for this queue
  *     @pfmemalloc: skbuff was allocated from PFMEMALLOC reserves
+ *     @active_extensions: active extensions (skb_ext_id types)
  *     @ndisc_nodetype: router type (from link layer)
  *     @ooo_okay: allow the mapping of a socket to a queue to be changed
  *     @l4_hash: indicate hash is a canonical 4-tuple hash over transport
@@ -662,6 +666,7 @@ typedef unsigned char *sk_buff_data_t;
  *     @data: Data head pointer
  *     @truesize: Buffer size
  *     @users: User count - see {datagram,tcp}.c
+ *     @extensions: allocated extensions, valid if active_extensions is nonzero
  */
 
 struct sk_buff {
@@ -709,14 +714,8 @@ struct sk_buff {
                struct list_head        tcp_tsorted_anchor;
        };
 
-#ifdef CONFIG_XFRM
-       struct  sec_path        *sp;
-#endif
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
        unsigned long            _nfct;
-#endif
-#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-       struct nf_bridge_info   *nf_bridge;
 #endif
        unsigned int            len,
                                data_len;
@@ -744,7 +743,9 @@ struct sk_buff {
                                head_frag:1,
                                xmit_more:1,
                                pfmemalloc:1;
-
+#ifdef CONFIG_SKB_EXTENSIONS
+       __u8                    active_extensions;
+#endif
        /* fields enclosed in headers_start/headers_end are copied
         * using a single memcpy() in __copy_skb_header()
         */
@@ -777,6 +778,14 @@ struct sk_buff {
        __u8                    encap_hdr_csum:1;
        __u8                    csum_valid:1;
 
+#ifdef __BIG_ENDIAN_BITFIELD
+#define PKT_VLAN_PRESENT_BIT   7
+#else
+#define PKT_VLAN_PRESENT_BIT   0
+#endif
+#define PKT_VLAN_PRESENT_OFFSET()      offsetof(struct sk_buff, __pkt_vlan_present_offset)
+       __u8                    __pkt_vlan_present_offset[0];
+       __u8                    vlan_present:1;
        __u8                    csum_complete_sw:1;
        __u8                    csum_level:2;
        __u8                    csum_not_inet:1;
@@ -784,13 +793,13 @@ struct sk_buff {
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
        __u8                    ndisc_nodetype:2;
 #endif
-       __u8                    ipvs_property:1;
 
+       __u8                    ipvs_property:1;
        __u8                    inner_protocol_type:1;
        __u8                    remcsum_offload:1;
 #ifdef CONFIG_NET_SWITCHDEV
        __u8                    offload_fwd_mark:1;
-       __u8                    offload_mr_fwd_mark:1;
+       __u8                    offload_l3_fwd_mark:1;
 #endif
 #ifdef CONFIG_NET_CLS_ACT
        __u8                    tc_skip_classify:1;
@@ -858,6 +867,11 @@ struct sk_buff {
                                *data;
        unsigned int            truesize;
        refcount_t              users;
+
+#ifdef CONFIG_SKB_EXTENSIONS
+       /* only useable after checking ->active_extensions != 0 */
+       struct skb_ext          *extensions;
+#endif
 };
 
 #ifdef __KERNEL__
@@ -1317,15 +1331,35 @@ static inline struct ubuf_info *skb_zcopy(struct sk_buff *skb)
        return is_zcopy ? skb_uarg(skb) : NULL;
 }
 
-static inline void skb_zcopy_set(struct sk_buff *skb, struct ubuf_info *uarg)
+static inline void skb_zcopy_set(struct sk_buff *skb, struct ubuf_info *uarg,
+                                bool *have_ref)
 {
        if (skb && uarg && !skb_zcopy(skb)) {
-               sock_zerocopy_get(uarg);
+               if (unlikely(have_ref && *have_ref))
+                       *have_ref = false;
+               else
+                       sock_zerocopy_get(uarg);
                skb_shinfo(skb)->destructor_arg = uarg;
                skb_shinfo(skb)->tx_flags |= SKBTX_ZEROCOPY_FRAG;
        }
 }
 
+static inline void skb_zcopy_set_nouarg(struct sk_buff *skb, void *val)
+{
+       skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t) val | 0x1UL);
+       skb_shinfo(skb)->tx_flags |= SKBTX_ZEROCOPY_FRAG;
+}
+
+static inline bool skb_zcopy_is_nouarg(struct sk_buff *skb)
+{
+       return (uintptr_t) skb_shinfo(skb)->destructor_arg & 0x1UL;
+}
+
+static inline void *skb_zcopy_get_nouarg(struct sk_buff *skb)
+{
+       return (void *)((uintptr_t) skb_shinfo(skb)->destructor_arg & ~0x1UL);
+}
+
 /* Release a reference on a zerocopy structure */
 static inline void skb_zcopy_clear(struct sk_buff *skb, bool zerocopy)
 {
@@ -1335,7 +1369,7 @@ static inline void skb_zcopy_clear(struct sk_buff *skb, bool zerocopy)
                if (uarg->callback == sock_zerocopy_callback) {
                        uarg->zerocopy = uarg->zerocopy && zerocopy;
                        sock_zerocopy_put(uarg);
-               } else {
+               } else if (!skb_zcopy_is_nouarg(skb)) {
                        uarg->callback(uarg, zerocopy);
                }
 
@@ -1349,7 +1383,7 @@ static inline void skb_zcopy_abort(struct sk_buff *skb)
        struct ubuf_info *uarg = skb_zcopy(skb);
 
        if (uarg) {
-               sock_zerocopy_put_abort(uarg);
+               sock_zerocopy_put_abort(uarg, false);
                skb_shinfo(skb)->tx_flags &= ~SKBTX_ZEROCOPY_FRAG;
        }
 }
@@ -1725,8 +1759,6 @@ static inline void skb_queue_head_init_class(struct sk_buff_head *list,
  *     The "__skb_xxxx()" functions are the non-atomic ones that
  *     can only be called with interrupts disabled.
  */
-void skb_insert(struct sk_buff *old, struct sk_buff *newsk,
-               struct sk_buff_head *list);
 static inline void __skb_insert(struct sk_buff *newsk,
                                struct sk_buff *prev, struct sk_buff *next,
                                struct sk_buff_head *list)
@@ -3327,7 +3359,6 @@ int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
                    unsigned int flags);
 int skb_send_sock_locked(struct sock *sk, struct sk_buff *skb, int offset,
                         int len);
-int skb_send_sock(struct sock *sk, struct sk_buff *skb, int offset, int len);
 void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
 unsigned int skb_zerocopy_headlen(const struct sk_buff *from);
 int skb_zerocopy(struct sk_buff *to, struct sk_buff *from,
@@ -3868,18 +3899,108 @@ static inline void nf_conntrack_get(struct nf_conntrack *nfct)
                atomic_inc(&nfct->use);
 }
 #endif
+
+#ifdef CONFIG_SKB_EXTENSIONS
+enum skb_ext_id {
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
+       SKB_EXT_BRIDGE_NF,
+#endif
+#ifdef CONFIG_XFRM
+       SKB_EXT_SEC_PATH,
+#endif
+       SKB_EXT_NUM, /* must be last */
+};
+
+/**
+ *     struct skb_ext - sk_buff extensions
+ *     @refcnt: 1 on allocation, deallocated on 0
+ *     @offset: offset to add to @data to obtain extension address
+ *     @chunks: size currently allocated, stored in SKB_EXT_ALIGN_SHIFT units
+ *     @data: start of extension data, variable sized
+ *
+ *     Note: offsets/lengths are stored in chunks of 8 bytes, this allows
+ *     to use 'u8' types while allowing up to 2kb worth of extension data.
+ */
+struct skb_ext {
+       refcount_t refcnt;
+       u8 offset[SKB_EXT_NUM]; /* in chunks of 8 bytes */
+       u8 chunks;              /* same */
+       char data[0] __aligned(8);
+};
+
+void *skb_ext_add(struct sk_buff *skb, enum skb_ext_id id);
+void __skb_ext_del(struct sk_buff *skb, enum skb_ext_id id);
+void __skb_ext_put(struct skb_ext *ext);
+
+static inline void skb_ext_put(struct sk_buff *skb)
+{
+       if (skb->active_extensions)
+               __skb_ext_put(skb->extensions);
+}
+
+static inline void skb_ext_get(struct sk_buff *skb)
 {
-       if (nf_bridge && refcount_dec_and_test(&nf_bridge->use))
-               kfree(nf_bridge);
+       if (skb->active_extensions) {
+               struct skb_ext *ext = skb->extensions;
+
+               if (ext)
+                       refcount_inc(&ext->refcnt);
+       }
+}
+
+static inline void __skb_ext_copy(struct sk_buff *dst,
+                                 const struct sk_buff *src)
+{
+       dst->active_extensions = src->active_extensions;
+
+       if (src->active_extensions) {
+               struct skb_ext *ext = src->extensions;
+
+               refcount_inc(&ext->refcnt);
+               dst->extensions = ext;
+       }
+}
+
+static inline void skb_ext_copy(struct sk_buff *dst, const struct sk_buff *src)
+{
+       skb_ext_put(dst);
+       __skb_ext_copy(dst, src);
+}
+
+static inline bool __skb_ext_exist(const struct skb_ext *ext, enum skb_ext_id i)
+{
+       return !!ext->offset[i];
+}
+
+static inline bool skb_ext_exist(const struct sk_buff *skb, enum skb_ext_id id)
+{
+       return skb->active_extensions & (1 << id);
 }
-static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
+
+static inline void skb_ext_del(struct sk_buff *skb, enum skb_ext_id id)
 {
-       if (nf_bridge)
-               refcount_inc(&nf_bridge->use);
+       if (skb_ext_exist(skb, id))
+               __skb_ext_del(skb, id);
 }
-#endif /* CONFIG_BRIDGE_NETFILTER */
+
+static inline void *skb_ext_find(const struct sk_buff *skb, enum skb_ext_id id)
+{
+       if (skb_ext_exist(skb, id)) {
+               struct skb_ext *ext = skb->extensions;
+
+               return (void *)ext + (ext->offset[id] << 3);
+       }
+
+       return NULL;
+}
+#else
+static inline void skb_ext_put(struct sk_buff *skb) {}
+static inline void skb_ext_get(struct sk_buff *skb) {}
+static inline void skb_ext_del(struct sk_buff *skb, int unused) {}
+static inline void __skb_ext_copy(struct sk_buff *d, const struct sk_buff *s) {}
+static inline void skb_ext_copy(struct sk_buff *dst, const struct sk_buff *s) {}
+#endif /* CONFIG_SKB_EXTENSIONS */
+
 static inline void nf_reset(struct sk_buff *skb)
 {
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
@@ -3887,8 +4008,7 @@ static inline void nf_reset(struct sk_buff *skb)
        skb->_nfct = 0;
 #endif
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-       nf_bridge_put(skb->nf_bridge);
-       skb->nf_bridge = NULL;
+       skb_ext_del(skb, SKB_EXT_BRIDGE_NF);
 #endif
 }
 
@@ -3906,7 +4026,7 @@ static inline void ipvs_reset(struct sk_buff *skb)
 #endif
 }
 
-/* Note: This doesn't put any conntrack and bridge info in dst. */
+/* Note: This doesn't put any conntrack info in dst. */
 static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src,
                             bool copy)
 {
@@ -3914,10 +4034,6 @@ static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src,
        dst->_nfct = src->_nfct;
        nf_conntrack_get(skb_nfct(src));
 #endif
-#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-       dst->nf_bridge  = src->nf_bridge;
-       nf_bridge_get(src->nf_bridge);
-#endif
 #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || defined(CONFIG_NF_TABLES)
        if (copy)
                dst->nf_trace = src->nf_trace;
@@ -3928,9 +4044,6 @@ static inline void nf_copy(struct sk_buff *dst, const struct sk_buff *src)
 {
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
        nf_conntrack_put(skb_nfct(dst));
-#endif
-#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-       nf_bridge_put(dst->nf_bridge);
 #endif
        __nf_copy(dst, src, true);
 }
@@ -3953,12 +4066,19 @@ static inline void skb_init_secmark(struct sk_buff *skb)
 { }
 #endif
 
+static inline int secpath_exists(const struct sk_buff *skb)
+{
+#ifdef CONFIG_XFRM
+       return skb_ext_exist(skb, SKB_EXT_SEC_PATH);
+#else
+       return 0;
+#endif
+}
+
 static inline bool skb_irq_freeable(const struct sk_buff *skb)
 {
        return !skb->destructor &&
-#if IS_ENABLED(CONFIG_XFRM)
-               !skb->sp &&
-#endif
+               !secpath_exists(skb) &&
                !skb_nfct(skb) &&
                !skb->_skb_refdst &&
                !skb_has_frag_list(skb);
@@ -4004,10 +4124,10 @@ static inline bool skb_get_dst_pending_confirm(const struct sk_buff *skb)
        return skb->dst_pending_confirm != 0;
 }
 
-static inline struct sec_path *skb_sec_path(struct sk_buff *skb)
+static inline struct sec_path *skb_sec_path(const struct sk_buff *skb)
 {
 #ifdef CONFIG_XFRM
-       return skb->sp;
+       return skb_ext_find(skb, SKB_EXT_SEC_PATH);
 #else
        return NULL;
 #endif
index 2a11e9d..178a393 100644 (file)
@@ -36,6 +36,7 @@ struct sk_msg_sg {
        struct scatterlist              data[MAX_MSG_FRAGS + 1];
 };
 
+/* UAPI in filter.c depends on struct sk_msg_sg being first element. */
 struct sk_msg {
        struct sk_msg_sg                sg;
        void                            *data;
@@ -416,6 +417,14 @@ static inline void sk_psock_put(struct sock *sk, struct sk_psock *psock)
                sk_psock_drop(sk, psock);
 }
 
+static inline void sk_psock_data_ready(struct sock *sk, struct sk_psock *psock)
+{
+       if (psock->parser.enabled)
+               psock->parser.saved_data_ready(sk);
+       else
+               sk->sk_data_ready(sk);
+}
+
 static inline void psock_set_prog(struct bpf_prog **pprog,
                                  struct bpf_prog *prog)
 {
index 8b571e9..84c48a3 100644 (file)
@@ -286,6 +286,7 @@ struct ucred {
 #define MSG_NOSIGNAL   0x4000  /* Do not generate SIGPIPE */
 #define MSG_MORE       0x8000  /* Sender will send more */
 #define MSG_WAITFORONE 0x10000 /* recvmmsg(): block until 1+ packets avail */
+#define MSG_SENDPAGE_NOPOLICY 0x10000 /* sendpage() internal : do no apply policy */
 #define MSG_SENDPAGE_NOTLAST 0x20000 /* sendpage() internal : not the last page */
 #define MSG_BATCH      0x40000 /* sendmmsg(): more messages coming */
 #define MSG_EOF         MSG_FIN
index 43106ff..2ec1280 100644 (file)
@@ -72,7 +72,6 @@ xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
        buf->head[0].iov_base = start;
        buf->head[0].iov_len = len;
        buf->tail[0].iov_len = 0;
-       buf->bvec = NULL;
        buf->pages = NULL;
        buf->page_len = 0;
        buf->flags = 0;
index b9626aa..3e2a80c 100644 (file)
@@ -39,12 +39,13 @@ struct t10_pi_tuple {
 
 static inline u32 t10_pi_ref_tag(struct request *rq)
 {
+       unsigned int shift = ilog2(queue_logical_block_size(rq->q));
+
 #ifdef CONFIG_BLK_DEV_INTEGRITY
-       return blk_rq_pos(rq) >>
-               (rq->q->integrity.interval_exp - 9) & 0xffffffff;
-#else
-       return -1U;
+       if (rq->q->integrity.interval_exp)
+               shift = rq->q->integrity.interval_exp;
 #endif
+       return blk_rq_pos(rq) >> (shift - SECTOR_SHIFT) & 0xffffffff;
 }
 
 extern const struct blk_integrity_profile t10_pi_type1_crc;
index 8ed77bb..a9b0280 100644 (file)
@@ -196,6 +196,7 @@ struct tcp_sock {
        u32     rcv_tstamp;     /* timestamp of last received ACK (for keepalives) */
        u32     lsndtime;       /* timestamp of last sent data packet (for restart window) */
        u32     last_oow_ack_time;  /* timestamp of last out-of-window ACK */
+       u32     compressed_ack_rcv_nxt;
 
        u32     tsoffset;       /* timestamp offset */
 
index 4130a54..8a62731 100644 (file)
@@ -471,7 +471,8 @@ void perf_event_detach_bpf_prog(struct perf_event *event);
 int perf_event_query_prog_array(struct perf_event *event, void __user *info);
 int bpf_probe_register(struct bpf_raw_event_map *btp, struct bpf_prog *prog);
 int bpf_probe_unregister(struct bpf_raw_event_map *btp, struct bpf_prog *prog);
-struct bpf_raw_event_map *bpf_find_raw_tracepoint(const char *name);
+struct bpf_raw_event_map *bpf_get_raw_tracepoint(const char *name);
+void bpf_put_raw_tracepoint(struct bpf_raw_event_map *btp);
 int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
                            u32 *fd_type, const char **buf,
                            u64 *probe_offset, u64 *probe_addr);
@@ -502,10 +503,13 @@ static inline int bpf_probe_unregister(struct bpf_raw_event_map *btp, struct bpf
 {
        return -EOPNOTSUPP;
 }
-static inline struct bpf_raw_event_map *bpf_find_raw_tracepoint(const char *name)
+static inline struct bpf_raw_event_map *bpf_get_raw_tracepoint(const char *name)
 {
        return NULL;
 }
+static inline void bpf_put_raw_tracepoint(struct bpf_raw_event_map *btp)
+{
+}
 static inline int bpf_get_perf_event_info(const struct perf_event *event,
                                          u32 *prog_id, u32 *fd_type,
                                          const char **buf, u64 *probe_offset,
index 40b0b4c..df20f8b 100644 (file)
@@ -83,8 +83,8 @@ static inline int ptrace_report_syscall(struct pt_regs *regs)
  * tracehook_report_syscall_entry - task is about to attempt a system call
  * @regs:              user register state of current task
  *
- * This will be called if %TIF_SYSCALL_TRACE has been set, when the
- * current task has just entered the kernel for a system call.
+ * This will be called if %TIF_SYSCALL_TRACE or %TIF_SYSCALL_EMU have been set,
+ * when the current task has just entered the kernel for a system call.
  * Full user register state is available here.  Changing the values
  * in @regs can affect the system call number and arguments to be tried.
  * It is safe to block here, preventing the system call from beginning.
index 538ba1a..e9de8ad 100644 (file)
@@ -166,7 +166,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
                struct tracepoint_func *it_func_ptr;                    \
                void *it_func;                                          \
                void *__data;                                           \
-               int __maybe_unused idx = 0;                             \
+               int __maybe_unused __idx = 0;                           \
                                                                        \
                if (!(cond))                                            \
                        return;                                         \
@@ -182,7 +182,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
                 * doesn't work from the idle path.                     \
                 */                                                     \
                if (rcuidle) {                                          \
-                       idx = srcu_read_lock_notrace(&tracepoint_srcu); \
+                       __idx = srcu_read_lock_notrace(&tracepoint_srcu);\
                        rcu_irq_enter_irqson();                         \
                }                                                       \
                                                                        \
@@ -198,7 +198,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
                                                                        \
                if (rcuidle) {                                          \
                        rcu_irq_exit_irqson();                          \
-                       srcu_read_unlock_notrace(&tracepoint_srcu, idx);\
+                       srcu_read_unlock_notrace(&tracepoint_srcu, __idx);\
                }                                                       \
                                                                        \
                preempt_enable_notrace();                               \
index 414db2b..392138f 100644 (file)
@@ -556,6 +556,7 @@ extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx);
 extern void tty_release_struct(struct tty_struct *tty, int idx);
 extern int tty_release(struct inode *inode, struct file *filp);
 extern void tty_init_termios(struct tty_struct *tty);
+extern void tty_save_termios(struct tty_struct *tty);
 extern int tty_standard_install(struct tty_driver *driver,
                struct tty_struct *tty);
 
index 4cdd515..5e49e82 100644 (file)
@@ -407,11 +407,11 @@ struct usb_host_bos {
 };
 
 int __usb_get_extra_descriptor(char *buffer, unsigned size,
-       unsigned char type, void **ptr);
+       unsigned char type, void **ptr, size_t min);
 #define usb_get_extra_descriptor(ifpoint, type, ptr) \
                                __usb_get_extra_descriptor((ifpoint)->extra, \
                                (ifpoint)->extralen, \
-                               type, (void **)ptr)
+                               type, (void **)ptr, sizeof(**(ptr)))
 
 /* ----------------------------------------------------------------------- */
 
index b7a99ce..a1be64c 100644 (file)
@@ -66,4 +66,7 @@
 /* Device needs a pause after every control message. */
 #define USB_QUIRK_DELAY_CTRL_MSG               BIT(13)
 
+/* Hub needs extra delay after resetting its port. */
+#define USB_QUIRK_HUB_SLOW_RESET               BIT(14)
+
 #endif /* __LINUX_USB_QUIRKS_H */
index d951492..f492e21 100644 (file)
@@ -289,9 +289,7 @@ struct xarray {
 void xa_init_flags(struct xarray *, gfp_t flags);
 void *xa_load(struct xarray *, unsigned long index);
 void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
-void *xa_cmpxchg(struct xarray *, unsigned long index,
-                       void *old, void *entry, gfp_t);
-int xa_reserve(struct xarray *, unsigned long index, gfp_t);
+void *xa_erase(struct xarray *, unsigned long index);
 void *xa_store_range(struct xarray *, unsigned long first, unsigned long last,
                        void *entry, gfp_t);
 bool xa_get_mark(struct xarray *, unsigned long index, xa_mark_t);
@@ -343,65 +341,6 @@ static inline bool xa_marked(const struct xarray *xa, xa_mark_t mark)
        return xa->xa_flags & XA_FLAGS_MARK(mark);
 }
 
-/**
- * xa_erase() - Erase this entry from the XArray.
- * @xa: XArray.
- * @index: Index of entry.
- *
- * This function is the equivalent of calling xa_store() with %NULL as
- * the third argument.  The XArray does not need to allocate memory, so
- * the user does not need to provide GFP flags.
- *
- * Context: Process context.  Takes and releases the xa_lock.
- * Return: The entry which used to be at this index.
- */
-static inline void *xa_erase(struct xarray *xa, unsigned long index)
-{
-       return xa_store(xa, index, NULL, 0);
-}
-
-/**
- * xa_insert() - Store this entry in the XArray unless another entry is
- *                     already present.
- * @xa: XArray.
- * @index: Index into array.
- * @entry: New entry.
- * @gfp: Memory allocation flags.
- *
- * If you would rather see the existing entry in the array, use xa_cmpxchg().
- * This function is for users who don't care what the entry is, only that
- * one is present.
- *
- * Context: Process context.  Takes and releases the xa_lock.
- *         May sleep if the @gfp flags permit.
- * Return: 0 if the store succeeded.  -EEXIST if another entry was present.
- * -ENOMEM if memory could not be allocated.
- */
-static inline int xa_insert(struct xarray *xa, unsigned long index,
-               void *entry, gfp_t gfp)
-{
-       void *curr = xa_cmpxchg(xa, index, NULL, entry, gfp);
-       if (!curr)
-               return 0;
-       if (xa_is_err(curr))
-               return xa_err(curr);
-       return -EEXIST;
-}
-
-/**
- * xa_release() - Release a reserved entry.
- * @xa: XArray.
- * @index: Index of entry.
- *
- * After calling xa_reserve(), you can call this function to release the
- * reservation.  If the entry at @index has been stored to, this function
- * will do nothing.
- */
-static inline void xa_release(struct xarray *xa, unsigned long index)
-{
-       xa_cmpxchg(xa, index, NULL, NULL, 0);
-}
-
 /**
  * xa_for_each() - Iterate over a portion of an XArray.
  * @xa: XArray.
@@ -455,6 +394,7 @@ void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
 void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old,
                void *entry, gfp_t);
 int __xa_alloc(struct xarray *, u32 *id, u32 max, void *entry, gfp_t);
+int __xa_reserve(struct xarray *, unsigned long index, gfp_t);
 void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t);
 void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t);
 
@@ -486,6 +426,58 @@ static inline int __xa_insert(struct xarray *xa, unsigned long index,
        return -EEXIST;
 }
 
+/**
+ * xa_store_bh() - Store this entry in the XArray.
+ * @xa: XArray.
+ * @index: Index into array.
+ * @entry: New entry.
+ * @gfp: Memory allocation flags.
+ *
+ * This function is like calling xa_store() except it disables softirqs
+ * while holding the array lock.
+ *
+ * Context: Any context.  Takes and releases the xa_lock while
+ * disabling softirqs.
+ * Return: The entry which used to be at this index.
+ */
+static inline void *xa_store_bh(struct xarray *xa, unsigned long index,
+               void *entry, gfp_t gfp)
+{
+       void *curr;
+
+       xa_lock_bh(xa);
+       curr = __xa_store(xa, index, entry, gfp);
+       xa_unlock_bh(xa);
+
+       return curr;
+}
+
+/**
+ * xa_store_irq() - Erase this entry from the XArray.
+ * @xa: XArray.
+ * @index: Index into array.
+ * @entry: New entry.
+ * @gfp: Memory allocation flags.
+ *
+ * This function is like calling xa_store() except it disables interrupts
+ * while holding the array lock.
+ *
+ * Context: Process context.  Takes and releases the xa_lock while
+ * disabling interrupts.
+ * Return: The entry which used to be at this index.
+ */
+static inline void *xa_store_irq(struct xarray *xa, unsigned long index,
+               void *entry, gfp_t gfp)
+{
+       void *curr;
+
+       xa_lock_irq(xa);
+       curr = __xa_store(xa, index, entry, gfp);
+       xa_unlock_irq(xa);
+
+       return curr;
+}
+
 /**
  * xa_erase_bh() - Erase this entry from the XArray.
  * @xa: XArray.
@@ -495,7 +487,7 @@ static inline int __xa_insert(struct xarray *xa, unsigned long index,
  * the third argument.  The XArray does not need to allocate memory, so
  * the user does not need to provide GFP flags.
  *
- * Context: Process context.  Takes and releases the xa_lock while
+ * Context: Any context.  Takes and releases the xa_lock while
  * disabling softirqs.
  * Return: The entry which used to be at this index.
  */
@@ -534,6 +526,115 @@ static inline void *xa_erase_irq(struct xarray *xa, unsigned long index)
        return entry;
 }
 
+/**
+ * xa_cmpxchg() - Conditionally replace an entry in the XArray.
+ * @xa: XArray.
+ * @index: Index into array.
+ * @old: Old value to test against.
+ * @entry: New value to place in array.
+ * @gfp: Memory allocation flags.
+ *
+ * If the entry at @index is the same as @old, replace it with @entry.
+ * If the return value is equal to @old, then the exchange was successful.
+ *
+ * Context: Any context.  Takes and releases the xa_lock.  May sleep
+ * if the @gfp flags permit.
+ * Return: The old value at this index or xa_err() if an error happened.
+ */
+static inline void *xa_cmpxchg(struct xarray *xa, unsigned long index,
+                       void *old, void *entry, gfp_t gfp)
+{
+       void *curr;
+
+       xa_lock(xa);
+       curr = __xa_cmpxchg(xa, index, old, entry, gfp);
+       xa_unlock(xa);
+
+       return curr;
+}
+
+/**
+ * xa_cmpxchg_bh() - Conditionally replace an entry in the XArray.
+ * @xa: XArray.
+ * @index: Index into array.
+ * @old: Old value to test against.
+ * @entry: New value to place in array.
+ * @gfp: Memory allocation flags.
+ *
+ * This function is like calling xa_cmpxchg() except it disables softirqs
+ * while holding the array lock.
+ *
+ * Context: Any context.  Takes and releases the xa_lock while
+ * disabling softirqs.  May sleep if the @gfp flags permit.
+ * Return: The old value at this index or xa_err() if an error happened.
+ */
+static inline void *xa_cmpxchg_bh(struct xarray *xa, unsigned long index,
+                       void *old, void *entry, gfp_t gfp)
+{
+       void *curr;
+
+       xa_lock_bh(xa);
+       curr = __xa_cmpxchg(xa, index, old, entry, gfp);
+       xa_unlock_bh(xa);
+
+       return curr;
+}
+
+/**
+ * xa_cmpxchg_irq() - Conditionally replace an entry in the XArray.
+ * @xa: XArray.
+ * @index: Index into array.
+ * @old: Old value to test against.
+ * @entry: New value to place in array.
+ * @gfp: Memory allocation flags.
+ *
+ * This function is like calling xa_cmpxchg() except it disables interrupts
+ * while holding the array lock.
+ *
+ * Context: Process context.  Takes and releases the xa_lock while
+ * disabling interrupts.  May sleep if the @gfp flags permit.
+ * Return: The old value at this index or xa_err() if an error happened.
+ */
+static inline void *xa_cmpxchg_irq(struct xarray *xa, unsigned long index,
+                       void *old, void *entry, gfp_t gfp)
+{
+       void *curr;
+
+       xa_lock_irq(xa);
+       curr = __xa_cmpxchg(xa, index, old, entry, gfp);
+       xa_unlock_irq(xa);
+
+       return curr;
+}
+
+/**
+ * xa_insert() - Store this entry in the XArray unless another entry is
+ *                     already present.
+ * @xa: XArray.
+ * @index: Index into array.
+ * @entry: New entry.
+ * @gfp: Memory allocation flags.
+ *
+ * If you would rather see the existing entry in the array, use xa_cmpxchg().
+ * This function is for users who don't care what the entry is, only that
+ * one is present.
+ *
+ * Context: Process context.  Takes and releases the xa_lock.
+ *         May sleep if the @gfp flags permit.
+ * Return: 0 if the store succeeded.  -EEXIST if another entry was present.
+ * -ENOMEM if memory could not be allocated.
+ */
+static inline int xa_insert(struct xarray *xa, unsigned long index,
+               void *entry, gfp_t gfp)
+{
+       void *curr = xa_cmpxchg(xa, index, NULL, entry, gfp);
+       if (!curr)
+               return 0;
+       if (xa_is_err(curr))
+               return xa_err(curr);
+       return -EEXIST;
+}
+
 /**
  * xa_alloc() - Find somewhere to store this entry in the XArray.
  * @xa: XArray.
@@ -575,7 +676,7 @@ static inline int xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry,
  * Updates the @id pointer with the index, then stores the entry at that
  * index.  A concurrent lookup will not see an uninitialised @id.
  *
- * Context: Process context.  Takes and releases the xa_lock while
+ * Context: Any context.  Takes and releases the xa_lock while
  * disabling softirqs.  May sleep if the @gfp flags permit.
  * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if
  * there is no more space in the XArray.
@@ -621,6 +722,98 @@ static inline int xa_alloc_irq(struct xarray *xa, u32 *id, u32 max, void *entry,
        return err;
 }
 
+/**
+ * xa_reserve() - Reserve this index in the XArray.
+ * @xa: XArray.
+ * @index: Index into array.
+ * @gfp: Memory allocation flags.
+ *
+ * Ensures there is somewhere to store an entry at @index in the array.
+ * If there is already something stored at @index, this function does
+ * nothing.  If there was nothing there, the entry is marked as reserved.
+ * Loading from a reserved entry returns a %NULL pointer.
+ *
+ * If you do not use the entry that you have reserved, call xa_release()
+ * or xa_erase() to free any unnecessary memory.
+ *
+ * Context: Any context.  Takes and releases the xa_lock.
+ * May sleep if the @gfp flags permit.
+ * Return: 0 if the reservation succeeded or -ENOMEM if it failed.
+ */
+static inline
+int xa_reserve(struct xarray *xa, unsigned long index, gfp_t gfp)
+{
+       int ret;
+
+       xa_lock(xa);
+       ret = __xa_reserve(xa, index, gfp);
+       xa_unlock(xa);
+
+       return ret;
+}
+
+/**
+ * xa_reserve_bh() - Reserve this index in the XArray.
+ * @xa: XArray.
+ * @index: Index into array.
+ * @gfp: Memory allocation flags.
+ *
+ * A softirq-disabling version of xa_reserve().
+ *
+ * Context: Any context.  Takes and releases the xa_lock while
+ * disabling softirqs.
+ * Return: 0 if the reservation succeeded or -ENOMEM if it failed.
+ */
+static inline
+int xa_reserve_bh(struct xarray *xa, unsigned long index, gfp_t gfp)
+{
+       int ret;
+
+       xa_lock_bh(xa);
+       ret = __xa_reserve(xa, index, gfp);
+       xa_unlock_bh(xa);
+
+       return ret;
+}
+
+/**
+ * xa_reserve_irq() - Reserve this index in the XArray.
+ * @xa: XArray.
+ * @index: Index into array.
+ * @gfp: Memory allocation flags.
+ *
+ * An interrupt-disabling version of xa_reserve().
+ *
+ * Context: Process context.  Takes and releases the xa_lock while
+ * disabling interrupts.
+ * Return: 0 if the reservation succeeded or -ENOMEM if it failed.
+ */
+static inline
+int xa_reserve_irq(struct xarray *xa, unsigned long index, gfp_t gfp)
+{
+       int ret;
+
+       xa_lock_irq(xa);
+       ret = __xa_reserve(xa, index, gfp);
+       xa_unlock_irq(xa);
+
+       return ret;
+}
+
+/**
+ * xa_release() - Release a reserved entry.
+ * @xa: XArray.
+ * @index: Index of entry.
+ *
+ * After calling xa_reserve(), you can call this function to release the
+ * reservation.  If the entry at @index has been stored to, this function
+ * will do nothing.
+ */
+static inline void xa_release(struct xarray *xa, unsigned long index)
+{
+       xa_cmpxchg(xa, index, NULL, NULL, 0);
+}
+
 /* Everything below here is the Advanced API.  Proceed with caution. */
 
 /*
index 0ce75c3..bd36d74 100644 (file)
@@ -68,7 +68,7 @@ struct media_request {
        unsigned int access_count;
        struct list_head objects;
        unsigned int num_incomplete_objects;
-       struct wait_queue_head poll_wait;
+       wait_queue_head_t poll_wait;
        spinlock_t lock;
 };
 
diff --git a/include/media/mpeg2-ctrls.h b/include/media/mpeg2-ctrls.h
new file mode 100644 (file)
index 0000000..d21f40e
--- /dev/null
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * These are the MPEG2 state controls for use with stateless MPEG-2
+ * codec drivers.
+ *
+ * It turns out that these structs are not stable yet and will undergo
+ * more changes. So keep them private until they are stable and ready to
+ * become part of the official public API.
+ */
+
+#ifndef _MPEG2_CTRLS_H_
+#define _MPEG2_CTRLS_H_
+
+#define V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS         (V4L2_CID_MPEG_BASE+250)
+#define V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION         (V4L2_CID_MPEG_BASE+251)
+
+/* enum v4l2_ctrl_type type values */
+#define V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS 0x0103
+#define        V4L2_CTRL_TYPE_MPEG2_QUANTIZATION 0x0104
+
+#define V4L2_MPEG2_PICTURE_CODING_TYPE_I       1
+#define V4L2_MPEG2_PICTURE_CODING_TYPE_P       2
+#define V4L2_MPEG2_PICTURE_CODING_TYPE_B       3
+#define V4L2_MPEG2_PICTURE_CODING_TYPE_D       4
+
+struct v4l2_mpeg2_sequence {
+       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Sequence header */
+       __u16   horizontal_size;
+       __u16   vertical_size;
+       __u32   vbv_buffer_size;
+
+       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Sequence extension */
+       __u8    profile_and_level_indication;
+       __u8    progressive_sequence;
+       __u8    chroma_format;
+       __u8    pad;
+};
+
+struct v4l2_mpeg2_picture {
+       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Picture header */
+       __u8    picture_coding_type;
+
+       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Picture coding extension */
+       __u8    f_code[2][2];
+       __u8    intra_dc_precision;
+       __u8    picture_structure;
+       __u8    top_field_first;
+       __u8    frame_pred_frame_dct;
+       __u8    concealment_motion_vectors;
+       __u8    q_scale_type;
+       __u8    intra_vlc_format;
+       __u8    alternate_scan;
+       __u8    repeat_first_field;
+       __u8    progressive_frame;
+       __u8    pad;
+};
+
+struct v4l2_ctrl_mpeg2_slice_params {
+       __u32   bit_size;
+       __u32   data_bit_offset;
+
+       struct v4l2_mpeg2_sequence sequence;
+       struct v4l2_mpeg2_picture picture;
+
+       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Slice */
+       __u8    quantiser_scale_code;
+
+       __u8    backward_ref_index;
+       __u8    forward_ref_index;
+       __u8    pad;
+};
+
+struct v4l2_ctrl_mpeg2_quantization {
+       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Quant matrix extension */
+       __u8    load_intra_quantiser_matrix;
+       __u8    load_non_intra_quantiser_matrix;
+       __u8    load_chroma_intra_quantiser_matrix;
+       __u8    load_chroma_non_intra_quantiser_matrix;
+
+       __u8    intra_quantiser_matrix[64];
+       __u8    non_intra_quantiser_matrix[64];
+       __u8    chroma_intra_quantiser_matrix[64];
+       __u8    chroma_non_intra_quantiser_matrix[64];
+};
+
+#endif
index 83ce059..d63cf22 100644 (file)
 #include <linux/videodev2.h>
 #include <media/media-request.h>
 
+/*
+ * Include the mpeg2 stateless codec compound control definitions.
+ * This will move to the public headers once this API is fully stable.
+ */
+#include <media/mpeg2-ctrls.h>
+
 /* forward references */
 struct file;
 struct v4l2_ctrl_handler;
index 58c1ecf..5467264 100644 (file)
@@ -624,7 +624,7 @@ v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx)
 
 /* v4l2 request helper */
 
-void vb2_m2m_request_queue(struct media_request *req);
+void v4l2_m2m_request_queue(struct media_request *req);
 
 /* v4l2 ioctl helpers */
 
index e86981d..4a737b2 100644 (file)
@@ -239,6 +239,7 @@ struct vb2_queue;
  * @num_planes:                number of planes in the buffer
  *                     on an internal driver queue.
  * @timestamp:         frame timestamp in ns.
+ * @request:           the request this buffer is associated with.
  * @req_obj:           used to bind this buffer to a request. This
  *                     request object has a refcount.
  */
@@ -249,6 +250,7 @@ struct vb2_buffer {
        unsigned int            memory;
        unsigned int            num_planes;
        u64                     timestamp;
+       struct media_request    *request;
        struct media_request_object     req_obj;
 
        /* private: internal use only
index 05c7df4..dbc795e 100644 (file)
@@ -194,35 +194,5 @@ static inline void tcf_action_stats_update(struct tc_action *a, u64 bytes,
 #endif
 }
 
-#ifdef CONFIG_NET_CLS_ACT
-int tc_setup_cb_egdev_register(const struct net_device *dev,
-                              tc_setup_cb_t *cb, void *cb_priv);
-void tc_setup_cb_egdev_unregister(const struct net_device *dev,
-                                 tc_setup_cb_t *cb, void *cb_priv);
-int tc_setup_cb_egdev_call(const struct net_device *dev,
-                          enum tc_setup_type type, void *type_data,
-                          bool err_stop);
-#else
-static inline
-int tc_setup_cb_egdev_register(const struct net_device *dev,
-                              tc_setup_cb_t *cb, void *cb_priv)
-{
-       return 0;
-}
-
-static inline
-void tc_setup_cb_egdev_unregister(const struct net_device *dev,
-                                 tc_setup_cb_t *cb, void *cb_priv)
-{
-}
-
-static inline
-int tc_setup_cb_egdev_call(const struct net_device *dev,
-                          enum tc_setup_type type, void *type_data,
-                          bool err_stop)
-{
-       return 0;
-}
-#endif
 
 #endif
index de58794..1adefe4 100644 (file)
@@ -77,7 +77,8 @@ int rxrpc_kernel_retry_call(struct socket *, struct rxrpc_call *,
                            struct sockaddr_rxrpc *, struct key *);
 int rxrpc_kernel_check_call(struct socket *, struct rxrpc_call *,
                            enum rxrpc_call_completion *, u32 *);
-u32 rxrpc_kernel_check_life(struct socket *, struct rxrpc_call *);
+u32 rxrpc_kernel_check_life(const struct socket *, const struct rxrpc_call *);
+void rxrpc_kernel_probe_life(struct socket *, struct rxrpc_call *);
 u32 rxrpc_kernel_get_epoch(struct socket *, struct rxrpc_call *);
 bool rxrpc_kernel_get_reply_time(struct socket *, struct rxrpc_call *,
                                 ktime_t *);
index 1fa41b7..e0c41eb 100644 (file)
@@ -777,8 +777,10 @@ struct cfg80211_crypto_settings {
  * @probe_resp: probe response template (AP mode only)
  * @ftm_responder: enable FTM responder functionality; -1 for no change
  *     (which also implies no change in LCI/civic location data)
- * @lci: LCI subelement content
- * @civicloc: Civic location subelement content
+ * @lci: Measurement Report element content, starting with Measurement Token
+ *     (measurement type 8)
+ * @civicloc: Measurement Report element content, starting with Measurement
+ *     Token (measurement type 11)
  * @lci_len: LCI data length
  * @civicloc_len: Civic location data length
  */
@@ -1296,6 +1298,7 @@ struct cfg80211_tid_stats {
  * @rx_beacon: number of beacons received from this peer
  * @rx_beacon_signal_avg: signal strength average (in dBm) for beacons received
  *     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
  * @pertid: per-TID statistics, see &struct cfg80211_tid_stats, using the last
  *     (IEEE80211_NUM_TIDS) index for MSDUs not encapsulated in QoS-MPDUs.
@@ -1350,6 +1353,8 @@ struct station_info {
        u64 rx_beacon;
        u64 rx_duration;
        u8 rx_beacon_signal_avg;
+       u8 connected_to_gate;
+
        struct cfg80211_tid_stats *pertid;
        s8 ack_signal;
        s8 avg_ack_signal;
@@ -1559,6 +1564,10 @@ struct bss_parameters {
  * @plink_timeout: If no tx activity is seen from a STA we've established
  *     peering with for longer than this time (in seconds), then remove it
  *     from the STA's list of peers.  Default is 30 minutes.
+ * @dot11MeshConnectedToMeshGate: if set to true, advertise that this STA is
+ *      connected to a mesh gate in mesh formation info.  If false, the
+ *      value in mesh formation is determined by the presence of root paths
+ *      in the mesh path table
  */
 struct mesh_config {
        u16 dot11MeshRetryTimeout;
@@ -1578,6 +1587,7 @@ struct mesh_config {
        u16 dot11MeshHWMPperrMinInterval;
        u16 dot11MeshHWMPnetDiameterTraversalTime;
        u8 dot11MeshHWMPRootMode;
+       bool dot11MeshConnectedToMeshGate;
        u16 dot11MeshHWMPRannInterval;
        bool dot11MeshGateAnnouncementProtocol;
        bool dot11MeshForwarding;
@@ -2815,7 +2825,7 @@ struct cfg80211_external_auth_params {
 };
 
 /**
- * cfg80211_ftm_responder_stats - FTM responder statistics
+ * struct cfg80211_ftm_responder_stats - FTM responder statistics
  *
  * @filled: bitflag of flags using the bits of &enum nl80211_ftm_stats to
  *     indicate the relevant values in this struct for them
@@ -2848,6 +2858,190 @@ struct cfg80211_ftm_responder_stats {
        u32 out_of_window_triggers_num;
 };
 
+/**
+ * struct cfg80211_pmsr_ftm_result - FTM result
+ * @failure_reason: if this measurement failed (PMSR status is
+ *     %NL80211_PMSR_STATUS_FAILURE), this gives a more precise
+ *     reason than just "failure"
+ * @burst_index: if reporting partial results, this is the index
+ *     in [0 .. num_bursts-1] of the burst that's being reported
+ * @num_ftmr_attempts: number of FTM request frames transmitted
+ * @num_ftmr_successes: number of FTM request frames acked
+ * @busy_retry_time: if failure_reason is %NL80211_PMSR_FTM_FAILURE_PEER_BUSY,
+ *     fill this to indicate in how many seconds a retry is deemed possible
+ *     by the responder
+ * @num_bursts_exp: actual number of bursts exponent negotiated
+ * @burst_duration: actual burst duration negotiated
+ * @ftms_per_burst: actual FTMs per burst negotiated
+ * @lci_len: length of LCI information (if present)
+ * @civicloc_len: length of civic location information (if present)
+ * @lci: LCI data (may be %NULL)
+ * @civicloc: civic location data (may be %NULL)
+ * @rssi_avg: average RSSI over FTM action frames reported
+ * @rssi_spread: spread of the RSSI over FTM action frames reported
+ * @tx_rate: bitrate for transmitted FTM action frame response
+ * @rx_rate: bitrate of received FTM action frame
+ * @rtt_avg: average of RTTs measured (must have either this or @dist_avg)
+ * @rtt_variance: variance of RTTs measured (note that standard deviation is
+ *     the square root of the variance)
+ * @rtt_spread: spread of the RTTs measured
+ * @dist_avg: average of distances (mm) measured
+ *     (must have either this or @rtt_avg)
+ * @dist_variance: variance of distances measured (see also @rtt_variance)
+ * @dist_spread: spread of distances measured (see also @rtt_spread)
+ * @num_ftmr_attempts_valid: @num_ftmr_attempts is valid
+ * @num_ftmr_successes_valid: @num_ftmr_successes is valid
+ * @rssi_avg_valid: @rssi_avg is valid
+ * @rssi_spread_valid: @rssi_spread is valid
+ * @tx_rate_valid: @tx_rate is valid
+ * @rx_rate_valid: @rx_rate is valid
+ * @rtt_avg_valid: @rtt_avg is valid
+ * @rtt_variance_valid: @rtt_variance is valid
+ * @rtt_spread_valid: @rtt_spread is valid
+ * @dist_avg_valid: @dist_avg is valid
+ * @dist_variance_valid: @dist_variance is valid
+ * @dist_spread_valid: @dist_spread is valid
+ */
+struct cfg80211_pmsr_ftm_result {
+       const u8 *lci;
+       const u8 *civicloc;
+       unsigned int lci_len;
+       unsigned int civicloc_len;
+       enum nl80211_peer_measurement_ftm_failure_reasons failure_reason;
+       u32 num_ftmr_attempts, num_ftmr_successes;
+       s16 burst_index;
+       u8 busy_retry_time;
+       u8 num_bursts_exp;
+       u8 burst_duration;
+       u8 ftms_per_burst;
+       s32 rssi_avg;
+       s32 rssi_spread;
+       struct rate_info tx_rate, rx_rate;
+       s64 rtt_avg;
+       s64 rtt_variance;
+       s64 rtt_spread;
+       s64 dist_avg;
+       s64 dist_variance;
+       s64 dist_spread;
+
+       u16 num_ftmr_attempts_valid:1,
+           num_ftmr_successes_valid:1,
+           rssi_avg_valid:1,
+           rssi_spread_valid:1,
+           tx_rate_valid:1,
+           rx_rate_valid:1,
+           rtt_avg_valid:1,
+           rtt_variance_valid:1,
+           rtt_spread_valid:1,
+           dist_avg_valid:1,
+           dist_variance_valid:1,
+           dist_spread_valid:1;
+};
+
+/**
+ * struct cfg80211_pmsr_result - peer measurement result
+ * @addr: address of the peer
+ * @host_time: host time (use ktime_get_boottime() adjust to the time when the
+ *     measurement was made)
+ * @ap_tsf: AP's TSF at measurement time
+ * @status: status of the measurement
+ * @final: if reporting partial results, mark this as the last one; if not
+ *     reporting partial results always set this flag
+ * @ap_tsf_valid: indicates the @ap_tsf value is valid
+ * @type: type of the measurement reported, note that we only support reporting
+ *     one type at a time, but you can report multiple results separately and
+ *     they're all aggregated for userspace.
+ */
+struct cfg80211_pmsr_result {
+       u64 host_time, ap_tsf;
+       enum nl80211_peer_measurement_status status;
+
+       u8 addr[ETH_ALEN];
+
+       u8 final:1,
+          ap_tsf_valid:1;
+
+       enum nl80211_peer_measurement_type type;
+
+       union {
+               struct cfg80211_pmsr_ftm_result ftm;
+       };
+};
+
+/**
+ * struct cfg80211_pmsr_ftm_request_peer - FTM request data
+ * @requested: indicates FTM is requested
+ * @preamble: frame preamble to use
+ * @burst_period: burst period to use
+ * @asap: indicates to use ASAP mode
+ * @num_bursts_exp: number of bursts exponent
+ * @burst_duration: burst duration
+ * @ftms_per_burst: number of FTMs per burst
+ * @ftmr_retries: number of retries for FTM request
+ * @request_lci: request LCI information
+ * @request_civicloc: request civic location information
+ *
+ * See also nl80211 for the respective attribute documentation.
+ */
+struct cfg80211_pmsr_ftm_request_peer {
+       enum nl80211_preamble preamble;
+       u16 burst_period;
+       u8 requested:1,
+          asap:1,
+          request_lci:1,
+          request_civicloc:1;
+       u8 num_bursts_exp;
+       u8 burst_duration;
+       u8 ftms_per_burst;
+       u8 ftmr_retries;
+};
+
+/**
+ * struct cfg80211_pmsr_request_peer - peer data for a peer measurement request
+ * @addr: MAC address
+ * @chandef: channel to use
+ * @report_ap_tsf: report the associated AP's TSF
+ * @ftm: FTM data, see &struct cfg80211_pmsr_ftm_request_peer
+ */
+struct cfg80211_pmsr_request_peer {
+       u8 addr[ETH_ALEN];
+       struct cfg80211_chan_def chandef;
+       u8 report_ap_tsf:1;
+       struct cfg80211_pmsr_ftm_request_peer ftm;
+};
+
+/**
+ * struct cfg80211_pmsr_request - peer measurement request
+ * @cookie: cookie, set by cfg80211
+ * @nl_portid: netlink portid - used by cfg80211
+ * @drv_data: driver data for this request, if required for aborting,
+ *     not otherwise freed or anything by cfg80211
+ * @mac_addr: MAC address used for (randomised) request
+ * @mac_addr_mask: MAC address mask used for randomisation, bits that
+ *     are 0 in the mask should be randomised, bits that are 1 should
+ *     be taken from the @mac_addr
+ * @list: used by cfg80211 to hold on to the request
+ * @timeout: timeout (in milliseconds) for the whole operation, if
+ *     zero it means there's no timeout
+ * @n_peers: number of peers to do measurements with
+ * @peers: per-peer measurement request data
+ */
+struct cfg80211_pmsr_request {
+       u64 cookie;
+       void *drv_data;
+       u32 n_peers;
+       u32 nl_portid;
+
+       u32 timeout;
+
+       u8 mac_addr[ETH_ALEN] __aligned(2);
+       u8 mac_addr_mask[ETH_ALEN] __aligned(2);
+
+       struct list_head list;
+
+       struct cfg80211_pmsr_request_peer peers[];
+};
+
 /**
  * struct cfg80211_ops - backend description for wireless configuration
  *
@@ -3183,6 +3377,8 @@ struct cfg80211_ftm_responder_stats {
  *
  * @get_ftm_responder_stats: Retrieve FTM responder statistics, if available.
  *     Statistics should be cumulative, currently no way to reset is provided.
+ * @start_pmsr: start peer measurement (e.g. FTM)
+ * @abort_pmsr: abort peer measurement
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -3492,6 +3688,11 @@ struct cfg80211_ops {
        int     (*get_ftm_responder_stats)(struct wiphy *wiphy,
                                struct net_device *dev,
                                struct cfg80211_ftm_responder_stats *ftm_stats);
+
+       int     (*start_pmsr)(struct wiphy *wiphy, struct wireless_dev *wdev,
+                             struct cfg80211_pmsr_request *request);
+       void    (*abort_pmsr)(struct wiphy *wiphy, struct wireless_dev *wdev,
+                             struct cfg80211_pmsr_request *request);
 };
 
 /*
@@ -3863,6 +4064,42 @@ struct wiphy_iftype_ext_capab {
        u8 extended_capabilities_len;
 };
 
+/**
+ * struct cfg80211_pmsr_capabilities - cfg80211 peer measurement capabilities
+ * @max_peers: maximum number of peers in a single measurement
+ * @report_ap_tsf: can report assoc AP's TSF for radio resource measurement
+ * @randomize_mac_addr: can randomize MAC address for measurement
+ * @ftm.supported: FTM measurement is supported
+ * @ftm.asap: ASAP-mode is supported
+ * @ftm.non_asap: non-ASAP-mode is supported
+ * @ftm.request_lci: can request LCI data
+ * @ftm.request_civicloc: can request civic location data
+ * @ftm.preambles: bitmap of preambles supported (&enum nl80211_preamble)
+ * @ftm.bandwidths: bitmap of bandwidths supported (&enum nl80211_chan_width)
+ * @ftm.max_bursts_exponent: maximum burst exponent supported
+ *     (set to -1 if not limited; note that setting this will necessarily
+ *     forbid using the value 15 to let the responder pick)
+ * @ftm.max_ftms_per_burst: maximum FTMs per burst supported (set to 0 if
+ *     not limited)
+ */
+struct cfg80211_pmsr_capabilities {
+       unsigned int max_peers;
+       u8 report_ap_tsf:1,
+          randomize_mac_addr:1;
+
+       struct {
+               u32 preambles;
+               u32 bandwidths;
+               s8 max_bursts_exponent;
+               u8 max_ftms_per_burst;
+               u8 supported:1,
+                  asap:1,
+                  non_asap:1,
+                  request_lci:1,
+                  request_civicloc:1;
+       } ftm;
+};
+
 /**
  * struct wiphy - wireless hardware description
  * @reg_notifier: the driver's regulatory notification callback,
@@ -4027,6 +4264,8 @@ struct wiphy_iftype_ext_capab {
  * @txq_limit: configuration of internal TX queue frame limit
  * @txq_memory_limit: configuration internal TX queue memory limit
  * @txq_quantum: configuration of internal TX queue scheduler quantum
+ *
+ * @pmsr_capa: peer measurement capabilities
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -4163,6 +4402,8 @@ struct wiphy {
        u32 txq_memory_limit;
        u32 txq_quantum;
 
+       const struct cfg80211_pmsr_capabilities *pmsr_capa;
+
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
@@ -4365,6 +4606,9 @@ struct cfg80211_cqm_config;
  * @owner_nlportid: (private) owner socket port ID
  * @nl_owner_dead: (private) owner socket went away
  * @cqm_config: (private) nl80211 RSSI monitor state
+ * @pmsr_list: (private) peer measurement requests
+ * @pmsr_lock: (private) peer measurements requests/results lock
+ * @pmsr_free_wk: (private) peer measurements cleanup work
  */
 struct wireless_dev {
        struct wiphy *wiphy;
@@ -4436,6 +4680,10 @@ struct wireless_dev {
 #endif
 
        struct cfg80211_cqm_config *cqm_config;
+
+       struct list_head pmsr_list;
+       spinlock_t pmsr_lock;
+       struct work_struct pmsr_free_wk;
 };
 
 static inline u8 *wdev_address(struct wireless_dev *wdev)
@@ -5328,7 +5576,8 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
  * cfg80211 then sends a notification to userspace.
  */
 void cfg80211_notify_new_peer_candidate(struct net_device *dev,
-               const u8 *macaddr, const u8 *ie, u8 ie_len, gfp_t gfp);
+               const u8 *macaddr, const u8 *ie, u8 ie_len,
+               int sig_dbm, gfp_t gfp);
 
 /**
  * DOC: RFkill integration
@@ -6630,6 +6879,31 @@ int cfg80211_external_auth_request(struct net_device *netdev,
                                   struct cfg80211_external_auth_params *params,
                                   gfp_t gfp);
 
+/**
+ * cfg80211_pmsr_report - report peer measurement result data
+ * @wdev: the wireless device reporting the measurement
+ * @req: the original measurement request
+ * @result: the result data
+ * @gfp: allocation flags
+ */
+void cfg80211_pmsr_report(struct wireless_dev *wdev,
+                         struct cfg80211_pmsr_request *req,
+                         struct cfg80211_pmsr_result *result,
+                         gfp_t gfp);
+
+/**
+ * cfg80211_pmsr_complete - report peer measurement completed
+ * @wdev: the wireless device reporting the measurement
+ * @req: the original measurement request
+ * @gfp: allocation flags
+ *
+ * Report that the entire measurement completed, after this
+ * the request pointer will no longer be valid.
+ */
+void cfg80211_pmsr_complete(struct wireless_dev *wdev,
+                           struct cfg80211_pmsr_request *req,
+                           gfp_t gfp);
+
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
 
 /* wiphy_printk helpers, similar to dev_printk */
index 45db0c7..67f4293 100644 (file)
@@ -365,6 +365,7 @@ enum devlink_param_generic_id {
        DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI,
        DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
        DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
+       DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
 
        /* add new param generic ids above here*/
        __DEVLINK_PARAM_GENERIC_ID_MAX,
@@ -392,6 +393,9 @@ enum devlink_param_generic_id {
 #define DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME "msix_vec_per_pf_min"
 #define DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE DEVLINK_PARAM_TYPE_U32
 
+#define DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME "fw_load_policy"
+#define DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE DEVLINK_PARAM_TYPE_U8
+
 #define DEVLINK_PARAM_GENERIC(_id, _cmodes, _get, _set, _validate)     \
 {                                                                      \
        .id = DEVLINK_PARAM_GENERIC_ID_##_id,                           \
index 23690c4..b3eefe8 100644 (file)
@@ -36,7 +36,7 @@ enum dsa_tag_protocol {
        DSA_TAG_PROTO_DSA,
        DSA_TAG_PROTO_EDSA,
        DSA_TAG_PROTO_GSWIP,
-       DSA_TAG_PROTO_KSZ,
+       DSA_TAG_PROTO_KSZ9477,
        DSA_TAG_PROTO_LAN9303,
        DSA_TAG_PROTO_MTK,
        DSA_TAG_PROTO_QCA,
@@ -113,6 +113,7 @@ struct dsa_device_ops {
                               struct packet_type *pt);
        int (*flow_dissect)(const struct sk_buff *skb, __be16 *proto,
                            int *offset);
+       unsigned int overhead;
 };
 
 struct dsa_switch_tree {
index 8ce2179..93f2c9a 100644 (file)
@@ -38,8 +38,8 @@ struct flowi_common {
 #define FLOWI_FLAG_KNOWN_NH            0x02
 #define FLOWI_FLAG_SKIP_NH_OIF         0x04
        __u32   flowic_secid;
-       struct flowi_tunnel flowic_tun_key;
        kuid_t  flowic_uid;
+       struct flowi_tunnel flowic_tun_key;
 };
 
 union flowi_uli {
index 6a4586d..2b26979 100644 (file)
@@ -209,8 +209,8 @@ enum flow_dissector_key_id {
        FLOW_DISSECTOR_KEY_ETH_ADDRS, /* struct flow_dissector_key_eth_addrs */
        FLOW_DISSECTOR_KEY_TIPC, /* struct flow_dissector_key_tipc */
        FLOW_DISSECTOR_KEY_ARP, /* struct flow_dissector_key_arp */
-       FLOW_DISSECTOR_KEY_VLAN, /* struct flow_dissector_key_flow_vlan */
-       FLOW_DISSECTOR_KEY_FLOW_LABEL, /* struct flow_dissector_key_flow_tags */
+       FLOW_DISSECTOR_KEY_VLAN, /* struct flow_dissector_key_vlan */
+       FLOW_DISSECTOR_KEY_FLOW_LABEL, /* struct flow_dissector_key_tags */
        FLOW_DISSECTOR_KEY_GRE_KEYID, /* struct flow_dissector_key_keyid */
        FLOW_DISSECTOR_KEY_MPLS_ENTROPY, /* struct flow_dissector_key_keyid */
        FLOW_DISSECTOR_KEY_ENC_KEYID, /* struct flow_dissector_key_keyid */
@@ -221,7 +221,7 @@ enum flow_dissector_key_id {
        FLOW_DISSECTOR_KEY_MPLS, /* struct flow_dissector_key_mpls */
        FLOW_DISSECTOR_KEY_TCP, /* struct flow_dissector_key_tcp */
        FLOW_DISSECTOR_KEY_IP, /* struct flow_dissector_key_ip */
-       FLOW_DISSECTOR_KEY_CVLAN, /* struct flow_dissector_key_flow_vlan */
+       FLOW_DISSECTOR_KEY_CVLAN, /* struct flow_dissector_key_vlan */
        FLOW_DISSECTOR_KEY_ENC_IP, /* struct flow_dissector_key_ip */
        FLOW_DISSECTOR_KEY_ENC_OPTS, /* struct flow_dissector_key_enc_opts */
 
index 946bd53..ca23860 100644 (file)
@@ -10,7 +10,7 @@
 struct gnet_stats_basic_cpu {
        struct gnet_stats_basic_packed bstats;
        struct u64_stats_sync syncp;
-};
+} __aligned(2 * sizeof(u64));
 
 struct net_rate_estimator;
 
index 797142e..b60f212 100644 (file)
@@ -37,8 +37,17 @@ struct net_device *gretap_fb_dev_create(struct net *net, const char *name,
 int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
                     bool *csum_err, __be16 proto, int nhs);
 
-bool is_gretap_dev(const struct net_device *dev);
-bool is_ip6gretap_dev(const struct net_device *dev);
+static inline bool netif_is_gretap(const struct net_device *dev)
+{
+       return dev->rtnl_link_ops &&
+              !strcmp(dev->rtnl_link_ops->kind, "gretap");
+}
+
+static inline bool netif_is_ip6gretap(const struct net_device *dev)
+{
+       return dev->rtnl_link_ops &&
+              !strcmp(dev->rtnl_link_ops->kind, "ip6gretap");
+}
 
 static inline int gre_calc_hlen(__be16 o_flags)
 {
index 3ca969c..975901a 100644 (file)
@@ -2,6 +2,8 @@
 #ifndef _INET_COMMON_H
 #define _INET_COMMON_H
 
+#include <linux/indirect_call_wrapper.h>
+
 extern const struct proto_ops inet_stream_ops;
 extern const struct proto_ops inet_dgram_ops;
 
@@ -54,4 +56,11 @@ static inline void inet_ctl_sock_destroy(struct sock *sk)
                sock_release(sk->sk_socket);
 }
 
+#define indirect_call_gro_receive(f2, f1, cb, head, skb)       \
+({                                                             \
+       unlikely(gro_recursion_inc_test(skb)) ?                 \
+               NAPI_GRO_CB(skb)->flush |= 1, NULL :            \
+               INDIRECT_CALL_2(cb, f2, f1, head, skb);         \
+})
+
 #endif
index db6b221..cbcf35c 100644 (file)
@@ -144,25 +144,6 @@ struct ip_tunnel {
        bool                    ignore_df;
 };
 
-#define TUNNEL_CSUM            __cpu_to_be16(0x01)
-#define TUNNEL_ROUTING         __cpu_to_be16(0x02)
-#define TUNNEL_KEY             __cpu_to_be16(0x04)
-#define TUNNEL_SEQ             __cpu_to_be16(0x08)
-#define TUNNEL_STRICT          __cpu_to_be16(0x10)
-#define TUNNEL_REC             __cpu_to_be16(0x20)
-#define TUNNEL_VERSION         __cpu_to_be16(0x40)
-#define TUNNEL_NO_KEY          __cpu_to_be16(0x80)
-#define TUNNEL_DONT_FRAGMENT    __cpu_to_be16(0x0100)
-#define TUNNEL_OAM             __cpu_to_be16(0x0200)
-#define TUNNEL_CRIT_OPT                __cpu_to_be16(0x0400)
-#define TUNNEL_GENEVE_OPT      __cpu_to_be16(0x0800)
-#define TUNNEL_VXLAN_OPT       __cpu_to_be16(0x1000)
-#define TUNNEL_NOCACHE         __cpu_to_be16(0x2000)
-#define TUNNEL_ERSPAN_OPT      __cpu_to_be16(0x4000)
-
-#define TUNNEL_OPTIONS_PRESENT \
-               (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT)
-
 struct tnl_ptk_info {
        __be16 flags;
        __be16 proto;
index 3832099..78fa0ac 100644 (file)
@@ -101,6 +101,17 @@ struct net_device *l3mdev_master_dev_rcu(const struct net_device *_dev)
        return master;
 }
 
+int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex);
+static inline
+int l3mdev_master_upper_ifindex_by_index(struct net *net, int ifindex)
+{
+       rcu_read_lock();
+       ifindex = l3mdev_master_upper_ifindex_by_index_rcu(net, ifindex);
+       rcu_read_unlock();
+
+       return ifindex;
+}
+
 u32 l3mdev_fib_table_rcu(const struct net_device *dev);
 u32 l3mdev_fib_table_by_index(struct net *net, int ifindex);
 static inline u32 l3mdev_fib_table(const struct net_device *dev)
@@ -207,6 +218,17 @@ static inline int l3mdev_master_ifindex_by_index(struct net *net, int ifindex)
        return 0;
 }
 
+static inline
+int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex)
+{
+       return 0;
+}
+static inline
+int l3mdev_master_upper_ifindex_by_index(struct net *net, int ifindex)
+{
+       return 0;
+}
+
 static inline
 struct net_device *l3mdev_master_dev_rcu(const struct net_device *dev)
 {
index 71985e9..88219cc 100644 (file)
@@ -467,7 +467,7 @@ struct ieee80211_mu_group_data {
 };
 
 /**
- * ieee80211_ftm_responder_params - FTM responder parameters
+ * struct ieee80211_ftm_responder_params - FTM responder parameters
  *
  * @lci: LCI subelement content
  * @civicloc: CIVIC location subelement content
@@ -496,6 +496,8 @@ struct ieee80211_ftm_responder_params {
  * @uora_ocw_range: UORA element's OCW Range field
  * @frame_time_rts_th: HE duration RTS threshold, in units of 32us
  * @he_support: does this BSS support HE
+ * @twt_requester: does this BSS support TWT requester (relevant for managed
+ *     mode only, set if the AP advertises TWT responder role)
  * @assoc: association status
  * @ibss_joined: indicates whether this station is part of an IBSS
  *     or not
@@ -594,6 +596,7 @@ struct ieee80211_bss_conf {
        u8 uora_ocw_range;
        u16 frame_time_rts_th;
        bool he_support;
+       bool twt_requester;
        /* association related data */
        bool assoc, ibss_joined;
        bool ibss_creator;
@@ -3239,6 +3242,11 @@ enum ieee80211_reconfig_type {
  *     When the scan finishes, ieee80211_scan_completed() must be called;
  *     note that it also must be called when the scan cannot finish due to
  *     any error unless this callback returned a negative error code.
+ *     This callback is also allowed to return the special return value 1,
+ *     this indicates that hardware scan isn't desirable right now and a
+ *     software scan should be done instead. A driver wishing to use this
+ *     capability must ensure its (hardware) scan capabilities aren't
+ *     advertised as more capable than mac80211's software scan is.
  *     The callback can sleep.
  *
  * @cancel_hw_scan: Ask the low-level tp cancel the active hw scan.
@@ -3623,6 +3631,9 @@ enum ieee80211_reconfig_type {
  *     skb is always a real frame, head may or may not be an A-MSDU.
  * @get_ftm_responder_stats: Retrieve FTM responder statistics, if available.
  *     Statistics should be cumulative, currently no way to reset is provided.
+ *
+ * @start_pmsr: start peer measurement (e.g. FTM) (this call can sleep)
+ * @abort_pmsr: abort peer measurement (this call can sleep)
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -3911,6 +3922,10 @@ struct ieee80211_ops {
        int (*get_ftm_responder_stats)(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
                                       struct cfg80211_ftm_responder_stats *ftm_stats);
+       int (*start_pmsr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                         struct cfg80211_pmsr_request *request);
+       void (*abort_pmsr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                          struct cfg80211_pmsr_request *request);
 };
 
 /**
@@ -6091,6 +6106,14 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
  * @txq: pointer obtained from station or virtual interface
  *
  * Returns the skb if successful, %NULL if no frame was available.
+ *
+ * Note that this must be called in an rcu_read_lock() critical section,
+ * which can only be released after the SKB was handled. Some pointers in
+ * skb->cb, e.g. the key pointer, are protected by by RCU and thus the
+ * critical section must persist not just for the duration of this call
+ * but for the duration of the frame handling.
+ * However, also note that while in the wake_tx_queue() method,
+ * rcu_read_lock() is already held.
  */
 struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
                                     struct ieee80211_txq *txq);
index f58b384..7c1ab9e 100644 (file)
@@ -140,8 +140,8 @@ struct neighbour {
        unsigned long           updated;
        rwlock_t                lock;
        refcount_t              refcnt;
-       struct sk_buff_head     arp_queue;
        unsigned int            arp_queue_len_bytes;
+       struct sk_buff_head     arp_queue;
        struct timer_list       timer;
        unsigned long           used;
        atomic_t                probes;
@@ -149,11 +149,13 @@ struct neighbour {
        __u8                    nud_state;
        __u8                    type;
        __u8                    dead;
+       u8                      protocol;
        seqlock_t               ha_lock;
-       unsigned char           ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))];
+       unsigned char           ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))] __aligned(8);
        struct hh_cache         hh;
        int                     (*output)(struct neighbour *, struct sk_buff *);
        const struct neigh_ops  *ops;
+       struct list_head        gc_list;
        struct rcu_head         rcu;
        struct net_device       *dev;
        u8                      primary_key[0];
@@ -172,6 +174,7 @@ struct pneigh_entry {
        possible_net_t          net;
        struct net_device       *dev;
        u8                      flags;
+       u8                      protocol;
        u8                      key[0];
 };
 
@@ -214,6 +217,8 @@ struct neigh_table {
        struct timer_list       proxy_timer;
        struct sk_buff_head     proxy_queue;
        atomic_t                entries;
+       atomic_t                gc_entries;
+       struct list_head        gc_list;
        rwlock_t                lock;
        unsigned long           last_rand;
        struct neigh_statistics __percpu *stats;
@@ -250,6 +255,7 @@ static inline void *neighbour_priv(const struct neighbour *n)
 #define NEIGH_UPDATE_F_ISROUTER                        0x40000000
 #define NEIGH_UPDATE_F_ADMIN                   0x80000000
 
+extern const struct nla_policy nda_policy[];
 
 static inline bool neigh_key_eq16(const struct neighbour *n, const void *pkey)
 {
@@ -454,6 +460,7 @@ static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb)
 
 static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb)
 {
+       unsigned int hh_alen = 0;
        unsigned int seq;
        unsigned int hh_len;
 
@@ -461,16 +468,33 @@ static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb
                seq = read_seqbegin(&hh->hh_lock);
                hh_len = hh->hh_len;
                if (likely(hh_len <= HH_DATA_MOD)) {
-                       /* this is inlined by gcc */
-                       memcpy(skb->data - HH_DATA_MOD, hh->hh_data, HH_DATA_MOD);
+                       hh_alen = HH_DATA_MOD;
+
+                       /* skb_push() would proceed silently if we have room for
+                        * the unaligned size but not for the aligned size:
+                        * check headroom explicitly.
+                        */
+                       if (likely(skb_headroom(skb) >= HH_DATA_MOD)) {
+                               /* this is inlined by gcc */
+                               memcpy(skb->data - HH_DATA_MOD, hh->hh_data,
+                                      HH_DATA_MOD);
+                       }
                } else {
-                       unsigned int hh_alen = HH_DATA_ALIGN(hh_len);
+                       hh_alen = HH_DATA_ALIGN(hh_len);
 
-                       memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);
+                       if (likely(skb_headroom(skb) >= hh_alen)) {
+                               memcpy(skb->data - hh_alen, hh->hh_data,
+                                      hh_alen);
+                       }
                }
        } while (read_seqretry(&hh->hh_lock, seq));
 
-       skb_push(skb, hh_len);
+       if (WARN_ON_ONCE(skb_headroom(skb) < hh_alen)) {
+               kfree_skb(skb);
+               return NET_XMIT_DROP;
+       }
+
+       __skb_push(skb, hh_len);
        return dev_queue_xmit(skb);
 }
 
@@ -528,24 +552,6 @@ static inline void neigh_ha_snapshot(char *dst, const struct neighbour *n,
        } while (read_seqretry(&n->ha_lock, seq));
 }
 
-static inline void neigh_update_ext_learned(struct neighbour *neigh, u32 flags,
-                                           int *notify)
-{
-       u8 ndm_flags = 0;
-
-       if (!(flags & NEIGH_UPDATE_F_ADMIN))
-               return;
-
-       ndm_flags |= (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0;
-       if ((neigh->flags ^ ndm_flags) & NTF_EXT_LEARNED) {
-               if (ndm_flags & NTF_EXT_LEARNED)
-                       neigh->flags |= NTF_EXT_LEARNED;
-               else
-                       neigh->flags &= ~NTF_EXT_LEARNED;
-               *notify = 1;
-       }
-}
-
 static inline void neigh_update_is_router(struct neighbour *neigh, u32 flags,
                                          int *notify)
 {
index 74af19c..4cd5680 100644 (file)
@@ -6,12 +6,12 @@
 
 static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
 {
-       skb->nf_bridge = kzalloc(sizeof(struct nf_bridge_info), GFP_ATOMIC);
+       struct nf_bridge_info *b = skb_ext_add(skb, SKB_EXT_BRIDGE_NF);
 
-       if (likely(skb->nf_bridge))
-               refcount_set(&(skb->nf_bridge->use), 1);
+       if (b)
+               memset(b, 0, sizeof(*b));
 
-       return skb->nf_bridge;
+       return b;
 }
 
 void nf_bridge_update_protocol(struct sk_buff *skb);
@@ -22,12 +22,6 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net, struct sock *sk,
                      int (*okfn)(struct net *, struct sock *,
                                  struct sk_buff *));
 
-static inline struct nf_bridge_info *
-nf_bridge_info_get(const struct sk_buff *skb)
-{
-       return skb->nf_bridge;
-}
-
 unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb);
 
 static inline void nf_bridge_push_encap_header(struct sk_buff *skb)
index cd24be4..13d5520 100644 (file)
@@ -9,7 +9,7 @@ nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum,
                       const struct nf_nat_range2 *range,
                       const struct net_device *out);
 
-void nf_nat_masquerade_ipv4_register_notifier(void);
+int nf_nat_masquerade_ipv4_register_notifier(void);
 void nf_nat_masquerade_ipv4_unregister_notifier(void);
 
 #endif /*_NF_NAT_MASQUERADE_IPV4_H_ */
index 0c3b5eb..2917bf9 100644 (file)
@@ -5,7 +5,7 @@
 unsigned int
 nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range,
                       const struct net_device *out);
-void nf_nat_masquerade_ipv6_register_notifier(void);
+int nf_nat_masquerade_ipv6_register_notifier(void);
 void nf_nat_masquerade_ipv6_unregister_notifier(void);
 
 #endif /* _NF_NAT_MASQUERADE_IPV6_H_ */
index 9991e5e..59f45b1 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/list.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
+#include <linux/rhashtable-types.h>
 #include <linux/xfrm.h>
 #include <net/dst_ops.h>
 
@@ -53,6 +54,7 @@ struct netns_xfrm {
        unsigned int            policy_count[XFRM_POLICY_MAX * 2];
        struct work_struct      policy_hash_work;
        struct xfrm_policy_hthresh policy_hthresh;
+       struct list_head        inexact_bins;
 
 
        struct sock             *nlsk;
index f6c0cd2..40965fb 100644 (file)
@@ -619,8 +619,8 @@ tcf_match_indev(struct sk_buff *skb, int ifindex)
 }
 #endif /* CONFIG_NET_CLS_IND */
 
-int tc_setup_cb_call(struct tcf_block *block, struct tcf_exts *exts,
-                    enum tc_setup_type type, void *type_data, bool err_stop);
+int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
+                    void *type_data, bool err_stop);
 
 enum tc_block_command {
        TC_BLOCK_BIND,
@@ -643,6 +643,7 @@ struct tc_cls_common_offload {
 
 struct tc_cls_u32_knode {
        struct tcf_exts *exts;
+       struct tcf_result *res;
        struct tc_u32_sel *sel;
        u32 handle;
        u32 val;
@@ -821,12 +822,21 @@ enum tc_mq_command {
        TC_MQ_CREATE,
        TC_MQ_DESTROY,
        TC_MQ_STATS,
+       TC_MQ_GRAFT,
+};
+
+struct tc_mq_opt_offload_graft_params {
+       unsigned long queue;
+       u32 child_handle;
 };
 
 struct tc_mq_qopt_offload {
        enum tc_mq_command command;
        u32 handle;
-       struct tc_qopt_offload_stats stats;
+       union {
+               struct tc_qopt_offload_stats stats;
+               struct tc_mq_opt_offload_graft_params graft_params;
+       };
 };
 
 enum tc_red_command {
@@ -834,12 +844,14 @@ enum tc_red_command {
        TC_RED_DESTROY,
        TC_RED_STATS,
        TC_RED_XSTATS,
+       TC_RED_GRAFT,
 };
 
 struct tc_red_qopt_offload_params {
        u32 min;
        u32 max;
        u32 probability;
+       u32 limit;
        bool is_ecn;
        bool is_harddrop;
        struct gnet_stats_queue *qstats;
@@ -853,6 +865,51 @@ struct tc_red_qopt_offload {
                struct tc_red_qopt_offload_params set;
                struct tc_qopt_offload_stats stats;
                struct red_stats *xstats;
+               u32 child_handle;
+       };
+};
+
+enum tc_gred_command {
+       TC_GRED_REPLACE,
+       TC_GRED_DESTROY,
+       TC_GRED_STATS,
+};
+
+struct tc_gred_vq_qopt_offload_params {
+       bool present;
+       u32 limit;
+       u32 prio;
+       u32 min;
+       u32 max;
+       bool is_ecn;
+       bool is_harddrop;
+       u32 probability;
+       /* Only need backlog, see struct tc_prio_qopt_offload_params */
+       u32 *backlog;
+};
+
+struct tc_gred_qopt_offload_params {
+       bool grio_on;
+       bool wred_on;
+       unsigned int dp_cnt;
+       unsigned int dp_def;
+       struct gnet_stats_queue *qstats;
+       struct tc_gred_vq_qopt_offload_params tab[MAX_DPs];
+};
+
+struct tc_gred_qopt_offload_stats {
+       struct gnet_stats_basic_packed bstats[MAX_DPs];
+       struct gnet_stats_queue qstats[MAX_DPs];
+       struct red_stats *xstats[MAX_DPs];
+};
+
+struct tc_gred_qopt_offload {
+       enum tc_gred_command command;
+       u32 handle;
+       u32 parent;
+       union {
+               struct tc_gred_qopt_offload_params set;
+               struct tc_gred_qopt_offload_stats stats;
        };
 };
 
@@ -889,4 +946,14 @@ struct tc_prio_qopt_offload {
        };
 };
 
+enum tc_root_command {
+       TC_ROOT_GRAFT,
+};
+
+struct tc_root_qopt_offload {
+       enum tc_root_command command;
+       u32 handle;
+       bool ingress;
+};
+
 #endif
index 8dadc74..4588bdc 100644 (file)
@@ -71,7 +71,7 @@ enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM };
                                         SCTP_NUM_AUTH_CHUNK_TYPES)
 
 /* These are the different flavours of event.  */
-enum sctp_event {
+enum sctp_event_type {
        SCTP_EVENT_T_CHUNK = 1,
        SCTP_EVENT_T_TIMEOUT,
        SCTP_EVENT_T_OTHER,
index 9a3b48a..1d13ec3 100644 (file)
@@ -152,7 +152,7 @@ int sctp_primitive_RECONF(struct net *net, struct sctp_association *asoc,
  */
 int sctp_rcv(struct sk_buff *skb);
 int sctp_v4_err(struct sk_buff *skb, u32 info);
-void sctp_hash_endpoint(struct sctp_endpoint *);
+int sctp_hash_endpoint(struct sctp_endpoint *ep);
 void sctp_unhash_endpoint(struct sctp_endpoint *);
 struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
                             struct sctphdr *, struct sctp_association **,
@@ -608,4 +608,21 @@ static inline __u32 sctp_dst_mtu(const struct dst_entry *dst)
                                 SCTP_DEFAULT_MINSEGMENT));
 }
 
+static inline bool sctp_transport_pmtu_check(struct sctp_transport *t)
+{
+       __u32 pmtu = sctp_dst_mtu(t->dst);
+
+       if (t->pathmtu == pmtu)
+               return true;
+
+       t->pathmtu = pmtu;
+
+       return false;
+}
+
+static inline __u32 sctp_min_frag_point(struct sctp_sock *sp, __u16 datasize)
+{
+       return sctp_mtu_payload(sp, SCTP_DEFAULT_MINSEGMENT, datasize);
+}
+
 #endif /* __net_sctp_h__ */
index 9e3d327..24825a8 100644 (file)
@@ -173,7 +173,7 @@ sctp_state_fn_t sctp_sf_autoclose_timer_expire;
 __u8 sctp_get_chunk_type(struct sctp_chunk *chunk);
 const struct sctp_sm_table_entry *sctp_sm_lookup_event(
                                        struct net *net,
-                                       enum sctp_event event_type,
+                                       enum sctp_event_type event_type,
                                        enum sctp_state state,
                                        union sctp_subtype event_subtype);
 int sctp_chunk_iif(const struct sctp_chunk *);
@@ -313,7 +313,7 @@ struct sctp_chunk *sctp_process_strreset_resp(
 
 /* Prototypes for statetable processing. */
 
-int sctp_do_sm(struct net *net, enum sctp_event event_type,
+int sctp_do_sm(struct net *net, enum sctp_event_type event_type,
               union sctp_subtype subtype, enum sctp_state state,
               struct sctp_endpoint *ep, struct sctp_association *asoc,
               void *event_arg, gfp_t gfp);
index a11f937..003020e 100644 (file)
@@ -96,7 +96,9 @@ struct sctp_stream;
 
 struct sctp_bind_bucket {
        unsigned short  port;
-       unsigned short  fastreuse;
+       signed char     fastreuse;
+       signed char     fastreuseport;
+       kuid_t          fastuid;
        struct hlist_node       node;
        struct hlist_head       owner;
        struct net      *net;
@@ -215,7 +217,7 @@ struct sctp_sock {
         * These two structures must be grouped together for the usercopy
         * whitelist region.
         */
-       struct sctp_event_subscribe subscribe;
+       __u16 subscribe;
        struct sctp_initmsg initmsg;
 
        int user_frag;
@@ -1190,6 +1192,8 @@ int sctp_bind_addr_conflict(struct sctp_bind_addr *, const union sctp_addr *,
                         struct sctp_sock *, struct sctp_sock *);
 int sctp_bind_addr_state(const struct sctp_bind_addr *bp,
                         const union sctp_addr *addr);
+int sctp_bind_addrs_check(struct sctp_sock *sp,
+                         struct sctp_sock *sp2, int cnt2);
 union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr  *bp,
                                        const union sctp_addr   *addrs,
                                        int                     addrcnt,
@@ -2073,8 +2077,12 @@ struct sctp_association {
 
        int sent_cnt_removable;
 
+       __u16 subscribe;
+
        __u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1];
        __u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1];
+
+       struct rcu_head rcu;
 };
 
 
index 51b4e06..bd922a0 100644 (file)
@@ -164,30 +164,39 @@ void sctp_ulpevent_read_nxtinfo(const struct sctp_ulpevent *event,
 
 __u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event);
 
+static inline void sctp_ulpevent_type_set(__u16 *subscribe,
+                                         __u16 sn_type, __u8 on)
+{
+       if (sn_type > SCTP_SN_TYPE_MAX)
+               return;
+
+       if (on)
+               *subscribe |=  (1 << (sn_type - SCTP_SN_TYPE_BASE));
+       else
+               *subscribe &= ~(1 << (sn_type - SCTP_SN_TYPE_BASE));
+}
+
 /* Is this event type enabled? */
-static inline int sctp_ulpevent_type_enabled(__u16 sn_type,
-                                            struct sctp_event_subscribe *mask)
+static inline bool sctp_ulpevent_type_enabled(__u16 subscribe, __u16 sn_type)
 {
-       int offset = sn_type - SCTP_SN_TYPE_BASE;
-       char *amask = (char *) mask;
+       if (sn_type > SCTP_SN_TYPE_MAX)
+               return false;
 
-       if (offset >= sizeof(struct sctp_event_subscribe))
-               return 0;
-       return amask[offset];
+       return subscribe & (1 << (sn_type - SCTP_SN_TYPE_BASE));
 }
 
 /* Given an event subscription, is this event enabled? */
-static inline int sctp_ulpevent_is_enabled(const struct sctp_ulpevent *event,
-                                          struct sctp_event_subscribe *mask)
+static inline bool sctp_ulpevent_is_enabled(const struct sctp_ulpevent *event,
+                                           __u16 subscribe)
 {
        __u16 sn_type;
-       int enabled = 1;
 
-       if (sctp_ulpevent_is_notification(event)) {
-               sn_type = sctp_ulpevent_get_notification_type(event);
-               enabled = sctp_ulpevent_type_enabled(sn_type, mask);
-       }
-       return enabled;
+       if (!sctp_ulpevent_is_notification(event))
+               return true;
+
+       sn_type = sctp_ulpevent_get_notification_type(event);
+
+       return sctp_ulpevent_type_enabled(subscribe, sn_type);
 }
 
 #endif /* __sctp_ulpevent_h__ */
index 2567941..8b2dc68 100644 (file)
@@ -16,7 +16,6 @@
 
 #include <linux/net.h>
 #include <linux/ipv6.h>
-#include <net/lwtunnel.h>
 #include <linux/seg6.h>
 #include <linux/rhashtable-types.h>
 
index f665d74..a6235c2 100644 (file)
@@ -1110,7 +1110,7 @@ struct proto {
        unsigned int            inuse_idx;
 #endif
 
-       bool                    (*stream_memory_free)(const struct sock *sk);
+       bool                    (*stream_memory_free)(const struct sock *sk, int wake);
        bool                    (*stream_memory_read)(const struct sock *sk);
        /* Memory pressure */
        void                    (*enter_memory_pressure)(struct sock *sk);
@@ -1192,19 +1192,29 @@ static inline void sk_refcnt_debug_release(const struct sock *sk)
 #define sk_refcnt_debug_release(sk) do { } while (0)
 #endif /* SOCK_REFCNT_DEBUG */
 
-static inline bool sk_stream_memory_free(const struct sock *sk)
+static inline bool __sk_stream_memory_free(const struct sock *sk, int wake)
 {
        if (sk->sk_wmem_queued >= sk->sk_sndbuf)
                return false;
 
        return sk->sk_prot->stream_memory_free ?
-               sk->sk_prot->stream_memory_free(sk) : true;
+               sk->sk_prot->stream_memory_free(sk, wake) : true;
 }
 
-static inline bool sk_stream_is_writeable(const struct sock *sk)
+static inline bool sk_stream_memory_free(const struct sock *sk)
+{
+       return __sk_stream_memory_free(sk, 0);
+}
+
+static inline bool __sk_stream_is_writeable(const struct sock *sk, int wake)
 {
        return sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) &&
-              sk_stream_memory_free(sk);
+              __sk_stream_memory_free(sk, wake);
+}
+
+static inline bool sk_stream_is_writeable(const struct sock *sk)
+{
+       return __sk_stream_is_writeable(sk, 0);
 }
 
 static inline int sk_under_cgroup_hierarchy(struct sock *sk,
@@ -2340,22 +2350,39 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk,
 void __sock_tx_timestamp(__u16 tsflags, __u8 *tx_flags);
 
 /**
- * sock_tx_timestamp - checks whether the outgoing packet is to be time stamped
+ * _sock_tx_timestamp - checks whether the outgoing packet is to be time stamped
  * @sk:                socket sending this packet
  * @tsflags:   timestamping flags to use
  * @tx_flags:  completed with instructions for time stamping
+ * @tskey:      filled in with next sk_tskey (not for TCP, which uses seqno)
  *
  * Note: callers should take care of initial ``*tx_flags`` value (usually 0)
  */
-static inline void sock_tx_timestamp(const struct sock *sk, __u16 tsflags,
-                                    __u8 *tx_flags)
+static inline void _sock_tx_timestamp(struct sock *sk, __u16 tsflags,
+                                     __u8 *tx_flags, __u32 *tskey)
 {
-       if (unlikely(tsflags))
+       if (unlikely(tsflags)) {
                __sock_tx_timestamp(tsflags, tx_flags);
+               if (tsflags & SOF_TIMESTAMPING_OPT_ID && tskey &&
+                   tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK)
+                       *tskey = sk->sk_tskey++;
+       }
        if (unlikely(sock_flag(sk, SOCK_WIFI_STATUS)))
                *tx_flags |= SKBTX_WIFI_STATUS;
 }
 
+static inline void sock_tx_timestamp(struct sock *sk, __u16 tsflags,
+                                    __u8 *tx_flags)
+{
+       _sock_tx_timestamp(sk, tsflags, tx_flags, NULL);
+}
+
+static inline void skb_setup_tx_timestamp(struct sk_buff *skb, __u16 tsflags)
+{
+       _sock_tx_timestamp(skb->sk, tsflags, &skb_shinfo(skb)->tx_flags,
+                          &skb_shinfo(skb)->tskey);
+}
+
 /**
  * sk_eat_skb - Release a skb if it is no longer needed
  * @sk: socket to eat this skb from
index 881ecb1..a7fdab5 100644 (file)
@@ -95,8 +95,8 @@ struct switchdev_obj_port_vlan {
        u16 vid_end;
 };
 
-#define SWITCHDEV_OBJ_PORT_VLAN(obj) \
-       container_of(obj, struct switchdev_obj_port_vlan, obj)
+#define SWITCHDEV_OBJ_PORT_VLAN(OBJ) \
+       container_of((OBJ), struct switchdev_obj_port_vlan, obj)
 
 /* SWITCHDEV_OBJ_ID_PORT_MDB */
 struct switchdev_obj_port_mdb {
@@ -105,8 +105,8 @@ struct switchdev_obj_port_mdb {
        u16 vid;
 };
 
-#define SWITCHDEV_OBJ_PORT_MDB(obj) \
-       container_of(obj, struct switchdev_obj_port_mdb, obj)
+#define SWITCHDEV_OBJ_PORT_MDB(OBJ) \
+       container_of((OBJ), struct switchdev_obj_port_mdb, obj)
 
 void switchdev_trans_item_enqueue(struct switchdev_trans *trans,
                                  void *data, void (*destructor)(void const *),
@@ -121,10 +121,6 @@ typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj);
  * @switchdev_port_attr_get: Get a port attribute (see switchdev_attr).
  *
  * @switchdev_port_attr_set: Set a port attribute (see switchdev_attr).
- *
- * @switchdev_port_obj_add: Add an object to port (see switchdev_obj_*).
- *
- * @switchdev_port_obj_del: Delete an object from port (see switchdev_obj_*).
  */
 struct switchdev_ops {
        int     (*switchdev_port_attr_get)(struct net_device *dev,
@@ -132,11 +128,6 @@ struct switchdev_ops {
        int     (*switchdev_port_attr_set)(struct net_device *dev,
                                           const struct switchdev_attr *attr,
                                           struct switchdev_trans *trans);
-       int     (*switchdev_port_obj_add)(struct net_device *dev,
-                                         const struct switchdev_obj *obj,
-                                         struct switchdev_trans *trans);
-       int     (*switchdev_port_obj_del)(struct net_device *dev,
-                                         const struct switchdev_obj *obj);
 };
 
 enum switchdev_notifier_type {
@@ -146,6 +137,11 @@ enum switchdev_notifier_type {
        SWITCHDEV_FDB_DEL_TO_DEVICE,
        SWITCHDEV_FDB_OFFLOADED,
 
+       SWITCHDEV_PORT_OBJ_ADD, /* Blocking. */
+       SWITCHDEV_PORT_OBJ_DEL, /* Blocking. */
+
+       SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE,
+       SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE,
        SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE,
        SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE,
        SWITCHDEV_VXLAN_FDB_OFFLOADED,
@@ -153,6 +149,7 @@ enum switchdev_notifier_type {
 
 struct switchdev_notifier_info {
        struct net_device *dev;
+       struct netlink_ext_ack *extack;
 };
 
 struct switchdev_notifier_fdb_info {
@@ -163,12 +160,25 @@ struct switchdev_notifier_fdb_info {
           offloaded:1;
 };
 
+struct switchdev_notifier_port_obj_info {
+       struct switchdev_notifier_info info; /* must be first */
+       const struct switchdev_obj *obj;
+       struct switchdev_trans *trans;
+       bool handled;
+};
+
 static inline struct net_device *
 switchdev_notifier_info_to_dev(const struct switchdev_notifier_info *info)
 {
        return info->dev;
 }
 
+static inline struct netlink_ext_ack *
+switchdev_notifier_info_to_extack(const struct switchdev_notifier_info *info)
+{
+       return info->extack;
+}
+
 #ifdef CONFIG_NET_SWITCHDEV
 
 void switchdev_deferred_process(void);
@@ -177,13 +187,22 @@ int switchdev_port_attr_get(struct net_device *dev,
 int switchdev_port_attr_set(struct net_device *dev,
                            const struct switchdev_attr *attr);
 int switchdev_port_obj_add(struct net_device *dev,
-                          const struct switchdev_obj *obj);
+                          const struct switchdev_obj *obj,
+                          struct netlink_ext_ack *extack);
 int switchdev_port_obj_del(struct net_device *dev,
                           const struct switchdev_obj *obj);
+
 int register_switchdev_notifier(struct notifier_block *nb);
 int unregister_switchdev_notifier(struct notifier_block *nb);
 int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
                             struct switchdev_notifier_info *info);
+
+int register_switchdev_blocking_notifier(struct notifier_block *nb);
+int unregister_switchdev_blocking_notifier(struct notifier_block *nb);
+int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
+                                     struct switchdev_notifier_info *info,
+                                     struct netlink_ext_ack *extack);
+
 void switchdev_port_fwd_mark_set(struct net_device *dev,
                                 struct net_device *group_dev,
                                 bool joining);
@@ -191,6 +210,19 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
 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),
+                       int (*add_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj,
+                                     struct switchdev_trans *trans,
+                                     struct netlink_ext_ack *extack));
+int switchdev_handle_port_obj_del(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*del_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj));
+
 #define SWITCHDEV_SET_OPS(netdev, ops) ((netdev)->switchdev_ops = (ops))
 #else
 
@@ -211,7 +243,8 @@ static inline int switchdev_port_attr_set(struct net_device *dev,
 }
 
 static inline int switchdev_port_obj_add(struct net_device *dev,
-                                        const struct switchdev_obj *obj)
+                                        const struct switchdev_obj *obj,
+                                        struct netlink_ext_ack *extack)
 {
        return -EOPNOTSUPP;
 }
@@ -239,12 +272,55 @@ static inline int call_switchdev_notifiers(unsigned long val,
        return NOTIFY_DONE;
 }
 
+static inline int
+register_switchdev_blocking_notifier(struct notifier_block *nb)
+{
+       return 0;
+}
+
+static inline int
+unregister_switchdev_blocking_notifier(struct notifier_block *nb)
+{
+       return 0;
+}
+
+static inline int
+call_switchdev_blocking_notifiers(unsigned long val,
+                                 struct net_device *dev,
+                                 struct switchdev_notifier_info *info,
+                                 struct netlink_ext_ack *extack)
+{
+       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,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*add_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj,
+                                     struct switchdev_trans *trans,
+                                     struct netlink_ext_ack *extack))
+{
+       return 0;
+}
+
+static inline int
+switchdev_handle_port_obj_del(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*del_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj))
+{
+       return 0;
+}
+
 #define SWITCHDEV_SET_OPS(netdev, ops) do {} while (0)
 
 #endif
index 4743836..e0a65c0 100644 (file)
@@ -1124,7 +1124,7 @@ void tcp_rate_check_app_limited(struct sock *sk);
  */
 static inline int tcp_is_sack(const struct tcp_sock *tp)
 {
-       return tp->rx_opt.sack_ok;
+       return likely(tp->rx_opt.sack_ok);
 }
 
 static inline bool tcp_is_reno(const struct tcp_sock *tp)
@@ -1315,33 +1315,16 @@ static inline __sum16 tcp_v4_check(int len, __be32 saddr,
        return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base);
 }
 
-static inline __sum16 __tcp_checksum_complete(struct sk_buff *skb)
-{
-       return __skb_checksum_complete(skb);
-}
-
 static inline bool tcp_checksum_complete(struct sk_buff *skb)
 {
        return !skb_csum_unnecessary(skb) &&
-               __tcp_checksum_complete(skb);
+               __skb_checksum_complete(skb);
 }
 
 bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb);
 int tcp_filter(struct sock *sk, struct sk_buff *skb);
-
-#undef STATE_TRACE
-
-#ifdef STATE_TRACE
-static const char *statename[]={
-       "Unused","Established","Syn Sent","Syn Recv",
-       "Fin Wait 1","Fin Wait 2","Time Wait", "Close",
-       "Close Wait","Last ACK","Listen","Closing"
-};
-#endif
 void tcp_set_state(struct sock *sk, int state);
-
 void tcp_done(struct sock *sk);
-
 int tcp_abort(struct sock *sk, int err);
 
 static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
@@ -1385,7 +1368,7 @@ static inline int tcp_win_from_space(const struct sock *sk, int space)
 /* Note: caller must be prepared to deal with negative returns */
 static inline int tcp_space(const struct sock *sk)
 {
-       return tcp_win_from_space(sk, sk->sk_rcvbuf -
+       return tcp_win_from_space(sk, sk->sk_rcvbuf - sk->sk_backlog.len -
                                  atomic_read(&sk->sk_rmem_alloc));
 }
 
@@ -1572,9 +1555,21 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
                                         const struct sock *addr_sk);
 
 #ifdef CONFIG_TCP_MD5SIG
-struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
-                                        const union tcp_md5_addr *addr,
-                                        int family);
+#include <linux/jump_label.h>
+extern struct static_key tcp_md5_needed;
+struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk,
+                                          const union tcp_md5_addr *addr,
+                                          int family);
+static inline struct tcp_md5sig_key *
+tcp_md5_do_lookup(const struct sock *sk,
+                 const union tcp_md5_addr *addr,
+                 int family)
+{
+       if (!static_key_false(&tcp_md5_needed))
+               return NULL;
+       return __tcp_md5_do_lookup(sk, addr, family);
+}
+
 #define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_key)
 #else
 static inline struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
@@ -1875,12 +1870,16 @@ static inline u32 tcp_notsent_lowat(const struct tcp_sock *tp)
        return tp->notsent_lowat ?: net->ipv4.sysctl_tcp_notsent_lowat;
 }
 
-static inline bool tcp_stream_memory_free(const struct sock *sk)
+/* @wake is one when sk_stream_write_space() calls us.
+ * This sends EPOLLOUT only if notsent_bytes is half the limit.
+ * This mimics the strategy used in sock_def_write_space().
+ */
+static inline bool tcp_stream_memory_free(const struct sock *sk, int wake)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
        u32 notsent_bytes = tp->write_seq - tp->snd_nxt;
 
-       return notsent_bytes < tcp_notsent_lowat(tp);
+       return (notsent_bytes << wake) < tcp_notsent_lowat(tp);
 }
 
 #ifdef CONFIG_PROC_FS
index bab5627..2a6ac8d 100644 (file)
  *
  * void (*unhash)(struct tls_device *device, struct sock *sk);
  *     This function cleans listen state set by Inline TLS driver
+ *
+ * void (*release)(struct kref *kref);
+ *     Release the registered device and allocated resources
+ * @kref: Number of reference to tls_device
  */
 struct tls_device {
        char name[TLS_DEVICE_NAME_MAX];
@@ -83,6 +87,8 @@ struct tls_device {
        int  (*feature)(struct tls_device *device);
        int  (*hash)(struct tls_device *device, struct sock *sk);
        void (*unhash)(struct tls_device *device, struct sock *sk);
+       void (*release)(struct kref *kref);
+       struct kref kref;
 };
 
 enum {
@@ -454,6 +460,15 @@ tls_offload_ctx_tx(const struct tls_context *tls_ctx)
        return (struct tls_offload_context_tx *)tls_ctx->priv_ctx_tx;
 }
 
+static inline bool tls_sw_has_ctx_tx(const struct sock *sk)
+{
+       struct tls_context *ctx = tls_get_ctx(sk);
+
+       if (!ctx)
+               return false;
+       return !!tls_sw_ctx_tx(ctx);
+}
+
 static inline struct tls_offload_context_rx *
 tls_offload_ctx_rx(const struct tls_context *tls_ctx)
 {
index dc8d804..b813795 100644 (file)
@@ -30,6 +30,7 @@ struct udp_port_cfg {
 
        __be16                  local_udp_port;
        __be16                  peer_udp_port;
+       int                     bind_ifindex;
        unsigned int            use_udp_checksums:1,
                                use_udp6_tx_checksums:1,
                                use_udp6_rx_checksums:1,
index ec999c4..236403e 100644 (file)
@@ -421,11 +421,16 @@ struct switchdev_notifier_vxlan_fdb_info {
        u8 eth_addr[ETH_ALEN];
        __be32 vni;
        bool offloaded;
+       bool added_by_user;
 };
 
 #if IS_ENABLED(CONFIG_VXLAN)
 int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
                      struct switchdev_notifier_vxlan_fdb_info *fdb_info);
+int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
+                    struct notifier_block *nb);
+void vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni);
+
 #else
 static inline int
 vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
@@ -433,6 +438,17 @@ vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
 {
        return -ENOENT;
 }
+
+static inline int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
+                                  struct notifier_block *nb)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline void
+vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni)
+{
+}
 #endif
 
 #endif
index 0eb390c..7298a53 100644 (file)
@@ -577,6 +577,7 @@ struct xfrm_policy {
        /* This lock only affects elements except for entry. */
        rwlock_t                lock;
        refcount_t              refcnt;
+       u32                     pos;
        struct timer_list       timer;
 
        atomic_t                genid;
@@ -589,6 +590,7 @@ struct xfrm_policy {
        struct xfrm_lifetime_cur curlft;
        struct xfrm_policy_walk_entry walk;
        struct xfrm_policy_queue polq;
+       bool                    bydst_reinsert;
        u8                      type;
        u8                      action;
        u8                      flags;
@@ -596,6 +598,7 @@ struct xfrm_policy {
        u16                     family;
        struct xfrm_sec_ctx     *security;
        struct xfrm_tmpl        xfrm_vec[XFRM_MAX_DEPTH];
+       struct hlist_node       bydst_inexact_list;
        struct rcu_head         rcu;
 };
 
@@ -1093,7 +1096,6 @@ struct xfrm_offload {
 };
 
 struct sec_path {
-       refcount_t              refcnt;
        int                     len;
        int                     olen;
 
@@ -1101,41 +1103,13 @@ struct sec_path {
        struct xfrm_offload     ovec[XFRM_MAX_OFFLOAD_DEPTH];
 };
 
-static inline int secpath_exists(struct sk_buff *skb)
-{
-#ifdef CONFIG_XFRM
-       return skb->sp != NULL;
-#else
-       return 0;
-#endif
-}
-
-static inline struct sec_path *
-secpath_get(struct sec_path *sp)
-{
-       if (sp)
-               refcount_inc(&sp->refcnt);
-       return sp;
-}
-
-void __secpath_destroy(struct sec_path *sp);
-
-static inline void
-secpath_put(struct sec_path *sp)
-{
-       if (sp && refcount_dec_and_test(&sp->refcnt))
-               __secpath_destroy(sp);
-}
-
-struct sec_path *secpath_dup(struct sec_path *src);
-int secpath_set(struct sk_buff *skb);
+struct sec_path *secpath_set(struct sk_buff *skb);
 
 static inline void
 secpath_reset(struct sk_buff *skb)
 {
 #ifdef CONFIG_XFRM
-       secpath_put(skb->sp);
-       skb->sp = NULL;
+       skb_ext_del(skb, SKB_EXT_SEC_PATH);
 #endif
 }
 
@@ -1191,7 +1165,7 @@ static inline int __xfrm_policy_check2(struct sock *sk, int dir,
        if (sk && sk->sk_policy[XFRM_POLICY_IN])
                return __xfrm_policy_check(sk, ndir, skb, family);
 
-       return  (!net->xfrm.policy_count[dir] && !skb->sp) ||
+       return  (!net->xfrm.policy_count[dir] && !secpath_exists(skb)) ||
                (skb_dst(skb)->flags & DST_NOPOLICY) ||
                __xfrm_policy_check(sk, ndir, skb, family);
 }
@@ -1552,6 +1526,7 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
                    int (*func)(struct xfrm_state *, int, void*), void *);
 void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net);
 struct xfrm_state *xfrm_state_alloc(struct net *net);
+void xfrm_state_free(struct xfrm_state *x);
 struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr,
                                   const xfrm_address_t *saddr,
                                   const struct flowi *fl,
@@ -1902,14 +1877,16 @@ static inline void xfrm_states_delete(struct xfrm_state **states, int n)
 #ifdef CONFIG_XFRM
 static inline struct xfrm_state *xfrm_input_state(struct sk_buff *skb)
 {
-       return skb->sp->xvec[skb->sp->len - 1];
+       struct sec_path *sp = skb_sec_path(skb);
+
+       return sp->xvec[sp->len - 1];
 }
 #endif
 
 static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb)
 {
 #ifdef CONFIG_XFRM
-       struct sec_path *sp = skb->sp;
+       struct sec_path *sp = skb_sec_path(skb);
 
        if (!sp || !sp->olen || sp->len != sp->olen)
                return NULL;
@@ -1967,7 +1944,7 @@ static inline void xfrm_dev_state_delete(struct xfrm_state *x)
 static inline void xfrm_dev_state_free(struct xfrm_state *x)
 {
        struct xfrm_state_offload *xso = &x->xso;
-        struct net_device *dev = xso->dev;
+       struct net_device *dev = xso->dev;
 
        if (dev && dev->xfrmdev_ops) {
                if (dev->xfrmdev_ops->xdo_dev_state_free)
index 70997ab..3fbd71c 100644 (file)
@@ -116,4 +116,8 @@ struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames,
 void dpaa2_io_store_destroy(struct dpaa2_io_store *s);
 struct dpaa2_dq *dpaa2_io_store_next(struct dpaa2_io_store *s, int *is_last);
 
+int dpaa2_io_query_fq_count(struct dpaa2_io *d, u32 fqid,
+                           u32 *fcnt, u32 *bcnt);
+int dpaa2_io_query_bp_count(struct dpaa2_io *d, u16 bpid,
+                           u32 *num);
 #endif /* __FSL_DPAA2_IO_H */
index 5687766..5cc7af0 100644 (file)
@@ -1205,8 +1205,10 @@ void qman_dqrr_get_ithresh(struct qman_portal *portal, u8 *ithresh);
  * qman_dqrr_set_ithresh - Set coalesce interrupt threshold
  * @portal: portal to set the new value on
  * @ithresh: new threshold value
+ *
+ * Returns 0 on success, or a negative error code.
  */
-void qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh);
+int qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh);
 
 /**
  * qman_dqrr_get_iperiod - Get coalesce interrupt period
@@ -1219,7 +1221,9 @@ void qman_portal_get_iperiod(struct qman_portal *portal, u32 *iperiod);
  * qman_dqrr_set_iperiod - Set coalesce interrupt period
  * @portal: portal to set the new value on
  * @ithresh: new period value
+ *
+ * Returns 0 on success, or a negative error code.
  */
-void qman_portal_set_iperiod(struct qman_portal *portal, u32 iperiod);
+int qman_portal_set_iperiod(struct qman_portal *portal, u32 iperiod);
 
 #endif /* __FSL_QMAN_H */
index 2dd37ca..888a833 100644 (file)
@@ -254,11 +254,13 @@ static inline int snd_interval_empty(const struct snd_interval *i)
 static inline int snd_interval_single(const struct snd_interval *i)
 {
        return (i->min == i->max || 
-               (i->min + 1 == i->max && i->openmax));
+               (i->min + 1 == i->max && (i->openmin || i->openmax)));
 }
 
 static inline int snd_interval_value(const struct snd_interval *i)
 {
+       if (i->openmin && !i->openmax)
+               return i->max;
        return i->min;
 }
 
index f1dab1f..70c10a8 100644 (file)
@@ -1192,7 +1192,7 @@ struct snd_soc_pcm_runtime {
             ((i) < rtd->num_codecs) && ((dai) = rtd->codec_dais[i]); \
             (i)++)
 #define for_each_rtd_codec_dai_rollback(rtd, i, dai)           \
-       for (; ((i--) >= 0) && ((dai) = rtd->codec_dais[i]);)
+       for (; ((--i) >= 0) && ((dai) = rtd->codec_dais[i]);)
 
 
 /* mixer control */
index a9834c3..c0e7d24 100644 (file)
@@ -31,8 +31,8 @@ TRACE_EVENT(kyber_latency,
 
        TP_fast_assign(
                __entry->dev            = disk_devt(dev_to_disk(kobj_to_dev(q->kobj.parent)));
-               strlcpy(__entry->domain, domain, DOMAIN_LEN);
-               strlcpy(__entry->type, type, DOMAIN_LEN);
+               strlcpy(__entry->domain, domain, sizeof(__entry->domain));
+               strlcpy(__entry->type, type, sizeof(__entry->type));
                __entry->percentile     = percentile;
                __entry->numerator      = numerator;
                __entry->denominator    = denominator;
@@ -60,7 +60,7 @@ TRACE_EVENT(kyber_adjust,
 
        TP_fast_assign(
                __entry->dev            = disk_devt(dev_to_disk(kobj_to_dev(q->kobj.parent)));
-               strlcpy(__entry->domain, domain, DOMAIN_LEN);
+               strlcpy(__entry->domain, domain, sizeof(__entry->domain));
                __entry->depth          = depth;
        ),
 
@@ -82,7 +82,7 @@ TRACE_EVENT(kyber_throttled,
 
        TP_fast_assign(
                __entry->dev            = disk_devt(dev_to_disk(kobj_to_dev(q->kobj.parent)));
-               strlcpy(__entry->domain, domain, DOMAIN_LEN);
+               strlcpy(__entry->domain, domain, sizeof(__entry->domain));
        ),
 
        TP_printk("%d,%d %s", MAJOR(__entry->dev), MINOR(__entry->dev),
index 00aa72c..1efd7d9 100644 (file)
@@ -244,6 +244,65 @@ DEFINE_EVENT(net_dev_rx_verbose_template, netif_rx_ni_entry,
        TP_ARGS(skb)
 );
 
+DECLARE_EVENT_CLASS(net_dev_rx_exit_template,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret),
+
+       TP_STRUCT__entry(
+               __field(int,    ret)
+       ),
+
+       TP_fast_assign(
+               __entry->ret = ret;
+       ),
+
+       TP_printk("ret=%d", __entry->ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, napi_gro_frags_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, napi_gro_receive_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, netif_receive_skb_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, netif_rx_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, netif_rx_ni_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, netif_receive_skb_list_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
 #endif /* _TRACE_NET_H */
 
 /* This part must be outside protection */
diff --git a/include/trace/events/objagg.h b/include/trace/events/objagg.h
new file mode 100644 (file)
index 0000000..fcec0fc
--- /dev/null
@@ -0,0 +1,228 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM objagg
+
+#if !defined(__TRACE_OBJAGG_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __TRACE_OBJAGG_H
+
+#include <linux/tracepoint.h>
+
+struct objagg;
+struct objagg_obj;
+
+TRACE_EVENT(objagg_create,
+       TP_PROTO(const struct objagg *objagg),
+
+       TP_ARGS(objagg),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+       ),
+
+       TP_printk("objagg %p", __entry->objagg)
+);
+
+TRACE_EVENT(objagg_destroy,
+       TP_PROTO(const struct objagg *objagg),
+
+       TP_ARGS(objagg),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+       ),
+
+       TP_printk("objagg %p", __entry->objagg)
+);
+
+TRACE_EVENT(objagg_obj_create,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj),
+
+       TP_ARGS(objagg, obj),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+       ),
+
+       TP_printk("objagg %p, obj %p", __entry->objagg, __entry->obj)
+);
+
+TRACE_EVENT(objagg_obj_destroy,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj),
+
+       TP_ARGS(objagg, obj),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+       ),
+
+       TP_printk("objagg %p, obj %p", __entry->objagg, __entry->obj)
+);
+
+TRACE_EVENT(objagg_obj_get,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj,
+                unsigned int refcount),
+
+       TP_ARGS(objagg, obj, refcount),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+               __field(unsigned int, refcount)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+               __entry->refcount = refcount;
+       ),
+
+       TP_printk("objagg %p, obj %p, refcount %u",
+                 __entry->objagg, __entry->obj, __entry->refcount)
+);
+
+TRACE_EVENT(objagg_obj_put,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj,
+                unsigned int refcount),
+
+       TP_ARGS(objagg, obj, refcount),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+               __field(unsigned int, refcount)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+               __entry->refcount = refcount;
+       ),
+
+       TP_printk("objagg %p, obj %p, refcount %u",
+                 __entry->objagg, __entry->obj, __entry->refcount)
+);
+
+TRACE_EVENT(objagg_obj_parent_assign,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj,
+                const struct objagg_obj *parent,
+                unsigned int parent_refcount),
+
+       TP_ARGS(objagg, obj, parent, parent_refcount),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+               __field(const void *, parent)
+               __field(unsigned int, parent_refcount)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+               __entry->parent = parent;
+               __entry->parent_refcount = parent_refcount;
+       ),
+
+       TP_printk("objagg %p, obj %p, parent %p, parent_refcount %u",
+                 __entry->objagg, __entry->obj,
+                 __entry->parent, __entry->parent_refcount)
+);
+
+TRACE_EVENT(objagg_obj_parent_unassign,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj,
+                const struct objagg_obj *parent,
+                unsigned int parent_refcount),
+
+       TP_ARGS(objagg, obj, parent, parent_refcount),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+               __field(const void *, parent)
+               __field(unsigned int, parent_refcount)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+               __entry->parent = parent;
+               __entry->parent_refcount = parent_refcount;
+       ),
+
+       TP_printk("objagg %p, obj %p, parent %p, parent_refcount %u",
+                 __entry->objagg, __entry->obj,
+                 __entry->parent, __entry->parent_refcount)
+);
+
+TRACE_EVENT(objagg_obj_root_create,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj),
+
+       TP_ARGS(objagg, obj),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+       ),
+
+       TP_printk("objagg %p, obj %p",
+                 __entry->objagg, __entry->obj)
+);
+
+TRACE_EVENT(objagg_obj_root_destroy,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj),
+
+       TP_ARGS(objagg, obj),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+       ),
+
+       TP_printk("objagg %p, obj %p",
+                 __entry->objagg, __entry->obj)
+);
+
+#endif /* __TRACE_OBJAGG_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 573d5b9..5b50fe4 100644 (file)
@@ -181,6 +181,7 @@ enum rxrpc_timer_trace {
 enum rxrpc_propose_ack_trace {
        rxrpc_propose_ack_client_tx_end,
        rxrpc_propose_ack_input_data,
+       rxrpc_propose_ack_ping_for_check_life,
        rxrpc_propose_ack_ping_for_keepalive,
        rxrpc_propose_ack_ping_for_lost_ack,
        rxrpc_propose_ack_ping_for_lost_reply,
@@ -380,6 +381,7 @@ enum rxrpc_tx_point {
 #define rxrpc_propose_ack_traces \
        EM(rxrpc_propose_ack_client_tx_end,     "ClTxEnd") \
        EM(rxrpc_propose_ack_input_data,        "DataIn ") \
+       EM(rxrpc_propose_ack_ping_for_check_life, "ChkLife") \
        EM(rxrpc_propose_ack_ping_for_keepalive, "KeepAlv") \
        EM(rxrpc_propose_ack_ping_for_lost_ack, "LostAck") \
        EM(rxrpc_propose_ack_ping_for_lost_reply, "LostRpl") \
index f07b270..9a4bdfa 100644 (file)
@@ -107,6 +107,8 @@ DEFINE_EVENT(sched_wakeup_template, sched_wakeup_new,
 #ifdef CREATE_TRACE_POINTS
 static inline long __trace_sched_switch_state(bool preempt, struct task_struct *p)
 {
+       unsigned int state;
+
 #ifdef CONFIG_SCHED_DEBUG
        BUG_ON(p != current);
 #endif /* CONFIG_SCHED_DEBUG */
@@ -118,7 +120,15 @@ static inline long __trace_sched_switch_state(bool preempt, struct task_struct *
        if (preempt)
                return TASK_REPORT_MAX;
 
-       return 1 << task_state_index(p);
+       /*
+        * task_state_index() uses fls() and returns a value from 0-8 range.
+        * Decrement it by 1 (except TASK_RUNNING state i.e 0) before using
+        * it for left shift operation to get the correct task->state
+        * mapping.
+        */
+       state = task_state_index(p);
+
+       return state ? (1 << (state - 1)) : state;
 }
 #endif /* CREATE_TRACE_POINTS */
 
index 2138144..355c4ac 100644 (file)
@@ -3,6 +3,7 @@
 #
 mandatory-y += auxvec.h
 mandatory-y += bitsperlong.h
+mandatory-y += bpf_perf_event.h
 mandatory-y += byteorder.h
 mandatory-y += errno.h
 mandatory-y += fcntl.h
index 538546e..c7f3321 100644 (file)
@@ -760,8 +760,10 @@ __SYSCALL(__NR_rseq, sys_rseq)
 #define __NR_ftruncate __NR3264_ftruncate
 #define __NR_lseek __NR3264_lseek
 #define __NR_sendfile __NR3264_sendfile
+#if defined(__ARCH_WANT_NEW_STAT) || defined(__ARCH_WANT_STAT64)
 #define __NR_newfstatat __NR3264_fstatat
 #define __NR_fstat __NR3264_fstat
+#endif
 #define __NR_mmap __NR3264_mmap
 #define __NR_fadvise64 __NR3264_fadvise64
 #ifdef __NR3264_stat
@@ -776,8 +778,10 @@ __SYSCALL(__NR_rseq, sys_rseq)
 #define __NR_ftruncate64 __NR3264_ftruncate
 #define __NR_llseek __NR3264_lseek
 #define __NR_sendfile64 __NR3264_sendfile
+#if defined(__ARCH_WANT_NEW_STAT) || defined(__ARCH_WANT_STAT64)
 #define __NR_fstatat64 __NR3264_fstatat
 #define __NR_fstat64 __NR3264_fstat
+#endif
 #define __NR_mmap2 __NR3264_mmap
 #define __NR_fadvise64_64 __NR3264_fadvise64
 #ifdef __NR3264_stat
index 8f08ff9..6fa38d0 100644 (file)
@@ -141,7 +141,7 @@ struct blk_zone_range {
  */
 #define BLKREPORTZONE  _IOWR(0x12, 130, struct blk_zone_report)
 #define BLKRESETZONE   _IOW(0x12, 131, struct blk_zone_range)
-#define BLKGETZONESZ   _IOW(0x12, 132, __u32)
-#define BLKGETNRZONES  _IOW(0x12, 133, __u32)
+#define BLKGETZONESZ   _IOR(0x12, 132, __u32)
+#define BLKGETNRZONES  _IOR(0x12, 133, __u32)
 
 #endif /* _UAPI_BLKZONED_H */
index 852dc17..91c4388 100644 (file)
@@ -133,6 +133,14 @@ enum bpf_map_type {
        BPF_MAP_TYPE_STACK,
 };
 
+/* Note that tracing related programs such as
+ * BPF_PROG_TYPE_{KPROBE,TRACEPOINT,PERF_EVENT,RAW_TRACEPOINT}
+ * are not subject to a stable API since kernel internal data
+ * structures can change from release to release and may
+ * therefore break existing tracing BPF programs. Tracing BPF
+ * programs correspond to /a/ specific kernel which is to be
+ * analyzed, and not /a/ specific kernel /and/ all future ones.
+ */
 enum bpf_prog_type {
        BPF_PROG_TYPE_UNSPEC,
        BPF_PROG_TYPE_SOCKET_FILTER,
@@ -232,6 +240,20 @@ enum bpf_attach_type {
  */
 #define BPF_F_STRICT_ALIGNMENT (1U << 0)
 
+/* If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the
+ * verifier will allow any alignment whatsoever.  On platforms
+ * with strict alignment requirements for loads ands stores (such
+ * as sparc and mips) the verifier validates that all loads and
+ * stores provably follow this requirement.  This flag turns that
+ * checking and enforcement off.
+ *
+ * It is mostly used for testing when we want to validate the
+ * context and memory access aspects of the verifier, but because
+ * of an unaligned access the alignment check would trigger before
+ * the one we are interested in.
+ */
+#define BPF_F_ANY_ALIGNMENT    (1U << 1)
+
 /* when bpf_ldimm64->src_reg == BPF_PSEUDO_MAP_FD, bpf_ldimm64->imm == fd */
 #define BPF_PSEUDO_MAP_FD      1
 
@@ -257,9 +279,6 @@ enum bpf_attach_type {
 /* Specify numa node during map creation */
 #define BPF_F_NUMA_NODE                (1U << 2)
 
-/* flags for BPF_PROG_QUERY */
-#define BPF_F_QUERY_EFFECTIVE  (1U << 0)
-
 #define BPF_OBJ_NAME_LEN 16U
 
 /* Flags for accessing BPF object */
@@ -269,6 +288,12 @@ enum bpf_attach_type {
 /* Flag for stack_map, store build_id+offset instead of pointer */
 #define BPF_F_STACK_BUILD_ID   (1U << 5)
 
+/* Zero-initialize hash function seed. This should only be used for testing. */
+#define BPF_F_ZERO_SEED                (1U << 6)
+
+/* flags for BPF_PROG_QUERY */
+#define BPF_F_QUERY_EFFECTIVE  (1U << 0)
+
 enum bpf_stack_build_id_status {
        /* user space need an empty entry to identify end of a trace */
        BPF_STACK_BUILD_ID_EMPTY = 0,
@@ -326,7 +351,7 @@ union bpf_attr {
                __u32           log_level;      /* verbosity level of verifier */
                __u32           log_size;       /* size of user buffer */
                __aligned_u64   log_buf;        /* user supplied buffer */
-               __u32           kern_version;   /* checked when prog_type=kprobe */
+               __u32           kern_version;   /* not used */
                __u32           prog_flags;
                char            prog_name[BPF_OBJ_NAME_LEN];
                __u32           prog_ifindex;   /* ifindex of netdev to prep for */
@@ -335,6 +360,13 @@ union bpf_attr {
                 * (context accesses, allowed helpers, etc).
                 */
                __u32           expected_attach_type;
+               __u32           prog_btf_fd;    /* fd pointing to BTF type data */
+               __u32           func_info_rec_size;     /* userspace bpf_func_info size */
+               __aligned_u64   func_info;      /* func info */
+               __u32           func_info_cnt;  /* number of bpf_func_info records */
+               __u32           line_info_rec_size;     /* userspace bpf_line_info size */
+               __aligned_u64   line_info;      /* line info */
+               __u32           line_info_cnt;  /* number of bpf_line_info records */
        };
 
        struct { /* anonymous struct used by BPF_OBJ_* commands */
@@ -353,8 +385,11 @@ union bpf_attr {
        struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */
                __u32           prog_fd;
                __u32           retval;
-               __u32           data_size_in;
-               __u32           data_size_out;
+               __u32           data_size_in;   /* input: len of data_in */
+               __u32           data_size_out;  /* input/output: len of data_out
+                                                *   returns ENOSPC if data_out
+                                                *   is too small.
+                                                */
                __aligned_u64   data_in;
                __aligned_u64   data_out;
                __u32           repeat;
@@ -475,18 +510,6 @@ union bpf_attr {
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
- * int bpf_map_pop_elem(struct bpf_map *map, void *value)
- *     Description
- *             Pop an element from *map*.
- * Return
- *             0 on success, or a negative error in case of failure.
- *
- * int bpf_map_peek_elem(struct bpf_map *map, void *value)
- *     Description
- *             Get an element from *map* without removing it.
- * Return
- *             0 on success, or a negative error in case of failure.
- *
  * int bpf_probe_read(void *dst, u32 size, const void *src)
  *     Description
  *             For tracing programs, safely attempt to read *size* bytes from
@@ -1910,9 +1933,9 @@ union bpf_attr {
  *             is set to metric from route (IPv4/IPv6 only), and ifindex
  *             is set to the device index of the nexthop from the FIB lookup.
  *
- *             *plen* argument is the size of the passed in struct.
- *             *flags* argument can be a combination of one or more of the
- *             following values:
+ *             *plen* argument is the size of the passed in struct.
+ *             *flags* argument can be a combination of one or more of the
+ *             following values:
  *
  *             **BPF_FIB_LOOKUP_DIRECT**
  *                     Do a direct table lookup vs full lookup using FIB
@@ -1921,9 +1944,9 @@ union bpf_attr {
  *                     Perform lookup from an egress perspective (default is
  *                     ingress).
  *
- *             *ctx* is either **struct xdp_md** for XDP programs or
- *             **struct sk_buff** tc cls_act programs.
- *     Return
+ *             *ctx* is either **struct xdp_md** for XDP programs or
+ *             **struct sk_buff** tc cls_act programs.
+ *     Return
  *             * < 0 if any input argument is invalid
  *             *   0 on success (packet is forwarded, nexthop neighbor exists)
  *             * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
@@ -2068,8 +2091,8 @@ union bpf_attr {
  *             translated to a keycode using the rc keymap, and reported as
  *             an input key down event. After a period a key up event is
  *             generated. This period can be extended by calling either
- *             **bpf_rc_keydown** () again with the same values, or calling
- *             **bpf_rc_repeat** ().
+ *             **bpf_rc_keydown**\ () again with the same values, or calling
+ *             **bpf_rc_repeat**\ ().
  *
  *             Some protocols include a toggle bit, in case the button was
  *             released and pressed again between consecutive scancodes.
@@ -2152,29 +2175,30 @@ union bpf_attr {
  *             The *flags* meaning is specific for each map type,
  *             and has to be 0 for cgroup local storage.
  *
- *             Depending on the bpf program type, a local storage area
- *             can be shared between multiple instances of the bpf program,
+ *             Depending on the BPF program type, a local storage area
+ *             can be shared between multiple instances of the BPF program,
  *             running simultaneously.
  *
  *             A user should care about the synchronization by himself.
- *             For example, by using the BPF_STX_XADD instruction to alter
+ *             For example, by using the **BPF_STX_XADD** instruction to alter
  *             the shared data.
  *     Return
- *             Pointer to the local storage area.
+ *             A pointer to the local storage area.
  *
  * int bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags)
  *     Description
- *             Select a SO_REUSEPORT sk from a BPF_MAP_TYPE_REUSEPORT_ARRAY map
- *             It checks the selected sk is matching the incoming
- *             request in the skb.
+ *             Select a **SO_REUSEPORT** socket from a
+ *             **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*.
+ *             It checks the selected socket is matching the incoming
+ *             request in the socket buffer.
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
- * struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u32 netns, u64 flags)
+ * struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
  *     Description
  *             Look for TCP socket matching *tuple*, optionally in a child
  *             network namespace *netns*. The return value must be checked,
- *             and if non-NULL, released via **bpf_sk_release**\ ().
+ *             and if non-**NULL**, released via **bpf_sk_release**\ ().
  *
  *             The *ctx* should point to the context of the program, such as
  *             the skb or socket (depending on the hook in use). This is used
@@ -2187,12 +2211,14 @@ union bpf_attr {
  *             **sizeof**\ (*tuple*\ **->ipv6**)
  *                     Look for an IPv6 socket.
  *
- *             If the *netns* is zero, then the socket lookup table in the
- *             netns associated with the *ctx* will be used. For the TC hooks,
- *             this in the netns of the device in the skb. For socket hooks,
- *             this in the netns of the socket. If *netns* is non-zero, then
- *             it specifies the ID of the netns relative to the netns
- *             associated with the *ctx*.
+ *             If the *netns* is a negative signed 32-bit integer, then the
+ *             socket lookup table in the netns associated with the *ctx* will
+ *             will be used. For the TC hooks, this is the netns of the device
+ *             in the skb. For socket hooks, this is the netns of the socket.
+ *             If *netns* is any other signed 32-bit value greater than or
+ *             equal to zero then it specifies the ID of the netns relative to
+ *             the netns associated with the *ctx*. *netns* values beyond the
+ *             range of 32-bit integers are reserved for future use.
  *
  *             All values for *flags* are reserved for future usage, and must
  *             be left at zero.
@@ -2200,13 +2226,15 @@ union bpf_attr {
  *             This helper is available only if the kernel was compiled with
  *             **CONFIG_NET** configuration option.
  *     Return
- *             Pointer to *struct bpf_sock*, or NULL in case of failure.
+ *             Pointer to **struct bpf_sock**, or **NULL** in case of failure.
+ *             For sockets with reuseport option, the **struct bpf_sock**
+ *             result is from **reuse->socks**\ [] using the hash of the tuple.
  *
- * struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u32 netns, u64 flags)
+ * struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
  *     Description
  *             Look for UDP socket matching *tuple*, optionally in a child
  *             network namespace *netns*. The return value must be checked,
- *             and if non-NULL, released via **bpf_sk_release**\ ().
+ *             and if non-**NULL**, released via **bpf_sk_release**\ ().
  *
  *             The *ctx* should point to the context of the program, such as
  *             the skb or socket (depending on the hook in use). This is used
@@ -2219,12 +2247,14 @@ union bpf_attr {
  *             **sizeof**\ (*tuple*\ **->ipv6**)
  *                     Look for an IPv6 socket.
  *
- *             If the *netns* is zero, then the socket lookup table in the
- *             netns associated with the *ctx* will be used. For the TC hooks,
- *             this in the netns of the device in the skb. For socket hooks,
- *             this in the netns of the socket. If *netns* is non-zero, then
- *             it specifies the ID of the netns relative to the netns
- *             associated with the *ctx*.
+ *             If the *netns* is a negative signed 32-bit integer, then the
+ *             socket lookup table in the netns associated with the *ctx* will
+ *             will be used. For the TC hooks, this is the netns of the device
+ *             in the skb. For socket hooks, this is the netns of the socket.
+ *             If *netns* is any other signed 32-bit value greater than or
+ *             equal to zero then it specifies the ID of the netns relative to
+ *             the netns associated with the *ctx*. *netns* values beyond the
+ *             range of 32-bit integers are reserved for future use.
  *
  *             All values for *flags* are reserved for future usage, and must
  *             be left at zero.
@@ -2232,31 +2262,71 @@ union bpf_attr {
  *             This helper is available only if the kernel was compiled with
  *             **CONFIG_NET** configuration option.
  *     Return
- *             Pointer to *struct bpf_sock*, or NULL in case of failure.
+ *             Pointer to **struct bpf_sock**, or **NULL** in case of failure.
+ *             For sockets with reuseport option, the **struct bpf_sock**
+ *             result is from **reuse->socks**\ [] using the hash of the tuple.
  *
- * int bpf_sk_release(struct bpf_sock *sk)
+ * int bpf_sk_release(struct bpf_sock *sock)
  *     Description
- *             Release the reference held by *sock*. *sock* must be a non-NULL
- *             pointer that was returned from bpf_sk_lookup_xxx\ ().
+ *             Release the reference held by *sock*. *sock* must be a
+ *             non-**NULL** pointer that was returned from
+ *             **bpf_sk_lookup_xxx**\ ().
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
+ * int bpf_map_pop_elem(struct bpf_map *map, void *value)
+ *     Description
+ *             Pop an element from *map*.
+ *     Return
+ *             0 on success, or a negative error in case of failure.
+ *
+ * int bpf_map_peek_elem(struct bpf_map *map, void *value)
+ *     Description
+ *             Get an element from *map* without removing it.
+ *     Return
+ *             0 on success, or a negative error in case of failure.
+ *
  * int bpf_msg_push_data(struct sk_buff *skb, u32 start, u32 len, u64 flags)
  *     Description
- *             For socket policies, insert *len* bytes into msg at offset
+ *             For socket policies, insert *len* bytes into *msg* at offset
  *             *start*.
  *
  *             If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a
- *             *msg* it may want to insert metadata or options into the msg.
+ *             *msg* it may want to insert metadata or options into the *msg*.
  *             This can later be read and used by any of the lower layer BPF
  *             hooks.
  *
  *             This helper may fail if under memory pressure (a malloc
  *             fails) in these cases BPF programs will get an appropriate
  *             error and BPF programs will need to handle them.
+ *     Return
+ *             0 on success, or a negative error in case of failure.
  *
+ * int bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 pop, u64 flags)
+ *     Description
+ *             Will remove *pop* bytes from a *msg* starting at byte *start*.
+ *             This may result in **ENOMEM** errors under certain situations if
+ *             an allocation and copy are required due to a full ring buffer.
+ *             However, the helper will try to avoid doing the allocation
+ *             if possible. Other errors can occur if input parameters are
+ *             invalid either due to *start* byte not being valid part of *msg*
+ *             payload and/or *pop* value being to large.
  *     Return
  *             0 on success, or a negative error in case of failure.
+ *
+ * int bpf_rc_pointer_rel(void *ctx, s32 rel_x, s32 rel_y)
+ *     Description
+ *             This helper is used in programs implementing IR decoding, to
+ *             report a successfully decoded pointer movement.
+ *
+ *             The *ctx* should point to the lirc sample as passed into
+ *             the program.
+ *
+ *             This helper is only available is the kernel was compiled with
+ *             the **CONFIG_BPF_LIRC_MODE2** configuration option set to
+ *             "**y**".
+ *     Return
+ *             0
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -2349,7 +2419,9 @@ union bpf_attr {
        FN(map_push_elem),              \
        FN(map_pop_elem),               \
        FN(map_peek_elem),              \
-       FN(msg_push_data),
+       FN(msg_push_data),              \
+       FN(msg_pop_data),               \
+       FN(rc_pointer_rel),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -2405,6 +2477,9 @@ enum bpf_func_id {
 /* BPF_FUNC_perf_event_output for sk_buff input context. */
 #define BPF_F_CTXLEN_MASK              (0xfffffULL << 32)
 
+/* Current network namespace */
+#define BPF_F_CURRENT_NETNS            (-1L)
+
 /* Mode for BPF_FUNC_skb_adjust_room helper. */
 enum bpf_adj_room_mode {
        BPF_ADJ_ROOM_NET,
@@ -2422,6 +2497,12 @@ enum bpf_lwt_encap_mode {
        BPF_LWT_ENCAP_SEG6_INLINE
 };
 
+#define __bpf_md_ptr(type, name)       \
+union {                                        \
+       type name;                      \
+       __u64 :64;                      \
+} __attribute__((aligned(8)))
+
 /* user accessible mirror of in-kernel sk_buff.
  * new fields can only be added to the end of this structure
  */
@@ -2456,7 +2537,9 @@ struct __sk_buff {
        /* ... here. */
 
        __u32 data_meta;
-       struct bpf_flow_keys *flow_keys;
+       __bpf_md_ptr(struct bpf_flow_keys *, flow_keys);
+       __u64 tstamp;
+       __u32 wire_len;
 };
 
 struct bpf_tunnel_key {
@@ -2572,8 +2655,8 @@ enum sk_action {
  * be added to the end of this structure
  */
 struct sk_msg_md {
-       void *data;
-       void *data_end;
+       __bpf_md_ptr(void *, data);
+       __bpf_md_ptr(void *, data_end);
 
        __u32 family;
        __u32 remote_ip4;       /* Stored in network byte order */
@@ -2582,6 +2665,7 @@ struct sk_msg_md {
        __u32 local_ip6[4];     /* Stored in network byte order */
        __u32 remote_port;      /* Stored in network byte order */
        __u32 local_port;       /* stored in host byte order */
+       __u32 size;             /* Total size of sk_msg */
 };
 
 struct sk_reuseport_md {
@@ -2589,8 +2673,9 @@ struct sk_reuseport_md {
         * Start of directly accessible data. It begins from
         * the tcp/udp header.
         */
-       void *data;
-       void *data_end;         /* End of directly accessible data */
+       __bpf_md_ptr(void *, data);
+       /* End of directly accessible data */
+       __bpf_md_ptr(void *, data_end);
        /*
         * Total length of packet (starting from the tcp/udp header).
         * Note that the directly accessible bytes (data_end - data)
@@ -2631,6 +2716,18 @@ struct bpf_prog_info {
        __u32 nr_jited_func_lens;
        __aligned_u64 jited_ksyms;
        __aligned_u64 jited_func_lens;
+       __u32 btf_id;
+       __u32 func_info_rec_size;
+       __aligned_u64 func_info;
+       __u32 nr_func_info;
+       __u32 nr_line_info;
+       __aligned_u64 line_info;
+       __aligned_u64 jited_line_info;
+       __u32 nr_jited_line_info;
+       __u32 line_info_rec_size;
+       __u32 jited_line_info_rec_size;
+       __u32 nr_prog_tags;
+       __aligned_u64 prog_tags;
 } __attribute__((aligned(8)));
 
 struct bpf_map_info {
@@ -2942,4 +3039,19 @@ struct bpf_flow_keys {
        };
 };
 
+struct bpf_func_info {
+       __u32   insn_off;
+       __u32   type_id;
+};
+
+#define BPF_LINE_INFO_LINE_NUM(line_col)       ((line_col) >> 10)
+#define BPF_LINE_INFO_LINE_COL(line_col)       ((line_col) & 0x3ff)
+
+struct bpf_line_info {
+       __u32   insn_off;
+       __u32   file_name_off;
+       __u32   line_off;
+       __u32   line_col;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
index 972265f..7b7475e 100644 (file)
@@ -34,13 +34,16 @@ struct btf_type {
         * bits  0-15: vlen (e.g. # of struct's members)
         * bits 16-23: unused
         * bits 24-27: kind (e.g. int, ptr, array...etc)
-        * bits 28-31: unused
+        * bits 28-30: unused
+        * bit     31: kind_flag, currently used by
+        *             struct, union and fwd
         */
        __u32 info;
        /* "size" is used by INT, ENUM, STRUCT and UNION.
         * "size" tells the size of the type it is describing.
         *
-        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
+        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
+        * FUNC and FUNC_PROTO.
         * "type" is a type_id referring to another type.
         */
        union {
@@ -51,6 +54,7 @@ struct btf_type {
 
 #define BTF_INFO_KIND(info)    (((info) >> 24) & 0x0f)
 #define BTF_INFO_VLEN(info)    ((info) & 0xffff)
+#define BTF_INFO_KFLAG(info)   ((info) >> 31)
 
 #define BTF_KIND_UNKN          0       /* Unknown      */
 #define BTF_KIND_INT           1       /* Integer      */
@@ -64,8 +68,10 @@ struct btf_type {
 #define BTF_KIND_VOLATILE      9       /* Volatile     */
 #define BTF_KIND_CONST         10      /* Const        */
 #define BTF_KIND_RESTRICT      11      /* Restrict     */
-#define BTF_KIND_MAX           11
-#define NR_BTF_KINDS           12
+#define BTF_KIND_FUNC          12      /* Function     */
+#define BTF_KIND_FUNC_PROTO    13      /* Function Proto       */
+#define BTF_KIND_MAX           13
+#define NR_BTF_KINDS           14
 
 /* For some specific BTF_KIND, "struct btf_type" is immediately
  * followed by extra data.
@@ -107,7 +113,29 @@ struct btf_array {
 struct btf_member {
        __u32   name_off;
        __u32   type;
-       __u32   offset; /* offset in bits */
+       /* If the type info kind_flag is set, the btf_member offset
+        * contains both member bitfield size and bit offset. The
+        * bitfield size is set for bitfield members. If the type
+        * info kind_flag is not set, the offset contains only bit
+        * offset.
+        */
+       __u32   offset;
+};
+
+/* If the struct/union type info kind_flag is set, the
+ * following two macros are used to access bitfield_size
+ * and bit_offset from btf_member.offset.
+ */
+#define BTF_MEMBER_BITFIELD_SIZE(val)  ((val) >> 24)
+#define BTF_MEMBER_BIT_OFFSET(val)     ((val) & 0xffffff)
+
+/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
+ * The exact number of btf_param is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_param {
+       __u32   name_off;
+       __u32   type;
 };
 
 #endif /* _UAPI__LINUX_BTF_H__ */
index 79407bb..6e52d36 100644 (file)
@@ -163,6 +163,11 @@ enum devlink_param_cmode {
        DEVLINK_PARAM_CMODE_MAX = __DEVLINK_PARAM_CMODE_MAX - 1
 };
 
+enum devlink_param_fw_load_policy_value {
+       DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
+       DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
+};
+
 enum devlink_attr {
        /* don't change the order or add anything between, this is ABI! */
        DEVLINK_ATTR_UNSPEC,
index c8f8e24..17be76a 100644 (file)
@@ -882,7 +882,7 @@ struct ethtool_rx_flow_spec {
        __u32           location;
 };
 
-/* How rings are layed out when accessing virtual functions or
+/* How rings are laid out when accessing virtual functions or
  * offloaded queues is device specific. To allow users to do flow
  * steering and specify these queues the ring cookie is partitioned
  * into a 32bit queue index with an 8 bit virtual function id.
@@ -891,7 +891,7 @@ struct ethtool_rx_flow_spec {
  * devices start supporting PCIe w/ARI. However at the moment I
  * do not know of any devices that support this so I do not reserve
  * space for this at this time. If a future patch consumes the next
- * byte it should be aware of this possiblity.
+ * byte it should be aware of this possibility.
  */
 #define ETHTOOL_RX_FLOW_SPEC_RING      0x00000000FFFFFFFFLL
 #define ETHTOOL_RX_FLOW_SPEC_RING_VF   0x000000FF00000000LL
index e41eda3..773e476 100644 (file)
@@ -292,4 +292,25 @@ struct br_mcast_stats {
        __u64 mcast_bytes[BR_MCAST_DIR_SIZE];
        __u64 mcast_packets[BR_MCAST_DIR_SIZE];
 };
+
+/* bridge boolean options
+ * BR_BOOLOPT_NO_LL_LEARN - disable learning from link-local packets
+ *
+ * IMPORTANT: if adding a new option do not forget to handle
+ *            it in br_boolopt_toggle/get and bridge sysfs
+ */
+enum br_boolopt_id {
+       BR_BOOLOPT_NO_LL_LEARN,
+       BR_BOOLOPT_MAX
+};
+
+/* struct br_boolopt_multi - change multiple bridge boolean options
+ *
+ * @optval: new option values (bit per option)
+ * @optmask: options to change (bit per option)
+ */
+struct br_boolopt_multi {
+       __u32 optval;
+       __u32 optmask;
+};
 #endif /* _UAPI_LINUX_IF_BRIDGE_H */
index f42c069..d653382 100644 (file)
@@ -288,6 +288,7 @@ enum {
        IFLA_BR_MCAST_IGMP_VERSION,
        IFLA_BR_MCAST_MLD_VERSION,
        IFLA_BR_VLAN_STATS_PER_PORT,
+       IFLA_BR_MULTI_BOOLOPT,
        __IFLA_BR_MAX,
 };
 
index ee432cd..23a6753 100644 (file)
@@ -59,6 +59,7 @@
 #define TUNGETVNETBE _IOR('T', 223, int)
 #define TUNSETSTEERINGEBPF _IOR('T', 224, int)
 #define TUNSETFILTEREBPF _IOR('T', 225, int)
+#define TUNSETCARRIER _IOW('T', 226, int)
 
 /* TUNSETIFF ifr flags */
 #define IFF_TUN                0x0001
index 1b3d148..7d91055 100644 (file)
@@ -160,4 +160,24 @@ enum {
 };
 
 #define IFLA_VTI_MAX   (__IFLA_VTI_MAX - 1)
+
+#define TUNNEL_CSUM            __cpu_to_be16(0x01)
+#define TUNNEL_ROUTING         __cpu_to_be16(0x02)
+#define TUNNEL_KEY             __cpu_to_be16(0x04)
+#define TUNNEL_SEQ             __cpu_to_be16(0x08)
+#define TUNNEL_STRICT          __cpu_to_be16(0x10)
+#define TUNNEL_REC             __cpu_to_be16(0x20)
+#define TUNNEL_VERSION         __cpu_to_be16(0x40)
+#define TUNNEL_NO_KEY          __cpu_to_be16(0x80)
+#define TUNNEL_DONT_FRAGMENT    __cpu_to_be16(0x0100)
+#define TUNNEL_OAM             __cpu_to_be16(0x0200)
+#define TUNNEL_CRIT_OPT                __cpu_to_be16(0x0400)
+#define TUNNEL_GENEVE_OPT      __cpu_to_be16(0x0800)
+#define TUNNEL_VXLAN_OPT       __cpu_to_be16(0x1000)
+#define TUNNEL_NOCACHE         __cpu_to_be16(0x2000)
+#define TUNNEL_ERSPAN_OPT      __cpu_to_be16(0x4000)
+
+#define TUNNEL_OPTIONS_PRESENT \
+               (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT)
+
 #endif /* _UAPI_IF_TUNNEL_H_ */
index 48e8a22..f6052e7 100644 (file)
@@ -266,10 +266,14 @@ struct sockaddr_in {
 
 #define        IN_CLASSD(a)            ((((long int) (a)) & 0xf0000000) == 0xe0000000)
 #define        IN_MULTICAST(a)         IN_CLASSD(a)
-#define IN_MULTICAST_NET       0xF0000000
+#define        IN_MULTICAST_NET        0xe0000000
 
-#define        IN_EXPERIMENTAL(a)      ((((long int) (a)) & 0xf0000000) == 0xf0000000)
-#define        IN_BADCLASS(a)          IN_EXPERIMENTAL((a))
+#define        IN_BADCLASS(a)          ((((long int) (a) ) == 0xffffffff)
+#define        IN_EXPERIMENTAL(a)      IN_BADCLASS((a))
+
+#define        IN_CLASSE(a)            ((((long int) (a)) & 0xf0000000) == 0xf0000000)
+#define        IN_CLASSE_NET           0xffffffff
+#define        IN_CLASSE_NSHIFT        0
 
 /* Address to accept any incoming messages. */
 #define        INADDR_ANY              ((unsigned long int) 0x00000000)
index 6d180cc..ae366b8 100644 (file)
  * the situation described above.
  */
 #define REL_RESERVED           0x0a
-#define REL_WHEEL_HI_RES       0x0b
 #define REL_MAX                        0x0f
 #define REL_CNT                        (REL_MAX+1)
 
index 0a26a55..a3f87c5 100644 (file)
  * @NCSI_CMD_SEND_CMD: send NC-SI command to network card.
  *     Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID
  *     and NCSI_ATTR_CHANNEL_ID.
+ * @NCSI_CMD_SET_PACKAGE_MASK: set a whitelist of allowed packages.
+ *     Requires NCSI_ATTR_IFINDEX and NCSI_ATTR_PACKAGE_MASK.
+ * @NCSI_CMD_SET_CHANNEL_MASK: set a whitelist of allowed channels.
+ *     Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID, and
+ *     NCSI_ATTR_CHANNEL_MASK. If NCSI_ATTR_CHANNEL_ID is present it sets
+ *     the primary channel.
  * @NCSI_CMD_MAX: highest command number
  */
 enum ncsi_nl_commands {
@@ -34,6 +40,8 @@ enum ncsi_nl_commands {
        NCSI_CMD_SET_INTERFACE,
        NCSI_CMD_CLEAR_INTERFACE,
        NCSI_CMD_SEND_CMD,
+       NCSI_CMD_SET_PACKAGE_MASK,
+       NCSI_CMD_SET_CHANNEL_MASK,
 
        __NCSI_CMD_AFTER_LAST,
        NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1
@@ -48,6 +56,10 @@ enum ncsi_nl_commands {
  * @NCSI_ATTR_PACKAGE_ID: package ID
  * @NCSI_ATTR_CHANNEL_ID: channel ID
  * @NCSI_ATTR_DATA: command payload
+ * @NCSI_ATTR_MULTI_FLAG: flag to signal that multi-mode should be enabled with
+ *     NCSI_CMD_SET_PACKAGE_MASK or NCSI_CMD_SET_CHANNEL_MASK.
+ * @NCSI_ATTR_PACKAGE_MASK: 32-bit mask of allowed packages.
+ * @NCSI_ATTR_CHANNEL_MASK: 32-bit mask of allowed channels.
  * @NCSI_ATTR_MAX: highest attribute number
  */
 enum ncsi_nl_attrs {
@@ -57,6 +69,9 @@ enum ncsi_nl_attrs {
        NCSI_ATTR_PACKAGE_ID,
        NCSI_ATTR_CHANNEL_ID,
        NCSI_ATTR_DATA,
+       NCSI_ATTR_MULTI_FLAG,
+       NCSI_ATTR_PACKAGE_MASK,
+       NCSI_ATTR_CHANNEL_MASK,
 
        __NCSI_ATTR_AFTER_LAST,
        NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1
index 9981554..cd144e3 100644 (file)
@@ -28,6 +28,7 @@ enum {
        NDA_MASTER,
        NDA_LINK_NETNSID,
        NDA_SRC_VNI,
+       NDA_PROTOCOL,  /* Originator of entry */
        __NDA_MAX
 };
 
index 0187c74..9f99568 100644 (file)
@@ -16,6 +16,8 @@ enum {
        NETNSA_NSID,
        NETNSA_PID,
        NETNSA_FD,
+       NETNSA_TARGET_NSID,
+       NETNSA_CURRENT_NSID,
        __NETNSA_MAX,
 };
 
index 97ff3c1..e5b3972 100644 (file)
@@ -155,8 +155,8 @@ enum txtime_flags {
 };
 
 struct sock_txtime {
-       clockid_t       clockid;        /* reference clockid */
-       __u32           flags;          /* as defined by enum txtime_flags */
+       __kernel_clockid_t      clockid;/* reference clockid */
+       __u32                   flags;  /* as defined by enum txtime_flags */
 };
 
 #endif /* _NET_TIMESTAMPING_H */
index 486ed1f..0a4d733 100644 (file)
@@ -155,7 +155,7 @@ enum nlmsgerr_attrs {
 #define NETLINK_LIST_MEMBERSHIPS       9
 #define NETLINK_CAP_ACK                        10
 #define NETLINK_EXT_ACK                        11
-#define NETLINK_DUMP_STRICT_CHK                12
+#define NETLINK_GET_STRICT_CHK         12
 
 struct nl_pktinfo {
        __u32   group;
index 6d610ba..31ae5c7 100644 (file)
  * @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in
  *     the %NL80211_ATTR_FTM_RESPONDER_STATS attribute.
  *
+ * @NL80211_CMD_PEER_MEASUREMENT_START: start a (set of) peer measurement(s)
+ *     with the given parameters, which are encapsulated in the nested
+ *     %NL80211_ATTR_PEER_MEASUREMENTS attribute. Optionally, MAC address
+ *     randomization may be enabled and configured by specifying the
+ *     %NL80211_ATTR_MAC and %NL80211_ATTR_MAC_MASK attributes.
+ *     If a timeout is requested, use the %NL80211_ATTR_TIMEOUT attribute.
+ *     A u64 cookie for further %NL80211_ATTR_COOKIE use is is returned in
+ *     the netlink extended ack message.
+ *
+ *     To cancel a measurement, close the socket that requested it.
+ *
+ *     Measurement results are reported to the socket that requested the
+ *     measurement using @NL80211_CMD_PEER_MEASUREMENT_RESULT when they
+ *     become available, so applications must ensure a large enough socket
+ *     buffer size.
+ *
+ *     Depending on driver support it may or may not be possible to start
+ *     multiple concurrent measurements.
+ * @NL80211_CMD_PEER_MEASUREMENT_RESULT: This command number is used for the
+ *     result notification from the driver to the requesting socket.
+ * @NL80211_CMD_PEER_MEASUREMENT_COMPLETE: Notification only, indicating that
+ *     the measurement completed, using the measurement cookie
+ *     (%NL80211_ATTR_COOKIE).
+ *
+ * @NL80211_CMD_NOTIFY_RADAR: Notify the kernel that a radar signal was
+ *     detected and reported by a neighboring device on the channel
+ *     indicated by %NL80211_ATTR_WIPHY_FREQ and other attributes
+ *     determining the width and type.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1250,6 +1279,12 @@ enum nl80211_commands {
 
        NL80211_CMD_GET_FTM_RESPONDER_STATS,
 
+       NL80211_CMD_PEER_MEASUREMENT_START,
+       NL80211_CMD_PEER_MEASUREMENT_RESULT,
+       NL80211_CMD_PEER_MEASUREMENT_COMPLETE,
+
+       NL80211_CMD_NOTIFY_RADAR,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1706,7 +1741,7 @@ enum nl80211_commands {
  *     the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID
  *     is included in the probe request, but the match attributes
  *     will never let it go through), -EINVAL may be returned.
- *     If ommited, no filtering is done.
+ *     If omitted, no filtering is done.
  *
  * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
  *     interface combinations. In each nested item, it contains attributes
@@ -1811,7 +1846,7 @@ enum nl80211_commands {
  *
  * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
  *     used by the drivers which has MLME in firmware and does not have support
- *     to report per station tx/rx activity to free up the staion entry from
+ *     to report per station tx/rx activity to free up the station entry from
  *     the list. This needs to be used when the driver advertises the
  *     capability to timeout the stations.
  *
@@ -2172,7 +2207,7 @@ enum nl80211_commands {
  *
  * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
  *     the specified band is to be adjusted before doing
- *     %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out
+ *     %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparison to figure out
  *     better BSSs. The attribute value is a packed structure
  *     value as specified by &struct nl80211_bss_select_rssi_adjust.
  *
@@ -2254,6 +2289,16 @@ enum nl80211_commands {
  * @NL80211_ATTR_FTM_RESPONDER_STATS: Nested attribute with FTM responder
  *     statistics, see &enum nl80211_ftm_responder_stats.
  *
+ * @NL80211_ATTR_TIMEOUT: Timeout for the given operation in milliseconds (u32),
+ *     if the attribute is not given no timeout is requested. Note that 0 is an
+ *     invalid value.
+ *
+ * @NL80211_ATTR_PEER_MEASUREMENTS: peer measurements request (and result)
+ *     data, uses nested attributes specified in
+ *     &enum nl80211_peer_measurement_attrs.
+ *     This is also used for capability advertisement in the wiphy information,
+ *     with the appropriate sub-attributes.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2699,6 +2744,10 @@ enum nl80211_attrs {
 
        NL80211_ATTR_FTM_RESPONDER_STATS,
 
+       NL80211_ATTR_TIMEOUT,
+
+       NL80211_ATTR_PEER_MEASUREMENTS,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3074,6 +3123,8 @@ enum nl80211_sta_bss_param {
  *     with an FCS error (u32, from this station). This count may not include
  *     some packets with an FCS error due to TA corruption. Hence this counter
  *     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_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -3116,6 +3167,7 @@ enum nl80211_sta_info {
        NL80211_STA_INFO_ACK_SIGNAL_AVG,
        NL80211_STA_INFO_RX_MPDUS,
        NL80211_STA_INFO_FCS_ERROR_COUNT,
+       NL80211_STA_INFO_CONNECTED_TO_GATE,
 
        /* keep last */
        __NL80211_STA_INFO_AFTER_LAST,
@@ -3895,6 +3947,11 @@ enum nl80211_mesh_power_mode {
  *     remove it from the STA's list of peers. You may set this to 0 to disable
  *     the removal of the STA. Default is 30 minutes.
  *
+ * @NL80211_MESHCONF_CONNECTED_TO_GATE: If set to true then this mesh STA
+ *     will advertise that it is connected to a gate in the mesh formation
+ *     field.  If left unset then the mesh formation field will only
+ *     advertise such if there is an active root mesh path.
+ *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_meshconf_params {
@@ -3927,6 +3984,7 @@ enum nl80211_meshconf_params {
        NL80211_MESHCONF_POWER_MODE,
        NL80211_MESHCONF_AWAKE_WINDOW,
        NL80211_MESHCONF_PLINK_TIMEOUT,
+       NL80211_MESHCONF_CONNECTED_TO_GATE,
 
        /* keep last */
        __NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -4859,7 +4917,7 @@ enum nl80211_iface_limit_attrs {
  *     numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
  *     => allows a STA plus three P2P interfaces
  *
- * The list of these four possiblities could completely be contained
+ * The list of these four possibilities could completely be contained
  * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate
  * that any of these groups must match.
  *
@@ -4889,7 +4947,7 @@ enum nl80211_if_combination_attrs {
  * enum nl80211_plink_state - state of a mesh peer link finite state machine
  *
  * @NL80211_PLINK_LISTEN: initial state, considered the implicit
- *     state of non existant mesh peer links
+ *     state of non existent mesh peer links
  * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
  *     this mesh peer
  * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
@@ -5381,7 +5439,7 @@ enum nl80211_timeout_reason {
  *     request parameters IE in the probe request
  * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
  * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
- *     rate of at least 5.5M. In case non OCE AP is dicovered in the channel,
+ *     rate of at least 5.5M. In case non OCE AP is discovered in the channel,
  *     only the first probe req in the channel will be sent in high rate.
  * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
  *     tx deferral (dot11FILSProbeDelay shall be set to 15ms)
@@ -5842,9 +5900,11 @@ enum nl80211_external_auth_action {
  * @__NL80211_FTM_RESP_ATTR_INVALID: Invalid
  * @NL80211_FTM_RESP_ATTR_ENABLED: FTM responder is enabled
  * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element
- *     (9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10)
+ *     (9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10),
+ *     i.e. starting with the measurement token
  * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element
- *     (9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13)
+ *     (9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13),
+ *     i.e. starting with the measurement token
  * @__NL80211_FTM_RESP_ATTR_LAST: Internal
  * @NL80211_FTM_RESP_ATTR_MAX: highest FTM responder attribute.
  */
@@ -5906,4 +5966,386 @@ enum nl80211_ftm_responder_stats {
        NL80211_FTM_STATS_MAX = __NL80211_FTM_STATS_AFTER_LAST - 1
 };
 
+/**
+ * enum nl80211_preamble - frame preamble types
+ * @NL80211_PREAMBLE_LEGACY: legacy (HR/DSSS, OFDM, ERP PHY) preamble
+ * @NL80211_PREAMBLE_HT: HT preamble
+ * @NL80211_PREAMBLE_VHT: VHT preamble
+ * @NL80211_PREAMBLE_DMG: DMG preamble
+ */
+enum nl80211_preamble {
+       NL80211_PREAMBLE_LEGACY,
+       NL80211_PREAMBLE_HT,
+       NL80211_PREAMBLE_VHT,
+       NL80211_PREAMBLE_DMG,
+};
+
+/**
+ * enum nl80211_peer_measurement_type - peer measurement types
+ * @NL80211_PMSR_TYPE_INVALID: invalid/unused, needed as we use
+ *     these numbers also for attributes
+ *
+ * @NL80211_PMSR_TYPE_FTM: flight time measurement
+ *
+ * @NUM_NL80211_PMSR_TYPES: internal
+ * @NL80211_PMSR_TYPE_MAX: highest type number
+ */
+enum nl80211_peer_measurement_type {
+       NL80211_PMSR_TYPE_INVALID,
+
+       NL80211_PMSR_TYPE_FTM,
+
+       NUM_NL80211_PMSR_TYPES,
+       NL80211_PMSR_TYPE_MAX = NUM_NL80211_PMSR_TYPES - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_status - peer measurement status
+ * @NL80211_PMSR_STATUS_SUCCESS: measurement completed successfully
+ * @NL80211_PMSR_STATUS_REFUSED: measurement was locally refused
+ * @NL80211_PMSR_STATUS_TIMEOUT: measurement timed out
+ * @NL80211_PMSR_STATUS_FAILURE: measurement failed, a type-dependent
+ *     reason may be available in the response data
+ */
+enum nl80211_peer_measurement_status {
+       NL80211_PMSR_STATUS_SUCCESS,
+       NL80211_PMSR_STATUS_REFUSED,
+       NL80211_PMSR_STATUS_TIMEOUT,
+       NL80211_PMSR_STATUS_FAILURE,
+};
+
+/**
+ * enum nl80211_peer_measurement_req - peer measurement request attributes
+ * @__NL80211_PMSR_REQ_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_REQ_ATTR_DATA: This is a nested attribute with measurement
+ *     type-specific request data inside. The attributes used are from the
+ *     enums named nl80211_peer_measurement_<type>_req.
+ * @NL80211_PMSR_REQ_ATTR_GET_AP_TSF: include AP TSF timestamp, if supported
+ *     (flag attribute)
+ *
+ * @NUM_NL80211_PMSR_REQ_ATTRS: internal
+ * @NL80211_PMSR_REQ_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_req {
+       __NL80211_PMSR_REQ_ATTR_INVALID,
+
+       NL80211_PMSR_REQ_ATTR_DATA,
+       NL80211_PMSR_REQ_ATTR_GET_AP_TSF,
+
+       /* keep last */
+       NUM_NL80211_PMSR_REQ_ATTRS,
+       NL80211_PMSR_REQ_ATTR_MAX = NUM_NL80211_PMSR_REQ_ATTRS - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_resp - peer measurement response attributes
+ * @__NL80211_PMSR_RESP_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_RESP_ATTR_DATA: This is a nested attribute with measurement
+ *     type-specific results inside. The attributes used are from the enums
+ *     named nl80211_peer_measurement_<type>_resp.
+ * @NL80211_PMSR_RESP_ATTR_STATUS: u32 value with the measurement status
+ *     (using values from &enum nl80211_peer_measurement_status.)
+ * @NL80211_PMSR_RESP_ATTR_HOST_TIME: host time (%CLOCK_BOOTTIME) when the
+ *     result was measured; this value is not expected to be accurate to
+ *     more than 20ms. (u64, nanoseconds)
+ * @NL80211_PMSR_RESP_ATTR_AP_TSF: TSF of the AP that the interface
+ *     doing the measurement is connected to when the result was measured.
+ *     This shall be accurately reported if supported and requested
+ *     (u64, usec)
+ * @NL80211_PMSR_RESP_ATTR_FINAL: If results are sent to the host partially
+ *     (*e.g. with FTM per-burst data) this flag will be cleared on all but
+ *     the last result; if all results are combined it's set on the single
+ *     result.
+ * @NL80211_PMSR_RESP_ATTR_PAD: padding for 64-bit attributes, ignore
+ *
+ * @NUM_NL80211_PMSR_RESP_ATTRS: internal
+ * @NL80211_PMSR_RESP_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_resp {
+       __NL80211_PMSR_RESP_ATTR_INVALID,
+
+       NL80211_PMSR_RESP_ATTR_DATA,
+       NL80211_PMSR_RESP_ATTR_STATUS,
+       NL80211_PMSR_RESP_ATTR_HOST_TIME,
+       NL80211_PMSR_RESP_ATTR_AP_TSF,
+       NL80211_PMSR_RESP_ATTR_FINAL,
+       NL80211_PMSR_RESP_ATTR_PAD,
+
+       /* keep last */
+       NUM_NL80211_PMSR_RESP_ATTRS,
+       NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement
+ * @__NL80211_PMSR_PEER_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_PEER_ATTR_ADDR: peer's MAC address
+ * @NL80211_PMSR_PEER_ATTR_CHAN: channel definition, nested, using top-level
+ *     attributes like %NL80211_ATTR_WIPHY_FREQ etc.
+ * @NL80211_PMSR_PEER_ATTR_REQ: This is a nested attribute indexed by
+ *     measurement type, with attributes from the
+ *     &enum nl80211_peer_measurement_req inside.
+ * @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by
+ *     measurement type, with attributes from the
+ *     &enum nl80211_peer_measurement_resp inside.
+ *
+ * @NUM_NL80211_PMSR_PEER_ATTRS: internal
+ * @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_peer_attrs {
+       __NL80211_PMSR_PEER_ATTR_INVALID,
+
+       NL80211_PMSR_PEER_ATTR_ADDR,
+       NL80211_PMSR_PEER_ATTR_CHAN,
+       NL80211_PMSR_PEER_ATTR_REQ,
+       NL80211_PMSR_PEER_ATTR_RESP,
+
+       /* keep last */
+       NUM_NL80211_PMSR_PEER_ATTRS,
+       NL80211_PMSR_PEER_ATTR_MAX = NUM_NL80211_PMSR_PEER_ATTRS - 1,
+};
+
+/**
+ * enum nl80211_peer_measurement_attrs - peer measurement attributes
+ * @__NL80211_PMSR_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_ATTR_MAX_PEERS: u32 attribute used for capability
+ *     advertisement only, indicates the maximum number of peers
+ *     measurements can be done with in a single request
+ * @NL80211_PMSR_ATTR_REPORT_AP_TSF: flag attribute in capability
+ *     indicating that the connected AP's TSF can be reported in
+ *     measurement results
+ * @NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR: flag attribute in capability
+ *     indicating that MAC address randomization is supported.
+ * @NL80211_PMSR_ATTR_TYPE_CAPA: capabilities reported by the device,
+ *     this contains a nesting indexed by measurement type, and
+ *     type-specific capabilities inside, which are from the enums
+ *     named nl80211_peer_measurement_<type>_capa.
+ * @NL80211_PMSR_ATTR_PEERS: nested attribute, the nesting index is
+ *     meaningless, just a list of peers to measure with, with the
+ *     sub-attributes taken from
+ *     &enum nl80211_peer_measurement_peer_attrs.
+ *
+ * @NUM_NL80211_PMSR_ATTR: internal
+ * @NL80211_PMSR_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_attrs {
+       __NL80211_PMSR_ATTR_INVALID,
+
+       NL80211_PMSR_ATTR_MAX_PEERS,
+       NL80211_PMSR_ATTR_REPORT_AP_TSF,
+       NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR,
+       NL80211_PMSR_ATTR_TYPE_CAPA,
+       NL80211_PMSR_ATTR_PEERS,
+
+       /* keep last */
+       NUM_NL80211_PMSR_ATTR,
+       NL80211_PMSR_ATTR_MAX = NUM_NL80211_PMSR_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_capa - FTM capabilities
+ * @__NL80211_PMSR_FTM_CAPA_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_CAPA_ATTR_ASAP: flag attribute indicating ASAP mode
+ *     is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP: flag attribute indicating non-ASAP
+ *     mode is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI: flag attribute indicating if LCI
+ *     data can be requested during the measurement
+ * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC: flag attribute indicating if civic
+ *     location data can be requested during the measurement
+ * @NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES: u32 bitmap attribute of bits
+ *     from &enum nl80211_preamble.
+ * @NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS: bitmap of values from
+ *     &enum nl80211_chan_width indicating the supported channel
+ *     bandwidths for FTM. Note that a higher channel bandwidth may be
+ *     configured to allow for other measurements types with different
+ *     bandwidth requirement in the same measurement.
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT: u32 attribute indicating
+ *     the maximum bursts exponent that can be used (if not present anything
+ *     is valid)
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST: u32 attribute indicating
+ *     the maximum FTMs per burst (if not present anything is valid)
+ *
+ * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_capa {
+       __NL80211_PMSR_FTM_CAPA_ATTR_INVALID,
+
+       NL80211_PMSR_FTM_CAPA_ATTR_ASAP,
+       NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP,
+       NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI,
+       NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC,
+       NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES,
+       NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS,
+       NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT,
+       NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST,
+
+       /* keep last */
+       NUM_NL80211_PMSR_FTM_CAPA_ATTR,
+       NL80211_PMSR_FTM_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_CAPA_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_req - FTM request attributes
+ * @__NL80211_PMSR_FTM_REQ_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_REQ_ATTR_ASAP: ASAP mode requested (flag)
+ * @NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE: preamble type (see
+ *     &enum nl80211_preamble), optional for DMG (u32)
+ * @NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP: number of bursts exponent as in
+ *     802.11-2016 9.4.2.168 "Fine Timing Measurement Parameters element"
+ *     (u8, 0-15, optional with default 15 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD: interval between bursts in units
+ *     of 100ms (u16, optional with default 0)
+ * @NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION: burst duration, as in 802.11-2016
+ *     Table 9-257 "Burst Duration field encoding" (u8, 0-15, optional with
+ *     default 15 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames
+ *     requested per burst
+ *     (u8, 0-31, optional with default 0 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries
+ *     (u8, default 3)
+ * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag)
+ * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC: request civic location data
+ *     (flag)
+ *
+ * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
+ * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_req {
+       __NL80211_PMSR_FTM_REQ_ATTR_INVALID,
+
+       NL80211_PMSR_FTM_REQ_ATTR_ASAP,
+       NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE,
+       NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP,
+       NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD,
+       NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION,
+       NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST,
+       NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES,
+       NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI,
+       NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC,
+
+       /* keep last */
+       NUM_NL80211_PMSR_FTM_REQ_ATTR,
+       NL80211_PMSR_FTM_REQ_ATTR_MAX = NUM_NL80211_PMSR_FTM_REQ_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_failure_reasons - FTM failure reasons
+ * @NL80211_PMSR_FTM_FAILURE_UNSPECIFIED: unspecified failure, not used
+ * @NL80211_PMSR_FTM_FAILURE_NO_RESPONSE: no response from the FTM responder
+ * @NL80211_PMSR_FTM_FAILURE_REJECTED: FTM responder rejected measurement
+ * @NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL: we already know the peer is
+ *     on a different channel, so can't measure (if we didn't know, we'd
+ *     try and get no response)
+ * @NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE: peer can't actually do FTM
+ * @NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP: invalid T1/T4 timestamps
+ *     received
+ * @NL80211_PMSR_FTM_FAILURE_PEER_BUSY: peer reports busy, you may retry
+ *     later (see %NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME)
+ * @NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS: parameters were changed
+ *     by the peer and are no longer supported
+ */
+enum nl80211_peer_measurement_ftm_failure_reasons {
+       NL80211_PMSR_FTM_FAILURE_UNSPECIFIED,
+       NL80211_PMSR_FTM_FAILURE_NO_RESPONSE,
+       NL80211_PMSR_FTM_FAILURE_REJECTED,
+       NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL,
+       NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE,
+       NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP,
+       NL80211_PMSR_FTM_FAILURE_PEER_BUSY,
+       NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS,
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_resp - FTM response attributes
+ * @__NL80211_PMSR_FTM_RESP_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON: FTM-specific failure reason
+ *     (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX: optional, if bursts are reported
+ *     as separate results then it will be the burst index 0...(N-1) and
+ *     the top level will indicate partial results (u32)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames
+ *     transmitted (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames
+ *     that were acknowleged (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the
+ *     busy peer (u32, seconds)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent
+ *     used by the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION: actual burst duration used by
+ *     the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST: actual FTMs per burst used
+ *     by the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG: average RSSI across all FTM action
+ *     frames (optional, s32, 1/2 dBm)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD: RSSI spread across all FTM action
+ *     frames (optional, s32, 1/2 dBm)
+ * @NL80211_PMSR_FTM_RESP_ATTR_TX_RATE: bitrate we used for the response to the
+ *     FTM action frame (optional, nested, using &enum nl80211_rate_info
+ *     attributes)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RX_RATE: bitrate the responder used for the FTM
+ *     action frame (optional, nested, using &enum nl80211_rate_info attrs)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG: average RTT (s64, picoseconds, optional
+ *     but one of RTT/DIST must be present)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE: RTT variance (u64, ps^2, note that
+ *     standard deviation is the square root of variance, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD: RTT spread (u64, picoseconds,
+ *     optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG: average distance (s64, mm, optional
+ *     but one of RTT/DIST must be present)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE: distance variance (u64, mm^2, note
+ *     that standard deviation is the square root of variance, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD: distance spread (u64, mm, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_LCI: LCI data from peer (binary, optional);
+ *     this is the contents of the Measurement Report Element (802.11-2016
+ *     9.4.2.22.1) starting with the Measurement Token, with Measurement
+ *     Type 8.
+ * @NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC: civic location data from peer
+ *     (binary, optional);
+ *     this is the contents of the Measurement Report Element (802.11-2016
+ *     9.4.2.22.1) starting with the Measurement Token, with Measurement
+ *     Type 11.
+ * @NL80211_PMSR_FTM_RESP_ATTR_PAD: ignore, for u64/s64 padding only
+ *
+ * @NUM_NL80211_PMSR_FTM_RESP_ATTR: internal
+ * @NL80211_PMSR_FTM_RESP_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_resp {
+       __NL80211_PMSR_FTM_RESP_ATTR_INVALID,
+
+       NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON,
+       NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX,
+       NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS,
+       NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES,
+       NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME,
+       NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP,
+       NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION,
+       NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST,
+       NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG,
+       NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD,
+       NL80211_PMSR_FTM_RESP_ATTR_TX_RATE,
+       NL80211_PMSR_FTM_RESP_ATTR_RX_RATE,
+       NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG,
+       NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE,
+       NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD,
+       NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG,
+       NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE,
+       NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD,
+       NL80211_PMSR_FTM_RESP_ATTR_LCI,
+       NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC,
+       NL80211_PMSR_FTM_RESP_ATTR_PAD,
+
+       /* keep last */
+       NUM_NL80211_PMSR_FTM_RESP_ATTR,
+       NL80211_PMSR_FTM_RESP_ATTR_MAX = NUM_NL80211_PMSR_FTM_RESP_ATTR - 1
+};
+
 #endif /* __LINUX_NL80211_H */
index 401d0c1..95d0db2 100644 (file)
@@ -485,6 +485,11 @@ enum {
 
        TCA_FLOWER_IN_HW_COUNT,
 
+       TCA_FLOWER_KEY_PORT_SRC_MIN,    /* be16 */
+       TCA_FLOWER_KEY_PORT_SRC_MAX,    /* be16 */
+       TCA_FLOWER_KEY_PORT_DST_MIN,    /* be16 */
+       TCA_FLOWER_KEY_PORT_DST_MAX,    /* be16 */
+
        __TCA_FLOWER_MAX,
 };
 
@@ -518,6 +523,8 @@ enum {
        TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
 };
 
+#define TCA_FLOWER_MASK_FLAGS_RANGE    (1 << 0) /* Range-based match */
+
 /* Match-all classifier */
 
 enum {
index ee017bc..0d18b1d 100644 (file)
@@ -291,11 +291,38 @@ enum {
        TCA_GRED_DPS,
        TCA_GRED_MAX_P,
        TCA_GRED_LIMIT,
+       TCA_GRED_VQ_LIST,       /* nested TCA_GRED_VQ_ENTRY */
        __TCA_GRED_MAX,
 };
 
 #define TCA_GRED_MAX (__TCA_GRED_MAX - 1)
 
+enum {
+       TCA_GRED_VQ_ENTRY_UNSPEC,
+       TCA_GRED_VQ_ENTRY,      /* nested TCA_GRED_VQ_* */
+       __TCA_GRED_VQ_ENTRY_MAX,
+};
+#define TCA_GRED_VQ_ENTRY_MAX (__TCA_GRED_VQ_ENTRY_MAX - 1)
+
+enum {
+       TCA_GRED_VQ_UNSPEC,
+       TCA_GRED_VQ_PAD,
+       TCA_GRED_VQ_DP,                 /* u32 */
+       TCA_GRED_VQ_STAT_BYTES,         /* u64 */
+       TCA_GRED_VQ_STAT_PACKETS,       /* u32 */
+       TCA_GRED_VQ_STAT_BACKLOG,       /* u32 */
+       TCA_GRED_VQ_STAT_PROB_DROP,     /* u32 */
+       TCA_GRED_VQ_STAT_PROB_MARK,     /* u32 */
+       TCA_GRED_VQ_STAT_FORCED_DROP,   /* u32 */
+       TCA_GRED_VQ_STAT_FORCED_MARK,   /* u32 */
+       TCA_GRED_VQ_STAT_PDROP,         /* u32 */
+       TCA_GRED_VQ_STAT_OTHER,         /* u32 */
+       TCA_GRED_VQ_FLAGS,              /* u32 */
+       __TCA_GRED_VQ_MAX
+};
+
+#define TCA_GRED_VQ_MAX (__TCA_GRED_VQ_MAX - 1)
+
 struct tc_gred_qopt {
        __u32           limit;        /* HARD maximal queue length (bytes)    */
        __u32           qth_min;      /* Min average length threshold (bytes) */
index c0d7ea0..b17201e 100644 (file)
@@ -212,6 +212,7 @@ struct prctl_mm_map {
 #define PR_SET_SPECULATION_CTRL                53
 /* Speculation control variants */
 # define PR_SPEC_STORE_BYPASS          0
+# define PR_SPEC_INDIRECT_BRANCH       1
 /* Return and control values for PR_SET/GET_SPECULATION_CTRL */
 # define PR_SPEC_NOT_AFFECTED          0
 # define PR_SPEC_PRCTL                 (1UL << 0)
index c81feb3..d584073 100644 (file)
@@ -129,6 +129,7 @@ typedef __s32 sctp_assoc_t;
 #define SCTP_STREAM_SCHEDULER_VALUE    124
 #define SCTP_INTERLEAVING_SUPPORTED    125
 #define SCTP_SENDMSG_CONNECT   126
+#define SCTP_EVENT     127
 
 /* PR-SCTP policies */
 #define SCTP_PR_SCTP_NONE      0x0000
@@ -632,7 +633,9 @@ union sctp_notification {
  */
 
 enum sctp_sn_type {
-       SCTP_SN_TYPE_BASE     = (1<<15),
+       SCTP_SN_TYPE_BASE       = (1<<15),
+       SCTP_DATA_IO_EVENT      = SCTP_SN_TYPE_BASE,
+#define SCTP_DATA_IO_EVENT             SCTP_DATA_IO_EVENT
        SCTP_ASSOC_CHANGE,
 #define SCTP_ASSOC_CHANGE              SCTP_ASSOC_CHANGE
        SCTP_PEER_ADDR_CHANGE,
@@ -657,6 +660,8 @@ enum sctp_sn_type {
 #define SCTP_ASSOC_RESET_EVENT         SCTP_ASSOC_RESET_EVENT
        SCTP_STREAM_CHANGE_EVENT,
 #define SCTP_STREAM_CHANGE_EVENT       SCTP_STREAM_CHANGE_EVENT
+       SCTP_SN_TYPE_MAX        = SCTP_STREAM_CHANGE_EVENT,
+#define SCTP_SN_TYPE_MAX               SCTP_SN_TYPE_MAX
 };
 
 /* Notification error codes used to fill up the error fields in some
@@ -1150,6 +1155,12 @@ struct sctp_add_streams {
        uint16_t sas_outstrms;
 };
 
+struct sctp_event {
+       sctp_assoc_t se_assoc_id;
+       uint16_t se_type;
+       uint8_t se_on;
+};
+
 /* SCTP Stream schedulers */
 enum sctp_sched_type {
        SCTP_SS_FCFS,
index f80135e..86dc24a 100644 (file)
@@ -243,6 +243,7 @@ enum
        LINUX_MIB_TCPREQQFULLDROP,              /* TCPReqQFullDrop */
        LINUX_MIB_TCPRETRANSFAIL,               /* TCPRetransFail */
        LINUX_MIB_TCPRCVCOALESCE,               /* TCPRcvCoalesce */
+       LINUX_MIB_TCPBACKLOGCOALESCE,           /* TCPBacklogCoalesce */
        LINUX_MIB_TCPOFOQUEUE,                  /* TCPOFOQueue */
        LINUX_MIB_TCPOFODROP,                   /* TCPOFODrop */
        LINUX_MIB_TCPOFOMERGE,                  /* TCPOFOMerge */
index e02d319..8bb6cc5 100644 (file)
@@ -266,6 +266,7 @@ enum {
        TCP_NLA_BYTES_RETRANS,  /* Data bytes retransmitted */
        TCP_NLA_DSACK_DUPS,     /* DSACK blocks received */
        TCP_NLA_REORD_SEEN,     /* reordering events seen */
+       TCP_NLA_SRTT,           /* smoothed RTT in usecs */
 };
 
 /* for TCP_MD5SIG socket option */
index 51b0958..3dcfc61 100644 (file)
@@ -50,6 +50,8 @@
 #ifndef __LINUX_V4L2_CONTROLS_H
 #define __LINUX_V4L2_CONTROLS_H
 
+#include <linux/types.h>
+
 /* Control classes */
 #define V4L2_CTRL_CLASS_USER           0x00980000      /* Old-style 'user' controls */
 #define V4L2_CTRL_CLASS_MPEG           0x00990000      /* MPEG-compression controls */
@@ -402,9 +404,6 @@ enum v4l2_mpeg_video_multi_slice_mode {
 #define V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE          (V4L2_CID_MPEG_BASE+228)
 #define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME            (V4L2_CID_MPEG_BASE+229)
 
-#define V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS         (V4L2_CID_MPEG_BASE+250)
-#define V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION         (V4L2_CID_MPEG_BASE+251)
-
 #define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP            (V4L2_CID_MPEG_BASE+300)
 #define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP            (V4L2_CID_MPEG_BASE+301)
 #define V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP            (V4L2_CID_MPEG_BASE+302)
@@ -1095,66 +1094,4 @@ enum v4l2_detect_md_mode {
 #define V4L2_CID_DETECT_MD_THRESHOLD_GRID      (V4L2_CID_DETECT_CLASS_BASE + 3)
 #define V4L2_CID_DETECT_MD_REGION_GRID         (V4L2_CID_DETECT_CLASS_BASE + 4)
 
-#define V4L2_MPEG2_PICTURE_CODING_TYPE_I       1
-#define V4L2_MPEG2_PICTURE_CODING_TYPE_P       2
-#define V4L2_MPEG2_PICTURE_CODING_TYPE_B       3
-#define V4L2_MPEG2_PICTURE_CODING_TYPE_D       4
-
-struct v4l2_mpeg2_sequence {
-       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Sequence header */
-       __u16   horizontal_size;
-       __u16   vertical_size;
-       __u32   vbv_buffer_size;
-
-       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Sequence extension */
-       __u8    profile_and_level_indication;
-       __u8    progressive_sequence;
-       __u8    chroma_format;
-};
-
-struct v4l2_mpeg2_picture {
-       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Picture header */
-       __u8    picture_coding_type;
-
-       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Picture coding extension */
-       __u8    f_code[2][2];
-       __u8    intra_dc_precision;
-       __u8    picture_structure;
-       __u8    top_field_first;
-       __u8    frame_pred_frame_dct;
-       __u8    concealment_motion_vectors;
-       __u8    q_scale_type;
-       __u8    intra_vlc_format;
-       __u8    alternate_scan;
-       __u8    repeat_first_field;
-       __u8    progressive_frame;
-};
-
-struct v4l2_ctrl_mpeg2_slice_params {
-       __u32   bit_size;
-       __u32   data_bit_offset;
-
-       struct v4l2_mpeg2_sequence sequence;
-       struct v4l2_mpeg2_picture picture;
-
-       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Slice */
-       __u8    quantiser_scale_code;
-
-       __u8    backward_ref_index;
-       __u8    forward_ref_index;
-};
-
-struct v4l2_ctrl_mpeg2_quantization {
-       /* ISO/IEC 13818-2, ITU-T Rec. H.262: Quant matrix extension */
-       __u8    load_intra_quantiser_matrix;
-       __u8    load_non_intra_quantiser_matrix;
-       __u8    load_chroma_intra_quantiser_matrix;
-       __u8    load_chroma_non_intra_quantiser_matrix;
-
-       __u8    intra_quantiser_matrix[64];
-       __u8    non_intra_quantiser_matrix[64];
-       __u8    chroma_intra_quantiser_matrix[64];
-       __u8    chroma_non_intra_quantiser_matrix[64];
-};
-
 #endif
index c8e8ff8..2ba2ad0 100644 (file)
@@ -1622,8 +1622,6 @@ struct v4l2_ext_control {
                __u8 __user *p_u8;
                __u16 __user *p_u16;
                __u32 __user *p_u32;
-               struct v4l2_ctrl_mpeg2_slice_params __user *p_mpeg2_slice_params;
-               struct v4l2_ctrl_mpeg2_quantization __user *p_mpeg2_quantization;
                void __user *ptr;
        };
 } __attribute__ ((packed));
@@ -1669,8 +1667,6 @@ enum v4l2_ctrl_type {
        V4L2_CTRL_TYPE_U8            = 0x0100,
        V4L2_CTRL_TYPE_U16           = 0x0101,
        V4L2_CTRL_TYPE_U32           = 0x0102,
-       V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS = 0x0103,
-       V4L2_CTRL_TYPE_MPEG2_QUANTIZATION = 0x0104,
 };
 
 /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
index 449132c..1196e1c 100644 (file)
@@ -75,6 +75,9 @@
  */
 #define VIRTIO_F_IOMMU_PLATFORM                33
 
+/* This feature indicates support for the packed virtqueue layout. */
+#define VIRTIO_F_RING_PACKED           34
+
 /*
  * Does the device support Single Root I/O Virtualization?
  */
index 6d5d5fa..2414f8a 100644 (file)
 /* This means the buffer contains a list of buffer descriptors. */
 #define VRING_DESC_F_INDIRECT  4
 
+/*
+ * Mark a descriptor as available or used in packed ring.
+ * Notice: they are defined as shifts instead of shifted values.
+ */
+#define VRING_PACKED_DESC_F_AVAIL      7
+#define VRING_PACKED_DESC_F_USED       15
+
 /* The Host uses this in used->flags to advise the Guest: don't kick me when
  * you add a buffer.  It's unreliable, so it's simply an optimization.  Guest
  * will still kick if it's out of buffers. */
  * optimization.  */
 #define VRING_AVAIL_F_NO_INTERRUPT     1
 
+/* Enable events in packed ring. */
+#define VRING_PACKED_EVENT_FLAG_ENABLE 0x0
+/* Disable events in packed ring. */
+#define VRING_PACKED_EVENT_FLAG_DISABLE        0x1
+/*
+ * Enable events for a specific descriptor in packed ring.
+ * (as specified by Descriptor Ring Change Event Offset/Wrap Counter).
+ * Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated.
+ */
+#define VRING_PACKED_EVENT_FLAG_DESC   0x2
+
+/*
+ * Wrap counter bit shift in event suppression structure
+ * of packed ring.
+ */
+#define VRING_PACKED_EVENT_F_WRAP_CTR  15
+
 /* We support indirect buffer descriptors */
 #define VIRTIO_RING_F_INDIRECT_DESC    28
 
@@ -171,4 +195,32 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)
        return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old);
 }
 
+struct vring_packed_desc_event {
+       /* Descriptor Ring Change Event Offset/Wrap Counter. */
+       __le16 off_wrap;
+       /* Descriptor Ring Change Event Flags. */
+       __le16 flags;
+};
+
+struct vring_packed_desc {
+       /* Buffer Address. */
+       __le64 addr;
+       /* Buffer Length. */
+       __le32 len;
+       /* Buffer ID. */
+       __le16 id;
+       /* The flags depending on descriptor type. */
+       __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 61f410f..4914b93 100644 (file)
@@ -44,8 +44,3 @@ static inline void xen_balloon_init(void)
 {
 }
 #endif
-
-#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
-struct resource;
-void arch_xen_balloon_init(struct resource *hostmem_resource);
-#endif
index a4112e9..ed93525 100644 (file)
@@ -509,6 +509,15 @@ config PSI
 
          Say N if unsure.
 
+config PSI_DEFAULT_DISABLED
+       bool "Require boot parameter to enable pressure stall information tracking"
+       default n
+       depends on PSI
+       help
+         If set, pressure stall information tracking will be disabled
+         per default but can be enabled through passing psi=1 on the
+         kernel commandline during boot.
+
 endmenu # "CPU/Task time and stats accounting"
 
 config CPU_ISOLATION
index 6405577..f6f4a1e 100644 (file)
@@ -291,16 +291,6 @@ static int __init do_reset(void)
        return 1;
 }
 
-static int __init maybe_link(void)
-{
-       if (nlink >= 2) {
-               char *old = find_link(major, minor, ino, mode, collected);
-               if (old)
-                       return (ksys_link(old, collected) < 0) ? -1 : 1;
-       }
-       return 0;
-}
-
 static void __init clean_path(char *path, umode_t fmode)
 {
        struct kstat st;
@@ -313,6 +303,18 @@ static void __init clean_path(char *path, umode_t fmode)
        }
 }
 
+static int __init maybe_link(void)
+{
+       if (nlink >= 2) {
+               char *old = find_link(major, minor, ino, mode, collected);
+               if (old) {
+                       clean_path(collected, 0);
+                       return (ksys_link(old, collected) < 0) ? -1 : 1;
+               }
+       }
+       return 0;
+}
+
 static __initdata int wfd;
 
 static int __init do_name(void)
index 24583da..25632a7 100644 (file)
@@ -382,6 +382,7 @@ static void percpu_array_map_seq_show_elem(struct bpf_map *map, void *key,
 }
 
 static int array_map_check_btf(const struct bpf_map *map,
+                              const struct btf *btf,
                               const struct btf_type *key_type,
                               const struct btf_type *value_type)
 {
index ee4c826..715f9fc 100644 (file)
@@ -5,6 +5,7 @@
 #include <uapi/linux/types.h>
 #include <linux/seq_file.h>
 #include <linux/compiler.h>
+#include <linux/ctype.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/anon_inodes.h>
 #define BITS_ROUNDUP_BYTES(bits) \
        (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
 
-#define BTF_INFO_MASK 0x0f00ffff
+#define BTF_INFO_MASK 0x8f00ffff
 #define BTF_INT_MASK 0x0fffffff
 #define BTF_TYPE_ID_VALID(type_id) ((type_id) <= BTF_MAX_TYPE)
 #define BTF_STR_OFFSET_VALID(name_off) ((name_off) <= BTF_MAX_NAME_OFFSET)
@@ -259,6 +260,8 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
        [BTF_KIND_VOLATILE]     = "VOLATILE",
        [BTF_KIND_CONST]        = "CONST",
        [BTF_KIND_RESTRICT]     = "RESTRICT",
+       [BTF_KIND_FUNC]         = "FUNC",
+       [BTF_KIND_FUNC_PROTO]   = "FUNC_PROTO",
 };
 
 struct btf_kind_operations {
@@ -271,6 +274,10 @@ struct btf_kind_operations {
                            const struct btf_type *struct_type,
                            const struct btf_member *member,
                            const struct btf_type *member_type);
+       int (*check_kflag_member)(struct btf_verifier_env *env,
+                                 const struct btf_type *struct_type,
+                                 const struct btf_member *member,
+                                 const struct btf_type *member_type);
        void (*log_details)(struct btf_verifier_env *env,
                            const struct btf_type *t);
        void (*seq_show)(const struct btf *btf, const struct btf_type *t,
@@ -281,6 +288,9 @@ struct btf_kind_operations {
 static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS];
 static struct btf_type btf_void;
 
+static int btf_resolve(struct btf_verifier_env *env,
+                      const struct btf_type *t, u32 type_id);
+
 static bool btf_type_is_modifier(const struct btf_type *t)
 {
        /* Some of them is not strictly a C modifier
@@ -306,15 +316,33 @@ static bool btf_type_is_modifier(const struct btf_type *t)
 
 static bool btf_type_is_void(const struct btf_type *t)
 {
-       /* void => no type and size info.
-        * Hence, FWD is also treated as void.
-        */
-       return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
+       return t == &btf_void;
+}
+
+static bool btf_type_is_fwd(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
+}
+
+static bool btf_type_is_func(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC;
+}
+
+static bool btf_type_is_func_proto(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC_PROTO;
+}
+
+static bool btf_type_nosize(const struct btf_type *t)
+{
+       return btf_type_is_void(t) || btf_type_is_fwd(t) ||
+              btf_type_is_func(t) || btf_type_is_func_proto(t);
 }
 
-static bool btf_type_is_void_or_null(const struct btf_type *t)
+static bool btf_type_nosize_or_null(const struct btf_type *t)
 {
-       return !t || btf_type_is_void(t);
+       return !t || btf_type_nosize(t);
 }
 
 /* union is only a special case of struct:
@@ -395,6 +423,25 @@ static u16 btf_type_vlen(const struct btf_type *t)
        return BTF_INFO_VLEN(t->info);
 }
 
+static bool btf_type_kflag(const struct btf_type *t)
+{
+       return BTF_INFO_KFLAG(t->info);
+}
+
+static u32 btf_member_bit_offset(const struct btf_type *struct_type,
+                            const struct btf_member *member)
+{
+       return btf_type_kflag(struct_type) ? BTF_MEMBER_BIT_OFFSET(member->offset)
+                                          : member->offset;
+}
+
+static u32 btf_member_bitfield_size(const struct btf_type *struct_type,
+                                   const struct btf_member *member)
+{
+       return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset)
+                                          : 0;
+}
+
 static u32 btf_type_int(const struct btf_type *t)
 {
        return *(u32 *)(t + 1);
@@ -420,13 +467,37 @@ static const struct btf_kind_operations *btf_type_ops(const struct btf_type *t)
        return kind_ops[BTF_INFO_KIND(t->info)];
 }
 
-static bool btf_name_offset_valid(const struct btf *btf, u32 offset)
+bool btf_name_offset_valid(const struct btf *btf, u32 offset)
 {
        return BTF_STR_OFFSET_VALID(offset) &&
                offset < btf->hdr.str_len;
 }
 
-static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
+/* Only C-style identifier is permitted. This can be relaxed if
+ * necessary.
+ */
+static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)
+{
+       /* offset must be valid */
+       const char *src = &btf->strings[offset];
+       const char *src_limit;
+
+       if (!isalpha(*src) && *src != '_')
+               return false;
+
+       /* set a limit on identifier length */
+       src_limit = src + KSYM_NAME_LEN;
+       src++;
+       while (*src && src < src_limit) {
+               if (!isalnum(*src) && *src != '_')
+                       return false;
+               src++;
+       }
+
+       return !*src;
+}
+
+static const char *__btf_name_by_offset(const struct btf *btf, u32 offset)
 {
        if (!offset)
                return "(anon)";
@@ -436,7 +507,15 @@ static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
                return "(invalid-name-offset)";
 }
 
-static const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id)
+const char *btf_name_by_offset(const struct btf *btf, u32 offset)
+{
+       if (offset < btf->hdr.str_len)
+               return &btf->strings[offset];
+
+       return NULL;
+}
+
+const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id)
 {
        if (type_id > btf->nr_types)
                return NULL;
@@ -466,6 +545,47 @@ static bool btf_type_int_is_regular(const struct btf_type *t)
        return true;
 }
 
+/*
+ * Check that given struct member is a regular int with expected
+ * offset and size.
+ */
+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)
+{
+       const struct btf_type *t;
+       u32 id, int_data;
+       u8 nr_bits;
+
+       id = m->type;
+       t = btf_type_id_size(btf, &id, NULL);
+       if (!t || !btf_type_is_int(t))
+               return false;
+
+       int_data = btf_type_int(t);
+       nr_bits = BTF_INT_BITS(int_data);
+       if (btf_type_kflag(s)) {
+               u32 bitfield_size = BTF_MEMBER_BITFIELD_SIZE(m->offset);
+               u32 bit_offset = BTF_MEMBER_BIT_OFFSET(m->offset);
+
+               /* if kflag set, int should be a regular int and
+                * bit offset should be at byte boundary.
+                */
+               return !bitfield_size &&
+                      BITS_ROUNDUP_BYTES(bit_offset) == expected_offset &&
+                      BITS_ROUNDUP_BYTES(nr_bits) == expected_size;
+       }
+
+       if (BTF_INT_OFFSET(int_data) ||
+           BITS_PER_BYTE_MASKED(m->offset) ||
+           BITS_ROUNDUP_BYTES(m->offset) != expected_offset ||
+           BITS_PER_BYTE_MASKED(nr_bits) ||
+           BITS_ROUNDUP_BYTES(nr_bits) != expected_size)
+               return false;
+
+       return true;
+}
+
 __printf(2, 3) static void __btf_verifier_log(struct bpf_verifier_log *log,
                                              const char *fmt, ...)
 {
@@ -506,7 +626,7 @@ __printf(4, 5) static void __btf_verifier_log_type(struct btf_verifier_env *env,
        __btf_verifier_log(log, "[%u] %s %s%s",
                           env->log_type_id,
                           btf_kind_str[kind],
-                          btf_name_by_offset(btf, t->name_off),
+                          __btf_name_by_offset(btf, t->name_off),
                           log_details ? " " : "");
 
        if (log_details)
@@ -549,9 +669,17 @@ static void btf_verifier_log_member(struct btf_verifier_env *env,
        if (env->phase != CHECK_META)
                btf_verifier_log_type(env, struct_type, NULL);
 
-       __btf_verifier_log(log, "\t%s type_id=%u bits_offset=%u",
-                          btf_name_by_offset(btf, member->name_off),
-                          member->type, member->offset);
+       if (btf_type_kflag(struct_type))
+               __btf_verifier_log(log,
+                                  "\t%s type_id=%u bitfield_size=%u bits_offset=%u",
+                                  __btf_name_by_offset(btf, member->name_off),
+                                  member->type,
+                                  BTF_MEMBER_BITFIELD_SIZE(member->offset),
+                                  BTF_MEMBER_BIT_OFFSET(member->offset));
+       else
+               __btf_verifier_log(log, "\t%s type_id=%u bits_offset=%u",
+                                  __btf_name_by_offset(btf, member->name_off),
+                                  member->type, member->offset);
 
        if (fmt && *fmt) {
                __btf_verifier_log(log, " ");
@@ -740,11 +868,15 @@ static bool env_type_is_resolve_sink(const struct btf_verifier_env *env,
                /* int, enum or void is a sink */
                return !btf_type_needs_resolve(next_type);
        case RESOLVE_PTR:
-               /* int, enum, void, struct or array is a sink for ptr */
+               /* int, enum, void, struct, array, func or func_proto is a sink
+                * for ptr
+                */
                return !btf_type_is_modifier(next_type) &&
                        !btf_type_is_ptr(next_type);
        case RESOLVE_STRUCT_OR_ARRAY:
-               /* int, enum, void or ptr is a sink for struct and array */
+               /* int, enum, void, ptr, func or func_proto is a sink
+                * for struct and array
+                */
                return !btf_type_is_modifier(next_type) &&
                        !btf_type_is_array(next_type) &&
                        !btf_type_is_struct(next_type);
@@ -826,7 +958,7 @@ const struct btf_type *btf_type_id_size(const struct btf *btf,
        u32 size = 0;
 
        size_type = btf_type_by_id(btf, size_type_id);
-       if (btf_type_is_void_or_null(size_type))
+       if (btf_type_nosize_or_null(size_type))
                return NULL;
 
        if (btf_type_has_size(size_type)) {
@@ -842,7 +974,7 @@ const struct btf_type *btf_type_id_size(const struct btf *btf,
                size = btf->resolved_sizes[size_type_id];
                size_type_id = btf->resolved_ids[size_type_id];
                size_type = btf_type_by_id(btf, size_type_id);
-               if (btf_type_is_void(size_type))
+               if (btf_type_nosize_or_null(size_type))
                        return NULL;
        }
 
@@ -863,6 +995,38 @@ static int btf_df_check_member(struct btf_verifier_env *env,
        return -EINVAL;
 }
 
+static int btf_df_check_kflag_member(struct btf_verifier_env *env,
+                                    const struct btf_type *struct_type,
+                                    const struct btf_member *member,
+                                    const struct btf_type *member_type)
+{
+       btf_verifier_log_basic(env, struct_type,
+                              "Unsupported check_kflag_member");
+       return -EINVAL;
+}
+
+/* Used for ptr, array and struct/union type members.
+ * int, enum and modifier types have their specific callback functions.
+ */
+static int btf_generic_check_kflag_member(struct btf_verifier_env *env,
+                                         const struct btf_type *struct_type,
+                                         const struct btf_member *member,
+                                         const struct btf_type *member_type)
+{
+       if (BTF_MEMBER_BITFIELD_SIZE(member->offset)) {
+               btf_verifier_log_member(env, struct_type, member,
+                                       "Invalid member bitfield_size");
+               return -EINVAL;
+       }
+
+       /* bitfield size is 0, so member->offset represents bit offset only.
+        * It is safe to call non kflag check_member variants.
+        */
+       return btf_type_ops(member_type)->check_member(env, struct_type,
+                                                      member,
+                                                      member_type);
+}
+
 static int btf_df_resolve(struct btf_verifier_env *env,
                          const struct resolve_vertex *v)
 {
@@ -915,6 +1079,62 @@ static int btf_int_check_member(struct btf_verifier_env *env,
        return 0;
 }
 
+static int btf_int_check_kflag_member(struct btf_verifier_env *env,
+                                     const struct btf_type *struct_type,
+                                     const struct btf_member *member,
+                                     const struct btf_type *member_type)
+{
+       u32 struct_bits_off, nr_bits, nr_int_data_bits, bytes_offset;
+       u32 int_data = btf_type_int(member_type);
+       u32 struct_size = struct_type->size;
+       u32 nr_copy_bits;
+
+       /* a regular int type is required for the kflag int member */
+       if (!btf_type_int_is_regular(member_type)) {
+               btf_verifier_log_member(env, struct_type, member,
+                                       "Invalid member base type");
+               return -EINVAL;
+       }
+
+       /* check sanity of bitfield size */
+       nr_bits = BTF_MEMBER_BITFIELD_SIZE(member->offset);
+       struct_bits_off = BTF_MEMBER_BIT_OFFSET(member->offset);
+       nr_int_data_bits = BTF_INT_BITS(int_data);
+       if (!nr_bits) {
+               /* Not a bitfield member, member offset must be at byte
+                * boundary.
+                */
+               if (BITS_PER_BYTE_MASKED(struct_bits_off)) {
+                       btf_verifier_log_member(env, struct_type, member,
+                                               "Invalid member offset");
+                       return -EINVAL;
+               }
+
+               nr_bits = nr_int_data_bits;
+       } else if (nr_bits > nr_int_data_bits) {
+               btf_verifier_log_member(env, struct_type, member,
+                                       "Invalid member bitfield_size");
+               return -EINVAL;
+       }
+
+       bytes_offset = BITS_ROUNDDOWN_BYTES(struct_bits_off);
+       nr_copy_bits = nr_bits + BITS_PER_BYTE_MASKED(struct_bits_off);
+       if (nr_copy_bits > BITS_PER_U64) {
+               btf_verifier_log_member(env, struct_type, member,
+                                       "nr_copy_bits exceeds 64");
+               return -EINVAL;
+       }
+
+       if (struct_size < bytes_offset ||
+           struct_size - bytes_offset < BITS_ROUNDUP_BYTES(nr_copy_bits)) {
+               btf_verifier_log_member(env, struct_type, member,
+                                       "Member exceeds struct_size");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static s32 btf_int_check_meta(struct btf_verifier_env *env,
                              const struct btf_type *t,
                              u32 meta_left)
@@ -934,6 +1154,11 @@ static s32 btf_int_check_meta(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
+       if (btf_type_kflag(t)) {
+               btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+               return -EINVAL;
+       }
+
        int_data = btf_type_int(t);
        if (int_data & ~BTF_INT_MASK) {
                btf_verifier_log_basic(env, t, "Invalid int_data:%x",
@@ -986,26 +1211,16 @@ static void btf_int_log(struct btf_verifier_env *env,
                         btf_int_encoding_str(BTF_INT_ENCODING(int_data)));
 }
 
-static void btf_int_bits_seq_show(const struct btf *btf,
-                                 const struct btf_type *t,
-                                 void *data, u8 bits_offset,
-                                 struct seq_file *m)
+static void btf_bitfield_seq_show(void *data, u8 bits_offset,
+                                 u8 nr_bits, struct seq_file *m)
 {
        u16 left_shift_bits, right_shift_bits;
-       u32 int_data = btf_type_int(t);
-       u8 nr_bits = BTF_INT_BITS(int_data);
-       u8 total_bits_offset;
        u8 nr_copy_bytes;
        u8 nr_copy_bits;
        u64 print_num;
 
-       /*
-        * bits_offset is at most 7.
-        * BTF_INT_OFFSET() cannot exceed 64 bits.
-        */
-       total_bits_offset = bits_offset + BTF_INT_OFFSET(int_data);
-       data += BITS_ROUNDDOWN_BYTES(total_bits_offset);
-       bits_offset = BITS_PER_BYTE_MASKED(total_bits_offset);
+       data += BITS_ROUNDDOWN_BYTES(bits_offset);
+       bits_offset = BITS_PER_BYTE_MASKED(bits_offset);
        nr_copy_bits = nr_bits + bits_offset;
        nr_copy_bytes = BITS_ROUNDUP_BYTES(nr_copy_bits);
 
@@ -1025,6 +1240,24 @@ static void btf_int_bits_seq_show(const struct btf *btf,
        seq_printf(m, "0x%llx", print_num);
 }
 
+
+static void btf_int_bits_seq_show(const struct btf *btf,
+                                 const struct btf_type *t,
+                                 void *data, u8 bits_offset,
+                                 struct seq_file *m)
+{
+       u32 int_data = btf_type_int(t);
+       u8 nr_bits = BTF_INT_BITS(int_data);
+       u8 total_bits_offset;
+
+       /*
+        * bits_offset is at most 7.
+        * BTF_INT_OFFSET() cannot exceed 64 bits.
+        */
+       total_bits_offset = bits_offset + BTF_INT_OFFSET(int_data);
+       btf_bitfield_seq_show(data, total_bits_offset, nr_bits, m);
+}
+
 static void btf_int_seq_show(const struct btf *btf, const struct btf_type *t,
                             u32 type_id, void *data, u8 bits_offset,
                             struct seq_file *m)
@@ -1074,6 +1307,7 @@ static const struct btf_kind_operations int_ops = {
        .check_meta = btf_int_check_meta,
        .resolve = btf_df_resolve,
        .check_member = btf_int_check_member,
+       .check_kflag_member = btf_int_check_kflag_member,
        .log_details = btf_int_log,
        .seq_show = btf_int_seq_show,
 };
@@ -1103,6 +1337,31 @@ static int btf_modifier_check_member(struct btf_verifier_env *env,
                                                         resolved_type);
 }
 
+static int btf_modifier_check_kflag_member(struct btf_verifier_env *env,
+                                          const struct btf_type *struct_type,
+                                          const struct btf_member *member,
+                                          const struct btf_type *member_type)
+{
+       const struct btf_type *resolved_type;
+       u32 resolved_type_id = member->type;
+       struct btf_member resolved_member;
+       struct btf *btf = env->btf;
+
+       resolved_type = btf_type_id_size(btf, &resolved_type_id, NULL);
+       if (!resolved_type) {
+               btf_verifier_log_member(env, struct_type, member,
+                                       "Invalid member");
+               return -EINVAL;
+       }
+
+       resolved_member = *member;
+       resolved_member.type = resolved_type_id;
+
+       return btf_type_ops(resolved_type)->check_kflag_member(env, struct_type,
+                                                              &resolved_member,
+                                                              resolved_type);
+}
+
 static int btf_ptr_check_member(struct btf_verifier_env *env,
                                const struct btf_type *struct_type,
                                const struct btf_member *member,
@@ -1138,11 +1397,32 @@ static int btf_ref_type_check_meta(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
+       if (btf_type_kflag(t)) {
+               btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+               return -EINVAL;
+       }
+
        if (!BTF_TYPE_ID_VALID(t->type)) {
                btf_verifier_log_type(env, t, "Invalid type_id");
                return -EINVAL;
        }
 
+       /* typedef type must have a valid name, and other ref types,
+        * volatile, const, restrict, should have a null name.
+        */
+       if (BTF_INFO_KIND(t->info) == BTF_KIND_TYPEDEF) {
+               if (!t->name_off ||
+                   !btf_name_valid_identifier(env->btf, t->name_off)) {
+                       btf_verifier_log_type(env, t, "Invalid name");
+                       return -EINVAL;
+               }
+       } else {
+               if (t->name_off) {
+                       btf_verifier_log_type(env, t, "Invalid name");
+                       return -EINVAL;
+               }
+       }
+
        btf_verifier_log_type(env, t, NULL);
 
        return 0;
@@ -1163,10 +1443,6 @@ static int btf_modifier_resolve(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
-       /* "typedef void new_void", "const void"...etc */
-       if (btf_type_is_void(next_type))
-               goto resolved;
-
        if (!env_type_is_resolve_sink(env, next_type) &&
            !env_type_is_resolved(env, next_type_id))
                return env_stack_push(env, next_type, next_type_id);
@@ -1177,13 +1453,18 @@ static int btf_modifier_resolve(struct btf_verifier_env *env,
         * save us a few type-following when we use it later (e.g. in
         * pretty print).
         */
-       if (!btf_type_id_size(btf, &next_type_id, &next_type_size) &&
-           !btf_type_is_void(btf_type_id_resolve(btf, &next_type_id))) {
-               btf_verifier_log_type(env, v->t, "Invalid type_id");
-               return -EINVAL;
+       if (!btf_type_id_size(btf, &next_type_id, &next_type_size)) {
+               if (env_type_is_resolved(env, next_type_id))
+                       next_type = btf_type_id_resolve(btf, &next_type_id);
+
+               /* "typedef void new_void", "const void"...etc */
+               if (!btf_type_is_void(next_type) &&
+                   !btf_type_is_fwd(next_type)) {
+                       btf_verifier_log_type(env, v->t, "Invalid type_id");
+                       return -EINVAL;
+               }
        }
 
-resolved:
        env_stack_pop_resolved(env, next_type_id, next_type_size);
 
        return 0;
@@ -1196,7 +1477,6 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
        const struct btf_type *t = v->t;
        u32 next_type_id = t->type;
        struct btf *btf = env->btf;
-       u32 next_type_size = 0;
 
        next_type = btf_type_by_id(btf, next_type_id);
        if (!next_type) {
@@ -1204,10 +1484,6 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
-       /* "void *" */
-       if (btf_type_is_void(next_type))
-               goto resolved;
-
        if (!env_type_is_resolve_sink(env, next_type) &&
            !env_type_is_resolved(env, next_type_id))
                return env_stack_push(env, next_type, next_type_id);
@@ -1234,13 +1510,18 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
                                              resolved_type_id);
        }
 
-       if (!btf_type_id_size(btf, &next_type_id, &next_type_size) &&
-           !btf_type_is_void(btf_type_id_resolve(btf, &next_type_id))) {
-               btf_verifier_log_type(env, v->t, "Invalid type_id");
-               return -EINVAL;
+       if (!btf_type_id_size(btf, &next_type_id, NULL)) {
+               if (env_type_is_resolved(env, next_type_id))
+                       next_type = btf_type_id_resolve(btf, &next_type_id);
+
+               if (!btf_type_is_void(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;
+               }
        }
 
-resolved:
        env_stack_pop_resolved(env, next_type_id, 0);
 
        return 0;
@@ -1274,6 +1555,7 @@ static struct btf_kind_operations modifier_ops = {
        .check_meta = btf_ref_type_check_meta,
        .resolve = btf_modifier_resolve,
        .check_member = btf_modifier_check_member,
+       .check_kflag_member = btf_modifier_check_kflag_member,
        .log_details = btf_ref_type_log,
        .seq_show = btf_modifier_seq_show,
 };
@@ -1282,6 +1564,7 @@ static struct btf_kind_operations ptr_ops = {
        .check_meta = btf_ref_type_check_meta,
        .resolve = btf_ptr_resolve,
        .check_member = btf_ptr_check_member,
+       .check_kflag_member = btf_generic_check_kflag_member,
        .log_details = btf_ref_type_log,
        .seq_show = btf_ptr_seq_show,
 };
@@ -1300,16 +1583,30 @@ static s32 btf_fwd_check_meta(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
+       /* fwd type must have a valid name */
+       if (!t->name_off ||
+           !btf_name_valid_identifier(env->btf, t->name_off)) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+
        btf_verifier_log_type(env, t, NULL);
 
        return 0;
 }
 
+static void btf_fwd_type_log(struct btf_verifier_env *env,
+                            const struct btf_type *t)
+{
+       btf_verifier_log(env, "%s", btf_type_kflag(t) ? "union" : "struct");
+}
+
 static struct btf_kind_operations fwd_ops = {
        .check_meta = btf_fwd_check_meta,
        .resolve = btf_df_resolve,
        .check_member = btf_df_check_member,
-       .log_details = btf_ref_type_log,
+       .check_kflag_member = btf_df_check_kflag_member,
+       .log_details = btf_fwd_type_log,
        .seq_show = btf_df_seq_show,
 };
 
@@ -1356,11 +1653,22 @@ static s32 btf_array_check_meta(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
+       /* array type should not have a name */
+       if (t->name_off) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+
        if (btf_type_vlen(t)) {
                btf_verifier_log_type(env, t, "vlen != 0");
                return -EINVAL;
        }
 
+       if (btf_type_kflag(t)) {
+               btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+               return -EINVAL;
+       }
+
        if (t->size) {
                btf_verifier_log_type(env, t, "size != 0");
                return -EINVAL;
@@ -1396,7 +1704,7 @@ static int btf_array_resolve(struct btf_verifier_env *env,
        /* Check array->index_type */
        index_type_id = array->index_type;
        index_type = btf_type_by_id(btf, index_type_id);
-       if (btf_type_is_void_or_null(index_type)) {
+       if (btf_type_nosize_or_null(index_type)) {
                btf_verifier_log_type(env, v->t, "Invalid index");
                return -EINVAL;
        }
@@ -1415,7 +1723,7 @@ static int btf_array_resolve(struct btf_verifier_env *env,
        /* Check array->type */
        elem_type_id = array->type;
        elem_type = btf_type_by_id(btf, elem_type_id);
-       if (btf_type_is_void_or_null(elem_type)) {
+       if (btf_type_nosize_or_null(elem_type)) {
                btf_verifier_log_type(env, v->t,
                                      "Invalid elem");
                return -EINVAL;
@@ -1484,6 +1792,7 @@ static struct btf_kind_operations array_ops = {
        .check_meta = btf_array_check_meta,
        .resolve = btf_array_resolve,
        .check_member = btf_array_check_member,
+       .check_kflag_member = btf_generic_check_kflag_member,
        .log_details = btf_array_log,
        .seq_show = btf_array_seq_show,
 };
@@ -1522,6 +1831,7 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
        u32 meta_needed, last_offset;
        struct btf *btf = env->btf;
        u32 struct_size = t->size;
+       u32 offset;
        u16 i;
 
        meta_needed = btf_type_vlen(t) * sizeof(*member);
@@ -1532,6 +1842,13 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
+       /* struct type either no name or a valid one */
+       if (t->name_off &&
+           !btf_name_valid_identifier(env->btf, t->name_off)) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+
        btf_verifier_log_type(env, t, NULL);
 
        last_offset = 0;
@@ -1543,6 +1860,12 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
                        return -EINVAL;
                }
 
+               /* struct member either no name or a valid one */
+               if (member->name_off &&
+                   !btf_name_valid_identifier(btf, member->name_off)) {
+                       btf_verifier_log_member(env, t, member, "Invalid name");
+                       return -EINVAL;
+               }
                /* A member cannot be in type void */
                if (!member->type || !BTF_TYPE_ID_VALID(member->type)) {
                        btf_verifier_log_member(env, t, member,
@@ -1550,7 +1873,8 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
                        return -EINVAL;
                }
 
-               if (is_union && member->offset) {
+               offset = btf_member_bit_offset(t, member);
+               if (is_union && offset) {
                        btf_verifier_log_member(env, t, member,
                                                "Invalid member bits_offset");
                        return -EINVAL;
@@ -1560,20 +1884,20 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
                 * ">" instead of ">=" because the last member could be
                 * "char a[0];"
                 */
-               if (last_offset > member->offset) {
+               if (last_offset > offset) {
                        btf_verifier_log_member(env, t, member,
                                                "Invalid member bits_offset");
                        return -EINVAL;
                }
 
-               if (BITS_ROUNDUP_BYTES(member->offset) > struct_size) {
+               if (BITS_ROUNDUP_BYTES(offset) > struct_size) {
                        btf_verifier_log_member(env, t, member,
-                                               "Memmber bits_offset exceeds its struct size");
+                                               "Member bits_offset exceeds its struct size");
                        return -EINVAL;
                }
 
                btf_verifier_log_member(env, t, member, NULL);
-               last_offset = member->offset;
+               last_offset = offset;
        }
 
        return meta_needed;
@@ -1603,9 +1927,14 @@ static int btf_struct_resolve(struct btf_verifier_env *env,
 
                last_member_type = btf_type_by_id(env->btf,
                                                  last_member_type_id);
-               err = btf_type_ops(last_member_type)->check_member(env, v->t,
-                                                       last_member,
-                                                       last_member_type);
+               if (btf_type_kflag(v->t))
+                       err = btf_type_ops(last_member_type)->check_kflag_member(env, v->t,
+                                                               last_member,
+                                                               last_member_type);
+               else
+                       err = btf_type_ops(last_member_type)->check_member(env, v->t,
+                                                               last_member,
+                                                               last_member_type);
                if (err)
                        return err;
        }
@@ -1615,7 +1944,7 @@ static int btf_struct_resolve(struct btf_verifier_env *env,
                const struct btf_type *member_type = btf_type_by_id(env->btf,
                                                                member_type_id);
 
-               if (btf_type_is_void_or_null(member_type)) {
+               if (btf_type_nosize_or_null(member_type)) {
                        btf_verifier_log_member(env, v->t, member,
                                                "Invalid member");
                        return -EINVAL;
@@ -1627,9 +1956,14 @@ static int btf_struct_resolve(struct btf_verifier_env *env,
                        return env_stack_push(env, member_type, member_type_id);
                }
 
-               err = btf_type_ops(member_type)->check_member(env, v->t,
-                                                             member,
-                                                             member_type);
+               if (btf_type_kflag(v->t))
+                       err = btf_type_ops(member_type)->check_kflag_member(env, v->t,
+                                                                           member,
+                                                                           member_type);
+               else
+                       err = btf_type_ops(member_type)->check_member(env, v->t,
+                                                                     member,
+                                                                     member_type);
                if (err)
                        return err;
        }
@@ -1657,17 +1991,26 @@ static void btf_struct_seq_show(const struct btf *btf, const struct btf_type *t,
        for_each_member(i, t, member) {
                const struct btf_type *member_type = btf_type_by_id(btf,
                                                                member->type);
-               u32 member_offset = member->offset;
-               u32 bytes_offset = BITS_ROUNDDOWN_BYTES(member_offset);
-               u8 bits8_offset = BITS_PER_BYTE_MASKED(member_offset);
                const struct btf_kind_operations *ops;
+               u32 member_offset, bitfield_size;
+               u32 bytes_offset;
+               u8 bits8_offset;
 
                if (i)
                        seq_puts(m, seq);
 
-               ops = btf_type_ops(member_type);
-               ops->seq_show(btf, member_type, member->type,
-                             data + bytes_offset, bits8_offset, m);
+               member_offset = btf_member_bit_offset(t, member);
+               bitfield_size = btf_member_bitfield_size(t, member);
+               if (bitfield_size) {
+                       btf_bitfield_seq_show(data, member_offset,
+                                             bitfield_size, m);
+               } else {
+                       bytes_offset = BITS_ROUNDDOWN_BYTES(member_offset);
+                       bits8_offset = BITS_PER_BYTE_MASKED(member_offset);
+                       ops = btf_type_ops(member_type);
+                       ops->seq_show(btf, member_type, member->type,
+                                     data + bytes_offset, bits8_offset, m);
+               }
        }
        seq_puts(m, "}");
 }
@@ -1676,6 +2019,7 @@ static struct btf_kind_operations struct_ops = {
        .check_meta = btf_struct_check_meta,
        .resolve = btf_struct_resolve,
        .check_member = btf_struct_check_member,
+       .check_kflag_member = btf_generic_check_kflag_member,
        .log_details = btf_struct_log,
        .seq_show = btf_struct_seq_show,
 };
@@ -1705,6 +2049,41 @@ static int btf_enum_check_member(struct btf_verifier_env *env,
        return 0;
 }
 
+static int btf_enum_check_kflag_member(struct btf_verifier_env *env,
+                                      const struct btf_type *struct_type,
+                                      const struct btf_member *member,
+                                      const struct btf_type *member_type)
+{
+       u32 struct_bits_off, nr_bits, bytes_end, struct_size;
+       u32 int_bitsize = sizeof(int) * BITS_PER_BYTE;
+
+       struct_bits_off = BTF_MEMBER_BIT_OFFSET(member->offset);
+       nr_bits = BTF_MEMBER_BITFIELD_SIZE(member->offset);
+       if (!nr_bits) {
+               if (BITS_PER_BYTE_MASKED(struct_bits_off)) {
+                       btf_verifier_log_member(env, struct_type, member,
+                                               "Member is not byte aligned");
+                               return -EINVAL;
+               }
+
+               nr_bits = int_bitsize;
+       } else if (nr_bits > int_bitsize) {
+               btf_verifier_log_member(env, struct_type, member,
+                                       "Invalid member bitfield_size");
+               return -EINVAL;
+       }
+
+       struct_size = struct_type->size;
+       bytes_end = BITS_ROUNDUP_BYTES(struct_bits_off + nr_bits);
+       if (struct_size < bytes_end) {
+               btf_verifier_log_member(env, struct_type, member,
+                                       "Member exceeds struct_size");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static s32 btf_enum_check_meta(struct btf_verifier_env *env,
                               const struct btf_type *t,
                               u32 meta_left)
@@ -1724,12 +2103,24 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
+       if (btf_type_kflag(t)) {
+               btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+               return -EINVAL;
+       }
+
        if (t->size != sizeof(int)) {
                btf_verifier_log_type(env, t, "Expected size:%zu",
                                      sizeof(int));
                return -EINVAL;
        }
 
+       /* enum type either no name or a valid one */
+       if (t->name_off &&
+           !btf_name_valid_identifier(env->btf, t->name_off)) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+
        btf_verifier_log_type(env, t, NULL);
 
        for (i = 0; i < nr_enums; i++) {
@@ -1739,8 +2130,16 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
                        return -EINVAL;
                }
 
+               /* enum member must have a valid name */
+               if (!enums[i].name_off ||
+                   !btf_name_valid_identifier(btf, enums[i].name_off)) {
+                       btf_verifier_log_type(env, t, "Invalid name");
+                       return -EINVAL;
+               }
+
+
                btf_verifier_log(env, "\t%s val=%d\n",
-                                btf_name_by_offset(btf, enums[i].name_off),
+                                __btf_name_by_offset(btf, enums[i].name_off),
                                 enums[i].val);
        }
 
@@ -1764,7 +2163,8 @@ static void btf_enum_seq_show(const struct btf *btf, const struct btf_type *t,
        for (i = 0; i < nr_enums; i++) {
                if (v == enums[i].val) {
                        seq_printf(m, "%s",
-                                  btf_name_by_offset(btf, enums[i].name_off));
+                                  __btf_name_by_offset(btf,
+                                                       enums[i].name_off));
                        return;
                }
        }
@@ -1776,10 +2176,249 @@ static struct btf_kind_operations enum_ops = {
        .check_meta = btf_enum_check_meta,
        .resolve = btf_df_resolve,
        .check_member = btf_enum_check_member,
+       .check_kflag_member = btf_enum_check_kflag_member,
        .log_details = btf_enum_log,
        .seq_show = btf_enum_seq_show,
 };
 
+static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
+                                    const struct btf_type *t,
+                                    u32 meta_left)
+{
+       u32 meta_needed = btf_type_vlen(t) * sizeof(struct btf_param);
+
+       if (meta_left < meta_needed) {
+               btf_verifier_log_basic(env, t,
+                                      "meta_left:%u meta_needed:%u",
+                                      meta_left, meta_needed);
+               return -EINVAL;
+       }
+
+       if (t->name_off) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+
+       if (btf_type_kflag(t)) {
+               btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+               return -EINVAL;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       return meta_needed;
+}
+
+static void btf_func_proto_log(struct btf_verifier_env *env,
+                              const struct btf_type *t)
+{
+       const struct btf_param *args = (const struct btf_param *)(t + 1);
+       u16 nr_args = btf_type_vlen(t), i;
+
+       btf_verifier_log(env, "return=%u args=(", t->type);
+       if (!nr_args) {
+               btf_verifier_log(env, "void");
+               goto done;
+       }
+
+       if (nr_args == 1 && !args[0].type) {
+               /* Only one vararg */
+               btf_verifier_log(env, "vararg");
+               goto done;
+       }
+
+       btf_verifier_log(env, "%u %s", args[0].type,
+                        __btf_name_by_offset(env->btf,
+                                             args[0].name_off));
+       for (i = 1; i < nr_args - 1; i++)
+               btf_verifier_log(env, ", %u %s", args[i].type,
+                                __btf_name_by_offset(env->btf,
+                                                     args[i].name_off));
+
+       if (nr_args > 1) {
+               const struct btf_param *last_arg = &args[nr_args - 1];
+
+               if (last_arg->type)
+                       btf_verifier_log(env, ", %u %s", last_arg->type,
+                                        __btf_name_by_offset(env->btf,
+                                                             last_arg->name_off));
+               else
+                       btf_verifier_log(env, ", vararg");
+       }
+
+done:
+       btf_verifier_log(env, ")");
+}
+
+static struct btf_kind_operations func_proto_ops = {
+       .check_meta = btf_func_proto_check_meta,
+       .resolve = btf_df_resolve,
+       /*
+        * BTF_KIND_FUNC_PROTO cannot be directly referred by
+        * a struct's member.
+        *
+        * It should be a funciton pointer instead.
+        * (i.e. struct's member -> BTF_KIND_PTR -> BTF_KIND_FUNC_PROTO)
+        *
+        * Hence, there is no btf_func_check_member().
+        */
+       .check_member = btf_df_check_member,
+       .check_kflag_member = btf_df_check_kflag_member,
+       .log_details = btf_func_proto_log,
+       .seq_show = btf_df_seq_show,
+};
+
+static s32 btf_func_check_meta(struct btf_verifier_env *env,
+                              const struct btf_type *t,
+                              u32 meta_left)
+{
+       if (!t->name_off ||
+           !btf_name_valid_identifier(env->btf, t->name_off)) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+
+       if (btf_type_vlen(t)) {
+               btf_verifier_log_type(env, t, "vlen != 0");
+               return -EINVAL;
+       }
+
+       if (btf_type_kflag(t)) {
+               btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+               return -EINVAL;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       return 0;
+}
+
+static struct btf_kind_operations func_ops = {
+       .check_meta = btf_func_check_meta,
+       .resolve = btf_df_resolve,
+       .check_member = btf_df_check_member,
+       .check_kflag_member = btf_df_check_kflag_member,
+       .log_details = btf_ref_type_log,
+       .seq_show = btf_df_seq_show,
+};
+
+static int btf_func_proto_check(struct btf_verifier_env *env,
+                               const struct btf_type *t)
+{
+       const struct btf_type *ret_type;
+       const struct btf_param *args;
+       const struct btf *btf;
+       u16 nr_args, i;
+       int err;
+
+       btf = env->btf;
+       args = (const struct btf_param *)(t + 1);
+       nr_args = btf_type_vlen(t);
+
+       /* Check func return type which could be "void" (t->type == 0) */
+       if (t->type) {
+               u32 ret_type_id = t->type;
+
+               ret_type = btf_type_by_id(btf, ret_type_id);
+               if (!ret_type) {
+                       btf_verifier_log_type(env, t, "Invalid return type");
+                       return -EINVAL;
+               }
+
+               if (btf_type_needs_resolve(ret_type) &&
+                   !env_type_is_resolved(env, ret_type_id)) {
+                       err = btf_resolve(env, ret_type, ret_type_id);
+                       if (err)
+                               return err;
+               }
+
+               /* Ensure the return type is a type that has a size */
+               if (!btf_type_id_size(btf, &ret_type_id, NULL)) {
+                       btf_verifier_log_type(env, t, "Invalid return type");
+                       return -EINVAL;
+               }
+       }
+
+       if (!nr_args)
+               return 0;
+
+       /* Last func arg type_id could be 0 if it is a vararg */
+       if (!args[nr_args - 1].type) {
+               if (args[nr_args - 1].name_off) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u",
+                                             nr_args);
+                       return -EINVAL;
+               }
+               nr_args--;
+       }
+
+       err = 0;
+       for (i = 0; i < nr_args; i++) {
+               const struct btf_type *arg_type;
+               u32 arg_type_id;
+
+               arg_type_id = args[i].type;
+               arg_type = btf_type_by_id(btf, arg_type_id);
+               if (!arg_type) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (args[i].name_off &&
+                   (!btf_name_offset_valid(btf, args[i].name_off) ||
+                    !btf_name_valid_identifier(btf, args[i].name_off))) {
+                       btf_verifier_log_type(env, t,
+                                             "Invalid arg#%u", i + 1);
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (btf_type_needs_resolve(arg_type) &&
+                   !env_type_is_resolved(env, arg_type_id)) {
+                       err = btf_resolve(env, arg_type, arg_type_id);
+                       if (err)
+                               break;
+               }
+
+               if (!btf_type_id_size(btf, &arg_type_id, NULL)) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
+                       err = -EINVAL;
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static int btf_func_check(struct btf_verifier_env *env,
+                         const struct btf_type *t)
+{
+       const struct btf_type *proto_type;
+       const struct btf_param *args;
+       const struct btf *btf;
+       u16 nr_args, i;
+
+       btf = env->btf;
+       proto_type = btf_type_by_id(btf, t->type);
+
+       if (!proto_type || !btf_type_is_func_proto(proto_type)) {
+               btf_verifier_log_type(env, t, "Invalid type_id");
+               return -EINVAL;
+       }
+
+       args = (const struct btf_param *)(proto_type + 1);
+       nr_args = btf_type_vlen(proto_type);
+       for (i = 0; i < nr_args; i++) {
+               if (!args[i].name_off && args[i].type) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
        [BTF_KIND_INT] = &int_ops,
        [BTF_KIND_PTR] = &ptr_ops,
@@ -1792,6 +2431,8 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
        [BTF_KIND_VOLATILE] = &modifier_ops,
        [BTF_KIND_CONST] = &modifier_ops,
        [BTF_KIND_RESTRICT] = &modifier_ops,
+       [BTF_KIND_FUNC] = &func_ops,
+       [BTF_KIND_FUNC_PROTO] = &func_proto_ops,
 };
 
 static s32 btf_check_meta(struct btf_verifier_env *env,
@@ -1863,30 +2504,6 @@ static int btf_check_all_metas(struct btf_verifier_env *env)
        return 0;
 }
 
-static int btf_resolve(struct btf_verifier_env *env,
-                      const struct btf_type *t, u32 type_id)
-{
-       const struct resolve_vertex *v;
-       int err = 0;
-
-       env->resolve_mode = RESOLVE_TBD;
-       env_stack_push(env, t, type_id);
-       while (!err && (v = env_stack_peak(env))) {
-               env->log_type_id = v->type_id;
-               err = btf_type_ops(v->t)->resolve(env, v);
-       }
-
-       env->log_type_id = type_id;
-       if (err == -E2BIG)
-               btf_verifier_log_type(env, t,
-                                     "Exceeded max resolving depth:%u",
-                                     MAX_RESOLVE_DEPTH);
-       else if (err == -EEXIST)
-               btf_verifier_log_type(env, t, "Loop detected");
-
-       return err;
-}
-
 static bool btf_resolve_valid(struct btf_verifier_env *env,
                              const struct btf_type *t,
                              u32 type_id)
@@ -1920,6 +2537,39 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
        return false;
 }
 
+static int btf_resolve(struct btf_verifier_env *env,
+                      const struct btf_type *t, u32 type_id)
+{
+       u32 save_log_type_id = env->log_type_id;
+       const struct resolve_vertex *v;
+       int err = 0;
+
+       env->resolve_mode = RESOLVE_TBD;
+       env_stack_push(env, t, type_id);
+       while (!err && (v = env_stack_peak(env))) {
+               env->log_type_id = v->type_id;
+               err = btf_type_ops(v->t)->resolve(env, v);
+       }
+
+       env->log_type_id = type_id;
+       if (err == -E2BIG) {
+               btf_verifier_log_type(env, t,
+                                     "Exceeded max resolving depth:%u",
+                                     MAX_RESOLVE_DEPTH);
+       } else if (err == -EEXIST) {
+               btf_verifier_log_type(env, t, "Loop detected");
+       }
+
+       /* Final sanity check */
+       if (!err && !btf_resolve_valid(env, t, type_id)) {
+               btf_verifier_log_type(env, t, "Invalid resolve state");
+               err = -EINVAL;
+       }
+
+       env->log_type_id = save_log_type_id;
+       return err;
+}
+
 static int btf_check_all_types(struct btf_verifier_env *env)
 {
        struct btf *btf = env->btf;
@@ -1942,10 +2592,16 @@ static int btf_check_all_types(struct btf_verifier_env *env)
                                return err;
                }
 
-               if (btf_type_needs_resolve(t) &&
-                   !btf_resolve_valid(env, t, type_id)) {
-                       btf_verifier_log_type(env, t, "Invalid resolve state");
-                       return -EINVAL;
+               if (btf_type_is_func_proto(t)) {
+                       err = btf_func_proto_check(env, t);
+                       if (err)
+                               return err;
+               }
+
+               if (btf_type_is_func(t)) {
+                       err = btf_func_check(env, t);
+                       if (err)
+                               return err;
                }
        }
 
index 1a796e0..38de580 100644 (file)
  * Kris Katterjohn - Added many additional checks in bpf_check_classic()
  */
 
+#include <uapi/linux/btf.h>
 #include <linux/filter.h>
 #include <linux/skbuff.h>
 #include <linux/vmalloc.h>
 #include <linux/random.h>
 #include <linux/moduleloader.h>
 #include <linux/bpf.h>
+#include <linux/btf.h>
 #include <linux/frame.h>
 #include <linux/rbtree_latch.h>
 #include <linux/kallsyms.h>
@@ -103,6 +105,91 @@ struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags)
 }
 EXPORT_SYMBOL_GPL(bpf_prog_alloc);
 
+int bpf_prog_alloc_jited_linfo(struct bpf_prog *prog)
+{
+       if (!prog->aux->nr_linfo || !prog->jit_requested)
+               return 0;
+
+       prog->aux->jited_linfo = kcalloc(prog->aux->nr_linfo,
+                                        sizeof(*prog->aux->jited_linfo),
+                                        GFP_KERNEL | __GFP_NOWARN);
+       if (!prog->aux->jited_linfo)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void bpf_prog_free_jited_linfo(struct bpf_prog *prog)
+{
+       kfree(prog->aux->jited_linfo);
+       prog->aux->jited_linfo = NULL;
+}
+
+void bpf_prog_free_unused_jited_linfo(struct bpf_prog *prog)
+{
+       if (prog->aux->jited_linfo && !prog->aux->jited_linfo[0])
+               bpf_prog_free_jited_linfo(prog);
+}
+
+/* The jit engine is responsible to provide an array
+ * for insn_off to the jited_off mapping (insn_to_jit_off).
+ *
+ * The idx to this array is the insn_off.  Hence, the insn_off
+ * here is relative to the prog itself instead of the main prog.
+ * This array has one entry for each xlated bpf insn.
+ *
+ * jited_off is the byte off to the last byte of the jited insn.
+ *
+ * Hence, with
+ * insn_start:
+ *      The first bpf insn off of the prog.  The insn off
+ *      here is relative to the main prog.
+ *      e.g. if prog is a subprog, insn_start > 0
+ * linfo_idx:
+ *      The prog's idx to prog->aux->linfo and jited_linfo
+ *
+ * jited_linfo[linfo_idx] = prog->bpf_func
+ *
+ * For i > linfo_idx,
+ *
+ * jited_linfo[i] = prog->bpf_func +
+ *     insn_to_jit_off[linfo[i].insn_off - insn_start - 1]
+ */
+void bpf_prog_fill_jited_linfo(struct bpf_prog *prog,
+                              const u32 *insn_to_jit_off)
+{
+       u32 linfo_idx, insn_start, insn_end, nr_linfo, i;
+       const struct bpf_line_info *linfo;
+       void **jited_linfo;
+
+       if (!prog->aux->jited_linfo)
+               /* Userspace did not provide linfo */
+               return;
+
+       linfo_idx = prog->aux->linfo_idx;
+       linfo = &prog->aux->linfo[linfo_idx];
+       insn_start = linfo[0].insn_off;
+       insn_end = insn_start + prog->len;
+
+       jited_linfo = &prog->aux->jited_linfo[linfo_idx];
+       jited_linfo[0] = prog->bpf_func;
+
+       nr_linfo = prog->aux->nr_linfo - linfo_idx;
+
+       for (i = 1; i < nr_linfo && linfo[i].insn_off < insn_end; i++)
+               /* The verifier ensures that linfo[i].insn_off is
+                * strictly increasing
+                */
+               jited_linfo[i] = prog->bpf_func +
+                       insn_to_jit_off[linfo[i].insn_off - insn_start - 1];
+}
+
+void bpf_prog_free_linfo(struct bpf_prog *prog)
+{
+       bpf_prog_free_jited_linfo(prog);
+       kvfree(prog->aux->linfo);
+}
+
 struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
                                  gfp_t gfp_extra_flags)
 {
@@ -292,6 +379,26 @@ static int bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta,
        return ret;
 }
 
+static void bpf_adj_linfo(struct bpf_prog *prog, u32 off, u32 delta)
+{
+       struct bpf_line_info *linfo;
+       u32 i, nr_linfo;
+
+       nr_linfo = prog->aux->nr_linfo;
+       if (!nr_linfo || !delta)
+               return;
+
+       linfo = prog->aux->linfo;
+
+       for (i = 0; i < nr_linfo; i++)
+               if (off < linfo[i].insn_off)
+                       break;
+
+       /* Push all off < linfo[i].insn_off by delta */
+       for (; i < nr_linfo; i++)
+               linfo[i].insn_off += delta;
+}
+
 struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
                                       const struct bpf_insn *patch, u32 len)
 {
@@ -347,6 +454,8 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
         */
        BUG_ON(bpf_adj_branches(prog_adj, off, insn_delta, false));
 
+       bpf_adj_linfo(prog_adj, off, insn_delta);
+
        return prog_adj;
 }
 
@@ -365,13 +474,11 @@ void bpf_prog_kallsyms_del_all(struct bpf_prog *fp)
 }
 
 #ifdef CONFIG_BPF_JIT
-# define BPF_JIT_LIMIT_DEFAULT (PAGE_SIZE * 40000)
-
 /* All BPF JIT sysctl knobs here. */
 int bpf_jit_enable   __read_mostly = IS_BUILTIN(CONFIG_BPF_JIT_ALWAYS_ON);
 int bpf_jit_harden   __read_mostly;
 int bpf_jit_kallsyms __read_mostly;
-int bpf_jit_limit    __read_mostly = BPF_JIT_LIMIT_DEFAULT;
+long bpf_jit_limit   __read_mostly;
 
 static __always_inline void
 bpf_get_prog_addr_region(const struct bpf_prog *prog,
@@ -390,6 +497,8 @@ bpf_get_prog_addr_region(const struct bpf_prog *prog,
 static void bpf_get_prog_name(const struct bpf_prog *prog, char *sym)
 {
        const char *end = sym + KSYM_NAME_LEN;
+       const struct btf_type *type;
+       const char *func_name;
 
        BUILD_BUG_ON(sizeof("bpf_prog_") +
                     sizeof(prog->tag) * 2 +
@@ -404,6 +513,16 @@ static void bpf_get_prog_name(const struct bpf_prog *prog, char *sym)
 
        sym += snprintf(sym, KSYM_NAME_LEN, "bpf_prog_");
        sym  = bin2hex(sym, prog->tag, sizeof(prog->tag));
+
+       /* prog->aux->name will be ignored if full btf name is available */
+       if (prog->aux->func_info_cnt) {
+               type = btf_type_by_id(prog->aux->btf,
+                                     prog->aux->func_info[prog->aux->func_idx].type_id);
+               func_name = btf_name_by_offset(prog->aux->btf, type->name_off);
+               snprintf(sym, (size_t)(end - sym), "_%s", func_name);
+               return;
+       }
+
        if (prog->aux->name[0])
                snprintf(sym, (size_t)(end - sym), "_%s", prog->aux->name);
        else
@@ -580,16 +699,27 @@ int bpf_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
 
 static atomic_long_t bpf_jit_current;
 
+/* Can be overridden by an arch's JIT compiler if it has a custom,
+ * dedicated BPF backend memory area, or if neither of the two
+ * below apply.
+ */
+u64 __weak bpf_jit_alloc_exec_limit(void)
+{
 #if defined(MODULES_VADDR)
+       return MODULES_END - MODULES_VADDR;
+#else
+       return VMALLOC_END - VMALLOC_START;
+#endif
+}
+
 static int __init bpf_jit_charge_init(void)
 {
        /* Only used as heuristic here to derive limit. */
-       bpf_jit_limit = min_t(u64, round_up((MODULES_END - MODULES_VADDR) >> 2,
-                                           PAGE_SIZE), INT_MAX);
+       bpf_jit_limit = min_t(u64, round_up(bpf_jit_alloc_exec_limit() >> 2,
+                                           PAGE_SIZE), LONG_MAX);
        return 0;
 }
 pure_initcall(bpf_jit_charge_init);
-#endif
 
 static int bpf_jit_charge_modmem(u32 pages)
 {
@@ -609,6 +739,16 @@ static void bpf_jit_uncharge_modmem(u32 pages)
        atomic_long_sub(pages, &bpf_jit_current);
 }
 
+void *__weak bpf_jit_alloc_exec(unsigned long size)
+{
+       return module_alloc(size);
+}
+
+void __weak bpf_jit_free_exec(void *addr)
+{
+       module_memfree(addr);
+}
+
 struct bpf_binary_header *
 bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
                     unsigned int alignment,
@@ -626,7 +766,7 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
 
        if (bpf_jit_charge_modmem(pages))
                return NULL;
-       hdr = module_alloc(size);
+       hdr = bpf_jit_alloc_exec(size);
        if (!hdr) {
                bpf_jit_uncharge_modmem(pages);
                return NULL;
@@ -650,7 +790,7 @@ void bpf_jit_binary_free(struct bpf_binary_header *hdr)
 {
        u32 pages = hdr->pages;
 
-       module_memfree(hdr);
+       bpf_jit_free_exec(hdr);
        bpf_jit_uncharge_modmem(pages);
 }
 
@@ -672,6 +812,40 @@ void __weak bpf_jit_free(struct bpf_prog *fp)
        bpf_prog_unlock_free(fp);
 }
 
+int bpf_jit_get_func_addr(const struct bpf_prog *prog,
+                         const struct bpf_insn *insn, bool extra_pass,
+                         u64 *func_addr, bool *func_addr_fixed)
+{
+       s16 off = insn->off;
+       s32 imm = insn->imm;
+       u8 *addr;
+
+       *func_addr_fixed = insn->src_reg != BPF_PSEUDO_CALL;
+       if (!*func_addr_fixed) {
+               /* Place-holder address till the last pass has collected
+                * all addresses for JITed subprograms in which case we
+                * can pick them up from prog->aux.
+                */
+               if (!extra_pass)
+                       addr = NULL;
+               else if (prog->aux->func &&
+                        off >= 0 && off < prog->aux->func_cnt)
+                       addr = (u8 *)prog->aux->func[off]->bpf_func;
+               else
+                       return -EINVAL;
+       } else {
+               /* Address of a BPF helper call. Since part of the core
+                * kernel, it's always at a fixed location. __bpf_call_base
+                * and the helper with imm relative to it are both in core
+                * kernel.
+                */
+               addr = (u8 *)__bpf_call_base + imm;
+       }
+
+       *func_addr = (unsigned long)addr;
+       return 0;
+}
+
 static int bpf_jit_blind_insn(const struct bpf_insn *from,
                              const struct bpf_insn *aux,
                              struct bpf_insn *to_buff)
@@ -875,32 +1049,34 @@ EXPORT_SYMBOL_GPL(__bpf_call_base);
 #define BPF_INSN_MAP(INSN_2, INSN_3)           \
        /* 32 bit ALU operations. */            \
        /*   Register based. */                 \
-       INSN_3(ALU, ADD, X),                    \
-       INSN_3(ALU, SUB, X),                    \
-       INSN_3(ALU, AND, X),                    \
-       INSN_3(ALU, OR,  X),                    \
-       INSN_3(ALU, LSH, X),                    \
-       INSN_3(ALU, RSH, X),                    \
-       INSN_3(ALU, XOR, X),                    \
-       INSN_3(ALU, MUL, X),                    \
-       INSN_3(ALU, MOV, X),                    \
-       INSN_3(ALU, DIV, X),                    \
-       INSN_3(ALU, MOD, X),                    \
+       INSN_3(ALU, ADD,  X),                   \
+       INSN_3(ALU, SUB,  X),                   \
+       INSN_3(ALU, AND,  X),                   \
+       INSN_3(ALU, OR,   X),                   \
+       INSN_3(ALU, LSH,  X),                   \
+       INSN_3(ALU, RSH,  X),                   \
+       INSN_3(ALU, XOR,  X),                   \
+       INSN_3(ALU, MUL,  X),                   \
+       INSN_3(ALU, MOV,  X),                   \
+       INSN_3(ALU, ARSH, X),                   \
+       INSN_3(ALU, DIV,  X),                   \
+       INSN_3(ALU, MOD,  X),                   \
        INSN_2(ALU, NEG),                       \
        INSN_3(ALU, END, TO_BE),                \
        INSN_3(ALU, END, TO_LE),                \
        /*   Immediate based. */                \
-       INSN_3(ALU, ADD, K),                    \
-       INSN_3(ALU, SUB, K),                    \
-       INSN_3(ALU, AND, K),                    \
-       INSN_3(ALU, OR,  K),                    \
-       INSN_3(ALU, LSH, K),                    \
-       INSN_3(ALU, RSH, K),                    \
-       INSN_3(ALU, XOR, K),                    \
-       INSN_3(ALU, MUL, K),                    \
-       INSN_3(ALU, MOV, K),                    \
-       INSN_3(ALU, DIV, K),                    \
-       INSN_3(ALU, MOD, K),                    \
+       INSN_3(ALU, ADD,  K),                   \
+       INSN_3(ALU, SUB,  K),                   \
+       INSN_3(ALU, AND,  K),                   \
+       INSN_3(ALU, OR,   K),                   \
+       INSN_3(ALU, LSH,  K),                   \
+       INSN_3(ALU, RSH,  K),                   \
+       INSN_3(ALU, XOR,  K),                   \
+       INSN_3(ALU, MUL,  K),                   \
+       INSN_3(ALU, MOV,  K),                   \
+       INSN_3(ALU, ARSH, K),                   \
+       INSN_3(ALU, DIV,  K),                   \
+       INSN_3(ALU, MOD,  K),                   \
        /* 64 bit ALU operations. */            \
        /*   Register based. */                 \
        INSN_3(ALU64, ADD,  X),                 \
@@ -1079,6 +1255,12 @@ select_insn:
                DST = (u64) (u32) insn[0].imm | ((u64) (u32) insn[1].imm) << 32;
                insn++;
                CONT;
+       ALU_ARSH_X:
+               DST = (u64) (u32) ((*(s32 *) &DST) >> SRC);
+               CONT;
+       ALU_ARSH_K:
+               DST = (u64) (u32) ((*(s32 *) &DST) >> IMM);
+               CONT;
        ALU64_ARSH_X:
                (*(s64 *) &DST) >>= SRC;
                CONT;
@@ -1525,13 +1707,20 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
         * be JITed, but falls back to the interpreter.
         */
        if (!bpf_prog_is_dev_bound(fp->aux)) {
+               *err = bpf_prog_alloc_jited_linfo(fp);
+               if (*err)
+                       return fp;
+
                fp = bpf_int_jit_compile(fp);
-#ifdef CONFIG_BPF_JIT_ALWAYS_ON
                if (!fp->jited) {
+                       bpf_prog_free_jited_linfo(fp);
+#ifdef CONFIG_BPF_JIT_ALWAYS_ON
                        *err = -ENOTSUPP;
                        return fp;
-               }
 #endif
+               } else {
+                       bpf_prog_free_unused_jited_linfo(fp);
+               }
        } else {
                *err = bpf_prog_offload_compile(fp);
                if (*err)
index 24aac0d..8974b37 100644 (file)
@@ -183,7 +183,7 @@ static struct sk_buff *cpu_map_build_skb(struct bpf_cpu_map_entry *rcpu,
         * is not at a fixed memory location, with mixed length
         * packets, which is bad for cache-line hotness.
         */
-       frame_size = SKB_DATA_ALIGN(xdpf->len) + xdpf->headroom +
+       frame_size = SKB_DATA_ALIGN(xdpf->len + xdpf->headroom) +
                SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 
        pkt_data_start = xdpf->data - xdpf->headroom;
index 2c17902..4b7c767 100644 (file)
@@ -23,7 +23,7 @@
 
 #define HTAB_CREATE_FLAG_MASK                                          \
        (BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE |    \
-        BPF_F_RDONLY | BPF_F_WRONLY)
+        BPF_F_RDONLY | BPF_F_WRONLY | BPF_F_ZERO_SEED)
 
 struct bucket {
        struct hlist_nulls_head head;
@@ -244,6 +244,7 @@ static int htab_map_alloc_check(union bpf_attr *attr)
         */
        bool percpu_lru = (attr->map_flags & BPF_F_NO_COMMON_LRU);
        bool prealloc = !(attr->map_flags & BPF_F_NO_PREALLOC);
+       bool zero_seed = (attr->map_flags & BPF_F_ZERO_SEED);
        int numa_node = bpf_map_attr_numa_node(attr);
 
        BUILD_BUG_ON(offsetof(struct htab_elem, htab) !=
@@ -257,6 +258,10 @@ static int htab_map_alloc_check(union bpf_attr *attr)
                 */
                return -EPERM;
 
+       if (zero_seed && !capable(CAP_SYS_ADMIN))
+               /* Guard against local DoS, and discourage production use. */
+               return -EPERM;
+
        if (attr->map_flags & ~HTAB_CREATE_FLAG_MASK)
                /* reserved bits should not be used */
                return -EINVAL;
@@ -373,7 +378,11 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
        if (!htab->buckets)
                goto free_htab;
 
-       htab->hashrnd = get_random_int();
+       if (htab->map.map_flags & BPF_F_ZERO_SEED)
+               htab->hashrnd = 0;
+       else
+               htab->hashrnd = get_random_int();
+
        for (i = 0; i < htab->n_buckets; i++) {
                INIT_HLIST_NULLS_HEAD(&htab->buckets[i].head, i);
                raw_spin_lock_init(&htab->buckets[i].lock);
index c97a8f9..07a34ef 100644 (file)
@@ -1,14 +1,15 @@
 //SPDX-License-Identifier: GPL-2.0
 #include <linux/bpf-cgroup.h>
 #include <linux/bpf.h>
+#include <linux/btf.h>
 #include <linux/bug.h>
 #include <linux/filter.h>
 #include <linux/mm.h>
 #include <linux/rbtree.h>
 #include <linux/slab.h>
+#include <uapi/linux/btf.h>
 
-DEFINE_PER_CPU(struct bpf_cgroup_storage*,
-              bpf_cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE]);
+DEFINE_PER_CPU(struct bpf_cgroup_storage*, bpf_cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE]);
 
 #ifdef CONFIG_CGROUP_BPF
 
@@ -139,7 +140,8 @@ static int cgroup_storage_update_elem(struct bpf_map *map, void *_key,
                return -ENOENT;
 
        new = kmalloc_node(sizeof(struct bpf_storage_buffer) +
-                          map->value_size, __GFP_ZERO | GFP_USER,
+                          map->value_size,
+                          __GFP_ZERO | GFP_ATOMIC | __GFP_NOWARN,
                           map->numa_node);
        if (!new)
                return -ENOMEM;
@@ -308,6 +310,85 @@ static int cgroup_storage_delete_elem(struct bpf_map *map, void *key)
        return -EINVAL;
 }
 
+static int cgroup_storage_check_btf(const struct bpf_map *map,
+                                   const struct btf *btf,
+                                   const struct btf_type *key_type,
+                                   const struct btf_type *value_type)
+{
+       struct btf_member *m;
+       u32 offset, size;
+
+       /* Key is expected to be of struct bpf_cgroup_storage_key type,
+        * which is:
+        * struct bpf_cgroup_storage_key {
+        *      __u64   cgroup_inode_id;
+        *      __u32   attach_type;
+        * };
+        */
+
+       /*
+        * Key_type must be a structure with two fields.
+        */
+       if (BTF_INFO_KIND(key_type->info) != BTF_KIND_STRUCT ||
+           BTF_INFO_VLEN(key_type->info) != 2)
+               return -EINVAL;
+
+       /*
+        * The first field must be a 64 bit integer at 0 offset.
+        */
+       m = (struct btf_member *)(key_type + 1);
+       size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, cgroup_inode_id);
+       if (!btf_member_is_reg_int(btf, key_type, m, 0, size))
+               return -EINVAL;
+
+       /*
+        * The second field must be a 32 bit integer at 64 bit offset.
+        */
+       m++;
+       offset = offsetof(struct bpf_cgroup_storage_key, attach_type);
+       size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, attach_type);
+       if (!btf_member_is_reg_int(btf, key_type, m, offset, size))
+               return -EINVAL;
+
+       return 0;
+}
+
+static void cgroup_storage_seq_show_elem(struct bpf_map *map, void *_key,
+                                        struct seq_file *m)
+{
+       enum bpf_cgroup_storage_type stype = cgroup_storage_type(map);
+       struct bpf_cgroup_storage_key *key = _key;
+       struct bpf_cgroup_storage *storage;
+       int cpu;
+
+       rcu_read_lock();
+       storage = cgroup_storage_lookup(map_to_storage(map), key, false);
+       if (!storage) {
+               rcu_read_unlock();
+               return;
+       }
+
+       btf_type_seq_show(map->btf, map->btf_key_type_id, key, m);
+       stype = cgroup_storage_type(map);
+       if (stype == BPF_CGROUP_STORAGE_SHARED) {
+               seq_puts(m, ": ");
+               btf_type_seq_show(map->btf, map->btf_value_type_id,
+                                 &READ_ONCE(storage->buf)->data[0], m);
+               seq_puts(m, "\n");
+       } else {
+               seq_puts(m, ": {\n");
+               for_each_possible_cpu(cpu) {
+                       seq_printf(m, "\tcpu%d: ", cpu);
+                       btf_type_seq_show(map->btf, map->btf_value_type_id,
+                                         per_cpu_ptr(storage->percpu_buf, cpu),
+                                         m);
+                       seq_puts(m, "\n");
+               }
+               seq_puts(m, "}\n");
+       }
+       rcu_read_unlock();
+}
+
 const struct bpf_map_ops cgroup_storage_map_ops = {
        .map_alloc = cgroup_storage_map_alloc,
        .map_free = cgroup_storage_map_free,
@@ -315,7 +396,8 @@ const struct bpf_map_ops cgroup_storage_map_ops = {
        .map_lookup_elem = cgroup_storage_lookup_elem,
        .map_update_elem = cgroup_storage_update_elem,
        .map_delete_elem = cgroup_storage_delete_elem,
-       .map_check_btf = map_check_no_btf,
+       .map_check_btf = cgroup_storage_check_btf,
+       .map_seq_show_elem = cgroup_storage_seq_show_elem,
 };
 
 int bpf_cgroup_storage_assign(struct bpf_prog *prog, struct bpf_map *_map)
index 9058317..abf1002 100644 (file)
@@ -168,20 +168,59 @@ static size_t longest_prefix_match(const struct lpm_trie *trie,
                                   const struct lpm_trie_node *node,
                                   const struct bpf_lpm_trie_key *key)
 {
-       size_t prefixlen = 0;
-       size_t i;
+       u32 limit = min(node->prefixlen, key->prefixlen);
+       u32 prefixlen = 0, i = 0;
 
-       for (i = 0; i < trie->data_size; i++) {
-               size_t b;
+       BUILD_BUG_ON(offsetof(struct lpm_trie_node, data) % sizeof(u32));
+       BUILD_BUG_ON(offsetof(struct bpf_lpm_trie_key, data) % sizeof(u32));
 
-               b = 8 - fls(node->data[i] ^ key->data[i]);
-               prefixlen += b;
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(CONFIG_64BIT)
 
-               if (prefixlen >= node->prefixlen || prefixlen >= key->prefixlen)
-                       return min(node->prefixlen, key->prefixlen);
+       /* data_size >= 16 has very small probability.
+        * We do not use a loop for optimal code generation.
+        */
+       if (trie->data_size >= 8) {
+               u64 diff = be64_to_cpu(*(__be64 *)node->data ^
+                                      *(__be64 *)key->data);
+
+               prefixlen = 64 - fls64(diff);
+               if (prefixlen >= limit)
+                       return limit;
+               if (diff)
+                       return prefixlen;
+               i = 8;
+       }
+#endif
+
+       while (trie->data_size >= i + 4) {
+               u32 diff = be32_to_cpu(*(__be32 *)&node->data[i] ^
+                                      *(__be32 *)&key->data[i]);
+
+               prefixlen += 32 - fls(diff);
+               if (prefixlen >= limit)
+                       return limit;
+               if (diff)
+                       return prefixlen;
+               i += 4;
+       }
 
-               if (b < 8)
-                       break;
+       if (trie->data_size >= i + 2) {
+               u16 diff = be16_to_cpu(*(__be16 *)&node->data[i] ^
+                                      *(__be16 *)&key->data[i]);
+
+               prefixlen += 16 - fls(diff);
+               if (prefixlen >= limit)
+                       return limit;
+               if (diff)
+                       return prefixlen;
+               i += 2;
+       }
+
+       if (trie->data_size >= i + 1) {
+               prefixlen += 8 - fls(node->data[i] ^ key->data[i]);
+
+               if (prefixlen >= limit)
+                       return limit;
        }
 
        return prefixlen;
@@ -689,6 +728,7 @@ free_stack:
 }
 
 static int trie_check_btf(const struct bpf_map *map,
+                         const struct btf *btf,
                          const struct btf_type *key_type,
                          const struct btf_type *value_type)
 {
index 8e93c47..54cf2b9 100644 (file)
@@ -33,6 +33,7 @@
 static DECLARE_RWSEM(bpf_devs_lock);
 
 struct bpf_offload_dev {
+       const struct bpf_prog_offload_ops *ops;
        struct list_head netdevs;
 };
 
@@ -106,6 +107,7 @@ int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr)
                err = -EINVAL;
                goto err_unlock;
        }
+       offload->offdev = ondev->offdev;
        prog->aux->offload = offload;
        list_add_tail(&offload->offloads, &ondev->progs);
        dev_put(offload->netdev);
@@ -121,40 +123,20 @@ err_maybe_put:
        return err;
 }
 
-static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd,
-                            struct netdev_bpf *data)
+int bpf_prog_offload_verifier_prep(struct bpf_prog *prog)
 {
-       struct bpf_prog_offload *offload = prog->aux->offload;
-       struct net_device *netdev;
-
-       ASSERT_RTNL();
-
-       if (!offload)
-               return -ENODEV;
-       netdev = offload->netdev;
-
-       data->command = cmd;
-
-       return netdev->netdev_ops->ndo_bpf(netdev, data);
-}
-
-int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env)
-{
-       struct netdev_bpf data = {};
-       int err;
-
-       data.verifier.prog = env->prog;
+       struct bpf_prog_offload *offload;
+       int ret = -ENODEV;
 
-       rtnl_lock();
-       err = __bpf_offload_ndo(env->prog, BPF_OFFLOAD_VERIFIER_PREP, &data);
-       if (err)
-               goto exit_unlock;
+       down_read(&bpf_devs_lock);
+       offload = prog->aux->offload;
+       if (offload) {
+               ret = offload->offdev->ops->prepare(prog);
+               offload->dev_state = !ret;
+       }
+       up_read(&bpf_devs_lock);
 
-       env->prog->aux->offload->dev_ops = data.verifier.ops;
-       env->prog->aux->offload->dev_state = true;
-exit_unlock:
-       rtnl_unlock();
-       return err;
+       return ret;
 }
 
 int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
@@ -166,7 +148,8 @@ int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
        down_read(&bpf_devs_lock);
        offload = env->prog->aux->offload;
        if (offload)
-               ret = offload->dev_ops->insn_hook(env, insn_idx, prev_insn_idx);
+               ret = offload->offdev->ops->insn_hook(env, insn_idx,
+                                                     prev_insn_idx);
        up_read(&bpf_devs_lock);
 
        return ret;
@@ -180,8 +163,8 @@ int bpf_prog_offload_finalize(struct bpf_verifier_env *env)
        down_read(&bpf_devs_lock);
        offload = env->prog->aux->offload;
        if (offload) {
-               if (offload->dev_ops->finalize)
-                       ret = offload->dev_ops->finalize(env);
+               if (offload->offdev->ops->finalize)
+                       ret = offload->offdev->ops->finalize(env);
                else
                        ret = 0;
        }
@@ -193,12 +176,9 @@ int bpf_prog_offload_finalize(struct bpf_verifier_env *env)
 static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
 {
        struct bpf_prog_offload *offload = prog->aux->offload;
-       struct netdev_bpf data = {};
-
-       data.offload.prog = prog;
 
        if (offload->dev_state)
-               WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data));
+               offload->offdev->ops->destroy(prog);
 
        /* Make sure BPF_PROG_GET_NEXT_ID can't find this dead program */
        bpf_prog_free_id(prog, true);
@@ -210,24 +190,22 @@ static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
 
 void bpf_prog_offload_destroy(struct bpf_prog *prog)
 {
-       rtnl_lock();
        down_write(&bpf_devs_lock);
        if (prog->aux->offload)
                __bpf_prog_offload_destroy(prog);
        up_write(&bpf_devs_lock);
-       rtnl_unlock();
 }
 
 static int bpf_prog_offload_translate(struct bpf_prog *prog)
 {
-       struct netdev_bpf data = {};
-       int ret;
-
-       data.offload.prog = prog;
+       struct bpf_prog_offload *offload;
+       int ret = -ENODEV;
 
-       rtnl_lock();
-       ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data);
-       rtnl_unlock();
+       down_read(&bpf_devs_lock);
+       offload = prog->aux->offload;
+       if (offload)
+               ret = offload->offdev->ops->translate(prog);
+       up_read(&bpf_devs_lock);
 
        return ret;
 }
@@ -655,7 +633,8 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(bpf_offload_dev_netdev_unregister);
 
-struct bpf_offload_dev *bpf_offload_dev_create(void)
+struct bpf_offload_dev *
+bpf_offload_dev_create(const struct bpf_prog_offload_ops *ops)
 {
        struct bpf_offload_dev *offdev;
        int err;
@@ -673,6 +652,7 @@ struct bpf_offload_dev *bpf_offload_dev_create(void)
        if (!offdev)
                return ERR_PTR(-ENOMEM);
 
+       offdev->ops = ops;
        INIT_LIST_HEAD(&offdev->netdevs);
 
        return offdev;
index 8bbd72d..b384ea9 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/bpf.h>
 #include <linux/list.h>
 #include <linux/slab.h>
+#include <linux/capability.h>
 #include "percpu_freelist.h"
 
 #define QUEUE_STACK_CREATE_FLAG_MASK \
@@ -45,8 +46,12 @@ static bool queue_stack_map_is_full(struct bpf_queue_stack *qs)
 /* Called from syscall */
 static int queue_stack_map_alloc_check(union bpf_attr *attr)
 {
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
        /* check sanity of attributes */
        if (attr->max_entries == 0 || attr->key_size != 0 ||
+           attr->value_size == 0 ||
            attr->map_flags & ~QUEUE_STACK_CREATE_FLAG_MASK)
                return -EINVAL;
 
@@ -63,15 +68,10 @@ static struct bpf_map *queue_stack_map_alloc(union bpf_attr *attr)
 {
        int ret, numa_node = bpf_map_attr_numa_node(attr);
        struct bpf_queue_stack *qs;
-       u32 size, value_size;
-       u64 queue_size, cost;
-
-       size = attr->max_entries + 1;
-       value_size = attr->value_size;
-
-       queue_size = sizeof(*qs) + (u64) value_size * size;
+       u64 size, queue_size, cost;
 
-       cost = queue_size;
+       size = (u64) attr->max_entries + 1;
+       cost = queue_size = sizeof(*qs) + size * attr->value_size;
        if (cost >= U32_MAX - PAGE_SIZE)
                return ERR_PTR(-E2BIG);
 
index cf5040f..0607db3 100644 (file)
@@ -456,6 +456,7 @@ static int bpf_obj_name_cpy(char *dst, const char *src)
 }
 
 int map_check_no_btf(const struct bpf_map *map,
+                    const struct btf *btf,
                     const struct btf_type *key_type,
                     const struct btf_type *value_type)
 {
@@ -478,7 +479,7 @@ static int map_check_btf(const struct bpf_map *map, const struct btf *btf,
                return -EINVAL;
 
        if (map->ops->map_check_btf)
-               ret = map->ops->map_check_btf(map, key_type, value_type);
+               ret = map->ops->map_check_btf(map, btf, key_type, value_type);
 
        return ret;
 }
@@ -1213,6 +1214,9 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
                /* bpf_prog_free_id() must be called first */
                bpf_prog_free_id(prog, do_idr_lock);
                bpf_prog_kallsyms_del_all(prog);
+               btf_put(prog->aux->btf);
+               kvfree(prog->aux->func_info);
+               bpf_prog_free_linfo(prog);
 
                call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
        }
@@ -1437,9 +1441,9 @@ bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type,
 }
 
 /* last field in 'union bpf_attr' used by this command */
-#define        BPF_PROG_LOAD_LAST_FIELD expected_attach_type
+#define        BPF_PROG_LOAD_LAST_FIELD line_info_cnt
 
-static int bpf_prog_load(union bpf_attr *attr)
+static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
 {
        enum bpf_prog_type type = attr->prog_type;
        struct bpf_prog *prog;
@@ -1450,9 +1454,14 @@ static int bpf_prog_load(union bpf_attr *attr)
        if (CHECK_ATTR(BPF_PROG_LOAD))
                return -EINVAL;
 
-       if (attr->prog_flags & ~BPF_F_STRICT_ALIGNMENT)
+       if (attr->prog_flags & ~(BPF_F_STRICT_ALIGNMENT | BPF_F_ANY_ALIGNMENT))
                return -EINVAL;
 
+       if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) &&
+           (attr->prog_flags & BPF_F_ANY_ALIGNMENT) &&
+           !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
        /* copy eBPF program license from user space */
        if (strncpy_from_user(license, u64_to_user_ptr(attr->license),
                              sizeof(license) - 1) < 0)
@@ -1464,11 +1473,6 @@ static int bpf_prog_load(union bpf_attr *attr)
 
        if (attr->insn_cnt == 0 || attr->insn_cnt > BPF_MAXINSNS)
                return -E2BIG;
-
-       if (type == BPF_PROG_TYPE_KPROBE &&
-           attr->kern_version != LINUX_VERSION_CODE)
-               return -EINVAL;
-
        if (type != BPF_PROG_TYPE_SOCKET_FILTER &&
            type != BPF_PROG_TYPE_CGROUP_SKB &&
            !capable(CAP_SYS_ADMIN))
@@ -1525,7 +1529,7 @@ static int bpf_prog_load(union bpf_attr *attr)
                goto free_prog;
 
        /* run eBPF verifier */
-       err = bpf_check(&prog, attr);
+       err = bpf_check(&prog, attr, uattr);
        if (err < 0)
                goto free_used_maps;
 
@@ -1553,6 +1557,9 @@ static int bpf_prog_load(union bpf_attr *attr)
        return err;
 
 free_used_maps:
+       bpf_prog_free_linfo(prog);
+       kvfree(prog->aux->func_info);
+       btf_put(prog->aux->btf);
        bpf_prog_kallsyms_del_subprogs(prog);
        free_used_maps(prog->aux);
 free_prog:
@@ -1597,6 +1604,7 @@ static int bpf_raw_tracepoint_release(struct inode *inode, struct file *filp)
                bpf_probe_unregister(raw_tp->btp, raw_tp->prog);
                bpf_prog_put(raw_tp->prog);
        }
+       bpf_put_raw_tracepoint(raw_tp->btp);
        kfree(raw_tp);
        return 0;
 }
@@ -1622,13 +1630,15 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
                return -EFAULT;
        tp_name[sizeof(tp_name) - 1] = 0;
 
-       btp = bpf_find_raw_tracepoint(tp_name);
+       btp = bpf_get_raw_tracepoint(tp_name);
        if (!btp)
                return -ENOENT;
 
        raw_tp = kzalloc(sizeof(*raw_tp), GFP_USER);
-       if (!raw_tp)
-               return -ENOMEM;
+       if (!raw_tp) {
+               err = -ENOMEM;
+               goto out_put_btp;
+       }
        raw_tp->btp = btp;
 
        prog = bpf_prog_get_type(attr->raw_tracepoint.prog_fd,
@@ -1656,6 +1666,8 @@ out_put_prog:
        bpf_prog_put(prog);
 out_free_tp:
        kfree(raw_tp);
+out_put_btp:
+       bpf_put_raw_tracepoint(btp);
        return err;
 }
 
@@ -2020,18 +2032,42 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog)
                        insns[i + 1].imm = 0;
                        continue;
                }
-
-               if (!bpf_dump_raw_ok() &&
-                   imm == (unsigned long)prog->aux) {
-                       insns[i].imm = 0;
-                       insns[i + 1].imm = 0;
-                       continue;
-               }
        }
 
        return insns;
 }
 
+static int set_info_rec_size(struct bpf_prog_info *info)
+{
+       /*
+        * Ensure info.*_rec_size is the same as kernel expected size
+        *
+        * or
+        *
+        * Only allow zero *_rec_size if both _rec_size and _cnt are
+        * zero.  In this case, the kernel will set the expected
+        * _rec_size back to the info.
+        */
+
+       if ((info->nr_func_info || info->func_info_rec_size) &&
+           info->func_info_rec_size != sizeof(struct bpf_func_info))
+               return -EINVAL;
+
+       if ((info->nr_line_info || info->line_info_rec_size) &&
+           info->line_info_rec_size != sizeof(struct bpf_line_info))
+               return -EINVAL;
+
+       if ((info->nr_jited_line_info || info->jited_line_info_rec_size) &&
+           info->jited_line_info_rec_size != sizeof(__u64))
+               return -EINVAL;
+
+       info->func_info_rec_size = sizeof(struct bpf_func_info);
+       info->line_info_rec_size = sizeof(struct bpf_line_info);
+       info->jited_line_info_rec_size = sizeof(__u64);
+
+       return 0;
+}
+
 static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
                                   const union bpf_attr *attr,
                                   union bpf_attr __user *uattr)
@@ -2074,11 +2110,18 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
                                return -EFAULT;
        }
 
+       err = set_info_rec_size(&info);
+       if (err)
+               return err;
+
        if (!capable(CAP_SYS_ADMIN)) {
                info.jited_prog_len = 0;
                info.xlated_prog_len = 0;
                info.nr_jited_ksyms = 0;
                info.nr_jited_func_lens = 0;
+               info.nr_func_info = 0;
+               info.nr_line_info = 0;
+               info.nr_jited_line_info = 0;
                goto done;
        }
 
@@ -2160,7 +2203,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
 
        ulen = info.nr_jited_ksyms;
        info.nr_jited_ksyms = prog->aux->func_cnt ? : 1;
-       if (info.nr_jited_ksyms && ulen) {
+       if (ulen) {
                if (bpf_dump_raw_ok()) {
                        unsigned long ksym_addr;
                        u64 __user *user_ksyms;
@@ -2191,7 +2234,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
 
        ulen = info.nr_jited_func_lens;
        info.nr_jited_func_lens = prog->aux->func_cnt ? : 1;
-       if (info.nr_jited_func_lens && ulen) {
+       if (ulen) {
                if (bpf_dump_raw_ok()) {
                        u32 __user *user_lens;
                        u32 func_len, i;
@@ -2216,6 +2259,77 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
                }
        }
 
+       if (prog->aux->btf)
+               info.btf_id = btf_id(prog->aux->btf);
+
+       ulen = info.nr_func_info;
+       info.nr_func_info = prog->aux->func_info_cnt;
+       if (info.nr_func_info && ulen) {
+               char __user *user_finfo;
+
+               user_finfo = u64_to_user_ptr(info.func_info);
+               ulen = min_t(u32, info.nr_func_info, ulen);
+               if (copy_to_user(user_finfo, prog->aux->func_info,
+                                info.func_info_rec_size * ulen))
+                       return -EFAULT;
+       }
+
+       ulen = info.nr_line_info;
+       info.nr_line_info = prog->aux->nr_linfo;
+       if (info.nr_line_info && ulen) {
+               __u8 __user *user_linfo;
+
+               user_linfo = u64_to_user_ptr(info.line_info);
+               ulen = min_t(u32, info.nr_line_info, ulen);
+               if (copy_to_user(user_linfo, prog->aux->linfo,
+                                info.line_info_rec_size * ulen))
+                       return -EFAULT;
+       }
+
+       ulen = info.nr_jited_line_info;
+       if (prog->aux->jited_linfo)
+               info.nr_jited_line_info = prog->aux->nr_linfo;
+       else
+               info.nr_jited_line_info = 0;
+       if (info.nr_jited_line_info && ulen) {
+               if (bpf_dump_raw_ok()) {
+                       __u64 __user *user_linfo;
+                       u32 i;
+
+                       user_linfo = u64_to_user_ptr(info.jited_line_info);
+                       ulen = min_t(u32, info.nr_jited_line_info, ulen);
+                       for (i = 0; i < ulen; i++) {
+                               if (put_user((__u64)(long)prog->aux->jited_linfo[i],
+                                            &user_linfo[i]))
+                                       return -EFAULT;
+                       }
+               } else {
+                       info.jited_line_info = 0;
+               }
+       }
+
+       ulen = info.nr_prog_tags;
+       info.nr_prog_tags = prog->aux->func_cnt ? : 1;
+       if (ulen) {
+               __u8 __user (*user_prog_tags)[BPF_TAG_SIZE];
+               u32 i;
+
+               user_prog_tags = u64_to_user_ptr(info.prog_tags);
+               ulen = min_t(u32, info.nr_prog_tags, ulen);
+               if (prog->aux->func_cnt) {
+                       for (i = 0; i < ulen; i++) {
+                               if (copy_to_user(user_prog_tags[i],
+                                                prog->aux->func[i]->tag,
+                                                BPF_TAG_SIZE))
+                                       return -EFAULT;
+                       }
+               } else {
+                       if (copy_to_user(user_prog_tags[0],
+                                        prog->tag, BPF_TAG_SIZE))
+                               return -EFAULT;
+               }
+       }
+
 done:
        if (copy_to_user(uinfo, &info, info_len) ||
            put_user(info_len, &uattr->info.info_len))
@@ -2501,7 +2615,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
                err = map_get_next_key(&attr);
                break;
        case BPF_PROG_LOAD:
-               err = bpf_prog_load(&attr);
+               err = bpf_prog_load(&attr, uattr);
                break;
        case BPF_OBJ_PIN:
                err = bpf_obj_pin(&attr);
index 1971ca3..71d86e3 100644 (file)
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * General Public License for more details.
  */
+#include <uapi/linux/btf.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/bpf.h>
+#include <linux/btf.h>
 #include <linux/bpf_verifier.h>
 #include <linux/filter.h>
 #include <net/netlink.h>
@@ -24,6 +26,7 @@
 #include <linux/bsearch.h>
 #include <linux/sort.h>
 #include <linux/perf_event.h>
+#include <linux/ctype.h>
 
 #include "disasm.h"
 
@@ -175,6 +178,7 @@ struct bpf_verifier_stack_elem {
 
 #define BPF_COMPLEXITY_LIMIT_INSNS     131072
 #define BPF_COMPLEXITY_LIMIT_STACK     1024
+#define BPF_COMPLEXITY_LIMIT_STATES    64
 
 #define BPF_MAP_PTR_UNPRIV     1UL
 #define BPF_MAP_PTR_POISON     ((void *)((0xeB9FUL << 1) +     \
@@ -213,6 +217,27 @@ struct bpf_call_arg_meta {
 
 static DEFINE_MUTEX(bpf_verifier_lock);
 
+static const struct bpf_line_info *
+find_linfo(const struct bpf_verifier_env *env, u32 insn_off)
+{
+       const struct bpf_line_info *linfo;
+       const struct bpf_prog *prog;
+       u32 i, nr_linfo;
+
+       prog = env->prog;
+       nr_linfo = prog->aux->nr_linfo;
+
+       if (!nr_linfo || insn_off >= prog->len)
+               return NULL;
+
+       linfo = prog->aux->linfo;
+       for (i = 1; i < nr_linfo; i++)
+               if (insn_off < linfo[i].insn_off)
+                       break;
+
+       return &linfo[i - 1];
+}
+
 void bpf_verifier_vlog(struct bpf_verifier_log *log, const char *fmt,
                       va_list args)
 {
@@ -263,6 +288,42 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...)
        va_end(args);
 }
 
+static const char *ltrim(const char *s)
+{
+       while (isspace(*s))
+               s++;
+
+       return s;
+}
+
+__printf(3, 4) static void verbose_linfo(struct bpf_verifier_env *env,
+                                        u32 insn_off,
+                                        const char *prefix_fmt, ...)
+{
+       const struct bpf_line_info *linfo;
+
+       if (!bpf_verifier_log_needed(&env->log))
+               return;
+
+       linfo = find_linfo(env, insn_off);
+       if (!linfo || linfo == env->prev_linfo)
+               return;
+
+       if (prefix_fmt) {
+               va_list args;
+
+               va_start(args, prefix_fmt);
+               bpf_verifier_vlog(&env->log, prefix_fmt, args);
+               va_end(args);
+       }
+
+       verbose(env, "%s\n",
+               ltrim(btf_name_by_offset(env->prog->aux->btf,
+                                        linfo->line_off)));
+
+       env->prev_linfo = linfo;
+}
+
 static bool type_is_pkt_pointer(enum bpf_reg_type type)
 {
        return type == PTR_TO_PACKET ||
@@ -336,12 +397,14 @@ static char slot_type_char[] = {
 static void print_liveness(struct bpf_verifier_env *env,
                           enum bpf_reg_liveness live)
 {
-       if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN))
+       if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN | REG_LIVE_DONE))
            verbose(env, "_");
        if (live & REG_LIVE_READ)
                verbose(env, "r");
        if (live & REG_LIVE_WRITTEN)
                verbose(env, "w");
+       if (live & REG_LIVE_DONE)
+               verbose(env, "D");
 }
 
 static struct bpf_func_state *func(struct bpf_verifier_env *env,
@@ -1071,6 +1134,12 @@ static int mark_reg_read(struct bpf_verifier_env *env,
                /* if read wasn't screened by an earlier write ... */
                if (writes && state->live & REG_LIVE_WRITTEN)
                        break;
+               if (parent->live & REG_LIVE_DONE) {
+                       verbose(env, "verifier BUG type %s var_off %lld off %d\n",
+                               reg_type_str[parent->type],
+                               parent->var_off.value, parent->off);
+                       return -EFAULT;
+               }
                /* ... then we depend on parent's value */
                parent->live |= REG_LIVE_READ;
                state = parent;
@@ -1217,6 +1286,10 @@ static int check_stack_write(struct bpf_verifier_env *env,
 
                /* regular write of data into stack destroys any spilled ptr */
                state->stack[spi].spilled_ptr.type = NOT_INIT;
+               /* Mark slots as STACK_MISC if they belonged to spilled ptr. */
+               if (state->stack[spi].slot_type[0] == STACK_SPILL)
+                       for (i = 0; i < BPF_REG_SIZE; i++)
+                               state->stack[spi].slot_type[i] = STACK_MISC;
 
                /* only mark the slot as written if all 8 bytes were written
                 * otherwise read propagation may incorrectly stop too soon
@@ -1234,6 +1307,7 @@ static int check_stack_write(struct bpf_verifier_env *env,
                    register_is_null(&cur->regs[value_regno]))
                        type = STACK_ZERO;
 
+               /* Mark slots affected by this stack write. */
                for (i = 0; i < size; i++)
                        state->stack[spi].slot_type[(slot - i) % BPF_REG_SIZE] =
                                type;
@@ -1455,6 +1529,17 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
                verbose(env, "R%d offset is outside of the packet\n", regno);
                return err;
        }
+
+       /* __check_packet_access has made sure "off + size - 1" is within u16.
+        * reg->umax_value can't be bigger than MAX_PACKET_OFF which is 0xffff,
+        * otherwise find_good_pkt_pointers would have refused to set range info
+        * that __check_packet_access would have rejected this pkt access.
+        * Therefore, "off + reg->umax_value + size - 1" won't overflow u32.
+        */
+       env->prog->aux->max_pkt_offset =
+               max_t(u32, env->prog->aux->max_pkt_offset,
+                     off + reg->umax_value + size - 1);
+
        return err;
 }
 
@@ -3570,12 +3655,15 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
                        return err;
 
                if (BPF_SRC(insn->code) == BPF_X) {
+                       struct bpf_reg_state *src_reg = regs + insn->src_reg;
+                       struct bpf_reg_state *dst_reg = regs + insn->dst_reg;
+
                        if (BPF_CLASS(insn->code) == BPF_ALU64) {
                                /* case: R1 = R2
                                 * copy register state to dest reg
                                 */
-                               regs[insn->dst_reg] = regs[insn->src_reg];
-                               regs[insn->dst_reg].live |= REG_LIVE_WRITTEN;
+                               *dst_reg = *src_reg;
+                               dst_reg->live |= REG_LIVE_WRITTEN;
                        } else {
                                /* R1 = (u32) R2 */
                                if (is_pointer_value(env, insn->src_reg)) {
@@ -3583,9 +3671,14 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
                                                "R%d partial copy of pointer\n",
                                                insn->src_reg);
                                        return -EACCES;
+                               } else if (src_reg->type == SCALAR_VALUE) {
+                                       *dst_reg = *src_reg;
+                                       dst_reg->live |= REG_LIVE_WRITTEN;
+                               } else {
+                                       mark_reg_unknown(env, regs,
+                                                        insn->dst_reg);
                                }
-                               mark_reg_unknown(env, regs, insn->dst_reg);
-                               coerce_reg_to_size(&regs[insn->dst_reg], 4);
+                               coerce_reg_to_size(dst_reg, 4);
                        }
                } else {
                        /* case: R = imm
@@ -3636,11 +3729,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
                        return -EINVAL;
                }
 
-               if (opcode == BPF_ARSH && BPF_CLASS(insn->code) != BPF_ALU64) {
-                       verbose(env, "BPF_ARSH not supported for 32 bit ALU\n");
-                       return -EINVAL;
-               }
-
                if ((opcode == BPF_LSH || opcode == BPF_RSH ||
                     opcode == BPF_ARSH) && BPF_SRC(insn->code) == BPF_K) {
                        int size = BPF_CLASS(insn->code) == BPF_ALU64 ? 64 : 32;
@@ -3751,6 +3839,85 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *vstate,
        }
 }
 
+/* compute branch direction of the expression "if (reg opcode val) goto target;"
+ * and return:
+ *  1 - branch will be taken and "goto target" will be executed
+ *  0 - branch will not be taken and fall-through to next insn
+ * -1 - unknown. Example: "if (reg < 5)" is unknown when register value range [0,10]
+ */
+static int is_branch_taken(struct bpf_reg_state *reg, u64 val, u8 opcode)
+{
+       if (__is_pointer_value(false, reg))
+               return -1;
+
+       switch (opcode) {
+       case BPF_JEQ:
+               if (tnum_is_const(reg->var_off))
+                       return !!tnum_equals_const(reg->var_off, val);
+               break;
+       case BPF_JNE:
+               if (tnum_is_const(reg->var_off))
+                       return !tnum_equals_const(reg->var_off, val);
+               break;
+       case BPF_JSET:
+               if ((~reg->var_off.mask & reg->var_off.value) & val)
+                       return 1;
+               if (!((reg->var_off.mask | reg->var_off.value) & val))
+                       return 0;
+               break;
+       case BPF_JGT:
+               if (reg->umin_value > val)
+                       return 1;
+               else if (reg->umax_value <= val)
+                       return 0;
+               break;
+       case BPF_JSGT:
+               if (reg->smin_value > (s64)val)
+                       return 1;
+               else if (reg->smax_value < (s64)val)
+                       return 0;
+               break;
+       case BPF_JLT:
+               if (reg->umax_value < val)
+                       return 1;
+               else if (reg->umin_value >= val)
+                       return 0;
+               break;
+       case BPF_JSLT:
+               if (reg->smax_value < (s64)val)
+                       return 1;
+               else if (reg->smin_value >= (s64)val)
+                       return 0;
+               break;
+       case BPF_JGE:
+               if (reg->umin_value >= val)
+                       return 1;
+               else if (reg->umax_value < val)
+                       return 0;
+               break;
+       case BPF_JSGE:
+               if (reg->smin_value >= (s64)val)
+                       return 1;
+               else if (reg->smax_value < (s64)val)
+                       return 0;
+               break;
+       case BPF_JLE:
+               if (reg->umax_value <= val)
+                       return 1;
+               else if (reg->umin_value > val)
+                       return 0;
+               break;
+       case BPF_JSLE:
+               if (reg->smax_value <= (s64)val)
+                       return 1;
+               else if (reg->smin_value > (s64)val)
+                       return 0;
+               break;
+       }
+
+       return -1;
+}
+
 /* Adjusts the register min/max values in the case that the dst_reg is the
  * variable register that we are working on, and src_reg is a constant or we're
  * simply doing a BPF_K check.
@@ -3782,6 +3949,13 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg,
                 */
                __mark_reg_known(false_reg, val);
                break;
+       case BPF_JSET:
+               false_reg->var_off = tnum_and(false_reg->var_off,
+                                             tnum_const(~val));
+               if (is_power_of_2(val))
+                       true_reg->var_off = tnum_or(true_reg->var_off,
+                                                   tnum_const(val));
+               break;
        case BPF_JGT:
                false_reg->umax_value = min(false_reg->umax_value, val);
                true_reg->umin_value = max(true_reg->umin_value, val + 1);
@@ -3854,6 +4028,13 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg,
                 */
                __mark_reg_known(false_reg, val);
                break;
+       case BPF_JSET:
+               false_reg->var_off = tnum_and(false_reg->var_off,
+                                             tnum_const(~val));
+               if (is_power_of_2(val))
+                       true_reg->var_off = tnum_or(true_reg->var_off,
+                                                   tnum_const(val));
+               break;
        case BPF_JGT:
                true_reg->umax_value = min(true_reg->umax_value, val - 1);
                false_reg->umin_value = max(false_reg->umin_value, val);
@@ -4152,21 +4333,15 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
 
        dst_reg = &regs[insn->dst_reg];
 
-       /* detect if R == 0 where R was initialized to zero earlier */
-       if (BPF_SRC(insn->code) == BPF_K &&
-           (opcode == BPF_JEQ || opcode == BPF_JNE) &&
-           dst_reg->type == SCALAR_VALUE &&
-           tnum_is_const(dst_reg->var_off)) {
-               if ((opcode == BPF_JEQ && dst_reg->var_off.value == insn->imm) ||
-                   (opcode == BPF_JNE && dst_reg->var_off.value != insn->imm)) {
-                       /* if (imm == imm) goto pc+off;
-                        * only follow the goto, ignore fall-through
-                        */
+       if (BPF_SRC(insn->code) == BPF_K) {
+               int pred = is_branch_taken(dst_reg, insn->imm, opcode);
+
+               if (pred == 1) {
+                        /* only follow the goto, ignore fall-through */
                        *insn_idx += insn->off;
                        return 0;
-               } else {
-                       /* if (imm != imm) goto pc+off;
-                        * only follow fall-through branch, since
+               } else if (pred == 0) {
+                       /* only follow fall-through branch, since
                         * that's where the program will go
                         */
                        return 0;
@@ -4477,6 +4652,7 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env)
                return 0;
 
        if (w < 0 || w >= env->prog->len) {
+               verbose_linfo(env, t, "%d: ", t);
                verbose(env, "jump out of range from insn %d to %d\n", t, w);
                return -EINVAL;
        }
@@ -4494,6 +4670,8 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env)
                insn_stack[cur_stack++] = w;
                return 1;
        } else if ((insn_state[w] & 0xF0) == DISCOVERED) {
+               verbose_linfo(env, t, "%d: ", t);
+               verbose_linfo(env, w, "%d: ", w);
                verbose(env, "back-edge from insn %d to %d\n", t, w);
                return -EINVAL;
        } else if (insn_state[w] == EXPLORED) {
@@ -4516,10 +4694,6 @@ static int check_cfg(struct bpf_verifier_env *env)
        int ret = 0;
        int i, t;
 
-       ret = check_subprogs(env);
-       if (ret < 0)
-               return ret;
-
        insn_state = kcalloc(insn_cnt, sizeof(int), GFP_KERNEL);
        if (!insn_state)
                return -ENOMEM;
@@ -4628,6 +4802,277 @@ err_free:
        return ret;
 }
 
+/* The minimum supported BTF func info size */
+#define MIN_BPF_FUNCINFO_SIZE  8
+#define MAX_FUNCINFO_REC_SIZE  252
+
+static int check_btf_func(struct bpf_verifier_env *env,
+                         const union bpf_attr *attr,
+                         union bpf_attr __user *uattr)
+{
+       u32 i, nfuncs, urec_size, min_size, prev_offset;
+       u32 krec_size = sizeof(struct bpf_func_info);
+       struct bpf_func_info *krecord;
+       const struct btf_type *type;
+       struct bpf_prog *prog;
+       const struct btf *btf;
+       void __user *urecord;
+       int ret = 0;
+
+       nfuncs = attr->func_info_cnt;
+       if (!nfuncs)
+               return 0;
+
+       if (nfuncs != env->subprog_cnt) {
+               verbose(env, "number of funcs in func_info doesn't match number of subprogs\n");
+               return -EINVAL;
+       }
+
+       urec_size = attr->func_info_rec_size;
+       if (urec_size < MIN_BPF_FUNCINFO_SIZE ||
+           urec_size > MAX_FUNCINFO_REC_SIZE ||
+           urec_size % sizeof(u32)) {
+               verbose(env, "invalid func info rec size %u\n", urec_size);
+               return -EINVAL;
+       }
+
+       prog = env->prog;
+       btf = prog->aux->btf;
+
+       urecord = u64_to_user_ptr(attr->func_info);
+       min_size = min_t(u32, krec_size, urec_size);
+
+       krecord = kvcalloc(nfuncs, krec_size, GFP_KERNEL | __GFP_NOWARN);
+       if (!krecord)
+               return -ENOMEM;
+
+       for (i = 0; i < nfuncs; i++) {
+               ret = bpf_check_uarg_tail_zero(urecord, krec_size, urec_size);
+               if (ret) {
+                       if (ret == -E2BIG) {
+                               verbose(env, "nonzero tailing record in func info");
+                               /* set the size kernel expects so loader can zero
+                                * out the rest of the record.
+                                */
+                               if (put_user(min_size, &uattr->func_info_rec_size))
+                                       ret = -EFAULT;
+                       }
+                       goto err_free;
+               }
+
+               if (copy_from_user(&krecord[i], urecord, min_size)) {
+                       ret = -EFAULT;
+                       goto err_free;
+               }
+
+               /* check insn_off */
+               if (i == 0) {
+                       if (krecord[i].insn_off) {
+                               verbose(env,
+                                       "nonzero insn_off %u for the first func info record",
+                                       krecord[i].insn_off);
+                               ret = -EINVAL;
+                               goto err_free;
+                       }
+               } else if (krecord[i].insn_off <= prev_offset) {
+                       verbose(env,
+                               "same or smaller insn offset (%u) than previous func info record (%u)",
+                               krecord[i].insn_off, prev_offset);
+                       ret = -EINVAL;
+                       goto err_free;
+               }
+
+               if (env->subprog_info[i].start != krecord[i].insn_off) {
+                       verbose(env, "func_info BTF section doesn't match subprog layout in BPF program\n");
+                       ret = -EINVAL;
+                       goto err_free;
+               }
+
+               /* check type_id */
+               type = btf_type_by_id(btf, krecord[i].type_id);
+               if (!type || BTF_INFO_KIND(type->info) != BTF_KIND_FUNC) {
+                       verbose(env, "invalid type id %d in func info",
+                               krecord[i].type_id);
+                       ret = -EINVAL;
+                       goto err_free;
+               }
+
+               prev_offset = krecord[i].insn_off;
+               urecord += urec_size;
+       }
+
+       prog->aux->func_info = krecord;
+       prog->aux->func_info_cnt = nfuncs;
+       return 0;
+
+err_free:
+       kvfree(krecord);
+       return ret;
+}
+
+static void adjust_btf_func(struct bpf_verifier_env *env)
+{
+       int i;
+
+       if (!env->prog->aux->func_info)
+               return;
+
+       for (i = 0; i < env->subprog_cnt; i++)
+               env->prog->aux->func_info[i].insn_off = env->subprog_info[i].start;
+}
+
+#define MIN_BPF_LINEINFO_SIZE  (offsetof(struct bpf_line_info, line_col) + \
+               sizeof(((struct bpf_line_info *)(0))->line_col))
+#define MAX_LINEINFO_REC_SIZE  MAX_FUNCINFO_REC_SIZE
+
+static int check_btf_line(struct bpf_verifier_env *env,
+                         const union bpf_attr *attr,
+                         union bpf_attr __user *uattr)
+{
+       u32 i, s, nr_linfo, ncopy, expected_size, rec_size, prev_offset = 0;
+       struct bpf_subprog_info *sub;
+       struct bpf_line_info *linfo;
+       struct bpf_prog *prog;
+       const struct btf *btf;
+       void __user *ulinfo;
+       int err;
+
+       nr_linfo = attr->line_info_cnt;
+       if (!nr_linfo)
+               return 0;
+
+       rec_size = attr->line_info_rec_size;
+       if (rec_size < MIN_BPF_LINEINFO_SIZE ||
+           rec_size > MAX_LINEINFO_REC_SIZE ||
+           rec_size & (sizeof(u32) - 1))
+               return -EINVAL;
+
+       /* Need to zero it in case the userspace may
+        * pass in a smaller bpf_line_info object.
+        */
+       linfo = kvcalloc(nr_linfo, sizeof(struct bpf_line_info),
+                        GFP_KERNEL | __GFP_NOWARN);
+       if (!linfo)
+               return -ENOMEM;
+
+       prog = env->prog;
+       btf = prog->aux->btf;
+
+       s = 0;
+       sub = env->subprog_info;
+       ulinfo = u64_to_user_ptr(attr->line_info);
+       expected_size = sizeof(struct bpf_line_info);
+       ncopy = min_t(u32, expected_size, rec_size);
+       for (i = 0; i < nr_linfo; i++) {
+               err = bpf_check_uarg_tail_zero(ulinfo, expected_size, rec_size);
+               if (err) {
+                       if (err == -E2BIG) {
+                               verbose(env, "nonzero tailing record in line_info");
+                               if (put_user(expected_size,
+                                            &uattr->line_info_rec_size))
+                                       err = -EFAULT;
+                       }
+                       goto err_free;
+               }
+
+               if (copy_from_user(&linfo[i], ulinfo, ncopy)) {
+                       err = -EFAULT;
+                       goto err_free;
+               }
+
+               /*
+                * Check insn_off to ensure
+                * 1) strictly increasing AND
+                * 2) bounded by prog->len
+                *
+                * The linfo[0].insn_off == 0 check logically falls into
+                * the later "missing bpf_line_info for func..." case
+                * because the first linfo[0].insn_off must be the
+                * first sub also and the first sub must have
+                * subprog_info[0].start == 0.
+                */
+               if ((i && linfo[i].insn_off <= prev_offset) ||
+                   linfo[i].insn_off >= prog->len) {
+                       verbose(env, "Invalid line_info[%u].insn_off:%u (prev_offset:%u prog->len:%u)\n",
+                               i, linfo[i].insn_off, prev_offset,
+                               prog->len);
+                       err = -EINVAL;
+                       goto err_free;
+               }
+
+               if (!prog->insnsi[linfo[i].insn_off].code) {
+                       verbose(env,
+                               "Invalid insn code at line_info[%u].insn_off\n",
+                               i);
+                       err = -EINVAL;
+                       goto err_free;
+               }
+
+               if (!btf_name_by_offset(btf, linfo[i].line_off) ||
+                   !btf_name_by_offset(btf, linfo[i].file_name_off)) {
+                       verbose(env, "Invalid line_info[%u].line_off or .file_name_off\n", i);
+                       err = -EINVAL;
+                       goto err_free;
+               }
+
+               if (s != env->subprog_cnt) {
+                       if (linfo[i].insn_off == sub[s].start) {
+                               sub[s].linfo_idx = i;
+                               s++;
+                       } else if (sub[s].start < linfo[i].insn_off) {
+                               verbose(env, "missing bpf_line_info for func#%u\n", s);
+                               err = -EINVAL;
+                               goto err_free;
+                       }
+               }
+
+               prev_offset = linfo[i].insn_off;
+               ulinfo += rec_size;
+       }
+
+       if (s != env->subprog_cnt) {
+               verbose(env, "missing bpf_line_info for %u funcs starting from func#%u\n",
+                       env->subprog_cnt - s, s);
+               err = -EINVAL;
+               goto err_free;
+       }
+
+       prog->aux->linfo = linfo;
+       prog->aux->nr_linfo = nr_linfo;
+
+       return 0;
+
+err_free:
+       kvfree(linfo);
+       return err;
+}
+
+static int check_btf_info(struct bpf_verifier_env *env,
+                         const union bpf_attr *attr,
+                         union bpf_attr __user *uattr)
+{
+       struct btf *btf;
+       int err;
+
+       if (!attr->func_info_cnt && !attr->line_info_cnt)
+               return 0;
+
+       btf = btf_get_by_fd(attr->prog_btf_fd);
+       if (IS_ERR(btf))
+               return PTR_ERR(btf);
+       env->prog->aux->btf = btf;
+
+       err = check_btf_func(env, attr, uattr);
+       if (err)
+               return err;
+
+       err = check_btf_line(env, attr, uattr);
+       if (err)
+               return err;
+
+       return 0;
+}
+
 /* check %cur's range satisfies %old's */
 static bool range_within(struct bpf_reg_state *old,
                         struct bpf_reg_state *cur)
@@ -4674,6 +5119,102 @@ static bool check_ids(u32 old_id, u32 cur_id, struct idpair *idmap)
        return false;
 }
 
+static void clean_func_state(struct bpf_verifier_env *env,
+                            struct bpf_func_state *st)
+{
+       enum bpf_reg_liveness live;
+       int i, j;
+
+       for (i = 0; i < BPF_REG_FP; i++) {
+               live = st->regs[i].live;
+               /* liveness must not touch this register anymore */
+               st->regs[i].live |= REG_LIVE_DONE;
+               if (!(live & REG_LIVE_READ))
+                       /* since the register is unused, clear its state
+                        * to make further comparison simpler
+                        */
+                       __mark_reg_not_init(&st->regs[i]);
+       }
+
+       for (i = 0; i < st->allocated_stack / BPF_REG_SIZE; i++) {
+               live = st->stack[i].spilled_ptr.live;
+               /* liveness must not touch this stack slot anymore */
+               st->stack[i].spilled_ptr.live |= REG_LIVE_DONE;
+               if (!(live & REG_LIVE_READ)) {
+                       __mark_reg_not_init(&st->stack[i].spilled_ptr);
+                       for (j = 0; j < BPF_REG_SIZE; j++)
+                               st->stack[i].slot_type[j] = STACK_INVALID;
+               }
+       }
+}
+
+static void clean_verifier_state(struct bpf_verifier_env *env,
+                                struct bpf_verifier_state *st)
+{
+       int i;
+
+       if (st->frame[0]->regs[0].live & REG_LIVE_DONE)
+               /* all regs in this state in all frames were already marked */
+               return;
+
+       for (i = 0; i <= st->curframe; i++)
+               clean_func_state(env, st->frame[i]);
+}
+
+/* the parentage chains form a tree.
+ * the verifier states are added to state lists at given insn and
+ * pushed into state stack for future exploration.
+ * when the verifier reaches bpf_exit insn some of the verifer states
+ * stored in the state lists have their final liveness state already,
+ * but a lot of states will get revised from liveness point of view when
+ * the verifier explores other branches.
+ * Example:
+ * 1: r0 = 1
+ * 2: if r1 == 100 goto pc+1
+ * 3: r0 = 2
+ * 4: exit
+ * when the verifier reaches exit insn the register r0 in the state list of
+ * insn 2 will be seen as !REG_LIVE_READ. Then the verifier pops the other_branch
+ * of insn 2 and goes exploring further. At the insn 4 it will walk the
+ * parentage chain from insn 4 into insn 2 and will mark r0 as REG_LIVE_READ.
+ *
+ * Since the verifier pushes the branch states as it sees them while exploring
+ * the program the condition of walking the branch instruction for the second
+ * time means that all states below this branch were already explored and
+ * their final liveness markes are already propagated.
+ * Hence when the verifier completes the search of state list in is_state_visited()
+ * we can call this clean_live_states() function to mark all liveness states
+ * as REG_LIVE_DONE to indicate that 'parent' pointers of 'struct bpf_reg_state'
+ * will not be used.
+ * This function also clears the registers and stack for states that !READ
+ * to simplify state merging.
+ *
+ * Important note here that walking the same branch instruction in the callee
+ * doesn't meant that the states are DONE. The verifier has to compare
+ * the callsites
+ */
+static void clean_live_states(struct bpf_verifier_env *env, int insn,
+                             struct bpf_verifier_state *cur)
+{
+       struct bpf_verifier_state_list *sl;
+       int i;
+
+       sl = env->explored_states[insn];
+       if (!sl)
+               return;
+
+       while (sl != STATE_LIST_MARK) {
+               if (sl->state.curframe != cur->curframe)
+                       goto next;
+               for (i = 0; i <= cur->curframe; i++)
+                       if (sl->state.frame[i]->callsite != cur->frame[i]->callsite)
+                               goto next;
+               clean_verifier_state(env, &sl->state);
+next:
+               sl = sl->next;
+       }
+}
+
 /* Returns true if (rold safe implies rcur safe) */
 static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur,
                    struct idpair *idmap)
@@ -4787,12 +5328,6 @@ static bool stacksafe(struct bpf_func_state *old,
 {
        int i, spi;
 
-       /* if explored stack has more populated slots than current stack
-        * such stacks are not equivalent
-        */
-       if (old->allocated_stack > cur->allocated_stack)
-               return false;
-
        /* walk slots of the explored stack and ignore any additional
         * slots in the current stack, since explored(safe) state
         * didn't use them
@@ -4800,12 +5335,21 @@ static bool stacksafe(struct bpf_func_state *old,
        for (i = 0; i < old->allocated_stack; i++) {
                spi = i / BPF_REG_SIZE;
 
-               if (!(old->stack[spi].spilled_ptr.live & REG_LIVE_READ))
+               if (!(old->stack[spi].spilled_ptr.live & REG_LIVE_READ)) {
+                       i += BPF_REG_SIZE - 1;
                        /* explored state didn't use this */
                        continue;
+               }
 
                if (old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_INVALID)
                        continue;
+
+               /* explored stack has more populated slots than current stack
+                * and these slots were used
+                */
+               if (i >= cur->allocated_stack)
+                       return false;
+
                /* if old state was safe with misc data in the stack
                 * it will be safe with zero-initialized stack.
                 * The opposite is not true
@@ -4980,7 +5524,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
        struct bpf_verifier_state_list *new_sl;
        struct bpf_verifier_state_list *sl;
        struct bpf_verifier_state *cur = env->cur_state, *new;
-       int i, j, err;
+       int i, j, err, states_cnt = 0;
 
        sl = env->explored_states[insn_idx];
        if (!sl)
@@ -4989,6 +5533,8 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
                 */
                return 0;
 
+       clean_live_states(env, insn_idx, cur);
+
        while (sl != STATE_LIST_MARK) {
                if (states_equal(env, &sl->state, cur)) {
                        /* reached equivalent register/stack state,
@@ -5007,8 +5553,12 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
                        return 1;
                }
                sl = sl->next;
+               states_cnt++;
        }
 
+       if (!env->allow_ptr_leaks && states_cnt > BPF_COMPLEXITY_LIMIT_STATES)
+               return 0;
+
        /* there were no equivalent states, remember current one.
         * technically the current state is not proven to be safe yet,
         * but it will either reach outer most bpf_exit (which means it's safe)
@@ -5030,9 +5580,16 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
        }
        new_sl->next = env->explored_states[insn_idx];
        env->explored_states[insn_idx] = new_sl;
-       /* connect new state to parentage chain */
-       for (i = 0; i < BPF_REG_FP; i++)
-               cur_regs(env)[i].parent = &new->frame[new->curframe]->regs[i];
+       /* connect new state to parentage chain. Current frame needs all
+        * registers connected. Only r6 - r9 of the callers are alive (pushed
+        * to the stack implicitly by JITs) so in callers' frames connect just
+        * r6 - r9 as an optimization. Callers will have r1 - r5 connected to
+        * the state of the call instruction (with WRITTEN set), and r0 comes
+        * from callee with its full parentage chain, anyway.
+        */
+       for (j = 0; j <= cur->curframe; j++)
+               for (i = j < cur->curframe ? BPF_REG_6 : 0; i < BPF_REG_FP; i++)
+                       cur->frame[j]->regs[i].parent = &new->frame[j]->regs[i];
        /* clear write marks in current state: the writes we did are not writes
         * our child did, so they don't screen off its reads from us.
         * (There are no read marks in current state, because reads always mark
@@ -5097,6 +5654,8 @@ static int do_check(struct bpf_verifier_env *env)
        int insn_processed = 0;
        bool do_print_state = false;
 
+       env->prev_linfo = NULL;
+
        state = kzalloc(sizeof(struct bpf_verifier_state), GFP_KERNEL);
        if (!state)
                return -ENOMEM;
@@ -5148,6 +5707,9 @@ static int do_check(struct bpf_verifier_env *env)
                        goto process_bpf_exit;
                }
 
+               if (signal_pending(current))
+                       return -EAGAIN;
+
                if (need_resched())
                        cond_resched();
 
@@ -5167,6 +5729,7 @@ static int do_check(struct bpf_verifier_env *env)
                                .private_data   = env,
                        };
 
+                       verbose_linfo(env, insn_idx, "; ");
                        verbose(env, "%d: ", insn_idx);
                        print_bpf_insn(&cbs, insn, env->allow_ptr_leaks);
                }
@@ -5650,7 +6213,7 @@ static void adjust_subprog_starts(struct bpf_verifier_env *env, u32 off, u32 len
                return;
        /* NOTE: fake 'exit' subprog should be updated as well. */
        for (i = 0; i <= env->subprog_cnt; i++) {
-               if (env->subprog_info[i].start < off)
+               if (env->subprog_info[i].start <= off)
                        continue;
                env->subprog_info[i].start += len - 1;
        }
@@ -5707,10 +6270,10 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
        int i, cnt, size, ctx_field_size, delta = 0;
        const int insn_cnt = env->prog->len;
        struct bpf_insn insn_buf[16], *insn;
+       u32 target_size, size_default, off;
        struct bpf_prog *new_prog;
        enum bpf_access_type type;
        bool is_narrower_load;
-       u32 target_size;
 
        if (ops->gen_prologue || env->seen_direct_write) {
                if (!ops->gen_prologue) {
@@ -5803,9 +6366,9 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
                 * we will apply proper mask to the result.
                 */
                is_narrower_load = size < ctx_field_size;
+               size_default = bpf_ctx_off_adjust_machine(ctx_field_size);
+               off = insn->off;
                if (is_narrower_load) {
-                       u32 size_default = bpf_ctx_off_adjust_machine(ctx_field_size);
-                       u32 off = insn->off;
                        u8 size_code;
 
                        if (type == BPF_WRITE) {
@@ -5833,12 +6396,23 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
                }
 
                if (is_narrower_load && size < target_size) {
-                       if (ctx_field_size <= 4)
+                       u8 shift = (off & (size_default - 1)) * 8;
+
+                       if (ctx_field_size <= 4) {
+                               if (shift)
+                                       insn_buf[cnt++] = BPF_ALU32_IMM(BPF_RSH,
+                                                                       insn->dst_reg,
+                                                                       shift);
                                insn_buf[cnt++] = BPF_ALU32_IMM(BPF_AND, insn->dst_reg,
                                                                (1 << size * 8) - 1);
-                       else
+                       } else {
+                               if (shift)
+                                       insn_buf[cnt++] = BPF_ALU64_IMM(BPF_RSH,
+                                                                       insn->dst_reg,
+                                                                       shift);
                                insn_buf[cnt++] = BPF_ALU64_IMM(BPF_AND, insn->dst_reg,
                                                                (1 << size * 8) - 1);
+                       }
                }
 
                new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
@@ -5861,7 +6435,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
        int i, j, subprog_start, subprog_end = 0, len, subprog;
        struct bpf_insn *insn;
        void *old_bpf_func;
-       int err = -ENOMEM;
+       int err;
 
        if (env->subprog_cnt <= 1)
                return 0;
@@ -5892,6 +6466,11 @@ static int jit_subprogs(struct bpf_verifier_env *env)
                insn->imm = 1;
        }
 
+       err = bpf_prog_alloc_jited_linfo(prog);
+       if (err)
+               goto out_undo_insn;
+
+       err = -ENOMEM;
        func = kcalloc(env->subprog_cnt, sizeof(prog), GFP_KERNEL);
        if (!func)
                goto out_undo_insn;
@@ -5911,12 +6490,21 @@ static int jit_subprogs(struct bpf_verifier_env *env)
                if (bpf_prog_calc_tag(func[i]))
                        goto out_free;
                func[i]->is_func = 1;
+               func[i]->aux->func_idx = i;
+               /* the btf and func_info will be freed only at prog->aux */
+               func[i]->aux->btf = prog->aux->btf;
+               func[i]->aux->func_info = prog->aux->func_info;
+
                /* Use bpf_prog_F_tag to indicate functions in stack traces.
                 * Long term would need debug info to populate names
                 */
                func[i]->aux->name[0] = 'F';
                func[i]->aux->stack_depth = env->subprog_info[i].stack_depth;
                func[i]->jit_requested = 1;
+               func[i]->aux->linfo = prog->aux->linfo;
+               func[i]->aux->nr_linfo = prog->aux->nr_linfo;
+               func[i]->aux->jited_linfo = prog->aux->jited_linfo;
+               func[i]->aux->linfo_idx = env->subprog_info[i].linfo_idx;
                func[i] = bpf_int_jit_compile(func[i]);
                if (!func[i]->jited) {
                        err = -ENOTSUPP;
@@ -5990,6 +6578,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
        prog->bpf_func = func[0]->bpf_func;
        prog->aux->func = func;
        prog->aux->func_cnt = env->subprog_cnt;
+       bpf_prog_free_unused_jited_linfo(prog);
        return 0;
 out_free:
        for (i = 0; i < env->subprog_cnt; i++)
@@ -6006,6 +6595,7 @@ out_undo_insn:
                insn->off = 0;
                insn->imm = env->insn_aux_data[i].call_imm;
        }
+       bpf_prog_free_jited_linfo(prog);
        return err;
 }
 
@@ -6138,6 +6728,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
                         */
                        prog->cb_access = 1;
                        env->prog->aux->stack_depth = MAX_BPF_STACK;
+                       env->prog->aux->max_pkt_offset = MAX_PACKET_OFF;
 
                        /* mark bpf_tail_call as different opcode to avoid
                         * conditional branch in the interpeter for every normal
@@ -6302,7 +6893,8 @@ static void free_states(struct bpf_verifier_env *env)
        kfree(env->explored_states);
 }
 
-int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
+int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
+             union bpf_attr __user *uattr)
 {
        struct bpf_verifier_env *env;
        struct bpf_verifier_log *log;
@@ -6350,13 +6942,15 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
        env->strict_alignment = !!(attr->prog_flags & BPF_F_STRICT_ALIGNMENT);
        if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
                env->strict_alignment = true;
+       if (attr->prog_flags & BPF_F_ANY_ALIGNMENT)
+               env->strict_alignment = false;
 
        ret = replace_map_fd_with_map_ptr(env);
        if (ret < 0)
                goto skip_full_check;
 
        if (bpf_prog_is_dev_bound(env->prog->aux)) {
-               ret = bpf_prog_offload_verifier_prep(env);
+               ret = bpf_prog_offload_verifier_prep(env->prog);
                if (ret)
                        goto skip_full_check;
        }
@@ -6370,6 +6964,14 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
 
        env->allow_ptr_leaks = capable(CAP_SYS_ADMIN);
 
+       ret = check_subprogs(env);
+       if (ret < 0)
+               goto skip_full_check;
+
+       ret = check_btf_info(env, attr, uattr);
+       if (ret < 0)
+               goto skip_full_check;
+
        ret = check_cfg(env);
        if (ret < 0)
                goto skip_full_check;
@@ -6388,10 +6990,11 @@ skip_full_check:
        free_states(env);
 
        if (ret == 0)
-               sanitize_dead_code(env);
+               ret = check_max_stack_depth(env);
 
+       /* instruction rewrites happen after this point */
        if (ret == 0)
-               ret = check_max_stack_depth(env);
+               sanitize_dead_code(env);
 
        if (ret == 0)
                /* program is valid, convert *(u32*)(ctx + off) accesses */
@@ -6431,6 +7034,9 @@ skip_full_check:
                convert_pseudo_ld_imm64(env);
        }
 
+       if (ret == 0)
+               adjust_btf_func(env);
+
 err_release_maps:
        if (!env->prog->aux->used_maps)
                /* if we didn't copy map pointers into bpf_prog_info, release
index 3c7f3b4..91d5c38 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/sched/signal.h>
 #include <linux/sched/hotplug.h>
 #include <linux/sched/task.h>
+#include <linux/sched/smt.h>
 #include <linux/unistd.h>
 #include <linux/cpu.h>
 #include <linux/oom.h>
@@ -367,6 +368,12 @@ static void lockdep_release_cpus_lock(void)
 
 #endif /* CONFIG_HOTPLUG_CPU */
 
+/*
+ * Architectures that need SMT-specific errata handling during SMT hotplug
+ * should override this.
+ */
+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);
@@ -1011,6 +1018,7 @@ out:
         * concurrent CPU hotplug via cpu_add_remove_lock.
         */
        lockup_detector_cleanup();
+       arch_smt_update();
        return ret;
 }
 
@@ -1139,6 +1147,7 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen, enum cpuhp_state target)
        ret = cpuhp_up_callbacks(cpu, st, target);
 out:
        cpus_write_unlock();
+       arch_smt_update();
        return ret;
 }
 
@@ -2055,12 +2064,6 @@ static void cpuhp_online_cpu_device(unsigned int cpu)
        kobject_uevent(&dev->kobj, KOBJ_ONLINE);
 }
 
-/*
- * Architectures that need SMT-specific errata handling during SMT hotplug
- * should override this.
- */
-void __weak arch_smt_update(void) { };
-
 static int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval)
 {
        int cpu, ret = 0;
index 6ad4a9f..7921ae4 100644 (file)
@@ -179,14 +179,14 @@ kdb_bt(int argc, const char **argv)
                                kdb_printf("no process for cpu %ld\n", cpu);
                                return 0;
                        }
-                       sprintf(buf, "btt 0x%p\n", KDB_TSK(cpu));
+                       sprintf(buf, "btt 0x%px\n", KDB_TSK(cpu));
                        kdb_parse(buf);
                        return 0;
                }
                kdb_printf("btc: cpu status: ");
                kdb_parse("cpu\n");
                for_each_online_cpu(cpu) {
-                       sprintf(buf, "btt 0x%p\n", KDB_TSK(cpu));
+                       sprintf(buf, "btt 0x%px\n", KDB_TSK(cpu));
                        kdb_parse(buf);
                        touch_nmi_watchdog();
                }
index ed5d349..6a4b414 100644 (file)
@@ -216,7 +216,7 @@ static char *kdb_read(char *buffer, size_t bufsize)
        int count;
        int i;
        int diag, dtab_count;
-       int key;
+       int key, buf_size, ret;
 
 
        diag = kdbgetintenv("DTABCOUNT", &dtab_count);
@@ -336,9 +336,8 @@ poll_again:
                else
                        p_tmp = tmpbuffer;
                len = strlen(p_tmp);
-               count = kallsyms_symbol_complete(p_tmp,
-                                                sizeof(tmpbuffer) -
-                                                (p_tmp - tmpbuffer));
+               buf_size = sizeof(tmpbuffer) - (p_tmp - tmpbuffer);
+               count = kallsyms_symbol_complete(p_tmp, buf_size);
                if (tab == 2 && count > 0) {
                        kdb_printf("\n%d symbols are found.", count);
                        if (count > dtab_count) {
@@ -350,9 +349,13 @@ poll_again:
                        }
                        kdb_printf("\n");
                        for (i = 0; i < count; i++) {
-                               if (WARN_ON(!kallsyms_symbol_next(p_tmp, i)))
+                               ret = kallsyms_symbol_next(p_tmp, i, buf_size);
+                               if (WARN_ON(!ret))
                                        break;
-                               kdb_printf("%s ", p_tmp);
+                               if (ret != -E2BIG)
+                                       kdb_printf("%s ", p_tmp);
+                               else
+                                       kdb_printf("%s... ", p_tmp);
                                *(p_tmp + len) = '\0';
                        }
                        if (i >= dtab_count)
index 118527a..750497b 100644 (file)
@@ -173,11 +173,11 @@ int kdb_get_kbd_char(void)
        case KT_LATIN:
                if (isprint(keychar))
                        break;          /* printable characters */
-               /* drop through */
+               /* fall through */
        case KT_SPEC:
                if (keychar == K_ENTER)
                        break;
-               /* drop through */
+               /* fall through */
        default:
                return -1;      /* ignore unprintables */
        }
index bb4fe4e..d72b32c 100644 (file)
@@ -1192,7 +1192,7 @@ static int kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs,
        if (reason == KDB_REASON_DEBUG) {
                /* special case below */
        } else {
-               kdb_printf("\nEntering kdb (current=0x%p, pid %d) ",
+               kdb_printf("\nEntering kdb (current=0x%px, pid %d) ",
                           kdb_current, kdb_current ? kdb_current->pid : 0);
 #if defined(CONFIG_SMP)
                kdb_printf("on processor %d ", raw_smp_processor_id());
@@ -1208,7 +1208,7 @@ static int kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs,
                 */
                switch (db_result) {
                case KDB_DB_BPT:
-                       kdb_printf("\nEntering kdb (0x%p, pid %d) ",
+                       kdb_printf("\nEntering kdb (0x%px, pid %d) ",
                                   kdb_current, kdb_current->pid);
 #if defined(CONFIG_SMP)
                        kdb_printf("on processor %d ", raw_smp_processor_id());
@@ -1493,6 +1493,7 @@ static void kdb_md_line(const char *fmtstr, unsigned long addr,
        char cbuf[32];
        char *c = cbuf;
        int i;
+       int j;
        unsigned long word;
 
        memset(cbuf, '\0', sizeof(cbuf));
@@ -1538,25 +1539,9 @@ static void kdb_md_line(const char *fmtstr, unsigned long addr,
                        wc.word = word;
 #define printable_char(c) \
        ({unsigned char __c = c; isascii(__c) && isprint(__c) ? __c : '.'; })
-                       switch (bytesperword) {
-                       case 8:
+                       for (j = 0; j < bytesperword; j++)
                                *c++ = printable_char(*cp++);
-                               *c++ = printable_char(*cp++);
-                               *c++ = printable_char(*cp++);
-                               *c++ = printable_char(*cp++);
-                               addr += 4;
-                       case 4:
-                               *c++ = printable_char(*cp++);
-                               *c++ = printable_char(*cp++);
-                               addr += 2;
-                       case 2:
-                               *c++ = printable_char(*cp++);
-                               addr++;
-                       case 1:
-                               *c++ = printable_char(*cp++);
-                               addr++;
-                               break;
-                       }
+                       addr += bytesperword;
 #undef printable_char
                }
        }
@@ -2048,7 +2033,7 @@ static int kdb_lsmod(int argc, const char **argv)
                if (mod->state == MODULE_STATE_UNFORMED)
                        continue;
 
-               kdb_printf("%-20s%8u  0x%p ", mod->name,
+               kdb_printf("%-20s%8u  0x%px ", mod->name,
                           mod->core_layout.size, (void *)mod);
 #ifdef CONFIG_MODULE_UNLOAD
                kdb_printf("%4d ", module_refcount(mod));
@@ -2059,7 +2044,7 @@ static int kdb_lsmod(int argc, const char **argv)
                        kdb_printf(" (Loading)");
                else
                        kdb_printf(" (Live)");
-               kdb_printf(" 0x%p", mod->core_layout.base);
+               kdb_printf(" 0x%px", mod->core_layout.base);
 
 #ifdef CONFIG_MODULE_UNLOAD
                {
@@ -2341,7 +2326,7 @@ void kdb_ps1(const struct task_struct *p)
                return;
 
        cpu = kdb_process_cpu(p);
-       kdb_printf("0x%p %8d %8d  %d %4d   %c  0x%p %c%s\n",
+       kdb_printf("0x%px %8d %8d  %d %4d   %c  0x%px %c%s\n",
                   (void *)p, p->pid, p->parent->pid,
                   kdb_task_has_cpu(p), kdb_process_cpu(p),
                   kdb_task_state_char(p),
@@ -2354,7 +2339,7 @@ void kdb_ps1(const struct task_struct *p)
                } else {
                        if (KDB_TSK(cpu) != p)
                                kdb_printf("  Error: does not match running "
-                                  "process table (0x%p)\n", KDB_TSK(cpu));
+                                  "process table (0x%px)\n", KDB_TSK(cpu));
                }
        }
 }
@@ -2687,7 +2672,7 @@ int kdb_register_flags(char *cmd,
        for_each_kdbcmd(kp, i) {
                if (kp->cmd_name && (strcmp(kp->cmd_name, cmd) == 0)) {
                        kdb_printf("Duplicate kdb command registered: "
-                               "%s, func %p help %s\n", cmd, func, help);
+                               "%s, func %px help %s\n", cmd, func, help);
                        return 1;
                }
        }
index 1e5a502..2118d82 100644 (file)
@@ -83,7 +83,7 @@ typedef struct __ksymtab {
                unsigned long sym_start;
                unsigned long sym_end;
                } kdb_symtab_t;
-extern int kallsyms_symbol_next(char *prefix_name, int flag);
+extern int kallsyms_symbol_next(char *prefix_name, int flag, int buf_size);
 extern int kallsyms_symbol_complete(char *prefix_name, int max_len);
 
 /* Exported Symbols for kernel loadable modules to use. */
index 990b3cc..50bf9b1 100644 (file)
@@ -40,7 +40,7 @@
 int kdbgetsymval(const char *symname, kdb_symtab_t *symtab)
 {
        if (KDB_DEBUG(AR))
-               kdb_printf("kdbgetsymval: symname=%s, symtab=%p\n", symname,
+               kdb_printf("kdbgetsymval: symname=%s, symtab=%px\n", symname,
                           symtab);
        memset(symtab, 0, sizeof(*symtab));
        symtab->sym_start = kallsyms_lookup_name(symname);
@@ -88,7 +88,7 @@ int kdbnearsym(unsigned long addr, kdb_symtab_t *symtab)
        char *knt1 = NULL;
 
        if (KDB_DEBUG(AR))
-               kdb_printf("kdbnearsym: addr=0x%lx, symtab=%p\n", addr, symtab);
+               kdb_printf("kdbnearsym: addr=0x%lx, symtab=%px\n", addr, symtab);
        memset(symtab, 0, sizeof(*symtab));
 
        if (addr < 4096)
@@ -149,7 +149,7 @@ int kdbnearsym(unsigned long addr, kdb_symtab_t *symtab)
                symtab->mod_name = "kernel";
        if (KDB_DEBUG(AR))
                kdb_printf("kdbnearsym: returns %d symtab->sym_start=0x%lx, "
-                  "symtab->mod_name=%p, symtab->sym_name=%p (%s)\n", ret,
+                  "symtab->mod_name=%px, symtab->sym_name=%px (%s)\n", ret,
                   symtab->sym_start, symtab->mod_name, symtab->sym_name,
                   symtab->sym_name);
 
@@ -221,11 +221,13 @@ int kallsyms_symbol_complete(char *prefix_name, int max_len)
  * Parameters:
  *     prefix_name     prefix of a symbol name to lookup
  *     flag    0 means search from the head, 1 means continue search.
+ *     buf_size        maximum length that can be written to prefix_name
+ *                     buffer
  * Returns:
  *     1 if a symbol matches the given prefix.
  *     0 if no string found
  */
-int kallsyms_symbol_next(char *prefix_name, int flag)
+int kallsyms_symbol_next(char *prefix_name, int flag, int buf_size)
 {
        int prefix_len = strlen(prefix_name);
        static loff_t pos;
@@ -235,10 +237,8 @@ int kallsyms_symbol_next(char *prefix_name, int flag)
                pos = 0;
 
        while ((name = kdb_walk_kallsyms(&pos))) {
-               if (strncmp(name, prefix_name, prefix_len) == 0) {
-                       strncpy(prefix_name, name, strlen(name)+1);
-                       return 1;
-               }
+               if (!strncmp(name, prefix_name, prefix_len))
+                       return strscpy(prefix_name, name, buf_size);
        }
        return 0;
 }
@@ -432,7 +432,7 @@ int kdb_getphysword(unsigned long *word, unsigned long addr, size_t size)
                                *word = w8;
                        break;
                }
-               /* drop through */
+               /* fall through */
        default:
                diag = KDB_BADWIDTH;
                kdb_printf("kdb_getphysword: bad width %ld\n", (long) size);
@@ -481,7 +481,7 @@ int kdb_getword(unsigned long *word, unsigned long addr, size_t size)
                                *word = w8;
                        break;
                }
-               /* drop through */
+               /* fall through */
        default:
                diag = KDB_BADWIDTH;
                kdb_printf("kdb_getword: bad width %ld\n", (long) size);
@@ -525,7 +525,7 @@ int kdb_putword(unsigned long addr, unsigned long word, size_t size)
                        diag = kdb_putarea(addr, w8);
                        break;
                }
-               /* drop through */
+               /* fall through */
        default:
                diag = KDB_BADWIDTH;
                kdb_printf("kdb_putword: bad width %ld\n", (long) size);
@@ -887,13 +887,13 @@ void debug_kusage(void)
                   __func__, dah_first);
        if (dah_first) {
                h_used = (struct debug_alloc_header *)debug_alloc_pool;
-               kdb_printf("%s: h_used %p size %d\n", __func__, h_used,
+               kdb_printf("%s: h_used %px size %d\n", __func__, h_used,
                           h_used->size);
        }
        do {
                h_used = (struct debug_alloc_header *)
                          ((char *)h_free + dah_overhead + h_free->size);
-               kdb_printf("%s: h_used %p size %d caller %p\n",
+               kdb_printf("%s: h_used %px size %d caller %px\n",
                           __func__, h_used, h_used->size, h_used->caller);
                h_free = (struct debug_alloc_header *)
                          (debug_alloc_pool + h_free->next);
@@ -902,7 +902,7 @@ void debug_kusage(void)
                  ((char *)h_free + dah_overhead + h_free->size);
        if ((char *)h_used - debug_alloc_pool !=
            sizeof(debug_alloc_pool_aligned))
-               kdb_printf("%s: h_used %p size %d caller %p\n",
+               kdb_printf("%s: h_used %px size %d caller %px\n",
                           __func__, h_used, h_used->size, h_used->caller);
 out:
        spin_unlock(&dap_lock);
index 22a12ab..375c77e 100644 (file)
@@ -309,7 +309,12 @@ int dma_direct_supported(struct device *dev, u64 mask)
 
        min_mask = min_t(u64, min_mask, (max_pfn - 1) << PAGE_SHIFT);
 
-       return mask >= phys_to_dma(dev, min_mask);
+       /*
+        * This check needs to be against the actual bit mask value, so
+        * use __phys_to_dma() here so that the SME encryption mask isn't
+        * part of the check.
+        */
+       return mask >= __phys_to_dma(dev, min_mask);
 }
 
 int dma_direct_mapping_error(struct device *dev, dma_addr_t dma_addr)
index 5731daa..045930e 100644 (file)
@@ -679,7 +679,8 @@ dma_addr_t swiotlb_map_page(struct device *dev, struct page *page,
        }
 
        if (!dev_is_dma_coherent(dev) &&
-           (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
+           (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0 &&
+           dev_addr != DIRECT_MAPPING_ERROR)
                arch_sync_dma_for_device(dev, phys, size, dir);
 
        return dev_addr;
index 96d4bee..abbd8da 100644 (file)
@@ -572,7 +572,9 @@ static void put_uprobe(struct uprobe *uprobe)
                 * gets called, we don't get a chance to remove uprobe from
                 * delayed_uprobe_list from remove_breakpoint(). Do it here.
                 */
+               mutex_lock(&delayed_uprobe_lock);
                delayed_uprobe_remove(uprobe, NULL);
+               mutex_unlock(&delayed_uprobe_lock);
                kfree(uprobe);
        }
 }
@@ -829,7 +831,7 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file,
        BUG_ON((uprobe->offset & ~PAGE_MASK) +
                        UPROBE_SWBP_INSN_SIZE > PAGE_SIZE);
 
-       smp_wmb(); /* pairs with rmb() in find_active_uprobe() */
+       smp_wmb(); /* pairs with the smp_rmb() in handle_swbp() */
        set_bit(UPROBE_COPY_INSN, &uprobe->flags);
 
  out:
@@ -2178,10 +2180,18 @@ static void handle_swbp(struct pt_regs *regs)
         * After we hit the bp, _unregister + _register can install the
         * new and not-yet-analyzed uprobe at the same address, restart.
         */
-       smp_rmb(); /* pairs with wmb() in install_breakpoint() */
        if (unlikely(!test_bit(UPROBE_COPY_INSN, &uprobe->flags)))
                goto out;
 
+       /*
+        * Pairs with the smp_wmb() in prepare_uprobe().
+        *
+        * Guarantees that if we see the UPROBE_COPY_INSN bit set, then
+        * we must also see the stores to &uprobe->arch performed by the
+        * prepare_uprobe() call.
+        */
+       smp_rmb();
+
        /* Tracing handlers use ->utask to communicate with fetch methods */
        if (!get_utask())
                goto out;
index 3ebd09e..97959d7 100644 (file)
@@ -56,7 +56,7 @@ struct kcov {
        struct task_struct      *t;
 };
 
-static bool check_kcov_mode(enum kcov_mode needed_mode, struct task_struct *t)
+static notrace bool check_kcov_mode(enum kcov_mode needed_mode, struct task_struct *t)
 {
        unsigned int mode;
 
@@ -78,7 +78,7 @@ static bool check_kcov_mode(enum kcov_mode needed_mode, struct task_struct *t)
        return mode == needed_mode;
 }
 
-static unsigned long canonicalize_ip(unsigned long ip)
+static notrace unsigned long canonicalize_ip(unsigned long ip)
 {
 #ifdef CONFIG_RANDOMIZE_BASE
        ip -= kaslr_offset();
index 49a4058..06ec68f 100644 (file)
@@ -3093,6 +3093,11 @@ static int find_module_sections(struct module *mod, struct load_info *info)
                                             sizeof(*mod->tracepoints_ptrs),
                                             &mod->num_tracepoints);
 #endif
+#ifdef CONFIG_BPF_EVENTS
+       mod->bpf_raw_events = section_objs(info, "__bpf_raw_tp_map",
+                                          sizeof(*mod->bpf_raw_events),
+                                          &mod->num_bpf_raw_events);
+#endif
 #ifdef HAVE_JUMP_LABEL
        mod->jump_entries = section_objs(info, "__jump_table",
                                        sizeof(*mod->jump_entries),
index 80b34df..c2cee9d 100644 (file)
@@ -261,9 +261,6 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state)
 
 static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode)
 {
-       if (mode & PTRACE_MODE_SCHED)
-               return false;
-
        if (mode & PTRACE_MODE_NOAUDIT)
                return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE);
        else
@@ -331,16 +328,9 @@ ok:
             !ptrace_has_cap(mm->user_ns, mode)))
            return -EPERM;
 
-       if (mode & PTRACE_MODE_SCHED)
-               return 0;
        return security_ptrace_access_check(task, mode);
 }
 
-bool ptrace_may_access_sched(struct task_struct *task, unsigned int mode)
-{
-       return __ptrace_may_access(task, mode | PTRACE_MODE_SCHED);
-}
-
 bool ptrace_may_access(struct task_struct *task, unsigned int mode)
 {
        int err;
index 091e089..6fedf3a 100644 (file)
@@ -5738,15 +5738,10 @@ int sched_cpu_activate(unsigned int cpu)
 
 #ifdef CONFIG_SCHED_SMT
        /*
-        * The sched_smt_present static key needs to be evaluated on every
-        * hotplug event because at boot time SMT might be disabled when
-        * the number of booted CPUs is limited.
-        *
-        * If then later a sibling gets hotplugged, then the key would stay
-        * off and SMT scheduling would never be functional.
+        * When going up, increment the number of cores with SMT present.
         */
-       if (cpumask_weight(cpu_smt_mask(cpu)) > 1)
-               static_branch_enable_cpuslocked(&sched_smt_present);
+       if (cpumask_weight(cpu_smt_mask(cpu)) == 2)
+               static_branch_inc_cpuslocked(&sched_smt_present);
 #endif
        set_cpu_active(cpu, true);
 
@@ -5790,6 +5785,14 @@ int sched_cpu_deactivate(unsigned int cpu)
         */
        synchronize_rcu_mult(call_rcu, call_rcu_sched);
 
+#ifdef CONFIG_SCHED_SMT
+       /*
+        * When going down, decrement the number of cores with SMT present.
+        */
+       if (cpumask_weight(cpu_smt_mask(cpu)) == 2)
+               static_branch_dec_cpuslocked(&sched_smt_present);
+#endif
+
        if (!sched_smp_initialized)
                return 0;
 
index 3648d03..ac855b2 100644 (file)
@@ -5674,11 +5674,11 @@ static int wake_affine(struct sched_domain *sd, struct task_struct *p,
        return target;
 }
 
-static unsigned long cpu_util_wake(int cpu, struct task_struct *p);
+static unsigned long cpu_util_without(int cpu, struct task_struct *p);
 
-static unsigned long capacity_spare_wake(int cpu, struct task_struct *p)
+static unsigned long capacity_spare_without(int cpu, struct task_struct *p)
 {
-       return max_t(long, capacity_of(cpu) - cpu_util_wake(cpu, p), 0);
+       return max_t(long, capacity_of(cpu) - cpu_util_without(cpu, p), 0);
 }
 
 /*
@@ -5738,7 +5738,7 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p,
 
                        avg_load += cfs_rq_load_avg(&cpu_rq(i)->cfs);
 
-                       spare_cap = capacity_spare_wake(i, p);
+                       spare_cap = capacity_spare_without(i, p);
 
                        if (spare_cap > max_spare_cap)
                                max_spare_cap = spare_cap;
@@ -5889,8 +5889,8 @@ static inline int find_idlest_cpu(struct sched_domain *sd, struct task_struct *p
                return prev_cpu;
 
        /*
-        * We need task's util for capacity_spare_wake, sync it up to prev_cpu's
-        * last_update_time.
+        * We need task's util for capacity_spare_without, sync it up to
+        * prev_cpu's last_update_time.
         */
        if (!(sd_flag & SD_BALANCE_FORK))
                sync_entity_load_avg(&p->se);
@@ -6216,10 +6216,19 @@ static inline unsigned long cpu_util(int cpu)
 }
 
 /*
- * cpu_util_wake: Compute CPU utilization with any contributions from
- * the waking task p removed.
+ * cpu_util_without: compute cpu utilization without any contributions from *p
+ * @cpu: the CPU which utilization is requested
+ * @p: the task which utilization should be discounted
+ *
+ * The utilization of a CPU is defined by the utilization of tasks currently
+ * enqueued on that CPU as well as tasks which are currently sleeping after an
+ * execution on that CPU.
+ *
+ * This method returns the utilization of the specified CPU by discounting the
+ * utilization of the specified task, whenever the task is currently
+ * contributing to the CPU utilization.
  */
-static unsigned long cpu_util_wake(int cpu, struct task_struct *p)
+static unsigned long cpu_util_without(int cpu, struct task_struct *p)
 {
        struct cfs_rq *cfs_rq;
        unsigned int util;
@@ -6231,7 +6240,7 @@ static unsigned long cpu_util_wake(int cpu, struct task_struct *p)
        cfs_rq = &cpu_rq(cpu)->cfs;
        util = READ_ONCE(cfs_rq->avg.util_avg);
 
-       /* Discount task's blocked util from CPU's util */
+       /* Discount task's util from CPU's util */
        util -= min_t(unsigned int, util, task_util(p));
 
        /*
@@ -6240,14 +6249,14 @@ static unsigned long cpu_util_wake(int cpu, struct task_struct *p)
         * a) if *p is the only task sleeping on this CPU, then:
         *      cpu_util (== task_util) > util_est (== 0)
         *    and thus we return:
-        *      cpu_util_wake = (cpu_util - task_util) = 0
+        *      cpu_util_without = (cpu_util - task_util) = 0
         *
         * b) if other tasks are SLEEPING on this CPU, which is now exiting
         *    IDLE, then:
         *      cpu_util >= task_util
         *      cpu_util > util_est (== 0)
         *    and thus we discount *p's blocked utilization to return:
-        *      cpu_util_wake = (cpu_util - task_util) >= 0
+        *      cpu_util_without = (cpu_util - task_util) >= 0
         *
         * c) if other tasks are RUNNABLE on that CPU and
         *      util_est > cpu_util
@@ -6260,8 +6269,33 @@ static unsigned long cpu_util_wake(int cpu, struct task_struct *p)
         * covered by the following code when estimated utilization is
         * enabled.
         */
-       if (sched_feat(UTIL_EST))
-               util = max(util, READ_ONCE(cfs_rq->avg.util_est.enqueued));
+       if (sched_feat(UTIL_EST)) {
+               unsigned int estimated =
+                       READ_ONCE(cfs_rq->avg.util_est.enqueued);
+
+               /*
+                * Despite the following checks we still have a small window
+                * for a possible race, when an execl's select_task_rq_fair()
+                * races with LB's detach_task():
+                *
+                *   detach_task()
+                *     p->on_rq = TASK_ON_RQ_MIGRATING;
+                *     ---------------------------------- A
+                *     deactivate_task()                   \
+                *       dequeue_task()                     + RaceTime
+                *         util_est_dequeue()              /
+                *     ---------------------------------- B
+                *
+                * The additional check on "current == p" it's required to
+                * properly fix the execl regression and it helps in further
+                * reducing the chances for the above race.
+                */
+               if (unlikely(task_on_rq_queued(p) || current == p)) {
+                       estimated -= min_t(unsigned int, estimated,
+                                          (_task_util_est(p) | UTIL_AVG_UNCHANGED));
+               }
+               util = max(util, estimated);
+       }
 
        /*
         * Utilization (estimated) can exceed the CPU capacity, thus let's
index 7cdecfc..fe24de3 100644 (file)
 
 static int psi_bug __read_mostly;
 
-bool psi_disabled __read_mostly;
-core_param(psi_disabled, psi_disabled, bool, 0644);
+DEFINE_STATIC_KEY_FALSE(psi_disabled);
+
+#ifdef CONFIG_PSI_DEFAULT_DISABLED
+bool psi_enable;
+#else
+bool psi_enable = true;
+#endif
+static int __init setup_psi(char *str)
+{
+       return kstrtobool(str, &psi_enable) == 0;
+}
+__setup("psi=", setup_psi);
 
 /* Running averages - we need to be higher-res than loadavg */
 #define PSI_FREQ       (2*HZ+1)        /* 2 sec intervals */
@@ -169,8 +179,10 @@ static void group_init(struct psi_group *group)
 
 void __init psi_init(void)
 {
-       if (psi_disabled)
+       if (!psi_enable) {
+               static_branch_enable(&psi_disabled);
                return;
+       }
 
        psi_period = jiffies_to_nsecs(PSI_FREQ);
        group_init(&psi_system);
@@ -549,7 +561,7 @@ void psi_memstall_enter(unsigned long *flags)
        struct rq_flags rf;
        struct rq *rq;
 
-       if (psi_disabled)
+       if (static_branch_likely(&psi_disabled))
                return;
 
        *flags = current->flags & PF_MEMSTALL;
@@ -579,7 +591,7 @@ void psi_memstall_leave(unsigned long *flags)
        struct rq_flags rf;
        struct rq *rq;
 
-       if (psi_disabled)
+       if (static_branch_likely(&psi_disabled))
                return;
 
        if (*flags)
@@ -600,7 +612,7 @@ void psi_memstall_leave(unsigned long *flags)
 #ifdef CONFIG_CGROUPS
 int psi_cgroup_alloc(struct cgroup *cgroup)
 {
-       if (psi_disabled)
+       if (static_branch_likely(&psi_disabled))
                return 0;
 
        cgroup->psi.pcpu = alloc_percpu(struct psi_group_cpu);
@@ -612,7 +624,7 @@ int psi_cgroup_alloc(struct cgroup *cgroup)
 
 void psi_cgroup_free(struct cgroup *cgroup)
 {
-       if (psi_disabled)
+       if (static_branch_likely(&psi_disabled))
                return;
 
        cancel_delayed_work_sync(&cgroup->psi.clock_work);
@@ -633,38 +645,39 @@ void psi_cgroup_free(struct cgroup *cgroup)
  */
 void cgroup_move_task(struct task_struct *task, struct css_set *to)
 {
-       bool move_psi = !psi_disabled;
        unsigned int task_flags = 0;
        struct rq_flags rf;
        struct rq *rq;
 
-       if (move_psi) {
-               rq = task_rq_lock(task, &rf);
+       if (static_branch_likely(&psi_disabled)) {
+               /*
+                * Lame to do this here, but the scheduler cannot be locked
+                * from the outside, so we move cgroups from inside sched/.
+                */
+               rcu_assign_pointer(task->cgroups, to);
+               return;
+       }
 
-               if (task_on_rq_queued(task))
-                       task_flags = TSK_RUNNING;
-               else if (task->in_iowait)
-                       task_flags = TSK_IOWAIT;
+       rq = task_rq_lock(task, &rf);
 
-               if (task->flags & PF_MEMSTALL)
-                       task_flags |= TSK_MEMSTALL;
+       if (task_on_rq_queued(task))
+               task_flags = TSK_RUNNING;
+       else if (task->in_iowait)
+               task_flags = TSK_IOWAIT;
 
-               if (task_flags)
-                       psi_task_change(task, task_flags, 0);
-       }
+       if (task->flags & PF_MEMSTALL)
+               task_flags |= TSK_MEMSTALL;
 
-       /*
-        * Lame to do this here, but the scheduler cannot be locked
-        * from the outside, so we move cgroups from inside sched/.
-        */
+       if (task_flags)
+               psi_task_change(task, task_flags, 0);
+
+       /* See comment above */
        rcu_assign_pointer(task->cgroups, to);
 
-       if (move_psi) {
-               if (task_flags)
-                       psi_task_change(task, 0, task_flags);
+       if (task_flags)
+               psi_task_change(task, 0, task_flags);
 
-               task_rq_unlock(rq, task, &rf);
-       }
+       task_rq_unlock(rq, task, &rf);
 }
 #endif /* CONFIG_CGROUPS */
 
@@ -672,7 +685,7 @@ int psi_show(struct seq_file *m, struct psi_group *group, enum psi_res res)
 {
        int full;
 
-       if (psi_disabled)
+       if (static_branch_likely(&psi_disabled))
                return -EOPNOTSUPP;
 
        update_stats(group);
index 618577f..4e524ab 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/sched/prio.h>
 #include <linux/sched/rt.h>
 #include <linux/sched/signal.h>
+#include <linux/sched/smt.h>
 #include <linux/sched/stat.h>
 #include <linux/sched/sysctl.h>
 #include <linux/sched/task.h>
@@ -936,9 +937,6 @@ static inline int cpu_of(struct rq *rq)
 
 
 #ifdef CONFIG_SCHED_SMT
-
-extern struct static_key_false sched_smt_present;
-
 extern void __update_idle_core(struct rq *rq);
 
 static inline void update_idle_core(struct rq *rq)
index 4904c46..aa0de24 100644 (file)
@@ -66,7 +66,7 @@ static inline void psi_enqueue(struct task_struct *p, bool wakeup)
 {
        int clear = 0, set = TSK_RUNNING;
 
-       if (psi_disabled)
+       if (static_branch_likely(&psi_disabled))
                return;
 
        if (!wakeup || p->sched_psi_wake_requeue) {
@@ -86,7 +86,7 @@ static inline void psi_dequeue(struct task_struct *p, bool sleep)
 {
        int clear = TSK_RUNNING, set = 0;
 
-       if (psi_disabled)
+       if (static_branch_likely(&psi_disabled))
                return;
 
        if (!sleep) {
@@ -102,7 +102,7 @@ static inline void psi_dequeue(struct task_struct *p, bool sleep)
 
 static inline void psi_ttwu_dequeue(struct task_struct *p)
 {
-       if (psi_disabled)
+       if (static_branch_likely(&psi_disabled))
                return;
        /*
         * Is the task being migrated during a wakeup? Make sure to
@@ -128,7 +128,7 @@ static inline void psi_ttwu_dequeue(struct task_struct *p)
 
 static inline void psi_task_tick(struct rq *rq)
 {
-       if (psi_disabled)
+       if (static_branch_likely(&psi_disabled))
                return;
 
        if (unlikely(rq->curr->flags & PF_MEMSTALL))
index e428929..b193a59 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <linux/stackleak.h>
+#include <linux/kprobes.h>
 
 #ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
 #include <linux/jump_label.h>
@@ -47,7 +48,7 @@ int stack_erasing_sysctl(struct ctl_table *table, int write,
 #define skip_erasing() false
 #endif /* CONFIG_STACKLEAK_RUNTIME_DISABLE */
 
-asmlinkage void stackleak_erase(void)
+asmlinkage void notrace stackleak_erase(void)
 {
        /* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */
        unsigned long kstack_ptr = current->lowest_stack;
@@ -101,8 +102,9 @@ asmlinkage void stackleak_erase(void)
        /* Reset the 'lowest_stack' value for the next syscall */
        current->lowest_stack = current_top_of_stack() - THREAD_SIZE/64;
 }
+NOKPROBE_SYMBOL(stackleak_erase);
 
-void __used stackleak_track_stack(void)
+void __used notrace stackleak_track_stack(void)
 {
        /*
         * N.B. stackleak_erase() fills the kernel stack with the poison value,
index 08fcfe4..9ddb6fd 100644 (file)
 #include "trace_probe.h"
 #include "trace.h"
 
+#ifdef CONFIG_MODULES
+struct bpf_trace_module {
+       struct module *module;
+       struct list_head list;
+};
+
+static LIST_HEAD(bpf_trace_modules);
+static DEFINE_MUTEX(bpf_module_mutex);
+
+static struct bpf_raw_event_map *bpf_get_raw_tracepoint_module(const char *name)
+{
+       struct bpf_raw_event_map *btp, *ret = NULL;
+       struct bpf_trace_module *btm;
+       unsigned int i;
+
+       mutex_lock(&bpf_module_mutex);
+       list_for_each_entry(btm, &bpf_trace_modules, list) {
+               for (i = 0; i < btm->module->num_bpf_raw_events; ++i) {
+                       btp = &btm->module->bpf_raw_events[i];
+                       if (!strcmp(btp->tp->name, name)) {
+                               if (try_module_get(btm->module))
+                                       ret = btp;
+                               goto out;
+                       }
+               }
+       }
+out:
+       mutex_unlock(&bpf_module_mutex);
+       return ret;
+}
+#else
+static struct bpf_raw_event_map *bpf_get_raw_tracepoint_module(const char *name)
+{
+       return NULL;
+}
+#endif /* CONFIG_MODULES */
+
 u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
 u64 bpf_get_stack(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
 
@@ -196,11 +233,13 @@ BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
                        i++;
                } else if (fmt[i] == 'p' || fmt[i] == 's') {
                        mod[fmt_cnt]++;
-                       i++;
-                       if (!isspace(fmt[i]) && !ispunct(fmt[i]) && fmt[i] != 0)
+                       /* disallow any further format extensions */
+                       if (fmt[i + 1] != 0 &&
+                           !isspace(fmt[i + 1]) &&
+                           !ispunct(fmt[i + 1]))
                                return -EINVAL;
                        fmt_cnt++;
-                       if (fmt[i - 1] == 's') {
+                       if (fmt[i] == 's') {
                                if (str_seen)
                                        /* allow only one '%s' per fmt string */
                                        return -EINVAL;
@@ -1074,7 +1113,7 @@ int perf_event_query_prog_array(struct perf_event *event, void __user *info)
 extern struct bpf_raw_event_map __start__bpf_raw_tp[];
 extern struct bpf_raw_event_map __stop__bpf_raw_tp[];
 
-struct bpf_raw_event_map *bpf_find_raw_tracepoint(const char *name)
+struct bpf_raw_event_map *bpf_get_raw_tracepoint(const char *name)
 {
        struct bpf_raw_event_map *btp = __start__bpf_raw_tp;
 
@@ -1082,7 +1121,16 @@ struct bpf_raw_event_map *bpf_find_raw_tracepoint(const char *name)
                if (!strcmp(btp->tp->name, name))
                        return btp;
        }
-       return NULL;
+
+       return bpf_get_raw_tracepoint_module(name);
+}
+
+void bpf_put_raw_tracepoint(struct bpf_raw_event_map *btp)
+{
+       struct module *mod = __module_address((unsigned long)btp);
+
+       if (mod)
+               module_put(mod);
 }
 
 static __always_inline
@@ -1220,3 +1268,52 @@ int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
 
        return err;
 }
+
+#ifdef CONFIG_MODULES
+int bpf_event_notify(struct notifier_block *nb, unsigned long op, void *module)
+{
+       struct bpf_trace_module *btm, *tmp;
+       struct module *mod = module;
+
+       if (mod->num_bpf_raw_events == 0 ||
+           (op != MODULE_STATE_COMING && op != MODULE_STATE_GOING))
+               return 0;
+
+       mutex_lock(&bpf_module_mutex);
+
+       switch (op) {
+       case MODULE_STATE_COMING:
+               btm = kzalloc(sizeof(*btm), GFP_KERNEL);
+               if (btm) {
+                       btm->module = module;
+                       list_add(&btm->list, &bpf_trace_modules);
+               }
+               break;
+       case MODULE_STATE_GOING:
+               list_for_each_entry_safe(btm, tmp, &bpf_trace_modules, list) {
+                       if (btm->module == module) {
+                               list_del(&btm->list);
+                               kfree(btm);
+                               break;
+                       }
+               }
+               break;
+       }
+
+       mutex_unlock(&bpf_module_mutex);
+
+       return 0;
+}
+
+static struct notifier_block bpf_module_nb = {
+       .notifier_call = bpf_event_notify,
+};
+
+int __init bpf_event_init(void)
+{
+       register_module_notifier(&bpf_module_nb);
+       return 0;
+}
+
+fs_initcall(bpf_event_init);
+#endif /* CONFIG_MODULES */
index f536f60..e23eb9f 100644 (file)
@@ -817,7 +817,7 @@ function_profile_call(unsigned long ip, unsigned long parent_ip,
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 static int profile_graph_entry(struct ftrace_graph_ent *trace)
 {
-       int index = trace->depth;
+       int index = current->curr_ret_stack;
 
        function_profile_call(trace->func, 0, NULL, NULL);
 
@@ -852,7 +852,7 @@ static void profile_graph_return(struct ftrace_graph_ret *trace)
        if (!fgraph_graph_time) {
                int index;
 
-               index = trace->depth;
+               index = current->curr_ret_stack;
 
                /* Append this call time to the parent time to subtract */
                if (index)
@@ -5460,6 +5460,7 @@ void ftrace_destroy_filter_files(struct ftrace_ops *ops)
        if (ops->flags & FTRACE_OPS_FL_ENABLED)
                ftrace_shutdown(ops, 0);
        ops->flags |= FTRACE_OPS_FL_DELETED;
+       ftrace_free_filter(ops);
        mutex_unlock(&ftrace_lock);
 }
 
@@ -6814,6 +6815,7 @@ static int alloc_retstack_tasklist(struct ftrace_ret_stack **ret_stack_list)
                        atomic_set(&t->tracing_graph_pause, 0);
                        atomic_set(&t->trace_overrun, 0);
                        t->curr_ret_stack = -1;
+                       t->curr_ret_depth = -1;
                        /* Make sure the tasks see the -1 first: */
                        smp_wmb();
                        t->ret_stack = ret_stack_list[start++];
@@ -7038,6 +7040,7 @@ graph_init_task(struct task_struct *t, struct ftrace_ret_stack *ret_stack)
 void ftrace_graph_init_idle_task(struct task_struct *t, int cpu)
 {
        t->curr_ret_stack = -1;
+       t->curr_ret_depth = -1;
        /*
         * The idle task has no parent, it either has its own
         * stack or no stack at all.
@@ -7068,6 +7071,7 @@ void ftrace_graph_init_task(struct task_struct *t)
        /* Make sure we do not use the parent ret_stack */
        t->ret_stack = NULL;
        t->curr_ret_stack = -1;
+       t->curr_ret_depth = -1;
 
        if (ftrace_graph_active) {
                struct ftrace_ret_stack *ret_stack;
index 3b8c0e2..447bd96 100644 (file)
@@ -512,12 +512,44 @@ enum {
  * can only be modified by current, we can reuse trace_recursion.
  */
        TRACE_IRQ_BIT,
+
+       /* Set if the function is in the set_graph_function file */
+       TRACE_GRAPH_BIT,
+
+       /*
+        * In the very unlikely case that an interrupt came in
+        * at a start of graph tracing, and we want to trace
+        * the function in that interrupt, the depth can be greater
+        * than zero, because of the preempted start of a previous
+        * trace. In an even more unlikely case, depth could be 2
+        * if a softirq interrupted the start of graph tracing,
+        * followed by an interrupt preempting a start of graph
+        * tracing in the softirq, and depth can even be 3
+        * if an NMI came in at the start of an interrupt function
+        * that preempted a softirq start of a function that
+        * preempted normal context!!!! Luckily, it can't be
+        * greater than 3, so the next two bits are a mask
+        * of what the depth is when we set TRACE_GRAPH_BIT
+        */
+
+       TRACE_GRAPH_DEPTH_START_BIT,
+       TRACE_GRAPH_DEPTH_END_BIT,
 };
 
 #define trace_recursion_set(bit)       do { (current)->trace_recursion |= (1<<(bit)); } while (0)
 #define trace_recursion_clear(bit)     do { (current)->trace_recursion &= ~(1<<(bit)); } while (0)
 #define trace_recursion_test(bit)      ((current)->trace_recursion & (1<<(bit)))
 
+#define trace_recursion_depth() \
+       (((current)->trace_recursion >> TRACE_GRAPH_DEPTH_START_BIT) & 3)
+#define trace_recursion_set_depth(depth) \
+       do {                                                            \
+               current->trace_recursion &=                             \
+                       ~(3 << TRACE_GRAPH_DEPTH_START_BIT);            \
+               current->trace_recursion |=                             \
+                       ((depth) & 3) << TRACE_GRAPH_DEPTH_START_BIT;   \
+       } while (0)
+
 #define TRACE_CONTEXT_BITS     4
 
 #define TRACE_FTRACE_START     TRACE_FTRACE_BIT
@@ -843,8 +875,9 @@ extern void __trace_graph_return(struct trace_array *tr,
 extern struct ftrace_hash *ftrace_graph_hash;
 extern struct ftrace_hash *ftrace_graph_notrace_hash;
 
-static inline int ftrace_graph_addr(unsigned long addr)
+static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)
 {
+       unsigned long addr = trace->func;
        int ret = 0;
 
        preempt_disable_notrace();
@@ -855,6 +888,14 @@ static inline int ftrace_graph_addr(unsigned long addr)
        }
 
        if (ftrace_lookup_ip(ftrace_graph_hash, addr)) {
+
+               /*
+                * This needs to be cleared on the return functions
+                * when the depth is zero.
+                */
+               trace_recursion_set(TRACE_GRAPH_BIT);
+               trace_recursion_set_depth(trace->depth);
+
                /*
                 * If no irqs are to be traced, but a set_graph_function
                 * is set, and called by an interrupt handler, we still
@@ -872,6 +913,13 @@ out:
        return ret;
 }
 
+static inline void ftrace_graph_addr_finish(struct ftrace_graph_ret *trace)
+{
+       if (trace_recursion_test(TRACE_GRAPH_BIT) &&
+           trace->depth == trace_recursion_depth())
+               trace_recursion_clear(TRACE_GRAPH_BIT);
+}
+
 static inline int ftrace_graph_notrace_addr(unsigned long addr)
 {
        int ret = 0;
@@ -885,7 +933,7 @@ static inline int ftrace_graph_notrace_addr(unsigned long addr)
        return ret;
 }
 #else
-static inline int ftrace_graph_addr(unsigned long addr)
+static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)
 {
        return 1;
 }
@@ -894,6 +942,8 @@ static inline int ftrace_graph_notrace_addr(unsigned long addr)
 {
        return 0;
 }
+static inline void ftrace_graph_addr_finish(struct ftrace_graph_ret *trace)
+{ }
 #endif /* CONFIG_DYNAMIC_FTRACE */
 
 extern unsigned int fgraph_max_depth;
@@ -901,7 +951,8 @@ extern unsigned int fgraph_max_depth;
 static inline bool ftrace_graph_ignore_func(struct ftrace_graph_ent *trace)
 {
        /* trace it when it is-nested-in or is a function enabled. */
-       return !(trace->depth || ftrace_graph_addr(trace->func)) ||
+       return !(trace_recursion_test(TRACE_GRAPH_BIT) ||
+                ftrace_graph_addr(trace)) ||
                (trace->depth < 0) ||
                (fgraph_max_depth && trace->depth >= fgraph_max_depth);
 }
index 84a6517..5574e86 100644 (file)
@@ -570,11 +570,13 @@ predicate_parse(const char *str, int nr_parens, int nr_preds,
                }
        }
 
+       kfree(op_stack);
+       kfree(inverts);
        return prog;
 out_free:
        kfree(op_stack);
-       kfree(prog_stack);
        kfree(inverts);
+       kfree(prog_stack);
        return ERR_PTR(ret);
 }
 
@@ -1718,6 +1720,7 @@ static int create_filter(struct trace_event_call *call,
        err = process_preds(call, filter_string, *filterp, pe);
        if (err && set_str)
                append_filter_err(pe, *filterp);
+       create_filter_finish(pe);
 
        return err;
 }
index 2152d1e..cd12ecb 100644 (file)
@@ -732,8 +732,10 @@ int set_trigger_filter(char *filter_str,
 
        /* The filter is for the 'trigger' event, not the triggered event */
        ret = create_event_filter(file->event_call, filter_str, false, &filter);
-       if (ret)
-               goto out;
+       /*
+        * If create_event_filter() fails, filter still needs to be freed.
+        * Which the calling code will do with data->filter.
+        */
  assign:
        tmp = rcu_access_pointer(data->filter);
 
index 169b3c4..086af4f 100644 (file)
@@ -118,8 +118,8 @@ print_graph_duration(struct trace_array *tr, unsigned long long duration,
                     struct trace_seq *s, u32 flags);
 
 /* Add a function return address to the trace stack on thread info.*/
-int
-ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth,
+static int
+ftrace_push_return_trace(unsigned long ret, unsigned long func,
                         unsigned long frame_pointer, unsigned long *retp)
 {
        unsigned long long calltime;
@@ -177,9 +177,31 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth,
 #ifdef HAVE_FUNCTION_GRAPH_RET_ADDR_PTR
        current->ret_stack[index].retp = retp;
 #endif
-       *depth = current->curr_ret_stack;
+       return 0;
+}
+
+int function_graph_enter(unsigned long ret, unsigned long func,
+                        unsigned long frame_pointer, unsigned long *retp)
+{
+       struct ftrace_graph_ent trace;
+
+       trace.func = func;
+       trace.depth = ++current->curr_ret_depth;
+
+       if (ftrace_push_return_trace(ret, func,
+                                    frame_pointer, retp))
+               goto out;
+
+       /* Only trace if the calling function expects to */
+       if (!ftrace_graph_entry(&trace))
+               goto out_ret;
 
        return 0;
+ out_ret:
+       current->curr_ret_stack--;
+ out:
+       current->curr_ret_depth--;
+       return -EBUSY;
 }
 
 /* Retrieve a function return address to the trace stack on thread info.*/
@@ -241,7 +263,13 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret,
        trace->func = current->ret_stack[index].func;
        trace->calltime = current->ret_stack[index].calltime;
        trace->overrun = atomic_read(&current->trace_overrun);
-       trace->depth = index;
+       trace->depth = current->curr_ret_depth--;
+       /*
+        * We still want to trace interrupts coming in if
+        * max_depth is set to 1. Make sure the decrement is
+        * seen before ftrace_graph_return.
+        */
+       barrier();
 }
 
 /*
@@ -255,6 +283,12 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)
 
        ftrace_pop_return_trace(&trace, &ret, frame_pointer);
        trace.rettime = trace_clock_local();
+       ftrace_graph_return(&trace);
+       /*
+        * The ftrace_graph_return() may still access the current
+        * ret_stack structure, we need to make sure the update of
+        * curr_ret_stack is after that.
+        */
        barrier();
        current->curr_ret_stack--;
        /*
@@ -267,13 +301,6 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)
                return ret;
        }
 
-       /*
-        * The trace should run after decrementing the ret counter
-        * in case an interrupt were to come in. We don't want to
-        * lose the interrupt if max_depth is set.
-        */
-       ftrace_graph_return(&trace);
-
        if (unlikely(!ret)) {
                ftrace_graph_stop();
                WARN_ON(1);
@@ -482,6 +509,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace)
        int cpu;
        int pc;
 
+       ftrace_graph_addr_finish(trace);
+
        local_irq_save(flags);
        cpu = raw_smp_processor_id();
        data = per_cpu_ptr(tr->trace_buffer.data, cpu);
@@ -505,6 +534,8 @@ void set_graph_array(struct trace_array *tr)
 
 static void trace_graph_thresh_return(struct ftrace_graph_ret *trace)
 {
+       ftrace_graph_addr_finish(trace);
+
        if (tracing_thresh &&
            (trace->rettime - trace->calltime < tracing_thresh))
                return;
index b7357f9..98ea6d2 100644 (file)
@@ -208,6 +208,8 @@ static void irqsoff_graph_return(struct ftrace_graph_ret *trace)
        unsigned long flags;
        int pc;
 
+       ftrace_graph_addr_finish(trace);
+
        if (!func_prolog_dec(tr, &data, &flags))
                return;
 
index a86b303..7d04b98 100644 (file)
@@ -270,6 +270,8 @@ static void wakeup_graph_return(struct ftrace_graph_ret *trace)
        unsigned long flags;
        int pc;
 
+       ftrace_graph_addr_finish(trace);
+
        if (!func_prolog_preempt_disable(tr, &data, &pc))
                return;
 
index a9965f4..7dbbcfe 100644 (file)
@@ -624,3 +624,6 @@ config GENERIC_LIB_CMPDI2
 
 config GENERIC_LIB_UCMPDI2
        bool
+
+config OBJAGG
+       tristate "objagg" if COMPILE_TEST
index 1af29b8..b3c91b9 100644 (file)
@@ -1976,6 +1976,16 @@ config TEST_MEMCAT_P
 
          If unsure, say N.
 
+config TEST_OBJAGG
+       tristate "Perform selftest on object aggreration manager"
+       default n
+       depends on OBJAGG
+       help
+         Enable this option to test object aggregation manager on boot
+         (or module load).
+
+         If unsure, say N.
+
 endif # RUNTIME_TESTING_MENU
 
 config MEMTEST
index db06d12..f5262d3 100644 (file)
@@ -75,6 +75,7 @@ obj-$(CONFIG_TEST_PARMAN) += test_parman.o
 obj-$(CONFIG_TEST_KMOD) += test_kmod.o
 obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o
 obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o
+obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o
 
 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
 CFLAGS_kobject.o += -DDEBUG
@@ -274,3 +275,4 @@ obj-$(CONFIG_GENERIC_LIB_LSHRDI3) += lshrdi3.o
 obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
 obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o
 obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o
+obj-$(CONFIG_OBJAGG) += objagg.o
index 6cf4778..8ef27c1 100644 (file)
 #include <linux/module.h>
 #include <linux/cordic.h>
 
-#define CORDIC_ANGLE_GEN       39797
-#define CORDIC_PRECISION_SHIFT 16
-#define        CORDIC_NUM_ITER         (CORDIC_PRECISION_SHIFT + 2)
-
-#define        FIXED(X)        ((s32)((X) << CORDIC_PRECISION_SHIFT))
-#define        FLOAT(X)        (((X) >= 0) \
-               ? ((((X) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1) \
-               : -((((-(X)) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1))
-
 static const s32 arctan_table[] = {
        2949120,
        1740967,
@@ -64,16 +55,16 @@ struct cordic_iq cordic_calc_iq(s32 theta)
        coord.q = 0;
        angle = 0;
 
-       theta = FIXED(theta);
+       theta = CORDIC_FIXED(theta);
        signtheta = (theta < 0) ? -1 : 1;
-       theta = ((theta + FIXED(180) * signtheta) % FIXED(360)) -
-               FIXED(180) * signtheta;
+       theta = ((theta + CORDIC_FIXED(180) * signtheta) % CORDIC_FIXED(360)) -
+               CORDIC_FIXED(180) * signtheta;
 
-       if (FLOAT(theta) > 90) {
-               theta -= FIXED(180);
+       if (CORDIC_FLOAT(theta) > 90) {
+               theta -= CORDIC_FIXED(180);
                signx = -1;
-       } else if (FLOAT(theta) < -90) {
-               theta += FIXED(180);
+       } else if (CORDIC_FLOAT(theta) < -90) {
+               theta += CORDIC_FIXED(180);
                signx = -1;
        }
 
index 70935ed..14afeeb 100644 (file)
@@ -135,7 +135,6 @@ static void fill_pool(void)
                if (!new)
                        return;
 
-               kmemleak_ignore(new);
                raw_spin_lock_irqsave(&pool_lock, flags);
                hlist_add_head(&new->node, &obj_pool);
                debug_objects_allocated++;
@@ -1128,7 +1127,6 @@ static int __init debug_objects_replace_static_objects(void)
                obj = kmem_cache_zalloc(obj_cache, GFP_KERNEL);
                if (!obj)
                        goto free;
-               kmemleak_ignore(obj);
                hlist_add_head(&obj->node, &objects);
        }
 
@@ -1184,7 +1182,8 @@ void __init debug_objects_mem_init(void)
 
        obj_cache = kmem_cache_create("debug_objects_cache",
                                      sizeof (struct debug_obj), 0,
-                                     SLAB_DEBUG_OBJECTS, NULL);
+                                     SLAB_DEBUG_OBJECTS | SLAB_NOLEAKTRACE,
+                                     NULL);
 
        if (!obj_cache || debug_objects_replace_static_objects()) {
                debug_objects_enabled = 0;
index 7ebccb5..54c2485 100644 (file)
@@ -560,6 +560,38 @@ static size_t copy_pipe_to_iter(const void *addr, size_t bytes,
        return bytes;
 }
 
+static size_t csum_and_copy_to_pipe_iter(const void *addr, size_t bytes,
+                               __wsum *csum, struct iov_iter *i)
+{
+       struct pipe_inode_info *pipe = i->pipe;
+       size_t n, r;
+       size_t off = 0;
+       __wsum sum = *csum, next;
+       int idx;
+
+       if (!sanity(i))
+               return 0;
+
+       bytes = n = push_pipe(i, bytes, &idx, &r);
+       if (unlikely(!n))
+               return 0;
+       for ( ; n; idx = next_idx(idx, pipe), r = 0) {
+               size_t chunk = min_t(size_t, n, PAGE_SIZE - r);
+               char *p = kmap_atomic(pipe->bufs[idx].page);
+               next = csum_partial_copy_nocheck(addr, p + r, chunk, 0);
+               sum = csum_block_add(sum, next, off);
+               kunmap_atomic(p);
+               i->idx = idx;
+               i->iov_offset = r + chunk;
+               n -= chunk;
+               off += chunk;
+               addr += chunk;
+       }
+       i->count -= bytes;
+       *csum = sum;
+       return bytes;
+}
+
 size_t _copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
 {
        const char *from = addr;
@@ -1438,8 +1470,12 @@ size_t csum_and_copy_to_iter(const void *addr, size_t bytes, __wsum *csum,
        const char *from = addr;
        __wsum sum, next;
        size_t off = 0;
+
+       if (unlikely(iov_iter_is_pipe(i)))
+               return csum_and_copy_to_pipe_iter(addr, bytes, csum, i);
+
        sum = *csum;
-       if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
+       if (unlikely(iov_iter_is_discard(i))) {
                WARN_ON(1);     /* for now */
                return 0;
        }
diff --git a/lib/objagg.c b/lib/objagg.c
new file mode 100644 (file)
index 0000000..c9b457a
--- /dev/null
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/rhashtable.h>
+#include <linux/list.h>
+#include <linux/sort.h>
+#include <linux/objagg.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/objagg.h>
+
+struct objagg {
+       const struct objagg_ops *ops;
+       void *priv;
+       struct rhashtable obj_ht;
+       struct rhashtable_params ht_params;
+       struct list_head obj_list;
+       unsigned int obj_count;
+};
+
+struct objagg_obj {
+       struct rhash_head ht_node; /* member of objagg->obj_ht */
+       struct list_head list; /* member of objagg->obj_list */
+       struct objagg_obj *parent; /* if the object is nested, this
+                                   * holds pointer to parent, otherwise NULL
+                                   */
+       union {
+               void *delta_priv; /* user delta private */
+               void *root_priv; /* user root private */
+       };
+       unsigned int refcount; /* counts number of users of this object
+                               * including nested objects
+                               */
+       struct objagg_obj_stats stats;
+       unsigned long obj[0];
+};
+
+static unsigned int objagg_obj_ref_inc(struct objagg_obj *objagg_obj)
+{
+       return ++objagg_obj->refcount;
+}
+
+static unsigned int objagg_obj_ref_dec(struct objagg_obj *objagg_obj)
+{
+       return --objagg_obj->refcount;
+}
+
+static void objagg_obj_stats_inc(struct objagg_obj *objagg_obj)
+{
+       objagg_obj->stats.user_count++;
+       objagg_obj->stats.delta_user_count++;
+       if (objagg_obj->parent)
+               objagg_obj->parent->stats.delta_user_count++;
+}
+
+static void objagg_obj_stats_dec(struct objagg_obj *objagg_obj)
+{
+       objagg_obj->stats.user_count--;
+       objagg_obj->stats.delta_user_count--;
+       if (objagg_obj->parent)
+               objagg_obj->parent->stats.delta_user_count--;
+}
+
+static bool objagg_obj_is_root(const struct objagg_obj *objagg_obj)
+{
+       /* Nesting is not supported, so we can use ->parent
+        * to figure out if the object is root.
+        */
+       return !objagg_obj->parent;
+}
+
+/**
+ * objagg_obj_root_priv - obtains root private for an object
+ * @objagg_obj:        objagg object instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * Either the object is root itself when the private is returned
+ * directly, or the parent is root and its private is returned
+ * instead.
+ *
+ * Returns a user private root pointer.
+ */
+const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj)
+{
+       if (objagg_obj_is_root(objagg_obj))
+               return objagg_obj->root_priv;
+       WARN_ON(!objagg_obj_is_root(objagg_obj->parent));
+       return objagg_obj->parent->root_priv;
+}
+EXPORT_SYMBOL(objagg_obj_root_priv);
+
+/**
+ * objagg_obj_delta_priv - obtains delta private for an object
+ * @objagg_obj:        objagg object instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * Returns user private delta pointer or NULL in case the passed
+ * object is root.
+ */
+const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj)
+{
+       if (objagg_obj_is_root(objagg_obj))
+               return NULL;
+       return objagg_obj->delta_priv;
+}
+EXPORT_SYMBOL(objagg_obj_delta_priv);
+
+/**
+ * objagg_obj_raw - obtains object user private pointer
+ * @objagg_obj:        objagg object instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * Returns user private pointer as was passed to objagg_obj_get() by "obj" arg.
+ */
+const void *objagg_obj_raw(const struct objagg_obj *objagg_obj)
+{
+       return objagg_obj->obj;
+}
+EXPORT_SYMBOL(objagg_obj_raw);
+
+static struct objagg_obj *objagg_obj_lookup(struct objagg *objagg, void *obj)
+{
+       return rhashtable_lookup_fast(&objagg->obj_ht, obj, objagg->ht_params);
+}
+
+static int objagg_obj_parent_assign(struct objagg *objagg,
+                                   struct objagg_obj *objagg_obj,
+                                   struct objagg_obj *parent)
+{
+       void *delta_priv;
+
+       delta_priv = objagg->ops->delta_create(objagg->priv, parent->obj,
+                                              objagg_obj->obj);
+       if (IS_ERR(delta_priv))
+               return PTR_ERR(delta_priv);
+
+       /* User returned a delta private, that means that
+        * our object can be aggregated into the parent.
+        */
+       objagg_obj->parent = parent;
+       objagg_obj->delta_priv = delta_priv;
+       objagg_obj_ref_inc(objagg_obj->parent);
+       trace_objagg_obj_parent_assign(objagg, objagg_obj,
+                                      parent,
+                                      parent->refcount);
+       return 0;
+}
+
+static int objagg_obj_parent_lookup_assign(struct objagg *objagg,
+                                          struct objagg_obj *objagg_obj)
+{
+       struct objagg_obj *objagg_obj_cur;
+       int err;
+
+       list_for_each_entry(objagg_obj_cur, &objagg->obj_list, list) {
+               /* Nesting is not supported. In case the object
+                * is not root, it cannot be assigned as parent.
+                */
+               if (!objagg_obj_is_root(objagg_obj_cur))
+                       continue;
+               err = objagg_obj_parent_assign(objagg, objagg_obj,
+                                              objagg_obj_cur);
+               if (!err)
+                       return 0;
+       }
+       return -ENOENT;
+}
+
+static void __objagg_obj_put(struct objagg *objagg,
+                            struct objagg_obj *objagg_obj);
+
+static void objagg_obj_parent_unassign(struct objagg *objagg,
+                                      struct objagg_obj *objagg_obj)
+{
+       trace_objagg_obj_parent_unassign(objagg, objagg_obj,
+                                        objagg_obj->parent,
+                                        objagg_obj->parent->refcount);
+       objagg->ops->delta_destroy(objagg->priv, objagg_obj->delta_priv);
+       __objagg_obj_put(objagg, objagg_obj->parent);
+}
+
+static int objagg_obj_root_create(struct objagg *objagg,
+                                 struct objagg_obj *objagg_obj)
+{
+       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);
+
+       trace_objagg_obj_root_create(objagg, objagg_obj);
+       return 0;
+}
+
+static void objagg_obj_root_destroy(struct objagg *objagg,
+                                   struct objagg_obj *objagg_obj)
+{
+       trace_objagg_obj_root_destroy(objagg, objagg_obj);
+       objagg->ops->root_destroy(objagg->priv, objagg_obj->root_priv);
+}
+
+static int objagg_obj_init(struct objagg *objagg,
+                          struct objagg_obj *objagg_obj)
+{
+       int err;
+
+       /* 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);
+}
+
+static void objagg_obj_fini(struct objagg *objagg,
+                           struct objagg_obj *objagg_obj)
+{
+       if (!objagg_obj_is_root(objagg_obj))
+               objagg_obj_parent_unassign(objagg, objagg_obj);
+       else
+               objagg_obj_root_destroy(objagg, objagg_obj);
+}
+
+static struct objagg_obj *objagg_obj_create(struct objagg *objagg, void *obj)
+{
+       struct objagg_obj *objagg_obj;
+       int err;
+
+       objagg_obj = kzalloc(sizeof(*objagg_obj) + objagg->ops->obj_size,
+                            GFP_KERNEL);
+       if (!objagg_obj)
+               return ERR_PTR(-ENOMEM);
+       objagg_obj_ref_inc(objagg_obj);
+       memcpy(objagg_obj->obj, obj, objagg->ops->obj_size);
+
+       err = objagg_obj_init(objagg, objagg_obj);
+       if (err)
+               goto err_obj_init;
+
+       err = rhashtable_insert_fast(&objagg->obj_ht, &objagg_obj->ht_node,
+                                    objagg->ht_params);
+       if (err)
+               goto err_ht_insert;
+       list_add(&objagg_obj->list, &objagg->obj_list);
+       objagg->obj_count++;
+       trace_objagg_obj_create(objagg, objagg_obj);
+
+       return objagg_obj;
+
+err_ht_insert:
+       objagg_obj_fini(objagg, objagg_obj);
+err_obj_init:
+       kfree(objagg_obj);
+       return ERR_PTR(err);
+}
+
+static struct objagg_obj *__objagg_obj_get(struct objagg *objagg, void *obj)
+{
+       struct objagg_obj *objagg_obj;
+
+       /* First, try to find the object exactly as user passed it,
+        * perhaps it is already in use.
+        */
+       objagg_obj = objagg_obj_lookup(objagg, obj);
+       if (objagg_obj) {
+               objagg_obj_ref_inc(objagg_obj);
+               return objagg_obj;
+       }
+
+       return objagg_obj_create(objagg, obj);
+}
+
+/**
+ * objagg_obj_get - gets an object within objagg instance
+ * @objagg:    objagg instance
+ * @obj:       user-specific private object pointer
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * Size of the "obj" memory is specified in "objagg->ops".
+ *
+ * There are 3 main options this function wraps:
+ * 1) The object according to "obj" already exist. In that case
+ *    the reference counter is incrementes and the object is returned.
+ * 2) The object does not exist, but it can be aggregated within
+ *    another object. In that case, user ops->delta_create() is called
+ *    to obtain delta data and a new object is created with returned
+ *    user-delta private pointer.
+ * 3) The object does not exist and cannot be aggregated into
+ *    any of the existing objects. In that case, user ops->root_create()
+ *    is called to create the root and a new object is created with
+ *    returned user-root private pointer.
+ *
+ * Returns a pointer to objagg object instance in case of success,
+ * otherwise it returns pointer error using ERR_PTR macro.
+ */
+struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj)
+{
+       struct objagg_obj *objagg_obj;
+
+       objagg_obj = __objagg_obj_get(objagg, obj);
+       if (IS_ERR(objagg_obj))
+               return objagg_obj;
+       objagg_obj_stats_inc(objagg_obj);
+       trace_objagg_obj_get(objagg, objagg_obj, objagg_obj->refcount);
+       return objagg_obj;
+}
+EXPORT_SYMBOL(objagg_obj_get);
+
+static void objagg_obj_destroy(struct objagg *objagg,
+                              struct objagg_obj *objagg_obj)
+{
+       trace_objagg_obj_destroy(objagg, objagg_obj);
+       --objagg->obj_count;
+       list_del(&objagg_obj->list);
+       rhashtable_remove_fast(&objagg->obj_ht, &objagg_obj->ht_node,
+                              objagg->ht_params);
+       objagg_obj_fini(objagg, objagg_obj);
+       kfree(objagg_obj);
+}
+
+static void __objagg_obj_put(struct objagg *objagg,
+                            struct objagg_obj *objagg_obj)
+{
+       if (!objagg_obj_ref_dec(objagg_obj))
+               objagg_obj_destroy(objagg, objagg_obj);
+}
+
+/**
+ * objagg_obj_put - puts an object within objagg instance
+ * @objagg:    objagg instance
+ * @objagg_obj:        objagg object instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * Symmetric to objagg_obj_get().
+ */
+void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj)
+{
+       trace_objagg_obj_put(objagg, objagg_obj, objagg_obj->refcount);
+       objagg_obj_stats_dec(objagg_obj);
+       __objagg_obj_put(objagg, objagg_obj);
+}
+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
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * The purpose of the library is to provide an infrastructure to
+ * aggregate user-specified objects. Library does not care about the type
+ * of the object. User fills-up ops which take care of the specific
+ * user object manipulation.
+ *
+ * As a very stupid example, consider integer numbers. For example
+ * number 8 as a root object. That can aggregate number 9 with delta 1,
+ * number 10 with delta 2, etc. This example is implemented as
+ * a part of a testing module in test_objagg.c file.
+ *
+ * Each objagg instance contains multiple trees. Each tree node is
+ * represented by "an object". In the current implementation there can be
+ * only roots and leafs nodes. Leaf nodes are called deltas.
+ * But in general, this can be easily extended for intermediate nodes.
+ * In that extension, a delta would be associated with all non-root
+ * nodes.
+ *
+ * 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;
+       int err;
+
+       if (WARN_ON(!ops || !ops->root_create || !ops->root_destroy ||
+                   !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;
+       objagg->priv = priv;
+       INIT_LIST_HEAD(&objagg->obj_list);
+
+       objagg->ht_params.key_len = ops->obj_size;
+       objagg->ht_params.key_offset = offsetof(struct objagg_obj, obj);
+       objagg->ht_params.head_offset = offsetof(struct objagg_obj, ht_node);
+
+       err = rhashtable_init(&objagg->obj_ht, &objagg->ht_params);
+       if (err)
+               goto err_rhashtable_init;
+
+       trace_objagg_create(objagg);
+       return objagg;
+
+err_rhashtable_init:
+       kfree(objagg);
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL(objagg_create);
+
+/**
+ * objagg_destroy - destroys a new objagg instance
+ * @objagg:    objagg instance
+ *
+ * Note: all locking must be provided by the caller.
+ */
+void objagg_destroy(struct objagg *objagg)
+{
+       trace_objagg_destroy(objagg);
+       WARN_ON(!list_empty(&objagg->obj_list));
+       rhashtable_destroy(&objagg->obj_ht);
+       kfree(objagg);
+}
+EXPORT_SYMBOL(objagg_destroy);
+
+static int objagg_stats_info_sort_cmp_func(const void *a, const void *b)
+{
+       const struct objagg_obj_stats_info *stats_info1 = a;
+       const struct objagg_obj_stats_info *stats_info2 = b;
+
+       if (stats_info1->is_root != stats_info2->is_root)
+               return stats_info2->is_root - stats_info1->is_root;
+       if (stats_info1->stats.delta_user_count !=
+           stats_info2->stats.delta_user_count)
+               return stats_info2->stats.delta_user_count -
+                      stats_info1->stats.delta_user_count;
+       return stats_info2->stats.user_count - stats_info1->stats.user_count;
+}
+
+/**
+ * objagg_stats_get - obtains stats of the objagg instance
+ * @objagg:    objagg instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * The returned structure contains statistics of all object
+ * 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 more 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_stats_get(struct objagg *objagg)
+{
+       struct objagg_stats *objagg_stats;
+       struct objagg_obj *objagg_obj;
+       size_t alloc_size;
+       int i;
+
+       alloc_size = sizeof(*objagg_stats) +
+                    sizeof(objagg_stats->stats_info[0]) * objagg->obj_count;
+       objagg_stats = kzalloc(alloc_size, GFP_KERNEL);
+       if (!objagg_stats)
+               return ERR_PTR(-ENOMEM);
+
+       i = 0;
+       list_for_each_entry(objagg_obj, &objagg->obj_list, list) {
+               memcpy(&objagg_stats->stats_info[i].stats, &objagg_obj->stats,
+                      sizeof(objagg_stats->stats_info[0].stats));
+               objagg_stats->stats_info[i].objagg_obj = objagg_obj;
+               objagg_stats->stats_info[i].is_root =
+                                       objagg_obj_is_root(objagg_obj);
+               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_stats_get);
+
+/**
+ * objagg_stats_puts - puts stats of the objagg instance
+ * @objagg_stats:      objagg instance stats
+ *
+ * Note: all locking must be provided by the caller.
+ */
+void objagg_stats_put(const struct objagg_stats *objagg_stats)
+{
+       kfree(objagg_stats);
+}
+EXPORT_SYMBOL(objagg_stats_put);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
+MODULE_DESCRIPTION("Object aggregation manager");
index 1106bb6..14d5154 100644 (file)
@@ -784,11 +784,11 @@ void *__radix_tree_lookup(const struct radix_tree_root *root,
        while (radix_tree_is_internal_node(node)) {
                unsigned offset;
 
-               if (node == RADIX_TREE_RETRY)
-                       goto restart;
                parent = entry_to_node(node);
                offset = radix_tree_descend(parent, &node, index);
                slot = parent->slots + offset;
+               if (node == RADIX_TREE_RETRY)
+                       goto restart;
                if (parent->shift == 0)
                        break;
        }
index 30526af..852ffa5 100644 (file)
@@ -1179,8 +1179,7 @@ struct rhash_head __rcu **rht_bucket_nested(const struct bucket_table *tbl,
                                            unsigned int hash)
 {
        const unsigned int shift = PAGE_SHIFT - ilog2(sizeof(void *));
-       static struct rhash_head __rcu *rhnull =
-               (struct rhash_head __rcu *)NULLS_MARKER(0);
+       static struct rhash_head __rcu *rhnull;
        unsigned int index = hash & ((1 << tbl->nest) - 1);
        unsigned int size = tbl->size >> tbl->nest;
        unsigned int subhash = hash;
@@ -1198,8 +1197,11 @@ struct rhash_head __rcu **rht_bucket_nested(const struct bucket_table *tbl,
                subhash >>= shift;
        }
 
-       if (!ntbl)
+       if (!ntbl) {
+               if (!rhnull)
+                       INIT_RHT_NULLS_HEAD(rhnull);
                return &rhnull;
+       }
 
        return &ntbl[subhash].bucket;
 
index aa22bca..f3e5707 100644 (file)
@@ -39,6 +39,7 @@
 #define SKB_HASH       0x1234aaab
 #define SKB_QUEUE_MAP  123
 #define SKB_VLAN_TCI   0xffff
+#define SKB_VLAN_PRESENT       1
 #define SKB_DEV_IFINDEX        577
 #define SKB_DEV_TYPE   588
 
@@ -725,8 +726,8 @@ static struct bpf_test tests[] = {
                CLASSIC,
                { },
                {
-                       { 1, SKB_VLAN_TCI & ~VLAN_TAG_PRESENT },
-                       { 10, SKB_VLAN_TCI & ~VLAN_TAG_PRESENT }
+                       { 1, SKB_VLAN_TCI },
+                       { 10, SKB_VLAN_TCI }
                },
        },
        {
@@ -739,8 +740,8 @@ static struct bpf_test tests[] = {
                CLASSIC,
                { },
                {
-                       { 1, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) },
-                       { 10, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) }
+                       { 1, SKB_VLAN_PRESENT },
+                       { 10, SKB_VLAN_PRESENT }
                },
        },
        {
@@ -5289,8 +5290,8 @@ static struct bpf_test tests[] = {
 #endif
                { },
                {
-                       {  1, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) },
-                       { 10, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) }
+                       {  1, SKB_VLAN_PRESENT },
+                       { 10, SKB_VLAN_PRESENT }
                },
                .fill_helper = bpf_fill_maxinsns6,
                .expected_errcode = -ENOTSUPP,
@@ -6493,6 +6494,7 @@ static struct sk_buff *populate_skb(char *buf, int size)
        skb->hash = SKB_HASH;
        skb->queue_mapping = SKB_QUEUE_MAP;
        skb->vlan_tci = SKB_VLAN_TCI;
+       skb->vlan_present = SKB_VLAN_PRESENT;
        skb->vlan_proto = htons(ETH_P_IP);
        dev_net_set(&dev, &init_net);
        skb->dev = &dev;
index b984806..7cab9a9 100644 (file)
@@ -837,6 +837,7 @@ static ssize_t read_firmware_show(struct device *dev,
        if (req->fw->size > PAGE_SIZE) {
                pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
                rc = -EINVAL;
+               goto out;
        }
        memcpy(buf, req->fw->data, req->fw->size);
 
index 626f580..5144899 100644 (file)
@@ -99,7 +99,7 @@ static void __init test_hexdump_prepare_test(size_t len, int rowsize,
                const char *q = *result++;
                size_t amount = strlen(q);
 
-               strncpy(p, q, amount);
+               memcpy(p, q, amount);
                p += amount;
 
                *p++ = ' ';
index e3ddd83..d82d022 100644 (file)
@@ -1214,7 +1214,6 @@ void unregister_test_dev_kmod(struct kmod_test_device *test_dev)
 
        dev_info(test_dev->dev, "removing interface\n");
        misc_deregister(&test_dev->misc_dev);
-       kfree(&test_dev->misc_dev.name);
 
        mutex_unlock(&test_dev->config_mutex);
        mutex_unlock(&test_dev->trigger_mutex);
diff --git a/lib/test_objagg.c b/lib/test_objagg.c
new file mode 100644 (file)
index 0000000..ab57144
--- /dev/null
@@ -0,0 +1,836 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/objagg.h>
+
+struct tokey {
+       unsigned int id;
+};
+
+#define NUM_KEYS 32
+
+static int key_id_index(unsigned int key_id)
+{
+       if (key_id >= NUM_KEYS) {
+               WARN_ON(1);
+               return 0;
+       }
+       return key_id;
+}
+
+#define BUF_LEN 128
+
+struct world {
+       unsigned int root_count;
+       unsigned int delta_count;
+       char next_root_buf[BUF_LEN];
+       struct objagg_obj *objagg_objs[NUM_KEYS];
+       unsigned int key_refs[NUM_KEYS];
+};
+
+struct root {
+       struct tokey key;
+       char buf[BUF_LEN];
+};
+
+struct delta {
+       unsigned int key_id_diff;
+};
+
+static struct objagg_obj *world_obj_get(struct world *world,
+                                       struct objagg *objagg,
+                                       unsigned int key_id)
+{
+       struct objagg_obj *objagg_obj;
+       struct tokey key;
+       int err;
+
+       key.id = key_id;
+       objagg_obj = objagg_obj_get(objagg, &key);
+       if (IS_ERR(objagg_obj)) {
+               pr_err("Key %u: Failed to get object.\n", key_id);
+               return objagg_obj;
+       }
+       if (!world->key_refs[key_id_index(key_id)]) {
+               world->objagg_objs[key_id_index(key_id)] = objagg_obj;
+       } else if (world->objagg_objs[key_id_index(key_id)] != objagg_obj) {
+               pr_err("Key %u: God another object for the same key.\n",
+                      key_id);
+               err = -EINVAL;
+               goto err_key_id_check;
+       }
+       world->key_refs[key_id_index(key_id)]++;
+       return objagg_obj;
+
+err_key_id_check:
+       objagg_obj_put(objagg, objagg_obj);
+       return ERR_PTR(err);
+}
+
+static void world_obj_put(struct world *world, struct objagg *objagg,
+                         unsigned int key_id)
+{
+       struct objagg_obj *objagg_obj;
+
+       if (!world->key_refs[key_id_index(key_id)])
+               return;
+       objagg_obj = world->objagg_objs[key_id_index(key_id)];
+       objagg_obj_put(objagg, objagg_obj);
+       world->key_refs[key_id_index(key_id)]--;
+}
+
+#define MAX_KEY_ID_DIFF 5
+
+static void *delta_create(void *priv, void *parent_obj, void *obj)
+{
+       struct tokey *parent_key = parent_obj;
+       struct world *world = priv;
+       struct tokey *key = obj;
+       int diff = key->id - parent_key->id;
+       struct delta *delta;
+
+       if (diff < 0 || diff > MAX_KEY_ID_DIFF)
+               return ERR_PTR(-EINVAL);
+
+       delta = kzalloc(sizeof(*delta), GFP_KERNEL);
+       if (!delta)
+               return ERR_PTR(-ENOMEM);
+       delta->key_id_diff = diff;
+       world->delta_count++;
+       return delta;
+}
+
+static void delta_destroy(void *priv, void *delta_priv)
+{
+       struct delta *delta = delta_priv;
+       struct world *world = priv;
+
+       world->delta_count--;
+       kfree(delta);
+}
+
+static void *root_create(void *priv, void *obj)
+{
+       struct world *world = priv;
+       struct tokey *key = obj;
+       struct root *root;
+
+       root = kzalloc(sizeof(*root), GFP_KERNEL);
+       if (!root)
+               return ERR_PTR(-ENOMEM);
+       memcpy(&root->key, key, sizeof(root->key));
+       memcpy(root->buf, world->next_root_buf, sizeof(root->buf));
+       world->root_count++;
+       return root;
+}
+
+static void root_destroy(void *priv, void *root_priv)
+{
+       struct root *root = root_priv;
+       struct world *world = priv;
+
+       world->root_count--;
+       kfree(root);
+}
+
+static int test_nodelta_obj_get(struct world *world, struct objagg *objagg,
+                               unsigned int key_id, bool should_create_root)
+{
+       unsigned int orig_root_count = world->root_count;
+       struct objagg_obj *objagg_obj;
+       const struct root *root;
+       int err;
+
+       if (should_create_root)
+               prandom_bytes(world->next_root_buf,
+                             sizeof(world->next_root_buf));
+
+       objagg_obj = world_obj_get(world, objagg, key_id);
+       if (IS_ERR(objagg_obj)) {
+               pr_err("Key %u: Failed to get object.\n", key_id);
+               return PTR_ERR(objagg_obj);
+       }
+       if (should_create_root) {
+               if (world->root_count != orig_root_count + 1) {
+                       pr_err("Key %u: Root was not created\n", key_id);
+                       err = -EINVAL;
+                       goto err_check_root_count;
+               }
+       } else {
+               if (world->root_count != orig_root_count) {
+                       pr_err("Key %u: Root was incorrectly created\n",
+                              key_id);
+                       err = -EINVAL;
+                       goto err_check_root_count;
+               }
+       }
+       root = objagg_obj_root_priv(objagg_obj);
+       if (root->key.id != key_id) {
+               pr_err("Key %u: Root has unexpected key id\n", key_id);
+               err = -EINVAL;
+               goto err_check_key_id;
+       }
+       if (should_create_root &&
+           memcmp(world->next_root_buf, root->buf, sizeof(root->buf))) {
+               pr_err("Key %u: Buffer does not match the expected content\n",
+                      key_id);
+               err = -EINVAL;
+               goto err_check_buf;
+       }
+       return 0;
+
+err_check_buf:
+err_check_key_id:
+err_check_root_count:
+       objagg_obj_put(objagg, objagg_obj);
+       return err;
+}
+
+static int test_nodelta_obj_put(struct world *world, struct objagg *objagg,
+                               unsigned int key_id, bool should_destroy_root)
+{
+       unsigned int orig_root_count = world->root_count;
+
+       world_obj_put(world, objagg, key_id);
+
+       if (should_destroy_root) {
+               if (world->root_count != orig_root_count - 1) {
+                       pr_err("Key %u: Root was not destroyed\n", key_id);
+                       return -EINVAL;
+               }
+       } else {
+               if (world->root_count != orig_root_count) {
+                       pr_err("Key %u: Root was incorrectly destroyed\n",
+                              key_id);
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+static int check_stats_zero(struct objagg *objagg)
+{
+       const struct objagg_stats *stats;
+       int err = 0;
+
+       stats = objagg_stats_get(objagg);
+       if (IS_ERR(stats))
+               return PTR_ERR(stats);
+
+       if (stats->stats_info_count != 0) {
+               pr_err("Stats: Object count is not zero while it should be\n");
+               err = -EINVAL;
+       }
+
+       objagg_stats_put(stats);
+       return err;
+}
+
+static int check_stats_nodelta(struct objagg *objagg)
+{
+       const struct objagg_stats *stats;
+       int i;
+       int err;
+
+       stats = objagg_stats_get(objagg);
+       if (IS_ERR(stats))
+               return PTR_ERR(stats);
+
+       if (stats->stats_info_count != NUM_KEYS) {
+               pr_err("Stats: Unexpected object count (%u expected, %u returned)\n",
+                      NUM_KEYS, stats->stats_info_count);
+               err = -EINVAL;
+               goto stats_put;
+       }
+
+       for (i = 0; i < stats->stats_info_count; i++) {
+               if (stats->stats_info[i].stats.user_count != 2) {
+                       pr_err("Stats: incorrect user count\n");
+                       err = -EINVAL;
+                       goto stats_put;
+               }
+               if (stats->stats_info[i].stats.delta_user_count != 2) {
+                       pr_err("Stats: incorrect delta user count\n");
+                       err = -EINVAL;
+                       goto stats_put;
+               }
+       }
+       err = 0;
+
+stats_put:
+       objagg_stats_put(stats);
+       return err;
+}
+
+static void *delta_create_dummy(void *priv, void *parent_obj, void *obj)
+{
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
+static void delta_destroy_dummy(void *priv, void *delta_priv)
+{
+}
+
+static const struct objagg_ops nodelta_ops = {
+       .obj_size = sizeof(struct tokey),
+       .delta_create = delta_create_dummy,
+       .delta_destroy = delta_destroy_dummy,
+       .root_create = root_create,
+       .root_destroy = root_destroy,
+};
+
+static int test_nodelta(void)
+{
+       struct world world = {};
+       struct objagg *objagg;
+       int i;
+       int err;
+
+       objagg = objagg_create(&nodelta_ops, &world);
+       if (IS_ERR(objagg))
+               return PTR_ERR(objagg);
+
+       err = check_stats_zero(objagg);
+       if (err)
+               goto err_stats_first_zero;
+
+       /* First round of gets, the root objects should be created */
+       for (i = 0; i < NUM_KEYS; i++) {
+               err = test_nodelta_obj_get(&world, objagg, i, true);
+               if (err)
+                       goto err_obj_first_get;
+       }
+
+       /* Do the second round of gets, all roots are already created,
+        * make sure that no new root is created
+        */
+       for (i = 0; i < NUM_KEYS; i++) {
+               err = test_nodelta_obj_get(&world, objagg, i, false);
+               if (err)
+                       goto err_obj_second_get;
+       }
+
+       err = check_stats_nodelta(objagg);
+       if (err)
+               goto err_stats_nodelta;
+
+       for (i = NUM_KEYS - 1; i >= 0; i--) {
+               err = test_nodelta_obj_put(&world, objagg, i, false);
+               if (err)
+                       goto err_obj_first_put;
+       }
+       for (i = NUM_KEYS - 1; i >= 0; i--) {
+               err = test_nodelta_obj_put(&world, objagg, i, true);
+               if (err)
+                       goto err_obj_second_put;
+       }
+
+       err = check_stats_zero(objagg);
+       if (err)
+               goto err_stats_second_zero;
+
+       objagg_destroy(objagg);
+       return 0;
+
+err_stats_nodelta:
+err_obj_first_put:
+err_obj_second_get:
+       for (i--; i >= 0; i--)
+               world_obj_put(&world, objagg, i);
+
+       i = NUM_KEYS;
+err_obj_first_get:
+err_obj_second_put:
+       for (i--; i >= 0; i--)
+               world_obj_put(&world, objagg, i);
+err_stats_first_zero:
+err_stats_second_zero:
+       objagg_destroy(objagg);
+       return err;
+}
+
+static const struct objagg_ops delta_ops = {
+       .obj_size = sizeof(struct tokey),
+       .delta_create = delta_create,
+       .delta_destroy = delta_destroy,
+       .root_create = root_create,
+       .root_destroy = root_destroy,
+};
+
+enum action {
+       ACTION_GET,
+       ACTION_PUT,
+};
+
+enum expect_delta {
+       EXPECT_DELTA_SAME,
+       EXPECT_DELTA_INC,
+       EXPECT_DELTA_DEC,
+};
+
+enum expect_root {
+       EXPECT_ROOT_SAME,
+       EXPECT_ROOT_INC,
+       EXPECT_ROOT_DEC,
+};
+
+struct expect_stats_info {
+       struct objagg_obj_stats stats;
+       bool is_root;
+       unsigned int key_id;
+};
+
+struct expect_stats {
+       unsigned int info_count;
+       struct expect_stats_info info[NUM_KEYS];
+};
+
+struct action_item {
+       unsigned int key_id;
+       enum action action;
+       enum expect_delta expect_delta;
+       enum expect_root expect_root;
+       struct expect_stats expect_stats;
+};
+
+#define EXPECT_STATS(count, ...)               \
+{                                              \
+       .info_count = count,                    \
+       .info = { __VA_ARGS__ }                 \
+}
+
+#define ROOT(key_id, user_count, delta_user_count)     \
+       {{user_count, delta_user_count}, true, key_id}
+
+#define DELTA(key_id, user_count)                      \
+       {{user_count, user_count}, false, key_id}
+
+static const struct action_item action_items[] = {
+       {
+               1, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
+               EXPECT_STATS(1, ROOT(1, 1, 1)),
+       },      /* r: 1                 d: */
+       {
+               7, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
+               EXPECT_STATS(2, ROOT(1, 1, 1), ROOT(7, 1, 1)),
+       },      /* r: 1, 7              d: */
+       {
+               3, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(3, ROOT(1, 1, 2), ROOT(7, 1, 1),
+                               DELTA(3, 1)),
+       },      /* r: 1, 7              d: 3^1 */
+       {
+               5, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(4, ROOT(1, 1, 3), ROOT(7, 1, 1),
+                               DELTA(3, 1), DELTA(5, 1)),
+       },      /* r: 1, 7              d: 3^1, 5^1 */
+       {
+               3, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(4, ROOT(1, 1, 4), ROOT(7, 1, 1),
+                               DELTA(3, 2), DELTA(5, 1)),
+       },      /* r: 1, 7              d: 3^1, 3^1, 5^1 */
+       {
+               1, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(4, ROOT(1, 2, 5), ROOT(7, 1, 1),
+                               DELTA(3, 2), DELTA(5, 1)),
+       },      /* r: 1, 1, 7           d: 3^1, 3^1, 5^1 */
+       {
+               30, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
+               EXPECT_STATS(5, ROOT(1, 2, 5), ROOT(7, 1, 1), ROOT(30, 1, 1),
+                               DELTA(3, 2), DELTA(5, 1)),
+       },      /* r: 1, 1, 7, 30       d: 3^1, 3^1, 5^1 */
+       {
+               8, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(6, ROOT(1, 2, 5), ROOT(7, 1, 2), ROOT(30, 1, 1),
+                               DELTA(3, 2), DELTA(5, 1), DELTA(8, 1)),
+       },      /* r: 1, 1, 7, 30       d: 3^1, 3^1, 5^1, 8^7 */
+       {
+               8, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(6, ROOT(1, 2, 5), ROOT(7, 1, 3), ROOT(30, 1, 1),
+                               DELTA(3, 2), DELTA(8, 2), DELTA(5, 1)),
+       },      /* r: 1, 1, 7, 30       d: 3^1, 3^1, 5^1, 8^7, 8^7 */
+       {
+               3, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(6, ROOT(1, 2, 4), ROOT(7, 1, 3), ROOT(30, 1, 1),
+                               DELTA(8, 2), DELTA(3, 1), DELTA(5, 1)),
+       },      /* r: 1, 1, 7, 30       d: 3^1, 5^1, 8^7, 8^7 */
+       {
+               3, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(1, 2, 3), ROOT(7, 1, 3), ROOT(30, 1, 1),
+                               DELTA(8, 2), DELTA(5, 1)),
+       },      /* r: 1, 1, 7, 30       d: 5^1, 8^7, 8^7 */
+       {
+               1, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(1, 1, 2), ROOT(30, 1, 1),
+                               DELTA(8, 2), DELTA(5, 1)),
+       },      /* r: 1, 7, 30          d: 5^1, 8^7, 8^7 */
+       {
+               1, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(30, 1, 1), ROOT(1, 0, 1),
+                               DELTA(8, 2), DELTA(5, 1)),
+       },      /* r: 7, 30             d: 5^1, 8^7, 8^7 */
+       {
+               5, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_DEC,
+               EXPECT_STATS(3, ROOT(7, 1, 3), ROOT(30, 1, 1),
+                               DELTA(8, 2)),
+       },      /* r: 7, 30             d: 8^7, 8^7 */
+       {
+               5, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
+               EXPECT_STATS(4, ROOT(7, 1, 3), ROOT(30, 1, 1), ROOT(5, 1, 1),
+                               DELTA(8, 2)),
+       },      /* r: 7, 30, 5          d: 8^7, 8^7 */
+       {
+               6, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(5, 1, 2), ROOT(30, 1, 1),
+                               DELTA(8, 2), DELTA(6, 1)),
+       },      /* r: 7, 30, 5          d: 8^7, 8^7, 6^5 */
+       {
+               8, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 4), ROOT(5, 1, 2), ROOT(30, 1, 1),
+                               DELTA(8, 3), DELTA(6, 1)),
+       },      /* r: 7, 30, 5          d: 8^7, 8^7, 8^7, 6^5 */
+       {
+               8, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(5, 1, 2), ROOT(30, 1, 1),
+                               DELTA(8, 2), DELTA(6, 1)),
+       },      /* r: 7, 30, 5          d: 8^7, 8^7, 6^5 */
+       {
+               8, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 2), ROOT(5, 1, 2), ROOT(30, 1, 1),
+                               DELTA(8, 1), DELTA(6, 1)),
+       },      /* r: 7, 30, 5          d: 8^7, 6^5 */
+       {
+               8, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(4, ROOT(5, 1, 2), ROOT(7, 1, 1), ROOT(30, 1, 1),
+                               DELTA(6, 1)),
+       },      /* r: 7, 30, 5          d: 6^5 */
+       {
+               8, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(5, 1, 3), ROOT(7, 1, 1), ROOT(30, 1, 1),
+                               DELTA(6, 1), DELTA(8, 1)),
+       },      /* r: 7, 30, 5          d: 6^5, 8^5 */
+       {
+               7, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_DEC,
+               EXPECT_STATS(4, ROOT(5, 1, 3), ROOT(30, 1, 1),
+                               DELTA(6, 1), DELTA(8, 1)),
+       },      /* r: 30, 5             d: 6^5, 8^5 */
+       {
+               30, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_DEC,
+               EXPECT_STATS(3, ROOT(5, 1, 3),
+                               DELTA(6, 1), DELTA(8, 1)),
+       },      /* r: 5                 d: 6^5, 8^5 */
+       {
+               5, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(3, ROOT(5, 0, 2),
+                               DELTA(6, 1), DELTA(8, 1)),
+       },      /* r:                   d: 6^5, 8^5 */
+       {
+               6, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(2, ROOT(5, 0, 1),
+                               DELTA(8, 1)),
+       },      /* r:                   d: 6^5 */
+       {
+               8, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_DEC,
+               EXPECT_STATS(0, ),
+       },      /* r:                   d: */
+};
+
+static int check_expect(struct world *world,
+                       const struct action_item *action_item,
+                       unsigned int orig_delta_count,
+                       unsigned int orig_root_count)
+{
+       unsigned int key_id = action_item->key_id;
+
+       switch (action_item->expect_delta) {
+       case EXPECT_DELTA_SAME:
+               if (orig_delta_count != world->delta_count) {
+                       pr_err("Key %u: Delta count changed while expected to remain the same.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+               break;
+       case EXPECT_DELTA_INC:
+               if (WARN_ON(action_item->action == ACTION_PUT))
+                       return -EINVAL;
+               if (orig_delta_count + 1 != world->delta_count) {
+                       pr_err("Key %u: Delta count was not incremented.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+               break;
+       case EXPECT_DELTA_DEC:
+               if (WARN_ON(action_item->action == ACTION_GET))
+                       return -EINVAL;
+               if (orig_delta_count - 1 != world->delta_count) {
+                       pr_err("Key %u: Delta count was not decremented.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+               break;
+       }
+
+       switch (action_item->expect_root) {
+       case EXPECT_ROOT_SAME:
+               if (orig_root_count != world->root_count) {
+                       pr_err("Key %u: Root count changed while expected to remain the same.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+               break;
+       case EXPECT_ROOT_INC:
+               if (WARN_ON(action_item->action == ACTION_PUT))
+                       return -EINVAL;
+               if (orig_root_count + 1 != world->root_count) {
+                       pr_err("Key %u: Root count was not incremented.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+               break;
+       case EXPECT_ROOT_DEC:
+               if (WARN_ON(action_item->action == ACTION_GET))
+                       return -EINVAL;
+               if (orig_root_count - 1 != world->root_count) {
+                       pr_err("Key %u: Root count was not decremented.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static unsigned int obj_to_key_id(struct objagg_obj *objagg_obj)
+{
+       const struct tokey *root_key;
+       const struct delta *delta;
+       unsigned int key_id;
+
+       root_key = objagg_obj_root_priv(objagg_obj);
+       key_id = root_key->id;
+       delta = objagg_obj_delta_priv(objagg_obj);
+       if (delta)
+               key_id += delta->key_id_diff;
+       return key_id;
+}
+
+static int
+check_expect_stats_nums(const struct objagg_obj_stats_info *stats_info,
+                       const struct expect_stats_info *expect_stats_info,
+                       const char **errmsg)
+{
+       if (stats_info->is_root != expect_stats_info->is_root) {
+               if (errmsg)
+                       *errmsg = "Incorrect root/delta indication";
+               return -EINVAL;
+       }
+       if (stats_info->stats.user_count !=
+           expect_stats_info->stats.user_count) {
+               if (errmsg)
+                       *errmsg = "Incorrect user count";
+               return -EINVAL;
+       }
+       if (stats_info->stats.delta_user_count !=
+           expect_stats_info->stats.delta_user_count) {
+               if (errmsg)
+                       *errmsg = "Incorrect delta user count";
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+check_expect_stats_key_id(const struct objagg_obj_stats_info *stats_info,
+                         const struct expect_stats_info *expect_stats_info,
+                         const char **errmsg)
+{
+       if (obj_to_key_id(stats_info->objagg_obj) !=
+           expect_stats_info->key_id) {
+               if (errmsg)
+                       *errmsg = "incorrect key id";
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int check_expect_stats_neigh(const struct objagg_stats *stats,
+                                   const struct expect_stats *expect_stats,
+                                   int pos)
+{
+       int i;
+       int err;
+
+       for (i = pos - 1; i >= 0; i--) {
+               err = check_expect_stats_nums(&stats->stats_info[i],
+                                             &expect_stats->info[pos], NULL);
+               if (err)
+                       break;
+               err = check_expect_stats_key_id(&stats->stats_info[i],
+                                               &expect_stats->info[pos], NULL);
+               if (!err)
+                       return 0;
+       }
+       for (i = pos + 1; i < stats->stats_info_count; i++) {
+               err = check_expect_stats_nums(&stats->stats_info[i],
+                                             &expect_stats->info[pos], NULL);
+               if (err)
+                       break;
+               err = check_expect_stats_key_id(&stats->stats_info[i],
+                                               &expect_stats->info[pos], NULL);
+               if (!err)
+                       return 0;
+       }
+       return -EINVAL;
+}
+
+static int __check_expect_stats(const struct objagg_stats *stats,
+                               const struct expect_stats *expect_stats,
+                               const char **errmsg)
+{
+       int i;
+       int err;
+
+       if (stats->stats_info_count != expect_stats->info_count) {
+               *errmsg = "Unexpected object count";
+               return -EINVAL;
+       }
+
+       for (i = 0; i < stats->stats_info_count; i++) {
+               err = check_expect_stats_nums(&stats->stats_info[i],
+                                             &expect_stats->info[i], errmsg);
+               if (err)
+                       return err;
+               err = check_expect_stats_key_id(&stats->stats_info[i],
+                                               &expect_stats->info[i], errmsg);
+               if (err) {
+                       /* It is possible that one of the neighbor stats with
+                        * same numbers have the correct key id, so check it
+                        */
+                       err = check_expect_stats_neigh(stats, expect_stats, i);
+                       if (err)
+                               return err;
+               }
+       }
+       return 0;
+}
+
+static int check_expect_stats(struct objagg *objagg,
+                             const struct expect_stats *expect_stats,
+                             const char **errmsg)
+{
+       const struct objagg_stats *stats;
+       int err;
+
+       stats = objagg_stats_get(objagg);
+       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_delta_action_item(struct world *world,
+                                 struct objagg *objagg,
+                                 const struct action_item *action_item,
+                                 bool inverse)
+{
+       unsigned int orig_delta_count = world->delta_count;
+       unsigned int orig_root_count = world->root_count;
+       unsigned int key_id = action_item->key_id;
+       enum action action = action_item->action;
+       struct objagg_obj *objagg_obj;
+       const char *errmsg;
+       int err;
+
+       if (inverse)
+               action = action == ACTION_GET ? ACTION_PUT : ACTION_GET;
+
+       switch (action) {
+       case ACTION_GET:
+               objagg_obj = world_obj_get(world, objagg, key_id);
+               if (IS_ERR(objagg_obj))
+                       return PTR_ERR(objagg_obj);
+               break;
+       case ACTION_PUT:
+               world_obj_put(world, objagg, key_id);
+               break;
+       }
+
+       if (inverse)
+               return 0;
+       err = check_expect(world, action_item,
+                          orig_delta_count, orig_root_count);
+       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);
+               goto errout;
+       }
+
+       return 0;
+
+errout:
+       /* This can only happen when action is not inversed.
+        * So in case of an error, cleanup by doing inverse action.
+        */
+       test_delta_action_item(world, objagg, action_item, true);
+       return err;
+}
+
+static int test_delta(void)
+{
+       struct world world = {};
+       struct objagg *objagg;
+       int i;
+       int err;
+
+       objagg = objagg_create(&delta_ops, &world);
+       if (IS_ERR(objagg))
+               return PTR_ERR(objagg);
+
+       for (i = 0; i < ARRAY_SIZE(action_items); i++) {
+               err = test_delta_action_item(&world, objagg,
+                                            &action_items[i], false);
+               if (err)
+                       goto err_do_action_item;
+       }
+
+       objagg_destroy(objagg);
+       return 0;
+
+err_do_action_item:
+       for (i--; i >= 0; i--)
+               test_delta_action_item(&world, objagg, &action_items[i], true);
+
+       objagg_destroy(objagg);
+       return err;
+}
+
+static int __init test_objagg_init(void)
+{
+       int err;
+
+       err = test_nodelta();
+       if (err)
+               return err;
+       return test_delta();
+}
+
+static void __exit test_objagg_exit(void)
+{
+}
+
+module_init(test_objagg_init);
+module_exit(test_objagg_exit);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
+MODULE_DESCRIPTION("Test module for objagg");
index 82ac39c..6a8ac76 100644 (file)
 #include <linux/module.h>
 #include <linux/rcupdate.h>
 #include <linux/rhashtable.h>
-#include <linux/semaphore.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/random.h>
 #include <linux/vmalloc.h>
+#include <linux/wait.h>
 
 #define MAX_ENTRIES    1000000
 #define TEST_INSERT_FAIL INT_MAX
@@ -112,8 +112,8 @@ static struct rhashtable_params test_rht_params_dup = {
        .automatic_shrinking = false,
 };
 
-static struct semaphore prestart_sem;
-static struct semaphore startup_sem = __SEMAPHORE_INITIALIZER(startup_sem, 0);
+static atomic_t startup_count;
+static DECLARE_WAIT_QUEUE_HEAD(startup_wait);
 
 static int insert_retry(struct rhashtable *ht, struct test_obj *obj,
                         const struct rhashtable_params params)
@@ -634,9 +634,12 @@ static int threadfunc(void *data)
        int i, step, err = 0, insert_retries = 0;
        struct thread_data *tdata = data;
 
-       up(&prestart_sem);
-       if (down_interruptible(&startup_sem))
-               pr_err("  thread[%d]: down_interruptible failed\n", tdata->id);
+       if (atomic_dec_and_test(&startup_count))
+               wake_up(&startup_wait);
+       if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == -1)) {
+               pr_err("  thread[%d]: interrupted\n", tdata->id);
+               goto out;
+       }
 
        for (i = 0; i < tdata->entries; i++) {
                tdata->objs[i].value.id = i;
@@ -755,7 +758,7 @@ static int __init test_rht_init(void)
 
        pr_info("Testing concurrent rhashtable access from %d threads\n",
                tcount);
-       sema_init(&prestart_sem, 1 - tcount);
+       atomic_set(&startup_count, tcount);
        tdata = vzalloc(array_size(tcount, sizeof(struct thread_data)));
        if (!tdata)
                return -ENOMEM;
@@ -781,15 +784,18 @@ static int __init test_rht_init(void)
                tdata[i].objs = objs + i * entries;
                tdata[i].task = kthread_run(threadfunc, &tdata[i],
                                            "rhashtable_thrad[%d]", i);
-               if (IS_ERR(tdata[i].task))
+               if (IS_ERR(tdata[i].task)) {
                        pr_err(" kthread_run failed for thread %d\n", i);
-               else
+                       atomic_dec(&startup_count);
+               } else {
                        started_threads++;
+               }
        }
-       if (down_interruptible(&prestart_sem))
-               pr_err("  down interruptible failed\n");
-       for (i = 0; i < tcount; i++)
-               up(&startup_sem);
+       if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == 0))
+               pr_err("  wait_event interruptible failed\n");
+       /* count is 0 now, set it to -1 and wake up all threads together */
+       atomic_dec(&startup_count);
+       wake_up_all(&startup_wait);
        for (i = 0; i < tcount; i++) {
                if (IS_ERR(tdata[i].task))
                        continue;
index aa47754..4676c0a 100644 (file)
@@ -28,23 +28,28 @@ void xa_dump(const struct xarray *xa) { }
 } while (0)
 #endif
 
+static void *xa_mk_index(unsigned long index)
+{
+       return xa_mk_value(index & LONG_MAX);
+}
+
 static void *xa_store_index(struct xarray *xa, unsigned long index, gfp_t gfp)
 {
-       return xa_store(xa, index, xa_mk_value(index & LONG_MAX), gfp);
+       return xa_store(xa, index, xa_mk_index(index), gfp);
 }
 
 static void xa_alloc_index(struct xarray *xa, unsigned long index, gfp_t gfp)
 {
        u32 id = 0;
 
-       XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_value(index & LONG_MAX),
+       XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(index),
                                gfp) != 0);
        XA_BUG_ON(xa, id != index);
 }
 
 static void xa_erase_index(struct xarray *xa, unsigned long index)
 {
-       XA_BUG_ON(xa, xa_erase(xa, index) != xa_mk_value(index & LONG_MAX));
+       XA_BUG_ON(xa, xa_erase(xa, index) != xa_mk_index(index));
        XA_BUG_ON(xa, xa_load(xa, index) != NULL);
 }
 
@@ -118,7 +123,7 @@ static noinline void check_xas_retry(struct xarray *xa)
 
        xas_set(&xas, 0);
        xas_for_each(&xas, entry, ULONG_MAX) {
-               xas_store(&xas, xa_mk_value(xas.xa_index));
+               xas_store(&xas, xa_mk_index(xas.xa_index));
        }
        xas_unlock(&xas);
 
@@ -196,7 +201,7 @@ static noinline void check_xa_mark_1(struct xarray *xa, unsigned long index)
                XA_BUG_ON(xa, xa_store_index(xa, index + 2, GFP_KERNEL));
                xa_set_mark(xa, index + 2, XA_MARK_1);
                XA_BUG_ON(xa, xa_store_index(xa, next, GFP_KERNEL));
-               xa_store_order(xa, index, order, xa_mk_value(index),
+               xa_store_order(xa, index, order, xa_mk_index(index),
                                GFP_KERNEL);
                for (i = base; i < next; i++) {
                        XA_STATE(xas, xa, i);
@@ -208,15 +213,19 @@ static noinline void check_xa_mark_1(struct xarray *xa, unsigned long index)
                        XA_BUG_ON(xa, xa_get_mark(xa, i, XA_MARK_2));
 
                        /* We should see two elements in the array */
+                       rcu_read_lock();
                        xas_for_each(&xas, entry, ULONG_MAX)
                                seen++;
+                       rcu_read_unlock();
                        XA_BUG_ON(xa, seen != 2);
 
                        /* One of which is marked */
                        xas_set(&xas, 0);
                        seen = 0;
+                       rcu_read_lock();
                        xas_for_each_marked(&xas, entry, ULONG_MAX, XA_MARK_0)
                                seen++;
+                       rcu_read_unlock();
                        XA_BUG_ON(xa, seen != 1);
                }
                XA_BUG_ON(xa, xa_get_mark(xa, next, XA_MARK_0));
@@ -373,6 +382,12 @@ static noinline void check_reserve(struct xarray *xa)
        xa_erase_index(xa, 12345678);
        XA_BUG_ON(xa, !xa_empty(xa));
 
+       /* And so does xa_insert */
+       xa_reserve(xa, 12345678, GFP_KERNEL);
+       XA_BUG_ON(xa, xa_insert(xa, 12345678, xa_mk_value(12345678), 0) != 0);
+       xa_erase_index(xa, 12345678);
+       XA_BUG_ON(xa, !xa_empty(xa));
+
        /* Can iterate through a reserved entry */
        xa_store_index(xa, 5, GFP_KERNEL);
        xa_reserve(xa, 6, GFP_KERNEL);
@@ -395,7 +410,7 @@ static noinline void check_xas_erase(struct xarray *xa)
                        xas_set(&xas, j);
                        do {
                                xas_lock(&xas);
-                               xas_store(&xas, xa_mk_value(j));
+                               xas_store(&xas, xa_mk_index(j));
                                xas_unlock(&xas);
                        } while (xas_nomem(&xas, GFP_KERNEL));
                }
@@ -413,7 +428,7 @@ static noinline void check_xas_erase(struct xarray *xa)
                xas_set(&xas, 0);
                j = i;
                xas_for_each(&xas, entry, ULONG_MAX) {
-                       XA_BUG_ON(xa, entry != xa_mk_value(j));
+                       XA_BUG_ON(xa, entry != xa_mk_index(j));
                        xas_store(&xas, NULL);
                        j++;
                }
@@ -430,15 +445,17 @@ static noinline void check_multi_store_1(struct xarray *xa, unsigned long index,
        unsigned long min = index & ~((1UL << order) - 1);
        unsigned long max = min + (1UL << order);
 
-       xa_store_order(xa, index, order, xa_mk_value(index), GFP_KERNEL);
-       XA_BUG_ON(xa, xa_load(xa, min) != xa_mk_value(index));
-       XA_BUG_ON(xa, xa_load(xa, max - 1) != xa_mk_value(index));
+       xa_store_order(xa, index, order, xa_mk_index(index), GFP_KERNEL);
+       XA_BUG_ON(xa, xa_load(xa, min) != xa_mk_index(index));
+       XA_BUG_ON(xa, xa_load(xa, max - 1) != xa_mk_index(index));
        XA_BUG_ON(xa, xa_load(xa, max) != NULL);
        XA_BUG_ON(xa, xa_load(xa, min - 1) != NULL);
 
-       XA_BUG_ON(xa, xas_store(&xas, xa_mk_value(min)) != xa_mk_value(index));
-       XA_BUG_ON(xa, xa_load(xa, min) != xa_mk_value(min));
-       XA_BUG_ON(xa, xa_load(xa, max - 1) != xa_mk_value(min));
+       xas_lock(&xas);
+       XA_BUG_ON(xa, xas_store(&xas, xa_mk_index(min)) != xa_mk_index(index));
+       xas_unlock(&xas);
+       XA_BUG_ON(xa, xa_load(xa, min) != xa_mk_index(min));
+       XA_BUG_ON(xa, xa_load(xa, max - 1) != xa_mk_index(min));
        XA_BUG_ON(xa, xa_load(xa, max) != NULL);
        XA_BUG_ON(xa, xa_load(xa, min - 1) != NULL);
 
@@ -452,11 +469,39 @@ static noinline void check_multi_store_2(struct xarray *xa, unsigned long index,
        XA_STATE(xas, xa, index);
        xa_store_order(xa, index, order, xa_mk_value(0), GFP_KERNEL);
 
+       xas_lock(&xas);
        XA_BUG_ON(xa, xas_store(&xas, xa_mk_value(1)) != xa_mk_value(0));
        XA_BUG_ON(xa, xas.xa_index != index);
        XA_BUG_ON(xa, xas_store(&xas, NULL) != xa_mk_value(1));
+       xas_unlock(&xas);
        XA_BUG_ON(xa, !xa_empty(xa));
 }
+
+static noinline void check_multi_store_3(struct xarray *xa, unsigned long index,
+               unsigned int order)
+{
+       XA_STATE(xas, xa, 0);
+       void *entry;
+       int n = 0;
+
+       xa_store_order(xa, index, order, xa_mk_index(index), GFP_KERNEL);
+
+       xas_lock(&xas);
+       xas_for_each(&xas, entry, ULONG_MAX) {
+               XA_BUG_ON(xa, entry != xa_mk_index(index));
+               n++;
+       }
+       XA_BUG_ON(xa, n != 1);
+       xas_set(&xas, index + 1);
+       xas_for_each(&xas, entry, ULONG_MAX) {
+               XA_BUG_ON(xa, entry != xa_mk_index(index));
+               n++;
+       }
+       XA_BUG_ON(xa, n != 2);
+       xas_unlock(&xas);
+
+       xa_destroy(xa);
+}
 #endif
 
 static noinline void check_multi_store(struct xarray *xa)
@@ -498,7 +543,7 @@ static noinline void check_multi_store(struct xarray *xa)
        rcu_read_unlock();
 
        /* We can erase multiple values with a single store */
-       xa_store_order(xa, 0, 63, NULL, GFP_KERNEL);
+       xa_store_order(xa, 0, BITS_PER_LONG - 1, NULL, GFP_KERNEL);
        XA_BUG_ON(xa, !xa_empty(xa));
 
        /* Even when the first slot is empty but the others aren't */
@@ -509,15 +554,15 @@ static noinline void check_multi_store(struct xarray *xa)
 
        for (i = 0; i < max_order; i++) {
                for (j = 0; j < max_order; j++) {
-                       xa_store_order(xa, 0, i, xa_mk_value(i), GFP_KERNEL);
-                       xa_store_order(xa, 0, j, xa_mk_value(j), GFP_KERNEL);
+                       xa_store_order(xa, 0, i, xa_mk_index(i), GFP_KERNEL);
+                       xa_store_order(xa, 0, j, xa_mk_index(j), GFP_KERNEL);
 
                        for (k = 0; k < max_order; k++) {
                                void *entry = xa_load(xa, (1UL << k) - 1);
                                if ((i < k) && (j < k))
                                        XA_BUG_ON(xa, entry != NULL);
                                else
-                                       XA_BUG_ON(xa, entry != xa_mk_value(j));
+                                       XA_BUG_ON(xa, entry != xa_mk_index(j));
                        }
 
                        xa_erase(xa, 0);
@@ -531,6 +576,11 @@ static noinline void check_multi_store(struct xarray *xa)
                check_multi_store_1(xa, (1UL << i) + 1, i);
        }
        check_multi_store_2(xa, 4095, 9);
+
+       for (i = 1; i < 20; i++) {
+               check_multi_store_3(xa, 0, i);
+               check_multi_store_3(xa, 1UL << i, i);
+       }
 #endif
 }
 
@@ -573,16 +623,25 @@ static noinline void check_xa_alloc(void)
        xa_destroy(&xa0);
 
        id = 0xfffffffeU;
-       XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_value(0),
+       XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_index(id),
                                GFP_KERNEL) != 0);
        XA_BUG_ON(&xa0, id != 0xfffffffeU);
-       XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_value(0),
+       XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_index(id),
                                GFP_KERNEL) != 0);
        XA_BUG_ON(&xa0, id != 0xffffffffU);
-       XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_value(0),
+       XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_index(id),
                                GFP_KERNEL) != -ENOSPC);
        XA_BUG_ON(&xa0, id != 0xffffffffU);
        xa_destroy(&xa0);
+
+       id = 10;
+       XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, 5, xa_mk_index(id),
+                               GFP_KERNEL) != -ENOSPC);
+       XA_BUG_ON(&xa0, xa_store_index(&xa0, 3, GFP_KERNEL) != 0);
+       XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, 5, xa_mk_index(id),
+                               GFP_KERNEL) != -ENOSPC);
+       xa_erase_index(&xa0, 3);
+       XA_BUG_ON(&xa0, !xa_empty(&xa0));
 }
 
 static noinline void __check_store_iter(struct xarray *xa, unsigned long start,
@@ -596,11 +655,11 @@ retry:
        xas_lock(&xas);
        xas_for_each_conflict(&xas, entry) {
                XA_BUG_ON(xa, !xa_is_value(entry));
-               XA_BUG_ON(xa, entry < xa_mk_value(start));
-               XA_BUG_ON(xa, entry > xa_mk_value(start + (1UL << order) - 1));
+               XA_BUG_ON(xa, entry < xa_mk_index(start));
+               XA_BUG_ON(xa, entry > xa_mk_index(start + (1UL << order) - 1));
                count++;
        }
-       xas_store(&xas, xa_mk_value(start));
+       xas_store(&xas, xa_mk_index(start));
        xas_unlock(&xas);
        if (xas_nomem(&xas, GFP_KERNEL)) {
                count = 0;
@@ -608,9 +667,9 @@ retry:
        }
        XA_BUG_ON(xa, xas_error(&xas));
        XA_BUG_ON(xa, count != present);
-       XA_BUG_ON(xa, xa_load(xa, start) != xa_mk_value(start));
+       XA_BUG_ON(xa, xa_load(xa, start) != xa_mk_index(start));
        XA_BUG_ON(xa, xa_load(xa, start + (1UL << order) - 1) !=
-                       xa_mk_value(start));
+                       xa_mk_index(start));
        xa_erase_index(xa, start);
 }
 
@@ -689,7 +748,7 @@ static noinline void check_multi_find_2(struct xarray *xa)
                for (j = 0; j < index; j++) {
                        XA_STATE(xas, xa, j + index);
                        xa_store_index(xa, index - 1, GFP_KERNEL);
-                       xa_store_order(xa, index, i, xa_mk_value(index),
+                       xa_store_order(xa, index, i, xa_mk_index(index),
                                        GFP_KERNEL);
                        rcu_read_lock();
                        xas_for_each(&xas, entry, ULONG_MAX) {
@@ -702,7 +761,7 @@ static noinline void check_multi_find_2(struct xarray *xa)
        }
 }
 
-static noinline void check_find(struct xarray *xa)
+static noinline void check_find_1(struct xarray *xa)
 {
        unsigned long i, j, k;
 
@@ -748,6 +807,58 @@ static noinline void check_find(struct xarray *xa)
                XA_BUG_ON(xa, xa_get_mark(xa, i, XA_MARK_0));
        }
        XA_BUG_ON(xa, !xa_empty(xa));
+}
+
+static noinline void check_find_2(struct xarray *xa)
+{
+       void *entry;
+       unsigned long i, j, index = 0;
+
+       xa_for_each(xa, entry, index, ULONG_MAX, XA_PRESENT) {
+               XA_BUG_ON(xa, true);
+       }
+
+       for (i = 0; i < 1024; i++) {
+               xa_store_index(xa, index, GFP_KERNEL);
+               j = 0;
+               index = 0;
+               xa_for_each(xa, entry, index, ULONG_MAX, XA_PRESENT) {
+                       XA_BUG_ON(xa, xa_mk_index(index) != entry);
+                       XA_BUG_ON(xa, index != j++);
+               }
+       }
+
+       xa_destroy(xa);
+}
+
+static noinline void check_find_3(struct xarray *xa)
+{
+       XA_STATE(xas, xa, 0);
+       unsigned long i, j, k;
+       void *entry;
+
+       for (i = 0; i < 100; i++) {
+               for (j = 0; j < 100; j++) {
+                       for (k = 0; k < 100; k++) {
+                               xas_set(&xas, j);
+                               xas_for_each_marked(&xas, entry, k, XA_MARK_0)
+                                       ;
+                               if (j > k)
+                                       XA_BUG_ON(xa,
+                                               xas.xa_node != XAS_RESTART);
+                       }
+               }
+               xa_store_index(xa, i, GFP_KERNEL);
+               xa_set_mark(xa, i, XA_MARK_0);
+       }
+       xa_destroy(xa);
+}
+
+static noinline void check_find(struct xarray *xa)
+{
+       check_find_1(xa);
+       check_find_2(xa);
+       check_find_3(xa);
        check_multi_find(xa);
        check_multi_find_2(xa);
 }
@@ -787,11 +898,11 @@ static noinline void check_find_entry(struct xarray *xa)
                        for (index = 0; index < (1UL << (order + 5));
                             index += (1UL << order)) {
                                xa_store_order(xa, index, order,
-                                               xa_mk_value(index), GFP_KERNEL);
+                                               xa_mk_index(index), GFP_KERNEL);
                                XA_BUG_ON(xa, xa_load(xa, index) !=
-                                               xa_mk_value(index));
+                                               xa_mk_index(index));
                                XA_BUG_ON(xa, xa_find_entry(xa,
-                                               xa_mk_value(index)) != index);
+                                               xa_mk_index(index)) != index);
                        }
                        XA_BUG_ON(xa, xa_find_entry(xa, xa) != -1);
                        xa_destroy(xa);
@@ -802,7 +913,7 @@ static noinline void check_find_entry(struct xarray *xa)
        XA_BUG_ON(xa, xa_find_entry(xa, xa) != -1);
        xa_store_index(xa, ULONG_MAX, GFP_KERNEL);
        XA_BUG_ON(xa, xa_find_entry(xa, xa) != -1);
-       XA_BUG_ON(xa, xa_find_entry(xa, xa_mk_value(LONG_MAX)) != -1);
+       XA_BUG_ON(xa, xa_find_entry(xa, xa_mk_index(ULONG_MAX)) != -1);
        xa_erase_index(xa, ULONG_MAX);
        XA_BUG_ON(xa, !xa_empty(xa));
 }
@@ -822,7 +933,7 @@ static noinline void check_move_small(struct xarray *xa, unsigned long idx)
                        XA_BUG_ON(xa, xas.xa_node == XAS_RESTART);
                XA_BUG_ON(xa, xas.xa_index != i);
                if (i == 0 || i == idx)
-                       XA_BUG_ON(xa, entry != xa_mk_value(i));
+                       XA_BUG_ON(xa, entry != xa_mk_index(i));
                else
                        XA_BUG_ON(xa, entry != NULL);
        }
@@ -836,7 +947,7 @@ static noinline void check_move_small(struct xarray *xa, unsigned long idx)
                        XA_BUG_ON(xa, xas.xa_node == XAS_RESTART);
                XA_BUG_ON(xa, xas.xa_index != i);
                if (i == 0 || i == idx)
-                       XA_BUG_ON(xa, entry != xa_mk_value(i));
+                       XA_BUG_ON(xa, entry != xa_mk_index(i));
                else
                        XA_BUG_ON(xa, entry != NULL);
        } while (i > 0);
@@ -867,7 +978,7 @@ static noinline void check_move(struct xarray *xa)
        do {
                void *entry = xas_prev(&xas);
                i--;
-               XA_BUG_ON(xa, entry != xa_mk_value(i));
+               XA_BUG_ON(xa, entry != xa_mk_index(i));
                XA_BUG_ON(xa, i != xas.xa_index);
        } while (i != 0);
 
@@ -876,7 +987,7 @@ static noinline void check_move(struct xarray *xa)
 
        do {
                void *entry = xas_next(&xas);
-               XA_BUG_ON(xa, entry != xa_mk_value(i));
+               XA_BUG_ON(xa, entry != xa_mk_index(i));
                XA_BUG_ON(xa, i != xas.xa_index);
                i++;
        } while (i < (1 << 16));
@@ -892,7 +1003,7 @@ static noinline void check_move(struct xarray *xa)
                void *entry = xas_prev(&xas);
                i--;
                if ((i < (1 << 8)) || (i >= (1 << 15)))
-                       XA_BUG_ON(xa, entry != xa_mk_value(i));
+                       XA_BUG_ON(xa, entry != xa_mk_index(i));
                else
                        XA_BUG_ON(xa, entry != NULL);
                XA_BUG_ON(xa, i != xas.xa_index);
@@ -904,7 +1015,7 @@ static noinline void check_move(struct xarray *xa)
        do {
                void *entry = xas_next(&xas);
                if ((i < (1 << 8)) || (i >= (1 << 15)))
-                       XA_BUG_ON(xa, entry != xa_mk_value(i));
+                       XA_BUG_ON(xa, entry != xa_mk_index(i));
                else
                        XA_BUG_ON(xa, entry != NULL);
                XA_BUG_ON(xa, i != xas.xa_index);
@@ -934,7 +1045,7 @@ static noinline void xa_store_many_order(struct xarray *xa,
                if (xas_error(&xas))
                        goto unlock;
                for (i = 0; i < (1U << order); i++) {
-                       XA_BUG_ON(xa, xas_store(&xas, xa_mk_value(index + i)));
+                       XA_BUG_ON(xa, xas_store(&xas, xa_mk_index(index + i)));
                        xas_next(&xas);
                }
 unlock:
@@ -989,9 +1100,9 @@ static noinline void check_create_range_4(struct xarray *xa,
                if (xas_error(&xas))
                        goto unlock;
                for (i = 0; i < (1UL << order); i++) {
-                       void *old = xas_store(&xas, xa_mk_value(base + i));
+                       void *old = xas_store(&xas, xa_mk_index(base + i));
                        if (xas.xa_index == index)
-                               XA_BUG_ON(xa, old != xa_mk_value(base + i));
+                               XA_BUG_ON(xa, old != xa_mk_index(base + i));
                        else
                                XA_BUG_ON(xa, old != NULL);
                        xas_next(&xas);
@@ -1043,10 +1154,10 @@ static noinline void __check_store_range(struct xarray *xa, unsigned long first,
                unsigned long last)
 {
 #ifdef CONFIG_XARRAY_MULTI
-       xa_store_range(xa, first, last, xa_mk_value(first), GFP_KERNEL);
+       xa_store_range(xa, first, last, xa_mk_index(first), GFP_KERNEL);
 
-       XA_BUG_ON(xa, xa_load(xa, first) != xa_mk_value(first));
-       XA_BUG_ON(xa, xa_load(xa, last) != xa_mk_value(first));
+       XA_BUG_ON(xa, xa_load(xa, first) != xa_mk_index(first));
+       XA_BUG_ON(xa, xa_load(xa, last) != xa_mk_index(first));
        XA_BUG_ON(xa, xa_load(xa, first - 1) != NULL);
        XA_BUG_ON(xa, xa_load(xa, last + 1) != NULL);
 
@@ -1067,7 +1178,7 @@ static noinline void check_store_range(struct xarray *xa)
                        __check_store_range(xa, 4095 + i, 4095 + j);
                        __check_store_range(xa, 4096 + i, 4096 + j);
                        __check_store_range(xa, 123456 + i, 123456 + j);
-                       __check_store_range(xa, UINT_MAX + i, UINT_MAX + j);
+                       __check_store_range(xa, (1 << 24) + i, (1 << 24) + j);
                }
        }
 }
@@ -1146,12 +1257,14 @@ static noinline void check_account(struct xarray *xa)
                XA_STATE(xas, xa, 1 << order);
 
                xa_store_order(xa, 0, order, xa, GFP_KERNEL);
+               rcu_read_lock();
                xas_load(&xas);
                XA_BUG_ON(xa, xas.xa_node->count == 0);
                XA_BUG_ON(xa, xas.xa_node->count > (1 << order));
                XA_BUG_ON(xa, xas.xa_node->nr_values != 0);
+               rcu_read_unlock();
 
-               xa_store_order(xa, 1 << order, order, xa_mk_value(1 << order),
+               xa_store_order(xa, 1 << order, order, xa_mk_index(1UL << order),
                                GFP_KERNEL);
                XA_BUG_ON(xa, xas.xa_node->count != xas.xa_node->nr_values * 2);
 
index 59fee96..e4162f5 100644 (file)
@@ -427,8 +427,7 @@ void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data,
 EXPORT_SYMBOL(__ubsan_handle_shift_out_of_bounds);
 
 
-void __noreturn
-__ubsan_handle_builtin_unreachable(struct unreachable_data *data)
+void __ubsan_handle_builtin_unreachable(struct unreachable_data *data)
 {
        unsigned long flags;
 
index 8b176f0..5f3f931 100644 (file)
@@ -610,8 +610,8 @@ static int xas_expand(struct xa_state *xas, void *head)
  * (see the xa_cmpxchg() implementation for an example).
  *
  * Return: If the slot already existed, returns the contents of this slot.
- * If the slot was newly created, returns NULL.  If it failed to create the
- * slot, returns NULL and indicates the error in @xas.
+ * If the slot was newly created, returns %NULL.  If it failed to create the
+ * slot, returns %NULL and indicates the error in @xas.
  */
 static void *xas_create(struct xa_state *xas)
 {
@@ -1131,7 +1131,7 @@ void *xas_find_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark)
                entry = xa_head(xas->xa);
                xas->xa_node = NULL;
                if (xas->xa_index > max_index(entry))
-                       goto bounds;
+                       goto out;
                if (!xa_is_node(entry)) {
                        if (xa_marked(xas->xa, mark))
                                return entry;
@@ -1180,11 +1180,9 @@ void *xas_find_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark)
        }
 
 out:
-       if (!max)
+       if (xas->xa_index > max)
                goto max;
-bounds:
-       xas->xa_node = XAS_BOUNDS;
-       return NULL;
+       return set_bounds(xas);
 max:
        xas->xa_node = XAS_RESTART;
        return NULL;
@@ -1334,44 +1332,31 @@ void *__xa_erase(struct xarray *xa, unsigned long index)
        XA_STATE(xas, xa, index);
        return xas_result(&xas, xas_store(&xas, NULL));
 }
-EXPORT_SYMBOL_GPL(__xa_erase);
+EXPORT_SYMBOL(__xa_erase);
 
 /**
- * xa_store() - Store this entry in the XArray.
+ * xa_erase() - Erase this entry from the XArray.
  * @xa: XArray.
- * @index: Index into array.
- * @entry: New entry.
- * @gfp: Memory allocation flags.
+ * @index: Index of entry.
  *
- * After this function returns, loads from this index will return @entry.
- * Storing into an existing multislot entry updates the entry of every index.
- * The marks associated with @index are unaffected unless @entry is %NULL.
+ * This function is the equivalent of calling xa_store() with %NULL as
+ * the third argument.  The XArray does not need to allocate memory, so
+ * the user does not need to provide GFP flags.
  *
- * Context: Process context.  Takes and releases the xa_lock.  May sleep
- * if the @gfp flags permit.
- * Return: The old entry at this index on success, xa_err(-EINVAL) if @entry
- * cannot be stored in an XArray, or xa_err(-ENOMEM) if memory allocation
- * failed.
+ * Context: Any context.  Takes and releases the xa_lock.
+ * Return: The entry which used to be at this index.
  */
-void *xa_store(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
+void *xa_erase(struct xarray *xa, unsigned long index)
 {
-       XA_STATE(xas, xa, index);
-       void *curr;
-
-       if (WARN_ON_ONCE(xa_is_internal(entry)))
-               return XA_ERROR(-EINVAL);
+       void *entry;
 
-       do {
-               xas_lock(&xas);
-               curr = xas_store(&xas, entry);
-               if (xa_track_free(xa) && entry)
-                       xas_clear_mark(&xas, XA_FREE_MARK);
-               xas_unlock(&xas);
-       } while (xas_nomem(&xas, gfp));
+       xa_lock(xa);
+       entry = __xa_erase(xa, index);
+       xa_unlock(xa);
 
-       return xas_result(&xas, curr);
+       return entry;
 }
-EXPORT_SYMBOL(xa_store);
+EXPORT_SYMBOL(xa_erase);
 
 /**
  * __xa_store() - Store this entry in the XArray.
@@ -1395,10 +1380,12 @@ void *__xa_store(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
 
        if (WARN_ON_ONCE(xa_is_internal(entry)))
                return XA_ERROR(-EINVAL);
+       if (xa_track_free(xa) && !entry)
+               entry = XA_ZERO_ENTRY;
 
        do {
                curr = xas_store(&xas, entry);
-               if (xa_track_free(xa) && entry)
+               if (xa_track_free(xa))
                        xas_clear_mark(&xas, XA_FREE_MARK);
        } while (__xas_nomem(&xas, gfp));
 
@@ -1407,45 +1394,33 @@ void *__xa_store(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
 EXPORT_SYMBOL(__xa_store);
 
 /**
- * xa_cmpxchg() - Conditionally replace an entry in the XArray.
+ * xa_store() - Store this entry in the XArray.
  * @xa: XArray.
  * @index: Index into array.
- * @old: Old value to test against.
- * @entry: New value to place in array.
+ * @entry: New entry.
  * @gfp: Memory allocation flags.
  *
- * If the entry at @index is the same as @old, replace it with @entry.
- * If the return value is equal to @old, then the exchange was successful.
+ * After this function returns, loads from this index will return @entry.
+ * Storing into an existing multislot entry updates the entry of every index.
+ * The marks associated with @index are unaffected unless @entry is %NULL.
  *
- * Context: Process context.  Takes and releases the xa_lock.  May sleep
- * if the @gfp flags permit.
- * Return: The old value at this index or xa_err() if an error happened.
+ * Context: Any context.  Takes and releases the xa_lock.
+ * May sleep if the @gfp flags permit.
+ * Return: The old entry at this index on success, xa_err(-EINVAL) if @entry
+ * cannot be stored in an XArray, or xa_err(-ENOMEM) if memory allocation
+ * failed.
  */
-void *xa_cmpxchg(struct xarray *xa, unsigned long index,
-                       void *old, void *entry, gfp_t gfp)
+void *xa_store(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
 {
-       XA_STATE(xas, xa, index);
        void *curr;
 
-       if (WARN_ON_ONCE(xa_is_internal(entry)))
-               return XA_ERROR(-EINVAL);
-
-       do {
-               xas_lock(&xas);
-               curr = xas_load(&xas);
-               if (curr == XA_ZERO_ENTRY)
-                       curr = NULL;
-               if (curr == old) {
-                       xas_store(&xas, entry);
-                       if (xa_track_free(xa) && entry)
-                               xas_clear_mark(&xas, XA_FREE_MARK);
-               }
-               xas_unlock(&xas);
-       } while (xas_nomem(&xas, gfp));
+       xa_lock(xa);
+       curr = __xa_store(xa, index, entry, gfp);
+       xa_unlock(xa);
 
-       return xas_result(&xas, curr);
+       return curr;
 }
-EXPORT_SYMBOL(xa_cmpxchg);
+EXPORT_SYMBOL(xa_store);
 
 /**
  * __xa_cmpxchg() - Store this entry in the XArray.
@@ -1471,6 +1446,8 @@ void *__xa_cmpxchg(struct xarray *xa, unsigned long index,
 
        if (WARN_ON_ONCE(xa_is_internal(entry)))
                return XA_ERROR(-EINVAL);
+       if (xa_track_free(xa) && !entry)
+               entry = XA_ZERO_ENTRY;
 
        do {
                curr = xas_load(&xas);
@@ -1478,7 +1455,7 @@ void *__xa_cmpxchg(struct xarray *xa, unsigned long index,
                        curr = NULL;
                if (curr == old) {
                        xas_store(&xas, entry);
-                       if (xa_track_free(xa) && entry)
+                       if (xa_track_free(xa))
                                xas_clear_mark(&xas, XA_FREE_MARK);
                }
        } while (__xas_nomem(&xas, gfp));
@@ -1488,7 +1465,7 @@ void *__xa_cmpxchg(struct xarray *xa, unsigned long index,
 EXPORT_SYMBOL(__xa_cmpxchg);
 
 /**
- * xa_reserve() - Reserve this index in the XArray.
+ * __xa_reserve() - Reserve this index in the XArray.
  * @xa: XArray.
  * @index: Index into array.
  * @gfp: Memory allocation flags.
@@ -1496,33 +1473,32 @@ EXPORT_SYMBOL(__xa_cmpxchg);
  * Ensures there is somewhere to store an entry at @index in the array.
  * If there is already something stored at @index, this function does
  * nothing.  If there was nothing there, the entry is marked as reserved.
- * Loads from @index will continue to see a %NULL pointer until a
- * subsequent store to @index.
+ * Loading from a reserved entry returns a %NULL pointer.
  *
  * If you do not use the entry that you have reserved, call xa_release()
  * or xa_erase() to free any unnecessary memory.
  *
- * Context: Process context.  Takes and releases the xa_lock, IRQ or BH safe
- * if specified in XArray flags.  May sleep if the @gfp flags permit.
+ * Context: Any context.  Expects the xa_lock to be held on entry.  May
+ * release the lock, sleep and reacquire the lock if the @gfp flags permit.
  * Return: 0 if the reservation succeeded or -ENOMEM if it failed.
  */
-int xa_reserve(struct xarray *xa, unsigned long index, gfp_t gfp)
+int __xa_reserve(struct xarray *xa, unsigned long index, gfp_t gfp)
 {
        XA_STATE(xas, xa, index);
-       unsigned int lock_type = xa_lock_type(xa);
        void *curr;
 
        do {
-               xas_lock_type(&xas, lock_type);
                curr = xas_load(&xas);
-               if (!curr)
+               if (!curr) {
                        xas_store(&xas, XA_ZERO_ENTRY);
-               xas_unlock_type(&xas, lock_type);
-       } while (xas_nomem(&xas, gfp));
+                       if (xa_track_free(xa))
+                               xas_clear_mark(&xas, XA_FREE_MARK);
+               }
+       } while (__xas_nomem(&xas, gfp));
 
        return xas_error(&xas);
 }
-EXPORT_SYMBOL(xa_reserve);
+EXPORT_SYMBOL(__xa_reserve);
 
 #ifdef CONFIG_XARRAY_MULTI
 static void xas_set_range(struct xa_state *xas, unsigned long first,
@@ -1587,8 +1563,9 @@ void *xa_store_range(struct xarray *xa, unsigned long first,
        do {
                xas_lock(&xas);
                if (entry) {
-                       unsigned int order = (last == ~0UL) ? 64 :
-                                               ilog2(last + 1);
+                       unsigned int order = BITS_PER_LONG;
+                       if (last + 1)
+                               order = __ffs(last + 1);
                        xas_set_order(&xas, last, order);
                        xas_create(&xas);
                        if (xas_error(&xas))
@@ -1662,7 +1639,7 @@ EXPORT_SYMBOL(__xa_alloc);
  * @index: Index of entry.
  * @mark: Mark number.
  *
- * Attempting to set a mark on a NULL entry does not succeed.
+ * Attempting to set a mark on a %NULL entry does not succeed.
  *
  * Context: Any context.  Expects xa_lock to be held on entry.
  */
@@ -1674,7 +1651,7 @@ void __xa_set_mark(struct xarray *xa, unsigned long index, xa_mark_t mark)
        if (entry)
                xas_set_mark(&xas, mark);
 }
-EXPORT_SYMBOL_GPL(__xa_set_mark);
+EXPORT_SYMBOL(__xa_set_mark);
 
 /**
  * __xa_clear_mark() - Clear this mark on this entry while locked.
@@ -1692,7 +1669,7 @@ void __xa_clear_mark(struct xarray *xa, unsigned long index, xa_mark_t mark)
        if (entry)
                xas_clear_mark(&xas, mark);
 }
-EXPORT_SYMBOL_GPL(__xa_clear_mark);
+EXPORT_SYMBOL(__xa_clear_mark);
 
 /**
  * xa_get_mark() - Inquire whether this mark is set on this entry.
@@ -1732,7 +1709,7 @@ EXPORT_SYMBOL(xa_get_mark);
  * @index: Index of entry.
  * @mark: Mark number.
  *
- * Attempting to set a mark on a NULL entry does not succeed.
+ * Attempting to set a mark on a %NULL entry does not succeed.
  *
  * Context: Process context.  Takes and releases the xa_lock.
  */
@@ -1829,6 +1806,8 @@ void *xa_find_after(struct xarray *xa, unsigned long *indexp,
                        entry = xas_find_marked(&xas, max, filter);
                else
                        entry = xas_find(&xas, max);
+               if (xas.xa_node == XAS_BOUNDS)
+                       break;
                if (xas.xa_shift) {
                        if (xas.xa_index & ((1UL << xas.xa_shift) - 1))
                                continue;
@@ -1899,7 +1878,7 @@ static unsigned int xas_extract_marked(struct xa_state *xas, void **dst,
  *
  * The @filter may be an XArray mark value, in which case entries which are
  * marked with that mark will be copied.  It may also be %XA_PRESENT, in
- * which case all entries which are not NULL will be copied.
+ * which case all entries which are not %NULL will be copied.
  *
  * The entries returned may not represent a snapshot of the XArray at a
  * moment in time.  For example, if another thread stores to index 5, then
index f76e77a..8cb68a5 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -385,11 +385,17 @@ static struct page *follow_p4d_mask(struct vm_area_struct *vma,
  * @vma: vm_area_struct mapping @address
  * @address: virtual address to look up
  * @flags: flags modifying lookup behaviour
- * @page_mask: on output, *page_mask is set according to the size of the page
+ * @ctx: contains dev_pagemap for %ZONE_DEVICE memory pinning and a
+ *       pointer to output page_mask
  *
  * @flags can have FOLL_ flags set, defined in <linux/mm.h>
  *
- * Returns the mapped (struct page *), %NULL if no mapping exists, or
+ * When getting pages from ZONE_DEVICE memory, the @ctx->pgmap caches
+ * the device's dev_pagemap metadata to avoid repeating expensive lookups.
+ *
+ * On output, the @ctx->page_mask is set according to the size of the page.
+ *
+ * Return: the mapped (struct page *), %NULL if no mapping exists, or
  * an error pointer if there is a mapping to something not represented
  * by a page descriptor (see also vm_normal_page()).
  */
@@ -696,12 +702,11 @@ static long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                if (!vma || start >= vma->vm_end) {
                        vma = find_extend_vma(mm, start);
                        if (!vma && in_gate_area(mm, start)) {
-                               int ret;
                                ret = get_gate_page(mm, start & PAGE_MASK,
                                                gup_flags, &vma,
                                                pages ? &pages[i] : NULL);
                                if (ret)
-                                       return i ? : ret;
+                                       goto out;
                                ctx.page_mask = 0;
                                goto next_page;
                        }
index 55478ab..5da55b3 100644 (file)
@@ -629,40 +629,30 @@ release:
  *         available
  * never: never stall for any thp allocation
  */
-static inline gfp_t alloc_hugepage_direct_gfpmask(struct vm_area_struct *vma, unsigned long addr)
+static inline gfp_t alloc_hugepage_direct_gfpmask(struct vm_area_struct *vma)
 {
        const bool vma_madvised = !!(vma->vm_flags & VM_HUGEPAGE);
-       gfp_t this_node = 0;
-
-#ifdef CONFIG_NUMA
-       struct mempolicy *pol;
-       /*
-        * __GFP_THISNODE is used only when __GFP_DIRECT_RECLAIM is not
-        * specified, to express a general desire to stay on the current
-        * node for optimistic allocation attempts. If the defrag mode
-        * and/or madvise hint requires the direct reclaim then we prefer
-        * to fallback to other node rather than node reclaim because that
-        * can lead to excessive reclaim even though there is free memory
-        * on other nodes. We expect that NUMA preferences are specified
-        * by memory policies.
-        */
-       pol = get_vma_policy(vma, addr);
-       if (pol->mode != MPOL_BIND)
-               this_node = __GFP_THISNODE;
-       mpol_cond_put(pol);
-#endif
 
+       /* Always do synchronous compaction */
        if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags))
                return GFP_TRANSHUGE | (vma_madvised ? 0 : __GFP_NORETRY);
+
+       /* Kick kcompactd and fail quickly */
        if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags))
-               return GFP_TRANSHUGE_LIGHT | __GFP_KSWAPD_RECLAIM | this_node;
+               return GFP_TRANSHUGE_LIGHT | __GFP_KSWAPD_RECLAIM;
+
+       /* Synchronous compaction if madvised, otherwise kick kcompactd */
        if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags))
-               return GFP_TRANSHUGE_LIGHT | (vma_madvised ? __GFP_DIRECT_RECLAIM :
-                                                            __GFP_KSWAPD_RECLAIM | this_node);
+               return GFP_TRANSHUGE_LIGHT |
+                       (vma_madvised ? __GFP_DIRECT_RECLAIM :
+                                       __GFP_KSWAPD_RECLAIM);
+
+       /* Only do synchronous compaction if madvised */
        if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags))
-               return GFP_TRANSHUGE_LIGHT | (vma_madvised ? __GFP_DIRECT_RECLAIM :
-                                                            this_node);
-       return GFP_TRANSHUGE_LIGHT | this_node;
+               return GFP_TRANSHUGE_LIGHT |
+                      (vma_madvised ? __GFP_DIRECT_RECLAIM : 0);
+
+       return GFP_TRANSHUGE_LIGHT;
 }
 
 /* Caller must hold page table lock. */
@@ -734,8 +724,8 @@ vm_fault_t do_huge_pmd_anonymous_page(struct vm_fault *vmf)
                        pte_free(vma->vm_mm, pgtable);
                return ret;
        }
-       gfp = alloc_hugepage_direct_gfpmask(vma, haddr);
-       page = alloc_pages_vma(gfp, HPAGE_PMD_ORDER, vma, haddr, numa_node_id());
+       gfp = alloc_hugepage_direct_gfpmask(vma);
+       page = alloc_hugepage_vma(gfp, vma, haddr, HPAGE_PMD_ORDER);
        if (unlikely(!page)) {
                count_vm_event(THP_FAULT_FALLBACK);
                return VM_FAULT_FALLBACK;
@@ -1305,9 +1295,8 @@ vm_fault_t do_huge_pmd_wp_page(struct vm_fault *vmf, pmd_t orig_pmd)
 alloc:
        if (transparent_hugepage_enabled(vma) &&
            !transparent_hugepage_debug_cow()) {
-               huge_gfp = alloc_hugepage_direct_gfpmask(vma, haddr);
-               new_page = alloc_pages_vma(huge_gfp, HPAGE_PMD_ORDER, vma,
-                               haddr, numa_node_id());
+               huge_gfp = alloc_hugepage_direct_gfpmask(vma);
+               new_page = alloc_hugepage_vma(huge_gfp, vma, haddr, HPAGE_PMD_ORDER);
        } else
                new_page = NULL;
 
@@ -2350,7 +2339,7 @@ void vma_adjust_trans_huge(struct vm_area_struct *vma,
        }
 }
 
-static void freeze_page(struct page *page)
+static void unmap_page(struct page *page)
 {
        enum ttu_flags ttu_flags = TTU_IGNORE_MLOCK | TTU_IGNORE_ACCESS |
                TTU_RMAP_LOCKED | TTU_SPLIT_HUGE_PMD;
@@ -2365,7 +2354,7 @@ static void freeze_page(struct page *page)
        VM_BUG_ON_PAGE(!unmap_success, page);
 }
 
-static void unfreeze_page(struct page *page)
+static void remap_page(struct page *page)
 {
        int i;
        if (PageTransHuge(page)) {
@@ -2402,6 +2391,12 @@ static void __split_huge_page_tail(struct page *head, int tail,
                         (1L << PG_unevictable) |
                         (1L << PG_dirty)));
 
+       /* ->mapping in first tail page is compound_mapcount */
+       VM_BUG_ON_PAGE(tail > 2 && page_tail->mapping != TAIL_MAPPING,
+                       page_tail);
+       page_tail->mapping = head->mapping;
+       page_tail->index = head->index + tail;
+
        /* Page flags must be visible before we make the page non-compound. */
        smp_wmb();
 
@@ -2422,12 +2417,6 @@ static void __split_huge_page_tail(struct page *head, int tail,
        if (page_is_idle(head))
                set_page_idle(page_tail);
 
-       /* ->mapping in first tail page is compound_mapcount */
-       VM_BUG_ON_PAGE(tail > 2 && page_tail->mapping != TAIL_MAPPING,
-                       page_tail);
-       page_tail->mapping = head->mapping;
-
-       page_tail->index = head->index + tail;
        page_cpupid_xchg_last(page_tail, page_cpupid_last(head));
 
        /*
@@ -2439,12 +2428,11 @@ static void __split_huge_page_tail(struct page *head, int tail,
 }
 
 static void __split_huge_page(struct page *page, struct list_head *list,
-               unsigned long flags)
+               pgoff_t end, unsigned long flags)
 {
        struct page *head = compound_head(page);
        struct zone *zone = page_zone(head);
        struct lruvec *lruvec;
-       pgoff_t end = -1;
        int i;
 
        lruvec = mem_cgroup_page_lruvec(head, zone->zone_pgdat);
@@ -2452,9 +2440,6 @@ static void __split_huge_page(struct page *page, struct list_head *list,
        /* complete memcg works before add pages to LRU */
        mem_cgroup_split_huge_fixup(head);
 
-       if (!PageAnon(page))
-               end = DIV_ROUND_UP(i_size_read(head->mapping->host), PAGE_SIZE);
-
        for (i = HPAGE_PMD_NR - 1; i >= 1; i--) {
                __split_huge_page_tail(head, i, lruvec, list);
                /* Some pages can be beyond i_size: drop them from page cache */
@@ -2483,7 +2468,7 @@ static void __split_huge_page(struct page *page, struct list_head *list,
 
        spin_unlock_irqrestore(zone_lru_lock(page_zone(head)), flags);
 
-       unfreeze_page(head);
+       remap_page(head);
 
        for (i = 0; i < HPAGE_PMD_NR; i++) {
                struct page *subpage = head + i;
@@ -2626,6 +2611,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)
        int count, mapcount, extra_pins, ret;
        bool mlocked;
        unsigned long flags;
+       pgoff_t end;
 
        VM_BUG_ON_PAGE(is_huge_zero_page(page), page);
        VM_BUG_ON_PAGE(!PageLocked(page), page);
@@ -2648,6 +2634,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)
                        ret = -EBUSY;
                        goto out;
                }
+               end = -1;
                mapping = NULL;
                anon_vma_lock_write(anon_vma);
        } else {
@@ -2661,10 +2648,19 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)
 
                anon_vma = NULL;
                i_mmap_lock_read(mapping);
+
+               /*
+                *__split_huge_page() may need to trim off pages beyond EOF:
+                * but on 32-bit, i_size_read() takes an irq-unsafe seqlock,
+                * which cannot be nested inside the page tree lock. So note
+                * end now: i_size itself may be changed at any moment, but
+                * head page lock is good enough to serialize the trimming.
+                */
+               end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE);
        }
 
        /*
-        * Racy check if we can split the page, before freeze_page() will
+        * Racy check if we can split the page, before unmap_page() will
         * split PMDs
         */
        if (!can_split_huge_page(head, &extra_pins)) {
@@ -2673,7 +2669,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)
        }
 
        mlocked = PageMlocked(page);
-       freeze_page(head);
+       unmap_page(head);
        VM_BUG_ON_PAGE(compound_mapcount(head), head);
 
        /* Make sure the page is not on per-CPU pagevec as it takes pin */
@@ -2707,7 +2703,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)
                if (mapping)
                        __dec_node_page_state(page, NR_SHMEM_THPS);
                spin_unlock(&pgdata->split_queue_lock);
-               __split_huge_page(page, list, flags);
+               __split_huge_page(page, list, end, flags);
                if (PageSwapCache(head)) {
                        swp_entry_t entry = { .val = page_private(head) };
 
@@ -2727,7 +2723,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)
 fail:          if (mapping)
                        xa_unlock(&mapping->i_pages);
                spin_unlock_irqrestore(zone_lru_lock(page_zone(head)), flags);
-               unfreeze_page(head);
+               remap_page(head);
                ret = -EBUSY;
        }
 
index c007fb5..a808324 100644 (file)
@@ -1248,10 +1248,11 @@ void free_huge_page(struct page *page)
                (struct hugepage_subpool *)page_private(page);
        bool restore_reserve;
 
-       set_page_private(page, 0);
-       page->mapping = NULL;
        VM_BUG_ON_PAGE(page_count(page), page);
        VM_BUG_ON_PAGE(page_mapcount(page), page);
+
+       set_page_private(page, 0);
+       page->mapping = NULL;
        restore_reserve = PagePrivate(page);
        ClearPagePrivate(page);
 
@@ -3233,7 +3234,7 @@ static int is_hugetlb_entry_hwpoisoned(pte_t pte)
 int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
                            struct vm_area_struct *vma)
 {
-       pte_t *src_pte, *dst_pte, entry;
+       pte_t *src_pte, *dst_pte, entry, dst_entry;
        struct page *ptepage;
        unsigned long addr;
        int cow;
@@ -3261,15 +3262,30 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
                        break;
                }
 
-               /* If the pagetables are shared don't copy or take references */
-               if (dst_pte == src_pte)
+               /*
+                * If the pagetables are shared don't copy or take references.
+                * dst_pte == src_pte is the common case of src/dest sharing.
+                *
+                * However, src could have 'unshared' and dst shares with
+                * another vma.  If dst_pte !none, this implies sharing.
+                * Check here before taking page table lock, and once again
+                * after taking the lock below.
+                */
+               dst_entry = huge_ptep_get(dst_pte);
+               if ((dst_pte == src_pte) || !huge_pte_none(dst_entry))
                        continue;
 
                dst_ptl = huge_pte_lock(h, dst, dst_pte);
                src_ptl = huge_pte_lockptr(h, src, src_pte);
                spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING);
                entry = huge_ptep_get(src_pte);
-               if (huge_pte_none(entry)) { /* skip none entry */
+               dst_entry = huge_ptep_get(dst_pte);
+               if (huge_pte_none(entry) || !huge_pte_none(dst_entry)) {
+                       /*
+                        * Skip if src entry none.  Also, skip in the
+                        * unlikely case dst entry !none as this implies
+                        * sharing with another vma.
+                        */
                        ;
                } else if (unlikely(is_hugetlb_entry_migration(entry) ||
                                    is_hugetlb_entry_hwpoisoned(entry))) {
@@ -4065,7 +4081,7 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,
 
                /* fallback to copy_from_user outside mmap_sem */
                if (unlikely(ret)) {
-                       ret = -EFAULT;
+                       ret = -ENOENT;
                        *pagep = page;
                        /* don't free the page */
                        goto out;
index c13625c..8e2ff19 100644 (file)
@@ -1287,7 +1287,7 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff)
  * collapse_shmem - collapse small tmpfs/shmem pages into huge one.
  *
  * Basic scheme is simple, details are more complex:
- *  - allocate and freeze a new huge page;
+ *  - allocate and lock a new huge page;
  *  - scan page cache replacing old pages with the new one
  *    + swap in pages if necessary;
  *    + fill in gaps;
@@ -1295,11 +1295,11 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff)
  *  - if replacing succeeds:
  *    + copy data over;
  *    + free old pages;
- *    + unfreeze huge page;
+ *    + unlock huge page;
  *  - if replacing failed;
  *    + put all pages back and unfreeze them;
  *    + restore gaps in the page cache;
- *    + free huge page;
+ *    + unlock and free huge page;
  */
 static void collapse_shmem(struct mm_struct *mm,
                struct address_space *mapping, pgoff_t start,
@@ -1329,19 +1329,6 @@ static void collapse_shmem(struct mm_struct *mm,
                goto out;
        }
 
-       new_page->index = start;
-       new_page->mapping = mapping;
-       __SetPageSwapBacked(new_page);
-       __SetPageLocked(new_page);
-       BUG_ON(!page_ref_freeze(new_page, 1));
-
-       /*
-        * At this point the new_page is 'frozen' (page_count() is zero),
-        * locked and not up-to-date. It's safe to insert it into the page
-        * cache, because nobody would be able to map it or use it in other
-        * way until we unfreeze it.
-        */
-
        /* This will be less messy when we use multi-index entries */
        do {
                xas_lock_irq(&xas);
@@ -1349,19 +1336,44 @@ static void collapse_shmem(struct mm_struct *mm,
                if (!xas_error(&xas))
                        break;
                xas_unlock_irq(&xas);
-               if (!xas_nomem(&xas, GFP_KERNEL))
+               if (!xas_nomem(&xas, GFP_KERNEL)) {
+                       mem_cgroup_cancel_charge(new_page, memcg, true);
+                       result = SCAN_FAIL;
                        goto out;
+               }
        } while (1);
 
+       __SetPageLocked(new_page);
+       __SetPageSwapBacked(new_page);
+       new_page->index = start;
+       new_page->mapping = mapping;
+
+       /*
+        * At this point the new_page is locked and not up-to-date.
+        * It's safe to insert it into the page cache, because nobody would
+        * be able to map it or use it in another way until we unlock it.
+        */
+
        xas_set(&xas, start);
        for (index = start; index < end; index++) {
                struct page *page = xas_next(&xas);
 
                VM_BUG_ON(index != xas.xa_index);
                if (!page) {
+                       /*
+                        * Stop if extent has been truncated or hole-punched,
+                        * and is now completely empty.
+                        */
+                       if (index == start) {
+                               if (!xas_next_entry(&xas, end - 1)) {
+                                       result = SCAN_TRUNCATED;
+                                       goto xa_locked;
+                               }
+                               xas_set(&xas, index);
+                       }
                        if (!shmem_charge(mapping->host, 1)) {
                                result = SCAN_FAIL;
-                               break;
+                               goto xa_locked;
                        }
                        xas_store(&xas, new_page + (index % HPAGE_PMD_NR));
                        nr_none++;
@@ -1376,13 +1388,12 @@ static void collapse_shmem(struct mm_struct *mm,
                                result = SCAN_FAIL;
                                goto xa_unlocked;
                        }
-                       xas_lock_irq(&xas);
-                       xas_set(&xas, index);
                } else if (trylock_page(page)) {
                        get_page(page);
+                       xas_unlock_irq(&xas);
                } else {
                        result = SCAN_PAGE_LOCK;
-                       break;
+                       goto xa_locked;
                }
 
                /*
@@ -1391,17 +1402,24 @@ static void collapse_shmem(struct mm_struct *mm,
                 */
                VM_BUG_ON_PAGE(!PageLocked(page), page);
                VM_BUG_ON_PAGE(!PageUptodate(page), page);
-               VM_BUG_ON_PAGE(PageTransCompound(page), page);
+
+               /*
+                * If file was truncated then extended, or hole-punched, before
+                * we locked the first page, then a THP might be there already.
+                */
+               if (PageTransCompound(page)) {
+                       result = SCAN_PAGE_COMPOUND;
+                       goto out_unlock;
+               }
 
                if (page_mapping(page) != mapping) {
                        result = SCAN_TRUNCATED;
                        goto out_unlock;
                }
-               xas_unlock_irq(&xas);
 
                if (isolate_lru_page(page)) {
                        result = SCAN_DEL_PAGE_LRU;
-                       goto out_isolate_failed;
+                       goto out_unlock;
                }
 
                if (page_mapped(page))
@@ -1421,7 +1439,9 @@ static void collapse_shmem(struct mm_struct *mm,
                 */
                if (!page_ref_freeze(page, 3)) {
                        result = SCAN_PAGE_COUNT;
-                       goto out_lru;
+                       xas_unlock_irq(&xas);
+                       putback_lru_page(page);
+                       goto out_unlock;
                }
 
                /*
@@ -1433,71 +1453,74 @@ static void collapse_shmem(struct mm_struct *mm,
                /* Finally, replace with the new page. */
                xas_store(&xas, new_page + (index % HPAGE_PMD_NR));
                continue;
-out_lru:
-               xas_unlock_irq(&xas);
-               putback_lru_page(page);
-out_isolate_failed:
-               unlock_page(page);
-               put_page(page);
-               goto xa_unlocked;
 out_unlock:
                unlock_page(page);
                put_page(page);
-               break;
+               goto xa_unlocked;
        }
-       xas_unlock_irq(&xas);
 
+       __inc_node_page_state(new_page, NR_SHMEM_THPS);
+       if (nr_none) {
+               struct zone *zone = page_zone(new_page);
+
+               __mod_node_page_state(zone->zone_pgdat, NR_FILE_PAGES, nr_none);
+               __mod_node_page_state(zone->zone_pgdat, NR_SHMEM, nr_none);
+       }
+
+xa_locked:
+       xas_unlock_irq(&xas);
 xa_unlocked:
+
        if (result == SCAN_SUCCEED) {
                struct page *page, *tmp;
-               struct zone *zone = page_zone(new_page);
 
                /*
                 * Replacing old pages with new one has succeeded, now we
                 * need to copy the content and free the old pages.
                 */
+               index = start;
                list_for_each_entry_safe(page, tmp, &pagelist, lru) {
+                       while (index < page->index) {
+                               clear_highpage(new_page + (index % HPAGE_PMD_NR));
+                               index++;
+                       }
                        copy_highpage(new_page + (page->index % HPAGE_PMD_NR),
                                        page);
                        list_del(&page->lru);
-                       unlock_page(page);
-                       page_ref_unfreeze(page, 1);
                        page->mapping = NULL;
+                       page_ref_unfreeze(page, 1);
                        ClearPageActive(page);
                        ClearPageUnevictable(page);
+                       unlock_page(page);
                        put_page(page);
+                       index++;
                }
-
-               local_irq_disable();
-               __inc_node_page_state(new_page, NR_SHMEM_THPS);
-               if (nr_none) {
-                       __mod_node_page_state(zone->zone_pgdat, NR_FILE_PAGES, nr_none);
-                       __mod_node_page_state(zone->zone_pgdat, NR_SHMEM, nr_none);
+               while (index < end) {
+                       clear_highpage(new_page + (index % HPAGE_PMD_NR));
+                       index++;
                }
-               local_irq_enable();
 
-               /*
-                * Remove pte page tables, so we can re-fault
-                * the page as huge.
-                */
-               retract_page_tables(mapping, start);
-
-               /* Everything is ready, let's unfreeze the new_page */
-               set_page_dirty(new_page);
                SetPageUptodate(new_page);
-               page_ref_unfreeze(new_page, HPAGE_PMD_NR);
+               page_ref_add(new_page, HPAGE_PMD_NR - 1);
+               set_page_dirty(new_page);
                mem_cgroup_commit_charge(new_page, memcg, false, true);
                lru_cache_add_anon(new_page);
-               unlock_page(new_page);
 
+               /*
+                * Remove pte page tables, so we can re-fault the page as huge.
+                */
+               retract_page_tables(mapping, start);
                *hpage = NULL;
 
                khugepaged_pages_collapsed++;
        } else {
                struct page *page;
+
                /* Something went wrong: roll back page cache changes */
-               shmem_uncharge(mapping->host, nr_none);
                xas_lock_irq(&xas);
+               mapping->nrpages -= nr_none;
+               shmem_uncharge(mapping->host, nr_none);
+
                xas_set(&xas, start);
                xas_for_each(&xas, page, end - 1) {
                        page = list_first_entry_or_null(&pagelist,
@@ -1519,19 +1542,18 @@ xa_unlocked:
                        xas_store(&xas, page);
                        xas_pause(&xas);
                        xas_unlock_irq(&xas);
-                       putback_lru_page(page);
                        unlock_page(page);
+                       putback_lru_page(page);
                        xas_lock_irq(&xas);
                }
                VM_BUG_ON(nr_none);
                xas_unlock_irq(&xas);
 
-               /* Unfreeze new_page, caller would take care about freeing it */
-               page_ref_unfreeze(new_page, 1);
                mem_cgroup_cancel_charge(new_page, memcg, true);
-               unlock_page(new_page);
                new_page->mapping = NULL;
        }
+
+       unlock_page(new_page);
 out:
        VM_BUG_ON(!list_empty(&pagelist));
        /* TODO: tracepoints */
index 7df468c..81ae63c 100644 (file)
@@ -1179,7 +1179,7 @@ void __init_memblock __next_mem_range_rev(u64 *idx, int nid,
 
 #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
 /*
- * Common iterator interface used to define for_each_mem_range().
+ * Common iterator interface used to define for_each_mem_pfn_range().
  */
 void __init_memblock __next_mem_pfn_range(int *idx, int nid,
                                unsigned long *out_start_pfn,
@@ -1727,7 +1727,7 @@ static int __init_memblock memblock_search(struct memblock_type *type, phys_addr
        return -1;
 }
 
-bool __init memblock_is_reserved(phys_addr_t addr)
+bool __init_memblock memblock_is_reserved(phys_addr_t addr)
 {
        return memblock_search(&memblock.reserved, addr) != -1;
 }
index 0cd3de3..7c72f2a 100644 (file)
@@ -1161,6 +1161,7 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
        LIST_HEAD(tokill);
        int rc = -EBUSY;
        loff_t start;
+       dax_entry_t cookie;
 
        /*
         * Prevent the inode from being freed while we are interrogating
@@ -1169,7 +1170,8 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
         * also prevents changes to the mapping of this pfn until
         * poison signaling is complete.
         */
-       if (!dax_lock_mapping_entry(page))
+       cookie = dax_lock_page(page);
+       if (!cookie)
                goto out;
 
        if (hwpoison_filter(page)) {
@@ -1220,7 +1222,7 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
        kill_procs(&tokill, flags & MF_MUST_KILL, !unmap_success, pfn, flags);
        rc = 0;
 unlock:
-       dax_unlock_mapping_entry(page);
+       dax_unlock_page(page, cookie);
 out:
        /* drop pgmap ref acquired in caller */
        put_dev_pagemap(pgmap);
index 5837a06..d4496d9 100644 (file)
@@ -1116,8 +1116,8 @@ static struct page *new_page(struct page *page, unsigned long start)
        } else if (PageTransHuge(page)) {
                struct page *thp;
 
-               thp = alloc_pages_vma(GFP_TRANSHUGE, HPAGE_PMD_ORDER, vma,
-                               address, numa_node_id());
+               thp = alloc_hugepage_vma(GFP_TRANSHUGE, vma, address,
+                                        HPAGE_PMD_ORDER);
                if (!thp)
                        return NULL;
                prep_transhuge_page(thp);
@@ -1662,7 +1662,7 @@ struct mempolicy *__get_vma_policy(struct vm_area_struct *vma,
  * freeing by another task.  It is the caller's responsibility to free the
  * extra reference for shared policies.
  */
-struct mempolicy *get_vma_policy(struct vm_area_struct *vma,
+static struct mempolicy *get_vma_policy(struct vm_area_struct *vma,
                                                unsigned long addr)
 {
        struct mempolicy *pol = __get_vma_policy(vma, addr);
@@ -2011,6 +2011,7 @@ static struct page *alloc_page_interleave(gfp_t gfp, unsigned order,
  *     @vma:  Pointer to VMA or NULL if not available.
  *     @addr: Virtual Address of the allocation. Must be inside the VMA.
  *     @node: Which node to prefer for allocation (modulo policy).
+ *     @hugepage: for hugepages try only the preferred node if possible
  *
  *     This function allocates a page from the kernel page pool and applies
  *     a NUMA policy associated with the VMA or the current process.
@@ -2021,7 +2022,7 @@ static struct page *alloc_page_interleave(gfp_t gfp, unsigned order,
  */
 struct page *
 alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma,
-               unsigned long addr, int node)
+               unsigned long addr, int node, bool hugepage)
 {
        struct mempolicy *pol;
        struct page *page;
@@ -2039,6 +2040,31 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma,
                goto out;
        }
 
+       if (unlikely(IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && hugepage)) {
+               int hpage_node = node;
+
+               /*
+                * For hugepage allocation and non-interleave policy which
+                * allows the current node (or other explicitly preferred
+                * node) we only try to allocate from the current/preferred
+                * node and don't fall back to other nodes, as the cost of
+                * remote accesses would likely offset THP benefits.
+                *
+                * If the policy is interleave, or does not allow the current
+                * node in its nodemask, we allocate the standard way.
+                */
+               if (pol->mode == MPOL_PREFERRED && !(pol->flags & MPOL_F_LOCAL))
+                       hpage_node = pol->v.preferred_node;
+
+               nmask = policy_nodemask(gfp, pol);
+               if (!nmask || node_isset(hpage_node, *nmask)) {
+                       mpol_cond_put(pol);
+                       page = __alloc_pages_node(hpage_node,
+                                               gfp | __GFP_THISNODE, order);
+                       goto out;
+               }
+       }
+
        nmask = policy_nodemask(gfp, pol);
        preferred_nid = policy_node(gfp, pol, node);
        page = __alloc_pages_nodemask(gfp, order, preferred_nid, nmask);
index a919ba5..2ec9cc4 100644 (file)
@@ -4060,17 +4060,6 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
        unsigned int cpuset_mems_cookie;
        int reserve_flags;
 
-       /*
-        * In the slowpath, we sanity check order to avoid ever trying to
-        * reclaim >= MAX_ORDER areas which will never succeed. Callers may
-        * be using allocators in order of preference for an area that is
-        * too large.
-        */
-       if (order >= MAX_ORDER) {
-               WARN_ON_ONCE(!(gfp_mask & __GFP_NOWARN));
-               return NULL;
-       }
-
        /*
         * We also sanity check to catch abuse of atomic reserves being used by
         * callers that are not in atomic context.
@@ -4364,6 +4353,15 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,
        gfp_t alloc_mask; /* The gfp_t that was actually used for allocation */
        struct alloc_context ac = { };
 
+       /*
+        * There are several places where we assume that the order value is sane
+        * so bail out early if the request is out of bound.
+        */
+       if (unlikely(order >= MAX_ORDER)) {
+               WARN_ON_ONCE(!(gfp_mask & __GFP_NOWARN));
+               return NULL;
+       }
+
        gfp_mask &= gfp_allowed_mask;
        alloc_mask = gfp_mask;
        if (!prepare_alloc_pages(gfp_mask, order, preferred_nid, nodemask, &ac, &alloc_mask, &alloc_flags))
@@ -5815,8 +5813,10 @@ void __meminit init_currently_empty_zone(struct zone *zone,
                                        unsigned long size)
 {
        struct pglist_data *pgdat = zone->zone_pgdat;
+       int zone_idx = zone_idx(zone) + 1;
 
-       pgdat->nr_zones = zone_idx(zone) + 1;
+       if (zone_idx > pgdat->nr_zones)
+               pgdat->nr_zones = zone_idx;
 
        zone->zone_start_pfn = zone_start_pfn;
 
@@ -7788,6 +7788,14 @@ bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
                if (PageReserved(page))
                        goto unmovable;
 
+               /*
+                * If the zone is movable and we have ruled out all reserved
+                * pages then it should be reasonably safe to assume the rest
+                * is movable.
+                */
+               if (zone_idx(zone) == ZONE_MOVABLE)
+                       continue;
+
                /*
                 * Hugepages are not in LRU lists, but they're movable.
                 * We need not scan over tail pages bacause we don't
index 1e79fac..85b7f94 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1627,16 +1627,9 @@ static bool try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
                                                      address + PAGE_SIZE);
                } else {
                        /*
-                        * We should not need to notify here as we reach this
-                        * case only from freeze_page() itself only call from
-                        * split_huge_page_to_list() so everything below must
-                        * be true:
-                        *   - page is not anonymous
-                        *   - page is locked
-                        *
-                        * So as it is a locked file back page thus it can not
-                        * be remove from the page cache and replace by a new
-                        * page before mmu_notifier_invalidate_range_end so no
+                        * This is a locked file-backed page, thus it cannot
+                        * be removed from the page cache and replaced by a new
+                        * page before mmu_notifier_invalidate_range_end, so no
                         * concurrent thread might update its page table to
                         * point at new page while a device still is using this
                         * page.
index ea26d7a..5d07e0b 100644 (file)
@@ -297,12 +297,14 @@ bool shmem_charge(struct inode *inode, long pages)
        if (!shmem_inode_acct_block(inode, pages))
                return false;
 
+       /* nrpages adjustment first, then shmem_recalc_inode() when balanced */
+       inode->i_mapping->nrpages += pages;
+
        spin_lock_irqsave(&info->lock, flags);
        info->alloced += pages;
        inode->i_blocks += pages * BLOCKS_PER_PAGE;
        shmem_recalc_inode(inode);
        spin_unlock_irqrestore(&info->lock, flags);
-       inode->i_mapping->nrpages += pages;
 
        return true;
 }
@@ -312,6 +314,8 @@ void shmem_uncharge(struct inode *inode, long pages)
        struct shmem_inode_info *info = SHMEM_I(inode);
        unsigned long flags;
 
+       /* nrpages adjustment done by __delete_from_page_cache() or caller */
+
        spin_lock_irqsave(&info->lock, flags);
        info->alloced -= pages;
        inode->i_blocks -= pages * BLOCKS_PER_PAGE;
@@ -657,9 +661,7 @@ static int shmem_free_swap(struct address_space *mapping,
 {
        void *old;
 
-       xa_lock_irq(&mapping->i_pages);
-       old = __xa_cmpxchg(&mapping->i_pages, index, radswap, NULL, 0);
-       xa_unlock_irq(&mapping->i_pages);
+       old = xa_cmpxchg_irq(&mapping->i_pages, index, radswap, NULL, 0);
        if (old != radswap)
                return -ENOENT;
        free_swap_and_cache(radix_to_swp_entry(radswap));
@@ -1435,7 +1437,7 @@ static struct page *shmem_alloc_hugepage(gfp_t gfp,
 
        shmem_pseudo_vma_init(&pvma, info, hindex);
        page = alloc_pages_vma(gfp | __GFP_COMP | __GFP_NORETRY | __GFP_NOWARN,
-                       HPAGE_PMD_ORDER, &pvma, 0, numa_node_id());
+                       HPAGE_PMD_ORDER, &pvma, 0, numa_node_id(), true);
        shmem_pseudo_vma_destroy(&pvma);
        if (page)
                prep_transhuge_page(page);
@@ -1509,11 +1511,13 @@ static int shmem_replace_page(struct page **pagep, gfp_t gfp,
 {
        struct page *oldpage, *newpage;
        struct address_space *swap_mapping;
+       swp_entry_t entry;
        pgoff_t swap_index;
        int error;
 
        oldpage = *pagep;
-       swap_index = page_private(oldpage);
+       entry.val = page_private(oldpage);
+       swap_index = swp_offset(entry);
        swap_mapping = page_mapping(oldpage);
 
        /*
@@ -1532,7 +1536,7 @@ static int shmem_replace_page(struct page **pagep, gfp_t gfp,
        __SetPageLocked(newpage);
        __SetPageSwapBacked(newpage);
        SetPageUptodate(newpage);
-       set_page_private(newpage, swap_index);
+       set_page_private(newpage, entry.val);
        SetPageSwapCache(newpage);
 
        /*
@@ -2214,6 +2218,7 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
        struct page *page;
        pte_t _dst_pte, *dst_pte;
        int ret;
+       pgoff_t offset, max_off;
 
        ret = -ENOMEM;
        if (!shmem_inode_acct_block(inode, 1))
@@ -2236,7 +2241,7 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
                                *pagep = page;
                                shmem_inode_unacct_blocks(inode, 1);
                                /* don't free the page */
-                               return -EFAULT;
+                               return -ENOENT;
                        }
                } else {                /* mfill_zeropage_atomic */
                        clear_highpage(page);
@@ -2251,6 +2256,12 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
        __SetPageSwapBacked(page);
        __SetPageUptodate(page);
 
+       ret = -EFAULT;
+       offset = linear_page_index(dst_vma, dst_addr);
+       max_off = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
+       if (unlikely(offset >= max_off))
+               goto out_release;
+
        ret = mem_cgroup_try_charge_delay(page, dst_mm, gfp, &memcg, false);
        if (ret)
                goto out_release;
@@ -2265,9 +2276,25 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
        _dst_pte = mk_pte(page, dst_vma->vm_page_prot);
        if (dst_vma->vm_flags & VM_WRITE)
                _dst_pte = pte_mkwrite(pte_mkdirty(_dst_pte));
+       else {
+               /*
+                * We don't set the pte dirty if the vma has no
+                * VM_WRITE permission, so mark the page dirty or it
+                * could be freed from under us. We could do it
+                * unconditionally before unlock_page(), but doing it
+                * only if VM_WRITE is not set is faster.
+                */
+               set_page_dirty(page);
+       }
 
-       ret = -EEXIST;
        dst_pte = pte_offset_map_lock(dst_mm, dst_pmd, dst_addr, &ptl);
+
+       ret = -EFAULT;
+       max_off = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
+       if (unlikely(offset >= max_off))
+               goto out_release_uncharge_unlock;
+
+       ret = -EEXIST;
        if (!pte_none(*dst_pte))
                goto out_release_uncharge_unlock;
 
@@ -2285,13 +2312,15 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
 
        /* No need to invalidate - it was non-present before */
        update_mmu_cache(dst_vma, dst_addr, dst_pte);
-       unlock_page(page);
        pte_unmap_unlock(dst_pte, ptl);
+       unlock_page(page);
        ret = 0;
 out:
        return ret;
 out_release_uncharge_unlock:
        pte_unmap_unlock(dst_pte, ptl);
+       ClearPageDirty(page);
+       delete_from_page_cache(page);
 out_release_uncharge:
        mem_cgroup_cancel_charge(page, memcg, false);
 out_release:
@@ -2563,9 +2592,7 @@ static loff_t shmem_file_llseek(struct file *file, loff_t offset, int whence)
        inode_lock(inode);
        /* We're holding i_mutex so we can access i_size directly */
 
-       if (offset < 0)
-               offset = -EINVAL;
-       else if (offset >= inode->i_size)
+       if (offset < 0 || offset >= inode->i_size)
                offset = -ENXIO;
        else {
                start = offset >> PAGE_SHIFT;
index 33307fc..3abc8cc 100644 (file)
@@ -239,6 +239,22 @@ void __init memory_present(int nid, unsigned long start, unsigned long end)
        }
 }
 
+/*
+ * Mark all memblocks as present using memory_present(). This is a
+ * convienence function that is useful for a number of arches
+ * to mark all of the systems memory as present during initialization.
+ */
+void __init memblocks_present(void)
+{
+       struct memblock_region *reg;
+
+       for_each_memblock(memory, reg) {
+               memory_present(memblock_get_region_node(reg),
+                              memblock_region_memory_base_pfn(reg),
+                              memblock_region_memory_end_pfn(reg));
+       }
+}
+
 /*
  * Subtle, we encode the real pfn into the mem_map such that
  * the identity pfn - section_mem_map will return the actual
index 644f746..8688ae6 100644 (file)
@@ -2813,7 +2813,7 @@ static struct swap_info_struct *alloc_swap_info(void)
        unsigned int type;
        int i;
 
-       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       p = kvzalloc(sizeof(*p), GFP_KERNEL);
        if (!p)
                return ERR_PTR(-ENOMEM);
 
@@ -2824,7 +2824,7 @@ static struct swap_info_struct *alloc_swap_info(void)
        }
        if (type >= MAX_SWAPFILES) {
                spin_unlock(&swap_lock);
-               kfree(p);
+               kvfree(p);
                return ERR_PTR(-EPERM);
        }
        if (type >= nr_swapfiles) {
@@ -2838,7 +2838,7 @@ static struct swap_info_struct *alloc_swap_info(void)
                smp_wmb();
                nr_swapfiles++;
        } else {
-               kfree(p);
+               kvfree(p);
                p = swap_info[type];
                /*
                 * Do not memset this entry: a racing procfs swap_next()
index 45d68e9..798e7cc 100644 (file)
@@ -517,9 +517,13 @@ void truncate_inode_pages_final(struct address_space *mapping)
                 */
                xa_lock_irq(&mapping->i_pages);
                xa_unlock_irq(&mapping->i_pages);
-
-               truncate_inode_pages(mapping, 0);
        }
+
+       /*
+        * Cleancache needs notification even if there are no pages or shadow
+        * entries.
+        */
+       truncate_inode_pages(mapping, 0);
 }
 EXPORT_SYMBOL(truncate_inode_pages_final);
 
index 5029f24..458acda 100644 (file)
@@ -33,6 +33,8 @@ static int mcopy_atomic_pte(struct mm_struct *dst_mm,
        void *page_kaddr;
        int ret;
        struct page *page;
+       pgoff_t offset, max_off;
+       struct inode *inode;
 
        if (!*pagep) {
                ret = -ENOMEM;
@@ -48,7 +50,7 @@ static int mcopy_atomic_pte(struct mm_struct *dst_mm,
 
                /* fallback to copy_from_user outside mmap_sem */
                if (unlikely(ret)) {
-                       ret = -EFAULT;
+                       ret = -ENOENT;
                        *pagep = page;
                        /* don't free the page */
                        goto out;
@@ -73,8 +75,17 @@ static int mcopy_atomic_pte(struct mm_struct *dst_mm,
        if (dst_vma->vm_flags & VM_WRITE)
                _dst_pte = pte_mkwrite(pte_mkdirty(_dst_pte));
 
-       ret = -EEXIST;
        dst_pte = pte_offset_map_lock(dst_mm, dst_pmd, dst_addr, &ptl);
+       if (dst_vma->vm_file) {
+               /* the shmem MAP_PRIVATE case requires checking the i_size */
+               inode = dst_vma->vm_file->f_inode;
+               offset = linear_page_index(dst_vma, dst_addr);
+               max_off = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
+               ret = -EFAULT;
+               if (unlikely(offset >= max_off))
+                       goto out_release_uncharge_unlock;
+       }
+       ret = -EEXIST;
        if (!pte_none(*dst_pte))
                goto out_release_uncharge_unlock;
 
@@ -108,11 +119,22 @@ static int mfill_zeropage_pte(struct mm_struct *dst_mm,
        pte_t _dst_pte, *dst_pte;
        spinlock_t *ptl;
        int ret;
+       pgoff_t offset, max_off;
+       struct inode *inode;
 
        _dst_pte = pte_mkspecial(pfn_pte(my_zero_pfn(dst_addr),
                                         dst_vma->vm_page_prot));
-       ret = -EEXIST;
        dst_pte = pte_offset_map_lock(dst_mm, dst_pmd, dst_addr, &ptl);
+       if (dst_vma->vm_file) {
+               /* the shmem MAP_PRIVATE case requires checking the i_size */
+               inode = dst_vma->vm_file->f_inode;
+               offset = linear_page_index(dst_vma, dst_addr);
+               max_off = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
+               ret = -EFAULT;
+               if (unlikely(offset >= max_off))
+                       goto out_unlock;
+       }
+       ret = -EEXIST;
        if (!pte_none(*dst_pte))
                goto out_unlock;
        set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte);
@@ -205,8 +227,9 @@ retry:
                if (!dst_vma || !is_vm_hugetlb_page(dst_vma))
                        goto out_unlock;
                /*
-                * Only allow __mcopy_atomic_hugetlb on userfaultfd
-                * registered ranges.
+                * Check the vma is registered in uffd, this is
+                * required to enforce the VM_MAYWRITE check done at
+                * uffd registration time.
                 */
                if (!dst_vma->vm_userfaultfd_ctx.ctx)
                        goto out_unlock;
@@ -274,7 +297,7 @@ retry:
 
                cond_resched();
 
-               if (unlikely(err == -EFAULT)) {
+               if (unlikely(err == -ENOENT)) {
                        up_read(&dst_mm->mmap_sem);
                        BUG_ON(!page);
 
@@ -380,7 +403,17 @@ static __always_inline ssize_t mfill_atomic_pte(struct mm_struct *dst_mm,
 {
        ssize_t err;
 
-       if (vma_is_anonymous(dst_vma)) {
+       /*
+        * The normal page fault path for a shmem will invoke the
+        * fault, fill the hole in the file and COW it right away. The
+        * result generates plain anonymous memory. So when we are
+        * asked to fill an hole in a MAP_PRIVATE shmem mapping, we'll
+        * generate anonymous memory directly without actually filling
+        * the hole. For the MAP_PRIVATE case the robustness check
+        * only happens in the pagetable (to verify it's still none)
+        * and not in the radix tree.
+        */
+       if (!(dst_vma->vm_flags & VM_SHARED)) {
                if (!zeropage)
                        err = mcopy_atomic_pte(dst_mm, dst_pmd, dst_vma,
                                               dst_addr, src_addr, page);
@@ -449,13 +482,9 @@ retry:
        if (!dst_vma)
                goto out_unlock;
        /*
-        * Be strict and only allow __mcopy_atomic on userfaultfd
-        * registered ranges to prevent userland errors going
-        * unnoticed. As far as the VM consistency is concerned, it
-        * would be perfectly safe to remove this check, but there's
-        * no useful usage for __mcopy_atomic ouside of userfaultfd
-        * registered ranges. This is after all why these are ioctls
-        * belonging to the userfaultfd and not syscalls.
+        * Check the vma is registered in uffd, this is required to
+        * enforce the VM_MAYWRITE check done at uffd registration
+        * time.
         */
        if (!dst_vma->vm_userfaultfd_ctx.ctx)
                goto out_unlock;
@@ -489,7 +518,8 @@ retry:
         * dst_vma.
         */
        err = -ENOMEM;
-       if (vma_is_anonymous(dst_vma) && unlikely(anon_vma_prepare(dst_vma)))
+       if (!(dst_vma->vm_flags & VM_SHARED) &&
+           unlikely(anon_vma_prepare(dst_vma)))
                goto out_unlock;
 
        while (src_addr < src_start + len) {
@@ -530,7 +560,7 @@ retry:
                                       src_addr, &page, zeropage);
                cond_resched();
 
-               if (unlikely(err == -EFAULT)) {
+               if (unlikely(err == -ENOENT)) {
                        void *page_kaddr;
 
                        up_read(&dst_mm->mmap_sem);
index 6038ce5..9c62459 100644 (file)
@@ -1827,12 +1827,13 @@ static bool need_update(int cpu)
 
                /*
                 * The fast way of checking if there are any vmstat diffs.
-                * This works because the diffs are byte sized items.
                 */
-               if (memchr_inv(p->vm_stat_diff, 0, NR_VM_ZONE_STAT_ITEMS))
+               if (memchr_inv(p->vm_stat_diff, 0, NR_VM_ZONE_STAT_ITEMS *
+                              sizeof(p->vm_stat_diff[0])))
                        return true;
 #ifdef CONFIG_NUMA
-               if (memchr_inv(p->vm_numa_stat_diff, 0, NR_VM_NUMA_STAT_ITEMS))
+               if (memchr_inv(p->vm_numa_stat_diff, 0, NR_VM_NUMA_STAT_ITEMS *
+                              sizeof(p->vm_numa_stat_diff[0])))
                        return true;
 #endif
        }
index 4b366d1..aee9b0b 100644 (file)
@@ -99,6 +99,7 @@ struct z3fold_header {
 #define NCHUNKS                ((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT)
 
 #define BUDDY_MASK     (0x3)
+#define BUDDY_SHIFT    2
 
 /**
  * struct z3fold_pool - stores metadata for each z3fold pool
@@ -145,7 +146,7 @@ enum z3fold_page_flags {
        MIDDLE_CHUNK_MAPPED,
        NEEDS_COMPACTING,
        PAGE_STALE,
-       UNDER_RECLAIM
+       PAGE_CLAIMED, /* by either reclaim or free */
 };
 
 /*****************
@@ -174,7 +175,7 @@ static struct z3fold_header *init_z3fold_page(struct page *page,
        clear_bit(MIDDLE_CHUNK_MAPPED, &page->private);
        clear_bit(NEEDS_COMPACTING, &page->private);
        clear_bit(PAGE_STALE, &page->private);
-       clear_bit(UNDER_RECLAIM, &page->private);
+       clear_bit(PAGE_CLAIMED, &page->private);
 
        spin_lock_init(&zhdr->page_lock);
        kref_init(&zhdr->refcount);
@@ -223,8 +224,11 @@ static unsigned long encode_handle(struct z3fold_header *zhdr, enum buddy bud)
        unsigned long handle;
 
        handle = (unsigned long)zhdr;
-       if (bud != HEADLESS)
-               handle += (bud + zhdr->first_num) & BUDDY_MASK;
+       if (bud != HEADLESS) {
+               handle |= (bud + zhdr->first_num) & BUDDY_MASK;
+               if (bud == LAST)
+                       handle |= (zhdr->last_chunks << BUDDY_SHIFT);
+       }
        return handle;
 }
 
@@ -234,6 +238,12 @@ static struct z3fold_header *handle_to_z3fold_header(unsigned long handle)
        return (struct z3fold_header *)(handle & PAGE_MASK);
 }
 
+/* only for LAST bud, returns zero otherwise */
+static unsigned short handle_to_chunks(unsigned long handle)
+{
+       return (handle & ~PAGE_MASK) >> BUDDY_SHIFT;
+}
+
 /*
  * (handle & BUDDY_MASK) < zhdr->first_num is possible in encode_handle
  *  but that doesn't matter. because the masking will result in the
@@ -720,37 +730,39 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle)
        page = virt_to_page(zhdr);
 
        if (test_bit(PAGE_HEADLESS, &page->private)) {
-               /* HEADLESS page stored */
-               bud = HEADLESS;
-       } else {
-               z3fold_page_lock(zhdr);
-               bud = handle_to_buddy(handle);
-
-               switch (bud) {
-               case FIRST:
-                       zhdr->first_chunks = 0;
-                       break;
-               case MIDDLE:
-                       zhdr->middle_chunks = 0;
-                       zhdr->start_middle = 0;
-                       break;
-               case LAST:
-                       zhdr->last_chunks = 0;
-                       break;
-               default:
-                       pr_err("%s: unknown bud %d\n", __func__, bud);
-                       WARN_ON(1);
-                       z3fold_page_unlock(zhdr);
-                       return;
+               /* if a headless page is under reclaim, just leave.
+                * NB: we use test_and_set_bit for a reason: if the bit
+                * has not been set before, we release this page
+                * immediately so we don't care about its value any more.
+                */
+               if (!test_and_set_bit(PAGE_CLAIMED, &page->private)) {
+                       spin_lock(&pool->lock);
+                       list_del(&page->lru);
+                       spin_unlock(&pool->lock);
+                       free_z3fold_page(page);
+                       atomic64_dec(&pool->pages_nr);
                }
+               return;
        }
 
-       if (bud == HEADLESS) {
-               spin_lock(&pool->lock);
-               list_del(&page->lru);
-               spin_unlock(&pool->lock);
-               free_z3fold_page(page);
-               atomic64_dec(&pool->pages_nr);
+       /* Non-headless case */
+       z3fold_page_lock(zhdr);
+       bud = handle_to_buddy(handle);
+
+       switch (bud) {
+       case FIRST:
+               zhdr->first_chunks = 0;
+               break;
+       case MIDDLE:
+               zhdr->middle_chunks = 0;
+               break;
+       case LAST:
+               zhdr->last_chunks = 0;
+               break;
+       default:
+               pr_err("%s: unknown bud %d\n", __func__, bud);
+               WARN_ON(1);
+               z3fold_page_unlock(zhdr);
                return;
        }
 
@@ -758,7 +770,7 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle)
                atomic64_dec(&pool->pages_nr);
                return;
        }
-       if (test_bit(UNDER_RECLAIM, &page->private)) {
+       if (test_bit(PAGE_CLAIMED, &page->private)) {
                z3fold_page_unlock(zhdr);
                return;
        }
@@ -836,20 +848,30 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
                }
                list_for_each_prev(pos, &pool->lru) {
                        page = list_entry(pos, struct page, lru);
+
+                       /* this bit could have been set by free, in which case
+                        * we pass over to the next page in the pool.
+                        */
+                       if (test_and_set_bit(PAGE_CLAIMED, &page->private))
+                               continue;
+
+                       zhdr = page_address(page);
                        if (test_bit(PAGE_HEADLESS, &page->private))
-                               /* candidate found */
                                break;
 
-                       zhdr = page_address(page);
-                       if (!z3fold_page_trylock(zhdr))
+                       if (!z3fold_page_trylock(zhdr)) {
+                               zhdr = NULL;
                                continue; /* can't evict at this point */
+                       }
                        kref_get(&zhdr->refcount);
                        list_del_init(&zhdr->buddy);
                        zhdr->cpu = -1;
-                       set_bit(UNDER_RECLAIM, &page->private);
                        break;
                }
 
+               if (!zhdr)
+                       break;
+
                list_del_init(&page->lru);
                spin_unlock(&pool->lock);
 
@@ -898,6 +920,7 @@ next:
                if (test_bit(PAGE_HEADLESS, &page->private)) {
                        if (ret == 0) {
                                free_z3fold_page(page);
+                               atomic64_dec(&pool->pages_nr);
                                return 0;
                        }
                        spin_lock(&pool->lock);
@@ -905,7 +928,7 @@ next:
                        spin_unlock(&pool->lock);
                } else {
                        z3fold_page_lock(zhdr);
-                       clear_bit(UNDER_RECLAIM, &page->private);
+                       clear_bit(PAGE_CLAIMED, &page->private);
                        if (kref_put(&zhdr->refcount,
                                        release_z3fold_page_locked)) {
                                atomic64_dec(&pool->pages_nr);
@@ -964,7 +987,7 @@ static void *z3fold_map(struct z3fold_pool *pool, unsigned long handle)
                set_bit(MIDDLE_CHUNK_MAPPED, &page->private);
                break;
        case LAST:
-               addr += PAGE_SIZE - (zhdr->last_chunks << CHUNK_SHIFT);
+               addr += PAGE_SIZE - (handle_to_chunks(handle) << CHUNK_SHIFT);
                break;
        default:
                pr_err("unknown buddy id %d\n", buddy);
index 24915e0..6c152f9 100644 (file)
@@ -232,18 +232,7 @@ static int lowpan_context_show(struct seq_file *file, void *offset)
 
        return 0;
 }
-
-static int lowpan_context_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, lowpan_context_show, inode->i_private);
-}
-
-static const struct file_operations lowpan_context_fops = {
-       .open           = lowpan_context_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(lowpan_context);
 
 static int lowpan_short_addr_get(void *data, u64 *val)
 {
index 1b7a375..dc44111 100644 (file)
@@ -358,6 +358,7 @@ static int __vlan_device_event(struct net_device *dev, unsigned long event)
 static int vlan_device_event(struct notifier_block *unused, unsigned long event,
                             void *ptr)
 {
+       struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct vlan_group *grp;
        struct vlan_info *vlan_info;
@@ -460,7 +461,8 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
 
                        vlan = vlan_dev_priv(vlandev);
                        if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
-                               dev_change_flags(vlandev, flgs | IFF_UP);
+                               dev_change_flags(vlandev, flgs | IFF_UP,
+                                                extack);
                        netif_stacked_transfer_operstate(dev, vlandev);
                }
                break;
@@ -648,93 +650,6 @@ out:
        return err;
 }
 
-static struct sk_buff *vlan_gro_receive(struct list_head *head,
-                                       struct sk_buff *skb)
-{
-       const struct packet_offload *ptype;
-       unsigned int hlen, off_vlan;
-       struct sk_buff *pp = NULL;
-       struct vlan_hdr *vhdr;
-       struct sk_buff *p;
-       __be16 type;
-       int flush = 1;
-
-       off_vlan = skb_gro_offset(skb);
-       hlen = off_vlan + sizeof(*vhdr);
-       vhdr = skb_gro_header_fast(skb, off_vlan);
-       if (skb_gro_header_hard(skb, hlen)) {
-               vhdr = skb_gro_header_slow(skb, hlen, off_vlan);
-               if (unlikely(!vhdr))
-                       goto out;
-       }
-
-       type = vhdr->h_vlan_encapsulated_proto;
-
-       rcu_read_lock();
-       ptype = gro_find_receive_by_type(type);
-       if (!ptype)
-               goto out_unlock;
-
-       flush = 0;
-
-       list_for_each_entry(p, head, list) {
-               struct vlan_hdr *vhdr2;
-
-               if (!NAPI_GRO_CB(p)->same_flow)
-                       continue;
-
-               vhdr2 = (struct vlan_hdr *)(p->data + off_vlan);
-               if (compare_vlan_header(vhdr, vhdr2))
-                       NAPI_GRO_CB(p)->same_flow = 0;
-       }
-
-       skb_gro_pull(skb, sizeof(*vhdr));
-       skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
-       pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
-
-out_unlock:
-       rcu_read_unlock();
-out:
-       skb_gro_flush_final(skb, pp, flush);
-
-       return pp;
-}
-
-static int vlan_gro_complete(struct sk_buff *skb, int nhoff)
-{
-       struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff);
-       __be16 type = vhdr->h_vlan_encapsulated_proto;
-       struct packet_offload *ptype;
-       int err = -ENOENT;
-
-       rcu_read_lock();
-       ptype = gro_find_complete_by_type(type);
-       if (ptype)
-               err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(*vhdr));
-
-       rcu_read_unlock();
-       return err;
-}
-
-static struct packet_offload vlan_packet_offloads[] __read_mostly = {
-       {
-               .type = cpu_to_be16(ETH_P_8021Q),
-               .priority = 10,
-               .callbacks = {
-                       .gro_receive = vlan_gro_receive,
-                       .gro_complete = vlan_gro_complete,
-               },
-       },
-       {
-               .type = cpu_to_be16(ETH_P_8021AD),
-               .priority = 10,
-               .callbacks = {
-                       .gro_receive = vlan_gro_receive,
-                       .gro_complete = vlan_gro_complete,
-               },
-       },
-};
-
 static int __net_init vlan_init_net(struct net *net)
 {
        struct vlan_net *vn = net_generic(net, vlan_net_id);
@@ -762,7 +677,6 @@ static struct pernet_operations vlan_net_ops = {
 static int __init vlan_proto_init(void)
 {
        int err;
-       unsigned int i;
 
        pr_info("%s v%s\n", vlan_fullname, vlan_version);
 
@@ -786,9 +700,6 @@ static int __init vlan_proto_init(void)
        if (err < 0)
                goto err5;
 
-       for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
-               dev_add_offload(&vlan_packet_offloads[i]);
-
        vlan_ioctl_set(vlan_ioctl_handler);
        return 0;
 
@@ -806,13 +717,8 @@ err0:
 
 static void __exit vlan_cleanup_module(void)
 {
-       unsigned int i;
-
        vlan_ioctl_set(NULL);
 
-       for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
-               dev_remove_offload(&vlan_packet_offloads[i]);
-
        vlan_netlink_fini();
 
        unregister_netdevice_notifier(&vlan_notifier_block);
index 5742504..a313165 100644 (file)
@@ -453,3 +453,102 @@ bool vlan_uses_dev(const struct net_device *dev)
        return vlan_info->grp.nr_vlan_devs ? true : false;
 }
 EXPORT_SYMBOL(vlan_uses_dev);
+
+static struct sk_buff *vlan_gro_receive(struct list_head *head,
+                                       struct sk_buff *skb)
+{
+       const struct packet_offload *ptype;
+       unsigned int hlen, off_vlan;
+       struct sk_buff *pp = NULL;
+       struct vlan_hdr *vhdr;
+       struct sk_buff *p;
+       __be16 type;
+       int flush = 1;
+
+       off_vlan = skb_gro_offset(skb);
+       hlen = off_vlan + sizeof(*vhdr);
+       vhdr = skb_gro_header_fast(skb, off_vlan);
+       if (skb_gro_header_hard(skb, hlen)) {
+               vhdr = skb_gro_header_slow(skb, hlen, off_vlan);
+               if (unlikely(!vhdr))
+                       goto out;
+       }
+
+       type = vhdr->h_vlan_encapsulated_proto;
+
+       rcu_read_lock();
+       ptype = gro_find_receive_by_type(type);
+       if (!ptype)
+               goto out_unlock;
+
+       flush = 0;
+
+       list_for_each_entry(p, head, list) {
+               struct vlan_hdr *vhdr2;
+
+               if (!NAPI_GRO_CB(p)->same_flow)
+                       continue;
+
+               vhdr2 = (struct vlan_hdr *)(p->data + off_vlan);
+               if (compare_vlan_header(vhdr, vhdr2))
+                       NAPI_GRO_CB(p)->same_flow = 0;
+       }
+
+       skb_gro_pull(skb, sizeof(*vhdr));
+       skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
+       pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
+
+out_unlock:
+       rcu_read_unlock();
+out:
+       skb_gro_flush_final(skb, pp, flush);
+
+       return pp;
+}
+
+static int vlan_gro_complete(struct sk_buff *skb, int nhoff)
+{
+       struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff);
+       __be16 type = vhdr->h_vlan_encapsulated_proto;
+       struct packet_offload *ptype;
+       int err = -ENOENT;
+
+       rcu_read_lock();
+       ptype = gro_find_complete_by_type(type);
+       if (ptype)
+               err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(*vhdr));
+
+       rcu_read_unlock();
+       return err;
+}
+
+static struct packet_offload vlan_packet_offloads[] __read_mostly = {
+       {
+               .type = cpu_to_be16(ETH_P_8021Q),
+               .priority = 10,
+               .callbacks = {
+                       .gro_receive = vlan_gro_receive,
+                       .gro_complete = vlan_gro_complete,
+               },
+       },
+       {
+               .type = cpu_to_be16(ETH_P_8021AD),
+               .priority = 10,
+               .callbacks = {
+                       .gro_receive = vlan_gro_receive,
+                       .gro_complete = vlan_gro_complete,
+               },
+       },
+};
+
+static int __init vlan_offload_init(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
+               dev_add_offload(&vlan_packet_offloads[i]);
+
+       return 0;
+}
+
+fs_initcall(vlan_offload_init);
index f235edb..5cb9de1 100644 (file)
@@ -51,6 +51,9 @@ config NET_INGRESS
 config NET_EGRESS
        bool
 
+config SKB_EXTENSIONS
+       bool
+
 menu "Networking options"
 
 source "net/packet/Kconfig"
@@ -184,6 +187,7 @@ config BRIDGE_NETFILTER
        depends on NETFILTER && INET
        depends on NETFILTER_ADVANCED
        select NETFILTER_FAMILY_BRIDGE
+       select SKB_EXTENSIONS
        default m
        ---help---
          Enabling this option will let arptables resp. iptables see bridged
index f75816f..c386e69 100644 (file)
@@ -22,7 +22,6 @@
 config BATMAN_ADV
        tristate "B.A.T.M.A.N. Advanced Meshing Protocol"
        depends on NET
-       select CRC16
        select LIBCRC32C
        help
           B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
@@ -48,6 +47,7 @@ config BATMAN_ADV_BATMAN_V
 config BATMAN_ADV_BLA
        bool "Bridge Loop Avoidance"
        depends on BATMAN_ADV && INET
+       select CRC16
        default y
        help
          This option enables BLA (Bridge Loop Avoidance), a mechanism
@@ -82,6 +82,7 @@ config BATMAN_ADV_NC
 config BATMAN_ADV_MCAST
        bool "Multicast optimisation"
        depends on BATMAN_ADV && INET && !(BRIDGE=m && BATMAN_ADV=y)
+       default y
        help
          This option enables the multicast optimisation which aims to
          reduce the air overhead while improving the reliability of
@@ -100,12 +101,13 @@ config BATMAN_ADV_DEBUGFS
 
 config BATMAN_ADV_DEBUG
        bool "B.A.T.M.A.N. debugging"
-       depends on BATMAN_ADV_DEBUGFS
+       depends on BATMAN_ADV
        help
          This is an option for use by developers; most people should
          say N here. This enables compilation of support for
-         outputting debugging information to the kernel log. The
-         output is controlled via the module parameter debug.
+         outputting debugging information to the debugfs log or tracing
+         buffer. The output is controlled via the batadv netdev specific
+         log_level setting.
 
 config BATMAN_ADV_TRACING
        bool "B.A.T.M.A.N. tracing support"
index d222709..f97e566 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/kernel.h>
 #include <linux/kref.h>
 #include <linux/list.h>
-#include <linux/lockdep.h>
 #include <linux/netdevice.h>
 #include <linux/netlink.h>
 #include <linux/pkt_sched.h>
@@ -2585,13 +2584,14 @@ static void batadv_iv_gw_print(struct batadv_priv *bat_priv,
  * batadv_iv_gw_dump_entry() - Dump a gateway into a message
  * @msg: Netlink message to dump into
  * @portid: Port making netlink request
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @bat_priv: The bat priv with all the soft interface information
  * @gw_node: Gateway to be dumped
  *
  * Return: Error code, or 0 on success
  */
-static int batadv_iv_gw_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+static int batadv_iv_gw_dump_entry(struct sk_buff *msg, u32 portid,
+                                  struct netlink_callback *cb,
                                   struct batadv_priv *bat_priv,
                                   struct batadv_gw_node *gw_node)
 {
@@ -2611,13 +2611,16 @@ static int batadv_iv_gw_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
 
        curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_GATEWAYS);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_GATEWAYS);
        if (!hdr) {
                ret = -ENOBUFS;
                goto out;
        }
 
+       genl_dump_check_consistent(cb, hdr);
+
        ret = -EMSGSIZE;
 
        if (curr_gw == gw_node)
@@ -2668,13 +2671,15 @@ static void batadv_iv_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
        int idx_skip = cb->args[0];
        int idx = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) {
+       spin_lock_bh(&bat_priv->gw.list_lock);
+       cb->seq = bat_priv->gw.generation << 1 | 1;
+
+       hlist_for_each_entry(gw_node, &bat_priv->gw.gateway_list, list) {
                if (idx++ < idx_skip)
                        continue;
 
-               if (batadv_iv_gw_dump_entry(msg, portid, cb->nlh->nlmsg_seq,
-                                           bat_priv, gw_node)) {
+               if (batadv_iv_gw_dump_entry(msg, portid, cb, bat_priv,
+                                           gw_node)) {
                        idx_skip = idx - 1;
                        goto unlock;
                }
@@ -2682,7 +2687,7 @@ static void batadv_iv_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
 
        idx_skip = idx;
 unlock:
-       rcu_read_unlock();
+       spin_unlock_bh(&bat_priv->gw.list_lock);
 
        cb->args[0] = idx_skip;
 }
index 6baec4e..90e33f8 100644 (file)
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/kref.h>
+#include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/netlink.h>
 #include <linux/rculist.h>
 #include <linux/rcupdate.h>
 #include <linux/seq_file.h>
+#include <linux/spinlock.h>
 #include <linux/stddef.h>
 #include <linux/types.h>
 #include <linux/workqueue.h>
@@ -915,13 +917,14 @@ static void batadv_v_gw_print(struct batadv_priv *bat_priv,
  * batadv_v_gw_dump_entry() - Dump a gateway into a message
  * @msg: Netlink message to dump into
  * @portid: Port making netlink request
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @bat_priv: The bat priv with all the soft interface information
  * @gw_node: Gateway to be dumped
  *
  * Return: Error code, or 0 on success
  */
-static int batadv_v_gw_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+static int batadv_v_gw_dump_entry(struct sk_buff *msg, u32 portid,
+                                 struct netlink_callback *cb,
                                  struct batadv_priv *bat_priv,
                                  struct batadv_gw_node *gw_node)
 {
@@ -941,13 +944,16 @@ static int batadv_v_gw_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
 
        curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_GATEWAYS);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_GATEWAYS);
        if (!hdr) {
                ret = -ENOBUFS;
                goto out;
        }
 
+       genl_dump_check_consistent(cb, hdr);
+
        ret = -EMSGSIZE;
 
        if (curr_gw == gw_node) {
@@ -1018,13 +1024,15 @@ static void batadv_v_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
        int idx_skip = cb->args[0];
        int idx = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) {
+       spin_lock_bh(&bat_priv->gw.list_lock);
+       cb->seq = bat_priv->gw.generation << 1 | 1;
+
+       hlist_for_each_entry(gw_node, &bat_priv->gw.gateway_list, list) {
                if (idx++ < idx_skip)
                        continue;
 
-               if (batadv_v_gw_dump_entry(msg, portid, cb->nlh->nlmsg_seq,
-                                          bat_priv, gw_node)) {
+               if (batadv_v_gw_dump_entry(msg, portid, cb, bat_priv,
+                                          gw_node)) {
                        idx_skip = idx - 1;
                        goto unlock;
                }
@@ -1032,7 +1040,7 @@ static void batadv_v_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
 
        idx_skip = idx;
 unlock:
-       rcu_read_unlock();
+       spin_unlock_bh(&bat_priv->gw.list_lock);
 
        cb->args[0] = idx_skip;
 }
index 9f481cf..e8090f0 100644 (file)
@@ -352,19 +352,21 @@ out:
  */
 int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface)
 {
+       static const size_t tvlv_padding = sizeof(__be32);
        struct batadv_elp_packet *elp_packet;
        unsigned char *elp_buff;
        u32 random_seqno;
        size_t size;
        int res = -ENOMEM;
 
-       size = ETH_HLEN + NET_IP_ALIGN + BATADV_ELP_HLEN;
+       size = ETH_HLEN + NET_IP_ALIGN + BATADV_ELP_HLEN + tvlv_padding;
        hard_iface->bat_v.elp_skb = dev_alloc_skb(size);
        if (!hard_iface->bat_v.elp_skb)
                goto out;
 
        skb_reserve(hard_iface->bat_v.elp_skb, ETH_HLEN + NET_IP_ALIGN);
-       elp_buff = skb_put_zero(hard_iface->bat_v.elp_skb, BATADV_ELP_HLEN);
+       elp_buff = skb_put_zero(hard_iface->bat_v.elp_skb,
+                               BATADV_ELP_HLEN + tvlv_padding);
        elp_packet = (struct batadv_elp_packet *)elp_buff;
 
        elp_packet->packet_type = BATADV_ELP;
index 5f1aeed..5fdde29 100644 (file)
@@ -2094,14 +2094,15 @@ out:
  * to a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @primary_if: primary interface
  * @claim: entry to dump
  *
  * Return: 0 or error code.
  */
 static int
-batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid,
+                           struct netlink_callback *cb,
                            struct batadv_hard_iface *primary_if,
                            struct batadv_bla_claim *claim)
 {
@@ -2111,13 +2112,16 @@ batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
        void *hdr;
        int ret = -EINVAL;
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_BLA_CLAIM);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_BLA_CLAIM);
        if (!hdr) {
                ret = -ENOBUFS;
                goto out;
        }
 
+       genl_dump_check_consistent(cb, hdr);
+
        is_own = batadv_compare_eth(claim->backbone_gw->orig,
                                    primary_addr);
 
@@ -2153,28 +2157,33 @@ out:
  * to a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @primary_if: primary interface
- * @head: bucket to dump
+ * @hash: hash to dump
+ * @bucket: bucket index to dump
  * @idx_skip: How many entries to skip
  *
  * Return: always 0.
  */
 static int
-batadv_bla_claim_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_bla_claim_dump_bucket(struct sk_buff *msg, u32 portid,
+                            struct netlink_callback *cb,
                             struct batadv_hard_iface *primary_if,
-                            struct hlist_head *head, int *idx_skip)
+                            struct batadv_hashtable *hash, unsigned int bucket,
+                            int *idx_skip)
 {
        struct batadv_bla_claim *claim;
        int idx = 0;
        int ret = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(claim, head, hash_entry) {
+       spin_lock_bh(&hash->list_locks[bucket]);
+       cb->seq = atomic_read(&hash->generation) << 1 | 1;
+
+       hlist_for_each_entry(claim, &hash->table[bucket], hash_entry) {
                if (idx++ < *idx_skip)
                        continue;
 
-               ret = batadv_bla_claim_dump_entry(msg, portid, seq,
+               ret = batadv_bla_claim_dump_entry(msg, portid, cb,
                                                  primary_if, claim);
                if (ret) {
                        *idx_skip = idx - 1;
@@ -2184,7 +2193,7 @@ batadv_bla_claim_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
 
        *idx_skip = 0;
 unlock:
-       rcu_read_unlock();
+       spin_unlock_bh(&hash->list_locks[bucket]);
        return ret;
 }
 
@@ -2204,7 +2213,6 @@ int batadv_bla_claim_dump(struct sk_buff *msg, struct netlink_callback *cb)
        struct batadv_hashtable *hash;
        struct batadv_priv *bat_priv;
        int bucket = cb->args[0];
-       struct hlist_head *head;
        int idx = cb->args[1];
        int ifindex;
        int ret = 0;
@@ -2230,11 +2238,8 @@ int batadv_bla_claim_dump(struct sk_buff *msg, struct netlink_callback *cb)
        }
 
        while (bucket < hash->size) {
-               head = &hash->table[bucket];
-
-               if (batadv_bla_claim_dump_bucket(msg, portid,
-                                                cb->nlh->nlmsg_seq,
-                                                primary_if, head, &idx))
+               if (batadv_bla_claim_dump_bucket(msg, portid, cb, primary_if,
+                                                hash, bucket, &idx))
                        break;
                bucket++;
        }
@@ -2325,14 +2330,15 @@ out:
  *  netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @primary_if: primary interface
  * @backbone_gw: entry to dump
  *
  * Return: 0 or error code.
  */
 static int
-batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid,
+                              struct netlink_callback *cb,
                               struct batadv_hard_iface *primary_if,
                               struct batadv_bla_backbone_gw *backbone_gw)
 {
@@ -2343,13 +2349,16 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
        void *hdr;
        int ret = -EINVAL;
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_BLA_BACKBONE);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_BLA_BACKBONE);
        if (!hdr) {
                ret = -ENOBUFS;
                goto out;
        }
 
+       genl_dump_check_consistent(cb, hdr);
+
        is_own = batadv_compare_eth(backbone_gw->orig, primary_addr);
 
        spin_lock_bh(&backbone_gw->crc_lock);
@@ -2386,28 +2395,33 @@ out:
  *  a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @primary_if: primary interface
- * @head: bucket to dump
+ * @hash: hash to dump
+ * @bucket: bucket index to dump
  * @idx_skip: How many entries to skip
  *
  * Return: always 0.
  */
 static int
-batadv_bla_backbone_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_bla_backbone_dump_bucket(struct sk_buff *msg, u32 portid,
+                               struct netlink_callback *cb,
                                struct batadv_hard_iface *primary_if,
-                               struct hlist_head *head, int *idx_skip)
+                               struct batadv_hashtable *hash,
+                               unsigned int bucket, int *idx_skip)
 {
        struct batadv_bla_backbone_gw *backbone_gw;
        int idx = 0;
        int ret = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(backbone_gw, head, hash_entry) {
+       spin_lock_bh(&hash->list_locks[bucket]);
+       cb->seq = atomic_read(&hash->generation) << 1 | 1;
+
+       hlist_for_each_entry(backbone_gw, &hash->table[bucket], hash_entry) {
                if (idx++ < *idx_skip)
                        continue;
 
-               ret = batadv_bla_backbone_dump_entry(msg, portid, seq,
+               ret = batadv_bla_backbone_dump_entry(msg, portid, cb,
                                                     primary_if, backbone_gw);
                if (ret) {
                        *idx_skip = idx - 1;
@@ -2417,7 +2431,7 @@ batadv_bla_backbone_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
 
        *idx_skip = 0;
 unlock:
-       rcu_read_unlock();
+       spin_unlock_bh(&hash->list_locks[bucket]);
        return ret;
 }
 
@@ -2437,7 +2451,6 @@ int batadv_bla_backbone_dump(struct sk_buff *msg, struct netlink_callback *cb)
        struct batadv_hashtable *hash;
        struct batadv_priv *bat_priv;
        int bucket = cb->args[0];
-       struct hlist_head *head;
        int idx = cb->args[1];
        int ifindex;
        int ret = 0;
@@ -2463,11 +2476,8 @@ int batadv_bla_backbone_dump(struct sk_buff *msg, struct netlink_callback *cb)
        }
 
        while (bucket < hash->size) {
-               head = &hash->table[bucket];
-
-               if (batadv_bla_backbone_dump_bucket(msg, portid,
-                                                   cb->nlh->nlmsg_seq,
-                                                   primary_if, head, &idx))
+               if (batadv_bla_backbone_dump_bucket(msg, portid, cb, primary_if,
+                                                   hash, bucket, &idx))
                        break;
                bucket++;
        }
index 8b608a2..d4a7702 100644 (file)
@@ -19,6 +19,7 @@
 #include "debugfs.h"
 #include "main.h"
 
+#include <asm/current.h>
 #include <linux/dcache.h>
 #include <linux/debugfs.h>
 #include <linux/err.h>
@@ -27,6 +28,7 @@
 #include <linux/fs.h>
 #include <linux/netdevice.h>
 #include <linux/printk.h>
+#include <linux/sched.h>
 #include <linux/seq_file.h>
 #include <linux/stat.h>
 #include <linux/stddef.h>
index a60bacf..b9ffe18 100644 (file)
@@ -863,23 +863,27 @@ out:
  *  netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @dat_entry: entry to dump
  *
  * Return: 0 or error code.
  */
 static int
-batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
+                           struct netlink_callback *cb,
                            struct batadv_dat_entry *dat_entry)
 {
        int msecs;
        void *hdr;
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_DAT_CACHE);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_DAT_CACHE);
        if (!hdr)
                return -ENOBUFS;
 
+       genl_dump_check_consistent(cb, hdr);
+
        msecs = jiffies_to_msecs(jiffies - dat_entry->last_update);
 
        if (nla_put_in_addr(msg, BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
@@ -901,27 +905,31 @@ batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
  *  a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
- * @head: bucket to dump
+ * @cb: Control block containing additional options
+ * @hash: hash to dump
+ * @bucket: bucket index to dump
  * @idx_skip: How many entries to skip
  *
  * Return: 0 or error code.
  */
 static int
-batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
-                            struct hlist_head *head, int *idx_skip)
+batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid,
+                            struct netlink_callback *cb,
+                            struct batadv_hashtable *hash, unsigned int bucket,
+                            int *idx_skip)
 {
        struct batadv_dat_entry *dat_entry;
        int idx = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(dat_entry, head, hash_entry) {
+       spin_lock_bh(&hash->list_locks[bucket]);
+       cb->seq = atomic_read(&hash->generation) << 1 | 1;
+
+       hlist_for_each_entry(dat_entry, &hash->table[bucket], hash_entry) {
                if (idx < *idx_skip)
                        goto skip;
 
-               if (batadv_dat_cache_dump_entry(msg, portid, seq,
-                                               dat_entry)) {
-                       rcu_read_unlock();
+               if (batadv_dat_cache_dump_entry(msg, portid, cb, dat_entry)) {
+                       spin_unlock_bh(&hash->list_locks[bucket]);
                        *idx_skip = idx;
 
                        return -EMSGSIZE;
@@ -930,7 +938,7 @@ batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
 skip:
                idx++;
        }
-       rcu_read_unlock();
+       spin_unlock_bh(&hash->list_locks[bucket]);
 
        return 0;
 }
@@ -951,7 +959,6 @@ int batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb)
        struct batadv_hashtable *hash;
        struct batadv_priv *bat_priv;
        int bucket = cb->args[0];
-       struct hlist_head *head;
        int idx = cb->args[1];
        int ifindex;
        int ret = 0;
@@ -977,10 +984,7 @@ int batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb)
        }
 
        while (bucket < hash->size) {
-               head = &hash->table[bucket];
-
-               if (batadv_dat_cache_dump_bucket(msg, portid,
-                                                cb->nlh->nlmsg_seq, head,
+               if (batadv_dat_cache_dump_bucket(msg, portid, cb, hash, bucket,
                                                 &idx))
                        break;
 
index 0fddc17..5b71a28 100644 (file)
@@ -275,7 +275,7 @@ batadv_frag_merge_packets(struct hlist_head *chain)
        kfree(entry);
 
        packet = (struct batadv_frag_packet *)skb_out->data;
-       size = ntohs(packet->total_size);
+       size = ntohs(packet->total_size) + hdr_size;
 
        /* Make room for the rest of the fragments. */
        if (pskb_expand_head(skb_out, 0, size - skb_out->len, GFP_ATOMIC) < 0) {
index 140c61a..9d8e5ed 100644 (file)
@@ -377,6 +377,7 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv,
 
        kref_get(&gw_node->refcount);
        hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.gateway_list);
+       bat_priv->gw.generation++;
 
        batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                   "Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n",
@@ -472,6 +473,7 @@ void batadv_gw_node_update(struct batadv_priv *bat_priv,
                if (!hlist_unhashed(&gw_node->list)) {
                        hlist_del_init_rcu(&gw_node->list);
                        batadv_gw_node_put(gw_node);
+                       bat_priv->gw.generation++;
                }
                spin_unlock_bh(&bat_priv->gw.list_lock);
 
@@ -518,6 +520,7 @@ void batadv_gw_node_free(struct batadv_priv *bat_priv)
                                  &bat_priv->gw.gateway_list, list) {
                hlist_del_init_rcu(&gw_node->list);
                batadv_gw_node_put(gw_node);
+               bat_priv->gw.generation++;
        }
        spin_unlock_bh(&bat_priv->gw.list_lock);
 }
index 781c5b6..508f441 100644 (file)
@@ -951,6 +951,7 @@ batadv_hardif_add_interface(struct net_device *net_dev)
        batadv_check_known_mac_addr(hard_iface->net_dev);
        kref_get(&hard_iface->refcount);
        list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
+       batadv_hardif_generation++;
 
        return hard_iface;
 
@@ -993,6 +994,7 @@ void batadv_hardif_remove_interfaces(void)
        list_for_each_entry_safe(hard_iface, hard_iface_tmp,
                                 &batadv_hardif_list, list) {
                list_del_rcu(&hard_iface->list);
+               batadv_hardif_generation++;
                batadv_hardif_remove_interface(hard_iface);
        }
        rtnl_unlock();
@@ -1054,6 +1056,7 @@ static int batadv_hard_if_event(struct notifier_block *this,
        case NETDEV_UNREGISTER:
        case NETDEV_PRE_TYPE_CHANGE:
                list_del_rcu(&hard_iface->list);
+               batadv_hardif_generation++;
 
                batadv_hardif_remove_interface(hard_iface);
                break;
index 7b49e40..9194f4d 100644 (file)
@@ -32,6 +32,8 @@ static void batadv_hash_init(struct batadv_hashtable *hash)
                INIT_HLIST_HEAD(&hash->table[i]);
                spin_lock_init(&hash->list_locks[i]);
        }
+
+       atomic_set(&hash->generation, 0);
 }
 
 /**
index 9490a7c..0e36fa1 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "main.h"
 
+#include <linux/atomic.h>
 #include <linux/compiler.h>
 #include <linux/list.h>
 #include <linux/rculist.h>
@@ -58,6 +59,9 @@ struct batadv_hashtable {
 
        /** @size: size of hashtable */
        u32 size;
+
+       /** @generation: current (generation) sequence number */
+       atomic_t generation;
 };
 
 /* allocates and clears the hash */
@@ -112,6 +116,7 @@ static inline int batadv_hash_add(struct batadv_hashtable *hash,
 
        /* no duplicate found in list, add new element */
        hlist_add_head_rcu(data_node, head);
+       atomic_inc(&hash->generation);
 
        ret = 0;
 
@@ -154,6 +159,7 @@ static inline void *batadv_hash_remove(struct batadv_hashtable *hash,
 
                data_save = node;
                hlist_del_rcu(node);
+               atomic_inc(&hash->generation);
                break;
        }
        spin_unlock_bh(&hash->list_locks[index]);
index 6beb5f0..02e55b7 100644 (file)
@@ -43,6 +43,8 @@
 #include "debugfs.h"
 #include "trace.h"
 
+#ifdef CONFIG_BATMAN_ADV_DEBUGFS
+
 #define BATADV_LOG_BUFF_MASK (batadv_log_buff_len - 1)
 
 static const int batadv_log_buff_len = BATADV_LOG_BUF_LEN;
@@ -92,33 +94,6 @@ static int batadv_fdebug_log(struct batadv_priv_debug_log *debug_log,
        return 0;
 }
 
-/**
- * batadv_debug_log() - Add debug log entry
- * @bat_priv: the bat priv with all the soft interface information
- * @fmt: format string
- *
- * Return: 0 on success or negative error number in case of failure
- */
-int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...)
-{
-       struct va_format vaf;
-       va_list args;
-
-       va_start(args, fmt);
-
-       vaf.fmt = fmt;
-       vaf.va = &args;
-
-       batadv_fdebug_log(bat_priv->debug_log, "[%10u] %pV",
-                         jiffies_to_msecs(jiffies), &vaf);
-
-       trace_batadv_dbg(bat_priv, &vaf);
-
-       va_end(args);
-
-       return 0;
-}
-
 static int batadv_log_open(struct inode *inode, struct file *file)
 {
        if (!try_module_get(THIS_MODULE))
@@ -259,3 +234,34 @@ void batadv_debug_log_cleanup(struct batadv_priv *bat_priv)
        kfree(bat_priv->debug_log);
        bat_priv->debug_log = NULL;
 }
+
+#endif /* CONFIG_BATMAN_ADV_DEBUGFS */
+
+/**
+ * batadv_debug_log() - Add debug log entry
+ * @bat_priv: the bat priv with all the soft interface information
+ * @fmt: format string
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+#ifdef CONFIG_BATMAN_ADV_DEBUGFS
+       batadv_fdebug_log(bat_priv->debug_log, "[%10u] %pV",
+                         jiffies_to_msecs(jiffies), &vaf);
+#endif
+
+       trace_batadv_dbg(bat_priv, &vaf);
+
+       va_end(args);
+
+       return 0;
+}
index 69c0d85..d1ed839 100644 (file)
@@ -74,6 +74,7 @@
  * list traversals just rcu-locked
  */
 struct list_head batadv_hardif_list;
+unsigned int batadv_hardif_generation;
 static int (*batadv_rx_handler[256])(struct sk_buff *skb,
                                     struct batadv_hard_iface *recv_if);
 
@@ -186,6 +187,8 @@ int batadv_mesh_init(struct net_device *soft_iface)
        INIT_HLIST_HEAD(&bat_priv->softif_vlan_list);
        INIT_HLIST_HEAD(&bat_priv->tp_list);
 
+       bat_priv->gw.generation = 0;
+
        ret = batadv_v_mesh_init(bat_priv);
        if (ret < 0)
                goto err;
index 2002b70..b572066 100644 (file)
@@ -25,7 +25,7 @@
 #define BATADV_DRIVER_DEVICE "batman-adv"
 
 #ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2018.4"
+#define BATADV_SOURCE_VERSION "2019.0"
 #endif
 
 /* B.A.T.M.A.N. parameters */
@@ -247,6 +247,7 @@ static inline int batadv_print_vid(unsigned short vid)
 }
 
 extern struct list_head batadv_hardif_list;
+extern unsigned int batadv_hardif_generation;
 
 extern unsigned char batadv_broadcast_addr[];
 extern struct workqueue_struct *batadv_event_workqueue;
index 86725d7..69244e4 100644 (file)
@@ -1365,22 +1365,26 @@ int batadv_mcast_mesh_info_put(struct sk_buff *msg,
  *  to a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @orig_node: originator to dump the multicast flags of
  *
  * Return: 0 or error code.
  */
 static int
-batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid,
+                             struct netlink_callback *cb,
                              struct batadv_orig_node *orig_node)
 {
        void *hdr;
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_MCAST_FLAGS);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_MCAST_FLAGS);
        if (!hdr)
                return -ENOBUFS;
 
+       genl_dump_check_consistent(cb, hdr);
+
        if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
                    orig_node->orig)) {
                genlmsg_cancel(msg, hdr);
@@ -1405,21 +1409,26 @@ batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
  *  table to a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
- * @head: bucket to dump
+ * @cb: Control block containing additional options
+ * @hash: hash to dump
+ * @bucket: bucket index to dump
  * @idx_skip: How many entries to skip
  *
  * Return: 0 or error code.
  */
 static int
-batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
-                              struct hlist_head *head, long *idx_skip)
+batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid,
+                              struct netlink_callback *cb,
+                              struct batadv_hashtable *hash,
+                              unsigned int bucket, long *idx_skip)
 {
        struct batadv_orig_node *orig_node;
        long idx = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
+       spin_lock_bh(&hash->list_locks[bucket]);
+       cb->seq = atomic_read(&hash->generation) << 1 | 1;
+
+       hlist_for_each_entry(orig_node, &hash->table[bucket], hash_entry) {
                if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST,
                              &orig_node->capa_initialized))
                        continue;
@@ -1427,9 +1436,8 @@ batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
                if (idx < *idx_skip)
                        goto skip;
 
-               if (batadv_mcast_flags_dump_entry(msg, portid, seq,
-                                                 orig_node)) {
-                       rcu_read_unlock();
+               if (batadv_mcast_flags_dump_entry(msg, portid, cb, orig_node)) {
+                       spin_unlock_bh(&hash->list_locks[bucket]);
                        *idx_skip = idx;
 
                        return -EMSGSIZE;
@@ -1438,7 +1446,7 @@ batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
 skip:
                idx++;
        }
-       rcu_read_unlock();
+       spin_unlock_bh(&hash->list_locks[bucket]);
 
        return 0;
 }
@@ -1447,7 +1455,7 @@ skip:
  * __batadv_mcast_flags_dump() - dump multicast flags table to a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @bat_priv: the bat priv with all the soft interface information
  * @bucket: current bucket to dump
  * @idx: index in current bucket to the next entry to dump
@@ -1455,19 +1463,17 @@ skip:
  * Return: 0 or error code.
  */
 static int
-__batadv_mcast_flags_dump(struct sk_buff *msg, u32 portid, u32 seq,
+__batadv_mcast_flags_dump(struct sk_buff *msg, u32 portid,
+                         struct netlink_callback *cb,
                          struct batadv_priv *bat_priv, long *bucket, long *idx)
 {
        struct batadv_hashtable *hash = bat_priv->orig_hash;
        long bucket_tmp = *bucket;
-       struct hlist_head *head;
        long idx_tmp = *idx;
 
        while (bucket_tmp < hash->size) {
-               head = &hash->table[bucket_tmp];
-
-               if (batadv_mcast_flags_dump_bucket(msg, portid, seq, head,
-                                                  &idx_tmp))
+               if (batadv_mcast_flags_dump_bucket(msg, portid, cb, hash,
+                                                  *bucket, &idx_tmp))
                        break;
 
                bucket_tmp++;
@@ -1550,8 +1556,7 @@ int batadv_mcast_flags_dump(struct sk_buff *msg, struct netlink_callback *cb)
                return ret;
 
        bat_priv = netdev_priv(primary_if->soft_iface);
-       ret = __batadv_mcast_flags_dump(msg, portid, cb->nlh->nlmsg_seq,
-                                       bat_priv, bucket, idx);
+       ret = __batadv_mcast_flags_dump(msg, portid, cb, bat_priv, bucket, idx);
 
        batadv_hardif_put(primary_if);
        return ret;
index 0d9459b..2dc3304 100644 (file)
 #include <linux/if_ether.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/netlink.h>
 #include <linux/printk.h>
-#include <linux/rculist.h>
-#include <linux/rcupdate.h>
+#include <linux/rtnetlink.h>
 #include <linux/skbuff.h>
 #include <linux/stddef.h>
 #include <linux/types.h>
@@ -445,23 +445,27 @@ out:
  * batadv_netlink_dump_hardif_entry() - Dump one hard interface into a message
  * @msg: Netlink message to dump into
  * @portid: Port making netlink request
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @hard_iface: Hard interface to dump
  *
  * Return: error code, or 0 on success
  */
 static int
-batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid,
+                                struct netlink_callback *cb,
                                 struct batadv_hard_iface *hard_iface)
 {
        struct net_device *net_dev = hard_iface->net_dev;
        void *hdr;
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI,
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
                          BATADV_CMD_GET_HARDIFS);
        if (!hdr)
                return -EMSGSIZE;
 
+       genl_dump_check_consistent(cb, hdr);
+
        if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
                        net_dev->ifindex) ||
            nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
@@ -498,7 +502,6 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
        struct batadv_hard_iface *hard_iface;
        int ifindex;
        int portid = NETLINK_CB(cb->skb).portid;
-       int seq = cb->nlh->nlmsg_seq;
        int skip = cb->args[0];
        int i = 0;
 
@@ -516,23 +519,24 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
                return -ENODEV;
        }
 
-       rcu_read_lock();
+       rtnl_lock();
+       cb->seq = batadv_hardif_generation << 1 | 1;
 
-       list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
+       list_for_each_entry(hard_iface, &batadv_hardif_list, list) {
                if (hard_iface->soft_iface != soft_iface)
                        continue;
 
                if (i++ < skip)
                        continue;
 
-               if (batadv_netlink_dump_hardif_entry(msg, portid, seq,
+               if (batadv_netlink_dump_hardif_entry(msg, portid, cb,
                                                     hard_iface)) {
                        i--;
                        break;
                }
        }
 
-       rcu_read_unlock();
+       rtnl_unlock();
 
        dev_put(soft_iface);
 
index 3d57f99..8e10242 100644 (file)
@@ -16,7 +16,5 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <linux/module.h>
-
 #define CREATE_TRACE_POINTS
 #include "trace.h"
index 3acda26..104784b 100644 (file)
 
 #include "main.h"
 
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/percpu.h>
+#include <linux/printk.h>
 #include <linux/tracepoint.h>
+#include <linux/types.h>
 
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM batadv
index d21624c..8dcd496 100644 (file)
@@ -1145,14 +1145,15 @@ out:
  * batadv_tt_local_dump_entry() - Dump one TT local entry into a message
  * @msg :Netlink message to dump into
  * @portid: Port making netlink request
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @bat_priv: The bat priv with all the soft interface information
  * @common: tt local & tt global common data
  *
  * Return: Error code, or 0 on success
  */
 static int
-batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid,
+                          struct netlink_callback *cb,
                           struct batadv_priv *bat_priv,
                           struct batadv_tt_common_entry *common)
 {
@@ -1173,12 +1174,14 @@ batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
 
        batadv_softif_vlan_put(vlan);
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI,
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family,  NLM_F_MULTI,
                          BATADV_CMD_GET_TRANSTABLE_LOCAL);
        if (!hdr)
                return -ENOBUFS;
 
+       genl_dump_check_consistent(cb, hdr);
+
        if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
            nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
            nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
@@ -1201,34 +1204,39 @@ batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
  * batadv_tt_local_dump_bucket() - Dump one TT local bucket into a message
  * @msg: Netlink message to dump into
  * @portid: Port making netlink request
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @bat_priv: The bat priv with all the soft interface information
- * @head: Pointer to the list containing the local tt entries
+ * @hash: hash to dump
+ * @bucket: bucket index to dump
  * @idx_s: Number of entries to skip
  *
  * Return: Error code, or 0 on success
  */
 static int
-batadv_tt_local_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_tt_local_dump_bucket(struct sk_buff *msg, u32 portid,
+                           struct netlink_callback *cb,
                            struct batadv_priv *bat_priv,
-                           struct hlist_head *head, int *idx_s)
+                           struct batadv_hashtable *hash, unsigned int bucket,
+                           int *idx_s)
 {
        struct batadv_tt_common_entry *common;
        int idx = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(common, head, hash_entry) {
+       spin_lock_bh(&hash->list_locks[bucket]);
+       cb->seq = atomic_read(&hash->generation) << 1 | 1;
+
+       hlist_for_each_entry(common, &hash->table[bucket], hash_entry) {
                if (idx++ < *idx_s)
                        continue;
 
-               if (batadv_tt_local_dump_entry(msg, portid, seq, bat_priv,
+               if (batadv_tt_local_dump_entry(msg, portid, cb, bat_priv,
                                               common)) {
-                       rcu_read_unlock();
+                       spin_unlock_bh(&hash->list_locks[bucket]);
                        *idx_s = idx - 1;
                        return -EMSGSIZE;
                }
        }
-       rcu_read_unlock();
+       spin_unlock_bh(&hash->list_locks[bucket]);
 
        *idx_s = 0;
        return 0;
@@ -1248,7 +1256,6 @@ int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
        struct batadv_priv *bat_priv;
        struct batadv_hard_iface *primary_if = NULL;
        struct batadv_hashtable *hash;
-       struct hlist_head *head;
        int ret;
        int ifindex;
        int bucket = cb->args[0];
@@ -1276,10 +1283,8 @@ int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
        hash = bat_priv->tt.local_hash;
 
        while (bucket < hash->size) {
-               head = &hash->table[bucket];
-
-               if (batadv_tt_local_dump_bucket(msg, portid, cb->nlh->nlmsg_seq,
-                                               bat_priv, head, &idx))
+               if (batadv_tt_local_dump_bucket(msg, portid, cb, bat_priv,
+                                               hash, bucket, &idx))
                        break;
 
                bucket++;
index 45b5592..cbe17da 100644 (file)
@@ -1096,12 +1096,15 @@ struct batadv_priv_gw {
        /** @gateway_list: list of available gateway nodes */
        struct hlist_head gateway_list;
 
-       /** @list_lock: lock protecting gateway_list & curr_gw */
+       /** @list_lock: lock protecting gateway_list, curr_gw, generation */
        spinlock_t list_lock;
 
        /** @curr_gw: pointer to currently selected gateway node */
        struct batadv_gw_node __rcu *curr_gw;
 
+       /** @generation: current (generation) sequence number */
+       unsigned int generation;
+
        /**
         * @mode: gateway operation: off, client or server (see batadv_gw_modes)
         */
index 828e87f..9d79c7d 100644 (file)
@@ -607,7 +607,7 @@ static void ifup(struct net_device *netdev)
        int err;
 
        rtnl_lock();
-       err = dev_open(netdev);
+       err = dev_open(netdev, NULL);
        if (err < 0)
                BT_INFO("iface %s cannot be opened (%d)", netdev->name, err);
        rtnl_unlock();
index ef9928d..ac2826c 100644 (file)
@@ -5711,6 +5711,12 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
                return true;
        }
 
+       /* Check if request ended in Command Status - no way to retreive
+        * any extra parameters in this case.
+        */
+       if (hdr->evt == HCI_EV_CMD_STATUS)
+               return false;
+
        if (hdr->evt != HCI_EV_CMD_COMPLETE) {
                bt_dev_err(hdev, "last event is not cmd complete (0x%2.2x)",
                           hdr->evt);
index e8c9ef1..ca73d36 100644 (file)
@@ -1556,7 +1556,7 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
        connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
                      mgmt_get_connectable(hdev);
 
-        if (!is_advertising_allowed(hdev, connectable))
+       if (!is_advertising_allowed(hdev, connectable))
                return -EPERM;
 
        /* Set require_privacy to true only when non-connectable
index 2146e0f..2a7fb51 100644 (file)
@@ -7650,17 +7650,7 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
        return 0;
 }
 
-static int l2cap_debugfs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, l2cap_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations l2cap_debugfs_fops = {
-       .open           = l2cap_debugfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(l2cap_debugfs);
 
 static struct dentry *l2cap_debugfs;
 
index b98225d..1a635df 100644 (file)
@@ -2166,17 +2166,7 @@ static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
        return 0;
 }
 
-static int rfcomm_dlc_debugfs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, rfcomm_dlc_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations rfcomm_dlc_debugfs_fops = {
-       .open           = rfcomm_dlc_debugfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(rfcomm_dlc_debugfs);
 
 static struct dentry *rfcomm_dlc_debugfs;
 
index d606e92..aa0db1d 100644 (file)
@@ -1020,17 +1020,7 @@ static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p)
        return 0;
 }
 
-static int rfcomm_sock_debugfs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, rfcomm_sock_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations rfcomm_sock_debugfs_fops = {
-       .open           = rfcomm_sock_debugfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(rfcomm_sock_debugfs);
 
 static struct dentry *rfcomm_sock_debugfs;
 
index 8f0f927..529b389 100644 (file)
@@ -1173,17 +1173,7 @@ static int sco_debugfs_show(struct seq_file *f, void *p)
        return 0;
 }
 
-static int sco_debugfs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, sco_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations sco_debugfs_fops = {
-       .open           = sco_debugfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(sco_debugfs);
 
 static struct dentry *sco_debugfs;
 
index c89c22c..fa2644d 100644 (file)
@@ -28,12 +28,13 @@ static __always_inline u32 bpf_test_run_one(struct bpf_prog *prog, void *ctx,
        return ret;
 }
 
-static u32 bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *time)
+static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *ret,
+                       u32 *time)
 {
        struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE] = { 0 };
        enum bpf_cgroup_storage_type stype;
        u64 time_start, time_spent = 0;
-       u32 ret = 0, i;
+       u32 i;
 
        for_each_cgroup_storage_type(stype) {
                storage[stype] = bpf_cgroup_storage_alloc(prog, stype);
@@ -49,7 +50,7 @@ static u32 bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *time)
                repeat = 1;
        time_start = ktime_get_ns();
        for (i = 0; i < repeat; i++) {
-               ret = bpf_test_run_one(prog, ctx, storage);
+               *ret = bpf_test_run_one(prog, ctx, storage);
                if (need_resched()) {
                        if (signal_pending(current))
                                break;
@@ -65,7 +66,7 @@ static u32 bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *time)
        for_each_cgroup_storage_type(stype)
                bpf_cgroup_storage_free(storage[stype]);
 
-       return ret;
+       return 0;
 }
 
 static int bpf_test_finish(const union bpf_attr *kattr,
@@ -74,8 +75,18 @@ static int bpf_test_finish(const union bpf_attr *kattr,
 {
        void __user *data_out = u64_to_user_ptr(kattr->test.data_out);
        int err = -EFAULT;
+       u32 copy_size = size;
+
+       /* Clamp copy if the user has provided a size hint, but copy the full
+        * buffer if not to retain old behaviour.
+        */
+       if (kattr->test.data_size_out &&
+           copy_size > kattr->test.data_size_out) {
+               copy_size = kattr->test.data_size_out;
+               err = -ENOSPC;
+       }
 
-       if (data_out && copy_to_user(data_out, data, size))
+       if (data_out && copy_to_user(data_out, data, copy_size))
                goto out;
        if (copy_to_user(&uattr->test.data_size_out, &size, sizeof(size)))
                goto out;
@@ -83,7 +94,8 @@ static int bpf_test_finish(const union bpf_attr *kattr,
                goto out;
        if (copy_to_user(&uattr->test.duration, &duration, sizeof(duration)))
                goto out;
-       err = 0;
+       if (err != -ENOSPC)
+               err = 0;
 out:
        return err;
 }
@@ -165,7 +177,12 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
                __skb_push(skb, hh_len);
        if (is_direct_pkt_access)
                bpf_compute_data_pointers(skb);
-       retval = bpf_test_run(prog, skb, repeat, &duration);
+       ret = bpf_test_run(prog, skb, repeat, &retval, &duration);
+       if (ret) {
+               kfree_skb(skb);
+               kfree(sk);
+               return ret;
+       }
        if (!is_l2) {
                if (skb_headroom(skb) < hh_len) {
                        int nhead = HH_DATA_ALIGN(hh_len - skb_headroom(skb));
@@ -212,11 +229,14 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,
        rxqueue = __netif_get_rx_queue(current->nsproxy->net_ns->loopback_dev, 0);
        xdp.rxq = &rxqueue->xdp_rxq;
 
-       retval = bpf_test_run(prog, &xdp, repeat, &duration);
+       ret = bpf_test_run(prog, &xdp, repeat, &retval, &duration);
+       if (ret)
+               goto out;
        if (xdp.data != data + XDP_PACKET_HEADROOM + NET_IP_ALIGN ||
            xdp.data_end != xdp.data + size)
                size = xdp.data_end - xdp.data;
        ret = bpf_test_finish(kattr, uattr, xdp.data, size, retval, duration);
+out:
        kfree(data);
        return ret;
 }
index 360ad66..a5174e5 100644 (file)
@@ -31,6 +31,8 @@
  */
 static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
 {
+       struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
+       struct netdev_notifier_pre_changeaddr_info *prechaddr_info;
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net_bridge_port *p;
        struct net_bridge *br;
@@ -56,6 +58,17 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
                br_mtu_auto_adjust(br);
                break;
 
+       case NETDEV_PRE_CHANGEADDR:
+               if (br->dev->addr_assign_type == NET_ADDR_SET)
+                       break;
+               prechaddr_info = ptr;
+               err = dev_pre_changeaddr_notify(br->dev,
+                                               prechaddr_info->dev_addr,
+                                               extack);
+               if (err)
+                       return notifier_from_errno(err);
+               break;
+
        case NETDEV_CHANGEADDR:
                spin_lock_bh(&br->lock);
                br_fdb_changeaddr(p, dev->dev_addr);
@@ -175,6 +188,82 @@ static struct notifier_block br_switchdev_notifier = {
        .notifier_call = br_switchdev_event,
 };
 
+/* br_boolopt_toggle - change user-controlled boolean option
+ *
+ * @br: bridge device
+ * @opt: id of the option to change
+ * @on: new option value
+ * @extack: extack for error messages
+ *
+ * Changes the value of the respective boolean option to @on taking care of
+ * any internal option value mapping and configuration.
+ */
+int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
+                     struct netlink_ext_ack *extack)
+{
+       switch (opt) {
+       case BR_BOOLOPT_NO_LL_LEARN:
+               br_opt_toggle(br, BROPT_NO_LL_LEARN, on);
+               break;
+       default:
+               /* shouldn't be called with unsupported options */
+               WARN_ON(1);
+               break;
+       }
+
+       return 0;
+}
+
+int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
+{
+       switch (opt) {
+       case BR_BOOLOPT_NO_LL_LEARN:
+               return br_opt_get(br, BROPT_NO_LL_LEARN);
+       default:
+               /* shouldn't be called with unsupported options */
+               WARN_ON(1);
+               break;
+       }
+
+       return 0;
+}
+
+int br_boolopt_multi_toggle(struct net_bridge *br,
+                           struct br_boolopt_multi *bm,
+                           struct netlink_ext_ack *extack)
+{
+       unsigned long bitmap = bm->optmask;
+       int err = 0;
+       int opt_id;
+
+       for_each_set_bit(opt_id, &bitmap, BR_BOOLOPT_MAX) {
+               bool on = !!(bm->optval & BIT(opt_id));
+
+               err = br_boolopt_toggle(br, opt_id, on, extack);
+               if (err) {
+                       br_debug(br, "boolopt multi-toggle error: option: %d current: %d new: %d error: %d\n",
+                                opt_id, br_boolopt_get(br, opt_id), on, err);
+                       break;
+               }
+       }
+
+       return err;
+}
+
+void br_boolopt_multi_get(const struct net_bridge *br,
+                         struct br_boolopt_multi *bm)
+{
+       u32 optval = 0;
+       int opt_id;
+
+       for (opt_id = 0; opt_id < BR_BOOLOPT_MAX; opt_id++)
+               optval |= (br_boolopt_get(br, opt_id) << opt_id);
+
+       bm->optval = optval;
+       bm->optmask = GENMASK((BR_BOOLOPT_MAX - 1), 0);
+}
+
+/* private bridge options, controlled by the kernel */
 void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on)
 {
        bool cur = !!br_opt_get(br, opt);
index c6abf92..013323b 100644 (file)
@@ -131,9 +131,17 @@ static int br_dev_init(struct net_device *dev)
                return err;
        }
 
+       err = br_mdb_hash_init(br);
+       if (err) {
+               free_percpu(br->stats);
+               br_fdb_hash_fini(br);
+               return err;
+       }
+
        err = br_vlan_init(br);
        if (err) {
                free_percpu(br->stats);
+               br_mdb_hash_fini(br);
                br_fdb_hash_fini(br);
                return err;
        }
@@ -142,6 +150,7 @@ static int br_dev_init(struct net_device *dev)
        if (err) {
                free_percpu(br->stats);
                br_vlan_flush(br);
+               br_mdb_hash_fini(br);
                br_fdb_hash_fini(br);
        }
        br_set_lockdep_class(dev);
@@ -156,6 +165,7 @@ static void br_dev_uninit(struct net_device *dev)
        br_multicast_dev_del(br);
        br_multicast_uninit_stats(br);
        br_vlan_flush(br);
+       br_mdb_hash_fini(br);
        br_fdb_hash_fini(br);
        free_percpu(br->stats);
 }
@@ -393,6 +403,7 @@ static const struct net_device_ops br_netdev_ops = {
        .ndo_fdb_add             = br_fdb_add,
        .ndo_fdb_del             = br_fdb_delete,
        .ndo_fdb_dump            = br_fdb_dump,
+       .ndo_fdb_get             = br_fdb_get,
        .ndo_bridge_getlink      = br_getlink,
        .ndo_bridge_setlink      = br_setlink,
        .ndo_bridge_dellink      = br_dellink,
index e56ba39..fe3c758 100644 (file)
@@ -773,6 +773,32 @@ skip:
        return err;
 }
 
+int br_fdb_get(struct sk_buff *skb,
+              struct nlattr *tb[],
+              struct net_device *dev,
+              const unsigned char *addr,
+              u16 vid, u32 portid, u32 seq,
+              struct netlink_ext_ack *extack)
+{
+       struct net_bridge *br = netdev_priv(dev);
+       struct net_bridge_fdb_entry *f;
+       int err = 0;
+
+       rcu_read_lock();
+       f = br_fdb_find_rcu(br, addr, vid);
+       if (!f) {
+               NL_SET_ERR_MSG(extack, "Fdb entry not found");
+               err = -ENOENT;
+               goto errout;
+       }
+
+       err = fdb_fill_info(skb, br, f, portid, seq,
+                           RTM_NEWNEIGH, 0);
+errout:
+       rcu_read_unlock();
+       return err;
+}
+
 /* Update (create or replace) forwarding database entry */
 static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
                         const u8 *addr, u16 state, u16 flags, u16 vid,
@@ -1164,3 +1190,23 @@ void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p,
 
        spin_unlock_bh(&br->hash_lock);
 }
+
+void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
+{
+       struct net_bridge_fdb_entry *f;
+       struct net_bridge_port *p;
+
+       ASSERT_RTNL();
+
+       p = br_port_get_rtnl(dev);
+       if (!p)
+               return;
+
+       spin_lock_bh(&p->br->hash_lock);
+       hlist_for_each_entry(f, &p->br->fdb_list, fdb_node) {
+               if (f->dst == p && f->key.vlan_id == vid)
+                       f->offloaded = 0;
+       }
+       spin_unlock_bh(&p->br->hash_lock);
+}
+EXPORT_SYMBOL_GPL(br_fdb_clear_offload);
index 9b46d2d..41f0a69 100644 (file)
@@ -650,7 +650,16 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
        if (br_fdb_insert(br, p, dev->dev_addr, 0))
                netdev_err(dev, "failed insert local address bridge forwarding table\n");
 
-       err = nbp_vlan_init(p);
+       if (br->dev->addr_assign_type != NET_ADDR_SET) {
+               /* Ask for permission to use this MAC address now, even if we
+                * don't end up choosing it below.
+                */
+               err = dev_pre_changeaddr_notify(br->dev, dev->dev_addr, extack);
+               if (err)
+                       goto err7;
+       }
+
+       err = nbp_vlan_init(p, extack);
        if (err) {
                netdev_err(dev, "failed to initialize vlan filtering on this port\n");
                goto err7;
@@ -741,3 +750,15 @@ void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
        if (mask & BR_NEIGH_SUPPRESS)
                br_recalculate_neigh_suppress_enabled(br);
 }
+
+bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
+{
+       struct net_bridge_port *p;
+
+       p = br_port_get_rtnl_rcu(dev);
+       if (!p)
+               return false;
+
+       return p->flags & flag;
+}
+EXPORT_SYMBOL_GPL(br_port_flag_is_set);
index 3ddca11..5ea7e56 100644 (file)
@@ -188,7 +188,9 @@ static void __br_handle_local_finish(struct sk_buff *skb)
        u16 vid = 0;
 
        /* check if vlan is allowed, to avoid spoofing */
-       if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid))
+       if ((p->flags & BR_LEARNING) &&
+           !br_opt_get(p->br, BROPT_NO_LL_LEARN) &&
+           br_should_learn(p, skb, &vid))
                br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false);
 }
 
index a7ea2d4..f69c8d9 100644 (file)
@@ -78,82 +78,72 @@ static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip)
 static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
                            struct net_device *dev)
 {
+       int idx = 0, s_idx = cb->args[1], err = 0;
        struct net_bridge *br = netdev_priv(dev);
-       struct net_bridge_mdb_htable *mdb;
+       struct net_bridge_mdb_entry *mp;
        struct nlattr *nest, *nest2;
-       int i, err = 0;
-       int idx = 0, s_idx = cb->args[1];
 
        if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
                return 0;
 
-       mdb = rcu_dereference(br->mdb);
-       if (!mdb)
-               return 0;
-
        nest = nla_nest_start(skb, MDBA_MDB);
        if (nest == NULL)
                return -EMSGSIZE;
 
-       for (i = 0; i < mdb->max; i++) {
-               struct net_bridge_mdb_entry *mp;
+       hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) {
                struct net_bridge_port_group *p;
                struct net_bridge_port_group __rcu **pp;
                struct net_bridge_port *port;
 
-               hlist_for_each_entry_rcu(mp, &mdb->mhash[i], hlist[mdb->ver]) {
-                       if (idx < s_idx)
-                               goto skip;
+               if (idx < s_idx)
+                       goto skip;
 
-                       nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
-                       if (nest2 == NULL) {
-                               err = -EMSGSIZE;
-                               goto out;
-                       }
+               nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
+               if (!nest2) {
+                       err = -EMSGSIZE;
+                       break;
+               }
 
-                       for (pp = &mp->ports;
-                            (p = rcu_dereference(*pp)) != NULL;
-                             pp = &p->next) {
-                               struct nlattr *nest_ent;
-                               struct br_mdb_entry e;
-
-                               port = p->port;
-                               if (!port)
-                                       continue;
-
-                               memset(&e, 0, sizeof(e));
-                               e.ifindex = port->dev->ifindex;
-                               e.vid = p->addr.vid;
-                               __mdb_entry_fill_flags(&e, p->flags);
-                               if (p->addr.proto == htons(ETH_P_IP))
-                                       e.addr.u.ip4 = p->addr.u.ip4;
+               for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL;
+                     pp = &p->next) {
+                       struct nlattr *nest_ent;
+                       struct br_mdb_entry e;
+
+                       port = p->port;
+                       if (!port)
+                               continue;
+
+                       memset(&e, 0, sizeof(e));
+                       e.ifindex = port->dev->ifindex;
+                       e.vid = p->addr.vid;
+                       __mdb_entry_fill_flags(&e, p->flags);
+                       if (p->addr.proto == htons(ETH_P_IP))
+                               e.addr.u.ip4 = p->addr.u.ip4;
 #if IS_ENABLED(CONFIG_IPV6)
-                               if (p->addr.proto == htons(ETH_P_IPV6))
-                                       e.addr.u.ip6 = p->addr.u.ip6;
+                       if (p->addr.proto == htons(ETH_P_IPV6))
+                               e.addr.u.ip6 = p->addr.u.ip6;
 #endif
-                               e.addr.proto = p->addr.proto;
-                               nest_ent = nla_nest_start(skb,
-                                                         MDBA_MDB_ENTRY_INFO);
-                               if (!nest_ent) {
-                                       nla_nest_cancel(skb, nest2);
-                                       err = -EMSGSIZE;
-                                       goto out;
-                               }
-                               if (nla_put_nohdr(skb, sizeof(e), &e) ||
-                                   nla_put_u32(skb,
-                                               MDBA_MDB_EATTR_TIMER,
-                                               br_timer_value(&p->timer))) {
-                                       nla_nest_cancel(skb, nest_ent);
-                                       nla_nest_cancel(skb, nest2);
-                                       err = -EMSGSIZE;
-                                       goto out;
-                               }
-                               nla_nest_end(skb, nest_ent);
+                       e.addr.proto = p->addr.proto;
+                       nest_ent = nla_nest_start(skb, MDBA_MDB_ENTRY_INFO);
+                       if (!nest_ent) {
+                               nla_nest_cancel(skb, nest2);
+                               err = -EMSGSIZE;
+                               goto out;
                        }
-                       nla_nest_end(skb, nest2);
-               skip:
-                       idx++;
+                       if (nla_put_nohdr(skb, sizeof(e), &e) ||
+                           nla_put_u32(skb,
+                                       MDBA_MDB_EATTR_TIMER,
+                                       br_timer_value(&p->timer))) {
+                               nla_nest_cancel(skb, nest_ent);
+                               nla_nest_cancel(skb, nest2);
+                               err = -EMSGSIZE;
+                               goto out;
+                       }
+                       nla_nest_end(skb, nest_ent);
                }
+               nla_nest_end(skb, nest2);
+skip:
+               idx++;
        }
 
 out:
@@ -203,8 +193,7 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
 
        rcu_read_lock();
 
-       /* In theory this could be wrapped to 0... */
-       cb->seq = net->dev_base_seq + br_mdb_rehash_seq;
+       cb->seq = net->dev_base_seq;
 
        for_each_netdev_rcu(net, dev) {
                if (dev->priv_flags & IFF_EBRIDGE) {
@@ -297,7 +286,6 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv)
        struct br_mdb_complete_info *data = priv;
        struct net_bridge_port_group __rcu **pp;
        struct net_bridge_port_group *p;
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port *port = data->port;
        struct net_bridge *br = port->br;
@@ -306,8 +294,7 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv)
                goto err;
 
        spin_lock_bh(&br->multicast_lock);
-       mdb = mlock_dereference(br->mdb, br);
-       mp = br_mdb_ip_get(mdb, &data->ip);
+       mp = br_mdb_ip_get(br, &data->ip);
        if (!mp)
                goto out;
        for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
@@ -344,7 +331,7 @@ static void br_mdb_switchdev_host_port(struct net_device *dev,
        mdb.obj.orig_dev = dev;
        switch (type) {
        case RTM_NEWMDB:
-               switchdev_port_obj_add(lower_dev, &mdb.obj);
+               switchdev_port_obj_add(lower_dev, &mdb.obj, NULL);
                break;
        case RTM_DELMDB:
                switchdev_port_obj_del(lower_dev, &mdb.obj);
@@ -394,7 +381,7 @@ static void __br_mdb_notify(struct net_device *dev, struct net_bridge_port *p,
                        __mdb_entry_to_br_ip(entry, &complete_info->ip);
                        mdb.obj.complete_priv = complete_info;
                        mdb.obj.complete = br_mdb_complete;
-                       if (switchdev_port_obj_add(port_dev, &mdb.obj))
+                       if (switchdev_port_obj_add(port_dev, &mdb.obj, NULL))
                                kfree(complete_info);
                }
        } else if (p && port_dev && type == RTM_DELMDB) {
@@ -588,14 +575,12 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
-       struct net_bridge_mdb_htable *mdb;
        unsigned long now = jiffies;
        int err;
 
-       mdb = mlock_dereference(br->mdb, br);
-       mp = br_mdb_ip_get(mdb, group);
+       mp = br_mdb_ip_get(br, group);
        if (!mp) {
-               mp = br_multicast_new_group(br, port, group);
+               mp = br_multicast_new_group(br, group);
                err = PTR_ERR_OR_ZERO(mp);
                if (err)
                        return err;
@@ -696,7 +681,6 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
 
 static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
@@ -709,9 +693,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
        __mdb_entry_to_br_ip(entry, &ip);
 
        spin_lock_bh(&br->multicast_lock);
-       mdb = mlock_dereference(br->mdb, br);
-
-       mp = br_mdb_ip_get(mdb, &ip);
+       mp = br_mdb_ip_get(br, &ip);
        if (!mp)
                goto unlock;
 
@@ -728,7 +710,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
                rcu_assign_pointer(*pp, p->next);
                hlist_del_init(&p->mglist);
                del_timer(&p->timer);
-               call_rcu_bh(&p->rcu, br_multicast_free_pg);
+               kfree_rcu(p, rcu);
                err = 0;
 
                if (!mp->ports && !mp->host_joined &&
index 6bac0d6..3aeff08 100644 (file)
 
 #include "br_private.h"
 
+static const struct rhashtable_params br_mdb_rht_params = {
+       .head_offset = offsetof(struct net_bridge_mdb_entry, rhnode),
+       .key_offset = offsetof(struct net_bridge_mdb_entry, addr),
+       .key_len = sizeof(struct br_ip),
+       .automatic_shrinking = true,
+       .locks_mul = 1,
+};
+
 static void br_multicast_start_querier(struct net_bridge *br,
                                       struct bridge_mcast_own_query *query);
 static void br_multicast_add_router(struct net_bridge *br,
@@ -54,7 +62,6 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br,
                                         const struct in6_addr *group,
                                         __u16 vid, const unsigned char *src);
 #endif
-unsigned int br_mdb_rehash_seq;
 
 static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
 {
@@ -73,89 +80,58 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
        return 0;
 }
 
-static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip,
-                               __u16 vid)
-{
-       return jhash_2words((__force u32)ip, vid, mdb->secret) & (mdb->max - 1);
-}
-
-#if IS_ENABLED(CONFIG_IPV6)
-static inline int __br_ip6_hash(struct net_bridge_mdb_htable *mdb,
-                               const struct in6_addr *ip,
-                               __u16 vid)
+static struct net_bridge_mdb_entry *br_mdb_ip_get_rcu(struct net_bridge *br,
+                                                     struct br_ip *dst)
 {
-       return jhash_2words(ipv6_addr_hash(ip), vid,
-                           mdb->secret) & (mdb->max - 1);
+       return rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
 }
-#endif
 
-static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb,
-                            struct br_ip *ip)
-{
-       switch (ip->proto) {
-       case htons(ETH_P_IP):
-               return __br_ip4_hash(mdb, ip->u.ip4, ip->vid);
-#if IS_ENABLED(CONFIG_IPV6)
-       case htons(ETH_P_IPV6):
-               return __br_ip6_hash(mdb, &ip->u.ip6, ip->vid);
-#endif
-       }
-       return 0;
-}
-
-static struct net_bridge_mdb_entry *__br_mdb_ip_get(
-       struct net_bridge_mdb_htable *mdb, struct br_ip *dst, int hash)
+struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge *br,
+                                          struct br_ip *dst)
 {
-       struct net_bridge_mdb_entry *mp;
-
-       hlist_for_each_entry_rcu(mp, &mdb->mhash[hash], hlist[mdb->ver]) {
-               if (br_ip_equal(&mp->addr, dst))
-                       return mp;
-       }
+       struct net_bridge_mdb_entry *ent;
 
-       return NULL;
-}
+       lockdep_assert_held_once(&br->multicast_lock);
 
-struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge_mdb_htable *mdb,
-                                          struct br_ip *dst)
-{
-       if (!mdb)
-               return NULL;
+       rcu_read_lock();
+       ent = rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
+       rcu_read_unlock();
 
-       return __br_mdb_ip_get(mdb, dst, br_ip_hash(mdb, dst));
+       return ent;
 }
 
-static struct net_bridge_mdb_entry *br_mdb_ip4_get(
-       struct net_bridge_mdb_htable *mdb, __be32 dst, __u16 vid)
+static struct net_bridge_mdb_entry *br_mdb_ip4_get(struct net_bridge *br,
+                                                  __be32 dst, __u16 vid)
 {
        struct br_ip br_dst;
 
+       memset(&br_dst, 0, sizeof(br_dst));
        br_dst.u.ip4 = dst;
        br_dst.proto = htons(ETH_P_IP);
        br_dst.vid = vid;
 
-       return br_mdb_ip_get(mdb, &br_dst);
+       return br_mdb_ip_get(br, &br_dst);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
-static struct net_bridge_mdb_entry *br_mdb_ip6_get(
-       struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst,
-       __u16 vid)
+static struct net_bridge_mdb_entry *br_mdb_ip6_get(struct net_bridge *br,
+                                                  const struct in6_addr *dst,
+                                                  __u16 vid)
 {
        struct br_ip br_dst;
 
+       memset(&br_dst, 0, sizeof(br_dst));
        br_dst.u.ip6 = *dst;
        br_dst.proto = htons(ETH_P_IPV6);
        br_dst.vid = vid;
 
-       return br_mdb_ip_get(mdb, &br_dst);
+       return br_mdb_ip_get(br, &br_dst);
 }
 #endif
 
 struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
                                        struct sk_buff *skb, u16 vid)
 {
-       struct net_bridge_mdb_htable *mdb = rcu_dereference(br->mdb);
        struct br_ip ip;
 
        if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
@@ -164,6 +140,7 @@ struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
        if (BR_INPUT_SKB_CB(skb)->igmp)
                return NULL;
 
+       memset(&ip, 0, sizeof(ip));
        ip.proto = skb->protocol;
        ip.vid = vid;
 
@@ -180,70 +157,13 @@ struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
                return NULL;
        }
 
-       return br_mdb_ip_get(mdb, &ip);
-}
-
-static void br_mdb_free(struct rcu_head *head)
-{
-       struct net_bridge_mdb_htable *mdb =
-               container_of(head, struct net_bridge_mdb_htable, rcu);
-       struct net_bridge_mdb_htable *old = mdb->old;
-
-       mdb->old = NULL;
-       kfree(old->mhash);
-       kfree(old);
-}
-
-static int br_mdb_copy(struct net_bridge_mdb_htable *new,
-                      struct net_bridge_mdb_htable *old,
-                      int elasticity)
-{
-       struct net_bridge_mdb_entry *mp;
-       int maxlen;
-       int len;
-       int i;
-
-       for (i = 0; i < old->max; i++)
-               hlist_for_each_entry(mp, &old->mhash[i], hlist[old->ver])
-                       hlist_add_head(&mp->hlist[new->ver],
-                                      &new->mhash[br_ip_hash(new, &mp->addr)]);
-
-       if (!elasticity)
-               return 0;
-
-       maxlen = 0;
-       for (i = 0; i < new->max; i++) {
-               len = 0;
-               hlist_for_each_entry(mp, &new->mhash[i], hlist[new->ver])
-                       len++;
-               if (len > maxlen)
-                       maxlen = len;
-       }
-
-       return maxlen > elasticity ? -EINVAL : 0;
-}
-
-void br_multicast_free_pg(struct rcu_head *head)
-{
-       struct net_bridge_port_group *p =
-               container_of(head, struct net_bridge_port_group, rcu);
-
-       kfree(p);
-}
-
-static void br_multicast_free_group(struct rcu_head *head)
-{
-       struct net_bridge_mdb_entry *mp =
-               container_of(head, struct net_bridge_mdb_entry, rcu);
-
-       kfree(mp);
+       return br_mdb_ip_get_rcu(br, &ip);
 }
 
 static void br_multicast_group_expired(struct timer_list *t)
 {
        struct net_bridge_mdb_entry *mp = from_timer(mp, t, timer);
        struct net_bridge *br = mp->br;
-       struct net_bridge_mdb_htable *mdb;
 
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) || timer_pending(&mp->timer))
@@ -255,12 +175,11 @@ static void br_multicast_group_expired(struct timer_list *t)
        if (mp->ports)
                goto out;
 
-       mdb = mlock_dereference(br->mdb, br);
-
-       hlist_del_rcu(&mp->hlist[mdb->ver]);
-       mdb->size--;
+       rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode,
+                              br_mdb_rht_params);
+       hlist_del_rcu(&mp->mdb_node);
 
-       call_rcu_bh(&mp->rcu, br_multicast_free_group);
+       kfree_rcu(mp, rcu);
 
 out:
        spin_unlock(&br->multicast_lock);
@@ -269,14 +188,11 @@ out:
 static void br_multicast_del_pg(struct net_bridge *br,
                                struct net_bridge_port_group *pg)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
 
-       mdb = mlock_dereference(br->mdb, br);
-
-       mp = br_mdb_ip_get(mdb, &pg->addr);
+       mp = br_mdb_ip_get(br, &pg->addr);
        if (WARN_ON(!mp))
                return;
 
@@ -291,7 +207,7 @@ static void br_multicast_del_pg(struct net_bridge *br,
                del_timer(&p->timer);
                br_mdb_notify(br->dev, p->port, &pg->addr, RTM_DELMDB,
                              p->flags);
-               call_rcu_bh(&p->rcu, br_multicast_free_pg);
+               kfree_rcu(p, rcu);
 
                if (!mp->ports && !mp->host_joined &&
                    netif_running(br->dev))
@@ -319,53 +235,6 @@ out:
        spin_unlock(&br->multicast_lock);
 }
 
-static int br_mdb_rehash(struct net_bridge_mdb_htable __rcu **mdbp, int max,
-                        int elasticity)
-{
-       struct net_bridge_mdb_htable *old = rcu_dereference_protected(*mdbp, 1);
-       struct net_bridge_mdb_htable *mdb;
-       int err;
-
-       mdb = kmalloc(sizeof(*mdb), GFP_ATOMIC);
-       if (!mdb)
-               return -ENOMEM;
-
-       mdb->max = max;
-       mdb->old = old;
-
-       mdb->mhash = kcalloc(max, sizeof(*mdb->mhash), GFP_ATOMIC);
-       if (!mdb->mhash) {
-               kfree(mdb);
-               return -ENOMEM;
-       }
-
-       mdb->size = old ? old->size : 0;
-       mdb->ver = old ? old->ver ^ 1 : 0;
-
-       if (!old || elasticity)
-               get_random_bytes(&mdb->secret, sizeof(mdb->secret));
-       else
-               mdb->secret = old->secret;
-
-       if (!old)
-               goto out;
-
-       err = br_mdb_copy(mdb, old, elasticity);
-       if (err) {
-               kfree(mdb->mhash);
-               kfree(mdb);
-               return err;
-       }
-
-       br_mdb_rehash_seq++;
-       call_rcu_bh(&mdb->rcu, br_mdb_free);
-
-out:
-       rcu_assign_pointer(*mdbp, mdb);
-
-       return 0;
-}
-
 static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
                                                    __be32 group,
                                                    u8 *igmp_type)
@@ -589,111 +458,19 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
        return NULL;
 }
 
-static struct net_bridge_mdb_entry *br_multicast_get_group(
-       struct net_bridge *br, struct net_bridge_port *port,
-       struct br_ip *group, int hash)
-{
-       struct net_bridge_mdb_htable *mdb;
-       struct net_bridge_mdb_entry *mp;
-       unsigned int count = 0;
-       unsigned int max;
-       int elasticity;
-       int err;
-
-       mdb = rcu_dereference_protected(br->mdb, 1);
-       hlist_for_each_entry(mp, &mdb->mhash[hash], hlist[mdb->ver]) {
-               count++;
-               if (unlikely(br_ip_equal(group, &mp->addr)))
-                       return mp;
-       }
-
-       elasticity = 0;
-       max = mdb->max;
-
-       if (unlikely(count > br->hash_elasticity && count)) {
-               if (net_ratelimit())
-                       br_info(br, "Multicast hash table "
-                               "chain limit reached: %s\n",
-                               port ? port->dev->name : br->dev->name);
-
-               elasticity = br->hash_elasticity;
-       }
-
-       if (mdb->size >= max) {
-               max *= 2;
-               if (unlikely(max > br->hash_max)) {
-                       br_warn(br, "Multicast hash table maximum of %d "
-                               "reached, disabling snooping: %s\n",
-                               br->hash_max,
-                               port ? port->dev->name : br->dev->name);
-                       err = -E2BIG;
-disable:
-                       br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
-                       goto err;
-               }
-       }
-
-       if (max > mdb->max || elasticity) {
-               if (mdb->old) {
-                       if (net_ratelimit())
-                               br_info(br, "Multicast hash table "
-                                       "on fire: %s\n",
-                                       port ? port->dev->name : br->dev->name);
-                       err = -EEXIST;
-                       goto err;
-               }
-
-               err = br_mdb_rehash(&br->mdb, max, elasticity);
-               if (err) {
-                       br_warn(br, "Cannot rehash multicast "
-                               "hash table, disabling snooping: %s, %d, %d\n",
-                               port ? port->dev->name : br->dev->name,
-                               mdb->size, err);
-                       goto disable;
-               }
-
-               err = -EAGAIN;
-               goto err;
-       }
-
-       return NULL;
-
-err:
-       mp = ERR_PTR(err);
-       return mp;
-}
-
 struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
-                                                   struct net_bridge_port *p,
                                                    struct br_ip *group)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
-       int hash;
        int err;
 
-       mdb = rcu_dereference_protected(br->mdb, 1);
-       if (!mdb) {
-               err = br_mdb_rehash(&br->mdb, BR_HASH_SIZE, 0);
-               if (err)
-                       return ERR_PTR(err);
-               goto rehash;
-       }
-
-       hash = br_ip_hash(mdb, group);
-       mp = br_multicast_get_group(br, p, group, hash);
-       switch (PTR_ERR(mp)) {
-       case 0:
-               break;
-
-       case -EAGAIN:
-rehash:
-               mdb = rcu_dereference_protected(br->mdb, 1);
-               hash = br_ip_hash(mdb, group);
-               break;
+       mp = br_mdb_ip_get(br, group);
+       if (mp)
+               return mp;
 
-       default:
-               goto out;
+       if (atomic_read(&br->mdb_hash_tbl.nelems) >= br->hash_max) {
+               br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
+               return ERR_PTR(-E2BIG);
        }
 
        mp = kzalloc(sizeof(*mp), GFP_ATOMIC);
@@ -703,11 +480,15 @@ rehash:
        mp->br = br;
        mp->addr = *group;
        timer_setup(&mp->timer, br_multicast_group_expired, 0);
+       err = rhashtable_lookup_insert_fast(&br->mdb_hash_tbl, &mp->rhnode,
+                                           br_mdb_rht_params);
+       if (err) {
+               kfree(mp);
+               mp = ERR_PTR(err);
+       } else {
+               hlist_add_head_rcu(&mp->mdb_node, &br->mdb_list);
+       }
 
-       hlist_add_head_rcu(&mp->hlist[mdb->ver], &mdb->mhash[hash]);
-       mdb->size++;
-
-out:
        return mp;
 }
 
@@ -768,7 +549,7 @@ static int br_multicast_add_group(struct net_bridge *br,
            (port && port->state == BR_STATE_DISABLED))
                goto out;
 
-       mp = br_multicast_new_group(br, port, group);
+       mp = br_multicast_new_group(br, group);
        err = PTR_ERR(mp);
        if (IS_ERR(mp))
                goto err;
@@ -837,6 +618,7 @@ static int br_ip6_multicast_add_group(struct net_bridge *br,
        if (ipv6_addr_is_ll_all_nodes(group))
                return 0;
 
+       memset(&br_group, 0, sizeof(br_group));
        br_group.u.ip6 = *group;
        br_group.proto = htons(ETH_P_IPV6);
        br_group.vid = vid;
@@ -1483,7 +1265,7 @@ static void br_ip4_multicast_query(struct net_bridge *br,
                goto out;
        }
 
-       mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
+       mp = br_mdb_ip4_get(br, group, vid);
        if (!mp)
                goto out;
 
@@ -1567,7 +1349,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
                goto out;
        }
 
-       mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
+       mp = br_mdb_ip6_get(br, group, vid);
        if (!mp)
                goto out;
 
@@ -1601,7 +1383,6 @@ br_multicast_leave_group(struct net_bridge *br,
                         struct bridge_mcast_own_query *own_query,
                         const unsigned char *src)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        unsigned long now;
@@ -1612,8 +1393,7 @@ br_multicast_leave_group(struct net_bridge *br,
            (port && port->state == BR_STATE_DISABLED))
                goto out;
 
-       mdb = mlock_dereference(br->mdb, br);
-       mp = br_mdb_ip_get(mdb, group);
+       mp = br_mdb_ip_get(br, group);
        if (!mp)
                goto out;
 
@@ -1629,7 +1409,7 @@ br_multicast_leave_group(struct net_bridge *br,
                        rcu_assign_pointer(*pp, p->next);
                        hlist_del_init(&p->mglist);
                        del_timer(&p->timer);
-                       call_rcu_bh(&p->rcu, br_multicast_free_pg);
+                       kfree_rcu(p, rcu);
                        br_mdb_notify(br->dev, port, group, RTM_DELMDB,
                                      p->flags);
 
@@ -1961,8 +1741,7 @@ static void br_ip6_multicast_query_expired(struct timer_list *t)
 
 void br_multicast_init(struct net_bridge *br)
 {
-       br->hash_elasticity = 4;
-       br->hash_max = 512;
+       br->hash_max = BR_MULTICAST_DEFAULT_HASH_MAX;
 
        br->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
        br->multicast_last_member_count = 2;
@@ -1999,6 +1778,7 @@ void br_multicast_init(struct net_bridge *br)
        timer_setup(&br->ip6_own_query.timer,
                    br_ip6_multicast_query_expired, 0);
 #endif
+       INIT_HLIST_HEAD(&br->mdb_list);
 }
 
 static void __br_multicast_open(struct net_bridge *br,
@@ -2033,40 +1813,20 @@ void br_multicast_stop(struct net_bridge *br)
 
 void br_multicast_dev_del(struct net_bridge *br)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
-       struct hlist_node *n;
-       u32 ver;
-       int i;
+       struct hlist_node *tmp;
 
        spin_lock_bh(&br->multicast_lock);
-       mdb = mlock_dereference(br->mdb, br);
-       if (!mdb)
-               goto out;
-
-       br->mdb = NULL;
-
-       ver = mdb->ver;
-       for (i = 0; i < mdb->max; i++) {
-               hlist_for_each_entry_safe(mp, n, &mdb->mhash[i],
-                                         hlist[ver]) {
-                       del_timer(&mp->timer);
-                       call_rcu_bh(&mp->rcu, br_multicast_free_group);
-               }
-       }
-
-       if (mdb->old) {
-               spin_unlock_bh(&br->multicast_lock);
-               rcu_barrier_bh();
-               spin_lock_bh(&br->multicast_lock);
-               WARN_ON(mdb->old);
+       hlist_for_each_entry_safe(mp, tmp, &br->mdb_list, mdb_node) {
+               del_timer(&mp->timer);
+               rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode,
+                                      br_mdb_rht_params);
+               hlist_del_rcu(&mp->mdb_node);
+               kfree_rcu(mp, rcu);
        }
-
-       mdb->old = mdb;
-       call_rcu_bh(&mdb->rcu, br_mdb_free);
-
-out:
        spin_unlock_bh(&br->multicast_lock);
+
+       rcu_barrier();
 }
 
 int br_multicast_set_router(struct net_bridge *br, unsigned long val)
@@ -2176,9 +1936,7 @@ static void br_multicast_start_querier(struct net_bridge *br,
 
 int br_multicast_toggle(struct net_bridge *br, unsigned long val)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_port *port;
-       int err = 0;
 
        spin_lock_bh(&br->multicast_lock);
        if (!!br_opt_get(br, BROPT_MULTICAST_ENABLED) == !!val)
@@ -2192,21 +1950,6 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val)
        if (!netif_running(br->dev))
                goto unlock;
 
-       mdb = mlock_dereference(br->mdb, br);
-       if (mdb) {
-               if (mdb->old) {
-                       err = -EEXIST;
-rollback:
-                       br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
-                       goto unlock;
-               }
-
-               err = br_mdb_rehash(&br->mdb, mdb->max,
-                                   br->hash_elasticity);
-               if (err)
-                       goto rollback;
-       }
-
        br_multicast_open(br);
        list_for_each_entry(port, &br->port_list, list)
                __br_multicast_enable_port(port);
@@ -2214,7 +1957,7 @@ rollback:
 unlock:
        spin_unlock_bh(&br->multicast_lock);
 
-       return err;
+       return 0;
 }
 
 bool br_multicast_enabled(const struct net_device *dev)
@@ -2271,45 +2014,6 @@ unlock:
        return 0;
 }
 
-int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val)
-{
-       int err = -EINVAL;
-       u32 old;
-       struct net_bridge_mdb_htable *mdb;
-
-       spin_lock_bh(&br->multicast_lock);
-       if (!is_power_of_2(val))
-               goto unlock;
-
-       mdb = mlock_dereference(br->mdb, br);
-       if (mdb && val < mdb->size)
-               goto unlock;
-
-       err = 0;
-
-       old = br->hash_max;
-       br->hash_max = val;
-
-       if (mdb) {
-               if (mdb->old) {
-                       err = -EEXIST;
-rollback:
-                       br->hash_max = old;
-                       goto unlock;
-               }
-
-               err = br_mdb_rehash(&br->mdb, br->hash_max,
-                                   br->hash_elasticity);
-               if (err)
-                       goto rollback;
-       }
-
-unlock:
-       spin_unlock_bh(&br->multicast_lock);
-
-       return err;
-}
-
 int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val)
 {
        /* Currently we support only version 2 and 3 */
@@ -2646,3 +2350,13 @@ void br_multicast_get_stats(const struct net_bridge *br,
        }
        memcpy(dest, &tdst, sizeof(*dest));
 }
+
+int br_mdb_hash_init(struct net_bridge *br)
+{
+       return rhashtable_init(&br->mdb_hash_tbl, &br_mdb_rht_params);
+}
+
+void br_mdb_hash_fini(struct net_bridge *br)
+{
+       rhashtable_destroy(&br->mdb_hash_tbl);
+}
index c9383c4..d21a236 100644 (file)
@@ -132,10 +132,7 @@ static DEFINE_PER_CPU(struct brnf_frag_data, brnf_frag_data_storage);
 
 static void nf_bridge_info_free(struct sk_buff *skb)
 {
-       if (skb->nf_bridge) {
-               nf_bridge_put(skb->nf_bridge);
-               skb->nf_bridge = NULL;
-       }
+       skb_ext_del(skb, SKB_EXT_BRIDGE_NF);
 }
 
 static inline struct net_device *bridge_parent(const struct net_device *dev)
@@ -148,19 +145,7 @@ static inline struct net_device *bridge_parent(const struct net_device *dev)
 
 static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
-
-       if (refcount_read(&nf_bridge->use) > 1) {
-               struct nf_bridge_info *tmp = nf_bridge_alloc(skb);
-
-               if (tmp) {
-                       memcpy(tmp, nf_bridge, sizeof(struct nf_bridge_info));
-                       refcount_set(&tmp->use, 1);
-               }
-               nf_bridge_put(nf_bridge);
-               nf_bridge = tmp;
-       }
-       return nf_bridge;
+       return skb_ext_add(skb, SKB_EXT_BRIDGE_NF);
 }
 
 unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb)
@@ -247,7 +232,9 @@ drop:
 
 void nf_bridge_update_protocol(struct sk_buff *skb)
 {
-       switch (skb->nf_bridge->orig_proto) {
+       const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
+
+       switch (nf_bridge->orig_proto) {
        case BRNF_PROTO_8021Q:
                skb->protocol = htons(ETH_P_8021Q);
                break;
@@ -506,7 +493,6 @@ static unsigned int br_nf_pre_routing(void *priv,
        if (br_validate_ipv4(state->net, skb))
                return NF_DROP;
 
-       nf_bridge_put(skb->nf_bridge);
        if (!nf_bridge_alloc(skb))
                return NF_DROP;
        if (!setup_pre_routing(skb))
@@ -569,7 +555,8 @@ static unsigned int br_nf_forward_ip(void *priv,
        struct net_device *parent;
        u_int8_t pf;
 
-       if (!skb->nf_bridge)
+       nf_bridge = nf_bridge_info_get(skb);
+       if (!nf_bridge)
                return NF_ACCEPT;
 
        /* Need exclusive nf_bridge_info since we might have multiple
@@ -701,7 +688,9 @@ br_nf_ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
 
 static unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb)
 {
-       if (skb->nf_bridge->orig_proto == BRNF_PROTO_PPPOE)
+       const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
+
+       if (nf_bridge->orig_proto == BRNF_PROTO_PPPOE)
                return PPPOE_SES_HLEN;
        return 0;
 }
@@ -839,7 +828,9 @@ static unsigned int ip_sabotage_in(void *priv,
                                   struct sk_buff *skb,
                                   const struct nf_hook_state *state)
 {
-       if (skb->nf_bridge && !skb->nf_bridge->in_prerouting &&
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
+
+       if (nf_bridge && !nf_bridge->in_prerouting &&
            !netif_is_l3_master(skb->dev)) {
                state->okfn(state->net, state->sk, skb);
                return NF_STOLEN;
@@ -877,7 +868,9 @@ static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb)
 
 static int br_nf_dev_xmit(struct sk_buff *skb)
 {
-       if (skb->nf_bridge && skb->nf_bridge->bridged_dnat) {
+       const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
+
+       if (nf_bridge && nf_bridge->bridged_dnat) {
                br_nf_pre_routing_finish_bridge_slow(skb);
                return 1;
        }
index 96c072e..94039f5 100644 (file)
@@ -224,8 +224,8 @@ unsigned int br_nf_pre_routing_ipv6(void *priv,
        if (br_validate_ipv6(state->net, skb))
                return NF_DROP;
 
-       nf_bridge_put(skb->nf_bridge);
-       if (!nf_bridge_alloc(skb))
+       nf_bridge = nf_bridge_alloc(skb);
+       if (!nf_bridge)
                return NF_DROP;
        if (!setup_pre_routing(skb))
                return NF_DROP;
index 3345f19..9c07591 100644 (file)
@@ -525,7 +525,8 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
 }
 
 static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
-                       int cmd, struct bridge_vlan_info *vinfo, bool *changed)
+                       int cmd, struct bridge_vlan_info *vinfo, bool *changed,
+                       struct netlink_ext_ack *extack)
 {
        bool curr_change;
        int err = 0;
@@ -537,11 +538,11 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
                         * per-VLAN entry as well
                         */
                        err = nbp_vlan_add(p, vinfo->vid, vinfo->flags,
-                                          &curr_change);
+                                          &curr_change, extack);
                } else {
                        vinfo->flags |= BRIDGE_VLAN_INFO_BRENTRY;
                        err = br_vlan_add(br, vinfo->vid, vinfo->flags,
-                                         &curr_change);
+                                         &curr_change, extack);
                }
                if (curr_change)
                        *changed = true;
@@ -568,7 +569,8 @@ static int br_process_vlan_info(struct net_bridge *br,
                                struct net_bridge_port *p, int cmd,
                                struct bridge_vlan_info *vinfo_curr,
                                struct bridge_vlan_info **vinfo_last,
-                               bool *changed)
+                               bool *changed,
+                               struct netlink_ext_ack *extack)
 {
        if (!vinfo_curr->vid || vinfo_curr->vid >= VLAN_VID_MASK)
                return -EINVAL;
@@ -598,7 +600,8 @@ static int br_process_vlan_info(struct net_bridge *br,
                       sizeof(struct bridge_vlan_info));
                for (v = (*vinfo_last)->vid; v <= vinfo_curr->vid; v++) {
                        tmp_vinfo.vid = v;
-                       err = br_vlan_info(br, p, cmd, &tmp_vinfo, changed);
+                       err = br_vlan_info(br, p, cmd, &tmp_vinfo, changed,
+                                          extack);
                        if (err)
                                break;
                }
@@ -607,13 +610,14 @@ static int br_process_vlan_info(struct net_bridge *br,
                return err;
        }
 
-       return br_vlan_info(br, p, cmd, vinfo_curr, changed);
+       return br_vlan_info(br, p, cmd, vinfo_curr, changed, extack);
 }
 
 static int br_afspec(struct net_bridge *br,
                     struct net_bridge_port *p,
                     struct nlattr *af_spec,
-                    int cmd, bool *changed)
+                    int cmd, bool *changed,
+                    struct netlink_ext_ack *extack)
 {
        struct bridge_vlan_info *vinfo_curr = NULL;
        struct bridge_vlan_info *vinfo_last = NULL;
@@ -643,7 +647,8 @@ static int br_afspec(struct net_bridge *br,
                                return -EINVAL;
                        vinfo_curr = nla_data(attr);
                        err = br_process_vlan_info(br, p, cmd, vinfo_curr,
-                                                  &vinfo_last, changed);
+                                                  &vinfo_last, changed,
+                                                  extack);
                        if (err)
                                return err;
                        break;
@@ -850,7 +855,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
 }
 
 /* Change state and parameters on port. */
-int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
+int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags,
+              struct netlink_ext_ack *extack)
 {
        struct net_bridge *br = (struct net_bridge *)netdev_priv(dev);
        struct nlattr *tb[IFLA_BRPORT_MAX + 1];
@@ -897,7 +903,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
        }
 
        if (afspec)
-               err = br_afspec(br, p, afspec, RTM_SETLINK, &changed);
+               err = br_afspec(br, p, afspec, RTM_SETLINK, &changed, extack);
 
        if (changed)
                br_ifinfo_notify(RTM_NEWLINK, br, p);
@@ -923,7 +929,7 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
        if (!p && !(dev->priv_flags & IFF_EBRIDGE))
                return -EINVAL;
 
-       err = br_afspec(br, p, afspec, RTM_DELLINK, &changed);
+       err = br_afspec(br, p, afspec, RTM_DELLINK, &changed, NULL);
        if (changed)
                /* Send RTM_NEWLINK because userspace
                 * expects RTM_NEWLINK for vlan dels
@@ -1035,6 +1041,8 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
        [IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
        [IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 },
        [IFLA_BR_VLAN_STATS_PER_PORT] = { .type = NLA_U8 },
+       [IFLA_BR_MULTI_BOOLOPT] = { .type = NLA_EXACT_LEN,
+                                   .len = sizeof(struct br_boolopt_multi) },
 };
 
 static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
@@ -1103,7 +1111,7 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
        if (data[IFLA_BR_VLAN_DEFAULT_PVID]) {
                __u16 defpvid = nla_get_u16(data[IFLA_BR_VLAN_DEFAULT_PVID]);
 
-               err = __br_vlan_set_default_pvid(br, defpvid);
+               err = __br_vlan_set_default_pvid(br, defpvid, extack);
                if (err)
                        return err;
        }
@@ -1167,9 +1175,7 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
        if (data[IFLA_BR_MCAST_SNOOPING]) {
                u8 mcast_snooping = nla_get_u8(data[IFLA_BR_MCAST_SNOOPING]);
 
-               err = br_multicast_toggle(br, mcast_snooping);
-               if (err)
-                       return err;
+               br_multicast_toggle(br, mcast_snooping);
        }
 
        if (data[IFLA_BR_MCAST_QUERY_USE_IFADDR]) {
@@ -1187,19 +1193,12 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
                        return err;
        }
 
-       if (data[IFLA_BR_MCAST_HASH_ELASTICITY]) {
-               u32 val = nla_get_u32(data[IFLA_BR_MCAST_HASH_ELASTICITY]);
-
-               br->hash_elasticity = val;
-       }
+       if (data[IFLA_BR_MCAST_HASH_ELASTICITY])
+               br_warn(br, "the hash_elasticity option has been deprecated and is always %u\n",
+                       RHT_ELASTICITY);
 
-       if (data[IFLA_BR_MCAST_HASH_MAX]) {
-               u32 hash_max = nla_get_u32(data[IFLA_BR_MCAST_HASH_MAX]);
-
-               err = br_multicast_set_hash_max(br, hash_max);
-               if (err)
-                       return err;
-       }
+       if (data[IFLA_BR_MCAST_HASH_MAX])
+               br->hash_max = nla_get_u32(data[IFLA_BR_MCAST_HASH_MAX]);
 
        if (data[IFLA_BR_MCAST_LAST_MEMBER_CNT]) {
                u32 val = nla_get_u32(data[IFLA_BR_MCAST_LAST_MEMBER_CNT]);
@@ -1296,6 +1295,15 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
        }
 #endif
 
+       if (data[IFLA_BR_MULTI_BOOLOPT]) {
+               struct br_boolopt_multi *bm;
+
+               bm = nla_data(data[IFLA_BR_MULTI_BOOLOPT]);
+               err = br_boolopt_multi_toggle(br, bm, extack);
+               if (err)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -1374,6 +1382,7 @@ static size_t br_get_size(const struct net_device *brdev)
               nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_IP6TABLES */
               nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_ARPTABLES */
 #endif
+              nla_total_size(sizeof(struct br_boolopt_multi)) + /* IFLA_BR_MULTI_BOOLOPT */
               0;
 }
 
@@ -1387,6 +1396,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
        u32 stp_enabled = br->stp_enabled;
        u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1];
        u8 vlan_enabled = br_vlan_enabled(br->dev);
+       struct br_boolopt_multi bm;
        u64 clockval;
 
        clockval = br_timer_value(&br->hello_timer);
@@ -1403,6 +1413,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
        if (nla_put_u64_64bit(skb, IFLA_BR_GC_TIMER, clockval, IFLA_BR_PAD))
                return -EMSGSIZE;
 
+       br_boolopt_multi_get(br, &bm);
        if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) ||
            nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) ||
            nla_put_u32(skb, IFLA_BR_MAX_AGE, age_time) ||
@@ -1420,7 +1431,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
            nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE, br->topology_change) ||
            nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
                       br->topology_change_detected) ||
-           nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr))
+           nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr) ||
+           nla_put(skb, IFLA_BR_MULTI_BOOLOPT, sizeof(bm), &bm))
                return -EMSGSIZE;
 
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
@@ -1442,8 +1454,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
                       br_opt_get(br, BROPT_MULTICAST_QUERIER)) ||
            nla_put_u8(skb, IFLA_BR_MCAST_STATS_ENABLED,
                       br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) ||
-           nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY,
-                       br->hash_elasticity) ||
+           nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, RHT_ELASTICITY) ||
            nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) ||
            nla_put_u32(skb, IFLA_BR_MCAST_LAST_MEMBER_CNT,
                        br->multicast_last_member_count) ||
index 67105c6..d240b3e 100644 (file)
@@ -31,6 +31,8 @@
 #define BR_PORT_BITS   10
 #define BR_MAX_PORTS   (1<<BR_PORT_BITS)
 
+#define BR_MULTICAST_DEFAULT_HASH_MAX 4096
+
 #define BR_VERSION     "2.3"
 
 /* Control of forwarding link local multicast */
@@ -102,12 +104,18 @@ struct br_tunnel_info {
        struct metadata_dst     *tunnel_dst;
 };
 
+/* private vlan flags */
+enum {
+       BR_VLFLAG_PER_PORT_STATS = BIT(0),
+};
+
 /**
  * struct net_bridge_vlan - per-vlan entry
  *
  * @vnode: rhashtable member
  * @vid: VLAN id
  * @flags: bridge vlan flags
+ * @priv_flags: private (in-kernel) bridge vlan flags
  * @stats: per-cpu VLAN statistics
  * @br: if MASTER flag set, this points to a bridge struct
  * @port: if MASTER flag unset, this points to a port struct
@@ -127,6 +135,7 @@ struct net_bridge_vlan {
        struct rhash_head               tnode;
        u16                             vid;
        u16                             flags;
+       u16                             priv_flags;
        struct br_vlan_stats __percpu   *stats;
        union {
                struct net_bridge       *br;
@@ -206,23 +215,14 @@ struct net_bridge_port_group {
 };
 
 struct net_bridge_mdb_entry {
-       struct hlist_node               hlist[2];
+       struct rhash_head               rhnode;
        struct net_bridge               *br;
        struct net_bridge_port_group __rcu *ports;
        struct rcu_head                 rcu;
        struct timer_list               timer;
        struct br_ip                    addr;
        bool                            host_joined;
-};
-
-struct net_bridge_mdb_htable {
-       struct hlist_head               *mhash;
-       struct rcu_head                 rcu;
-       struct net_bridge_mdb_htable    *old;
-       u32                             size;
-       u32                             max;
-       u32                             secret;
-       u32                             ver;
+       struct hlist_node               mdb_node;
 };
 
 struct net_bridge_port {
@@ -321,6 +321,7 @@ enum net_bridge_opts {
        BROPT_NEIGH_SUPPRESS_ENABLED,
        BROPT_MTU_SET_BY_USER,
        BROPT_VLAN_STATS_PER_PORT,
+       BROPT_NO_LL_LEARN,
 };
 
 struct net_bridge {
@@ -373,7 +374,6 @@ struct net_bridge {
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 
-       u32                             hash_elasticity;
        u32                             hash_max;
 
        u32                             multicast_last_member_count;
@@ -392,7 +392,9 @@ struct net_bridge {
        unsigned long                   multicast_query_response_interval;
        unsigned long                   multicast_startup_query_interval;
 
-       struct net_bridge_mdb_htable __rcu *mdb;
+       struct rhashtable               mdb_hash_tbl;
+
+       struct hlist_head               mdb_list;
        struct hlist_head               router_list;
 
        struct timer_list               multicast_router_timer;
@@ -500,6 +502,14 @@ static inline int br_opt_get(const struct net_bridge *br,
        return test_bit(opt, &br->options);
 }
 
+int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
+                     struct netlink_ext_ack *extack);
+int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt);
+int br_boolopt_multi_toggle(struct net_bridge *br,
+                           struct br_boolopt_multi *bm,
+                           struct netlink_ext_ack *extack);
+void br_boolopt_multi_get(const struct net_bridge *br,
+                         struct br_boolopt_multi *bm);
 void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on);
 
 /* br_device.c */
@@ -565,6 +575,9 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
               const unsigned char *addr, u16 vid, u16 nlh_flags);
 int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
                struct net_device *dev, struct net_device *fdev, int *idx);
+int br_fdb_get(struct sk_buff *skb, struct nlattr *tb[], struct net_device *dev,
+              const unsigned char *addr, u16 vid, u32 portid, u32 seq,
+              struct netlink_ext_ack *extack);
 int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
 void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
 int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
@@ -643,7 +656,6 @@ int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd,
 
 /* br_multicast.c */
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
-extern unsigned int br_mdb_rehash_seq;
 int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
                     struct sk_buff *skb, u16 vid);
 struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
@@ -668,17 +680,15 @@ int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val);
 int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val);
 #endif
 struct net_bridge_mdb_entry *
-br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, struct br_ip *dst);
+br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst);
 struct net_bridge_mdb_entry *
-br_multicast_new_group(struct net_bridge *br, struct net_bridge_port *port,
-                      struct br_ip *group);
-void br_multicast_free_pg(struct rcu_head *head);
+br_multicast_new_group(struct net_bridge *br, struct br_ip *group);
 struct net_bridge_port_group *
 br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
                            struct net_bridge_port_group __rcu *next,
                            unsigned char flags, const unsigned char *src);
-void br_mdb_init(void);
-void br_mdb_uninit(void);
+int br_mdb_hash_init(struct net_bridge *br);
+void br_mdb_hash_fini(struct net_bridge *br);
 void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
                   struct br_ip *group, int type, u8 flags);
 void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
@@ -690,6 +700,8 @@ void br_multicast_uninit_stats(struct net_bridge *br);
 void br_multicast_get_stats(const struct net_bridge *br,
                            const struct net_bridge_port *p,
                            struct br_mcast_stats *dest);
+void br_mdb_init(void);
+void br_mdb_uninit(void);
 
 #define mlock_dereference(X, br) \
        rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))
@@ -815,6 +827,15 @@ static inline void br_mdb_uninit(void)
 {
 }
 
+static inline int br_mdb_hash_init(struct net_bridge *br)
+{
+       return 0;
+}
+
+static inline void br_mdb_hash_fini(struct net_bridge *br)
+{
+}
+
 static inline void br_multicast_count(struct net_bridge *br,
                                      const struct net_bridge_port *p,
                                      const struct sk_buff *skb,
@@ -850,7 +871,7 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
                               struct net_bridge_vlan_group *vg,
                               struct sk_buff *skb);
 int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags,
-               bool *changed);
+               bool *changed, struct netlink_ext_ack *extack);
 int br_vlan_delete(struct net_bridge *br, u16 vid);
 void br_vlan_flush(struct net_bridge *br);
 struct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group *vg, u16 vid);
@@ -863,12 +884,13 @@ int br_vlan_set_stats(struct net_bridge *br, unsigned long val);
 int br_vlan_set_stats_per_port(struct net_bridge *br, unsigned long val);
 int br_vlan_init(struct net_bridge *br);
 int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val);
-int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid);
+int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
+                              struct netlink_ext_ack *extack);
 int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
-                bool *changed);
+                bool *changed, struct netlink_ext_ack *extack);
 int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
 void nbp_vlan_flush(struct net_bridge_port *port);
-int nbp_vlan_init(struct net_bridge_port *port);
+int nbp_vlan_init(struct net_bridge_port *port, struct netlink_ext_ack *extack);
 int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask);
 void br_vlan_get_stats(const struct net_bridge_vlan *v,
                       struct br_vlan_stats *stats);
@@ -953,7 +975,7 @@ static inline struct sk_buff *br_handle_vlan(struct net_bridge *br,
 }
 
 static inline int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags,
-                             bool *changed)
+                             bool *changed, struct netlink_ext_ack *extack)
 {
        *changed = false;
        return -EOPNOTSUPP;
@@ -978,7 +1000,7 @@ static inline int br_vlan_init(struct net_bridge *br)
 }
 
 static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
-                              bool *changed)
+                              bool *changed, struct netlink_ext_ack *extack)
 {
        *changed = false;
        return -EOPNOTSUPP;
@@ -999,7 +1021,8 @@ static inline struct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group
        return NULL;
 }
 
-static inline int nbp_vlan_init(struct net_bridge_port *port)
+static inline int nbp_vlan_init(struct net_bridge_port *port,
+                               struct netlink_ext_ack *extack)
 {
        return 0;
 }
@@ -1120,7 +1143,8 @@ int br_netlink_init(void);
 void br_netlink_fini(void);
 void br_ifinfo_notify(int event, const struct net_bridge *br,
                      const struct net_bridge_port *port);
-int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
+int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags,
+              struct netlink_ext_ack *extack);
 int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
 int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev,
               u32 filter_mask, int nlflags);
@@ -1155,7 +1179,8 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
                               unsigned long mask);
 void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb,
                             int type);
-int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags);
+int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
+                              struct netlink_ext_ack *extack);
 int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid);
 
 static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
@@ -1187,7 +1212,8 @@ static inline int br_switchdev_set_port_flag(struct net_bridge_port *p,
 }
 
 static inline int br_switchdev_port_vlan_add(struct net_device *dev,
-                                            u16 vid, u16 flags)
+                                            u16 vid, u16 flags,
+                                            struct netlink_ext_ack *extack)
 {
        return -EOPNOTSUPP;
 }
index b993df7..035ff59 100644 (file)
@@ -140,7 +140,8 @@ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
        }
 }
 
-int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags)
+int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
+                              struct netlink_ext_ack *extack)
 {
        struct switchdev_obj_port_vlan v = {
                .obj.orig_dev = dev,
@@ -150,7 +151,7 @@ int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags)
                .vid_end = vid,
        };
 
-       return switchdev_port_obj_add(dev, &v.obj);
+       return switchdev_port_obj_add(dev, &v.obj, extack);
 }
 
 int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
index 60182be..b05b94e 100644 (file)
@@ -328,6 +328,27 @@ static ssize_t flush_store(struct device *d,
 }
 static DEVICE_ATTR_WO(flush);
 
+static ssize_t no_linklocal_learn_show(struct device *d,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct net_bridge *br = to_bridge(d);
+       return sprintf(buf, "%d\n", br_boolopt_get(br, BR_BOOLOPT_NO_LL_LEARN));
+}
+
+static int set_no_linklocal_learn(struct net_bridge *br, unsigned long val)
+{
+       return br_boolopt_toggle(br, BR_BOOLOPT_NO_LL_LEARN, !!val, NULL);
+}
+
+static ssize_t no_linklocal_learn_store(struct device *d,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       return store_bridge_parm(d, buf, len, set_no_linklocal_learn);
+}
+static DEVICE_ATTR_RW(no_linklocal_learn);
+
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 static ssize_t multicast_router_show(struct device *d,
                                     struct device_attribute *attr, char *buf)
@@ -403,13 +424,13 @@ static DEVICE_ATTR_RW(multicast_querier);
 static ssize_t hash_elasticity_show(struct device *d,
                                    struct device_attribute *attr, char *buf)
 {
-       struct net_bridge *br = to_bridge(d);
-       return sprintf(buf, "%u\n", br->hash_elasticity);
+       return sprintf(buf, "%u\n", RHT_ELASTICITY);
 }
 
 static int set_elasticity(struct net_bridge *br, unsigned long val)
 {
-       br->hash_elasticity = val;
+       br_warn(br, "the hash_elasticity option has been deprecated and is always %u\n",
+               RHT_ELASTICITY);
        return 0;
 }
 
@@ -428,10 +449,16 @@ static ssize_t hash_max_show(struct device *d, struct device_attribute *attr,
        return sprintf(buf, "%u\n", br->hash_max);
 }
 
+static int set_hash_max(struct net_bridge *br, unsigned long val)
+{
+       br->hash_max = val;
+       return 0;
+}
+
 static ssize_t hash_max_store(struct device *d, struct device_attribute *attr,
                              const char *buf, size_t len)
 {
-       return store_bridge_parm(d, buf, len, br_multicast_set_hash_max);
+       return store_bridge_parm(d, buf, len, set_hash_max);
 }
 static DEVICE_ATTR_RW(hash_max);
 
@@ -841,6 +868,7 @@ static struct attribute *bridge_attrs[] = {
        &dev_attr_gc_timer.attr,
        &dev_attr_group_addr.attr,
        &dev_attr_flush.attr,
+       &dev_attr_no_linklocal_learn.attr,
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
        &dev_attr_multicast_router.attr,
        &dev_attr_multicast_snooping.attr,
index 7c87a2f..88715ed 100644 (file)
@@ -320,9 +320,6 @@ static ssize_t brport_store(struct kobject *kobj,
        if (!rtnl_trylock())
                return restart_syscall();
 
-       if (!p->dev || !p->br)
-               goto out_unlock;
-
        if (brport_attr->store_raw) {
                char *buf_copy;
 
index a7e869d..4a2f311 100644 (file)
@@ -80,14 +80,14 @@ static bool __vlan_add_flags(struct net_bridge_vlan *v, u16 flags)
 }
 
 static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br,
-                         u16 vid, u16 flags)
+                         u16 vid, u16 flags, struct netlink_ext_ack *extack)
 {
        int err;
 
        /* Try switchdev op first. In case it is not supported, fallback to
         * 8021q add.
         */
-       err = br_switchdev_port_vlan_add(dev, vid, flags);
+       err = br_switchdev_port_vlan_add(dev, vid, flags, extack);
        if (err == -EOPNOTSUPP)
                return vlan_vid_add(dev, br->vlan_proto, vid);
        return err;
@@ -139,7 +139,9 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
 /* Returns a master vlan, if it didn't exist it gets created. In all cases a
  * a reference is taken to the master vlan before returning.
  */
-static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid)
+static struct net_bridge_vlan *
+br_vlan_get_master(struct net_bridge *br, u16 vid,
+                  struct netlink_ext_ack *extack)
 {
        struct net_bridge_vlan_group *vg;
        struct net_bridge_vlan *masterv;
@@ -150,7 +152,7 @@ static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid
                bool changed;
 
                /* missing global ctx, create it now */
-               if (br_vlan_add(br, vid, 0, &changed))
+               if (br_vlan_add(br, vid, 0, &changed, extack))
                        return NULL;
                masterv = br_vlan_find(vg, vid);
                if (WARN_ON(!masterv))
@@ -197,7 +199,7 @@ static void nbp_vlan_rcu_free(struct rcu_head *rcu)
        v = container_of(rcu, struct net_bridge_vlan, rcu);
        WARN_ON(br_vlan_is_master(v));
        /* if we had per-port stats configured then free them here */
-       if (v->brvlan->stats != v->stats)
+       if (v->priv_flags & BR_VLFLAG_PER_PORT_STATS)
                free_percpu(v->stats);
        v->stats = NULL;
        kfree(v);
@@ -214,7 +216,8 @@ static void nbp_vlan_rcu_free(struct rcu_head *rcu)
  * 4. same as 3 but with both master and brentry flags set so the entry
  *    will be used for filtering in both the port and the bridge
  */
-static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
+static int __vlan_add(struct net_bridge_vlan *v, u16 flags,
+                     struct netlink_ext_ack *extack)
 {
        struct net_bridge_vlan *masterv = NULL;
        struct net_bridge_port *p = NULL;
@@ -239,7 +242,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
                 * This ensures tagged traffic enters the bridge when
                 * promiscuous mode is disabled by br_manage_promisc().
                 */
-               err = __vlan_vid_add(dev, br, v->vid, flags);
+               err = __vlan_vid_add(dev, br, v->vid, flags, extack);
                if (err)
                        goto out;
 
@@ -249,12 +252,12 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
 
                        err = br_vlan_add(br, v->vid,
                                          flags | BRIDGE_VLAN_INFO_BRENTRY,
-                                         &changed);
+                                         &changed, extack);
                        if (err)
                                goto out_filt;
                }
 
-               masterv = br_vlan_get_master(br, v->vid);
+               masterv = br_vlan_get_master(br, v->vid, extack);
                if (!masterv)
                        goto out_filt;
                v->brvlan = masterv;
@@ -264,11 +267,12 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
                                err = -ENOMEM;
                                goto out_filt;
                        }
+                       v->priv_flags |= BR_VLFLAG_PER_PORT_STATS;
                } else {
                        v->stats = masterv->stats;
                }
        } else {
-               err = br_switchdev_port_vlan_add(dev, v->vid, flags);
+               err = br_switchdev_port_vlan_add(dev, v->vid, flags, extack);
                if (err && err != -EOPNOTSUPP)
                        goto out;
        }
@@ -590,11 +594,12 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
 static int br_vlan_add_existing(struct net_bridge *br,
                                struct net_bridge_vlan_group *vg,
                                struct net_bridge_vlan *vlan,
-                               u16 flags, bool *changed)
+                               u16 flags, bool *changed,
+                               struct netlink_ext_ack *extack)
 {
        int err;
 
-       err = br_switchdev_port_vlan_add(br->dev, vlan->vid, flags);
+       err = br_switchdev_port_vlan_add(br->dev, vlan->vid, flags, extack);
        if (err && err != -EOPNOTSUPP)
                return err;
 
@@ -633,7 +638,8 @@ err_flags:
  * Must be called with vid in range from 1 to 4094 inclusive.
  * changed must be true only if the vlan was created or updated
  */
-int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed)
+int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed,
+               struct netlink_ext_ack *extack)
 {
        struct net_bridge_vlan_group *vg;
        struct net_bridge_vlan *vlan;
@@ -645,7 +651,8 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed)
        vg = br_vlan_group(br);
        vlan = br_vlan_find(vg, vid);
        if (vlan)
-               return br_vlan_add_existing(br, vg, vlan, flags, changed);
+               return br_vlan_add_existing(br, vg, vlan, flags, changed,
+                                           extack);
 
        vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
        if (!vlan)
@@ -662,7 +669,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed)
        vlan->br = br;
        if (flags & BRIDGE_VLAN_INFO_BRENTRY)
                refcount_set(&vlan->refcnt, 1);
-       ret = __vlan_add(vlan, flags);
+       ret = __vlan_add(vlan, flags, extack);
        if (ret) {
                free_percpu(vlan->stats);
                kfree(vlan);
@@ -913,7 +920,8 @@ static void br_vlan_disable_default_pvid(struct net_bridge *br)
        br->default_pvid = 0;
 }
 
-int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
+int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
+                              struct netlink_ext_ack *extack)
 {
        const struct net_bridge_vlan *pvent;
        struct net_bridge_vlan_group *vg;
@@ -945,7 +953,7 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
                                  BRIDGE_VLAN_INFO_PVID |
                                  BRIDGE_VLAN_INFO_UNTAGGED |
                                  BRIDGE_VLAN_INFO_BRENTRY,
-                                 &vlchange);
+                                 &vlchange, extack);
                if (err)
                        goto out;
                br_vlan_delete(br, old_pvid);
@@ -965,7 +973,7 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
                err = nbp_vlan_add(p, pvid,
                                   BRIDGE_VLAN_INFO_PVID |
                                   BRIDGE_VLAN_INFO_UNTAGGED,
-                                  &vlchange);
+                                  &vlchange, extack);
                if (err)
                        goto err_port;
                nbp_vlan_delete(p, old_pvid);
@@ -987,7 +995,7 @@ err_port:
                        nbp_vlan_add(p, old_pvid,
                                     BRIDGE_VLAN_INFO_PVID |
                                     BRIDGE_VLAN_INFO_UNTAGGED,
-                                    &vlchange);
+                                    &vlchange, NULL);
                nbp_vlan_delete(p, pvid);
        }
 
@@ -997,7 +1005,7 @@ err_port:
                                    BRIDGE_VLAN_INFO_PVID |
                                    BRIDGE_VLAN_INFO_UNTAGGED |
                                    BRIDGE_VLAN_INFO_BRENTRY,
-                                   &vlchange);
+                                   &vlchange, NULL);
                br_vlan_delete(br, pvid);
        }
        goto out;
@@ -1020,7 +1028,7 @@ int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val)
                err = -EPERM;
                goto out;
        }
-       err = __br_vlan_set_default_pvid(br, pvid);
+       err = __br_vlan_set_default_pvid(br, pvid, NULL);
 out:
        return err;
 }
@@ -1046,7 +1054,7 @@ int br_vlan_init(struct net_bridge *br)
        rcu_assign_pointer(br->vlgrp, vg);
        ret = br_vlan_add(br, 1,
                          BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED |
-                         BRIDGE_VLAN_INFO_BRENTRY, &changed);
+                         BRIDGE_VLAN_INFO_BRENTRY, &changed, NULL);
        if (ret)
                goto err_vlan_add;
 
@@ -1063,7 +1071,7 @@ err_rhtbl:
        goto out;
 }
 
-int nbp_vlan_init(struct net_bridge_port *p)
+int nbp_vlan_init(struct net_bridge_port *p, struct netlink_ext_ack *extack)
 {
        struct switchdev_attr attr = {
                .orig_dev = p->br->dev,
@@ -1096,7 +1104,7 @@ int nbp_vlan_init(struct net_bridge_port *p)
                ret = nbp_vlan_add(p, p->br->default_pvid,
                                   BRIDGE_VLAN_INFO_PVID |
                                   BRIDGE_VLAN_INFO_UNTAGGED,
-                                  &changed);
+                                  &changed, extack);
                if (ret)
                        goto err_vlan_add;
        }
@@ -1121,7 +1129,7 @@ err_vlan_enabled:
  * changed must be true only if the vlan was created or updated
  */
 int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
-                bool *changed)
+                bool *changed, struct netlink_ext_ack *extack)
 {
        struct net_bridge_vlan *vlan;
        int ret;
@@ -1132,7 +1140,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
        vlan = br_vlan_find(nbp_vlan_group(port), vid);
        if (vlan) {
                /* Pass the flags to the hardware bridge */
-               ret = br_switchdev_port_vlan_add(port->dev, vid, flags);
+               ret = br_switchdev_port_vlan_add(port->dev, vid, flags, extack);
                if (ret && ret != -EOPNOTSUPP)
                        return ret;
                *changed = __vlan_add_flags(vlan, flags);
@@ -1146,7 +1154,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
 
        vlan->vid = vid;
        vlan->port = port;
-       ret = __vlan_add(vlan, flags);
+       ret = __vlan_add(vlan, flags, extack);
        if (ret)
                kfree(vlan);
        else
@@ -1216,9 +1224,13 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v,
 int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid)
 {
        struct net_bridge_vlan_group *vg;
+       struct net_bridge_port *p;
 
        ASSERT_RTNL();
-       if (netif_is_bridge_master(dev))
+       p = br_port_get_check_rtnl(dev);
+       if (p)
+               vg = nbp_vlan_group(p);
+       else if (netif_is_bridge_master(dev))
                vg = br_vlan_group(netdev_priv(dev));
        else
                return -EINVAL;
index 1051eee..c702075 100644 (file)
@@ -745,18 +745,19 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
        } else
                ifindex = ro->ifindex;
 
-       if (ro->fd_frames) {
+       dev = dev_get_by_index(sock_net(sk), ifindex);
+       if (!dev)
+               return -ENXIO;
+
+       err = -EINVAL;
+       if (ro->fd_frames && dev->mtu == CANFD_MTU) {
                if (unlikely(size != CANFD_MTU && size != CAN_MTU))
-                       return -EINVAL;
+                       goto put_dev;
        } else {
                if (unlikely(size != CAN_MTU))
-                       return -EINVAL;
+                       goto put_dev;
        }
 
-       dev = dev_get_by_index(sock_net(sk), ifindex);
-       if (!dev)
-               return -ENXIO;
-
        skb = sock_alloc_send_skb(sk, size + sizeof(struct can_skb_priv),
                                  msg->msg_flags & MSG_DONTWAIT, &err);
        if (!skb)
@@ -770,7 +771,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
        if (err < 0)
                goto free_skb;
 
-       sock_tx_timestamp(sk, sk->sk_tsflags, &skb_shinfo(skb)->tx_flags);
+       skb_setup_tx_timestamp(skb, sk->sk_tsflags);
 
        skb->dev = dev;
        skb->sk  = sk;
index 57fcc6b..2f126ef 100644 (file)
@@ -580,9 +580,15 @@ static int ceph_tcp_sendpage(struct socket *sock, struct page *page,
        struct bio_vec bvec;
        int ret;
 
-       /* sendpage cannot properly handle pages with page_count == 0,
-        * we need to fallback to sendmsg if that's the case */
-       if (page_count(page) >= 1)
+       /*
+        * sendpage cannot properly handle pages with page_count == 0,
+        * we need to fall back to sendmsg if that's the case.
+        *
+        * Same goes for slab pages: skb_can_coalesce() allows
+        * coalescing neighboring slab objects into a single frag which
+        * triggers one of hardened usercopy checks.
+        */
+       if (page_count(page) >= 1 && !PageSlab(page))
                return __ceph_tcp_sendpage(sock, page, offset, size, more);
 
        bvec.bv_page = page;
index 07983b9..4bf62b1 100644 (file)
@@ -767,7 +767,7 @@ int skb_copy_and_csum_datagram_msg(struct sk_buff *skb,
 
                if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
                    !skb->csum_complete_sw)
-                       netdev_rx_csum_fault(NULL);
+                       netdev_rx_csum_fault(NULL, skb);
        }
        return 0;
 fault:
index bf7e0a4..1b5a441 100644 (file)
 #include <linux/sctp.h>
 #include <net/udp_tunnel.h>
 #include <linux/net_namespace.h>
+#include <linux/indirect_call_wrapper.h>
 
 #include "net-sysfs.h"
 
@@ -162,6 +163,9 @@ static struct list_head offload_base __read_mostly;
 static int netif_rx_internal(struct sk_buff *skb);
 static int call_netdevice_notifiers_info(unsigned long val,
                                         struct netdev_notifier_info *info);
+static int call_netdevice_notifiers_extack(unsigned long val,
+                                          struct net_device *dev,
+                                          struct netlink_ext_ack *extack);
 static struct napi_struct *napi_by_id(unsigned int napi_id);
 
 /*
@@ -1361,7 +1365,7 @@ void netdev_notify_peers(struct net_device *dev)
 }
 EXPORT_SYMBOL(netdev_notify_peers);
 
-static int __dev_open(struct net_device *dev)
+static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
        int ret;
@@ -1377,7 +1381,7 @@ static int __dev_open(struct net_device *dev)
         */
        netpoll_poll_disable(dev);
 
-       ret = call_netdevice_notifiers(NETDEV_PRE_UP, dev);
+       ret = call_netdevice_notifiers_extack(NETDEV_PRE_UP, dev, extack);
        ret = notifier_to_errno(ret);
        if (ret)
                return ret;
@@ -1406,7 +1410,8 @@ static int __dev_open(struct net_device *dev)
 
 /**
  *     dev_open        - prepare an interface for use.
- *     @dev:   device to open
+ *     @dev: device to open
+ *     @extack: netlink extended ack
  *
  *     Takes a device from down to up state. The device's private open
  *     function is invoked and then the multicast lists are loaded. Finally
@@ -1416,14 +1421,14 @@ static int __dev_open(struct net_device *dev)
  *     Calling this function on an active interface is a nop. On a failure
  *     a negative errno code is returned.
  */
-int dev_open(struct net_device *dev)
+int dev_open(struct net_device *dev, struct netlink_ext_ack *extack)
 {
        int ret;
 
        if (dev->flags & IFF_UP)
                return 0;
 
-       ret = __dev_open(dev);
+       ret = __dev_open(dev, extack);
        if (ret < 0)
                return ret;
 
@@ -1585,6 +1590,7 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd)
        N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN)
        N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO)
        N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO)
+       N(PRE_CHANGEADDR)
        }
 #undef N
        return "UNKNOWN_NETDEV_EVENT";
@@ -1733,6 +1739,18 @@ static int call_netdevice_notifiers_info(unsigned long val,
        return raw_notifier_call_chain(&netdev_chain, val, info);
 }
 
+static int call_netdevice_notifiers_extack(unsigned long val,
+                                          struct net_device *dev,
+                                          struct netlink_ext_ack *extack)
+{
+       struct netdev_notifier_info info = {
+               .dev = dev,
+               .extack = extack,
+       };
+
+       return call_netdevice_notifiers_info(val, &info);
+}
+
 /**
  *     call_netdevice_notifiers - call all network notifier blocks
  *      @val: value passed unmodified to notifier function
@@ -1744,11 +1762,7 @@ static int call_netdevice_notifiers_info(unsigned long val,
 
 int call_netdevice_notifiers(unsigned long val, struct net_device *dev)
 {
-       struct netdev_notifier_info info = {
-               .dev = dev,
-       };
-
-       return call_netdevice_notifiers_info(val, &info);
+       return call_netdevice_notifiers_extack(val, dev, NULL);
 }
 EXPORT_SYMBOL(call_netdevice_notifiers);
 
@@ -2175,6 +2189,20 @@ static bool remove_xps_queue_cpu(struct net_device *dev,
        return active;
 }
 
+static void reset_xps_maps(struct net_device *dev,
+                          struct xps_dev_maps *dev_maps,
+                          bool is_rxqs_map)
+{
+       if (is_rxqs_map) {
+               static_key_slow_dec_cpuslocked(&xps_rxqs_needed);
+               RCU_INIT_POINTER(dev->xps_rxqs_map, NULL);
+       } else {
+               RCU_INIT_POINTER(dev->xps_cpus_map, NULL);
+       }
+       static_key_slow_dec_cpuslocked(&xps_needed);
+       kfree_rcu(dev_maps, rcu);
+}
+
 static void clean_xps_maps(struct net_device *dev, const unsigned long *mask,
                           struct xps_dev_maps *dev_maps, unsigned int nr_ids,
                           u16 offset, u16 count, bool is_rxqs_map)
@@ -2186,18 +2214,15 @@ static void clean_xps_maps(struct net_device *dev, const unsigned long *mask,
             j < nr_ids;)
                active |= remove_xps_queue_cpu(dev, dev_maps, j, offset,
                                               count);
-       if (!active) {
-               if (is_rxqs_map) {
-                       RCU_INIT_POINTER(dev->xps_rxqs_map, NULL);
-               } else {
-                       RCU_INIT_POINTER(dev->xps_cpus_map, NULL);
+       if (!active)
+               reset_xps_maps(dev, dev_maps, is_rxqs_map);
 
-                       for (i = offset + (count - 1); count--; i--)
-                               netdev_queue_numa_node_write(
-                                       netdev_get_tx_queue(dev, i),
-                                                       NUMA_NO_NODE);
+       if (!is_rxqs_map) {
+               for (i = offset + (count - 1); count--; i--) {
+                       netdev_queue_numa_node_write(
+                               netdev_get_tx_queue(dev, i),
+                               NUMA_NO_NODE);
                }
-               kfree_rcu(dev_maps, rcu);
        }
 }
 
@@ -2234,10 +2259,6 @@ static void netif_reset_xps_queues(struct net_device *dev, u16 offset,
                       false);
 
 out_no_maps:
-       if (static_key_enabled(&xps_rxqs_needed))
-               static_key_slow_dec_cpuslocked(&xps_rxqs_needed);
-
-       static_key_slow_dec_cpuslocked(&xps_needed);
        mutex_unlock(&xps_map_mutex);
        cpus_read_unlock();
 }
@@ -2355,9 +2376,12 @@ int __netif_set_xps_queue(struct net_device *dev, const unsigned long *mask,
        if (!new_dev_maps)
                goto out_no_new_maps;
 
-       static_key_slow_inc_cpuslocked(&xps_needed);
-       if (is_rxqs_map)
-               static_key_slow_inc_cpuslocked(&xps_rxqs_needed);
+       if (!dev_maps) {
+               /* Increment static keys at most once per type */
+               static_key_slow_inc_cpuslocked(&xps_needed);
+               if (is_rxqs_map)
+                       static_key_slow_inc_cpuslocked(&xps_rxqs_needed);
+       }
 
        for (j = -1; j = netif_attrmask_next(j, possible_mask, nr_ids),
             j < nr_ids;) {
@@ -2455,13 +2479,8 @@ out_no_new_maps:
        }
 
        /* free map if not active */
-       if (!active) {
-               if (is_rxqs_map)
-                       RCU_INIT_POINTER(dev->xps_rxqs_map, NULL);
-               else
-                       RCU_INIT_POINTER(dev->xps_cpus_map, NULL);
-               kfree_rcu(dev_maps, rcu);
-       }
+       if (!active)
+               reset_xps_maps(dev, dev_maps, is_rxqs_map);
 
 out_no_maps:
        mutex_unlock(&xps_map_mutex);
@@ -3091,10 +3110,17 @@ EXPORT_SYMBOL(__skb_gso_segment);
 
 /* Take action when hardware reception checksum errors are detected. */
 #ifdef CONFIG_BUG
-void netdev_rx_csum_fault(struct net_device *dev)
+void netdev_rx_csum_fault(struct net_device *dev, struct sk_buff *skb)
 {
        if (net_ratelimit()) {
                pr_err("%s: hw csum failure\n", dev ? dev->name : "<unknown>");
+               if (dev)
+                       pr_err("dev features: %pNF\n", &dev->features);
+               pr_err("skb len=%u data_len=%u pkt_type=%u gso_size=%u gso_type=%u nr_frags=%u ip_summed=%u csum=%x csum_complete_sw=%d csum_valid=%d csum_level=%u\n",
+                      skb->len, skb->data_len, skb->pkt_type,
+                      skb_shinfo(skb)->gso_size, skb_shinfo(skb)->gso_type,
+                      skb_shinfo(skb)->nr_frags, skb->ip_summed, skb->csum,
+                      skb->csum_complete_sw, skb->csum_valid, skb->csum_level);
                dump_stack();
        }
 }
@@ -4520,9 +4546,14 @@ static int netif_rx_internal(struct sk_buff *skb)
 
 int netif_rx(struct sk_buff *skb)
 {
+       int ret;
+
        trace_netif_rx_entry(skb);
 
-       return netif_rx_internal(skb);
+       ret = netif_rx_internal(skb);
+       trace_netif_rx_exit(ret);
+
+       return ret;
 }
 EXPORT_SYMBOL(netif_rx);
 
@@ -4537,6 +4568,7 @@ int netif_rx_ni(struct sk_buff *skb)
        if (local_softirq_pending())
                do_softirq();
        preempt_enable();
+       trace_netif_rx_ni_exit(err);
 
        return err;
 }
@@ -5009,7 +5041,7 @@ static void __netif_receive_skb_list_core(struct list_head *head, bool pfmemallo
                struct net_device *orig_dev = skb->dev;
                struct packet_type *pt_prev = NULL;
 
-               list_del(&skb->list);
+               skb_list_del_init(skb);
                __netif_receive_skb_core(skb, pfmemalloc, &pt_prev);
                if (!pt_prev)
                        continue;
@@ -5165,7 +5197,7 @@ static void netif_receive_skb_list_internal(struct list_head *head)
        INIT_LIST_HEAD(&sublist);
        list_for_each_entry_safe(skb, next, head, list) {
                net_timestamp_check(netdev_tstamp_prequeue, skb);
-               list_del(&skb->list);
+               skb_list_del_init(skb);
                if (!skb_defer_rx_timestamp(skb))
                        list_add_tail(&skb->list, &sublist);
        }
@@ -5176,7 +5208,7 @@ static void netif_receive_skb_list_internal(struct list_head *head)
                rcu_read_lock();
                list_for_each_entry_safe(skb, next, head, list) {
                        xdp_prog = rcu_dereference(skb->dev->xdp_prog);
-                       list_del(&skb->list);
+                       skb_list_del_init(skb);
                        if (do_xdp_generic(xdp_prog, skb) == XDP_PASS)
                                list_add_tail(&skb->list, &sublist);
                }
@@ -5195,7 +5227,7 @@ static void netif_receive_skb_list_internal(struct list_head *head)
 
                        if (cpu >= 0) {
                                /* Will be handled, remove from list */
-                               list_del(&skb->list);
+                               skb_list_del_init(skb);
                                enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
                        }
                }
@@ -5222,9 +5254,14 @@ static void netif_receive_skb_list_internal(struct list_head *head)
  */
 int netif_receive_skb(struct sk_buff *skb)
 {
+       int ret;
+
        trace_netif_receive_skb_entry(skb);
 
-       return netif_receive_skb_internal(skb);
+       ret = netif_receive_skb_internal(skb);
+       trace_netif_receive_skb_exit(ret);
+
+       return ret;
 }
 EXPORT_SYMBOL(netif_receive_skb);
 
@@ -5244,9 +5281,12 @@ void netif_receive_skb_list(struct list_head *head)
 
        if (list_empty(head))
                return;
-       list_for_each_entry(skb, head, list)
-               trace_netif_receive_skb_list_entry(skb);
+       if (trace_netif_receive_skb_list_entry_enabled()) {
+               list_for_each_entry(skb, head, list)
+                       trace_netif_receive_skb_list_entry(skb);
+       }
        netif_receive_skb_list_internal(head);
+       trace_netif_receive_skb_list_exit(0);
 }
 EXPORT_SYMBOL(netif_receive_skb_list);
 
@@ -5299,6 +5339,8 @@ static void flush_all_backlogs(void)
        put_online_cpus();
 }
 
+INDIRECT_CALLABLE_DECLARE(int inet_gro_complete(struct sk_buff *, int));
+INDIRECT_CALLABLE_DECLARE(int ipv6_gro_complete(struct sk_buff *, int));
 static int napi_gro_complete(struct sk_buff *skb)
 {
        struct packet_offload *ptype;
@@ -5318,7 +5360,9 @@ static int napi_gro_complete(struct sk_buff *skb)
                if (ptype->type != type || !ptype->callbacks.gro_complete)
                        continue;
 
-               err = ptype->callbacks.gro_complete(skb, 0);
+               err = INDIRECT_CALL_INET(ptype->callbacks.gro_complete,
+                                        ipv6_gro_complete, inet_gro_complete,
+                                        skb, 0);
                break;
        }
        rcu_read_unlock();
@@ -5357,11 +5401,13 @@ static void __napi_gro_flush_chain(struct napi_struct *napi, u32 index,
  */
 void napi_gro_flush(struct napi_struct *napi, bool flush_old)
 {
-       u32 i;
+       unsigned long bitmask = napi->gro_bitmask;
+       unsigned int i, base = ~0U;
 
-       for (i = 0; i < GRO_HASH_BUCKETS; i++) {
-               if (test_bit(i, &napi->gro_bitmask))
-                       __napi_gro_flush_chain(napi, i, flush_old);
+       while ((i = ffs(bitmask)) != 0) {
+               bitmask >>= i;
+               base += i;
+               __napi_gro_flush_chain(napi, base, flush_old);
        }
 }
 EXPORT_SYMBOL(napi_gro_flush);
@@ -5463,6 +5509,10 @@ static void gro_flush_oldest(struct list_head *head)
        napi_gro_complete(oldest);
 }
 
+INDIRECT_CALLABLE_DECLARE(struct sk_buff *inet_gro_receive(struct list_head *,
+                                                          struct sk_buff *));
+INDIRECT_CALLABLE_DECLARE(struct sk_buff *ipv6_gro_receive(struct list_head *,
+                                                          struct sk_buff *));
 static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
 {
        u32 hash = skb_get_hash_raw(skb) & (GRO_HASH_BUCKETS - 1);
@@ -5512,7 +5562,9 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
                        NAPI_GRO_CB(skb)->csum_valid = 0;
                }
 
-               pp = ptype->callbacks.gro_receive(gro_head, skb);
+               pp = INDIRECT_CALL_INET(ptype->callbacks.gro_receive,
+                                       ipv6_gro_receive, inet_gro_receive,
+                                       gro_head, skb);
                break;
        }
        rcu_read_unlock();
@@ -5636,12 +5688,17 @@ static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
 
 gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
 {
+       gro_result_t ret;
+
        skb_mark_napi_id(skb, napi);
        trace_napi_gro_receive_entry(skb);
 
        skb_gro_reset_offset(skb);
 
-       return napi_skb_finish(dev_gro_receive(napi, skb), skb);
+       ret = napi_skb_finish(dev_gro_receive(napi, skb), skb);
+       trace_napi_gro_receive_exit(ret);
+
+       return ret;
 }
 EXPORT_SYMBOL(napi_gro_receive);
 
@@ -5657,6 +5714,10 @@ static void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb)
        __vlan_hwaccel_clear_tag(skb);
        skb->dev = napi->dev;
        skb->skb_iif = 0;
+
+       /* eth_type_trans() assumes pkt_type is PACKET_HOST */
+       skb->pkt_type = PACKET_HOST;
+
        skb->encapsulation = 0;
        skb_shinfo(skb)->gso_type = 0;
        skb->truesize = SKB_TRUESIZE(skb_end_offset(skb));
@@ -5755,6 +5816,7 @@ static struct sk_buff *napi_frags_skb(struct napi_struct *napi)
 
 gro_result_t napi_gro_frags(struct napi_struct *napi)
 {
+       gro_result_t ret;
        struct sk_buff *skb = napi_frags_skb(napi);
 
        if (!skb)
@@ -5762,7 +5824,10 @@ gro_result_t napi_gro_frags(struct napi_struct *napi)
 
        trace_napi_gro_frags_entry(skb);
 
-       return napi_frags_finish(napi, skb, dev_gro_receive(napi, skb));
+       ret = napi_frags_finish(napi, skb, dev_gro_receive(napi, skb));
+       trace_napi_gro_frags_exit(ret);
+
+       return ret;
 }
 EXPORT_SYMBOL(napi_gro_frags);
 
@@ -5778,10 +5843,11 @@ __sum16 __skb_gro_checksum_complete(struct sk_buff *skb)
 
        /* NAPI_GRO_CB(skb)->csum holds pseudo checksum */
        sum = csum_fold(csum_add(NAPI_GRO_CB(skb)->csum, wsum));
+       /* See comments in __skb_checksum_complete(). */
        if (likely(!sum)) {
                if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
                    !skb->csum_complete_sw)
-                       netdev_rx_csum_fault(skb->dev);
+                       netdev_rx_csum_fault(skb->dev, skb);
        }
 
        NAPI_GRO_CB(skb)->csum = wsum;
@@ -5968,11 +6034,14 @@ bool napi_complete_done(struct napi_struct *n, int work_done)
                if (work_done)
                        timeout = n->dev->gro_flush_timeout;
 
+               /* When the NAPI instance uses a timeout and keeps postponing
+                * it, we need to bound somehow the time packets are kept in
+                * the GRO layer
+                */
+               napi_gro_flush(n, !!timeout);
                if (timeout)
                        hrtimer_start(&n->timer, ns_to_ktime(timeout),
                                      HRTIMER_MODE_REL_PINNED);
-               else
-                       napi_gro_flush(n, false);
        }
        if (unlikely(!list_empty(&n->poll_list))) {
                /* If n->poll_list is not empty, we need to mask irqs */
@@ -6199,8 +6268,8 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
        napi->skb = NULL;
        napi->poll = poll;
        if (weight > NAPI_POLL_WEIGHT)
-               pr_err_once("netif_napi_add() called with weight %d on device %s\n",
-                           weight, dev->name);
+               netdev_err_once(dev, "%s() called with weight %d\n", __func__,
+                               weight);
        napi->weight = weight;
        list_add(&napi->dev_list, &dev->napi_list);
        napi->dev = dev;
@@ -7457,7 +7526,8 @@ unsigned int dev_get_flags(const struct net_device *dev)
 }
 EXPORT_SYMBOL(dev_get_flags);
 
-int __dev_change_flags(struct net_device *dev, unsigned int flags)
+int __dev_change_flags(struct net_device *dev, unsigned int flags,
+                      struct netlink_ext_ack *extack)
 {
        unsigned int old_flags = dev->flags;
        int ret;
@@ -7494,7 +7564,7 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags)
                if (old_flags & IFF_UP)
                        __dev_close(dev);
                else
-                       ret = __dev_open(dev);
+                       ret = __dev_open(dev, extack);
        }
 
        if ((flags ^ dev->gflags) & IFF_PROMISC) {
@@ -7554,16 +7624,18 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
  *     dev_change_flags - change device settings
  *     @dev: device
  *     @flags: device state flags
+ *     @extack: netlink extended ack
  *
  *     Change settings on device based state flags. The flags are
  *     in the userspace exported format.
  */
-int dev_change_flags(struct net_device *dev, unsigned int flags)
+int dev_change_flags(struct net_device *dev, unsigned int flags,
+                    struct netlink_ext_ack *extack)
 {
        int ret;
        unsigned int changes, old_flags = dev->flags, old_gflags = dev->gflags;
 
-       ret = __dev_change_flags(dev, flags);
+       ret = __dev_change_flags(dev, flags, extack);
        if (ret < 0)
                return ret;
 
@@ -7695,14 +7767,37 @@ void dev_set_group(struct net_device *dev, int new_group)
 }
 EXPORT_SYMBOL(dev_set_group);
 
+/**
+ *     dev_pre_changeaddr_notify - Call NETDEV_PRE_CHANGEADDR.
+ *     @dev: device
+ *     @addr: new address
+ *     @extack: netlink extended ack
+ */
+int dev_pre_changeaddr_notify(struct net_device *dev, const char *addr,
+                             struct netlink_ext_ack *extack)
+{
+       struct netdev_notifier_pre_changeaddr_info info = {
+               .info.dev = dev,
+               .info.extack = extack,
+               .dev_addr = addr,
+       };
+       int rc;
+
+       rc = call_netdevice_notifiers_info(NETDEV_PRE_CHANGEADDR, &info.info);
+       return notifier_to_errno(rc);
+}
+EXPORT_SYMBOL(dev_pre_changeaddr_notify);
+
 /**
  *     dev_set_mac_address - Change Media Access Control Address
  *     @dev: device
  *     @sa: new address
+ *     @extack: netlink extended ack
  *
  *     Change the hardware (MAC) address of the device
  */
-int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
+int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa,
+                       struct netlink_ext_ack *extack)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
        int err;
@@ -7713,6 +7808,9 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
                return -EINVAL;
        if (!netif_device_present(dev))
                return -ENODEV;
+       err = dev_pre_changeaddr_notify(dev, sa->sa_data, extack);
+       if (err)
+               return err;
        err = ops->ndo_set_mac_address(dev, sa);
        if (err)
                return err;
index 81a8cd4..a6723b3 100644 (file)
@@ -498,6 +498,9 @@ int dev_addr_add(struct net_device *dev, const unsigned char *addr,
 
        ASSERT_RTNL();
 
+       err = dev_pre_changeaddr_notify(dev, addr, NULL);
+       if (err)
+               return err;
        err = __hw_addr_add(&dev->dev_addrs, addr, dev->addr_len, addr_type);
        if (!err)
                call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
index 90e8aa3..31380fd 100644 (file)
@@ -234,7 +234,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
 
        switch (cmd) {
        case SIOCSIFFLAGS:      /* Set interface flags */
-               return dev_change_flags(dev, ifr->ifr_flags);
+               return dev_change_flags(dev, ifr->ifr_flags, NULL);
 
        case SIOCSIFMETRIC:     /* Set the metric on the interface
                                   (currently unused) */
@@ -246,7 +246,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
        case SIOCSIFHWADDR:
                if (dev->addr_len > sizeof(struct sockaddr))
                        return -EINVAL;
-               return dev_set_mac_address(dev, &ifr->ifr_hwaddr);
+               return dev_set_mac_address(dev, &ifr->ifr_hwaddr, NULL);
 
        case SIOCSIFHWBROADCAST:
                if (ifr->ifr_hwaddr.sa_family != dev->type)
index 3a4b29a..abb0da9 100644 (file)
@@ -2692,6 +2692,11 @@ static const struct devlink_param devlink_param_generic[] = {
                .name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME,
                .type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE,
        },
+       {
+               .id = DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
+               .name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME,
+               .type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE,
+       },
 };
 
 static int devlink_param_generic_verify(const struct devlink_param *param)
index e521c5e..447dd1b 100644 (file)
@@ -296,22 +296,18 @@ static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg,
                break;
 
        case SKF_AD_VLAN_TAG:
-       case SKF_AD_VLAN_TAG_PRESENT:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
-               BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
 
                /* dst_reg = *(u16 *) (src_reg + offsetof(vlan_tci)) */
                *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg,
                                      offsetof(struct sk_buff, vlan_tci));
-               if (skb_field == SKF_AD_VLAN_TAG) {
-                       *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg,
-                                               ~VLAN_TAG_PRESENT);
-               } else {
-                       /* dst_reg >>= 12 */
-                       *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, 12);
-                       /* dst_reg &= 1 */
+               break;
+       case SKF_AD_VLAN_TAG_PRESENT:
+               *insn++ = BPF_LDX_MEM(BPF_B, dst_reg, src_reg, PKT_VLAN_PRESENT_OFFSET());
+               if (PKT_VLAN_PRESENT_BIT)
+                       *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, PKT_VLAN_PRESENT_BIT);
+               if (PKT_VLAN_PRESENT_BIT < 7)
                        *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, 1);
-               }
                break;
        }
 
@@ -467,7 +463,8 @@ static bool convert_bpf_ld_abs(struct sock_filter *fp, struct bpf_insn **insnp)
                bool ldx_off_ok = offset <= S16_MAX;
 
                *insn++ = BPF_MOV64_REG(BPF_REG_TMP, BPF_REG_H);
-               *insn++ = BPF_ALU64_IMM(BPF_SUB, BPF_REG_TMP, offset);
+               if (offset)
+                       *insn++ = BPF_ALU64_IMM(BPF_SUB, BPF_REG_TMP, offset);
                *insn++ = BPF_JMP_IMM(BPF_JSLT, BPF_REG_TMP,
                                      size, 2 + endian + (!ldx_off_ok * 2));
                if (ldx_off_ok) {
@@ -2428,6 +2425,174 @@ static const struct bpf_func_proto bpf_msg_push_data_proto = {
        .arg4_type      = ARG_ANYTHING,
 };
 
+static void sk_msg_shift_left(struct sk_msg *msg, int i)
+{
+       int prev;
+
+       do {
+               prev = i;
+               sk_msg_iter_var_next(i);
+               msg->sg.data[prev] = msg->sg.data[i];
+       } while (i != msg->sg.end);
+
+       sk_msg_iter_prev(msg, end);
+}
+
+static void sk_msg_shift_right(struct sk_msg *msg, int i)
+{
+       struct scatterlist tmp, sge;
+
+       sk_msg_iter_next(msg, end);
+       sge = sk_msg_elem_cpy(msg, i);
+       sk_msg_iter_var_next(i);
+       tmp = sk_msg_elem_cpy(msg, i);
+
+       while (i != msg->sg.end) {
+               msg->sg.data[i] = sge;
+               sk_msg_iter_var_next(i);
+               sge = tmp;
+               tmp = sk_msg_elem_cpy(msg, i);
+       }
+}
+
+BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start,
+          u32, len, u64, flags)
+{
+       u32 i = 0, l, space, offset = 0;
+       u64 last = start + len;
+       int pop;
+
+       if (unlikely(flags))
+               return -EINVAL;
+
+       /* First find the starting scatterlist element */
+       i = msg->sg.start;
+       do {
+               l = sk_msg_elem(msg, i)->length;
+
+               if (start < offset + l)
+                       break;
+               offset += l;
+               sk_msg_iter_var_next(i);
+       } while (i != msg->sg.end);
+
+       /* Bounds checks: start and pop must be inside message */
+       if (start >= offset + l || last >= msg->sg.size)
+               return -EINVAL;
+
+       space = MAX_MSG_FRAGS - sk_msg_elem_used(msg);
+
+       pop = len;
+       /* --------------| offset
+        * -| start      |-------- len -------|
+        *
+        *  |----- a ----|-------- pop -------|----- b ----|
+        *  |______________________________________________| length
+        *
+        *
+        * a:   region at front of scatter element to save
+        * b:   region at back of scatter element to save when length > A + pop
+        * pop: region to pop from element, same as input 'pop' here will be
+        *      decremented below per iteration.
+        *
+        * Two top-level cases to handle when start != offset, first B is non
+        * zero and second B is zero corresponding to when a pop includes more
+        * than one element.
+        *
+        * Then if B is non-zero AND there is no space allocate space and
+        * compact A, B regions into page. If there is space shift ring to
+        * the rigth free'ing the next element in ring to place B, leaving
+        * A untouched except to reduce length.
+        */
+       if (start != offset) {
+               struct scatterlist *nsge, *sge = sk_msg_elem(msg, i);
+               int a = start;
+               int b = sge->length - pop - a;
+
+               sk_msg_iter_var_next(i);
+
+               if (pop < sge->length - a) {
+                       if (space) {
+                               sge->length = a;
+                               sk_msg_shift_right(msg, i);
+                               nsge = sk_msg_elem(msg, i);
+                               get_page(sg_page(sge));
+                               sg_set_page(nsge,
+                                           sg_page(sge),
+                                           b, sge->offset + pop + a);
+                       } else {
+                               struct page *page, *orig;
+                               u8 *to, *from;
+
+                               page = alloc_pages(__GFP_NOWARN |
+                                                  __GFP_COMP   | GFP_ATOMIC,
+                                                  get_order(a + b));
+                               if (unlikely(!page))
+                                       return -ENOMEM;
+
+                               sge->length = a;
+                               orig = sg_page(sge);
+                               from = sg_virt(sge);
+                               to = page_address(page);
+                               memcpy(to, from, a);
+                               memcpy(to + a, from + a + pop, b);
+                               sg_set_page(sge, page, a + b, 0);
+                               put_page(orig);
+                       }
+                       pop = 0;
+               } else if (pop >= sge->length - a) {
+                       sge->length = a;
+                       pop -= (sge->length - a);
+               }
+       }
+
+       /* From above the current layout _must_ be as follows,
+        *
+        * -| offset
+        * -| start
+        *
+        *  |---- pop ---|---------------- b ------------|
+        *  |____________________________________________| length
+        *
+        * Offset and start of the current msg elem are equal because in the
+        * previous case we handled offset != start and either consumed the
+        * entire element and advanced to the next element OR pop == 0.
+        *
+        * Two cases to handle here are first pop is less than the length
+        * leaving some remainder b above. Simply adjust the element's layout
+        * in this case. Or pop >= length of the element so that b = 0. In this
+        * case advance to next element decrementing pop.
+        */
+       while (pop) {
+               struct scatterlist *sge = sk_msg_elem(msg, i);
+
+               if (pop < sge->length) {
+                       sge->length -= pop;
+                       sge->offset += pop;
+                       pop = 0;
+               } else {
+                       pop -= sge->length;
+                       sk_msg_shift_left(msg, i);
+               }
+               sk_msg_iter_var_next(i);
+       }
+
+       sk_mem_uncharge(msg->sk, len - pop);
+       msg->sg.size -= (len - pop);
+       sk_msg_compute_data_pointers(msg);
+       return 0;
+}
+
+static const struct bpf_func_proto bpf_msg_pop_data_proto = {
+       .func           = bpf_msg_pop_data,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_ANYTHING,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_ANYTHING,
+};
+
 BPF_CALL_1(bpf_get_cgroup_classid, const struct sk_buff *, skb)
 {
        return task_get_classid(skb);
@@ -3908,6 +4073,26 @@ static const struct bpf_func_proto bpf_get_socket_uid_proto = {
        .arg1_type      = ARG_PTR_TO_CTX,
 };
 
+BPF_CALL_5(bpf_sockopt_event_output, struct bpf_sock_ops_kern *, bpf_sock,
+          struct bpf_map *, map, u64, flags, void *, data, u64, size)
+{
+       if (unlikely(flags & ~(BPF_F_INDEX_MASK)))
+               return -EINVAL;
+
+       return bpf_event_output(map, flags, data, size, NULL, 0, NULL);
+}
+
+static const struct bpf_func_proto bpf_sockopt_event_output_proto =  {
+       .func           = bpf_sockopt_event_output,
+       .gpl_only       = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_CONST_MAP_PTR,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_PTR_TO_MEM,
+       .arg5_type      = ARG_CONST_SIZE_OR_ZERO,
+};
+
 BPF_CALL_5(bpf_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
           int, level, int, optname, char *, optval, int, optlen)
 {
@@ -4825,47 +5010,40 @@ static const struct bpf_func_proto bpf_lwt_seg6_adjust_srh_proto = {
 
 #ifdef CONFIG_INET
 static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple,
-                             struct sk_buff *skb, u8 family, u8 proto)
+                             int dif, int sdif, u8 family, u8 proto)
 {
        bool refcounted = false;
        struct sock *sk = NULL;
-       int dif = 0;
-
-       if (skb->dev)
-               dif = skb->dev->ifindex;
 
        if (family == AF_INET) {
                __be32 src4 = tuple->ipv4.saddr;
                __be32 dst4 = tuple->ipv4.daddr;
-               int sdif = inet_sdif(skb);
 
                if (proto == IPPROTO_TCP)
-                       sk = __inet_lookup(net, &tcp_hashinfo, skb, 0,
+                       sk = __inet_lookup(net, &tcp_hashinfo, NULL, 0,
                                           src4, tuple->ipv4.sport,
                                           dst4, tuple->ipv4.dport,
                                           dif, sdif, &refcounted);
                else
                        sk = __udp4_lib_lookup(net, src4, tuple->ipv4.sport,
                                               dst4, tuple->ipv4.dport,
-                                              dif, sdif, &udp_table, skb);
+                                              dif, sdif, &udp_table, NULL);
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
                struct in6_addr *src6 = (struct in6_addr *)&tuple->ipv6.saddr;
                struct in6_addr *dst6 = (struct in6_addr *)&tuple->ipv6.daddr;
-               u16 hnum = ntohs(tuple->ipv6.dport);
-               int sdif = inet6_sdif(skb);
 
                if (proto == IPPROTO_TCP)
-                       sk = __inet6_lookup(net, &tcp_hashinfo, skb, 0,
+                       sk = __inet6_lookup(net, &tcp_hashinfo, NULL, 0,
                                            src6, tuple->ipv6.sport,
-                                           dst6, hnum,
+                                           dst6, ntohs(tuple->ipv6.dport),
                                            dif, sdif, &refcounted);
                else if (likely(ipv6_bpf_stub))
                        sk = ipv6_bpf_stub->udp6_lib_lookup(net,
                                                            src6, tuple->ipv6.sport,
-                                                           dst6, hnum,
+                                                           dst6, tuple->ipv6.dport,
                                                            dif, sdif,
-                                                           &udp_table, skb);
+                                                           &udp_table, NULL);
 #endif
        }
 
@@ -4882,31 +5060,34 @@ static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple,
  * callers to satisfy BPF_CALL declarations.
  */
 static unsigned long
-bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len,
-             u8 proto, u64 netns_id, u64 flags)
+__bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len,
+               struct net *caller_net, u32 ifindex, u8 proto, u64 netns_id,
+               u64 flags)
 {
-       struct net *caller_net;
        struct sock *sk = NULL;
        u8 family = AF_UNSPEC;
        struct net *net;
+       int sdif;
 
        family = len == sizeof(tuple->ipv4) ? AF_INET : AF_INET6;
-       if (unlikely(family == AF_UNSPEC || netns_id > U32_MAX || flags))
+       if (unlikely(family == AF_UNSPEC || flags ||
+                    !((s32)netns_id < 0 || netns_id <= S32_MAX)))
                goto out;
 
-       if (skb->dev)
-               caller_net = dev_net(skb->dev);
+       if (family == AF_INET)
+               sdif = inet_sdif(skb);
        else
-               caller_net = sock_net(skb->sk);
-       if (netns_id) {
+               sdif = inet6_sdif(skb);
+
+       if ((s32)netns_id < 0) {
+               net = caller_net;
+               sk = sk_lookup(net, tuple, ifindex, sdif, family, proto);
+       } else {
                net = get_net_ns_by_id(caller_net, netns_id);
                if (unlikely(!net))
                        goto out;
-               sk = sk_lookup(net, tuple, skb, family, proto);
+               sk = sk_lookup(net, tuple, ifindex, sdif, family, proto);
                put_net(net);
-       } else {
-               net = caller_net;
-               sk = sk_lookup(net, tuple, skb, family, proto);
        }
 
        if (sk)
@@ -4915,6 +5096,25 @@ out:
        return (unsigned long) sk;
 }
 
+static unsigned long
+bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len,
+             u8 proto, u64 netns_id, u64 flags)
+{
+       struct net *caller_net;
+       int ifindex;
+
+       if (skb->dev) {
+               caller_net = dev_net(skb->dev);
+               ifindex = skb->dev->ifindex;
+       } else {
+               caller_net = sock_net(skb->sk);
+               ifindex = 0;
+       }
+
+       return __bpf_sk_lookup(skb, tuple, len, caller_net, ifindex,
+                             proto, netns_id, flags);
+}
+
 BPF_CALL_5(bpf_sk_lookup_tcp, struct sk_buff *, skb,
           struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags)
 {
@@ -4964,6 +5164,87 @@ static const struct bpf_func_proto bpf_sk_release_proto = {
        .ret_type       = RET_INTEGER,
        .arg1_type      = ARG_PTR_TO_SOCKET,
 };
+
+BPF_CALL_5(bpf_xdp_sk_lookup_udp, struct xdp_buff *, ctx,
+          struct bpf_sock_tuple *, tuple, u32, len, u32, netns_id, u64, flags)
+{
+       struct net *caller_net = dev_net(ctx->rxq->dev);
+       int ifindex = ctx->rxq->dev->ifindex;
+
+       return __bpf_sk_lookup(NULL, tuple, len, caller_net, ifindex,
+                             IPPROTO_UDP, netns_id, flags);
+}
+
+static const struct bpf_func_proto bpf_xdp_sk_lookup_udp_proto = {
+       .func           = bpf_xdp_sk_lookup_udp,
+       .gpl_only       = false,
+       .pkt_access     = true,
+       .ret_type       = RET_PTR_TO_SOCKET_OR_NULL,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_ANYTHING,
+       .arg5_type      = ARG_ANYTHING,
+};
+
+BPF_CALL_5(bpf_xdp_sk_lookup_tcp, struct xdp_buff *, ctx,
+          struct bpf_sock_tuple *, tuple, u32, len, u32, netns_id, u64, flags)
+{
+       struct net *caller_net = dev_net(ctx->rxq->dev);
+       int ifindex = ctx->rxq->dev->ifindex;
+
+       return __bpf_sk_lookup(NULL, tuple, len, caller_net, ifindex,
+                             IPPROTO_TCP, netns_id, flags);
+}
+
+static const struct bpf_func_proto bpf_xdp_sk_lookup_tcp_proto = {
+       .func           = bpf_xdp_sk_lookup_tcp,
+       .gpl_only       = false,
+       .pkt_access     = true,
+       .ret_type       = RET_PTR_TO_SOCKET_OR_NULL,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_ANYTHING,
+       .arg5_type      = ARG_ANYTHING,
+};
+
+BPF_CALL_5(bpf_sock_addr_sk_lookup_tcp, struct bpf_sock_addr_kern *, ctx,
+          struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags)
+{
+       return __bpf_sk_lookup(NULL, tuple, len, sock_net(ctx->sk), 0,
+                              IPPROTO_TCP, netns_id, flags);
+}
+
+static const struct bpf_func_proto bpf_sock_addr_sk_lookup_tcp_proto = {
+       .func           = bpf_sock_addr_sk_lookup_tcp,
+       .gpl_only       = false,
+       .ret_type       = RET_PTR_TO_SOCKET_OR_NULL,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_ANYTHING,
+       .arg5_type      = ARG_ANYTHING,
+};
+
+BPF_CALL_5(bpf_sock_addr_sk_lookup_udp, struct bpf_sock_addr_kern *, ctx,
+          struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags)
+{
+       return __bpf_sk_lookup(NULL, tuple, len, sock_net(ctx->sk), 0,
+                              IPPROTO_UDP, netns_id, flags);
+}
+
+static const struct bpf_func_proto bpf_sock_addr_sk_lookup_udp_proto = {
+       .func           = bpf_sock_addr_sk_lookup_udp,
+       .gpl_only       = false,
+       .ret_type       = RET_PTR_TO_SOCKET_OR_NULL,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_ANYTHING,
+       .arg5_type      = ARG_ANYTHING,
+};
+
 #endif /* CONFIG_INET */
 
 bool bpf_helper_changes_pkt_data(void *func)
@@ -4986,6 +5267,7 @@ bool bpf_helper_changes_pkt_data(void *func)
            func == bpf_xdp_adjust_meta ||
            func == bpf_msg_pull_data ||
            func == bpf_msg_push_data ||
+           func == bpf_msg_pop_data ||
            func == bpf_xdp_adjust_tail ||
 #if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
            func == bpf_lwt_seg6_store_bytes ||
@@ -5070,6 +5352,14 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_socket_cookie_sock_addr_proto;
        case BPF_FUNC_get_local_storage:
                return &bpf_get_local_storage_proto;
+#ifdef CONFIG_INET
+       case BPF_FUNC_sk_lookup_tcp:
+               return &bpf_sock_addr_sk_lookup_tcp_proto;
+       case BPF_FUNC_sk_lookup_udp:
+               return &bpf_sock_addr_sk_lookup_udp_proto;
+       case BPF_FUNC_sk_release:
+               return &bpf_sk_release_proto;
+#endif /* CONFIG_INET */
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -5214,6 +5504,14 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_xdp_adjust_tail_proto;
        case BPF_FUNC_fib_lookup:
                return &bpf_xdp_fib_lookup_proto;
+#ifdef CONFIG_INET
+       case BPF_FUNC_sk_lookup_udp:
+               return &bpf_xdp_sk_lookup_udp_proto;
+       case BPF_FUNC_sk_lookup_tcp:
+               return &bpf_xdp_sk_lookup_tcp_proto;
+       case BPF_FUNC_sk_release:
+               return &bpf_sk_release_proto;
+#endif
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -5240,6 +5538,8 @@ sock_ops_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_socket_cookie_sock_ops_proto;
        case BPF_FUNC_get_local_storage:
                return &bpf_get_local_storage_proto;
+       case BPF_FUNC_perf_event_output:
+               return &bpf_sockopt_event_output_proto;
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -5264,6 +5564,8 @@ sk_msg_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_msg_pull_data_proto;
        case BPF_FUNC_msg_push_data:
                return &bpf_msg_push_data_proto;
+       case BPF_FUNC_msg_pop_data:
+               return &bpf_msg_pop_data_proto;
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -5436,8 +5738,12 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type
                if (size != size_default)
                        return false;
                break;
-       case bpf_ctx_range(struct __sk_buff, flow_keys):
-               if (size != sizeof(struct bpf_flow_keys *))
+       case bpf_ctx_range_ptr(struct __sk_buff, flow_keys):
+               if (size != sizeof(__u64))
+                       return false;
+               break;
+       case bpf_ctx_range(struct __sk_buff, tstamp):
+               if (size != sizeof(__u64))
                        return false;
                break;
        default:
@@ -5465,8 +5771,10 @@ static bool sk_filter_is_valid_access(int off, int size,
        case bpf_ctx_range(struct __sk_buff, data):
        case bpf_ctx_range(struct __sk_buff, data_meta):
        case bpf_ctx_range(struct __sk_buff, data_end):
-       case bpf_ctx_range(struct __sk_buff, flow_keys):
+       case bpf_ctx_range_ptr(struct __sk_buff, flow_keys):
        case bpf_ctx_range_till(struct __sk_buff, family, local_port):
+       case bpf_ctx_range(struct __sk_buff, tstamp):
+       case bpf_ctx_range(struct __sk_buff, wire_len):
                return false;
        }
 
@@ -5490,7 +5798,8 @@ static bool cg_skb_is_valid_access(int off, int size,
        switch (off) {
        case bpf_ctx_range(struct __sk_buff, tc_classid):
        case bpf_ctx_range(struct __sk_buff, data_meta):
-       case bpf_ctx_range(struct __sk_buff, flow_keys):
+       case bpf_ctx_range_ptr(struct __sk_buff, flow_keys):
+       case bpf_ctx_range(struct __sk_buff, wire_len):
                return false;
        case bpf_ctx_range(struct __sk_buff, data):
        case bpf_ctx_range(struct __sk_buff, data_end):
@@ -5505,6 +5814,10 @@ static bool cg_skb_is_valid_access(int off, int size,
                case bpf_ctx_range(struct __sk_buff, priority):
                case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]):
                        break;
+               case bpf_ctx_range(struct __sk_buff, tstamp):
+                       if (!capable(CAP_SYS_ADMIN))
+                               return false;
+                       break;
                default:
                        return false;
                }
@@ -5531,7 +5844,9 @@ static bool lwt_is_valid_access(int off, int size,
        case bpf_ctx_range(struct __sk_buff, tc_classid):
        case bpf_ctx_range_till(struct __sk_buff, family, local_port):
        case bpf_ctx_range(struct __sk_buff, data_meta):
-       case bpf_ctx_range(struct __sk_buff, flow_keys):
+       case bpf_ctx_range_ptr(struct __sk_buff, flow_keys):
+       case bpf_ctx_range(struct __sk_buff, tstamp):
+       case bpf_ctx_range(struct __sk_buff, wire_len):
                return false;
        }
 
@@ -5741,6 +6056,7 @@ static bool tc_cls_act_is_valid_access(int off, int size,
                case bpf_ctx_range(struct __sk_buff, priority):
                case bpf_ctx_range(struct __sk_buff, tc_classid):
                case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]):
+               case bpf_ctx_range(struct __sk_buff, tstamp):
                        break;
                default:
                        return false;
@@ -5757,7 +6073,7 @@ static bool tc_cls_act_is_valid_access(int off, int size,
        case bpf_ctx_range(struct __sk_buff, data_end):
                info->reg_type = PTR_TO_PACKET_END;
                break;
-       case bpf_ctx_range(struct __sk_buff, flow_keys):
+       case bpf_ctx_range_ptr(struct __sk_buff, flow_keys):
        case bpf_ctx_range_till(struct __sk_buff, family, local_port):
                return false;
        }
@@ -5959,7 +6275,9 @@ static bool sk_skb_is_valid_access(int off, int size,
        switch (off) {
        case bpf_ctx_range(struct __sk_buff, tc_classid):
        case bpf_ctx_range(struct __sk_buff, data_meta):
-       case bpf_ctx_range(struct __sk_buff, flow_keys):
+       case bpf_ctx_range_ptr(struct __sk_buff, flow_keys):
+       case bpf_ctx_range(struct __sk_buff, tstamp):
+       case bpf_ctx_range(struct __sk_buff, wire_len):
                return false;
        }
 
@@ -5995,6 +6313,9 @@ static bool sk_msg_is_valid_access(int off, int size,
        if (type == BPF_WRITE)
                return false;
 
+       if (off % size != 0)
+               return false;
+
        switch (off) {
        case offsetof(struct sk_msg_md, data):
                info->reg_type = PTR_TO_PACKET;
@@ -6006,16 +6327,20 @@ static bool sk_msg_is_valid_access(int off, int size,
                if (size != sizeof(__u64))
                        return false;
                break;
-       default:
+       case bpf_ctx_range(struct sk_msg_md, family):
+       case bpf_ctx_range(struct sk_msg_md, remote_ip4):
+       case bpf_ctx_range(struct sk_msg_md, local_ip4):
+       case bpf_ctx_range_till(struct sk_msg_md, remote_ip6[0], remote_ip6[3]):
+       case bpf_ctx_range_till(struct sk_msg_md, local_ip6[0], local_ip6[3]):
+       case bpf_ctx_range(struct sk_msg_md, remote_port):
+       case bpf_ctx_range(struct sk_msg_md, local_port):
+       case bpf_ctx_range(struct sk_msg_md, size):
                if (size != sizeof(__u32))
                        return false;
-       }
-
-       if (off < 0 || off >= sizeof(struct sk_msg_md))
-               return false;
-       if (off % size != 0)
+               break;
+       default:
                return false;
-
+       }
        return true;
 }
 
@@ -6040,12 +6365,14 @@ static bool flow_dissector_is_valid_access(int off, int size,
        case bpf_ctx_range(struct __sk_buff, data_end):
                info->reg_type = PTR_TO_PACKET_END;
                break;
-       case bpf_ctx_range(struct __sk_buff, flow_keys):
+       case bpf_ctx_range_ptr(struct __sk_buff, flow_keys):
                info->reg_type = PTR_TO_FLOW_KEYS;
                break;
        case bpf_ctx_range(struct __sk_buff, tc_classid):
        case bpf_ctx_range(struct __sk_buff, data_meta):
        case bpf_ctx_range_till(struct __sk_buff, family, local_port):
+       case bpf_ctx_range(struct __sk_buff, tstamp):
+       case bpf_ctx_range(struct __sk_buff, wire_len):
                return false;
        }
 
@@ -6140,19 +6467,19 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type,
                break;
 
        case offsetof(struct __sk_buff, vlan_present):
-       case offsetof(struct __sk_buff, vlan_tci):
-               BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
+               *target_size = 1;
+               *insn++ = BPF_LDX_MEM(BPF_B, si->dst_reg, si->src_reg,
+                                     PKT_VLAN_PRESENT_OFFSET());
+               if (PKT_VLAN_PRESENT_BIT)
+                       *insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, PKT_VLAN_PRESENT_BIT);
+               if (PKT_VLAN_PRESENT_BIT < 7)
+                       *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, 1);
+               break;
 
+       case offsetof(struct __sk_buff, vlan_tci):
                *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
                                      bpf_target_off(struct sk_buff, vlan_tci, 2,
                                                     target_size));
-               if (si->off == offsetof(struct __sk_buff, vlan_tci)) {
-                       *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg,
-                                               ~VLAN_TAG_PRESENT);
-               } else {
-                       *insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, 12);
-                       *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, 1);
-               }
                break;
 
        case offsetof(struct __sk_buff, cb[0]) ...
@@ -6355,6 +6682,33 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type,
                *insn++ = BPF_LDX_MEM(BPF_SIZEOF(void *), si->dst_reg,
                                      si->src_reg, off);
                break;
+
+       case offsetof(struct __sk_buff, tstamp):
+               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tstamp) != 8);
+
+               if (type == BPF_WRITE)
+                       *insn++ = BPF_STX_MEM(BPF_DW,
+                                             si->dst_reg, si->src_reg,
+                                             bpf_target_off(struct sk_buff,
+                                                            tstamp, 8,
+                                                            target_size));
+               else
+                       *insn++ = BPF_LDX_MEM(BPF_DW,
+                                             si->dst_reg, si->src_reg,
+                                             bpf_target_off(struct sk_buff,
+                                                            tstamp, 8,
+                                                            target_size));
+               break;
+
+       case offsetof(struct __sk_buff, wire_len):
+               BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, pkt_len) != 4);
+
+               off = si->off;
+               off -= offsetof(struct __sk_buff, wire_len);
+               off += offsetof(struct sk_buff, cb);
+               off += offsetof(struct qdisc_skb_cb, pkt_len);
+               *target_size = 4;
+               *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, off);
        }
 
        return insn - insn_buf;
@@ -7071,6 +7425,9 @@ static u32 sk_msg_convert_ctx_access(enum bpf_access_type type,
        int off;
 #endif
 
+       /* convert ctx uses the fact sg element is first in struct */
+       BUILD_BUG_ON(offsetof(struct sk_msg, sg) != 0);
+
        switch (si->off) {
        case offsetof(struct sk_msg_md, data):
                *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_msg, data),
@@ -7183,6 +7540,12 @@ static u32 sk_msg_convert_ctx_access(enum bpf_access_type type,
                *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->dst_reg,
                                      offsetof(struct sock_common, skc_num));
                break;
+
+       case offsetof(struct sk_msg_md, size):
+               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_msg_sg, size),
+                                     si->dst_reg, si->src_reg,
+                                     offsetof(struct sk_msg_sg, size));
+               break;
        }
 
        return insn - insn_buf;
index 2e8d91e..9f28405 100644 (file)
@@ -783,6 +783,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                /* Pass parameters to the BPF program */
                cb->qdisc_cb.flow_keys = &flow_keys;
                flow_keys.nhoff = nhoff;
+               flow_keys.thoff = nhoff;
 
                bpf_compute_data_pointers((struct sk_buff *)skb);
                result = BPF_PROG_RUN(attached, skb);
@@ -790,9 +791,12 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                /* Restore state */
                memcpy(cb, &cb_saved, sizeof(cb_saved));
 
+               flow_keys.nhoff = clamp_t(u16, flow_keys.nhoff, 0, skb->len);
+               flow_keys.thoff = clamp_t(u16, flow_keys.thoff,
+                                         flow_keys.nhoff, skb->len);
+
                __skb_flow_bpf_to_target(&flow_keys, flow_dissector,
                                         target_container);
-               key_control->thoff = min_t(u16, key_control->thoff, skb->len);
                rcu_read_unlock();
                return result == BPF_OK;
        }
index 4b54e5f..acf45dd 100644 (file)
@@ -84,6 +84,7 @@ void gro_cells_destroy(struct gro_cells *gcells)
        for_each_possible_cpu(i) {
                struct gro_cell *cell = per_cpu_ptr(gcells->cells, i);
 
+               napi_disable(&cell->napi);
                netif_napi_del(&cell->napi);
                __skb_queue_purge(&cell->napi_skbs);
        }
index 41954e4..763a7b0 100644 (file)
@@ -118,21 +118,77 @@ unsigned long neigh_rand_reach_time(unsigned long base)
 }
 EXPORT_SYMBOL(neigh_rand_reach_time);
 
+static void neigh_mark_dead(struct neighbour *n)
+{
+       n->dead = 1;
+       if (!list_empty(&n->gc_list)) {
+               list_del_init(&n->gc_list);
+               atomic_dec(&n->tbl->gc_entries);
+       }
+}
+
+static void neigh_update_gc_list(struct neighbour *n)
+{
+       bool on_gc_list, exempt_from_gc;
+
+       write_lock_bh(&n->tbl->lock);
+       write_lock(&n->lock);
+
+       /* remove from the gc list if new state is permanent or if neighbor
+        * is externally learned; otherwise entry should be on the gc list
+        */
+       exempt_from_gc = n->nud_state & NUD_PERMANENT ||
+                        n->flags & NTF_EXT_LEARNED;
+       on_gc_list = !list_empty(&n->gc_list);
+
+       if (exempt_from_gc && on_gc_list) {
+               list_del_init(&n->gc_list);
+               atomic_dec(&n->tbl->gc_entries);
+       } else if (!exempt_from_gc && !on_gc_list) {
+               /* add entries to the tail; cleaning removes from the front */
+               list_add_tail(&n->gc_list, &n->tbl->gc_list);
+               atomic_inc(&n->tbl->gc_entries);
+       }
+
+       write_unlock(&n->lock);
+       write_unlock_bh(&n->tbl->lock);
+}
 
-static bool neigh_del(struct neighbour *n, __u8 state, __u8 flags,
-                     struct neighbour __rcu **np, struct neigh_table *tbl)
+static bool neigh_update_ext_learned(struct neighbour *neigh, u32 flags,
+                                    int *notify)
+{
+       bool rc = false;
+       u8 ndm_flags;
+
+       if (!(flags & NEIGH_UPDATE_F_ADMIN))
+               return rc;
+
+       ndm_flags = (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0;
+       if ((neigh->flags ^ ndm_flags) & NTF_EXT_LEARNED) {
+               if (ndm_flags & NTF_EXT_LEARNED)
+                       neigh->flags |= NTF_EXT_LEARNED;
+               else
+                       neigh->flags &= ~NTF_EXT_LEARNED;
+               rc = true;
+               *notify = 1;
+       }
+
+       return rc;
+}
+
+static bool neigh_del(struct neighbour *n, struct neighbour __rcu **np,
+                     struct neigh_table *tbl)
 {
        bool retval = false;
 
        write_lock(&n->lock);
-       if (refcount_read(&n->refcnt) == 1 && !(n->nud_state & state) &&
-           !(n->flags & flags)) {
+       if (refcount_read(&n->refcnt) == 1) {
                struct neighbour *neigh;
 
                neigh = rcu_dereference_protected(n->next,
                                                  lockdep_is_held(&tbl->lock));
                rcu_assign_pointer(*np, neigh);
-               n->dead = 1;
+               neigh_mark_dead(n);
                retval = true;
        }
        write_unlock(&n->lock);
@@ -158,7 +214,7 @@ bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl)
        while ((n = rcu_dereference_protected(*np,
                                              lockdep_is_held(&tbl->lock)))) {
                if (n == ndel)
-                       return neigh_del(n, 0, 0, np, tbl);
+                       return neigh_del(n, np, tbl);
                np = &n->next;
        }
        return false;
@@ -166,32 +222,29 @@ bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl)
 
 static int neigh_forced_gc(struct neigh_table *tbl)
 {
+       int max_clean = atomic_read(&tbl->gc_entries) - tbl->gc_thresh2;
+       unsigned long tref = jiffies - 5 * HZ;
+       struct neighbour *n, *tmp;
        int shrunk = 0;
-       int i;
-       struct neigh_hash_table *nht;
 
        NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
 
        write_lock_bh(&tbl->lock);
-       nht = rcu_dereference_protected(tbl->nht,
-                                       lockdep_is_held(&tbl->lock));
-       for (i = 0; i < (1 << nht->hash_shift); i++) {
-               struct neighbour *n;
-               struct neighbour __rcu **np;
 
-               np = &nht->hash_buckets[i];
-               while ((n = rcu_dereference_protected(*np,
-                                       lockdep_is_held(&tbl->lock))) != NULL) {
-                       /* Neighbour record may be discarded if:
-                        * - nobody refers to it.
-                        * - it is not permanent
-                        */
-                       if (neigh_del(n, NUD_PERMANENT, NTF_EXT_LEARNED, np,
-                                     tbl)) {
-                               shrunk = 1;
-                               continue;
-                       }
-                       np = &n->next;
+       list_for_each_entry_safe(n, tmp, &tbl->gc_list, gc_list) {
+               if (refcount_read(&n->refcnt) == 1) {
+                       bool remove = false;
+
+                       write_lock(&n->lock);
+                       if ((n->nud_state == NUD_FAILED) ||
+                           time_after(tref, n->updated))
+                               remove = true;
+                       write_unlock(&n->lock);
+
+                       if (remove && neigh_remove_one(n, tbl))
+                               shrunk++;
+                       if (shrunk >= max_clean)
+                               break;
                }
        }
 
@@ -260,8 +313,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,
                                                lockdep_is_held(&tbl->lock)));
                        write_lock(&n->lock);
                        neigh_del_timer(n);
-                       n->dead = 1;
-
+                       neigh_mark_dead(n);
                        if (refcount_read(&n->refcnt) != 1) {
                                /* The most unpleasant situation.
                                   We must destroy neighbour entry,
@@ -321,13 +373,18 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
 }
 EXPORT_SYMBOL(neigh_ifdown);
 
-static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device *dev)
+static struct neighbour *neigh_alloc(struct neigh_table *tbl,
+                                    struct net_device *dev,
+                                    bool exempt_from_gc)
 {
        struct neighbour *n = NULL;
        unsigned long now = jiffies;
        int entries;
 
-       entries = atomic_inc_return(&tbl->entries) - 1;
+       if (exempt_from_gc)
+               goto do_alloc;
+
+       entries = atomic_inc_return(&tbl->gc_entries) - 1;
        if (entries >= tbl->gc_thresh3 ||
            (entries >= tbl->gc_thresh2 &&
             time_after(now, tbl->last_flush + 5 * HZ))) {
@@ -340,6 +397,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device
                }
        }
 
+do_alloc:
        n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC);
        if (!n)
                goto out_entries;
@@ -358,11 +416,15 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device
        n->tbl            = tbl;
        refcount_set(&n->refcnt, 1);
        n->dead           = 1;
+       INIT_LIST_HEAD(&n->gc_list);
+
+       atomic_inc(&tbl->entries);
 out:
        return n;
 
 out_entries:
-       atomic_dec(&tbl->entries);
+       if (!exempt_from_gc)
+               atomic_dec(&tbl->gc_entries);
        goto out;
 }
 
@@ -505,13 +567,15 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
 }
 EXPORT_SYMBOL(neigh_lookup_nodev);
 
-struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
-                                struct net_device *dev, bool want_ref)
+static struct neighbour *___neigh_create(struct neigh_table *tbl,
+                                        const void *pkey,
+                                        struct net_device *dev,
+                                        bool exempt_from_gc, bool want_ref)
 {
+       struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev, exempt_from_gc);
        u32 hash_val;
        unsigned int key_len = tbl->key_len;
        int error;
-       struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev);
        struct neigh_hash_table *nht;
 
        if (!n) {
@@ -574,6 +638,9 @@ struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
        }
 
        n->dead = 0;
+       if (!exempt_from_gc)
+               list_add_tail(&n->gc_list, &n->tbl->gc_list);
+
        if (want_ref)
                neigh_hold(n);
        rcu_assign_pointer(n->next,
@@ -591,6 +658,12 @@ out_neigh_release:
        neigh_release(n);
        goto out;
 }
+
+struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
+                                struct net_device *dev, bool want_ref)
+{
+       return ___neigh_create(tbl, pkey, dev, false, want_ref);
+}
 EXPORT_SYMBOL(__neigh_create);
 
 static u32 pneigh_hash(const void *pkey, unsigned int key_len)
@@ -652,6 +725,7 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl,
        if (!n)
                goto out;
 
+       n->protocol = 0;
        write_pnet(&n->net, net);
        memcpy(n->key, pkey, key_len);
        n->dev = dev;
@@ -854,7 +928,7 @@ static void neigh_periodic_work(struct work_struct *work)
                            (state == NUD_FAILED ||
                             time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) {
                                *np = n->next;
-                               n->dead = 1;
+                               neigh_mark_dead(n);
                                write_unlock(&n->lock);
                                neigh_cleanup_and_release(n);
                                continue;
@@ -1137,9 +1211,11 @@ static void neigh_update_hhs(struct neighbour *neigh)
    Caller MUST hold reference count on the entry.
  */
 
-int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
-                u32 flags, u32 nlmsg_pid)
+static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
+                         u8 new, u32 flags, u32 nlmsg_pid,
+                         struct netlink_ext_ack *extack)
 {
+       bool ext_learn_change = false;
        u8 old;
        int err;
        int notify = 0;
@@ -1155,10 +1231,12 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
        if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
            (old & (NUD_NOARP | NUD_PERMANENT)))
                goto out;
-       if (neigh->dead)
+       if (neigh->dead) {
+               NL_SET_ERR_MSG(extack, "Neighbor entry is now dead");
                goto out;
+       }
 
-       neigh_update_ext_learned(neigh, flags, &notify);
+       ext_learn_change = neigh_update_ext_learned(neigh, flags, &notify);
 
        if (!(new & NUD_VALID)) {
                neigh_del_timer(neigh);
@@ -1193,8 +1271,10 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
                   use it, otherwise discard the request.
                 */
                err = -EINVAL;
-               if (!(old & NUD_VALID))
+               if (!(old & NUD_VALID)) {
+                       NL_SET_ERR_MSG(extack, "No link layer address given");
                        goto out;
+               }
                lladdr = neigh->ha;
        }
 
@@ -1302,11 +1382,20 @@ out:
                neigh_update_is_router(neigh, flags, &notify);
        write_unlock_bh(&neigh->lock);
 
+       if (((new ^ old) & NUD_PERMANENT) || ext_learn_change)
+               neigh_update_gc_list(neigh);
+
        if (notify)
                neigh_update_notify(neigh, nlmsg_pid);
 
        return err;
 }
+
+int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
+                u32 flags, u32 nlmsg_pid)
+{
+       return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL);
+}
 EXPORT_SYMBOL(neigh_update);
 
 /* Update the neigh to listen temporarily for probe responses, even if it is
@@ -1571,6 +1660,7 @@ void neigh_table_init(int index, struct neigh_table *tbl)
        unsigned long phsize;
 
        INIT_LIST_HEAD(&tbl->parms_list);
+       INIT_LIST_HEAD(&tbl->gc_list);
        list_add(&tbl->parms.list, &tbl->parms_list);
        write_pnet(&tbl->parms.net, &init_net);
        refcount_set(&tbl->parms.refcnt, 1);
@@ -1662,6 +1752,19 @@ static struct neigh_table *neigh_find_table(int family)
        return tbl;
 }
 
+const struct nla_policy nda_policy[NDA_MAX+1] = {
+       [NDA_DST]               = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
+       [NDA_LLADDR]            = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
+       [NDA_CACHEINFO]         = { .len = sizeof(struct nda_cacheinfo) },
+       [NDA_PROBES]            = { .type = NLA_U32 },
+       [NDA_VLAN]              = { .type = NLA_U16 },
+       [NDA_PORT]              = { .type = NLA_U16 },
+       [NDA_VNI]               = { .type = NLA_U32 },
+       [NDA_IFINDEX]           = { .type = NLA_U32 },
+       [NDA_MASTER]            = { .type = NLA_U32 },
+       [NDA_PROTOCOL]          = { .type = NLA_U8 },
+};
+
 static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
                        struct netlink_ext_ack *extack)
 {
@@ -1678,8 +1781,10 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
                goto out;
 
        dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST);
-       if (dst_attr == NULL)
+       if (!dst_attr) {
+               NL_SET_ERR_MSG(extack, "Network address not specified");
                goto out;
+       }
 
        ndm = nlmsg_data(nlh);
        if (ndm->ndm_ifindex) {
@@ -1694,8 +1799,10 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (tbl == NULL)
                return -EAFNOSUPPORT;
 
-       if (nla_len(dst_attr) < (int)tbl->key_len)
+       if (nla_len(dst_attr) < (int)tbl->key_len) {
+               NL_SET_ERR_MSG(extack, "Invalid network address");
                goto out;
+       }
 
        if (ndm->ndm_flags & NTF_PROXY) {
                err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
@@ -1711,10 +1818,9 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
                goto out;
        }
 
-       err = neigh_update(neigh, NULL, NUD_FAILED,
-                          NEIGH_UPDATE_F_OVERRIDE |
-                          NEIGH_UPDATE_F_ADMIN,
-                          NETLINK_CB(skb).portid);
+       err = __neigh_update(neigh, NULL, NUD_FAILED,
+                            NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN,
+                            NETLINK_CB(skb).portid, extack);
        write_lock_bh(&tbl->lock);
        neigh_release(neigh);
        neigh_remove_one(neigh, tbl);
@@ -1736,16 +1842,19 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct net_device *dev = NULL;
        struct neighbour *neigh;
        void *dst, *lladdr;
+       u8 protocol = 0;
        int err;
 
        ASSERT_RTNL();
-       err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, extack);
+       err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, nda_policy, extack);
        if (err < 0)
                goto out;
 
        err = -EINVAL;
-       if (tb[NDA_DST] == NULL)
+       if (!tb[NDA_DST]) {
+               NL_SET_ERR_MSG(extack, "Network address not specified");
                goto out;
+       }
 
        ndm = nlmsg_data(nlh);
        if (ndm->ndm_ifindex) {
@@ -1755,19 +1864,27 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                        goto out;
                }
 
-               if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len)
+               if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) {
+                       NL_SET_ERR_MSG(extack, "Invalid link address");
                        goto out;
+               }
        }
 
        tbl = neigh_find_table(ndm->ndm_family);
        if (tbl == NULL)
                return -EAFNOSUPPORT;
 
-       if (nla_len(tb[NDA_DST]) < (int)tbl->key_len)
+       if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) {
+               NL_SET_ERR_MSG(extack, "Invalid network address");
                goto out;
+       }
+
        dst = nla_data(tb[NDA_DST]);
        lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;
 
+       if (tb[NDA_PROTOCOL])
+               protocol = nla_get_u8(tb[NDA_PROTOCOL]);
+
        if (ndm->ndm_flags & NTF_PROXY) {
                struct pneigh_entry *pn;
 
@@ -1775,22 +1892,30 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                pn = pneigh_lookup(tbl, net, dst, dev, 1);
                if (pn) {
                        pn->flags = ndm->ndm_flags;
+                       if (protocol)
+                               pn->protocol = protocol;
                        err = 0;
                }
                goto out;
        }
 
-       if (dev == NULL)
+       if (!dev) {
+               NL_SET_ERR_MSG(extack, "Device not specified");
                goto out;
+       }
 
        neigh = neigh_lookup(tbl, dst, dev);
        if (neigh == NULL) {
+               bool exempt_from_gc;
+
                if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
                        err = -ENOENT;
                        goto out;
                }
 
-               neigh = __neigh_lookup_errno(tbl, dst, dev);
+               exempt_from_gc = ndm->ndm_state & NUD_PERMANENT ||
+                                ndm->ndm_flags & NTF_EXT_LEARNED;
+               neigh = ___neigh_create(tbl, dst, dev, exempt_from_gc, true);
                if (IS_ERR(neigh)) {
                        err = PTR_ERR(neigh);
                        goto out;
@@ -1817,8 +1942,12 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                neigh_event_send(neigh, NULL);
                err = 0;
        } else
-               err = neigh_update(neigh, lladdr, ndm->ndm_state, flags,
-                                  NETLINK_CB(skb).portid);
+               err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags,
+                                    NETLINK_CB(skb).portid, extack);
+
+       if (protocol)
+               neigh->protocol = protocol;
+
        neigh_release(neigh);
 
 out:
@@ -2312,6 +2441,9 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,
            nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci))
                goto nla_put_failure;
 
+       if (neigh->protocol && nla_put_u8(skb, NDA_PROTOCOL, neigh->protocol))
+               goto nla_put_failure;
+
        nlmsg_end(skb, nlh);
        return 0;
 
@@ -2343,6 +2475,9 @@ static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
        if (nla_put(skb, NDA_DST, tbl->key_len, pn->key))
                goto nla_put_failure;
 
+       if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol))
+               goto nla_put_failure;
+
        nlmsg_end(skb, nlh);
        return 0;
 
@@ -2494,16 +2629,21 @@ static int neigh_valid_dump_req(const struct nlmsghdr *nlh,
 
                ndm = nlmsg_data(nlh);
                if (ndm->ndm_pad1  || ndm->ndm_pad2  || ndm->ndm_ifindex ||
-                   ndm->ndm_state || ndm->ndm_flags || ndm->ndm_type) {
+                   ndm->ndm_state || ndm->ndm_type) {
                        NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor dump request");
                        return -EINVAL;
                }
 
+               if (ndm->ndm_flags & ~NTF_PROXY) {
+                       NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor dump request");
+                       return -EINVAL;
+               }
+
                err = nlmsg_parse_strict(nlh, sizeof(struct ndmsg), tb, NDA_MAX,
-                                        NULL, extack);
+                                        nda_policy, extack);
        } else {
                err = nlmsg_parse(nlh, sizeof(struct ndmsg), tb, NDA_MAX,
-                                 NULL, extack);
+                                 nda_policy, extack);
        }
        if (err < 0)
                return err;
@@ -2515,17 +2655,9 @@ static int neigh_valid_dump_req(const struct nlmsghdr *nlh,
                /* all new attributes should require strict_check */
                switch (i) {
                case NDA_IFINDEX:
-                       if (nla_len(tb[i]) != sizeof(u32)) {
-                               NL_SET_ERR_MSG(extack, "Invalid IFINDEX attribute in neighbor dump request");
-                               return -EINVAL;
-                       }
                        filter->dev_idx = nla_get_u32(tb[i]);
                        break;
                case NDA_MASTER:
-                       if (nla_len(tb[i]) != sizeof(u32)) {
-                               NL_SET_ERR_MSG(extack, "Invalid MASTER attribute in neighbor dump request");
-                               return -EINVAL;
-                       }
                        filter->master_idx = nla_get_u32(tb[i]);
                        break;
                default:
@@ -2585,6 +2717,186 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
+static int neigh_valid_get_req(const struct nlmsghdr *nlh,
+                              struct neigh_table **tbl,
+                              void **dst, int *dev_idx, u8 *ndm_flags,
+                              struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb[NDA_MAX + 1];
+       struct ndmsg *ndm;
+       int err, i;
+
+       if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) {
+               NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request");
+               return -EINVAL;
+       }
+
+       ndm = nlmsg_data(nlh);
+       if (ndm->ndm_pad1  || ndm->ndm_pad2  || ndm->ndm_state ||
+           ndm->ndm_type) {
+               NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request");
+               return -EINVAL;
+       }
+
+       if (ndm->ndm_flags & ~NTF_PROXY) {
+               NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request");
+               return -EINVAL;
+       }
+
+       err = nlmsg_parse_strict(nlh, sizeof(struct ndmsg), tb, NDA_MAX,
+                                nda_policy, extack);
+       if (err < 0)
+               return err;
+
+       *ndm_flags = ndm->ndm_flags;
+       *dev_idx = ndm->ndm_ifindex;
+       *tbl = neigh_find_table(ndm->ndm_family);
+       if (*tbl == NULL) {
+               NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request");
+               return -EAFNOSUPPORT;
+       }
+
+       for (i = 0; i <= NDA_MAX; ++i) {
+               if (!tb[i])
+                       continue;
+
+               switch (i) {
+               case NDA_DST:
+                       if (nla_len(tb[i]) != (int)(*tbl)->key_len) {
+                               NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request");
+                               return -EINVAL;
+                       }
+                       *dst = nla_data(tb[i]);
+                       break;
+               default:
+                       NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static inline size_t neigh_nlmsg_size(void)
+{
+       return NLMSG_ALIGN(sizeof(struct ndmsg))
+              + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */
+              + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */
+              + nla_total_size(sizeof(struct nda_cacheinfo))
+              + nla_total_size(4)  /* NDA_PROBES */
+              + nla_total_size(1); /* NDA_PROTOCOL */
+}
+
+static int neigh_get_reply(struct net *net, struct neighbour *neigh,
+                          u32 pid, u32 seq)
+{
+       struct sk_buff *skb;
+       int err = 0;
+
+       skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL);
+       if (!skb)
+               return -ENOBUFS;
+
+       err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0);
+       if (err) {
+               kfree_skb(skb);
+               goto errout;
+       }
+
+       err = rtnl_unicast(skb, net, pid);
+errout:
+       return err;
+}
+
+static inline size_t pneigh_nlmsg_size(void)
+{
+       return NLMSG_ALIGN(sizeof(struct ndmsg))
+              + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */
+              + nla_total_size(1); /* NDA_PROTOCOL */
+}
+
+static int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh,
+                           u32 pid, u32 seq, struct neigh_table *tbl)
+{
+       struct sk_buff *skb;
+       int err = 0;
+
+       skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL);
+       if (!skb)
+               return -ENOBUFS;
+
+       err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl);
+       if (err) {
+               kfree_skb(skb);
+               goto errout;
+       }
+
+       err = rtnl_unicast(skb, net, pid);
+errout:
+       return err;
+}
+
+static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
+                    struct netlink_ext_ack *extack)
+{
+       struct net *net = sock_net(in_skb->sk);
+       struct net_device *dev = NULL;
+       struct neigh_table *tbl = NULL;
+       struct neighbour *neigh;
+       void *dst = NULL;
+       u8 ndm_flags = 0;
+       int dev_idx = 0;
+       int err;
+
+       err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags,
+                                 extack);
+       if (err < 0)
+               return err;
+
+       if (dev_idx) {
+               dev = __dev_get_by_index(net, dev_idx);
+               if (!dev) {
+                       NL_SET_ERR_MSG(extack, "Unknown device ifindex");
+                       return -ENODEV;
+               }
+       }
+
+       if (!dst) {
+               NL_SET_ERR_MSG(extack, "Network address not specified");
+               return -EINVAL;
+       }
+
+       if (ndm_flags & NTF_PROXY) {
+               struct pneigh_entry *pn;
+
+               pn = pneigh_lookup(tbl, net, dst, dev, 0);
+               if (!pn) {
+                       NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found");
+                       return -ENOENT;
+               }
+               return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid,
+                                       nlh->nlmsg_seq, tbl);
+       }
+
+       if (!dev) {
+               NL_SET_ERR_MSG(extack, "No device specified");
+               return -EINVAL;
+       }
+
+       neigh = neigh_lookup(tbl, dst, dev);
+       if (!neigh) {
+               NL_SET_ERR_MSG(extack, "Neighbour entry not found");
+               return -ENOENT;
+       }
+
+       err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid,
+                             nlh->nlmsg_seq);
+
+       neigh_release(neigh);
+
+       return err;
+}
+
 void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie)
 {
        int chain;
@@ -2631,7 +2943,7 @@ void __neigh_for_each_release(struct neigh_table *tbl,
                                rcu_assign_pointer(*np,
                                        rcu_dereference_protected(n->next,
                                                lockdep_is_held(&tbl->lock)));
-                               n->dead = 1;
+                               neigh_mark_dead(n);
                        } else
                                np = &n->next;
                        write_unlock(&n->lock);
@@ -2992,15 +3304,6 @@ static const struct seq_operations neigh_stat_seq_ops = {
 };
 #endif /* CONFIG_PROC_FS */
 
-static inline size_t neigh_nlmsg_size(void)
-{
-       return NLMSG_ALIGN(sizeof(struct ndmsg))
-              + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */
-              + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */
-              + nla_total_size(sizeof(struct nda_cacheinfo))
-              + nla_total_size(4); /* NDA_PROBES */
-}
-
 static void __neigh_notify(struct neighbour *n, int type, int flags,
                           u32 pid)
 {
@@ -3384,7 +3687,7 @@ static int __init neigh_init(void)
 {
        rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0);
        rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0);
-       rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, 0);
+       rtnl_register(PF_UNSPEC, RTM_GETNEIGH, neigh_get, neigh_dump_info, 0);
 
        rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info,
                      0);
index bd67c4d..ff9fd2b 100644 (file)
@@ -337,7 +337,7 @@ NETDEVICE_SHOW_RW(mtu, fmt_dec);
 
 static int change_flags(struct net_device *dev, unsigned long new_flags)
 {
-       return dev_change_flags(dev, (unsigned int)new_flags);
+       return dev_change_flags(dev, (unsigned int)new_flags, NULL);
 }
 
 static ssize_t flags_store(struct device *dev, struct device_attribute *attr,
index fefe727..05b23b2 100644 (file)
@@ -669,6 +669,7 @@ static const struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = {
        [NETNSA_NSID]           = { .type = NLA_S32 },
        [NETNSA_PID]            = { .type = NLA_U32 },
        [NETNSA_FD]             = { .type = NLA_U32 },
+       [NETNSA_TARGET_NSID]    = { .type = NLA_S32 },
 };
 
 static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -735,23 +736,38 @@ static int rtnl_net_get_size(void)
 {
        return NLMSG_ALIGN(sizeof(struct rtgenmsg))
               + nla_total_size(sizeof(s32)) /* NETNSA_NSID */
+              + nla_total_size(sizeof(s32)) /* NETNSA_CURRENT_NSID */
               ;
 }
 
-static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags,
-                        int cmd, struct net *net, int nsid)
+struct net_fill_args {
+       u32 portid;
+       u32 seq;
+       int flags;
+       int cmd;
+       int nsid;
+       bool add_ref;
+       int ref_nsid;
+};
+
+static int rtnl_net_fill(struct sk_buff *skb, struct net_fill_args *args)
 {
        struct nlmsghdr *nlh;
        struct rtgenmsg *rth;
 
-       nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rth), flags);
+       nlh = nlmsg_put(skb, args->portid, args->seq, args->cmd, sizeof(*rth),
+                       args->flags);
        if (!nlh)
                return -EMSGSIZE;
 
        rth = nlmsg_data(nlh);
        rth->rtgen_family = AF_UNSPEC;
 
-       if (nla_put_s32(skb, NETNSA_NSID, nsid))
+       if (nla_put_s32(skb, NETNSA_NSID, args->nsid))
+               goto nla_put_failure;
+
+       if (args->add_ref &&
+           nla_put_s32(skb, NETNSA_CURRENT_NSID, args->ref_nsid))
                goto nla_put_failure;
 
        nlmsg_end(skb, nlh);
@@ -767,10 +783,15 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[NETNSA_MAX + 1];
+       struct net_fill_args fillargs = {
+               .portid = NETLINK_CB(skb).portid,
+               .seq = nlh->nlmsg_seq,
+               .cmd = RTM_NEWNSID,
+       };
+       struct net *peer, *target = net;
        struct nlattr *nla;
        struct sk_buff *msg;
-       struct net *peer;
-       int err, id;
+       int err;
 
        err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX,
                          rtnl_net_policy, extack);
@@ -782,6 +803,11 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
        } else if (tb[NETNSA_FD]) {
                peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD]));
                nla = tb[NETNSA_FD];
+       } else if (tb[NETNSA_NSID]) {
+               peer = get_net_ns_by_id(net, nla_get_u32(tb[NETNSA_NSID]));
+               if (!peer)
+                       peer = ERR_PTR(-ENOENT);
+               nla = tb[NETNSA_NSID];
        } else {
                NL_SET_ERR_MSG(extack, "Peer netns reference is missing");
                return -EINVAL;
@@ -793,15 +819,29 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
                return PTR_ERR(peer);
        }
 
+       if (tb[NETNSA_TARGET_NSID]) {
+               int id = nla_get_s32(tb[NETNSA_TARGET_NSID]);
+
+               target = rtnl_get_net_ns_capable(NETLINK_CB(skb).sk, id);
+               if (IS_ERR(target)) {
+                       NL_SET_BAD_ATTR(extack, tb[NETNSA_TARGET_NSID]);
+                       NL_SET_ERR_MSG(extack,
+                                      "Target netns reference is invalid");
+                       err = PTR_ERR(target);
+                       goto out;
+               }
+               fillargs.add_ref = true;
+               fillargs.ref_nsid = peernet2id(net, peer);
+       }
+
        msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL);
        if (!msg) {
                err = -ENOMEM;
                goto out;
        }
 
-       id = peernet2id(net, peer);
-       err = rtnl_net_fill(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
-                           RTM_NEWNSID, net, id);
+       fillargs.nsid = peernet2id(target, peer);
+       err = rtnl_net_fill(msg, &fillargs);
        if (err < 0)
                goto err_out;
 
@@ -811,14 +851,17 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
 err_out:
        nlmsg_free(msg);
 out:
+       if (fillargs.add_ref)
+               put_net(target);
        put_net(peer);
        return err;
 }
 
 struct rtnl_net_dump_cb {
-       struct net *net;
+       struct net *tgt_net;
+       struct net *ref_net;
        struct sk_buff *skb;
-       struct netlink_callback *cb;
+       struct net_fill_args fillargs;
        int idx;
        int s_idx;
 };
@@ -831,9 +874,10 @@ static int rtnl_net_dumpid_one(int id, void *peer, void *data)
        if (net_cb->idx < net_cb->s_idx)
                goto cont;
 
-       ret = rtnl_net_fill(net_cb->skb, NETLINK_CB(net_cb->cb->skb).portid,
-                           net_cb->cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                           RTM_NEWNSID, net_cb->net, id);
+       net_cb->fillargs.nsid = id;
+       if (net_cb->fillargs.add_ref)
+               net_cb->fillargs.ref_nsid = __peernet2id(net_cb->ref_net, peer);
+       ret = rtnl_net_fill(net_cb->skb, &net_cb->fillargs);
        if (ret < 0)
                return ret;
 
@@ -842,33 +886,96 @@ cont:
        return 0;
 }
 
+static int rtnl_valid_dump_net_req(const struct nlmsghdr *nlh, struct sock *sk,
+                                  struct rtnl_net_dump_cb *net_cb,
+                                  struct netlink_callback *cb)
+{
+       struct netlink_ext_ack *extack = cb->extack;
+       struct nlattr *tb[NETNSA_MAX + 1];
+       int err, i;
+
+       err = nlmsg_parse_strict(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX,
+                                rtnl_net_policy, extack);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i <= NETNSA_MAX; i++) {
+               if (!tb[i])
+                       continue;
+
+               if (i == NETNSA_TARGET_NSID) {
+                       struct net *net;
+
+                       net = rtnl_get_net_ns_capable(sk, nla_get_s32(tb[i]));
+                       if (IS_ERR(net)) {
+                               NL_SET_BAD_ATTR(extack, tb[i]);
+                               NL_SET_ERR_MSG(extack,
+                                              "Invalid target network namespace id");
+                               return PTR_ERR(net);
+                       }
+                       net_cb->fillargs.add_ref = true;
+                       net_cb->ref_net = net_cb->tgt_net;
+                       net_cb->tgt_net = net;
+               } else {
+                       NL_SET_BAD_ATTR(extack, tb[i]);
+                       NL_SET_ERR_MSG(extack,
+                                      "Unsupported attribute in dump request");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       struct net *net = sock_net(skb->sk);
        struct rtnl_net_dump_cb net_cb = {
-               .net = net,
+               .tgt_net = sock_net(skb->sk),
                .skb = skb,
-               .cb = cb,
+               .fillargs = {
+                       .portid = NETLINK_CB(cb->skb).portid,
+                       .seq = cb->nlh->nlmsg_seq,
+                       .flags = NLM_F_MULTI,
+                       .cmd = RTM_NEWNSID,
+               },
                .idx = 0,
                .s_idx = cb->args[0],
        };
+       int err = 0;
 
-       if (cb->strict_check &&
-           nlmsg_attrlen(cb->nlh, sizeof(struct rtgenmsg))) {
-                       NL_SET_ERR_MSG(cb->extack, "Unknown data in network namespace id dump request");
-                       return -EINVAL;
+       if (cb->strict_check) {
+               err = rtnl_valid_dump_net_req(cb->nlh, skb->sk, &net_cb, cb);
+               if (err < 0)
+                       goto end;
        }
 
-       spin_lock_bh(&net->nsid_lock);
-       idr_for_each(&net->netns_ids, rtnl_net_dumpid_one, &net_cb);
-       spin_unlock_bh(&net->nsid_lock);
+       spin_lock_bh(&net_cb.tgt_net->nsid_lock);
+       if (net_cb.fillargs.add_ref &&
+           !net_eq(net_cb.ref_net, net_cb.tgt_net) &&
+           !spin_trylock_bh(&net_cb.ref_net->nsid_lock)) {
+               spin_unlock_bh(&net_cb.tgt_net->nsid_lock);
+               err = -EAGAIN;
+               goto end;
+       }
+       idr_for_each(&net_cb.tgt_net->netns_ids, rtnl_net_dumpid_one, &net_cb);
+       if (net_cb.fillargs.add_ref &&
+           !net_eq(net_cb.ref_net, net_cb.tgt_net))
+               spin_unlock_bh(&net_cb.ref_net->nsid_lock);
+       spin_unlock_bh(&net_cb.tgt_net->nsid_lock);
 
        cb->args[0] = net_cb.idx;
-       return skb->len;
+end:
+       if (net_cb.fillargs.add_ref)
+               put_net(net_cb.tgt_net);
+       return err < 0 ? err : skb->len;
 }
 
 static void rtnl_net_notifyid(struct net *net, int cmd, int id)
 {
+       struct net_fill_args fillargs = {
+               .cmd = cmd,
+               .nsid = id,
+       };
        struct sk_buff *msg;
        int err = -ENOMEM;
 
@@ -876,7 +983,7 @@ static void rtnl_net_notifyid(struct net *net, int cmd, int id)
        if (!msg)
                goto out;
 
-       err = rtnl_net_fill(msg, 0, 0, 0, cmd, net, id);
+       err = rtnl_net_fill(msg, &fillargs);
        if (err < 0)
                goto err_out;
 
index 2b9fdbc..36a2b63 100644 (file)
@@ -663,7 +663,7 @@ int netpoll_setup(struct netpoll *np)
 
                np_info(np, "device %s not up yet, forcing it\n", np->dev_name);
 
-               err = dev_open(ndev);
+               err = dev_open(ndev, NULL);
 
                if (err) {
                        np_err(np, "failed to open %s\n", ndev->name);
index 86f2d9c..48f6188 100644 (file)
@@ -59,7 +59,7 @@
 #include <net/rtnetlink.h>
 #include <net/net_namespace.h>
 
-#define RTNL_MAX_TYPE          49
+#define RTNL_MAX_TYPE          50
 #define RTNL_SLAVE_MAX_TYPE    36
 
 struct rtnl_link {
@@ -2444,7 +2444,7 @@ static int do_setlink(const struct sk_buff *skb,
                sa->sa_family = dev->type;
                memcpy(sa->sa_data, nla_data(tb[IFLA_ADDRESS]),
                       dev->addr_len);
-               err = dev_set_mac_address(dev, sa);
+               err = dev_set_mac_address(dev, sa, extack);
                kfree(sa);
                if (err)
                        goto errout;
@@ -2489,7 +2489,8 @@ static int do_setlink(const struct sk_buff *skb,
        }
 
        if (ifm->ifi_flags || ifm->ifi_change) {
-               err = dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm));
+               err = dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm),
+                                      extack);
                if (err < 0)
                        goto errout;
        }
@@ -2870,7 +2871,8 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)
 
        old_flags = dev->flags;
        if (ifm && (ifm->ifi_flags || ifm->ifi_change)) {
-               err = __dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm));
+               err = __dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm),
+                                        NULL);
                if (err < 0)
                        return err;
        }
@@ -2971,20 +2973,24 @@ static int rtnl_group_changelink(const struct sk_buff *skb,
        return 0;
 }
 
-static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
-                       struct netlink_ext_ack *extack)
+static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
+                         struct nlattr **attr, struct netlink_ext_ack *extack)
 {
+       struct nlattr *slave_attr[RTNL_SLAVE_MAX_TYPE + 1];
+       unsigned char name_assign_type = NET_NAME_USER;
+       struct nlattr *linkinfo[IFLA_INFO_MAX + 1];
+       const struct rtnl_link_ops *m_ops = NULL;
+       struct net_device *master_dev = NULL;
        struct net *net = sock_net(skb->sk);
        const struct rtnl_link_ops *ops;
-       const struct rtnl_link_ops *m_ops = NULL;
+       struct nlattr *tb[IFLA_MAX + 1];
+       struct net *dest_net, *link_net;
+       struct nlattr **slave_data;
+       char kind[MODULE_NAME_LEN];
        struct net_device *dev;
-       struct net_device *master_dev = NULL;
        struct ifinfomsg *ifm;
-       char kind[MODULE_NAME_LEN];
        char ifname[IFNAMSIZ];
-       struct nlattr *tb[IFLA_MAX+1];
-       struct nlattr *linkinfo[IFLA_INFO_MAX+1];
-       unsigned char name_assign_type = NET_NAME_USER;
+       struct nlattr **data;
        int err;
 
 #ifdef CONFIG_MODULES
@@ -3040,195 +3046,200 @@ replay:
                ops = NULL;
        }
 
-       if (1) {
-               struct nlattr *attr[RTNL_MAX_TYPE + 1];
-               struct nlattr *slave_attr[RTNL_SLAVE_MAX_TYPE + 1];
-               struct nlattr **data = NULL;
-               struct nlattr **slave_data = NULL;
-               struct net *dest_net, *link_net = NULL;
-
-               if (ops) {
-                       if (ops->maxtype > RTNL_MAX_TYPE)
-                               return -EINVAL;
+       data = NULL;
+       if (ops) {
+               if (ops->maxtype > RTNL_MAX_TYPE)
+                       return -EINVAL;
 
-                       if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
-                               err = nla_parse_nested(attr, ops->maxtype,
-                                                      linkinfo[IFLA_INFO_DATA],
-                                                      ops->policy, extack);
-                               if (err < 0)
-                                       return err;
-                               data = attr;
-                       }
-                       if (ops->validate) {
-                               err = ops->validate(tb, data, extack);
-                               if (err < 0)
-                                       return err;
-                       }
+               if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
+                       err = nla_parse_nested(attr, ops->maxtype,
+                                              linkinfo[IFLA_INFO_DATA],
+                                              ops->policy, extack);
+                       if (err < 0)
+                               return err;
+                       data = attr;
+               }
+               if (ops->validate) {
+                       err = ops->validate(tb, data, extack);
+                       if (err < 0)
+                               return err;
                }
+       }
 
-               if (m_ops) {
-                       if (m_ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
-                               return -EINVAL;
+       slave_data = NULL;
+       if (m_ops) {
+               if (m_ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
+                       return -EINVAL;
 
-                       if (m_ops->slave_maxtype &&
-                           linkinfo[IFLA_INFO_SLAVE_DATA]) {
-                               err = nla_parse_nested(slave_attr,
-                                                      m_ops->slave_maxtype,
-                                                      linkinfo[IFLA_INFO_SLAVE_DATA],
-                                                      m_ops->slave_policy,
-                                                      extack);
-                               if (err < 0)
-                                       return err;
-                               slave_data = slave_attr;
-                       }
+               if (m_ops->slave_maxtype &&
+                   linkinfo[IFLA_INFO_SLAVE_DATA]) {
+                       err = nla_parse_nested(slave_attr, m_ops->slave_maxtype,
+                                              linkinfo[IFLA_INFO_SLAVE_DATA],
+                                              m_ops->slave_policy, extack);
+                       if (err < 0)
+                               return err;
+                       slave_data = slave_attr;
                }
+       }
 
-               if (dev) {
-                       int status = 0;
-
-                       if (nlh->nlmsg_flags & NLM_F_EXCL)
-                               return -EEXIST;
-                       if (nlh->nlmsg_flags & NLM_F_REPLACE)
-                               return -EOPNOTSUPP;
+       if (dev) {
+               int status = 0;
 
-                       if (linkinfo[IFLA_INFO_DATA]) {
-                               if (!ops || ops != dev->rtnl_link_ops ||
-                                   !ops->changelink)
-                                       return -EOPNOTSUPP;
+               if (nlh->nlmsg_flags & NLM_F_EXCL)
+                       return -EEXIST;
+               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+                       return -EOPNOTSUPP;
 
-                               err = ops->changelink(dev, tb, data, extack);
-                               if (err < 0)
-                                       return err;
-                               status |= DO_SETLINK_NOTIFY;
-                       }
+               if (linkinfo[IFLA_INFO_DATA]) {
+                       if (!ops || ops != dev->rtnl_link_ops ||
+                           !ops->changelink)
+                               return -EOPNOTSUPP;
 
-                       if (linkinfo[IFLA_INFO_SLAVE_DATA]) {
-                               if (!m_ops || !m_ops->slave_changelink)
-                                       return -EOPNOTSUPP;
+                       err = ops->changelink(dev, tb, data, extack);
+                       if (err < 0)
+                               return err;
+                       status |= DO_SETLINK_NOTIFY;
+               }
 
-                               err = m_ops->slave_changelink(master_dev, dev,
-                                                             tb, slave_data,
-                                                             extack);
-                               if (err < 0)
-                                       return err;
-                               status |= DO_SETLINK_NOTIFY;
-                       }
+               if (linkinfo[IFLA_INFO_SLAVE_DATA]) {
+                       if (!m_ops || !m_ops->slave_changelink)
+                               return -EOPNOTSUPP;
 
-                       return do_setlink(skb, dev, ifm, extack, tb, ifname,
-                                         status);
+                       err = m_ops->slave_changelink(master_dev, dev, tb,
+                                                     slave_data, extack);
+                       if (err < 0)
+                               return err;
+                       status |= DO_SETLINK_NOTIFY;
                }
 
-               if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
-                       if (ifm->ifi_index == 0 && tb[IFLA_GROUP])
-                               return rtnl_group_changelink(skb, net,
+               return do_setlink(skb, dev, ifm, extack, tb, ifname, status);
+       }
+
+       if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
+               if (ifm->ifi_index == 0 && tb[IFLA_GROUP])
+                       return rtnl_group_changelink(skb, net,
                                                nla_get_u32(tb[IFLA_GROUP]),
                                                ifm, extack, tb);
-                       return -ENODEV;
-               }
+               return -ENODEV;
+       }
 
-               if (tb[IFLA_MAP] || tb[IFLA_PROTINFO])
-                       return -EOPNOTSUPP;
+       if (tb[IFLA_MAP] || tb[IFLA_PROTINFO])
+               return -EOPNOTSUPP;
 
-               if (!ops) {
+       if (!ops) {
 #ifdef CONFIG_MODULES
-                       if (kind[0]) {
-                               __rtnl_unlock();
-                               request_module("rtnl-link-%s", kind);
-                               rtnl_lock();
-                               ops = rtnl_link_ops_get(kind);
-                               if (ops)
-                                       goto replay;
-                       }
-#endif
-                       NL_SET_ERR_MSG(extack, "Unknown device type");
-                       return -EOPNOTSUPP;
+               if (kind[0]) {
+                       __rtnl_unlock();
+                       request_module("rtnl-link-%s", kind);
+                       rtnl_lock();
+                       ops = rtnl_link_ops_get(kind);
+                       if (ops)
+                               goto replay;
                }
+#endif
+               NL_SET_ERR_MSG(extack, "Unknown device type");
+               return -EOPNOTSUPP;
+       }
 
-               if (!ops->setup)
-                       return -EOPNOTSUPP;
-
-               if (!ifname[0]) {
-                       snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
-                       name_assign_type = NET_NAME_ENUM;
-               }
+       if (!ops->setup)
+               return -EOPNOTSUPP;
 
-               dest_net = rtnl_link_get_net_capable(skb, net, tb, CAP_NET_ADMIN);
-               if (IS_ERR(dest_net))
-                       return PTR_ERR(dest_net);
+       if (!ifname[0]) {
+               snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
+               name_assign_type = NET_NAME_ENUM;
+       }
 
-               if (tb[IFLA_LINK_NETNSID]) {
-                       int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
+       dest_net = rtnl_link_get_net_capable(skb, net, tb, CAP_NET_ADMIN);
+       if (IS_ERR(dest_net))
+               return PTR_ERR(dest_net);
 
-                       link_net = get_net_ns_by_id(dest_net, id);
-                       if (!link_net) {
-                               NL_SET_ERR_MSG(extack, "Unknown network namespace id");
-                               err =  -EINVAL;
-                               goto out;
-                       }
-                       err = -EPERM;
-                       if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN))
-                               goto out;
-               }
+       if (tb[IFLA_LINK_NETNSID]) {
+               int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
 
-               dev = rtnl_create_link(link_net ? : dest_net, ifname,
-                                      name_assign_type, ops, tb, extack);
-               if (IS_ERR(dev)) {
-                       err = PTR_ERR(dev);
+               link_net = get_net_ns_by_id(dest_net, id);
+               if (!link_net) {
+                       NL_SET_ERR_MSG(extack, "Unknown network namespace id");
+                       err =  -EINVAL;
                        goto out;
                }
+               err = -EPERM;
+               if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN))
+                       goto out;
+       } else {
+               link_net = NULL;
+       }
 
-               dev->ifindex = ifm->ifi_index;
+       dev = rtnl_create_link(link_net ? : dest_net, ifname,
+                              name_assign_type, ops, tb, extack);
+       if (IS_ERR(dev)) {
+               err = PTR_ERR(dev);
+               goto out;
+       }
 
-               if (ops->newlink) {
-                       err = ops->newlink(link_net ? : net, dev, tb, data,
-                                          extack);
-                       /* Drivers should call free_netdev() in ->destructor
-                        * and unregister it on failure after registration
-                        * so that device could be finally freed in rtnl_unlock.
-                        */
-                       if (err < 0) {
-                               /* If device is not registered at all, free it now */
-                               if (dev->reg_state == NETREG_UNINITIALIZED)
-                                       free_netdev(dev);
-                               goto out;
-                       }
-               } else {
-                       err = register_netdevice(dev);
-                       if (err < 0) {
+       dev->ifindex = ifm->ifi_index;
+
+       if (ops->newlink) {
+               err = ops->newlink(link_net ? : net, dev, tb, data, extack);
+               /* Drivers should call free_netdev() in ->destructor
+                * and unregister it on failure after registration
+                * so that device could be finally freed in rtnl_unlock.
+                */
+               if (err < 0) {
+                       /* If device is not registered at all, free it now */
+                       if (dev->reg_state == NETREG_UNINITIALIZED)
                                free_netdev(dev);
-                               goto out;
-                       }
+                       goto out;
+               }
+       } else {
+               err = register_netdevice(dev);
+               if (err < 0) {
+                       free_netdev(dev);
+                       goto out;
                }
-               err = rtnl_configure_link(dev, ifm);
+       }
+       err = rtnl_configure_link(dev, ifm);
+       if (err < 0)
+               goto out_unregister;
+       if (link_net) {
+               err = dev_change_net_namespace(dev, dest_net, ifname);
                if (err < 0)
                        goto out_unregister;
-               if (link_net) {
-                       err = dev_change_net_namespace(dev, dest_net, ifname);
-                       if (err < 0)
-                               goto out_unregister;
-               }
-               if (tb[IFLA_MASTER]) {
-                       err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]),
-                                           extack);
-                       if (err)
-                               goto out_unregister;
-               }
+       }
+       if (tb[IFLA_MASTER]) {
+               err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack);
+               if (err)
+                       goto out_unregister;
+       }
 out:
-               if (link_net)
-                       put_net(link_net);
-               put_net(dest_net);
-               return err;
+       if (link_net)
+               put_net(link_net);
+       put_net(dest_net);
+       return err;
 out_unregister:
-               if (ops->newlink) {
-                       LIST_HEAD(list_kill);
+       if (ops->newlink) {
+               LIST_HEAD(list_kill);
 
-                       ops->dellink(dev, &list_kill);
-                       unregister_netdevice_many(&list_kill);
-               } else {
-                       unregister_netdevice(dev);
-               }
-               goto out;
+               ops->dellink(dev, &list_kill);
+               unregister_netdevice_many(&list_kill);
+       } else {
+               unregister_netdevice(dev);
        }
+       goto out;
+}
+
+static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
+                       struct netlink_ext_ack *extack)
+{
+       struct nlattr **attr;
+       int ret;
+
+       attr = kmalloc_array(RTNL_MAX_TYPE + 1, sizeof(*attr), GFP_KERNEL);
+       if (!attr)
+               return -ENOMEM;
+
+       ret = __rtnl_newlink(skb, nlh, attr, extack);
+       kfree(attr);
+       return ret;
 }
 
 static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -3808,6 +3819,9 @@ int ndo_dflt_fdb_dump(struct sk_buff *skb,
 {
        int err;
 
+       if (dev->type != ARPHRD_ETHER)
+               return -EINVAL;
+
        netif_addr_lock_bh(dev);
        err = nlmsg_populate_fdb(skb, cb, dev, idx, &dev->uc);
        if (err)
@@ -4007,6 +4021,160 @@ out:
        return skb->len;
 }
 
+static int valid_fdb_get_strict(const struct nlmsghdr *nlh,
+                               struct nlattr **tb, u8 *ndm_flags,
+                               int *br_idx, int *brport_idx, u8 **addr,
+                               u16 *vid, struct netlink_ext_ack *extack)
+{
+       struct ndmsg *ndm;
+       int err, i;
+
+       if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) {
+               NL_SET_ERR_MSG(extack, "Invalid header for fdb get request");
+               return -EINVAL;
+       }
+
+       ndm = nlmsg_data(nlh);
+       if (ndm->ndm_pad1  || ndm->ndm_pad2  || ndm->ndm_state ||
+           ndm->ndm_type) {
+               NL_SET_ERR_MSG(extack, "Invalid values in header for fdb get request");
+               return -EINVAL;
+       }
+
+       if (ndm->ndm_flags & ~(NTF_MASTER | NTF_SELF)) {
+               NL_SET_ERR_MSG(extack, "Invalid flags in header for fdb get request");
+               return -EINVAL;
+       }
+
+       err = nlmsg_parse_strict(nlh, sizeof(struct ndmsg), tb, NDA_MAX,
+                                nda_policy, extack);
+       if (err < 0)
+               return err;
+
+       *ndm_flags = ndm->ndm_flags;
+       *brport_idx = ndm->ndm_ifindex;
+       for (i = 0; i <= NDA_MAX; ++i) {
+               if (!tb[i])
+                       continue;
+
+               switch (i) {
+               case NDA_MASTER:
+                       *br_idx = nla_get_u32(tb[i]);
+                       break;
+               case NDA_LLADDR:
+                       if (nla_len(tb[i]) != ETH_ALEN) {
+                               NL_SET_ERR_MSG(extack, "Invalid address in fdb get request");
+                               return -EINVAL;
+                       }
+                       *addr = nla_data(tb[i]);
+                       break;
+               case NDA_VLAN:
+                       err = fdb_vid_parse(tb[i], vid, extack);
+                       if (err)
+                               return err;
+                       break;
+               case NDA_VNI:
+                       break;
+               default:
+                       NL_SET_ERR_MSG(extack, "Unsupported attribute in fdb get request");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int rtnl_fdb_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
+                       struct netlink_ext_ack *extack)
+{
+       struct net_device *dev = NULL, *br_dev = NULL;
+       const struct net_device_ops *ops = NULL;
+       struct net *net = sock_net(in_skb->sk);
+       struct nlattr *tb[NDA_MAX + 1];
+       struct sk_buff *skb;
+       int brport_idx = 0;
+       u8 ndm_flags = 0;
+       int br_idx = 0;
+       u8 *addr = NULL;
+       u16 vid = 0;
+       int err;
+
+       err = valid_fdb_get_strict(nlh, tb, &ndm_flags, &br_idx,
+                                  &brport_idx, &addr, &vid, extack);
+       if (err < 0)
+               return err;
+
+       if (brport_idx) {
+               dev = __dev_get_by_index(net, brport_idx);
+               if (!dev) {
+                       NL_SET_ERR_MSG(extack, "Unknown device ifindex");
+                       return -ENODEV;
+               }
+       }
+
+       if (br_idx) {
+               if (dev) {
+                       NL_SET_ERR_MSG(extack, "Master and device are mutually exclusive");
+                       return -EINVAL;
+               }
+
+               br_dev = __dev_get_by_index(net, br_idx);
+               if (!br_dev) {
+                       NL_SET_ERR_MSG(extack, "Invalid master ifindex");
+                       return -EINVAL;
+               }
+               ops = br_dev->netdev_ops;
+       }
+
+       if (dev) {
+               if (!ndm_flags || (ndm_flags & NTF_MASTER)) {
+                       if (!(dev->priv_flags & IFF_BRIDGE_PORT)) {
+                               NL_SET_ERR_MSG(extack, "Device is not a bridge port");
+                               return -EINVAL;
+                       }
+                       br_dev = netdev_master_upper_dev_get(dev);
+                       if (!br_dev) {
+                               NL_SET_ERR_MSG(extack, "Master of device not found");
+                               return -EINVAL;
+                       }
+                       ops = br_dev->netdev_ops;
+               } else {
+                       if (!(ndm_flags & NTF_SELF)) {
+                               NL_SET_ERR_MSG(extack, "Missing NTF_SELF");
+                               return -EINVAL;
+                       }
+                       ops = dev->netdev_ops;
+               }
+       }
+
+       if (!br_dev && !dev) {
+               NL_SET_ERR_MSG(extack, "No device specified");
+               return -ENODEV;
+       }
+
+       if (!ops || !ops->ndo_fdb_get) {
+               NL_SET_ERR_MSG(extack, "Fdb get operation not supported by device");
+               return -EOPNOTSUPP;
+       }
+
+       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb)
+               return -ENOBUFS;
+
+       if (br_dev)
+               dev = br_dev;
+       err = ops->ndo_fdb_get(skb, tb, dev, addr, vid,
+                              NETLINK_CB(in_skb).portid,
+                              nlh->nlmsg_seq, extack);
+       if (err)
+               goto out;
+
+       return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
+out:
+       kfree_skb(skb);
+       return err;
+}
+
 static int brport_nla_put_flag(struct sk_buff *skb, u32 flags, u32 mask,
                               unsigned int attrnum, unsigned int flag)
 {
@@ -4318,7 +4486,8 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
                        goto out;
                }
 
-               err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh, flags);
+               err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh, flags,
+                                                            extack);
                if (err)
                        goto out;
 
@@ -4330,7 +4499,8 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
                        err = -EOPNOTSUPP;
                else
                        err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh,
-                                                                 flags);
+                                                                 flags,
+                                                                 extack);
                if (!err) {
                        flags &= ~BRIDGE_FLAGS_SELF;
 
@@ -5065,7 +5235,7 @@ void __init rtnetlink_init(void)
 
        rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, 0);
        rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, 0);
-       rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, 0);
+       rtnl_register(PF_BRIDGE, RTM_GETNEIGH, rtnl_fdb_get, rtnl_fdb_dump, 0);
 
        rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, 0);
        rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, 0);
index 396fcb3..cb0bf42 100644 (file)
@@ -79,6 +79,9 @@
 
 struct kmem_cache *skbuff_head_cache __ro_after_init;
 static struct kmem_cache *skbuff_fclone_cache __ro_after_init;
+#ifdef CONFIG_SKB_EXTENSIONS
+static struct kmem_cache *skbuff_ext_cache __ro_after_init;
+#endif
 int sysctl_max_skb_frags __read_mostly = MAX_SKB_FRAGS;
 EXPORT_SYMBOL(sysctl_max_skb_frags);
 
@@ -606,7 +609,6 @@ fastpath:
 void skb_release_head_state(struct sk_buff *skb)
 {
        skb_dst_drop(skb);
-       secpath_reset(skb);
        if (skb->destructor) {
                WARN_ON(in_irq());
                skb->destructor(skb);
@@ -614,9 +616,7 @@ void skb_release_head_state(struct sk_buff *skb)
 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
        nf_conntrack_put(skb_nfct(skb));
 #endif
-#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-       nf_bridge_put(skb->nf_bridge);
-#endif
+       skb_ext_put(skb);
 }
 
 /* Free everything but the sk_buff shell. */
@@ -796,9 +796,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->dev                = old->dev;
        memcpy(new->cb, old->cb, sizeof(old->cb));
        skb_dst_copy(new, old);
-#ifdef CONFIG_XFRM
-       new->sp                 = secpath_get(old->sp);
-#endif
+       __skb_ext_copy(new, old);
        __nf_copy(new, old, false);
 
        /* Note : this field could be in headers_start/headers_end section
@@ -1089,7 +1087,7 @@ void sock_zerocopy_put(struct ubuf_info *uarg)
 }
 EXPORT_SYMBOL_GPL(sock_zerocopy_put);
 
-void sock_zerocopy_put_abort(struct ubuf_info *uarg)
+void sock_zerocopy_put_abort(struct ubuf_info *uarg, bool have_uref)
 {
        if (uarg) {
                struct sock *sk = skb_from_uarg(uarg)->sk;
@@ -1097,7 +1095,8 @@ void sock_zerocopy_put_abort(struct ubuf_info *uarg)
                atomic_dec(&sk->sk_zckey);
                uarg->len--;
 
-               sock_zerocopy_put(uarg);
+               if (have_uref)
+                       sock_zerocopy_put(uarg);
        }
 }
 EXPORT_SYMBOL_GPL(sock_zerocopy_put_abort);
@@ -1105,6 +1104,12 @@ EXPORT_SYMBOL_GPL(sock_zerocopy_put_abort);
 extern int __zerocopy_sg_from_iter(struct sock *sk, struct sk_buff *skb,
                                   struct iov_iter *from, size_t length);
 
+int skb_zerocopy_iter_dgram(struct sk_buff *skb, struct msghdr *msg, int len)
+{
+       return __zerocopy_sg_from_iter(skb->sk, skb, &msg->msg_iter, len);
+}
+EXPORT_SYMBOL_GPL(skb_zerocopy_iter_dgram);
+
 int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,
                             struct msghdr *msg, int len,
                             struct ubuf_info *uarg)
@@ -1131,7 +1136,7 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,
                return err;
        }
 
-       skb_zcopy_set(skb, uarg);
+       skb_zcopy_set(skb, uarg, NULL);
        return skb->len - orig_len;
 }
 EXPORT_SYMBOL_GPL(skb_zerocopy_iter_stream);
@@ -1151,7 +1156,7 @@ static int skb_zerocopy_clone(struct sk_buff *nskb, struct sk_buff *orig,
                        if (skb_copy_ubufs(nskb, GFP_ATOMIC))
                                return -EIO;
                }
-               skb_zcopy_set(nskb, skb_uarg(orig));
+               skb_zcopy_set(nskb, skb_uarg(orig), NULL);
        }
        return 0;
 }
@@ -1925,8 +1930,6 @@ void *__pskb_pull_tail(struct sk_buff *skb, int delta)
                struct sk_buff *insp = NULL;
 
                do {
-                       BUG_ON(!list);
-
                        if (list->len <= eat) {
                                /* Eaten as whole. */
                                eat -= list->len;
@@ -2366,19 +2369,6 @@ error:
 }
 EXPORT_SYMBOL_GPL(skb_send_sock_locked);
 
-/* Send skb data on a socket. */
-int skb_send_sock(struct sock *sk, struct sk_buff *skb, int offset, int len)
-{
-       int ret = 0;
-
-       lock_sock(sk);
-       ret = skb_send_sock_locked(sk, skb, offset, len);
-       release_sock(sk);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(skb_send_sock);
-
 /**
  *     skb_store_bits - store bits from kernel buffer to skb
  *     @skb: destination buffer
@@ -2650,10 +2640,11 @@ __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
        __sum16 sum;
 
        sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));
+       /* See comments in __skb_checksum_complete(). */
        if (likely(!sum)) {
                if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
                    !skb->csum_complete_sw)
-                       netdev_rx_csum_fault(skb->dev);
+                       netdev_rx_csum_fault(skb->dev, skb);
        }
        if (!skb_shared(skb))
                skb->csum_valid = !sum;
@@ -2661,6 +2652,15 @@ __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
 }
 EXPORT_SYMBOL(__skb_checksum_complete_head);
 
+/* This function assumes skb->csum already holds pseudo header's checksum,
+ * which has been changed from the hardware checksum, for example, by
+ * __skb_checksum_validate_complete(). And, the original skb->csum must
+ * have been validated unsuccessfully for CHECKSUM_COMPLETE case.
+ *
+ * It returns non-zero if the recomputed checksum is still invalid, otherwise
+ * zero. The new checksum is stored back into skb->csum unless the skb is
+ * shared.
+ */
 __sum16 __skb_checksum_complete(struct sk_buff *skb)
 {
        __wsum csum;
@@ -2668,12 +2668,18 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb)
 
        csum = skb_checksum(skb, 0, skb->len, 0);
 
-       /* skb->csum holds pseudo checksum */
        sum = csum_fold(csum_add(skb->csum, csum));
+       /* This check is inverted, because we already knew the hardware
+        * checksum is invalid before calling this function. So, if the
+        * re-computed checksum is valid instead, then we have a mismatch
+        * between the original skb->csum and skb_checksum(). This means either
+        * the original hardware checksum is incorrect or we screw up skb->csum
+        * when moving skb->data around.
+        */
        if (likely(!sum)) {
                if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
                    !skb->csum_complete_sw)
-                       netdev_rx_csum_fault(skb->dev);
+                       netdev_rx_csum_fault(skb->dev, skb);
        }
 
        if (!skb_shared(skb)) {
@@ -3005,28 +3011,6 @@ void skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head
 }
 EXPORT_SYMBOL(skb_append);
 
-/**
- *     skb_insert      -       insert a buffer
- *     @old: buffer to insert before
- *     @newsk: buffer to insert
- *     @list: list to use
- *
- *     Place a packet before a given packet in a list. The list locks are
- *     taken and this function is atomic with respect to other list locked
- *     calls.
- *
- *     A buffer cannot be placed on two lists at the same time.
- */
-void skb_insert(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&list->lock, flags);
-       __skb_insert(newsk, old->prev, old, list);
-       spin_unlock_irqrestore(&list->lock, flags);
-}
-EXPORT_SYMBOL(skb_insert);
-
 static inline void skb_split_inside_header(struct sk_buff *skb,
                                           struct sk_buff* skb1,
                                           const u32 len, const int pos)
@@ -3916,6 +3900,46 @@ done:
 }
 EXPORT_SYMBOL_GPL(skb_gro_receive);
 
+#ifdef CONFIG_SKB_EXTENSIONS
+#define SKB_EXT_ALIGN_VALUE    8
+#define SKB_EXT_CHUNKSIZEOF(x) (ALIGN((sizeof(x)), SKB_EXT_ALIGN_VALUE) / SKB_EXT_ALIGN_VALUE)
+
+static const u8 skb_ext_type_len[] = {
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+       [SKB_EXT_BRIDGE_NF] = SKB_EXT_CHUNKSIZEOF(struct nf_bridge_info),
+#endif
+#ifdef CONFIG_XFRM
+       [SKB_EXT_SEC_PATH] = SKB_EXT_CHUNKSIZEOF(struct sec_path),
+#endif
+};
+
+static __always_inline unsigned int skb_ext_total_length(void)
+{
+       return SKB_EXT_CHUNKSIZEOF(struct skb_ext) +
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+               skb_ext_type_len[SKB_EXT_BRIDGE_NF] +
+#endif
+#ifdef CONFIG_XFRM
+               skb_ext_type_len[SKB_EXT_SEC_PATH] +
+#endif
+               0;
+}
+
+static void skb_extensions_init(void)
+{
+       BUILD_BUG_ON(SKB_EXT_NUM >= 8);
+       BUILD_BUG_ON(skb_ext_total_length() > 255);
+
+       skbuff_ext_cache = kmem_cache_create("skbuff_ext_cache",
+                                            SKB_EXT_ALIGN_VALUE * skb_ext_total_length(),
+                                            0,
+                                            SLAB_HWCACHE_ALIGN|SLAB_PANIC,
+                                            NULL);
+}
+#else
+static void skb_extensions_init(void) {}
+#endif
+
 void __init skb_init(void)
 {
        skbuff_head_cache = kmem_cache_create_usercopy("skbuff_head_cache",
@@ -3930,6 +3954,7 @@ void __init skb_init(void)
                                                0,
                                                SLAB_HWCACHE_ALIGN|SLAB_PANIC,
                                                NULL);
+       skb_extensions_init();
 }
 
 static int
@@ -4897,6 +4922,11 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet)
        nf_reset(skb);
        nf_reset_trace(skb);
 
+#ifdef CONFIG_NET_SWITCHDEV
+       skb->offload_fwd_mark = 0;
+       skb->offload_l3_fwd_mark = 0;
+#endif
+
        if (!xnet)
                return;
 
@@ -5563,3 +5593,151 @@ void skb_condense(struct sk_buff *skb)
         */
        skb->truesize = SKB_TRUESIZE(skb_end_offset(skb));
 }
+
+#ifdef CONFIG_SKB_EXTENSIONS
+static void *skb_ext_get_ptr(struct skb_ext *ext, enum skb_ext_id id)
+{
+       return (void *)ext + (ext->offset[id] * SKB_EXT_ALIGN_VALUE);
+}
+
+static struct skb_ext *skb_ext_alloc(void)
+{
+       struct skb_ext *new = kmem_cache_alloc(skbuff_ext_cache, GFP_ATOMIC);
+
+       if (new) {
+               memset(new->offset, 0, sizeof(new->offset));
+               refcount_set(&new->refcnt, 1);
+       }
+
+       return new;
+}
+
+static struct skb_ext *skb_ext_maybe_cow(struct skb_ext *old,
+                                        unsigned int old_active)
+{
+       struct skb_ext *new;
+
+       if (refcount_read(&old->refcnt) == 1)
+               return old;
+
+       new = kmem_cache_alloc(skbuff_ext_cache, GFP_ATOMIC);
+       if (!new)
+               return NULL;
+
+       memcpy(new, old, old->chunks * SKB_EXT_ALIGN_VALUE);
+       refcount_set(&new->refcnt, 1);
+
+#ifdef CONFIG_XFRM
+       if (old_active & (1 << SKB_EXT_SEC_PATH)) {
+               struct sec_path *sp = skb_ext_get_ptr(old, SKB_EXT_SEC_PATH);
+               unsigned int i;
+
+               for (i = 0; i < sp->len; i++)
+                       xfrm_state_hold(sp->xvec[i]);
+       }
+#endif
+       __skb_ext_put(old);
+       return new;
+}
+
+/**
+ * skb_ext_add - allocate space for given extension, COW if needed
+ * @skb: buffer
+ * @id: extension to allocate space for
+ *
+ * Allocates enough space for the given extension.
+ * If the extension is already present, a pointer to that extension
+ * is returned.
+ *
+ * If the skb was cloned, COW applies and the returned memory can be
+ * modified without changing the extension space of clones buffers.
+ *
+ * Returns pointer to the extension or NULL on allocation failure.
+ */
+void *skb_ext_add(struct sk_buff *skb, enum skb_ext_id id)
+{
+       struct skb_ext *new, *old = NULL;
+       unsigned int newlen, newoff;
+
+       if (skb->active_extensions) {
+               old = skb->extensions;
+
+               new = skb_ext_maybe_cow(old, skb->active_extensions);
+               if (!new)
+                       return NULL;
+
+               if (__skb_ext_exist(old, id)) {
+                       if (old != new)
+                               skb->extensions = new;
+                       goto set_active;
+               }
+
+               newoff = old->chunks;
+       } else {
+               newoff = SKB_EXT_CHUNKSIZEOF(*new);
+
+               new = skb_ext_alloc();
+               if (!new)
+                       return NULL;
+       }
+
+       newlen = newoff + skb_ext_type_len[id];
+       new->chunks = newlen;
+       new->offset[id] = newoff;
+       skb->extensions = new;
+set_active:
+       skb->active_extensions |= 1 << id;
+       return skb_ext_get_ptr(new, id);
+}
+EXPORT_SYMBOL(skb_ext_add);
+
+#ifdef CONFIG_XFRM
+static void skb_ext_put_sp(struct sec_path *sp)
+{
+       unsigned int i;
+
+       for (i = 0; i < sp->len; i++)
+               xfrm_state_put(sp->xvec[i]);
+}
+#endif
+
+void __skb_ext_del(struct sk_buff *skb, enum skb_ext_id id)
+{
+       struct skb_ext *ext = skb->extensions;
+
+       skb->active_extensions &= ~(1 << id);
+       if (skb->active_extensions == 0) {
+               skb->extensions = NULL;
+               __skb_ext_put(ext);
+#ifdef CONFIG_XFRM
+       } else if (id == SKB_EXT_SEC_PATH &&
+                  refcount_read(&ext->refcnt) == 1) {
+               struct sec_path *sp = skb_ext_get_ptr(ext, SKB_EXT_SEC_PATH);
+
+               skb_ext_put_sp(sp);
+               sp->len = 0;
+#endif
+       }
+}
+EXPORT_SYMBOL(__skb_ext_del);
+
+void __skb_ext_put(struct skb_ext *ext)
+{
+       /* If this is last clone, nothing can increment
+        * it after check passes.  Avoids one atomic op.
+        */
+       if (refcount_read(&ext->refcnt) == 1)
+               goto free_now;
+
+       if (!refcount_dec_and_test(&ext->refcnt))
+               return;
+free_now:
+#ifdef CONFIG_XFRM
+       if (__skb_ext_exist(ext, SKB_EXT_SEC_PATH))
+               skb_ext_put_sp(skb_ext_get_ptr(ext, SKB_EXT_SEC_PATH));
+#endif
+
+       kmem_cache_free(skbuff_ext_cache, ext);
+}
+EXPORT_SYMBOL(__skb_ext_put);
+#endif /* CONFIG_SKB_EXTENSIONS */
index 56a99d0..86c9726 100644 (file)
@@ -403,7 +403,7 @@ static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb)
        msg->skb = skb;
 
        sk_psock_queue_msg(psock, msg);
-       sk->sk_data_ready(sk);
+       sk_psock_data_ready(sk, psock);
        return copied;
 }
 
@@ -572,6 +572,7 @@ void sk_psock_drop(struct sock *sk, struct sk_psock *psock)
 {
        rcu_assign_sk_user_data(sk, NULL);
        sk_psock_cork_free(psock);
+       sk_psock_zap_ingress(psock);
        sk_psock_restore_proto(sk, psock);
 
        write_lock_bh(&sk->sk_callback_lock);
@@ -669,6 +670,22 @@ static void sk_psock_verdict_apply(struct sk_psock *psock,
        bool ingress;
 
        switch (verdict) {
+       case __SK_PASS:
+               sk_other = psock->sk;
+               if (sock_flag(sk_other, SOCK_DEAD) ||
+                   !sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) {
+                       goto out_free;
+               }
+               if (atomic_read(&sk_other->sk_rmem_alloc) <=
+                   sk_other->sk_rcvbuf) {
+                       struct tcp_skb_cb *tcp = TCP_SKB_CB(skb);
+
+                       tcp->bpf.flags |= BPF_F_INGRESS;
+                       skb_queue_tail(&psock->ingress_skb, skb);
+                       schedule_work(&psock->work);
+                       break;
+               }
+               goto out_free;
        case __SK_REDIRECT:
                sk_other = tcp_skb_bpf_redirect_fetch(skb);
                if (unlikely(!sk_other))
@@ -735,7 +752,7 @@ static int sk_psock_strp_parse(struct strparser *strp, struct sk_buff *skb)
 }
 
 /* Called with socket lock held. */
-static void sk_psock_data_ready(struct sock *sk)
+static void sk_psock_strp_data_ready(struct sock *sk)
 {
        struct sk_psock *psock;
 
@@ -783,7 +800,7 @@ void sk_psock_start_strp(struct sock *sk, struct sk_psock *psock)
                return;
 
        parser->saved_data_ready = sk->sk_data_ready;
-       sk->sk_data_ready = sk_psock_data_ready;
+       sk->sk_data_ready = sk_psock_strp_data_ready;
        sk->sk_write_space = sk_psock_write_space;
        parser->enabled = true;
 }
index 6d7e189..f00902c 100644 (file)
@@ -700,6 +700,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
                break;
        case SO_DONTROUTE:
                sock_valbool_flag(sk, SOCK_LOCALROUTE, valbool);
+               sk_dst_reset(sk);
                break;
        case SO_BROADCAST:
                sock_valbool_flag(sk, SOCK_BROADCAST, valbool);
@@ -1018,7 +1019,10 @@ set_rcvbuf:
 
        case SO_ZEROCOPY:
                if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6) {
-                       if (sk->sk_protocol != IPPROTO_TCP)
+                       if (!((sk->sk_type == SOCK_STREAM &&
+                              sk->sk_protocol == IPPROTO_TCP) ||
+                             (sk->sk_type == SOCK_DGRAM &&
+                              sk->sk_protocol == IPPROTO_UDP)))
                                ret = -ENOTSUPP;
                } else if (sk->sk_family != PF_RDS) {
                        ret = -ENOTSUPP;
index ba5cba5..d8fe3e5 100644 (file)
@@ -187,6 +187,7 @@ int reuseport_add_sock(struct sock *sk, struct sock *sk2, bool bind_inany)
                call_rcu(&old_reuse->rcu, reuseport_free_rcu);
        return 0;
 }
+EXPORT_SYMBOL(reuseport_add_sock);
 
 void reuseport_detach_sock(struct sock *sk)
 {
index 7d329fb..e94bb02 100644 (file)
@@ -32,7 +32,7 @@ void sk_stream_write_space(struct sock *sk)
        struct socket *sock = sk->sk_socket;
        struct socket_wq *wq;
 
-       if (sk_stream_is_writeable(sk) && sock) {
+       if (__sk_stream_is_writeable(sk, 1) && sock) {
                clear_bit(SOCK_NOSPACE, &sock->flags);
 
                rcu_read_lock();
index 37b4667..d67ec17 100644 (file)
@@ -28,6 +28,8 @@ static int two __maybe_unused = 2;
 static int min_sndbuf = SOCK_MIN_SNDBUF;
 static int min_rcvbuf = SOCK_MIN_RCVBUF;
 static int max_skb_frags = MAX_SKB_FRAGS;
+static long long_one __maybe_unused = 1;
+static long long_max __maybe_unused = LONG_MAX;
 
 static int net_msg_warn;       /* Unused, but still a sysctl */
 
@@ -289,6 +291,17 @@ proc_dointvec_minmax_bpf_restricted(struct ctl_table *table, int write,
 
        return proc_dointvec_minmax(table, write, buffer, lenp, ppos);
 }
+
+static int
+proc_dolongvec_minmax_bpf_restricted(struct ctl_table *table, int write,
+                                    void __user *buffer, size_t *lenp,
+                                    loff_t *ppos)
+{
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
+}
 #endif
 
 static struct ctl_table net_core_table[] = {
@@ -398,10 +411,11 @@ static struct ctl_table net_core_table[] = {
        {
                .procname       = "bpf_jit_limit",
                .data           = &bpf_jit_limit,
-               .maxlen         = sizeof(int),
+               .maxlen         = sizeof(long),
                .mode           = 0600,
-               .proc_handler   = proc_dointvec_minmax_bpf_restricted,
-               .extra1         = &one,
+               .proc_handler   = proc_dolongvec_minmax_bpf_restricted,
+               .extra1         = &long_one,
+               .extra2         = &long_max,
        },
 #endif
        {
index 658cd32..be0b223 100644 (file)
@@ -1141,6 +1141,9 @@ static int __init dccp_init(void)
                goto out_fail;
        rc = -ENOBUFS;
        inet_hashinfo_init(&dccp_hashinfo);
+       inet_hashinfo2_init(&dccp_hashinfo, "dccp_listen_portaddr_hash",
+                           INET_LHTABLE_SIZE, 21,  /* one slot per 2 MB*/
+                           0, 64 * 1024);
        dccp_hashinfo.bind_bucket_cachep =
                kmem_cache_create("dccp_bind_bucket",
                                  sizeof(struct inet_bind_bucket), 0,
index 7d6ff98..7aab5d0 100644 (file)
@@ -192,7 +192,7 @@ static int check_port(__le16 port)
 static unsigned short port_alloc(struct sock *sk)
 {
        struct dn_scp *scp = DN_SK(sk);
-static unsigned short port = 0x2000;
+       static unsigned short port = 0x2000;
        unsigned short i_port = port;
 
        while(check_port(cpu_to_le16(++port)) != 0) {
index 48c4191..91e5297 100644 (file)
@@ -44,6 +44,10 @@ config NET_DSA_TAG_GSWIP
 config NET_DSA_TAG_KSZ
        bool
 
+config NET_DSA_TAG_KSZ9477
+       bool
+       select NET_DSA_TAG_KSZ
+
 config NET_DSA_TAG_LAN9303
        bool
 
index a69c179..aee909b 100644 (file)
@@ -55,8 +55,8 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = {
 #ifdef CONFIG_NET_DSA_TAG_GSWIP
        [DSA_TAG_PROTO_GSWIP] = &gswip_netdev_ops,
 #endif
-#ifdef CONFIG_NET_DSA_TAG_KSZ
-       [DSA_TAG_PROTO_KSZ] = &ksz_netdev_ops,
+#ifdef CONFIG_NET_DSA_TAG_KSZ9477
+       [DSA_TAG_PROTO_KSZ9477] = &ksz9477_netdev_ops,
 #endif
 #ifdef CONFIG_NET_DSA_TAG_LAN9303
        [DSA_TAG_PROTO_LAN9303] = &lan9303_netdev_ops,
@@ -91,8 +91,8 @@ const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops)
 #ifdef CONFIG_NET_DSA_TAG_GSWIP
                [DSA_TAG_PROTO_GSWIP] = "gswip",
 #endif
-#ifdef CONFIG_NET_DSA_TAG_KSZ
-               [DSA_TAG_PROTO_KSZ] = "ksz",
+#ifdef CONFIG_NET_DSA_TAG_KSZ9477
+               [DSA_TAG_PROTO_KSZ9477] = "ksz9477",
 #endif
 #ifdef CONFIG_NET_DSA_TAG_LAN9303
                [DSA_TAG_PROTO_LAN9303] = "lan9303",
index 9e4fd04..026a057 100644 (file)
@@ -210,7 +210,7 @@ extern const struct dsa_device_ops edsa_netdev_ops;
 extern const struct dsa_device_ops gswip_netdev_ops;
 
 /* tag_ksz.c */
-extern const struct dsa_device_ops ksz_netdev_ops;
+extern const struct dsa_device_ops ksz9477_netdev_ops;
 
 /* tag_lan9303.c */
 extern const struct dsa_device_ops lan9303_netdev_ops;
index c90ee32..71bb15f 100644 (file)
@@ -158,8 +158,59 @@ static void dsa_master_ethtool_teardown(struct net_device *dev)
        cpu_dp->orig_ethtool_ops = NULL;
 }
 
+static ssize_t tagging_show(struct device *d, struct device_attribute *attr,
+                           char *buf)
+{
+       struct net_device *dev = to_net_dev(d);
+       struct dsa_port *cpu_dp = dev->dsa_ptr;
+
+       return sprintf(buf, "%s\n",
+                      dsa_tag_protocol_to_str(cpu_dp->tag_ops));
+}
+static DEVICE_ATTR_RO(tagging);
+
+static struct attribute *dsa_slave_attrs[] = {
+       &dev_attr_tagging.attr,
+       NULL
+};
+
+static const struct attribute_group dsa_group = {
+       .name   = "dsa",
+       .attrs  = dsa_slave_attrs,
+};
+
+static void dsa_master_set_mtu(struct net_device *dev, struct dsa_port *cpu_dp)
+{
+       unsigned int mtu = ETH_DATA_LEN + cpu_dp->tag_ops->overhead;
+       int err;
+
+       rtnl_lock();
+       if (mtu <= dev->max_mtu) {
+               err = dev_set_mtu(dev, mtu);
+               if (err)
+                       netdev_dbg(dev, "Unable to set MTU to include for DSA overheads\n");
+       }
+       rtnl_unlock();
+}
+
+static void dsa_master_reset_mtu(struct net_device *dev)
+{
+       int err;
+
+       rtnl_lock();
+       err = dev_set_mtu(dev, ETH_DATA_LEN);
+       if (err)
+               netdev_dbg(dev,
+                          "Unable to reset MTU to exclude DSA overheads\n");
+       rtnl_unlock();
+}
+
 int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
 {
+       int ret;
+
+       dsa_master_set_mtu(dev,  cpu_dp);
+
        /* If we use a tagging format that doesn't have an ethertype
         * field, make sure that all packets from this point on get
         * sent to the tag format's receive function.
@@ -168,12 +219,22 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
 
        dev->dsa_ptr = cpu_dp;
 
-       return dsa_master_ethtool_setup(dev);
+       ret = dsa_master_ethtool_setup(dev);
+       if (ret)
+               return ret;
+
+       ret = sysfs_create_group(&dev->dev.kobj, &dsa_group);
+       if (ret)
+               dsa_master_ethtool_teardown(dev);
+
+       return ret;
 }
 
 void dsa_master_teardown(struct net_device *dev)
 {
+       sysfs_remove_group(&dev->dev.kobj, &dsa_group);
        dsa_master_ethtool_teardown(dev);
+       dsa_master_reset_mtu(dev);
 
        dev->dsa_ptr = NULL;
 
index ed05954..2d7e01b 100644 (file)
@@ -252,9 +252,6 @@ int dsa_port_vlan_add(struct dsa_port *dp,
                .vlan = vlan,
        };
 
-       if (netif_is_bridge_master(vlan->obj.orig_dev))
-               return -EOPNOTSUPP;
-
        if (br_vlan_enabled(dp->bridge_dev))
                return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
 
index 7d0c19e..a3fcc1d 100644 (file)
@@ -1050,35 +1050,12 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
 static const struct switchdev_ops dsa_slave_switchdev_ops = {
        .switchdev_port_attr_get        = dsa_slave_port_attr_get,
        .switchdev_port_attr_set        = dsa_slave_port_attr_set,
-       .switchdev_port_obj_add         = dsa_slave_port_obj_add,
-       .switchdev_port_obj_del         = dsa_slave_port_obj_del,
 };
 
 static struct device_type dsa_type = {
        .name   = "dsa",
 };
 
-static ssize_t tagging_show(struct device *d, struct device_attribute *attr,
-                           char *buf)
-{
-       struct net_device *dev = to_net_dev(d);
-       struct dsa_port *dp = dsa_slave_to_port(dev);
-
-       return sprintf(buf, "%s\n",
-                      dsa_tag_protocol_to_str(dp->cpu_dp->tag_ops));
-}
-static DEVICE_ATTR_RO(tagging);
-
-static struct attribute *dsa_slave_attrs[] = {
-       &dev_attr_tagging.attr,
-       NULL
-};
-
-static const struct attribute_group dsa_group = {
-       .name   = "dsa",
-       .attrs  = dsa_slave_attrs,
-};
-
 static void dsa_slave_phylink_validate(struct net_device *dev,
                                       unsigned long *supported,
                                       struct phylink_link_state *state)
@@ -1374,14 +1351,8 @@ int dsa_slave_create(struct dsa_port *port)
                goto out_phy;
        }
 
-       ret = sysfs_create_group(&slave_dev->dev.kobj, &dsa_group);
-       if (ret)
-               goto out_unreg;
-
        return 0;
 
-out_unreg:
-       unregister_netdev(slave_dev);
 out_phy:
        rtnl_lock();
        phylink_disconnect_phy(p->dp->pl);
@@ -1405,7 +1376,6 @@ void dsa_slave_destroy(struct net_device *slave_dev)
        rtnl_unlock();
 
        dsa_slave_notify(slave_dev, DSA_PORT_UNREGISTER);
-       sysfs_remove_group(&slave_dev->dev.kobj, &dsa_group);
        unregister_netdev(slave_dev);
        phylink_destroy(dp->pl);
        free_percpu(p->stats64);
@@ -1557,6 +1527,44 @@ err_fdb_work_init:
        return NOTIFY_BAD;
 }
 
+static int
+dsa_slave_switchdev_port_obj_event(unsigned long event,
+                       struct net_device *netdev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info)
+{
+       int err = -EOPNOTSUPP;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD:
+               err = dsa_slave_port_obj_add(netdev, port_obj_info->obj,
+                                            port_obj_info->trans);
+               break;
+       case SWITCHDEV_PORT_OBJ_DEL:
+               err = dsa_slave_port_obj_del(netdev, port_obj_info->obj);
+               break;
+       }
+
+       port_obj_info->handled = true;
+       return notifier_from_errno(err);
+}
+
+static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,
+                                             unsigned long event, void *ptr)
+{
+       struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+
+       if (!dsa_slave_dev_check(dev))
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD: /* fall through */
+       case SWITCHDEV_PORT_OBJ_DEL:
+               return dsa_slave_switchdev_port_obj_event(event, dev, ptr);
+       }
+
+       return NOTIFY_DONE;
+}
+
 static struct notifier_block dsa_slave_nb __read_mostly = {
        .notifier_call  = dsa_slave_netdevice_event,
 };
@@ -1565,8 +1573,13 @@ static struct notifier_block dsa_slave_switchdev_notifier = {
        .notifier_call = dsa_slave_switchdev_event,
 };
 
+static struct notifier_block dsa_slave_switchdev_blocking_notifier = {
+       .notifier_call = dsa_slave_switchdev_blocking_event,
+};
+
 int dsa_slave_register_notifier(void)
 {
+       struct notifier_block *nb;
        int err;
 
        err = register_netdevice_notifier(&dsa_slave_nb);
@@ -1577,8 +1590,15 @@ int dsa_slave_register_notifier(void)
        if (err)
                goto err_switchdev_nb;
 
+       nb = &dsa_slave_switchdev_blocking_notifier;
+       err = register_switchdev_blocking_notifier(nb);
+       if (err)
+               goto err_switchdev_blocking_nb;
+
        return 0;
 
+err_switchdev_blocking_nb:
+       unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
 err_switchdev_nb:
        unregister_netdevice_notifier(&dsa_slave_nb);
        return err;
@@ -1586,8 +1606,14 @@ err_switchdev_nb:
 
 void dsa_slave_unregister_notifier(void)
 {
+       struct notifier_block *nb;
        int err;
 
+       nb = &dsa_slave_switchdev_blocking_notifier;
+       err = unregister_switchdev_blocking_notifier(nb);
+       if (err)
+               pr_err("DSA: failed to unregister switchdev blocking notifier (%d)\n", err);
+
        err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
        if (err)
                pr_err("DSA: failed to unregister switchdev notifier (%d)\n", err);
index 2b06bb9..4aa1d36 100644 (file)
@@ -174,6 +174,7 @@ static struct sk_buff *brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops brcm_netdev_ops = {
        .xmit   = brcm_tag_xmit,
        .rcv    = brcm_tag_rcv,
+       .overhead = BRCM_TAG_LEN,
 };
 #endif
 
@@ -196,5 +197,6 @@ static struct sk_buff *brcm_tag_rcv_prepend(struct sk_buff *skb,
 const struct dsa_device_ops brcm_prepend_netdev_ops = {
        .xmit   = brcm_tag_xmit_prepend,
        .rcv    = brcm_tag_rcv_prepend,
+       .overhead = BRCM_TAG_LEN,
 };
 #endif
index cd13cfc..8b2f92e 100644 (file)
@@ -149,4 +149,5 @@ static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops dsa_netdev_ops = {
        .xmit   = dsa_xmit,
        .rcv    = dsa_rcv,
+       .overhead = DSA_HLEN,
 };
index 4083326..f5b87ee 100644 (file)
@@ -168,4 +168,5 @@ static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops edsa_netdev_ops = {
        .xmit   = edsa_xmit,
        .rcv    = edsa_rcv,
+       .overhead = EDSA_HLEN,
 };
index 49e9b73..cb6f82f 100644 (file)
@@ -106,4 +106,5 @@ static struct sk_buff *gswip_tag_rcv(struct sk_buff *skb,
 const struct dsa_device_ops gswip_netdev_ops = {
        .xmit = gswip_tag_xmit,
        .rcv = gswip_tag_rcv,
+       .overhead = GSWIP_RX_HEADER_LEN,
 };
index 0f62eff..da71b9e 100644 (file)
 #include <net/dsa.h>
 #include "dsa_priv.h"
 
-/* For Ingress (Host -> KSZ), 2 bytes are added before FCS.
- * ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|tag1(1byte)|FCS(4bytes)
- * ---------------------------------------------------------------------------
- * tag0 : Prioritization (not used now)
- * tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
- *
- * For Egress (KSZ -> Host), 1 byte is added before FCS.
- * ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
- * ---------------------------------------------------------------------------
- * tag0 : zero-based value represents port
- *       (eg, 0x00=port1, 0x02=port3, 0x06=port7)
- */
-
-#define        KSZ_INGRESS_TAG_LEN     2
-#define        KSZ_EGRESS_TAG_LEN      1
+/* Typically only one byte is used for tail tag. */
+#define KSZ_EGRESS_TAG_LEN             1
 
-static struct sk_buff *ksz_xmit(struct sk_buff *skb, struct net_device *dev)
+static struct sk_buff *ksz_common_xmit(struct sk_buff *skb,
+                                      struct net_device *dev, int len)
 {
-       struct dsa_port *dp = dsa_slave_to_port(dev);
        struct sk_buff *nskb;
        int padlen;
-       u8 *tag;
 
        padlen = (skb->len >= ETH_ZLEN) ? 0 : ETH_ZLEN - skb->len;
 
-       if (skb_tailroom(skb) >= padlen + KSZ_INGRESS_TAG_LEN) {
+       if (skb_tailroom(skb) >= padlen + len) {
                /* Let dsa_slave_xmit() free skb */
                if (__skb_put_padto(skb, skb->len + padlen, false))
                        return NULL;
@@ -49,7 +33,7 @@ static struct sk_buff *ksz_xmit(struct sk_buff *skb, struct net_device *dev)
                nskb = skb;
        } else {
                nskb = alloc_skb(NET_IP_ALIGN + skb->len +
-                                padlen + KSZ_INGRESS_TAG_LEN, GFP_ATOMIC);
+                                padlen + len, GFP_ATOMIC);
                if (!nskb)
                        return NULL;
                skb_reserve(nskb, NET_IP_ALIGN);
@@ -70,33 +54,88 @@ static struct sk_buff *ksz_xmit(struct sk_buff *skb, struct net_device *dev)
                consume_skb(skb);
        }
 
-       tag = skb_put(nskb, KSZ_INGRESS_TAG_LEN);
-       tag[0] = 0;
-       tag[1] = 1 << dp->index; /* destination port */
-
        return nskb;
 }
 
-static struct sk_buff *ksz_rcv(struct sk_buff *skb, struct net_device *dev,
-                              struct packet_type *pt)
+static struct sk_buff *ksz_common_rcv(struct sk_buff *skb,
+                                     struct net_device *dev,
+                                     unsigned int port, unsigned int len)
 {
-       u8 *tag;
-       int source_port;
+       skb->dev = dsa_master_find_slave(dev, 0, port);
+       if (!skb->dev)
+               return NULL;
 
-       tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
+       pskb_trim_rcsum(skb, skb->len - len);
 
-       source_port = tag[0] & 7;
+       return skb;
+}
 
-       skb->dev = dsa_master_find_slave(dev, 0, source_port);
-       if (!skb->dev)
+/*
+ * For Ingress (Host -> KSZ9477), 2 bytes are added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|tag1(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag0 : Prioritization (not used now)
+ * tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
+ *
+ * For Egress (KSZ9477 -> Host), 1 byte is added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag0 : zero-based value represents port
+ *       (eg, 0x00=port1, 0x02=port3, 0x06=port7)
+ */
+
+#define KSZ9477_INGRESS_TAG_LEN                2
+#define KSZ9477_PTP_TAG_LEN            4
+#define KSZ9477_PTP_TAG_INDICATION     0x80
+
+#define KSZ9477_TAIL_TAG_OVERRIDE      BIT(9)
+#define KSZ9477_TAIL_TAG_LOOKUP                BIT(10)
+
+static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,
+                                   struct net_device *dev)
+{
+       struct dsa_port *dp = dsa_slave_to_port(dev);
+       struct sk_buff *nskb;
+       u16 *tag;
+       u8 *addr;
+
+       nskb = ksz_common_xmit(skb, dev, KSZ9477_INGRESS_TAG_LEN);
+       if (!nskb)
                return NULL;
 
-       pskb_trim_rcsum(skb, skb->len - KSZ_EGRESS_TAG_LEN);
+       /* Tag encoding */
+       tag = skb_put(nskb, KSZ9477_INGRESS_TAG_LEN);
+       addr = skb_mac_header(nskb);
 
-       return skb;
+       *tag = BIT(dp->index);
+
+       if (is_link_local_ether_addr(addr))
+               *tag |= KSZ9477_TAIL_TAG_OVERRIDE;
+
+       *tag = cpu_to_be16(*tag);
+
+       return nskb;
+}
+
+static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev,
+                                  struct packet_type *pt)
+{
+       /* Tag decoding */
+       u8 *tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
+       unsigned int port = tag[0] & 7;
+       unsigned int len = KSZ_EGRESS_TAG_LEN;
+
+       /* Extra 4-bytes PTP timestamp */
+       if (tag[0] & KSZ9477_PTP_TAG_INDICATION)
+               len += KSZ9477_PTP_TAG_LEN;
+
+       return ksz_common_rcv(skb, dev, port, len);
 }
 
-const struct dsa_device_ops ksz_netdev_ops = {
-       .xmit   = ksz_xmit,
-       .rcv    = ksz_rcv,
+const struct dsa_device_ops ksz9477_netdev_ops = {
+       .xmit   = ksz9477_xmit,
+       .rcv    = ksz9477_rcv,
+       .overhead = KSZ9477_INGRESS_TAG_LEN,
 };
index 548c002..f48889e 100644 (file)
@@ -140,4 +140,5 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops lan9303_netdev_ops = {
        .xmit = lan9303_xmit,
        .rcv = lan9303_rcv,
+       .overhead = LAN9303_TAG_LEN,
 };
index 11535bc..f39f4df 100644 (file)
@@ -109,4 +109,5 @@ const struct dsa_device_ops mtk_netdev_ops = {
        .xmit           = mtk_tag_xmit,
        .rcv            = mtk_tag_rcv,
        .flow_dissect   = mtk_tag_flow_dissect,
+       .overhead       = MTK_HDR_LEN,
 };
index 613f4ee..ed4f6dc 100644 (file)
@@ -101,4 +101,5 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops qca_netdev_ops = {
        .xmit   = qca_tag_xmit,
        .rcv    = qca_tag_rcv,
+       .overhead = QCA_HDR_LEN,
 };
index 56197f0..b40756e 100644 (file)
@@ -84,4 +84,5 @@ static struct sk_buff *trailer_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops trailer_netdev_ops = {
        .xmit   = trailer_xmit,
        .rcv    = trailer_rcv,
+       .overhead = 4,
 };
index fd8faa0..4c52011 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/inet.h>
 #include <linux/ip.h>
 #include <linux/netdevice.h>
+#include <linux/nvmem-consumer.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/errno.h>
@@ -165,15 +166,17 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
        eth = (struct ethhdr *)skb->data;
        skb_pull_inline(skb, ETH_HLEN);
 
-       if (unlikely(is_multicast_ether_addr_64bits(eth->h_dest))) {
-               if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
-                       skb->pkt_type = PACKET_BROADCAST;
-               else
-                       skb->pkt_type = PACKET_MULTICAST;
+       if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
+                                             dev->dev_addr))) {
+               if (unlikely(is_multicast_ether_addr_64bits(eth->h_dest))) {
+                       if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
+                               skb->pkt_type = PACKET_BROADCAST;
+                       else
+                               skb->pkt_type = PACKET_MULTICAST;
+               } else {
+                       skb->pkt_type = PACKET_OTHERHOST;
+               }
        }
-       else if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
-                                                  dev->dev_addr)))
-               skb->pkt_type = PACKET_OTHERHOST;
 
        /*
         * Some variants of DSA tagging don't have an ethertype field
@@ -548,3 +551,40 @@ int eth_platform_get_mac_address(struct device *dev, u8 *mac_addr)
        return 0;
 }
 EXPORT_SYMBOL(eth_platform_get_mac_address);
+
+/**
+ * Obtain the MAC address from an nvmem cell named 'mac-address' associated
+ * with given device.
+ *
+ * @dev:       Device with which the mac-address cell is associated.
+ * @addrbuf:   Buffer to which the MAC address will be copied on success.
+ *
+ * Returns 0 on success or a negative error number on failure.
+ */
+int nvmem_get_mac_address(struct device *dev, void *addrbuf)
+{
+       struct nvmem_cell *cell;
+       const void *mac;
+       size_t len;
+
+       cell = nvmem_cell_get(dev, "mac-address");
+       if (IS_ERR(cell))
+               return PTR_ERR(cell);
+
+       mac = nvmem_cell_read(cell, &len);
+       nvmem_cell_put(cell);
+
+       if (IS_ERR(mac))
+               return PTR_ERR(mac);
+
+       if (len != ETH_ALEN || !is_valid_ether_addr(mac)) {
+               kfree(mac);
+               return -EINVAL;
+       }
+
+       ether_addr_copy(addrbuf, mac);
+       kfree(mac);
+
+       return 0;
+}
+EXPORT_SYMBOL(nvmem_get_mac_address);
index b231e40..0c25c0b 100644 (file)
@@ -242,7 +242,7 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
                 * dev_set_mac_address require RTNL_LOCK
                 */
                rtnl_lock();
-               rc = dev_set_mac_address(dev, &addr);
+               rc = dev_set_mac_address(dev, &addr, NULL);
                rtnl_unlock();
                if (rc)
                        goto dev_unregister;
index 326c422..0dfb72c 100644 (file)
@@ -1385,6 +1385,10 @@ out:
 }
 EXPORT_SYMBOL(inet_gso_segment);
 
+INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *,
+                                                          struct sk_buff *));
+INDIRECT_CALLABLE_DECLARE(struct sk_buff *udp4_gro_receive(struct list_head *,
+                                                          struct sk_buff *));
 struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb)
 {
        const struct net_offload *ops;
@@ -1494,7 +1498,8 @@ struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb)
        skb_gro_pull(skb, sizeof(*iph));
        skb_set_transport_header(skb, skb_gro_offset(skb));
 
-       pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
+       pp = indirect_call_gro_receive(tcp4_gro_receive, udp4_gro_receive,
+                                      ops->callbacks.gro_receive, head, skb);
 
 out_unlock:
        rcu_read_unlock();
@@ -1556,6 +1561,8 @@ int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
        return -EINVAL;
 }
 
+INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *, int));
+INDIRECT_CALLABLE_DECLARE(int udp4_gro_complete(struct sk_buff *, int));
 int inet_gro_complete(struct sk_buff *skb, int nhoff)
 {
        __be16 newlen = htons(skb->len - nhoff);
@@ -1581,7 +1588,9 @@ int inet_gro_complete(struct sk_buff *skb, int nhoff)
         * because any hdr with option will have been flushed in
         * inet_gro_receive().
         */
-       err = ops->callbacks.gro_complete(skb, nhoff + sizeof(*iph));
+       err = INDIRECT_CALL_2(ops->callbacks.gro_complete,
+                             tcp4_gro_complete, udp4_gro_complete,
+                             skb, nhoff + sizeof(*iph));
 
 out_unlock:
        rcu_read_unlock();
index a34602a..04ba321 100644 (file)
@@ -952,17 +952,18 @@ static int inet_abc_len(__be32 addr)
 {
        int rc = -1;    /* Something else, probably a multicast. */
 
-       if (ipv4_is_zeronet(addr))
+       if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
                rc = 0;
        else {
                __u32 haddr = ntohl(addr);
-
                if (IN_CLASSA(haddr))
                        rc = 8;
                else if (IN_CLASSB(haddr))
                        rc = 16;
                else if (IN_CLASSC(haddr))
                        rc = 24;
+               else if (IN_CLASSE(haddr))
+                       rc = 32;
        }
 
        return rc;
@@ -1100,7 +1101,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
                                inet_del_ifa(in_dev, ifap, 1);
                        break;
                }
-               ret = dev_change_flags(dev, ifr->ifr_flags);
+               ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
                break;
 
        case SIOCSIFADDR:       /* Set interface address (and family) */
index 9e1c840..5459f41 100644 (file)
@@ -125,10 +125,13 @@ static void esp_output_done(struct crypto_async_request *base, int err)
        void *tmp;
        struct xfrm_state *x;
 
-       if (xo && (xo->flags & XFRM_DEV_RESUME))
-               x = skb->sp->xvec[skb->sp->len - 1];
-       else
+       if (xo && (xo->flags & XFRM_DEV_RESUME)) {
+               struct sec_path *sp = skb_sec_path(skb);
+
+               x = sp->xvec[sp->len - 1];
+       } else {
                x = skb_dst(skb)->xfrm;
+       }
 
        tmp = ESP_SKB_CB(skb)->tmp;
        esp_ssg_unref(x, tmp);
index 58834a1..8756e0e 100644 (file)
@@ -46,11 +46,12 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
 
        xo = xfrm_offload(skb);
        if (!xo || !(xo->flags & CRYPTO_DONE)) {
-               err = secpath_set(skb);
-               if (err)
+               struct sec_path *sp = secpath_set(skb);
+
+               if (!sp)
                        goto out;
 
-               if (skb->sp->len == XFRM_MAX_DEPTH)
+               if (sp->len == XFRM_MAX_DEPTH)
                        goto out;
 
                x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
@@ -59,8 +60,8 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
                if (!x)
                        goto out;
 
-               skb->sp->xvec[skb->sp->len++] = x;
-               skb->sp->olen++;
+               sp->xvec[sp->len++] = x;
+               sp->olen++;
 
                xo = xfrm_offload(skb);
                if (!xo) {
@@ -114,6 +115,7 @@ static struct sk_buff *esp4_gso_segment(struct sk_buff *skb,
        struct crypto_aead *aead;
        netdev_features_t esp_features = features;
        struct xfrm_offload *xo = xfrm_offload(skb);
+       struct sec_path *sp;
 
        if (!xo)
                return ERR_PTR(-EINVAL);
@@ -121,7 +123,8 @@ static struct sk_buff *esp4_gso_segment(struct sk_buff *skb,
        if (!(skb_shinfo(skb)->gso_type & SKB_GSO_ESP))
                return ERR_PTR(-EINVAL);
 
-       x = skb->sp->xvec[skb->sp->len - 1];
+       sp = skb_sec_path(skb);
+       x = sp->xvec[sp->len - 1];
        aead = x->data;
        esph = ip_esp_hdr(skb);
 
index 0d0ad19..0c9f171 100644 (file)
@@ -1061,6 +1061,13 @@ static int gue_err(struct sk_buff *skb, u32 info)
        if (validate_gue_flags(guehdr, optlen))
                return -EINVAL;
 
+       /* Handling exceptions for direct UDP encapsulation in GUE would lead to
+        * recursion. Besides, this kind of encapsulation can't even be
+        * configured currently. Discard this.
+        */
+       if (guehdr->proto_ctype == IPPROTO_UDP)
+               return -EOPNOTSUPP;
+
        skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
        ret = gue_err_proto_handler(guehdr->proto_ctype, skb, info);
 
index 13890d5..2445614 100644 (file)
@@ -234,24 +234,16 @@ static inline int compute_score(struct sock *sk, struct net *net,
                                const int dif, const int sdif, bool exact_dif)
 {
        int score = -1;
-       struct inet_sock *inet = inet_sk(sk);
-       bool dev_match;
 
-       if (net_eq(sock_net(sk), net) && inet->inet_num == hnum &&
+       if (net_eq(sock_net(sk), net) && sk->sk_num == hnum &&
                        !ipv6_only_sock(sk)) {
-               __be32 rcv_saddr = inet->inet_rcv_saddr;
-               score = sk->sk_family == PF_INET ? 2 : 1;
-               if (rcv_saddr) {
-                       if (rcv_saddr != daddr)
-                               return -1;
-                       score += 4;
-               }
-               dev_match = inet_sk_bound_dev_eq(net, sk->sk_bound_dev_if,
-                                                dif, sdif);
-               if (!dev_match)
+               if (sk->sk_rcv_saddr != daddr)
+                       return -1;
+
+               if (!inet_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif))
                        return -1;
-               score += 4;
 
+               score = sk->sk_family == PF_INET ? 2 : 1;
                if (sk->sk_incoming_cpu == raw_smp_processor_id())
                        score++;
        }
@@ -307,26 +299,12 @@ struct sock *__inet_lookup_listener(struct net *net,
                                    const __be32 daddr, const unsigned short hnum,
                                    const int dif, const int sdif)
 {
-       unsigned int hash = inet_lhashfn(net, hnum);
-       struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
-       bool exact_dif = inet_exact_dif_match(net, skb);
        struct inet_listen_hashbucket *ilb2;
-       struct sock *sk, *result = NULL;
-       int score, hiscore = 0;
+       struct sock *result = NULL;
        unsigned int hash2;
-       u32 phash = 0;
-
-       if (ilb->count <= 10 || !hashinfo->lhash2)
-               goto port_lookup;
-
-       /* Too many sk in the ilb bucket (which is hashed by port alone).
-        * Try lhash2 (which is hashed by port and addr) instead.
-        */
 
        hash2 = ipv4_portaddr_hash(net, daddr, hnum);
        ilb2 = inet_lhash2_bucket(hashinfo, hash2);
-       if (ilb2->count > ilb->count)
-               goto port_lookup;
 
        result = inet_lhash2_lookup(net, ilb2, skb, doff,
                                    saddr, sport, daddr, hnum,
@@ -335,34 +313,12 @@ struct sock *__inet_lookup_listener(struct net *net,
                goto done;
 
        /* Lookup lhash2 with INADDR_ANY */
-
        hash2 = ipv4_portaddr_hash(net, htonl(INADDR_ANY), hnum);
        ilb2 = inet_lhash2_bucket(hashinfo, hash2);
-       if (ilb2->count > ilb->count)
-               goto port_lookup;
 
        result = inet_lhash2_lookup(net, ilb2, skb, doff,
-                                   saddr, sport, daddr, hnum,
+                                   saddr, sport, htonl(INADDR_ANY), hnum,
                                    dif, sdif);
-       goto done;
-
-port_lookup:
-       sk_for_each_rcu(sk, &ilb->head) {
-               score = compute_score(sk, net, hnum, daddr,
-                                     dif, sdif, exact_dif);
-               if (score > hiscore) {
-                       if (sk->sk_reuseport) {
-                               phash = inet_ehashfn(net, daddr, hnum,
-                                                    saddr, sport);
-                               result = reuseport_select_sock(sk, phash,
-                                                              skb, doff);
-                               if (result)
-                                       goto done;
-                       }
-                       result = sk;
-                       hiscore = score;
-               }
-       }
 done:
        if (unlikely(IS_ERR(result)))
                return NULL;
@@ -829,6 +785,7 @@ void __init inet_hashinfo2_init(struct inet_hashinfo *h, const char *name,
                h->lhash2[i].count = 0;
        }
 }
+EXPORT_SYMBOL_GPL(inet_hashinfo2_init);
 
 int inet_ehash_locks_alloc(struct inet_hashinfo *hashinfo)
 {
index 32662e9..00ec819 100644 (file)
@@ -69,9 +69,17 @@ static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *s
        __IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS);
        __IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len);
 
+#ifdef CONFIG_NET_SWITCHDEV
+       if (skb->offload_l3_fwd_mark) {
+               consume_skb(skb);
+               return 0;
+       }
+#endif
+
        if (unlikely(opt->optlen))
                ip_forward_options(skb);
 
+       skb->tstamp = 0;
        return dst_output(net, sk, skb);
 }
 
index d6ee343..867be8f 100644 (file)
@@ -346,10 +346,10 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
        struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
        struct rb_node **rbn, *parent;
        struct sk_buff *skb1, *prev_tail;
+       int ihl, end, skb1_run_end;
        struct net_device *dev;
        unsigned int fragsize;
        int flags, offset;
-       int ihl, end;
        int err = -ENOENT;
        u8 ecn;
 
@@ -419,7 +419,9 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
         *   overlapping fragment, the entire datagram (and any constituent
         *   fragments) MUST be silently discarded.
         *
-        * We do the same here for IPv4 (and increment an snmp counter).
+        * We do the same here for IPv4 (and increment an snmp counter) but
+        * we do not want to drop the whole queue in response to a duplicate
+        * fragment.
         */
 
        err = -EINVAL;
@@ -444,13 +446,17 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
                do {
                        parent = *rbn;
                        skb1 = rb_to_skb(parent);
+                       skb1_run_end = skb1->ip_defrag_offset +
+                                      FRAG_CB(skb1)->frag_run_len;
                        if (end <= skb1->ip_defrag_offset)
                                rbn = &parent->rb_left;
-                       else if (offset >= skb1->ip_defrag_offset +
-                                               FRAG_CB(skb1)->frag_run_len)
+                       else if (offset >= skb1_run_end)
                                rbn = &parent->rb_right;
-                       else /* Found an overlap with skb1. */
-                               goto overlap;
+                       else if (offset >= skb1->ip_defrag_offset &&
+                                end <= skb1_run_end)
+                               goto err; /* No new data, potential duplicate */
+                       else
+                               goto overlap; /* Found an overlap */
                } while (*rbn);
                /* Here we have parent properly set, and rbn pointing to
                 * one of its NULL left/right children. Insert skb.
@@ -515,6 +521,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
        struct rb_node *rbn;
        int len;
        int ihlen;
+       int delta;
        int err;
        u8 ecn;
 
@@ -556,10 +563,16 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
        if (len > 65535)
                goto out_oversize;
 
+       delta = - head->truesize;
+
        /* Head of list must not be cloned. */
        if (skb_unclone(head, GFP_ATOMIC))
                goto out_nomem;
 
+       delta += head->truesize;
+       if (delta)
+               add_frag_mem_limit(qp->q.net, delta);
+
        /* If the first fragment is fragmented itself, we split
         * it to two chunks: the first with data and paged part
         * and the second, holding only fragments. */
index 76a9a5f..c7a7bd5 100644 (file)
@@ -1341,12 +1341,6 @@ static void ipgre_tap_setup(struct net_device *dev)
        ip_tunnel_setup(dev, gre_tap_net_id);
 }
 
-bool is_gretap_dev(const struct net_device *dev)
-{
-       return dev->netdev_ops == &gre_tap_netdev_ops;
-}
-EXPORT_SYMBOL_GPL(is_gretap_dev);
-
 static int ipgre_newlink(struct net *src_net, struct net_device *dev,
                         struct nlattr *tb[], struct nlattr *data[],
                         struct netlink_ext_ack *extack)
index 72250b4..26921f6 100644 (file)
@@ -546,7 +546,7 @@ static void ip_list_rcv_finish(struct net *net, struct sock *sk,
        list_for_each_entry_safe(skb, next, head, list) {
                struct dst_entry *dst;
 
-               list_del(&skb->list);
+               skb_list_del_init(skb);
                /* if ingress device is enslaved to an L3 master device pass the
                 * skb to its handler for processing
                 */
@@ -593,7 +593,7 @@ void ip_list_rcv(struct list_head *head, struct packet_type *pt,
                struct net_device *dev = skb->dev;
                struct net *net = dev_net(dev);
 
-               list_del(&skb->list);
+               skb_list_del_init(skb);
                skb = ip_rcv_core(skb, net);
                if (skb == NULL)
                        continue;
index c09219e..c801888 100644 (file)
@@ -533,6 +533,7 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        to->tc_index = from->tc_index;
 #endif
        nf_copy(to, from);
+       skb_ext_copy(to, from);
 #if IS_ENABLED(CONFIG_IP_VS)
        to->ipvs_property = from->ipvs_property;
 #endif
@@ -867,6 +868,7 @@ static int __ip_append_data(struct sock *sk,
                            unsigned int flags)
 {
        struct inet_sock *inet = inet_sk(sk);
+       struct ubuf_info *uarg = NULL;
        struct sk_buff *skb;
 
        struct ip_options *opt = cork->opt;
@@ -880,8 +882,8 @@ static int __ip_append_data(struct sock *sk,
        int csummode = CHECKSUM_NONE;
        struct rtable *rt = (struct rtable *)cork->dst;
        unsigned int wmem_alloc_delta = 0;
+       bool paged, extra_uref;
        u32 tskey = 0;
-       bool paged;
 
        skb = skb_peek_tail(queue);
 
@@ -916,6 +918,20 @@ static int __ip_append_data(struct sock *sk,
            (!exthdrlen || (rt->dst.dev->features & NETIF_F_HW_ESP_TX_CSUM)))
                csummode = CHECKSUM_PARTIAL;
 
+       if (flags & MSG_ZEROCOPY && length && sock_flag(sk, SOCK_ZEROCOPY)) {
+               uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb));
+               if (!uarg)
+                       return -ENOBUFS;
+               extra_uref = true;
+               if (rt->dst.dev->features & NETIF_F_SG &&
+                   csummode == CHECKSUM_PARTIAL) {
+                       paged = true;
+               } else {
+                       uarg->zerocopy = 0;
+                       skb_zcopy_set(skb, uarg, &extra_uref);
+               }
+       }
+
        cork->length += length;
 
        /* So, what's going on in the loop below?
@@ -939,7 +955,7 @@ static int __ip_append_data(struct sock *sk,
                        unsigned int fraglen;
                        unsigned int fraggap;
                        unsigned int alloclen;
-                       unsigned int pagedlen = 0;
+                       unsigned int pagedlen;
                        struct sk_buff *skb_prev;
 alloc_new_skb:
                        skb_prev = skb;
@@ -956,6 +972,7 @@ alloc_new_skb:
                        if (datalen > mtu - fragheaderlen)
                                datalen = maxfraglen - fragheaderlen;
                        fraglen = datalen + fragheaderlen;
+                       pagedlen = 0;
 
                        if ((flags & MSG_MORE) &&
                            !(rt->dst.dev->features&NETIF_F_SG))
@@ -1000,12 +1017,6 @@ alloc_new_skb:
                        skb->csum = 0;
                        skb_reserve(skb, hh_len);
 
-                       /* only the initial fragment is time stamped */
-                       skb_shinfo(skb)->tx_flags = cork->tx_flags;
-                       cork->tx_flags = 0;
-                       skb_shinfo(skb)->tskey = tskey;
-                       tskey = 0;
-
                        /*
                         *      Find where to start putting bytes.
                         */
@@ -1038,6 +1049,13 @@ alloc_new_skb:
                        exthdrlen = 0;
                        csummode = CHECKSUM_NONE;
 
+                       /* only the initial fragment is time stamped */
+                       skb_shinfo(skb)->tx_flags = cork->tx_flags;
+                       cork->tx_flags = 0;
+                       skb_shinfo(skb)->tskey = tskey;
+                       tskey = 0;
+                       skb_zcopy_set(skb, uarg, &extra_uref);
+
                        if ((flags & MSG_CONFIRM) && !skb_prev)
                                skb_set_dst_pending_confirm(skb, 1);
 
@@ -1067,7 +1085,7 @@ alloc_new_skb:
                                err = -EFAULT;
                                goto error;
                        }
-               } else {
+               } else if (!uarg || !uarg->zerocopy) {
                        int i = skb_shinfo(skb)->nr_frags;
 
                        err = -ENOMEM;
@@ -1097,6 +1115,10 @@ alloc_new_skb:
                        skb->data_len += copy;
                        skb->truesize += copy;
                        wmem_alloc_delta += copy;
+               } else {
+                       err = skb_zerocopy_iter_dgram(skb, from, copy);
+                       if (err < 0)
+                               goto error;
                }
                offset += copy;
                length -= copy;
@@ -1109,6 +1131,8 @@ alloc_new_skb:
 error_efault:
        err = -EFAULT;
 error:
+       if (uarg)
+               sock_zerocopy_put_abort(uarg, extra_uref);
        cork->length -= length;
        IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS);
        refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc);
index f45b96d..c857ec6 100644 (file)
@@ -80,7 +80,7 @@ void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
 
        iph->version    =       4;
        iph->ihl        =       sizeof(struct iphdr) >> 2;
-       iph->frag_off   =       df;
+       iph->frag_off   =       ip_mtu_locked(&rt->dst) ? 0 : df;
        iph->protocol   =       proto;
        iph->tos        =       tos;
        iph->daddr      =       dst;
index 8821261..b9a9873 100644 (file)
@@ -220,7 +220,7 @@ static int __init ic_open_devs(void)
        for_each_netdev(&init_net, dev) {
                if (!(dev->flags & IFF_LOOPBACK) && !netdev_uses_dsa(dev))
                        continue;
-               if (dev_change_flags(dev, dev->flags | IFF_UP) < 0)
+               if (dev_change_flags(dev, dev->flags | IFF_UP, NULL) < 0)
                        pr_err("IP-Config: Failed to open %s\n", dev->name);
        }
 
@@ -238,7 +238,7 @@ static int __init ic_open_devs(void)
                        if (ic_proto_enabled && !able)
                                continue;
                        oflags = dev->flags;
-                       if (dev_change_flags(dev, oflags | IFF_UP) < 0) {
+                       if (dev_change_flags(dev, oflags | IFF_UP, NULL) < 0) {
                                pr_err("IP-Config: Failed to open %s\n",
                                       dev->name);
                                continue;
@@ -315,7 +315,7 @@ static void __init ic_close_devs(void)
                dev = d->dev;
                if (d != ic_dev && !netdev_uses_dsa(dev)) {
                        pr_debug("IP-Config: Downing %s\n", dev->name);
-                       dev_change_flags(dev, d->flags);
+                       dev_change_flags(dev, d->flags, NULL);
                }
                kfree(d);
        }
@@ -429,6 +429,8 @@ static int __init ic_defaults(void)
                        ic_netmask = htonl(IN_CLASSB_NET);
                else if (IN_CLASSC(ntohl(ic_myaddr)))
                        ic_netmask = htonl(IN_CLASSC_NET);
+               else if (IN_CLASSE(ntohl(ic_myaddr)))
+                       ic_netmask = htonl(IN_CLASSE_NET);
                else {
                        pr_err("IP-Config: Unable to guess netmask for address %pI4\n",
                               &ic_myaddr);
@@ -1361,18 +1363,7 @@ static int ntp_servers_seq_show(struct seq_file *seq, void *v)
        }
        return 0;
 }
-
-static int ntp_servers_seq_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ntp_servers_seq_show, NULL);
-}
-
-static const struct file_operations ntp_servers_seq_fops = {
-       .open           = ntp_servers_seq_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(ntp_servers_seq);
 #endif /* CONFIG_PROC_FS */
 
 /*
index a6defbe..ddbf8c9 100644 (file)
@@ -69,6 +69,8 @@
 #include <net/nexthop.h>
 #include <net/switchdev.h>
 
+#include <linux/nospec.h>
+
 struct ipmr_rule {
        struct fib_rule         common;
 };
@@ -506,7 +508,7 @@ static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
                        dev->flags |= IFF_MULTICAST;
                        if (!ipmr_init_vif_indev(dev))
                                goto failure;
-                       if (dev_open(dev))
+                       if (dev_open(dev, NULL))
                                goto failure;
                        dev_hold(dev);
                }
@@ -589,7 +591,7 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
 
        if (!ipmr_init_vif_indev(dev))
                goto failure;
-       if (dev_open(dev))
+       if (dev_open(dev, NULL))
                goto failure;
 
        dev_hold(dev);
@@ -1612,6 +1614,7 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
                        return -EFAULT;
                if (vr.vifi >= mrt->maxvif)
                        return -EINVAL;
+               vr.vifi = array_index_nospec(vr.vifi, mrt->maxvif);
                read_lock(&mrt_lock);
                vif = &mrt->vif_table[vr.vifi];
                if (VIF_EXISTS(mrt, vr.vifi)) {
@@ -1686,6 +1689,7 @@ int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
                        return -EFAULT;
                if (vr.vifi >= mrt->maxvif)
                        return -EINVAL;
+               vr.vifi = array_index_nospec(vr.vifi, mrt->maxvif);
                read_lock(&mrt_lock);
                vif = &mrt->vif_table[vr.vifi];
                if (VIF_EXISTS(mrt, vr.vifi)) {
@@ -1802,7 +1806,7 @@ static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt,
        struct vif_device *out_vif = &mrt->vif_table[out_vifi];
        struct vif_device *in_vif = &mrt->vif_table[in_vifi];
 
-       if (!skb->offload_mr_fwd_mark)
+       if (!skb->offload_l3_fwd_mark)
                return false;
        if (!out_vif->dev_parent_id.id_len || !in_vif->dev_parent_id.id_len)
                return false;
@@ -1820,8 +1824,7 @@ static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt,
 /* Processing handlers for ipmr_forward */
 
 static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
-                           int in_vifi, struct sk_buff *skb,
-                           struct mfc_cache *c, int vifi)
+                           int in_vifi, struct sk_buff *skb, int vifi)
 {
        const struct iphdr *iph = ip_hdr(skb);
        struct vif_device *vif = &mrt->vif_table[vifi];
@@ -2027,7 +2030,7 @@ forward:
 
                                if (skb2)
                                        ipmr_queue_xmit(net, mrt, true_vifi,
-                                                       skb2, c, psend);
+                                                       skb2, psend);
                        }
                        psend = ct;
                }
@@ -2039,9 +2042,9 @@ last_forward:
 
                        if (skb2)
                                ipmr_queue_xmit(net, mrt, true_vifi, skb2,
-                                               c, psend);
+                                               psend);
                } else {
-                       ipmr_queue_xmit(net, mrt, true_vifi, skb, c, psend);
+                       ipmr_queue_xmit(net, mrt, true_vifi, skb, psend);
                        return;
                }
        }
index ce1512b..fd3f9e8 100644 (file)
@@ -81,9 +81,12 @@ static int __init masquerade_tg_init(void)
        int ret;
 
        ret = xt_register_target(&masquerade_tg_reg);
+       if (ret)
+               return ret;
 
-       if (ret == 0)
-               nf_nat_masquerade_ipv4_register_notifier();
+       ret = nf_nat_masquerade_ipv4_register_notifier();
+       if (ret)
+               xt_unregister_target(&masquerade_tg_reg);
 
        return ret;
 }
index a9d5e01..41327bb 100644 (file)
@@ -147,28 +147,50 @@ static struct notifier_block masq_inet_notifier = {
        .notifier_call  = masq_inet_event,
 };
 
-static atomic_t masquerade_notifier_refcount = ATOMIC_INIT(0);
+static int masq_refcnt;
+static DEFINE_MUTEX(masq_mutex);
 
-void nf_nat_masquerade_ipv4_register_notifier(void)
+int nf_nat_masquerade_ipv4_register_notifier(void)
 {
+       int ret = 0;
+
+       mutex_lock(&masq_mutex);
        /* check if the notifier was already set */
-       if (atomic_inc_return(&masquerade_notifier_refcount) > 1)
-               return;
+       if (++masq_refcnt > 1)
+               goto out_unlock;
 
        /* Register for device down reports */
-       register_netdevice_notifier(&masq_dev_notifier);
+       ret = register_netdevice_notifier(&masq_dev_notifier);
+       if (ret)
+               goto err_dec;
        /* Register IP address change reports */
-       register_inetaddr_notifier(&masq_inet_notifier);
+       ret = register_inetaddr_notifier(&masq_inet_notifier);
+       if (ret)
+               goto err_unregister;
+
+       mutex_unlock(&masq_mutex);
+       return ret;
+
+err_unregister:
+       unregister_netdevice_notifier(&masq_dev_notifier);
+err_dec:
+       masq_refcnt--;
+out_unlock:
+       mutex_unlock(&masq_mutex);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_register_notifier);
 
 void nf_nat_masquerade_ipv4_unregister_notifier(void)
 {
+       mutex_lock(&masq_mutex);
        /* check if the notifier still has clients */
-       if (atomic_dec_return(&masquerade_notifier_refcount) > 0)
-               return;
+       if (--masq_refcnt > 0)
+               goto out_unlock;
 
        unregister_netdevice_notifier(&masq_dev_notifier);
        unregister_inetaddr_notifier(&masq_inet_notifier);
+out_unlock:
+       mutex_unlock(&masq_mutex);
 }
 EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_unregister_notifier);
index 5cd06ba..aa8304c 100644 (file)
@@ -102,6 +102,7 @@ EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put);
 /* Send RST reply */
 void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook)
 {
+       struct net_device *br_indev __maybe_unused;
        struct sk_buff *nskb;
        struct iphdr *niph;
        const struct tcphdr *oth;
@@ -147,10 +148,11 @@ void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook)
         * build the eth header using the original destination's MAC as the
         * source, and send the RST packet directly.
         */
-       if (oldskb->nf_bridge) {
+       br_indev = nf_bridge_get_physindev(oldskb);
+       if (br_indev) {
                struct ethhdr *oeth = eth_hdr(oldskb);
 
-               nskb->dev = nf_bridge_get_physindev(oldskb);
+               nskb->dev = br_indev;
                niph->tot_len = htons(nskb->len);
                ip_send_check(niph);
                if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
index f1193e1..6847de1 100644 (file)
@@ -69,7 +69,9 @@ static int __init nft_masq_ipv4_module_init(void)
        if (ret < 0)
                return ret;
 
-       nf_nat_masquerade_ipv4_register_notifier();
+       ret = nf_nat_masquerade_ipv4_register_notifier();
+       if (ret)
+               nft_unregister_expr(&nft_masq_ipv4_type);
 
        return ret;
 }
index 7028968..c3610b3 100644 (file)
@@ -219,6 +219,7 @@ static const struct snmp_mib snmp4_net_list[] = {
        SNMP_MIB_ITEM("TCPRenoRecoveryFail", LINUX_MIB_TCPRENORECOVERYFAIL),
        SNMP_MIB_ITEM("TCPSackRecoveryFail", LINUX_MIB_TCPSACKRECOVERYFAIL),
        SNMP_MIB_ITEM("TCPRcvCollapsed", LINUX_MIB_TCPRCVCOLLAPSED),
+       SNMP_MIB_ITEM("TCPBacklogCoalesce", LINUX_MIB_TCPBACKLOGCOALESCE),
        SNMP_MIB_ITEM("TCPDSACKOldSent", LINUX_MIB_TCPDSACKOLDSENT),
        SNMP_MIB_ITEM("TCPDSACKOfoSent", LINUX_MIB_TCPDSACKOFOSENT),
        SNMP_MIB_ITEM("TCPDSACKRecv", LINUX_MIB_TCPDSACKRECV),
index fb1f020..c55a543 100644 (file)
@@ -390,7 +390,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
 
        skb->ip_summed = CHECKSUM_NONE;
 
-       sock_tx_timestamp(sk, sockc->tsflags, &skb_shinfo(skb)->tx_flags);
+       skb_setup_tx_timestamp(skb, sockc->tsflags);
 
        if (flags & MSG_CONFIRM)
                skb_set_dst_pending_confirm(skb, 1);
@@ -1132,6 +1132,7 @@ void __init raw_proc_exit(void)
 {
        unregister_pernet_subsys(&raw_net_ops);
 }
+#endif /* CONFIG_PROC_FS */
 
 static void raw_sysctl_init_net(struct net *net)
 {
@@ -1156,4 +1157,3 @@ void __init raw_init(void)
        if (register_pernet_subsys(&raw_sysctl_ops))
                panic("RAW: failed to init sysctl parameters.\n");
 }
-#endif /* CONFIG_PROC_FS */
index c0a9d26..ce92f73 100644 (file)
@@ -1677,7 +1677,7 @@ static void ip_handle_martian_source(struct net_device *dev,
                        print_hex_dump(KERN_WARNING, "ll header: ",
                                       DUMP_PREFIX_OFFSET, 16, 1,
                                       skb_mac_header(skb),
-                                      dev->hard_header_len, true);
+                                      dev->hard_header_len, false);
                }
        }
 #endif
@@ -2849,6 +2849,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
                        err = -rt->dst.error;
        } else {
                fl4.flowi4_iif = LOOPBACK_IFINDEX;
+               skb->dev = net->loopback_dev;
                rt = ip_route_output_key_hash_rcu(net, &fl4, &res, skb);
                err = 0;
                if (IS_ERR(rt))
index 9e6bc4d..27e2f68 100644 (file)
@@ -1423,7 +1423,7 @@ do_error:
        if (copied + copied_syn)
                goto out;
 out_err:
-       sock_zerocopy_put_abort(uarg);
+       sock_zerocopy_put_abort(uarg, true);
        err = sk_stream_error(sk, flags, err);
        /* make sure we wake any epoll edge trigger waiter */
        if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 &&
@@ -2088,7 +2088,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
                }
                continue;
 
-       found_ok_skb:
+found_ok_skb:
                /* Ok so how much can we use? */
                used = skb->len - offset;
                if (len < used)
@@ -2147,7 +2147,7 @@ skip_copy:
                        sk_eat_skb(sk, skb);
                continue;
 
-       found_fin_ok:
+found_fin_ok:
                /* Process the FIN. */
                ++*seq;
                if (!(flags & MSG_PEEK))
@@ -2241,10 +2241,6 @@ void tcp_set_state(struct sock *sk, int state)
         * socket sitting in hash tables.
         */
        inet_sk_state_store(sk, state);
-
-#ifdef STATE_TRACE
-       SOCK_DEBUG(sk, "TCP sk=%p, State %s -> %s\n", sk, statename[oldstate], statename[state]);
-#endif
 }
 EXPORT_SYMBOL_GPL(tcp_set_state);
 
@@ -3246,6 +3242,7 @@ static size_t tcp_opt_stats_get_size(void)
                nla_total_size_64bit(sizeof(u64)) + /* TCP_NLA_BYTES_RETRANS */
                nla_total_size(sizeof(u32)) + /* TCP_NLA_DSACK_DUPS */
                nla_total_size(sizeof(u32)) + /* TCP_NLA_REORD_SEEN */
+               nla_total_size(sizeof(u32)) + /* TCP_NLA_SRTT */
                0;
 }
 
@@ -3299,6 +3296,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk)
                          TCP_NLA_PAD);
        nla_put_u32(stats, TCP_NLA_DSACK_DUPS, tp->dsack_dups);
        nla_put_u32(stats, TCP_NLA_REORD_SEEN, tp->reord_seen);
+       nla_put_u32(stats, TCP_NLA_SRTT, tp->srtt_us >> 3);
 
        return stats;
 }
@@ -3658,8 +3656,11 @@ bool tcp_alloc_md5sig_pool(void)
        if (unlikely(!tcp_md5sig_pool_populated)) {
                mutex_lock(&tcp_md5sig_mutex);
 
-               if (!tcp_md5sig_pool_populated)
+               if (!tcp_md5sig_pool_populated) {
                        __tcp_alloc_md5sig_pool();
+                       if (tcp_md5sig_pool_populated)
+                               static_key_slow_inc(&tcp_md5_needed);
+               }
 
                mutex_unlock(&tcp_md5sig_mutex);
        }
index 3b45fe5..1bb7321 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/wait.h>
 
 #include <net/inet_common.h>
+#include <net/tls.h>
 
 static bool tcp_bpf_stream_read(const struct sock *sk)
 {
@@ -198,7 +199,7 @@ static int bpf_tcp_ingress(struct sock *sk, struct sk_psock *psock,
                msg->sg.start = i;
                msg->sg.size -= apply_bytes;
                sk_psock_queue_msg(psock, tmp);
-               sk->sk_data_ready(sk);
+               sk_psock_data_ready(sk, psock);
        } else {
                sk_msg_free(sk, tmp);
                kfree(tmp);
@@ -218,6 +219,8 @@ static int tcp_bpf_push(struct sock *sk, struct sk_msg *msg, u32 apply_bytes,
        u32 off;
 
        while (1) {
+               bool has_tx_ulp;
+
                sge = sk_msg_elem(msg, msg->sg.start);
                size = (apply && apply_bytes < sge->length) ?
                        apply_bytes : sge->length;
@@ -226,7 +229,15 @@ static int tcp_bpf_push(struct sock *sk, struct sk_msg *msg, u32 apply_bytes,
 
                tcp_rate_check_app_limited(sk);
 retry:
-               ret = do_tcp_sendpages(sk, page, off, size, flags);
+               has_tx_ulp = tls_sw_has_ctx_tx(sk);
+               if (has_tx_ulp) {
+                       flags |= MSG_SENDPAGE_NOPOLICY;
+                       ret = kernel_sendpage_locked(sk,
+                                                    page, off, size, flags);
+               } else {
+                       ret = do_tcp_sendpages(sk, page, off, size, flags);
+               }
+
                if (ret <= 0)
                        return ret;
                if (apply)
@@ -289,12 +300,23 @@ static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock,
 {
        bool cork = false, enospc = msg->sg.start == msg->sg.end;
        struct sock *sk_redir;
-       u32 tosend;
+       u32 tosend, delta = 0;
        int ret;
 
 more_data:
-       if (psock->eval == __SK_NONE)
+       if (psock->eval == __SK_NONE) {
+               /* Track delta in msg size to add/subtract it on SK_DROP from
+                * returned to user copied size. This ensures user doesn't
+                * get a positive return code with msg_cut_data and SK_DROP
+                * verdict.
+                */
+               delta = msg->sg.size;
                psock->eval = sk_psock_msg_verdict(sk, psock, msg);
+               if (msg->sg.size < delta)
+                       delta -= msg->sg.size;
+               else
+                       delta = 0;
+       }
 
        if (msg->cork_bytes &&
            msg->cork_bytes > msg->sg.size && !enospc) {
@@ -350,7 +372,7 @@ more_data:
        default:
                sk_msg_free_partial(sk, msg, tosend);
                sk_msg_apply_bytes(psock, tosend);
-               *copied -= tosend;
+               *copied -= (tosend + delta);
                return -EACCES;
        }
 
index edaaebf..76858b1 100644 (file)
@@ -579,10 +579,12 @@ static inline void tcp_rcv_rtt_measure_ts(struct sock *sk,
                u32 delta = tcp_time_stamp(tp) - tp->rx_opt.rcv_tsecr;
                u32 delta_us;
 
-               if (!delta)
-                       delta = 1;
-               delta_us = delta * (USEC_PER_SEC / TCP_TS_HZ);
-               tcp_rcv_rtt_update(tp, delta_us, 0);
+               if (likely(delta < INT_MAX / (USEC_PER_SEC / TCP_TS_HZ))) {
+                       if (!delta)
+                               delta = 1;
+                       delta_us = delta * (USEC_PER_SEC / TCP_TS_HZ);
+                       tcp_rcv_rtt_update(tp, delta_us, 0);
+               }
        }
 }
 
@@ -1863,16 +1865,20 @@ static void tcp_check_reno_reordering(struct sock *sk, const int addend)
 
 /* Emulate SACKs for SACKless connection: account for a new dupack. */
 
-static void tcp_add_reno_sack(struct sock *sk)
+static void tcp_add_reno_sack(struct sock *sk, int num_dupack)
 {
-       struct tcp_sock *tp = tcp_sk(sk);
-       u32 prior_sacked = tp->sacked_out;
+       if (num_dupack) {
+               struct tcp_sock *tp = tcp_sk(sk);
+               u32 prior_sacked = tp->sacked_out;
+               s32 delivered;
 
-       tp->sacked_out++;
-       tcp_check_reno_reordering(sk, 0);
-       if (tp->sacked_out > prior_sacked)
-               tp->delivered++; /* Some out-of-order packet is delivered */
-       tcp_verify_left_out(tp);
+               tp->sacked_out += num_dupack;
+               tcp_check_reno_reordering(sk, 0);
+               delivered = tp->sacked_out - prior_sacked;
+               if (delivered > 0)
+                       tp->delivered += delivered;
+               tcp_verify_left_out(tp);
+       }
 }
 
 /* Account for ACK, ACKing some data in Reno Recovery phase. */
@@ -2634,7 +2640,7 @@ void tcp_enter_recovery(struct sock *sk, bool ece_ack)
 /* Process an ACK in CA_Loss state. Move to CA_Open if lost data are
  * recovered or spurious. Otherwise retransmits more on partial ACKs.
  */
-static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack,
+static void tcp_process_loss(struct sock *sk, int flag, int num_dupack,
                             int *rexmit)
 {
        struct tcp_sock *tp = tcp_sk(sk);
@@ -2653,7 +2659,7 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack,
                        return;
 
                if (after(tp->snd_nxt, tp->high_seq)) {
-                       if (flag & FLAG_DATA_SACKED || is_dupack)
+                       if (flag & FLAG_DATA_SACKED || num_dupack)
                                tp->frto = 0; /* Step 3.a. loss was real */
                } else if (flag & FLAG_SND_UNA_ADVANCED && !recovered) {
                        tp->high_seq = tp->snd_nxt;
@@ -2679,8 +2685,8 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack,
                /* A Reno DUPACK means new data in F-RTO step 2.b above are
                 * delivered. Lower inflight to clock out (re)tranmissions.
                 */
-               if (after(tp->snd_nxt, tp->high_seq) && is_dupack)
-                       tcp_add_reno_sack(sk);
+               if (after(tp->snd_nxt, tp->high_seq) && num_dupack)
+                       tcp_add_reno_sack(sk, num_dupack);
                else if (flag & FLAG_SND_UNA_ADVANCED)
                        tcp_reset_reno_sack(tp);
        }
@@ -2757,13 +2763,13 @@ static bool tcp_force_fast_retransmit(struct sock *sk)
  * tcp_xmit_retransmit_queue().
  */
 static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
-                                 bool is_dupack, int *ack_flag, int *rexmit)
+                                 int num_dupack, int *ack_flag, int *rexmit)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        int fast_rexmit = 0, flag = *ack_flag;
-       bool do_lost = is_dupack || ((flag & FLAG_DATA_SACKED) &&
-                                    tcp_force_fast_retransmit(sk));
+       bool do_lost = num_dupack || ((flag & FLAG_DATA_SACKED) &&
+                                     tcp_force_fast_retransmit(sk));
 
        if (!tp->packets_out && tp->sacked_out)
                tp->sacked_out = 0;
@@ -2810,8 +2816,8 @@ static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
        switch (icsk->icsk_ca_state) {
        case TCP_CA_Recovery:
                if (!(flag & FLAG_SND_UNA_ADVANCED)) {
-                       if (tcp_is_reno(tp) && is_dupack)
-                               tcp_add_reno_sack(sk);
+                       if (tcp_is_reno(tp))
+                               tcp_add_reno_sack(sk, num_dupack);
                } else {
                        if (tcp_try_undo_partial(sk, prior_snd_una))
                                return;
@@ -2826,7 +2832,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
                tcp_identify_packet_loss(sk, ack_flag);
                break;
        case TCP_CA_Loss:
-               tcp_process_loss(sk, flag, is_dupack, rexmit);
+               tcp_process_loss(sk, flag, num_dupack, rexmit);
                tcp_identify_packet_loss(sk, ack_flag);
                if (!(icsk->icsk_ca_state == TCP_CA_Open ||
                      (*ack_flag & FLAG_LOST_RETRANS)))
@@ -2837,8 +2843,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
                if (tcp_is_reno(tp)) {
                        if (flag & FLAG_SND_UNA_ADVANCED)
                                tcp_reset_reno_sack(tp);
-                       if (is_dupack)
-                               tcp_add_reno_sack(sk);
+                       tcp_add_reno_sack(sk, num_dupack);
                }
 
                if (icsk->icsk_ca_state <= TCP_CA_Disorder)
@@ -2910,9 +2915,11 @@ static bool tcp_ack_update_rtt(struct sock *sk, const int flag,
        if (seq_rtt_us < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
            flag & FLAG_ACKED) {
                u32 delta = tcp_time_stamp(tp) - tp->rx_opt.rcv_tsecr;
-               u32 delta_us = delta * (USEC_PER_SEC / TCP_TS_HZ);
 
-               seq_rtt_us = ca_rtt_us = delta_us;
+               if (likely(delta < INT_MAX / (USEC_PER_SEC / TCP_TS_HZ))) {
+                       seq_rtt_us = delta * (USEC_PER_SEC / TCP_TS_HZ);
+                       ca_rtt_us = seq_rtt_us;
+               }
        }
        rs->rtt_us = ca_rtt_us; /* RTT of last (S)ACKed packet (or -1) */
        if (seq_rtt_us < 0)
@@ -3558,7 +3565,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
        bool is_sack_reneg = tp->is_sack_reneg;
        u32 ack_seq = TCP_SKB_CB(skb)->seq;
        u32 ack = TCP_SKB_CB(skb)->ack_seq;
-       bool is_dupack = false;
+       int num_dupack = 0;
        int prior_packets = tp->packets_out;
        u32 delivered = tp->delivered;
        u32 lost = tp->lost;
@@ -3669,8 +3676,13 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
                tcp_set_xmit_timer(sk);
 
        if (tcp_ack_is_dubious(sk, flag)) {
-               is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
-               tcp_fastretrans_alert(sk, prior_snd_una, is_dupack, &flag,
+               if (!(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP))) {
+                       num_dupack = 1;
+                       /* Consider if pure acks were aggregated in tcp_add_backlog() */
+                       if (!(flag & FLAG_DATA))
+                               num_dupack = max_t(u16, 1, skb_shinfo(skb)->gso_segs);
+               }
+               tcp_fastretrans_alert(sk, prior_snd_una, num_dupack, &flag,
                                      &rexmit);
        }
 
@@ -3688,7 +3700,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
 no_queue:
        /* If data was DSACKed, see if we can undo a cwnd reduction. */
        if (flag & FLAG_DSACKING_ACK) {
-               tcp_fastretrans_alert(sk, prior_snd_una, is_dupack, &flag,
+               tcp_fastretrans_alert(sk, prior_snd_una, num_dupack, &flag,
                                      &rexmit);
                tcp_newly_delivered(sk, delivered, flag);
        }
@@ -3713,7 +3725,7 @@ old_ack:
        if (TCP_SKB_CB(skb)->sacked) {
                flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una,
                                                &sack_state);
-               tcp_fastretrans_alert(sk, prior_snd_una, is_dupack, &flag,
+               tcp_fastretrans_alert(sk, prior_snd_una, num_dupack, &flag,
                                      &rexmit);
                tcp_newly_delivered(sk, delivered, flag);
                tcp_xmit_recovery(sk, rexmit);
@@ -4269,7 +4281,7 @@ static void tcp_sack_new_ofo_skb(struct sock *sk, u32 seq, u32 end_seq)
         * If the sack array is full, forget about the last one.
         */
        if (this_sack >= TCP_NUM_SACKS) {
-               if (tp->compressed_ack)
+               if (tp->compressed_ack > TCP_FASTRETRANS_THRESH)
                        tcp_send_ack(sk);
                this_sack--;
                tp->rx_opt.num_sacks--;
@@ -4364,6 +4376,7 @@ static bool tcp_try_coalesce(struct sock *sk,
        if (TCP_SKB_CB(from)->has_rxtstamp) {
                TCP_SKB_CB(to)->has_rxtstamp = true;
                to->tstamp = from->tstamp;
+               skb_hwtstamps(to)->hwtstamp = skb_hwtstamps(from)->hwtstamp;
        }
 
        return true;
@@ -4602,13 +4615,12 @@ end:
        }
 }
 
-static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen,
-                 bool *fragstolen)
+static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb,
+                                     bool *fragstolen)
 {
        int eaten;
        struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue);
 
-       __skb_pull(skb, hdrlen);
        eaten = (tail &&
                 tcp_try_coalesce(sk, tail,
                                  skb, fragstolen)) ? 1 : 0;
@@ -4659,7 +4671,7 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
        TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + size;
        TCP_SKB_CB(skb)->ack_seq = tcp_sk(sk)->snd_una - 1;
 
-       if (tcp_queue_rcv(sk, skb, 0, &fragstolen)) {
+       if (tcp_queue_rcv(sk, skb, &fragstolen)) {
                WARN_ON_ONCE(fragstolen); /* should not happen */
                __kfree_skb(skb);
        }
@@ -4719,7 +4731,7 @@ queue_and_out:
                        goto drop;
                }
 
-               eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen);
+               eaten = tcp_queue_rcv(sk, skb, &fragstolen);
                if (skb->len)
                        tcp_event_data_recv(sk, skb);
                if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
@@ -5189,7 +5201,17 @@ send_now:
        if (!tcp_is_sack(tp) ||
            tp->compressed_ack >= sock_net(sk)->ipv4.sysctl_tcp_comp_sack_nr)
                goto send_now;
-       tp->compressed_ack++;
+
+       if (tp->compressed_ack_rcv_nxt != tp->rcv_nxt) {
+               tp->compressed_ack_rcv_nxt = tp->rcv_nxt;
+               if (tp->compressed_ack > TCP_FASTRETRANS_THRESH)
+                       NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPACKCOMPRESSED,
+                                     tp->compressed_ack - TCP_FASTRETRANS_THRESH);
+               tp->compressed_ack = 0;
+       }
+
+       if (++tp->compressed_ack <= TCP_FASTRETRANS_THRESH)
+               goto send_now;
 
        if (hrtimer_is_queued(&tp->compressed_ack_timer))
                return;
@@ -5585,8 +5607,8 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb)
                        NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHPHITS);
 
                        /* Bulk data transfer: receiver */
-                       eaten = tcp_queue_rcv(sk, skb, tcp_header_len,
-                                             &fragstolen);
+                       __skb_pull(skb, tcp_header_len);
+                       eaten = tcp_queue_rcv(sk, skb, &fragstolen);
 
                        tcp_event_data_recv(sk, skb);
 
index 0952d4b..efc6fef 100644 (file)
@@ -970,10 +970,13 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req)
  * We need to maintain these in the sk structure.
  */
 
+struct static_key tcp_md5_needed __read_mostly;
+EXPORT_SYMBOL(tcp_md5_needed);
+
 /* Find the Key structure for an address.  */
-struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
-                                        const union tcp_md5_addr *addr,
-                                        int family)
+struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk,
+                                          const union tcp_md5_addr *addr,
+                                          int family)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_md5sig_key *key;
@@ -1011,7 +1014,7 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
        }
        return best_match;
 }
-EXPORT_SYMBOL(tcp_md5_do_lookup);
+EXPORT_SYMBOL(__tcp_md5_do_lookup);
 
 static struct tcp_md5sig_key *tcp_md5_do_lookup_exact(const struct sock *sk,
                                                      const union tcp_md5_addr *addr,
@@ -1619,12 +1622,14 @@ int tcp_v4_early_demux(struct sk_buff *skb)
 bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
 {
        u32 limit = sk->sk_rcvbuf + sk->sk_sndbuf;
-
-       /* Only socket owner can try to collapse/prune rx queues
-        * to reduce memory overhead, so add a little headroom here.
-        * Few sockets backlog are possibly concurrently non empty.
-        */
-       limit += 64*1024;
+       struct skb_shared_info *shinfo;
+       const struct tcphdr *th;
+       struct tcphdr *thtail;
+       struct sk_buff *tail;
+       unsigned int hdrlen;
+       bool fragstolen;
+       u32 gso_segs;
+       int delta;
 
        /* In case all data was pulled from skb frags (in __pskb_pull_tail()),
         * we can fix skb->truesize to its real value to avoid future drops.
@@ -1634,6 +1639,86 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
         */
        skb_condense(skb);
 
+       skb_dst_drop(skb);
+
+       if (unlikely(tcp_checksum_complete(skb))) {
+               bh_unlock_sock(sk);
+               __TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS);
+               __TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS);
+               return true;
+       }
+
+       /* Attempt coalescing to last skb in backlog, even if we are
+        * above the limits.
+        * This is okay because skb capacity is limited to MAX_SKB_FRAGS.
+        */
+       th = (const struct tcphdr *)skb->data;
+       hdrlen = th->doff * 4;
+       shinfo = skb_shinfo(skb);
+
+       if (!shinfo->gso_size)
+               shinfo->gso_size = skb->len - hdrlen;
+
+       if (!shinfo->gso_segs)
+               shinfo->gso_segs = 1;
+
+       tail = sk->sk_backlog.tail;
+       if (!tail)
+               goto no_coalesce;
+       thtail = (struct tcphdr *)tail->data;
+
+       if (TCP_SKB_CB(tail)->end_seq != TCP_SKB_CB(skb)->seq ||
+           TCP_SKB_CB(tail)->ip_dsfield != TCP_SKB_CB(skb)->ip_dsfield ||
+           ((TCP_SKB_CB(tail)->tcp_flags |
+             TCP_SKB_CB(skb)->tcp_flags) & TCPHDR_URG) ||
+           ((TCP_SKB_CB(tail)->tcp_flags ^
+             TCP_SKB_CB(skb)->tcp_flags) & (TCPHDR_ECE | TCPHDR_CWR)) ||
+#ifdef CONFIG_TLS_DEVICE
+           tail->decrypted != skb->decrypted ||
+#endif
+           thtail->doff != th->doff ||
+           memcmp(thtail + 1, th + 1, hdrlen - sizeof(*th)))
+               goto no_coalesce;
+
+       __skb_pull(skb, hdrlen);
+       if (skb_try_coalesce(tail, skb, &fragstolen, &delta)) {
+               thtail->window = th->window;
+
+               TCP_SKB_CB(tail)->end_seq = TCP_SKB_CB(skb)->end_seq;
+
+               if (after(TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(tail)->ack_seq))
+                       TCP_SKB_CB(tail)->ack_seq = TCP_SKB_CB(skb)->ack_seq;
+
+               TCP_SKB_CB(tail)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags;
+
+               if (TCP_SKB_CB(skb)->has_rxtstamp) {
+                       TCP_SKB_CB(tail)->has_rxtstamp = true;
+                       tail->tstamp = skb->tstamp;
+                       skb_hwtstamps(tail)->hwtstamp = skb_hwtstamps(skb)->hwtstamp;
+               }
+
+               /* Not as strict as GRO. We only need to carry mss max value */
+               skb_shinfo(tail)->gso_size = max(shinfo->gso_size,
+                                                skb_shinfo(tail)->gso_size);
+
+               gso_segs = skb_shinfo(tail)->gso_segs + shinfo->gso_segs;
+               skb_shinfo(tail)->gso_segs = min_t(u32, gso_segs, 0xFFFF);
+
+               sk->sk_backlog.len += delta;
+               __NET_INC_STATS(sock_net(sk),
+                               LINUX_MIB_TCPBACKLOGCOALESCE);
+               kfree_skb_partial(skb, fragstolen);
+               return false;
+       }
+       __skb_push(skb, hdrlen);
+
+no_coalesce:
+       /* Only socket owner can try to collapse/prune rx queues
+        * to reduce memory overhead, so add a little headroom here.
+        * Few sockets backlog are possibly concurrently non empty.
+        */
+       limit += 64*1024;
+
        if (unlikely(sk_add_backlog(sk, skb, limit))) {
                bh_unlock_sock(sk);
                __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPBACKLOGDROP);
index 870b0a3..0fbf7d4 100644 (file)
@@ -10,6 +10,7 @@
  *     TCPv4 GSO/GRO support
  */
 
+#include <linux/indirect_call_wrapper.h>
 #include <linux/skbuff.h>
 #include <net/tcp.h>
 #include <net/protocol.h>
@@ -305,7 +306,8 @@ int tcp_gro_complete(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(tcp_gro_complete);
 
-static struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
+INDIRECT_CALLABLE_SCOPE
+struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
 {
        /* Don't bother verifying checksum if we're going to flush anyway. */
        if (!NAPI_GRO_CB(skb)->flush &&
@@ -318,7 +320,7 @@ static struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *
        return tcp_gro_receive(head, skb);
 }
 
-static int tcp4_gro_complete(struct sk_buff *skb, int thoff)
+INDIRECT_CALLABLE_SCOPE int tcp4_gro_complete(struct sk_buff *skb, int thoff)
 {
        const struct iphdr *iph = ip_hdr(skb);
        struct tcphdr *th = tcp_hdr(skb);
index d40d4cc..730bc44 100644 (file)
@@ -180,10 +180,10 @@ static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts,
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (unlikely(tp->compressed_ack)) {
+       if (unlikely(tp->compressed_ack > TCP_FASTRETRANS_THRESH)) {
                NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPACKCOMPRESSED,
-                             tp->compressed_ack);
-               tp->compressed_ack = 0;
+                             tp->compressed_ack - TCP_FASTRETRANS_THRESH);
+               tp->compressed_ack = TCP_FASTRETRANS_THRESH;
                if (hrtimer_try_to_cancel(&tp->compressed_ack_timer) == 1)
                        __sock_put(sk);
        }
@@ -233,16 +233,14 @@ void tcp_select_initial_window(const struct sock *sk, int __space, __u32 mss,
        if (init_rcv_wnd)
                *rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss);
 
-       (*rcv_wscale) = 0;
+       *rcv_wscale = 0;
        if (wscale_ok) {
                /* Set window scaling on max possible window */
                space = max_t(u32, space, sock_net(sk)->ipv4.sysctl_tcp_rmem[2]);
                space = max_t(u32, space, sysctl_rmem_max);
                space = min_t(u32, space, *window_clamp);
-               while (space > U16_MAX && (*rcv_wscale) < TCP_MAX_WSCALE) {
-                       space >>= 1;
-                       (*rcv_wscale)++;
-               }
+               *rcv_wscale = clamp_t(int, ilog2(space) - 15,
+                                     0, TCP_MAX_WSCALE);
        }
        /* Set the clamp no higher than max representable value */
        (*window_clamp) = min_t(__u32, U16_MAX << (*rcv_wscale), *window_clamp);
@@ -596,7 +594,8 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
 
        *md5 = NULL;
 #ifdef CONFIG_TCP_MD5SIG
-       if (unlikely(rcu_access_pointer(tp->md5sig_info))) {
+       if (static_key_false(&tcp_md5_needed) &&
+           rcu_access_pointer(tp->md5sig_info)) {
                *md5 = tp->af_specific->md5_lookup(sk, sk);
                if (*md5) {
                        opts->options |= OPTION_MD5;
@@ -732,7 +731,8 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
 
        *md5 = NULL;
 #ifdef CONFIG_TCP_MD5SIG
-       if (unlikely(rcu_access_pointer(tp->md5sig_info))) {
+       if (static_key_false(&tcp_md5_needed) &&
+           rcu_access_pointer(tp->md5sig_info)) {
                *md5 = tp->af_specific->md5_lookup(sk, sk);
                if (*md5) {
                        opts->options |= OPTION_MD5;
@@ -1904,7 +1904,9 @@ static int tso_fragment(struct sock *sk, enum tcp_queue tcp_queue,
  * This algorithm is from John Heffner.
  */
 static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
-                                bool *is_cwnd_limited, u32 max_segs)
+                                bool *is_cwnd_limited,
+                                bool *is_rwnd_limited,
+                                u32 max_segs)
 {
        const struct inet_connection_sock *icsk = inet_csk(sk);
        u32 send_win, cong_win, limit, in_flight;
@@ -1913,9 +1915,6 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
        int win_divisor;
        s64 delta;
 
-       if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
-               goto send_now;
-
        if (icsk->icsk_ca_state >= TCP_CA_Recovery)
                goto send_now;
 
@@ -1948,10 +1947,6 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
        if ((skb != tcp_write_queue_tail(sk)) && (limit >= skb->len))
                goto send_now;
 
-       /* If this packet won't get more data, do not wait. */
-       if (TCP_SKB_CB(skb)->eor)
-               goto send_now;
-
        win_divisor = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_tso_win_divisor);
        if (win_divisor) {
                u32 chunk = min(tp->snd_wnd, tp->snd_cwnd * tp->mss_cache);
@@ -1981,10 +1976,28 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
        if ((s64)(delta - (u64)NSEC_PER_USEC * (tp->srtt_us >> 4)) < 0)
                goto send_now;
 
-       /* Ok, it looks like it is advisable to defer. */
+       /* Ok, it looks like it is advisable to defer.
+        * Three cases are tracked :
+        * 1) We are cwnd-limited
+        * 2) We are rwnd-limited
+        * 3) We are application limited.
+        */
+       if (cong_win < send_win) {
+               if (cong_win <= skb->len) {
+                       *is_cwnd_limited = true;
+                       return true;
+               }
+       } else {
+               if (send_win <= skb->len) {
+                       *is_rwnd_limited = true;
+                       return true;
+               }
+       }
 
-       if (cong_win < send_win && cong_win <= skb->len)
-               *is_cwnd_limited = true;
+       /* If this packet won't get more data, do not wait. */
+       if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) ||
+           TCP_SKB_CB(skb)->eor)
+               goto send_now;
 
        return true;
 
@@ -2365,7 +2378,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                } else {
                        if (!push_one &&
                            tcp_tso_should_defer(sk, skb, &is_cwnd_limited,
-                                                max_segs))
+                                                &is_rwnd_limited, max_segs))
                                break;
                }
 
@@ -2503,15 +2516,18 @@ void tcp_send_loss_probe(struct sock *sk)
                goto rearm_timer;
        }
        skb = skb_rb_last(&sk->tcp_rtx_queue);
+       if (unlikely(!skb)) {
+               WARN_ONCE(tp->packets_out,
+                         "invalid inflight: %u state %u cwnd %u mss %d\n",
+                         tp->packets_out, sk->sk_state, tp->snd_cwnd, mss);
+               inet_csk(sk)->icsk_pending = 0;
+               return;
+       }
 
        /* At most one outstanding TLP retransmission. */
        if (tp->tlp_high_seq)
                goto rearm_timer;
 
-       /* Retransmit last segment. */
-       if (WARN_ON(!skb))
-               goto rearm_timer;
-
        if (skb_still_in_host_queue(sk, skb))
                goto rearm_timer;
 
@@ -2929,7 +2945,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
                TCP_SKB_CB(skb)->sacked |= TCPCB_EVER_RETRANS;
                trace_tcp_retransmit_skb(sk, skb);
        } else if (err != -EBUSY) {
-               NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL);
+               NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL, segs);
        }
        return err;
 }
index 6760206..f87dbc7 100644 (file)
@@ -40,15 +40,17 @@ static u32 tcp_clamp_rto_to_user_timeout(const struct sock *sk)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        u32 elapsed, start_ts;
+       s32 remaining;
 
        start_ts = tcp_retransmit_stamp(sk);
        if (!icsk->icsk_user_timeout || !start_ts)
                return icsk->icsk_rto;
        elapsed = tcp_time_stamp(tcp_sk(sk)) - start_ts;
-       if (elapsed >= icsk->icsk_user_timeout)
+       remaining = icsk->icsk_user_timeout - elapsed;
+       if (remaining <= 0)
                return 1; /* user timeout has passed; fire ASAP */
-       else
-               return min_t(u32, icsk->icsk_rto, msecs_to_jiffies(icsk->icsk_user_timeout - elapsed));
+
+       return min_t(u32, icsk->icsk_rto, msecs_to_jiffies(remaining));
 }
 
 /**
@@ -209,7 +211,7 @@ static bool retransmits_timed_out(struct sock *sk,
                                (boundary - linear_backoff_thresh) * TCP_RTO_MAX;
                timeout = jiffies_to_msecs(timeout);
        }
-       return (tcp_time_stamp(tcp_sk(sk)) - start_ts) >= timeout;
+       return (s32)(tcp_time_stamp(tcp_sk(sk)) - start_ts - timeout) >= 0;
 }
 
 /* A write timeout has occurred. Process the after effects. */
@@ -376,7 +378,7 @@ static void tcp_probe_timer(struct sock *sk)
                        return;
        }
 
-       if (icsk->icsk_probes_out > max_probes) {
+       if (icsk->icsk_probes_out >= max_probes) {
 abort:         tcp_write_err(sk);
        } else {
                /* Only send another probe if we didn't close things up. */
@@ -482,11 +484,12 @@ void tcp_retransmit_timer(struct sock *sk)
                goto out_reset_timer;
        }
 
+       __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPTIMEOUTS);
        if (tcp_write_timeout(sk))
                goto out;
 
        if (icsk->icsk_retransmits == 0) {
-               int mib_idx;
+               int mib_idx = 0;
 
                if (icsk->icsk_ca_state == TCP_CA_Recovery) {
                        if (tcp_is_sack(tp))
@@ -501,10 +504,9 @@ void tcp_retransmit_timer(struct sock *sk)
                                mib_idx = LINUX_MIB_TCPSACKFAILURES;
                        else
                                mib_idx = LINUX_MIB_TCPRENOFAILURES;
-               } else {
-                       mib_idx = LINUX_MIB_TCPTIMEOUTS;
                }
-               __NET_INC_STATS(sock_net(sk), mib_idx);
+               if (mib_idx)
+                       __NET_INC_STATS(sock_net(sk), mib_idx);
        }
 
        tcp_enter_loss(sk);
@@ -740,7 +742,7 @@ static enum hrtimer_restart tcp_compressed_ack_kick(struct hrtimer *timer)
 
        bh_lock_sock(sk);
        if (!sock_owned_by_user(sk)) {
-               if (tp->compressed_ack)
+               if (tp->compressed_ack > TCP_FASTRETRANS_THRESH)
                        tcp_send_ack(sk);
        } else {
                if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED,
index 6f8890c..3fb0ed5 100644 (file)
@@ -380,15 +380,12 @@ static int compute_score(struct sock *sk, struct net *net,
            ipv6_only_sock(sk))
                return -1;
 
-       score = (sk->sk_family == PF_INET) ? 2 : 1;
-       inet = inet_sk(sk);
+       if (sk->sk_rcv_saddr != daddr)
+               return -1;
 
-       if (inet->inet_rcv_saddr) {
-               if (inet->inet_rcv_saddr != daddr)
-                       return -1;
-               score += 4;
-       }
+       score = (sk->sk_family == PF_INET) ? 2 : 1;
 
+       inet = inet_sk(sk);
        if (inet->inet_daddr) {
                if (inet->inet_daddr != saddr)
                        return -1;
@@ -464,65 +461,30 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
                __be16 sport, __be32 daddr, __be16 dport, int dif,
                int sdif, struct udp_table *udptable, struct sk_buff *skb)
 {
-       struct sock *sk, *result;
+       struct sock *result;
        unsigned short hnum = ntohs(dport);
-       unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask);
-       struct udp_hslot *hslot2, *hslot = &udptable->hash[slot];
+       unsigned int hash2, slot2;
+       struct udp_hslot *hslot2;
        bool exact_dif = udp_lib_exact_dif_match(net, skb);
-       int score, badness;
-       u32 hash = 0;
 
-       if (hslot->count > 10) {
-               hash2 = ipv4_portaddr_hash(net, daddr, hnum);
+       hash2 = ipv4_portaddr_hash(net, daddr, hnum);
+       slot2 = hash2 & udptable->mask;
+       hslot2 = &udptable->hash2[slot2];
+
+       result = udp4_lib_lookup2(net, saddr, sport,
+                                 daddr, hnum, dif, sdif,
+                                 exact_dif, hslot2, skb);
+       if (!result) {
+               hash2 = ipv4_portaddr_hash(net, htonl(INADDR_ANY), hnum);
                slot2 = hash2 & udptable->mask;
                hslot2 = &udptable->hash2[slot2];
-               if (hslot->count < hslot2->count)
-                       goto begin;
 
                result = udp4_lib_lookup2(net, saddr, sport,
-                                         daddr, hnum, dif, sdif,
+                                         htonl(INADDR_ANY), hnum, dif, sdif,
                                          exact_dif, hslot2, skb);
-               if (!result) {
-                       unsigned int old_slot2 = slot2;
-                       hash2 = ipv4_portaddr_hash(net, htonl(INADDR_ANY), hnum);
-                       slot2 = hash2 & udptable->mask;
-                       /* avoid searching the same slot again. */
-                       if (unlikely(slot2 == old_slot2))
-                               return result;
-
-                       hslot2 = &udptable->hash2[slot2];
-                       if (hslot->count < hslot2->count)
-                               goto begin;
-
-                       result = udp4_lib_lookup2(net, saddr, sport,
-                                                 daddr, hnum, dif, sdif,
-                                                 exact_dif, hslot2, skb);
-               }
-               if (unlikely(IS_ERR(result)))
-                       return NULL;
-               return result;
-       }
-begin:
-       result = NULL;
-       badness = 0;
-       sk_for_each_rcu(sk, &hslot->head) {
-               score = compute_score(sk, net, saddr, sport,
-                                     daddr, hnum, dif, sdif, exact_dif);
-               if (score > badness) {
-                       if (sk->sk_reuseport) {
-                               hash = udp_ehashfn(net, daddr, hnum,
-                                                  saddr, sport);
-                               result = reuseport_select_sock(sk, hash, skb,
-                                                       sizeof(struct udphdr));
-                               if (unlikely(IS_ERR(result)))
-                                       return NULL;
-                               if (result)
-                                       return result;
-                       }
-                       result = sk;
-                       badness = score;
-               }
        }
+       if (unlikely(IS_ERR(result)))
+               return NULL;
        return result;
 }
 EXPORT_SYMBOL_GPL(__udp4_lib_lookup);
@@ -587,7 +549,7 @@ static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk,
 DEFINE_STATIC_KEY_FALSE(udp_encap_needed_key);
 void udp_encap_enable(void)
 {
-       static_branch_enable(&udp_encap_needed_key);
+       static_branch_inc(&udp_encap_needed_key);
 }
 EXPORT_SYMBOL(udp_encap_enable);
 
@@ -2524,7 +2486,7 @@ void udp_destroy_sock(struct sock *sk)
                                encap_destroy(sk);
                }
                if (up->encap_enabled)
-                       static_branch_disable(&udp_encap_needed_key);
+                       static_branch_dec(&udp_encap_needed_key);
        }
 }
 
index 0646d61..64f9715 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/skbuff.h>
 #include <net/udp.h>
 #include <net/protocol.h>
+#include <net/inet_common.h>
 
 static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
        netdev_features_t features,
@@ -391,6 +392,8 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
        return NULL;
 }
 
+INDIRECT_CALLABLE_DECLARE(struct sock *udp6_lib_lookup_skb(struct sk_buff *skb,
+                                                  __be16 sport, __be16 dport));
 struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
                                struct udphdr *uh, udp_lookup_t lookup)
 {
@@ -402,7 +405,8 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
        struct sock *sk;
 
        rcu_read_lock();
-       sk = (*lookup)(skb, uh->source, uh->dest);
+       sk = INDIRECT_CALL_INET(lookup, udp6_lib_lookup_skb,
+                               udp4_lib_lookup_skb, skb, uh->source, uh->dest);
        if (!sk)
                goto out_unlock;
 
@@ -451,8 +455,8 @@ out_unlock:
 }
 EXPORT_SYMBOL(udp_gro_receive);
 
-static struct sk_buff *udp4_gro_receive(struct list_head *head,
-                                       struct sk_buff *skb)
+INDIRECT_CALLABLE_SCOPE
+struct sk_buff *udp4_gro_receive(struct list_head *head, struct sk_buff *skb)
 {
        struct udphdr *uh = udp_gro_udphdr(skb);
 
@@ -502,7 +506,8 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff,
        uh->len = newlen;
 
        rcu_read_lock();
-       sk = (*lookup)(skb, uh->source, uh->dest);
+       sk = INDIRECT_CALL_INET(lookup, udp6_lib_lookup_skb,
+                               udp4_lib_lookup_skb, skb, uh->source, uh->dest);
        if (sk && udp_sk(sk)->gro_enabled) {
                err = udp_gro_complete_segment(skb);
        } else if (sk && udp_sk(sk)->gro_complete) {
@@ -525,7 +530,7 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff,
 }
 EXPORT_SYMBOL(udp_gro_complete);
 
-static int udp4_gro_complete(struct sk_buff *skb, int nhoff)
+INDIRECT_CALLABLE_SCOPE int udp4_gro_complete(struct sk_buff *skb, int nhoff)
 {
        const struct iphdr *iph = ip_hdr(skb);
        struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
index d0c412f..be8b5b2 100644 (file)
@@ -20,6 +20,23 @@ int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
        if (err < 0)
                goto error;
 
+       if (cfg->bind_ifindex) {
+               struct net_device *dev;
+
+               dev = dev_get_by_index(net, cfg->bind_ifindex);
+               if (!dev) {
+                       err = -ENODEV;
+                       goto error;
+               }
+
+               err = kernel_setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
+                                       dev->name, strlen(dev->name) + 1);
+               dev_put(dev);
+
+               if (err < 0)
+                       goto error;
+       }
+
        udp_addr.sin_family = AF_INET;
        udp_addr.sin_addr = cfg->local_ip;
        udp_addr.sin_port = cfg->local_udp_port;
index 63a808d..521e471 100644 (file)
@@ -179,7 +179,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp);
 static void addrconf_dad_work(struct work_struct *w);
 static void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id,
                                   bool send_na);
-static void addrconf_dad_run(struct inet6_dev *idev);
+static void addrconf_dad_run(struct inet6_dev *idev, bool restart);
 static void addrconf_rs_timer(struct timer_list *t);
 static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
 static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
@@ -2820,7 +2820,7 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg)
                        dev = __dev_get_by_name(net, p.name);
                        if (!dev)
                                goto err_exit;
-                       err = dev_open(dev);
+                       err = dev_open(dev, NULL);
                }
        }
 #endif
@@ -3439,6 +3439,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                           void *ptr)
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct netdev_notifier_change_info *change_info;
        struct netdev_notifier_changeupper_info *info;
        struct inet6_dev *idev = __in6_dev_get(dev);
        struct net *net = dev_net(dev);
@@ -3513,7 +3514,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                                break;
                        }
 
-                       if (idev) {
+                       if (!IS_ERR_OR_NULL(idev)) {
                                if (idev->if_flags & IF_READY) {
                                        /* device is already configured -
                                         * but resend MLD reports, we might
@@ -3521,6 +3522,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                                         * multicast snooping switches
                                         */
                                        ipv6_mc_up(idev);
+                                       change_info = ptr;
+                                       if (change_info->flags_changed & IFF_NOARP)
+                                               addrconf_dad_run(idev, true);
                                        rt6_sync_up(dev, RTNH_F_LINKDOWN);
                                        break;
                                }
@@ -3555,7 +3559,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
 
                if (!IS_ERR_OR_NULL(idev)) {
                        if (run_pending)
-                               addrconf_dad_run(idev);
+                               addrconf_dad_run(idev, false);
 
                        /* Device has an address by now */
                        rt6_sync_up(dev, RTNH_F_DEAD);
@@ -4173,16 +4177,19 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id,
                addrconf_verify_rtnl();
 }
 
-static void addrconf_dad_run(struct inet6_dev *idev)
+static void addrconf_dad_run(struct inet6_dev *idev, bool restart)
 {
        struct inet6_ifaddr *ifp;
 
        read_lock_bh(&idev->lock);
        list_for_each_entry(ifp, &idev->addr_list, if_list) {
                spin_lock(&ifp->lock);
-               if (ifp->flags & IFA_F_TENTATIVE &&
-                   ifp->state == INET6_IFADDR_STATE_DAD)
+               if ((ifp->flags & IFA_F_TENTATIVE &&
+                    ifp->state == INET6_IFADDR_STATE_DAD) || restart) {
+                       if (restart)
+                               ifp->state = INET6_IFADDR_STATE_PREDAD;
                        addrconf_dad_kick(ifp);
+               }
                spin_unlock(&ifp->lock);
        }
        read_unlock_bh(&idev->lock);
index 63b2b66..5afe9f8 100644 (file)
@@ -145,10 +145,13 @@ static void esp_output_done(struct crypto_async_request *base, int err)
        void *tmp;
        struct xfrm_state *x;
 
-       if (xo && (xo->flags & XFRM_DEV_RESUME))
-               x = skb->sp->xvec[skb->sp->len - 1];
-       else
+       if (xo && (xo->flags & XFRM_DEV_RESUME)) {
+               struct sec_path *sp = skb_sec_path(skb);
+
+               x = sp->xvec[sp->len - 1];
+       } else {
                x = skb_dst(skb)->xfrm;
+       }
 
        tmp = ESP_SKB_CB(skb)->tmp;
        esp_ssg_unref(x, tmp);
index 6177e21..d46b4eb 100644 (file)
@@ -68,11 +68,12 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
 
        xo = xfrm_offload(skb);
        if (!xo || !(xo->flags & CRYPTO_DONE)) {
-               err = secpath_set(skb);
-               if (err)
+               struct sec_path *sp = secpath_set(skb);
+
+               if (!sp)
                        goto out;
 
-               if (skb->sp->len == XFRM_MAX_DEPTH)
+               if (sp->len == XFRM_MAX_DEPTH)
                        goto out;
 
                x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
@@ -81,8 +82,8 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
                if (!x)
                        goto out;
 
-               skb->sp->xvec[skb->sp->len++] = x;
-               skb->sp->olen++;
+               sp->xvec[sp->len++] = x;
+               sp->olen++;
 
                xo = xfrm_offload(skb);
                if (!xo) {
@@ -141,6 +142,7 @@ static struct sk_buff *esp6_gso_segment(struct sk_buff *skb,
        struct crypto_aead *aead;
        netdev_features_t esp_features = features;
        struct xfrm_offload *xo = xfrm_offload(skb);
+       struct sec_path *sp;
 
        if (!xo)
                return ERR_PTR(-EINVAL);
@@ -148,7 +150,8 @@ static struct sk_buff *esp6_gso_segment(struct sk_buff *skb,
        if (!(skb_shinfo(skb)->gso_type & SKB_GSO_ESP))
                return ERR_PTR(-EINVAL);
 
-       x = skb->sp->xvec[skb->sp->len - 1];
+       sp = skb_sec_path(skb);
+       x = sp->xvec[sp->len - 1];
        aead = x->data;
        esph = ip_esp_hdr(skb);
 
index 5eeeba7..f3515eb 100644 (file)
@@ -99,23 +99,16 @@ static inline int compute_score(struct sock *sk, struct net *net,
                                const int dif, const int sdif, bool exact_dif)
 {
        int score = -1;
-       bool dev_match;
 
        if (net_eq(sock_net(sk), net) && inet_sk(sk)->inet_num == hnum &&
            sk->sk_family == PF_INET6) {
+               if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
+                       return -1;
 
-               score = 1;
-               if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
-                       if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
-                               return -1;
-                       score++;
-               }
-               dev_match = inet_sk_bound_dev_eq(net, sk->sk_bound_dev_if,
-                                                dif, sdif);
-               if (!dev_match)
+               if (!inet_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif))
                        return -1;
-               score++;
 
+               score = 1;
                if (sk->sk_incoming_cpu == raw_smp_processor_id())
                        score++;
        }
@@ -164,26 +157,12 @@ struct sock *inet6_lookup_listener(struct net *net,
                const __be16 sport, const struct in6_addr *daddr,
                const unsigned short hnum, const int dif, const int sdif)
 {
-       unsigned int hash = inet_lhashfn(net, hnum);
-       struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
-       bool exact_dif = inet6_exact_dif_match(net, skb);
        struct inet_listen_hashbucket *ilb2;
-       struct sock *sk, *result = NULL;
-       int score, hiscore = 0;
+       struct sock *result = NULL;
        unsigned int hash2;
-       u32 phash = 0;
-
-       if (ilb->count <= 10 || !hashinfo->lhash2)
-               goto port_lookup;
-
-       /* Too many sk in the ilb bucket (which is hashed by port alone).
-        * Try lhash2 (which is hashed by port and addr) instead.
-        */
 
        hash2 = ipv6_portaddr_hash(net, daddr, hnum);
        ilb2 = inet_lhash2_bucket(hashinfo, hash2);
-       if (ilb2->count > ilb->count)
-               goto port_lookup;
 
        result = inet6_lhash2_lookup(net, ilb2, skb, doff,
                                     saddr, sport, daddr, hnum,
@@ -192,33 +171,12 @@ struct sock *inet6_lookup_listener(struct net *net,
                goto done;
 
        /* Lookup lhash2 with in6addr_any */
-
        hash2 = ipv6_portaddr_hash(net, &in6addr_any, hnum);
        ilb2 = inet_lhash2_bucket(hashinfo, hash2);
-       if (ilb2->count > ilb->count)
-               goto port_lookup;
 
        result = inet6_lhash2_lookup(net, ilb2, skb, doff,
-                                    saddr, sport, daddr, hnum,
+                                    saddr, sport, &in6addr_any, hnum,
                                     dif, sdif);
-       goto done;
-
-port_lookup:
-       sk_for_each(sk, &ilb->head) {
-               score = compute_score(sk, net, hnum, daddr, dif, sdif, exact_dif);
-               if (score > hiscore) {
-                       if (sk->sk_reuseport) {
-                               phash = inet6_ehashfn(net, daddr, hnum,
-                                                     saddr, sport);
-                               result = reuseport_select_sock(sk, phash,
-                                                              skb, doff);
-                               if (result)
-                                       goto done;
-                       }
-                       result = sk;
-                       hiscore = score;
-               }
-       }
 done:
        if (unlikely(IS_ERR(result)))
                return NULL;
index 81b69bc..229e55c 100644 (file)
@@ -1885,12 +1885,6 @@ static void ip6gre_tap_setup(struct net_device *dev)
        netif_keep_dst(dev);
 }
 
-bool is_ip6gretap_dev(const struct net_device *dev)
-{
-       return dev->netdev_ops == &ip6gre_tap_netdev_ops;
-}
-EXPORT_SYMBOL_GPL(is_ip6gretap_dev);
-
 static bool ip6gre_netlink_encap_parms(struct nlattr *data[],
                                       struct ip_tunnel_encap *ipencap)
 {
index 3c06cc9..c7ed2b6 100644 (file)
@@ -95,7 +95,7 @@ static void ip6_list_rcv_finish(struct net *net, struct sock *sk,
        list_for_each_entry_safe(skb, next, head, list) {
                struct dst_entry *dst;
 
-               list_del(&skb->list);
+               skb_list_del_init(skb);
                /* if ingress device is enslaved to an L3 master device pass the
                 * skb to its handler for processing
                 */
@@ -296,7 +296,7 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,
                struct net_device *dev = skb->dev;
                struct net *net = dev_net(dev);
 
-               list_del(&skb->list);
+               skb_list_del_init(skb);
                skb = ip6_rcv_core(skb, dev, net);
                if (skb == NULL)
                        continue;
index 70f525c..5c04569 100644 (file)
 
 #include "ip6_offload.h"
 
+/* All GRO functions are always builtin, except UDP over ipv6, which lays in
+ * ipv6 module, as it depends on UDPv6 lookup function, so we need special care
+ * when ipv6 is built as a module
+ */
+#if IS_BUILTIN(CONFIG_IPV6)
+#define INDIRECT_CALL_L4(f, f2, f1, ...) INDIRECT_CALL_2(f, f2, f1, __VA_ARGS__)
+#else
+#define INDIRECT_CALL_L4(f, f2, f1, ...) INDIRECT_CALL_1(f, f2, __VA_ARGS__)
+#endif
+
+#define indirect_call_gro_receive_l4(f2, f1, cb, head, skb)    \
+({                                                             \
+       unlikely(gro_recursion_inc_test(skb)) ?                 \
+               NAPI_GRO_CB(skb)->flush |= 1, NULL :            \
+               INDIRECT_CALL_L4(cb, f2, f1, head, skb);        \
+})
+
 static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
 {
        const struct net_offload *ops = NULL;
@@ -164,8 +181,12 @@ static int ipv6_exthdrs_len(struct ipv6hdr *iph,
        return len;
 }
 
-static struct sk_buff *ipv6_gro_receive(struct list_head *head,
-                                       struct sk_buff *skb)
+INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp6_gro_receive(struct list_head *,
+                                                          struct sk_buff *));
+INDIRECT_CALLABLE_DECLARE(struct sk_buff *udp6_gro_receive(struct list_head *,
+                                                          struct sk_buff *));
+INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head,
+                                                        struct sk_buff *skb)
 {
        const struct net_offload *ops;
        struct sk_buff *pp = NULL;
@@ -260,7 +281,8 @@ not_same_flow:
 
        skb_gro_postpull_rcsum(skb, iph, nlen);
 
-       pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
+       pp = indirect_call_gro_receive_l4(tcp6_gro_receive, udp6_gro_receive,
+                                        ops->callbacks.gro_receive, head, skb);
 
 out_unlock:
        rcu_read_unlock();
@@ -301,7 +323,9 @@ static struct sk_buff *ip4ip6_gro_receive(struct list_head *head,
        return inet_gro_receive(head, skb);
 }
 
-static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
+INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *, int));
+INDIRECT_CALLABLE_DECLARE(int udp6_gro_complete(struct sk_buff *, int));
+INDIRECT_CALLABLE_SCOPE int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
 {
        const struct net_offload *ops;
        struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + nhoff);
@@ -320,7 +344,8 @@ static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
        if (WARN_ON(!ops || !ops->callbacks.gro_complete))
                goto out_unlock;
 
-       err = ops->callbacks.gro_complete(skb, nhoff);
+       err = INDIRECT_CALL_L4(ops->callbacks.gro_complete, tcp6_gro_complete,
+                              udp6_gro_complete, skb, nhoff);
 
 out_unlock:
        rcu_read_unlock();
index 89e0d51..5f9fa03 100644 (file)
@@ -195,37 +195,37 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
        const struct ipv6_pinfo *np = inet6_sk(sk);
        struct in6_addr *first_hop = &fl6->daddr;
        struct dst_entry *dst = skb_dst(skb);
+       unsigned int head_room;
        struct ipv6hdr *hdr;
        u8  proto = fl6->flowi6_proto;
        int seg_len = skb->len;
        int hlimit = -1;
        u32 mtu;
 
-       if (opt) {
-               unsigned int head_room;
+       head_room = sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dst->dev);
+       if (opt)
+               head_room += opt->opt_nflen + opt->opt_flen;
 
-               /* First: exthdrs may take lots of space (~8K for now)
-                  MAX_HEADER is not enough.
-                */
-               head_room = opt->opt_nflen + opt->opt_flen;
-               seg_len += head_room;
-               head_room += sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dst->dev);
-
-               if (skb_headroom(skb) < head_room) {
-                       struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
-                       if (!skb2) {
-                               IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
-                                             IPSTATS_MIB_OUTDISCARDS);
-                               kfree_skb(skb);
-                               return -ENOBUFS;
-                       }
-                       if (skb->sk)
-                               skb_set_owner_w(skb2, skb->sk);
-                       consume_skb(skb);
-                       skb = skb2;
+       if (unlikely(skb_headroom(skb) < head_room)) {
+               struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
+               if (!skb2) {
+                       IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
+                                     IPSTATS_MIB_OUTDISCARDS);
+                       kfree_skb(skb);
+                       return -ENOBUFS;
                }
+               if (skb->sk)
+                       skb_set_owner_w(skb2, skb->sk);
+               consume_skb(skb);
+               skb = skb2;
+       }
+
+       if (opt) {
+               seg_len += opt->opt_nflen + opt->opt_flen;
+
                if (opt->opt_flen)
                        ipv6_push_frag_opts(skb, opt, &proto);
+
                if (opt->opt_nflen)
                        ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop,
                                             &fl6->saddr);
@@ -378,6 +378,14 @@ static inline int ip6_forward_finish(struct net *net, struct sock *sk,
        __IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
        __IP6_ADD_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);
 
+#ifdef CONFIG_NET_SWITCHDEV
+       if (skb->offload_l3_fwd_mark) {
+               consume_skb(skb);
+               return 0;
+       }
+#endif
+
+       skb->tstamp = 0;
        return dst_output(net, sk, skb);
 }
 
@@ -574,6 +582,7 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        to->tc_index = from->tc_index;
 #endif
        nf_copy(to, from);
+       skb_ext_copy(to, from);
        skb_copy_secmark(to, from);
 }
 
@@ -1245,6 +1254,7 @@ static int __ip6_append_data(struct sock *sk,
 {
        struct sk_buff *skb, *skb_prev = NULL;
        unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu, pmtu;
+       struct ubuf_info *uarg = NULL;
        int exthdrlen = 0;
        int dst_exthdrlen = 0;
        int hh_len;
@@ -1257,7 +1267,7 @@ static int __ip6_append_data(struct sock *sk,
        int csummode = CHECKSUM_NONE;
        unsigned int maxnonfragsize, headersize;
        unsigned int wmem_alloc_delta = 0;
-       bool paged;
+       bool paged, extra_uref;
 
        skb = skb_peek_tail(queue);
        if (!skb) {
@@ -1322,6 +1332,20 @@ emsgsize:
            rt->dst.dev->features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))
                csummode = CHECKSUM_PARTIAL;
 
+       if (flags & MSG_ZEROCOPY && length && sock_flag(sk, SOCK_ZEROCOPY)) {
+               uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb));
+               if (!uarg)
+                       return -ENOBUFS;
+               extra_uref = true;
+               if (rt->dst.dev->features & NETIF_F_SG &&
+                   csummode == CHECKSUM_PARTIAL) {
+                       paged = true;
+               } else {
+                       uarg->zerocopy = 0;
+                       skb_zcopy_set(skb, uarg, &extra_uref);
+               }
+       }
+
        /*
         * Let's try using as much space as possible.
         * Use MTU if total length of the message fits into the MTU.
@@ -1354,7 +1378,7 @@ emsgsize:
                        unsigned int fraglen;
                        unsigned int fraggap;
                        unsigned int alloclen;
-                       unsigned int pagedlen = 0;
+                       unsigned int pagedlen;
 alloc_new_skb:
                        /* There's no room in the current skb */
                        if (skb)
@@ -1378,6 +1402,7 @@ alloc_new_skb:
                        if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
                                datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len;
                        fraglen = datalen + fragheaderlen;
+                       pagedlen = 0;
 
                        if ((flags & MSG_MORE) &&
                            !(rt->dst.dev->features&NETIF_F_SG))
@@ -1439,12 +1464,6 @@ alloc_new_skb:
                        skb_reserve(skb, hh_len + sizeof(struct frag_hdr) +
                                    dst_exthdrlen);
 
-                       /* Only the initial fragment is time stamped */
-                       skb_shinfo(skb)->tx_flags = cork->tx_flags;
-                       cork->tx_flags = 0;
-                       skb_shinfo(skb)->tskey = tskey;
-                       tskey = 0;
-
                        /*
                         *      Find where to start putting bytes
                         */
@@ -1476,6 +1495,13 @@ alloc_new_skb:
                        exthdrlen = 0;
                        dst_exthdrlen = 0;
 
+                       /* Only the initial fragment is time stamped */
+                       skb_shinfo(skb)->tx_flags = cork->tx_flags;
+                       cork->tx_flags = 0;
+                       skb_shinfo(skb)->tskey = tskey;
+                       tskey = 0;
+                       skb_zcopy_set(skb, uarg, &extra_uref);
+
                        if ((flags & MSG_CONFIRM) && !skb_prev)
                                skb_set_dst_pending_confirm(skb, 1);
 
@@ -1505,7 +1531,7 @@ alloc_new_skb:
                                err = -EFAULT;
                                goto error;
                        }
-               } else {
+               } else if (!uarg || !uarg->zerocopy) {
                        int i = skb_shinfo(skb)->nr_frags;
 
                        err = -ENOMEM;
@@ -1535,6 +1561,10 @@ alloc_new_skb:
                        skb->data_len += copy;
                        skb->truesize += copy;
                        wmem_alloc_delta += copy;
+               } else {
+                       err = skb_zerocopy_iter_dgram(skb, from, copy);
+                       if (err < 0)
+                               goto error;
                }
                offset += copy;
                length -= copy;
@@ -1547,6 +1577,8 @@ alloc_new_skb:
 error_efault:
        err = -EFAULT;
 error:
+       if (uarg)
+               sock_zerocopy_put_abort(uarg, extra_uref);
        cork->length -= length;
        IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
        refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc);
index b283f29..ad1a9cc 100644 (file)
@@ -15,7 +15,7 @@
 int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
                     struct socket **sockp)
 {
-       struct sockaddr_in6 udp6_addr;
+       struct sockaddr_in6 udp6_addr = {};
        int err;
        struct socket *sock = NULL;
 
@@ -31,6 +31,22 @@ int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
                if (err < 0)
                        goto error;
        }
+       if (cfg->bind_ifindex) {
+               struct net_device *dev;
+
+               dev = dev_get_by_index(net, cfg->bind_ifindex);
+               if (!dev) {
+                       err = -ENODEV;
+                       goto error;
+               }
+
+               err = kernel_setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
+                                       dev->name, strlen(dev->name) + 1);
+               dev_put(dev);
+
+               if (err < 0)
+                       goto error;
+       }
 
        udp6_addr.sin6_family = AF_INET6;
        memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
@@ -42,6 +58,7 @@ int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
                goto error;
 
        if (cfg->peer_udp_port) {
+               memset(&udp6_addr, 0, sizeof(udp6_addr));
                udp6_addr.sin6_family = AF_INET6;
                memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
                       sizeof(udp6_addr.sin6_addr));
index e2ea691..8276f12 100644 (file)
@@ -52,6 +52,8 @@
 #include <net/ip6_checksum.h>
 #include <linux/netconf.h>
 
+#include <linux/nospec.h>
+
 struct ip6mr_rule {
        struct fib_rule         common;
 };
@@ -655,7 +657,7 @@ static struct net_device *ip6mr_reg_vif(struct net *net, struct mr_table *mrt)
                return NULL;
        }
 
-       if (dev_open(dev))
+       if (dev_open(dev, NULL))
                goto failure;
 
        dev_hold(dev);
@@ -1841,6 +1843,7 @@ int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg)
                        return -EFAULT;
                if (vr.mifi >= mrt->maxvif)
                        return -EINVAL;
+               vr.mifi = array_index_nospec(vr.mifi, mrt->maxvif);
                read_lock(&mrt_lock);
                vif = &mrt->vif_table[vr.mifi];
                if (VIF_EXISTS(mrt, vr.mifi)) {
@@ -1915,6 +1918,7 @@ int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
                        return -EFAULT;
                if (vr.mifi >= mrt->maxvif)
                        return -EINVAL;
+               vr.mifi = array_index_nospec(vr.mifi, mrt->maxvif);
                read_lock(&mrt_lock);
                vif = &mrt->vif_table[vr.mifi];
                if (VIF_EXISTS(mrt, vr.mifi)) {
@@ -1968,7 +1972,7 @@ static inline int ip6mr_forward2_finish(struct net *net, struct sock *sk, struct
  */
 
 static int ip6mr_forward2(struct net *net, struct mr_table *mrt,
-                         struct sk_buff *skb, struct mfc6_cache *c, int vifi)
+                         struct sk_buff *skb, int vifi)
 {
        struct ipv6hdr *ipv6h;
        struct vif_device *vif = &mrt->vif_table[vifi];
@@ -2134,15 +2138,14 @@ forward:
                        if (psend != -1) {
                                struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
                                if (skb2)
-                                       ip6mr_forward2(net, mrt, skb2,
-                                                      c, psend);
+                                       ip6mr_forward2(net, mrt, skb2, psend);
                        }
                        psend = ct;
                }
        }
 last_forward:
        if (psend != -1) {
-               ip6mr_forward2(net, mrt, skb, c, psend);
+               ip6mr_forward2(net, mrt, skb, psend);
                return;
        }
 
index 5ae8e1c..8b075f0 100644 (file)
@@ -24,7 +24,8 @@ int ip6_route_me_harder(struct net *net, struct sk_buff *skb)
        unsigned int hh_len;
        struct dst_entry *dst;
        struct flowi6 fl6 = {
-               .flowi6_oif = sk ? sk->sk_bound_dev_if : 0,
+               .flowi6_oif = sk && sk->sk_bound_dev_if ? sk->sk_bound_dev_if :
+                       rt6_need_strict(&iph->daddr) ? skb_dst(skb)->dev->ifindex : 0,
                .flowi6_mark = skb->mark,
                .flowi6_uid = sock_net_uid(net, sk),
                .daddr = iph->daddr,
index 491f808..29c7f19 100644 (file)
@@ -58,8 +58,12 @@ static int __init masquerade_tg6_init(void)
        int err;
 
        err = xt_register_target(&masquerade_tg6_reg);
-       if (err == 0)
-               nf_nat_masquerade_ipv6_register_notifier();
+       if (err)
+               return err;
+
+       err = nf_nat_masquerade_ipv6_register_notifier();
+       if (err)
+               xt_unregister_target(&masquerade_tg6_reg);
 
        return err;
 }
index d219979..181da2c 100644 (file)
@@ -341,7 +341,7 @@ static bool
 nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev,  struct net_device *dev)
 {
        struct sk_buff *fp, *head = fq->q.fragments;
-       int    payload_len;
+       int    payload_len, delta;
        u8 ecn;
 
        inet_frag_kill(&fq->q);
@@ -363,10 +363,16 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev,  struct net_devic
                return false;
        }
 
+       delta = - head->truesize;
+
        /* Head of list must not be cloned. */
        if (skb_unclone(head, GFP_ATOMIC))
                return false;
 
+       delta += head->truesize;
+       if (delta)
+               add_frag_mem_limit(fq->q.net, delta);
+
        /* If the first fragment is fragmented itself, we split
         * it to two chunks: the first with data and paged part
         * and the second, holding only fragments. */
index 3e4bf22..0ad0da5 100644 (file)
@@ -132,8 +132,8 @@ static void iterate_cleanup_work(struct work_struct *work)
  * of ipv6 addresses being deleted), we also need to add an upper
  * limit to the number of queued work items.
  */
-static int masq_inet_event(struct notifier_block *this,
-                          unsigned long event, void *ptr)
+static int masq_inet6_event(struct notifier_block *this,
+                           unsigned long event, void *ptr)
 {
        struct inet6_ifaddr *ifa = ptr;
        const struct net_device *dev;
@@ -171,30 +171,53 @@ static int masq_inet_event(struct notifier_block *this,
        return NOTIFY_DONE;
 }
 
-static struct notifier_block masq_inet_notifier = {
-       .notifier_call  = masq_inet_event,
+static struct notifier_block masq_inet6_notifier = {
+       .notifier_call  = masq_inet6_event,
 };
 
-static atomic_t masquerade_notifier_refcount = ATOMIC_INIT(0);
+static int masq_refcnt;
+static DEFINE_MUTEX(masq_mutex);
 
-void nf_nat_masquerade_ipv6_register_notifier(void)
+int nf_nat_masquerade_ipv6_register_notifier(void)
 {
+       int ret = 0;
+
+       mutex_lock(&masq_mutex);
        /* check if the notifier is already set */
-       if (atomic_inc_return(&masquerade_notifier_refcount) > 1)
-               return;
+       if (++masq_refcnt > 1)
+               goto out_unlock;
+
+       ret = register_netdevice_notifier(&masq_dev_notifier);
+       if (ret)
+               goto err_dec;
+
+       ret = register_inet6addr_notifier(&masq_inet6_notifier);
+       if (ret)
+               goto err_unregister;
 
-       register_netdevice_notifier(&masq_dev_notifier);
-       register_inet6addr_notifier(&masq_inet_notifier);
+       mutex_unlock(&masq_mutex);
+       return ret;
+
+err_unregister:
+       unregister_netdevice_notifier(&masq_dev_notifier);
+err_dec:
+       masq_refcnt--;
+out_unlock:
+       mutex_unlock(&masq_mutex);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_register_notifier);
 
 void nf_nat_masquerade_ipv6_unregister_notifier(void)
 {
+       mutex_lock(&masq_mutex);
        /* check if the notifier still has clients */
-       if (atomic_dec_return(&masquerade_notifier_refcount) > 0)
-               return;
+       if (--masq_refcnt > 0)
+               goto out_unlock;
 
-       unregister_inet6addr_notifier(&masq_inet_notifier);
+       unregister_inet6addr_notifier(&masq_inet6_notifier);
        unregister_netdevice_notifier(&masq_dev_notifier);
+out_unlock:
+       mutex_unlock(&masq_mutex);
 }
 EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_unregister_notifier);
index 2485840..b9c8a76 100644 (file)
@@ -131,6 +131,7 @@ EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_put);
 
 void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
 {
+       struct net_device *br_indev __maybe_unused;
        struct sk_buff *nskb;
        struct tcphdr _otcph;
        const struct tcphdr *otcph;
@@ -197,15 +198,18 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
         * build the eth header using the original destination's MAC as the
         * source, and send the RST packet directly.
         */
-       if (oldskb->nf_bridge) {
+       br_indev = nf_bridge_get_physindev(oldskb);
+       if (br_indev) {
                struct ethhdr *oeth = eth_hdr(oldskb);
 
-               nskb->dev = nf_bridge_get_physindev(oldskb);
+               nskb->dev = br_indev;
                nskb->protocol = htons(ETH_P_IPV6);
                ip6h->payload_len = htons(sizeof(struct tcphdr));
                if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
-                                   oeth->h_source, oeth->h_dest, nskb->len) < 0)
+                                   oeth->h_source, oeth->h_dest, nskb->len) < 0) {
+                       kfree_skb(nskb);
                        return;
+               }
                dev_queue_xmit(nskb);
        } else
 #endif
index dd0122f..e06c82e 100644 (file)
@@ -70,7 +70,9 @@ static int __init nft_masq_ipv6_module_init(void)
        if (ret < 0)
                return ret;
 
-       nf_nat_masquerade_ipv6_register_notifier();
+       ret = nf_nat_masquerade_ipv6_register_notifier();
+       if (ret)
+               nft_unregister_expr(&nft_masq_ipv6_type);
 
        return ret;
 }
index aed7eb5..5a42622 100644 (file)
@@ -657,6 +657,8 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
 
        skb->ip_summed = CHECKSUM_NONE;
 
+       skb_setup_tx_timestamp(skb, sockc->tsflags);
+
        if (flags & MSG_CONFIRM)
                skb_set_dst_pending_confirm(skb, 1);
 
index 5c3c927..aa26c45 100644 (file)
@@ -281,7 +281,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
 {
        struct net *net = container_of(fq->q.net, struct net, ipv6.frags);
        struct sk_buff *fp, *head = fq->q.fragments;
-       int    payload_len;
+       int    payload_len, delta;
        unsigned int nhoff;
        int sum_truesize;
        u8 ecn;
@@ -322,10 +322,16 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
        if (payload_len > IPV6_MAXPLEN)
                goto out_oversize;
 
+       delta = - head->truesize;
+
        /* Head of list must not be cloned. */
        if (skb_unclone(head, GFP_ATOMIC))
                goto out_oom;
 
+       delta += head->truesize;
+       if (delta)
+               add_frag_mem_limit(fq->q.net, delta);
+
        /* If the first fragment is fragmented itself, we split
         * it to two chunks: the first with data and paged part
         * and the second, holding only fragments. */
index b2447b7..194bc16 100644 (file)
@@ -2232,8 +2232,7 @@ static void ip6_link_failure(struct sk_buff *skb)
        if (rt) {
                rcu_read_lock();
                if (rt->rt6i_flags & RTF_CACHE) {
-                       if (dst_hold_safe(&rt->dst))
-                               rt6_remove_exception_rt(rt);
+                       rt6_remove_exception_rt(rt);
                } else {
                        struct fib6_info *from;
                        struct fib6_node *fn;
@@ -2360,10 +2359,13 @@ EXPORT_SYMBOL_GPL(ip6_update_pmtu);
 
 void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
 {
+       int oif = sk->sk_bound_dev_if;
        struct dst_entry *dst;
 
-       ip6_update_pmtu(skb, sock_net(sk), mtu,
-                       sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid);
+       if (!oif && skb->dev)
+               oif = l3mdev_master_ifindex(skb->dev);
+
+       ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid);
 
        dst = __sk_dst_get(sk);
        if (!dst || !dst->obsolete ||
@@ -3215,8 +3217,8 @@ static int ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
        if (cfg->fc_flags & RTF_GATEWAY &&
            !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
                goto out;
-       if (dst_hold_safe(&rt->dst))
-               rc = rt6_remove_exception_rt(rt);
+
+       rc = rt6_remove_exception_rt(rt);
 out:
        return rc;
 }
index a8854dd..8181ee7 100644 (file)
@@ -347,6 +347,7 @@ static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
                struct ipv6hdr *hdr = ipv6_hdr(skb);
                struct flowi6 fl6;
 
+               memset(&fl6, 0, sizeof(fl6));
                fl6.daddr = hdr->daddr;
                fl6.saddr = hdr->saddr;
                fl6.flowlabel = ip6_flowinfo(hdr);
index a3f5591..b81eb7c 100644 (file)
@@ -737,6 +737,7 @@ static void tcp_v6_init_req(struct request_sock *req,
                            const struct sock *sk_listener,
                            struct sk_buff *skb)
 {
+       bool l3_slave = ipv6_l3mdev_skb(TCP_SKB_CB(skb)->header.h6.flags);
        struct inet_request_sock *ireq = inet_rsk(req);
        const struct ipv6_pinfo *np = inet6_sk(sk_listener);
 
@@ -744,7 +745,7 @@ static void tcp_v6_init_req(struct request_sock *req,
        ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
 
        /* So that link locals have meaning */
-       if (!sk_listener->sk_bound_dev_if &&
+       if ((!sk_listener->sk_bound_dev_if || l3_slave) &&
            ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
                ireq->ir_iif = tcp_v6_iif(skb);
 
index e72947c..3179c42 100644 (file)
@@ -9,14 +9,15 @@
  *
  *      TCPv6 GSO/GRO support
  */
+#include <linux/indirect_call_wrapper.h>
 #include <linux/skbuff.h>
 #include <net/protocol.h>
 #include <net/tcp.h>
 #include <net/ip6_checksum.h>
 #include "ip6_offload.h"
 
-static struct sk_buff *tcp6_gro_receive(struct list_head *head,
-                                       struct sk_buff *skb)
+INDIRECT_CALLABLE_SCOPE
+struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
 {
        /* Don't bother verifying checksum if we're going to flush anyway. */
        if (!NAPI_GRO_CB(skb)->flush &&
@@ -29,7 +30,7 @@ static struct sk_buff *tcp6_gro_receive(struct list_head *head,
        return tcp_gro_receive(head, skb);
 }
 
-static int tcp6_gro_complete(struct sk_buff *skb, int thoff)
+INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
 {
        const struct ipv6hdr *iph = ipv6_hdr(skb);
        struct tcphdr *th = tcp_hdr(skb);
index dde51fc..9cbf363 100644 (file)
@@ -125,6 +125,9 @@ static int compute_score(struct sock *sk, struct net *net,
            sk->sk_family != PF_INET6)
                return -1;
 
+       if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
+               return -1;
+
        score = 0;
        inet = inet_sk(sk);
 
@@ -134,12 +137,6 @@ static int compute_score(struct sock *sk, struct net *net,
                score++;
        }
 
-       if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
-               if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
-                       return -1;
-               score++;
-       }
-
        if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
                if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr))
                        return -1;
@@ -197,66 +194,32 @@ struct sock *__udp6_lib_lookup(struct net *net,
                               int dif, int sdif, struct udp_table *udptable,
                               struct sk_buff *skb)
 {
-       struct sock *sk, *result;
        unsigned short hnum = ntohs(dport);
-       unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask);
-       struct udp_hslot *hslot2, *hslot = &udptable->hash[slot];
+       unsigned int hash2, slot2;
+       struct udp_hslot *hslot2;
+       struct sock *result;
        bool exact_dif = udp6_lib_exact_dif_match(net, skb);
-       int score, badness;
-       u32 hash = 0;
 
-       if (hslot->count > 10) {
-               hash2 = ipv6_portaddr_hash(net, daddr, hnum);
+       hash2 = ipv6_portaddr_hash(net, daddr, hnum);
+       slot2 = hash2 & udptable->mask;
+       hslot2 = &udptable->hash2[slot2];
+
+       result = udp6_lib_lookup2(net, saddr, sport,
+                                 daddr, hnum, dif, sdif, exact_dif,
+                                 hslot2, skb);
+       if (!result) {
+               hash2 = ipv6_portaddr_hash(net, &in6addr_any, hnum);
                slot2 = hash2 & udptable->mask;
+
                hslot2 = &udptable->hash2[slot2];
-               if (hslot->count < hslot2->count)
-                       goto begin;
 
                result = udp6_lib_lookup2(net, saddr, sport,
-                                         daddr, hnum, dif, sdif, exact_dif,
-                                         hslot2, skb);
-               if (!result) {
-                       unsigned int old_slot2 = slot2;
-                       hash2 = ipv6_portaddr_hash(net, &in6addr_any, hnum);
-                       slot2 = hash2 & udptable->mask;
-                       /* avoid searching the same slot again. */
-                       if (unlikely(slot2 == old_slot2))
-                               return result;
-
-                       hslot2 = &udptable->hash2[slot2];
-                       if (hslot->count < hslot2->count)
-                               goto begin;
-
-                       result = udp6_lib_lookup2(net, saddr, sport,
-                                                 daddr, hnum, dif, sdif,
-                                                 exact_dif, hslot2,
-                                                 skb);
-               }
-               if (unlikely(IS_ERR(result)))
-                       return NULL;
-               return result;
-       }
-begin:
-       result = NULL;
-       badness = -1;
-       sk_for_each_rcu(sk, &hslot->head) {
-               score = compute_score(sk, net, saddr, sport, daddr, hnum, dif,
-                                     sdif, exact_dif);
-               if (score > badness) {
-                       if (sk->sk_reuseport) {
-                               hash = udp6_ehashfn(net, daddr, hnum,
-                                                   saddr, sport);
-                               result = reuseport_select_sock(sk, hash, skb,
-                                                       sizeof(struct udphdr));
-                               if (unlikely(IS_ERR(result)))
-                                       return NULL;
-                               if (result)
-                                       return result;
-                       }
-                       result = sk;
-                       badness = score;
-               }
+                                         &in6addr_any, hnum, dif, sdif,
+                                         exact_dif, hslot2,
+                                         skb);
        }
+       if (unlikely(IS_ERR(result)))
+               return NULL;
        return result;
 }
 EXPORT_SYMBOL_GPL(__udp6_lib_lookup);
@@ -448,7 +411,7 @@ csum_copy_err:
 DEFINE_STATIC_KEY_FALSE(udpv6_encap_needed_key);
 void udpv6_encap_enable(void)
 {
-       static_branch_enable(&udpv6_encap_needed_key);
+       static_branch_inc(&udpv6_encap_needed_key);
 }
 EXPORT_SYMBOL(udpv6_encap_enable);
 
@@ -1579,7 +1542,7 @@ void udpv6_destroy_sock(struct sock *sk)
                                encap_destroy(sk);
                }
                if (up->encap_enabled)
-                       static_branch_disable(&udpv6_encap_needed_key);
+                       static_branch_dec(&udpv6_encap_needed_key);
        }
 
        inet6_destroy_sock(sk);
index 828b245..83b11d0 100644 (file)
@@ -11,6 +11,7 @@
  */
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
+#include <linux/indirect_call_wrapper.h>
 #include <net/protocol.h>
 #include <net/ipv6.h>
 #include <net/udp.h>
@@ -114,8 +115,8 @@ out:
        return segs;
 }
 
-static struct sk_buff *udp6_gro_receive(struct list_head *head,
-                                       struct sk_buff *skb)
+INDIRECT_CALLABLE_SCOPE
+struct sk_buff *udp6_gro_receive(struct list_head *head, struct sk_buff *skb)
 {
        struct udphdr *uh = udp_gro_udphdr(skb);
 
@@ -142,7 +143,7 @@ flush:
        return NULL;
 }
 
-static int udp6_gro_complete(struct sk_buff *skb, int nhoff)
+INDIRECT_CALLABLE_SCOPE int udp6_gro_complete(struct sk_buff *skb, int nhoff)
 {
        const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
        struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
index 9ef490d..a52cb3f 100644 (file)
@@ -86,14 +86,16 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
 {
        struct net *net = dev_net(skb->dev);
        struct xfrm_state *x = NULL;
+       struct sec_path *sp;
        int i = 0;
 
-       if (secpath_set(skb)) {
+       sp = secpath_set(skb);
+       if (!sp) {
                XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
                goto drop;
        }
 
-       if (1 + skb->sp->len == XFRM_MAX_DEPTH) {
+       if (1 + sp->len == XFRM_MAX_DEPTH) {
                XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
                goto drop;
        }
@@ -145,7 +147,7 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
                goto drop;
        }
 
-       skb->sp->xvec[skb->sp->len++] = x;
+       sp->xvec[sp->len++] = x;
 
        spin_lock(&x->lock);
 
index d35bcf9..769f8f7 100644 (file)
@@ -262,7 +262,6 @@ static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
        if (xdst->u.rt6.rt6i_idev->dev == dev) {
                struct inet6_dev *loopback_idev =
                        in6_dev_get(dev_net(dev)->loopback_dev);
-               BUG_ON(!loopback_idev);
 
                do {
                        in6_dev_put(xdst->u.rt6.rt6i_idev);
index 4a46df8..f5b4feb 100644 (file)
@@ -144,6 +144,9 @@ static u32 __xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr)
                index = __xfrm6_tunnel_spi_check(net, spi);
                if (index >= 0)
                        goto alloc_spi;
+
+               if (spi == XFRM6_TUNNEL_SPI_MAX)
+                       break;
        }
        for (spi = XFRM6_TUNNEL_SPI_MIN; spi < xfrm6_tn->spi; spi++) {
                index = __xfrm6_tunnel_spi_check(net, spi);
index 9d61266..655c787 100644 (file)
@@ -2020,7 +2020,7 @@ parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol)
 
 static inline int pfkey_xfrm_policy2sec_ctx_size(const struct xfrm_policy *xp)
 {
-  struct xfrm_sec_ctx *xfrm_ctx = xp->security;
+       struct xfrm_sec_ctx *xfrm_ctx = xp->security;
 
        if (xfrm_ctx) {
                int len = sizeof(struct sadb_x_sec_ctx);
index 82cdf90..26f1d43 100644 (file)
@@ -1490,12 +1490,7 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
                        goto err_sock;
        }
 
-       sk = sock->sk;
-
-       sock_hold(sk);
-       tunnel->sock = sk;
        tunnel->l2tp_net = net;
-
        pn = l2tp_pernet(net);
 
        spin_lock_bh(&pn->l2tp_tunnel_list_lock);
@@ -1510,6 +1505,10 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
        list_add_rcu(&tunnel->list, &pn->l2tp_tunnel_list);
        spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
 
+       sk = sock->sk;
+       sock_hold(sk);
+       tunnel->sock = sk;
+
        if (tunnel->encap == L2TP_ENCAPTYPE_UDP) {
                struct udp_tunnel_sock_cfg udp_cfg = {
                        .sk_user_data = tunnel,
index 8da86ce..309dee7 100644 (file)
@@ -46,6 +46,24 @@ int l3mdev_master_ifindex_rcu(const struct net_device *dev)
 }
 EXPORT_SYMBOL_GPL(l3mdev_master_ifindex_rcu);
 
+/**
+ *     l3mdev_master_upper_ifindex_by_index - get index of upper l3 master
+ *                                            device
+ *     @net: network namespace for device index lookup
+ *     @ifindex: targeted interface
+ */
+int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex)
+{
+       struct net_device *dev;
+
+       dev = dev_get_by_index_rcu(net, ifindex);
+       while (dev && !netif_is_l3_master(dev))
+               dev = netdev_master_upper_dev_get(dev);
+
+       return dev ? dev->ifindex : 0;
+}
+EXPORT_SYMBOL_GPL(l3mdev_master_upper_ifindex_by_index_rcu);
+
 /**
  *     l3mdev_fib_table - get FIB table id associated with an L3
  *                             master interface
index f869e35..be471fe 100644 (file)
@@ -57,14 +57,13 @@ comment "Some wireless drivers require a rate control algorithm"
        depends on MAC80211 && MAC80211_HAS_RC=n
 
 config MAC80211_MESH
-       bool "Enable mac80211 mesh networking (pre-802.11s) support"
+       bool "Enable mac80211 mesh networking support"
        depends on MAC80211
        ---help---
-        This options enables support of Draft 802.11s mesh networking.
-        The implementation is based on Draft 2.08 of the Mesh Networking
-        amendment.  However, no compliance with that draft is claimed or even
-        possible, as drafts leave a number of identifiers to be defined after
-        ratification.  For more information visit http://o11s.org/.
+         Select this option to enable 802.11 mesh operation in mac80211
+         drivers that support it.  802.11 mesh connects multiple stations
+         over (possibly multi-hop) wireless links to form a single logical
+         LAN.
 
 config MAC80211_LEDS
        bool "Enable LED triggers"
index 5162233..de65fe3 100644 (file)
@@ -800,8 +800,8 @@ static int ieee80211_set_ftm_responder_params(
        u8 *pos;
        int len;
 
-       if ((!lci || !lci_len) && (!civicloc || !civicloc_len))
-               return 1;
+       if (!lci_len && !civicloc_len)
+               return 0;
 
        bss_conf = &sdata->vif.bss_conf;
        old = bss_conf->ftmr_params;
@@ -2028,6 +2028,9 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
                        nconf->dot11MeshAwakeWindowDuration;
        if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask))
                conf->plink_timeout = nconf->plink_timeout;
+       if (_chg_mesh_attr(NL80211_MESHCONF_CONNECTED_TO_GATE, mask))
+               conf->dot11MeshConnectedToMeshGate =
+                       nconf->dot11MeshConnectedToMeshGate;
        ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
        return 0;
 }
@@ -2891,7 +2894,7 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
 
        len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
              beacon->proberesp_ies_len + beacon->assocresp_ies_len +
-             beacon->probe_resp_len;
+             beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len;
 
        new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
        if (!new_beacon)
@@ -2934,8 +2937,9 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
                memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
                pos += beacon->probe_resp_len;
        }
-       if (beacon->ftm_responder)
-               new_beacon->ftm_responder = beacon->ftm_responder;
+
+       /* might copy -1, meaning no changes requested */
+       new_beacon->ftm_responder = beacon->ftm_responder;
        if (beacon->lci) {
                new_beacon->lci_len = beacon->lci_len;
                new_beacon->lci = pos;
@@ -3849,6 +3853,26 @@ ieee80211_get_ftm_responder_stats(struct wiphy *wiphy,
        return drv_get_ftm_responder_stats(local, sdata, ftm_stats);
 }
 
+static int
+ieee80211_start_pmsr(struct wiphy *wiphy, struct wireless_dev *dev,
+                    struct cfg80211_pmsr_request *request)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev);
+
+       return drv_start_pmsr(local, sdata, request);
+}
+
+static void
+ieee80211_abort_pmsr(struct wiphy *wiphy, struct wireless_dev *dev,
+                    struct cfg80211_pmsr_request *request)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev);
+
+       return drv_abort_pmsr(local, sdata, request);
+}
+
 const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -3944,4 +3968,6 @@ const struct cfg80211_ops mac80211_config_ops = {
        .tx_control_port = ieee80211_tx_control_port,
        .get_txq_stats = ieee80211_get_txq_stats,
        .get_ftm_responder_stats = ieee80211_get_ftm_responder_stats,
+       .start_pmsr = ieee80211_start_pmsr,
+       .abort_pmsr = ieee80211_abort_pmsr,
 };
index c813207..cff0fb3 100644 (file)
@@ -641,6 +641,8 @@ IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval,
 IEEE80211_IF_FILE(power_mode, u.mesh.mshcfg.power_mode, DEC);
 IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,
                  u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC);
+IEEE80211_IF_FILE(dot11MeshConnectedToMeshGate,
+                 u.mesh.mshcfg.dot11MeshConnectedToMeshGate, DEC);
 #endif
 
 #define DEBUGFS_ADD_MODE(name, mode) \
@@ -762,6 +764,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
        MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval);
        MESHPARAMS_ADD(power_mode);
        MESHPARAMS_ADD(dot11MeshAwakeWindowDuration);
+       MESHPARAMS_ADD(dot11MeshConnectedToMeshGate);
 #undef MESHPARAMS_ADD
 }
 #endif
index af5185a..b753194 100644 (file)
@@ -795,22 +795,22 @@ static ssize_t sta_he_capa_read(struct file *file, char __user *userbuf,
 
 #define PRINT_NSS_SUPP(f, n)                                           \
        do {                                                            \
-               int i;                                                  \
+               int _i;                                                 \
                u16 v = le16_to_cpu(nss->f);                            \
                p += scnprintf(p, buf_sz + buf - p, n ": %#.4x\n", v);  \
-               for (i = 0; i < 8; i += 2) {                            \
-                       switch ((v >> i) & 0x3) {                       \
+               for (_i = 0; _i < 8; _i += 2) {                         \
+                       switch ((v >> _i) & 0x3) {                      \
                        case 0:                                         \
-                               PRINT(n "-%d-SUPPORT-0-7", i / 2);      \
+                               PRINT(n "-%d-SUPPORT-0-7", _i / 2);     \
                                break;                                  \
                        case 1:                                         \
-                               PRINT(n "-%d-SUPPORT-0-9", i / 2);      \
+                               PRINT(n "-%d-SUPPORT-0-9", _i / 2);     \
                                break;                                  \
                        case 2:                                         \
-                               PRINT(n "-%d-SUPPORT-0-11", i / 2);     \
+                               PRINT(n "-%d-SUPPORT-0-11", _i / 2);    \
                                break;                                  \
                        case 3:                                         \
-                               PRINT(n "-%d-NOT-SUPPORTED", i / 2);    \
+                               PRINT(n "-%d-NOT-SUPPORTED", _i / 2);   \
                                break;                                  \
                        }                                               \
                }                                                       \
index 0b1747a..3e0d592 100644 (file)
@@ -1199,6 +1199,40 @@ drv_get_ftm_responder_stats(struct ieee80211_local *local,
        return ret;
 }
 
+static inline int drv_start_pmsr(struct ieee80211_local *local,
+                                struct ieee80211_sub_if_data *sdata,
+                                struct cfg80211_pmsr_request *request)
+{
+       int ret = -EOPNOTSUPP;
+
+       might_sleep();
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
+
+       trace_drv_start_pmsr(local, sdata);
+
+       if (local->ops->start_pmsr)
+               ret = local->ops->start_pmsr(&local->hw, &sdata->vif, request);
+       trace_drv_return_int(local, ret);
+
+       return ret;
+}
+
+static inline void drv_abort_pmsr(struct ieee80211_local *local,
+                                 struct ieee80211_sub_if_data *sdata,
+                                 struct cfg80211_pmsr_request *request)
+{
+       trace_drv_abort_pmsr(local, sdata);
+
+       might_sleep();
+       if (!check_sdata_in_driver(sdata))
+               return;
+
+       if (local->ops->abort_pmsr)
+               local->ops->abort_pmsr(&local->hw, &sdata->vif, request);
+       trace_drv_return_void(local);
+}
+
 static inline int drv_start_nan(struct ieee80211_local *local,
                                struct ieee80211_sub_if_data *sdata,
                                struct cfg80211_nan_conf *conf)
index 10a0506..7dfb4e2 100644 (file)
@@ -500,6 +500,7 @@ struct ieee80211_if_managed {
        unsigned int uapsd_max_sp_len;
 
        int wmm_last_param_set;
+       int mu_edca_last_param_set;
 
        u8 use_4addr;
 
index 5836dde..4a6ff14 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (c) 2016        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 the GNU General Public License version 2 as
@@ -1015,6 +1016,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        if (local->open_count == 0)
                ieee80211_clear_tx_pending(local);
 
+       sdata->vif.bss_conf.beacon_int = 0;
+
        /*
         * If the interface goes down while suspended, presumably because
         * the device was unplugged and that happens before our resume,
@@ -1799,7 +1802,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                }
 
                ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
-               if (params && is_valid_ether_addr(params->macaddr))
+               if (is_valid_ether_addr(params->macaddr))
                        memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
                else
                        memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
@@ -1868,11 +1871,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
        ieee80211_setup_sdata(sdata, type);
 
        if (ndev) {
-               if (params) {
-                       ndev->ieee80211_ptr->use_4addr = params->use_4addr;
-                       if (type == NL80211_IFTYPE_STATION)
-                               sdata->u.mgd.use_4addr = params->use_4addr;
-               }
+               ndev->ieee80211_ptr->use_4addr = params->use_4addr;
+               if (type == NL80211_IFTYPE_STATION)
+                       sdata->u.mgd.use_4addr = params->use_4addr;
 
                ndev->features |= local->hw.netdev_features;
 
@@ -1949,6 +1950,8 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)
        WARN(local->open_count, "%s: open count remains %d\n",
             wiphy_name(local->hw.wiphy), local->open_count);
 
+       ieee80211_txq_teardown_flows(local);
+
        mutex_lock(&local->iflist_mtx);
        list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
                list_del(&sdata->list);
index 83e71e6..87a7299 100644 (file)
@@ -1221,8 +1221,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        /* add one default STA interface if supported */
        if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
            !ieee80211_hw_check(hw, NO_AUTO_VIF)) {
+               struct vif_params params = {0};
+
                result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
-                                         NL80211_IFTYPE_STATION, NULL);
+                                         NL80211_IFTYPE_STATION, &params);
                if (result)
                        wiphy_warn(local->hw.wiphy,
                                   "Failed to add default virtual iface\n");
@@ -1262,7 +1264,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        rtnl_unlock();
        ieee80211_led_exit(local);
        ieee80211_wep_free(local);
-       ieee80211_txq_teardown_flows(local);
  fail_flows:
        destroy_workqueue(local->workqueue);
  fail_workqueue:
@@ -1288,7 +1289,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 #if IS_ENABLED(CONFIG_IPV6)
        unregister_inet6addr_notifier(&local->ifa6_notifier);
 #endif
-       ieee80211_txq_teardown_flows(local);
 
        rtnl_lock();
 
index 8bad414..c90452a 100644 (file)
@@ -254,6 +254,9 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        u8 *pos, neighbors;
        u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie);
+       bool is_connected_to_gate = ifmsh->num_gates > 0 ||
+               ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol ||
+               ifmsh->mshcfg.dot11MeshConnectedToMeshGate;
 
        if (skb_tailroom(skb) < 2 + meshconf_len)
                return -ENOMEM;
@@ -278,7 +281,7 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
        /* Mesh Formation Info - number of neighbors */
        neighbors = atomic_read(&ifmsh->estab_plinks);
        neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
-       *pos++ = neighbors << 1;
+       *pos++ = (neighbors << 1) | is_connected_to_gate;
        /* Mesh capability */
        *pos = 0x00;
        *pos |= ifmsh->mshcfg.dot11MeshForwarding ?
@@ -1191,7 +1194,8 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
                if (!sdata->u.mesh.user_mpm ||
                    sdata->u.mesh.mshcfg.rssi_threshold == 0 ||
                    sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal)
-                       mesh_neighbour_update(sdata, mgmt->sa, &elems);
+                       mesh_neighbour_update(sdata, mgmt->sa, &elems,
+                                             rx_status);
        }
 
        if (ifmsh->sync_ops)
index 2152663..cad6592 100644 (file)
@@ -273,7 +273,8 @@ int mesh_gate_num(struct ieee80211_sub_if_data *sdata);
 
 /* Mesh plinks */
 void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
-                          u8 *hw_addr, struct ieee802_11_elems *ie);
+                          u8 *hw_addr, struct ieee802_11_elems *ie,
+                          struct ieee80211_rx_status *rx_status);
 bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie);
 u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);
 void mesh_plink_timer(struct timer_list *t);
index 5b5b0f9..33055c8 100644 (file)
@@ -513,7 +513,8 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
 
 static struct sta_info *
 mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
-                   struct ieee802_11_elems *elems)
+                   struct ieee802_11_elems *elems,
+                   struct ieee80211_rx_status *rx_status)
 {
        struct sta_info *sta = NULL;
 
@@ -521,11 +522,17 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
        if (sdata->u.mesh.user_mpm ||
            sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) {
                if (mesh_peer_accepts_plinks(elems) &&
-                   mesh_plink_availables(sdata))
+                   mesh_plink_availables(sdata)) {
+                       int sig = 0;
+
+                       if (ieee80211_hw_check(&sdata->local->hw, SIGNAL_DBM))
+                               sig = rx_status->signal;
+
                        cfg80211_notify_new_peer_candidate(sdata->dev, addr,
                                                           elems->ie_start,
                                                           elems->total_len,
-                                                          GFP_KERNEL);
+                                                          sig, GFP_KERNEL);
+               }
        } else
                sta = __mesh_sta_info_alloc(sdata, addr);
 
@@ -538,13 +545,15 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
  * @sdata: local meshif
  * @addr: peer's address
  * @elems: IEs from beacon or mesh peering frame.
+ * @rx_status: rx status for the frame for signal reporting
  *
  * Return existing or newly allocated sta_info under RCU read lock.
  * (re)initialize with given IEs.
  */
 static struct sta_info *
 mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
-                 u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU)
+                 u8 *addr, struct ieee802_11_elems *elems,
+                 struct ieee80211_rx_status *rx_status) __acquires(RCU)
 {
        struct sta_info *sta = NULL;
 
@@ -555,7 +564,7 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
        } else {
                rcu_read_unlock();
                /* can't run atomic */
-               sta = mesh_sta_info_alloc(sdata, addr, elems);
+               sta = mesh_sta_info_alloc(sdata, addr, elems, rx_status);
                if (!sta) {
                        rcu_read_lock();
                        return NULL;
@@ -576,20 +585,25 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
  * @sdata: local meshif
  * @addr: peer's address
  * @elems: IEs from beacon or mesh peering frame
+ * @rx_status: rx status for the frame for signal reporting
  *
  * Initiates peering if appropriate.
  */
 void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
                           u8 *hw_addr,
-                          struct ieee802_11_elems *elems)
+                          struct ieee802_11_elems *elems,
+                          struct ieee80211_rx_status *rx_status)
 {
        struct sta_info *sta;
        u32 changed = 0;
 
-       sta = mesh_sta_info_get(sdata, hw_addr, elems);
+       sta = mesh_sta_info_get(sdata, hw_addr, elems, rx_status);
        if (!sta)
                goto out;
 
+       sta->mesh->connected_to_gate = elems->mesh_config->meshconf_form &
+               IEEE80211_MESHCONF_FORM_CONNECTED_TO_GATE;
+
        if (mesh_peer_accepts_plinks(elems) &&
            sta->mesh->plink_state == NL80211_PLINK_LISTEN &&
            sdata->u.mesh.accepting_plinks &&
@@ -1069,7 +1083,8 @@ out:
 static void
 mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
                         struct ieee80211_mgmt *mgmt,
-                        struct ieee802_11_elems *elems)
+                        struct ieee802_11_elems *elems,
+                        struct ieee80211_rx_status *rx_status)
 {
 
        struct sta_info *sta;
@@ -1134,7 +1149,7 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
        if (event == OPN_ACPT) {
                rcu_read_unlock();
                /* allocate sta entry if necessary and update info */
-               sta = mesh_sta_info_get(sdata, mgmt->sa, elems);
+               sta = mesh_sta_info_get(sdata, mgmt->sa, elems, rx_status);
                if (!sta) {
                        mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
                        goto unlock_rcu;
@@ -1200,5 +1215,5 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
                        return;
        }
        ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems);
-       mesh_process_plink_frame(sdata, mgmt, &elems);
+       mesh_process_plink_frame(sdata, mgmt, &elems, rx_status);
 }
index d2bc8d5..6878215 100644 (file)
@@ -916,6 +916,15 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                ieee80211_add_vht_ie(sdata, skb, sband,
                                     &assoc_data->ap_vht_cap);
 
+       /*
+        * If AP doesn't support HT, mark HE as disabled.
+        * If on the 5GHz band, make sure it supports VHT.
+        */
+       if (ifmgd->flags & IEEE80211_STA_DISABLE_HT ||
+           (sband->band == NL80211_BAND_5GHZ &&
+            ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
+               ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
                ieee80211_add_he_ie(sdata, skb, sband);
 
@@ -1869,7 +1878,7 @@ ieee80211_sta_wmm_params(struct ieee80211_local *local,
        struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS];
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        size_t left;
-       int count, ac;
+       int count, mu_edca_count, ac;
        const u8 *pos;
        u8 uapsd_queues = 0;
 
@@ -1889,9 +1898,16 @@ ieee80211_sta_wmm_params(struct ieee80211_local *local,
                uapsd_queues = ifmgd->uapsd_queues;
 
        count = wmm_param[6] & 0x0f;
-       if (count == ifmgd->wmm_last_param_set)
+       /* -1 is the initial value of ifmgd->mu_edca_last_param_set.
+        * if mu_edca was preset before and now it disappeared tell
+        * the driver about it.
+        */
+       mu_edca_count = mu_edca ? mu_edca->mu_qos_info & 0x0f : -1;
+       if (count == ifmgd->wmm_last_param_set &&
+           mu_edca_count == ifmgd->mu_edca_last_param_set)
                return false;
        ifmgd->wmm_last_param_set = count;
+       ifmgd->mu_edca_last_param_set = mu_edca_count;
 
        pos = wmm_param + 8;
        left = wmm_param_len - 8;
@@ -2766,6 +2782,7 @@ static bool ieee80211_mark_sta_auth(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct sta_info *sta;
+       bool result = true;
 
        sdata_info(sdata, "authenticated\n");
        ifmgd->auth_data->done = true;
@@ -2778,15 +2795,18 @@ static bool ieee80211_mark_sta_auth(struct ieee80211_sub_if_data *sdata,
        sta = sta_info_get(sdata, bssid);
        if (!sta) {
                WARN_ONCE(1, "%s: STA %pM not found", sdata->name, bssid);
-               return false;
+               result = false;
+               goto out;
        }
        if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) {
                sdata_info(sdata, "failed moving %pM to auth\n", bssid);
-               return false;
+               result = false;
+               goto out;
        }
-       mutex_unlock(&sdata->local->sta_mtx);
 
-       return true;
+out:
+       mutex_unlock(&sdata->local->sta_mtx);
+       return result;
 }
 
 static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
@@ -3058,6 +3078,19 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
        }
 }
 
+static bool ieee80211_twt_req_supported(const struct sta_info *sta,
+                                       const struct ieee802_11_elems *elems)
+{
+       if (elems->ext_capab_len < 10)
+               return false;
+
+       if (!(elems->ext_capab[9] & WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT))
+               return false;
+
+       return sta->sta.he_cap.he_cap_elem.mac_cap_info[0] &
+               IEEE80211_HE_MAC_CAP0_TWT_RES;
+}
+
 static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                                    struct cfg80211_bss *cbss,
                                    struct ieee80211_mgmt *mgmt, size_t len)
@@ -3211,16 +3244,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
-       /*
-        * If AP doesn't support HT, or it doesn't have HE mandatory IEs, mark
-        * HE as disabled. If on the 5GHz band, make sure it supports VHT.
-        */
-       if (ifmgd->flags & IEEE80211_STA_DISABLE_HT ||
-           (sband->band == NL80211_BAND_5GHZ &&
-            ifmgd->flags & IEEE80211_STA_DISABLE_VHT) ||
-           (!elems.he_cap && !elems.he_operation))
-               ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
-
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
            (!elems.he_cap || !elems.he_operation)) {
                mutex_unlock(&sdata->local->sta_mtx);
@@ -3247,8 +3270,11 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                                                  sta);
 
                bss_conf->he_support = sta->sta.he_cap.has_he;
+               bss_conf->twt_requester =
+                       ieee80211_twt_req_supported(sta, &elems);
        } else {
                bss_conf->he_support = false;
+               bss_conf->twt_requester = false;
        }
 
        if (bss_conf->he_support) {
@@ -3333,6 +3359,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
         * 4-bit value.
         */
        ifmgd->wmm_last_param_set = -1;
+       ifmgd->mu_edca_last_param_set = -1;
 
        if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
                ieee80211_set_wmm_default(sdata, false, false);
@@ -4656,8 +4683,10 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
-           ieee80211_get_he_sta_cap(sband)) {
+       if (!ieee80211_get_he_sta_cap(sband))
+               ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) {
                const struct cfg80211_bss_ies *ies;
                const u8 *he_oper_ie;
 
index 3bd3b57..45aad3d 100644 (file)
@@ -143,6 +143,9 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
        /* allocate extra bitmaps */
        if (status->chains)
                len += 4 * hweight8(status->chains);
+       /* vendor presence bitmap */
+       if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)
+               len += 4;
 
        if (ieee80211_have_rx_timestamp(status)) {
                len = ALIGN(len, 8);
@@ -207,8 +210,6 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
        if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
                struct ieee80211_vendor_radiotap *rtap = (void *)skb->data;
 
-               /* vendor presence bitmap */
-               len += 4;
                /* alignment for fixed 6-byte vendor data header */
                len = ALIGN(len, 2);
                /* vendor data header */
@@ -753,6 +754,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
        struct ieee80211_sub_if_data *monitor_sdata =
                rcu_dereference(local->monitor_sdata);
        bool only_monitor = false;
+       unsigned int min_head_len;
 
        if (status->flag & RX_FLAG_RADIOTAP_HE)
                rtap_space += sizeof(struct ieee80211_radiotap_he);
@@ -760,12 +762,18 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
        if (status->flag & RX_FLAG_RADIOTAP_HE_MU)
                rtap_space += sizeof(struct ieee80211_radiotap_he_mu);
 
+       if (status->flag & RX_FLAG_RADIOTAP_LSIG)
+               rtap_space += sizeof(struct ieee80211_radiotap_lsig);
+
        if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
-               struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
+               struct ieee80211_vendor_radiotap *rtap =
+                       (void *)(origskb->data + rtap_space);
 
                rtap_space += sizeof(*rtap) + rtap->len + rtap->pad;
        }
 
+       min_head_len = rtap_space;
+
        /*
         * First, we may need to make a copy of the skb because
         *  (1) we need to modify it for radiotap (if not present), and
@@ -775,18 +783,23 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
         * the SKB because it has a bad FCS/PLCP checksum.
         */
 
-       if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) {
-               if (unlikely(origskb->len <= FCS_LEN)) {
-                       /* driver bug */
-                       WARN_ON(1);
-                       dev_kfree_skb(origskb);
-                       return NULL;
+       if (!(status->flag & RX_FLAG_NO_PSDU)) {
+               if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) {
+                       if (unlikely(origskb->len <= FCS_LEN + rtap_space)) {
+                               /* driver bug */
+                               WARN_ON(1);
+                               dev_kfree_skb(origskb);
+                               return NULL;
+                       }
+                       present_fcs_len = FCS_LEN;
                }
-               present_fcs_len = FCS_LEN;
+
+               /* also consider the hdr->frame_control */
+               min_head_len += 2;
        }
 
-       /* ensure hdr->frame_control and vendor radiotap data are in skb head */
-       if (!pskb_may_pull(origskb, 2 + rtap_space)) {
+       /* ensure that the expected data elements are in skb head */
+       if (!pskb_may_pull(origskb, min_head_len)) {
                dev_kfree_skb(origskb);
                return NULL;
        }
@@ -1403,6 +1416,7 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx)
                return RX_CONTINUE;
 
        if (ieee80211_is_ctl(hdr->frame_control) ||
+           ieee80211_is_nullfunc(hdr->frame_control) ||
            ieee80211_is_qos_nullfunc(hdr->frame_control) ||
            is_multicast_ether_addr(hdr->addr1))
                return RX_CONTINUE;
@@ -3063,7 +3077,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                        cfg80211_sta_opmode_change_notify(sdata->dev,
                                                          rx->sta->addr,
                                                          &sta_opmode,
-                                                         GFP_KERNEL);
+                                                         GFP_ATOMIC);
                        goto handled;
                }
                case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
@@ -3100,7 +3114,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                        cfg80211_sta_opmode_change_notify(sdata->dev,
                                                          rx->sta->addr,
                                                          &sta_opmode,
-                                                         GFP_KERNEL);
+                                                         GFP_ATOMIC);
                        goto handled;
                }
                default:
index 5d2a117..9541341 100644 (file)
@@ -356,7 +356,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
 static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       bool hw_scan = local->ops->hw_scan;
+       bool hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
        bool was_scanning = local->scanning;
        struct cfg80211_scan_request *scan_req;
        struct ieee80211_sub_if_data *scan_sdata;
@@ -606,6 +606,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                                  struct cfg80211_scan_request *req)
 {
        struct ieee80211_local *local = sdata->local;
+       bool hw_scan = local->ops->hw_scan;
        int rc;
 
        lockdep_assert_held(&local->mtx);
@@ -620,7 +621,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                return 0;
        }
 
-       if (local->ops->hw_scan) {
+ again:
+       if (hw_scan) {
                u8 *ies;
 
                local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
@@ -679,7 +681,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
        else
                memcpy(local->scan_addr, sdata->vif.addr, ETH_ALEN);
 
-       if (local->ops->hw_scan) {
+       if (hw_scan) {
                __set_bit(SCAN_HW_SCANNING, &local->scanning);
        } else if ((req->n_channels == 1) &&
                   (req->channels[0] == local->_oper_chandef.chan)) {
@@ -722,7 +724,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_recalc_idle(local);
 
-       if (local->ops->hw_scan) {
+       if (hw_scan) {
                WARN_ON(!ieee80211_prep_hw_scan(local));
                rc = drv_hw_scan(local, sdata, local->hw_scan_req);
        } else {
@@ -740,6 +742,18 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                RCU_INIT_POINTER(local->scan_sdata, NULL);
        }
 
+       if (hw_scan && rc == 1) {
+               /*
+                * we can't fall back to software for P2P-GO
+                * as it must update NoA etc.
+                */
+               if (ieee80211_vif_type_p2p(&sdata->vif) ==
+                               NL80211_IFTYPE_P2P_GO)
+                       return -EOPNOTSUPP;
+               hw_scan = false;
+               goto again;
+       }
+
        return rc;
 }
 
index fb8c225..c4a8f11 100644 (file)
@@ -2253,11 +2253,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
        }
 
        if (tidstats && !cfg80211_sinfo_alloc_tid_stats(sinfo, GFP_KERNEL)) {
-               for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
-                       struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i];
-
-                       sta_set_tidstats(sta, tidstats, i);
-               }
+               for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
+                       sta_set_tidstats(sta, &sinfo->pertid[i], i);
        }
 
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
@@ -2267,7 +2264,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
                                 BIT_ULL(NL80211_STA_INFO_PLINK_STATE) |
                                 BIT_ULL(NL80211_STA_INFO_LOCAL_PM) |
                                 BIT_ULL(NL80211_STA_INFO_PEER_PM) |
-                                BIT_ULL(NL80211_STA_INFO_NONPEER_PM);
+                                BIT_ULL(NL80211_STA_INFO_NONPEER_PM) |
+                                BIT_ULL(NL80211_STA_INFO_CONNECTED_TO_GATE);
 
                sinfo->llid = sta->mesh->llid;
                sinfo->plid = sta->mesh->plid;
@@ -2279,6 +2277,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
                sinfo->local_pm = sta->mesh->local_pm;
                sinfo->peer_pm = sta->mesh->peer_pm;
                sinfo->nonpeer_pm = sta->mesh->nonpeer_pm;
+               sinfo->connected_to_gate = sta->mesh->connected_to_gate;
 #endif
        }
 
index 9a04327..8eb2904 100644 (file)
@@ -364,6 +364,7 @@ DECLARE_EWMA(mesh_fail_avg, 20, 8)
  * @nonpeer_pm: STA power save mode towards non-peer neighbors
  * @processed_beacon: set to true after peer rates and capabilities are
  *     processed
+ * @connected_to_gate: true if mesh STA has a path to a mesh gate
  * @fail_avg: moving percentage of failed MSDUs
  */
 struct mesh_sta {
@@ -381,6 +382,7 @@ struct mesh_sta {
        u8 plink_retries;
 
        bool processed_beacon;
+       bool connected_to_gate;
 
        enum nl80211_plink_state plink_state;
        u32 plink_timeout;
index aa4afbf..3f0b96e 100644 (file)
@@ -556,6 +556,11 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
        }
 
        ieee80211_led_tx(local);
+
+       if (skb_has_frag_list(skb)) {
+               kfree_skb_list(skb_shinfo(skb)->frag_list);
+               skb_shinfo(skb)->frag_list = NULL;
+       }
 }
 
 /*
@@ -964,6 +969,8 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
                        /* Track when last TDLS packet was ACKed */
                        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
                                sta->status_stats.last_tdls_pkt_time = jiffies;
+               } else if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
+                       return;
                } else {
                        ieee80211_lost_packet(sta, info);
                }
index 588c51a..35ea0dc 100644 (file)
@@ -1052,10 +1052,10 @@ TRACE_EVENT(drv_ampdu_action,
 );
 
 TRACE_EVENT(drv_get_survey,
-       TP_PROTO(struct ieee80211_local *local, int idx,
+       TP_PROTO(struct ieee80211_local *local, int _idx,
                 struct survey_info *survey),
 
-       TP_ARGS(local, idx, survey),
+       TP_ARGS(local, _idx, survey),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
@@ -1064,7 +1064,7 @@ TRACE_EVENT(drv_get_survey,
 
        TP_fast_assign(
                LOCAL_ASSIGN;
-               __entry->idx = idx;
+               __entry->idx = _idx;
        ),
 
        TP_printk(
@@ -1882,6 +1882,18 @@ TRACE_EVENT(drv_del_nan_func,
        )
 );
 
+DEFINE_EVENT(local_sdata_evt, drv_start_pmsr,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+       TP_ARGS(local, sdata)
+);
+
+DEFINE_EVENT(local_sdata_evt, drv_abort_pmsr,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+       TP_ARGS(local, sdata)
+);
+
 /*
  * Tracing for API calls that drivers call.
  */
index e0ccee2..f170d6c 100644 (file)
@@ -439,8 +439,8 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
        if (ieee80211_hw_check(&tx->local->hw, QUEUE_CONTROL))
                info->hw_queue = tx->sdata->vif.cab_queue;
 
-       /* no stations in PS mode */
-       if (!atomic_read(&ps->num_sta_ps))
+       /* no stations in PS mode and no buffered packets */
+       if (!atomic_read(&ps->num_sta_ps) && skb_queue_empty(&ps->bc_buf))
                return TX_CONTINUE;
 
        info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
@@ -3218,6 +3218,9 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
        if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
                return false;
 
+       if (skb_is_gso(skb))
+               return false;
+
        if (!txq)
                return false;
 
@@ -3242,7 +3245,7 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
        tin = &txqi->tin;
        flow = fq_flow_classify(fq, tin, skb, fq_flow_get_default_func);
        head = skb_peek_tail(&flow->queue);
-       if (!head)
+       if (!head || skb_is_gso(head))
                goto out;
 
        orig_len = head->len;
@@ -3583,7 +3586,7 @@ begin:
                        skb_queue_splice_tail(&tx.skbs, &txqi->frags);
        }
 
-       if (skb && skb_has_frag_list(skb) &&
+       if (skb_has_frag_list(skb) &&
            !ieee80211_hw_check(&local->hw, TX_FRAG_LIST)) {
                if (skb_linearize(skb)) {
                        ieee80211_free_txskb(&local->hw, skb);
@@ -4579,7 +4582,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
                                              IEEE80211_STYPE_NULLFUNC |
                                              IEEE80211_FCTL_TODS);
        if (qos) {
-               __le16 qos = cpu_to_le16(7);
+               __le16 qoshdr = cpu_to_le16(7);
 
                BUILD_BUG_ON((IEEE80211_STYPE_QOS_NULLFUNC |
                              IEEE80211_STYPE_NULLFUNC) !=
@@ -4588,7 +4591,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
                        cpu_to_le16(IEEE80211_STYPE_QOS_NULLFUNC);
                skb->priority = 7;
                skb_set_queue_mapping(skb, IEEE80211_AC_VO);
-               skb_put_data(skb, &qos, sizeof(qos));
+               skb_put_data(skb, &qoshdr, sizeof(qoshdr));
        }
 
        memcpy(nullfunc->addr1, ifmgd->bssid, ETH_ALEN);
index bec4243..d0eb38b 100644 (file)
@@ -299,16 +299,16 @@ out:
        spin_unlock_bh(&fq->lock);
 }
 
-void ieee80211_wake_txqs(unsigned long data)
+static void
+__releases(&local->queue_stop_reason_lock)
+__acquires(&local->queue_stop_reason_lock)
+_ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags)
 {
-       struct ieee80211_local *local = (struct ieee80211_local *)data;
        struct ieee80211_sub_if_data *sdata;
        int n_acs = IEEE80211_NUM_ACS;
-       unsigned long flags;
        int i;
 
        rcu_read_lock();
-       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
        if (local->hw.queues < IEEE80211_NUM_ACS)
                n_acs = 1;
@@ -317,7 +317,7 @@ void ieee80211_wake_txqs(unsigned long data)
                if (local->queue_stop_reasons[i])
                        continue;
 
-               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+               spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags);
                list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                        int ac;
 
@@ -329,13 +329,22 @@ void ieee80211_wake_txqs(unsigned long data)
                                        __ieee80211_wake_txqs(sdata, ac);
                        }
                }
-               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+               spin_lock_irqsave(&local->queue_stop_reason_lock, *flags);
        }
 
-       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
        rcu_read_unlock();
 }
 
+void ieee80211_wake_txqs(unsigned long data)
+{
+       struct ieee80211_local *local = (struct ieee80211_local *)data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       _ieee80211_wake_txqs(local, &flags);
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
 void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
 {
        struct ieee80211_sub_if_data *sdata;
@@ -371,7 +380,8 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
 
 static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
                                   enum queue_stop_reason reason,
-                                  bool refcounted)
+                                  bool refcounted,
+                                  unsigned long *flags)
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
@@ -405,8 +415,19 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
        } else
                tasklet_schedule(&local->tx_pending_tasklet);
 
-       if (local->ops->wake_tx_queue)
-               tasklet_schedule(&local->wake_txqs_tasklet);
+       /*
+        * Calling _ieee80211_wake_txqs here can be a problem because it may
+        * release queue_stop_reason_lock which has been taken by
+        * __ieee80211_wake_queue's caller. It is certainly not very nice to
+        * release someone's lock, but it is fine because all the callers of
+        * __ieee80211_wake_queue call it right before releasing the lock.
+        */
+       if (local->ops->wake_tx_queue) {
+               if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
+                       tasklet_schedule(&local->wake_txqs_tasklet);
+               else
+                       _ieee80211_wake_txqs(local, flags);
+       }
 }
 
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -417,7 +438,7 @@ void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
        unsigned long flags;
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-       __ieee80211_wake_queue(hw, queue, reason, refcounted);
+       __ieee80211_wake_queue(hw, queue, reason, refcounted, &flags);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
@@ -514,7 +535,7 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,
                               false);
        __skb_queue_tail(&local->pending[queue], skb);
        __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
-                              false);
+                              false, &flags);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
@@ -547,7 +568,7 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local,
        for (i = 0; i < hw->queues; i++)
                __ieee80211_wake_queue(hw, i,
                        IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
-                       false);
+                       false, &flags);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
@@ -605,7 +626,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
        for_each_set_bit(i, &queues, hw->queues)
-               __ieee80211_wake_queue(hw, i, reason, refcounted);
+               __ieee80211_wake_queue(hw, i, reason, refcounted, &flags);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
@@ -1202,6 +1223,8 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                        if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA &&
                            elen >= (sizeof(*elems->mu_edca_param_set) + 1)) {
                                elems->mu_edca_param_set = (void *)&pos[1];
+                               if (calc_crc)
+                                       crc = crc32_be(crc, pos - 2, elen + 2);
                        } else if (pos[0] == WLAN_EID_EXT_HE_CAPABILITY) {
                                elems->he_cap = (void *)&pos[1];
                                elems->he_cap_len = elen - 1;
index 1dae77c..8750560 100644 (file)
@@ -73,10 +73,15 @@ enum {
 #define NCSI_OEM_MFR_BCM_ID             0x113d
 /* Broadcom specific OEM Command */
 #define NCSI_OEM_BCM_CMD_GMA            0x01   /* CMD ID for Get MAC */
+/* Mellanox specific OEM Command */
+#define NCSI_OEM_MLX_CMD_GMA            0x00   /* CMD ID for Get MAC */
+#define NCSI_OEM_MLX_CMD_GMA_PARAM      0x1b   /* Parameter for GMA  */
 /* OEM Command payload lengths*/
 #define NCSI_OEM_BCM_CMD_GMA_LEN        12
+#define NCSI_OEM_MLX_CMD_GMA_LEN        8
 /* Mac address offset in OEM response */
 #define BCM_MAC_ADDR_OFFSET             28
+#define MLX_MAC_ADDR_OFFSET             8
 
 
 struct ncsi_channel_version {
@@ -222,6 +227,10 @@ struct ncsi_package {
        unsigned int         channel_num; /* Number of channels     */
        struct list_head     channels;    /* List of chanels        */
        struct list_head     node;        /* Form list of packages  */
+
+       bool                 multi_channel; /* Enable multiple channels  */
+       u32                  channel_whitelist; /* Channels to configure */
+       struct ncsi_channel  *preferred_channel; /* Primary channel      */
 };
 
 struct ncsi_request {
@@ -287,16 +296,16 @@ struct ncsi_dev_priv {
 #define NCSI_DEV_PROBED                1            /* Finalized NCSI topology    */
 #define NCSI_DEV_HWA           2            /* Enabled HW arbitration     */
 #define NCSI_DEV_RESHUFFLE     4
+#define NCSI_DEV_RESET         8            /* Reset state of NC          */
        unsigned int        gma_flag;        /* OEM GMA flag               */
        spinlock_t          lock;            /* Protect the NCSI device    */
 #if IS_ENABLED(CONFIG_IPV6)
        unsigned int        inet6_addr_num;  /* Number of IPv6 addresses   */
 #endif
+       unsigned int        package_probe_id;/* Current ID during probe    */
        unsigned int        package_num;     /* Number of packages         */
        struct list_head    packages;        /* List of packages           */
        struct ncsi_channel *hot_channel;    /* Channel was ever active    */
-       struct ncsi_package *force_package;  /* Force a specific package   */
-       struct ncsi_channel *force_channel;  /* Force a specific channel   */
        struct ncsi_request requests[256];   /* Request table              */
        unsigned int        request_id;      /* Last used request ID       */
 #define NCSI_REQ_START_IDX     1
@@ -309,6 +318,9 @@ struct ncsi_dev_priv {
        struct list_head    node;            /* Form NCSI device list      */
 #define NCSI_MAX_VLAN_VIDS     15
        struct list_head    vlan_vids;       /* List of active VLAN IDs */
+
+       bool                multi_package;   /* Enable multiple packages   */
+       u32                 package_whitelist; /* Packages to configure    */
 };
 
 struct ncsi_cmd_arg {
@@ -341,6 +353,7 @@ extern spinlock_t ncsi_dev_lock;
        list_for_each_entry_rcu(nc, &np->channels, node)
 
 /* Resources */
+int ncsi_reset_dev(struct ncsi_dev *nd);
 void ncsi_start_channel_monitor(struct ncsi_channel *nc);
 void ncsi_stop_channel_monitor(struct ncsi_channel *nc);
 struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
@@ -361,6 +374,13 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
 void ncsi_free_request(struct ncsi_request *nr);
 struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
 int ncsi_process_next_channel(struct ncsi_dev_priv *ndp);
+bool ncsi_channel_has_link(struct ncsi_channel *channel);
+bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
+                         struct ncsi_channel *channel);
+int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
+                          struct ncsi_package *np,
+                          struct ncsi_channel *disable,
+                          struct ncsi_channel *enable);
 
 /* Packet handlers */
 u32 ncsi_calculate_checksum(unsigned char *data, int len);
index 25e483e..26d67e2 100644 (file)
@@ -50,13 +50,15 @@ static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
 static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
                                struct ncsi_aen_pkt_hdr *h)
 {
-       struct ncsi_aen_lsc_pkt *lsc;
-       struct ncsi_channel *nc;
+       struct ncsi_channel *nc, *tmp;
        struct ncsi_channel_mode *ncm;
-       bool chained;
-       int state;
        unsigned long old_data, data;
+       struct ncsi_aen_lsc_pkt *lsc;
+       struct ncsi_package *np;
+       bool had_link, has_link;
        unsigned long flags;
+       bool chained;
+       int state;
 
        /* Find the NCSI channel */
        ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
@@ -73,6 +75,9 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
        ncm->data[2] = data;
        ncm->data[4] = ntohl(lsc->oem_status);
 
+       had_link = !!(old_data & 0x1);
+       has_link = !!(data & 0x1);
+
        netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n",
                   nc->id, data & 0x1 ? "up" : "down");
 
@@ -80,22 +85,60 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
        state = nc->state;
        spin_unlock_irqrestore(&nc->lock, flags);
 
-       if (!((old_data ^ data) & 0x1) || chained)
-               return 0;
-       if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) &&
-           !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1)))
+       if (state == NCSI_CHANNEL_INACTIVE)
+               netdev_warn(ndp->ndev.dev,
+                           "NCSI: Inactive channel %u received AEN!\n",
+                           nc->id);
+
+       if ((had_link == has_link) || chained)
                return 0;
 
-       if (!(ndp->flags & NCSI_DEV_HWA) &&
-           state == NCSI_CHANNEL_ACTIVE)
-               ndp->flags |= NCSI_DEV_RESHUFFLE;
+       if (!ndp->multi_package && !nc->package->multi_channel) {
+               if (had_link) {
+                       ndp->flags |= NCSI_DEV_RESHUFFLE;
+                       ncsi_stop_channel_monitor(nc);
+                       spin_lock_irqsave(&ndp->lock, flags);
+                       list_add_tail_rcu(&nc->link, &ndp->channel_queue);
+                       spin_unlock_irqrestore(&ndp->lock, flags);
+                       return ncsi_process_next_channel(ndp);
+               }
+               /* Configured channel came up */
+               return 0;
+       }
 
-       ncsi_stop_channel_monitor(nc);
-       spin_lock_irqsave(&ndp->lock, flags);
-       list_add_tail_rcu(&nc->link, &ndp->channel_queue);
-       spin_unlock_irqrestore(&ndp->lock, flags);
+       if (had_link) {
+               ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
+               if (ncsi_channel_is_last(ndp, nc)) {
+                       /* No channels left, reconfigure */
+                       return ncsi_reset_dev(&ndp->ndev);
+               } else if (ncm->enable) {
+                       /* Need to failover Tx channel */
+                       ncsi_update_tx_channel(ndp, nc->package, nc, NULL);
+               }
+       } else if (has_link && nc->package->preferred_channel == nc) {
+               /* Return Tx to preferred channel */
+               ncsi_update_tx_channel(ndp, nc->package, NULL, nc);
+       } else if (has_link) {
+               NCSI_FOR_EACH_PACKAGE(ndp, np) {
+                       NCSI_FOR_EACH_CHANNEL(np, tmp) {
+                               /* Enable Tx on this channel if the current Tx
+                                * channel is down.
+                                */
+                               ncm = &tmp->modes[NCSI_MODE_TX_ENABLE];
+                               if (ncm->enable &&
+                                   !ncsi_channel_has_link(tmp)) {
+                                       ncsi_update_tx_channel(ndp, nc->package,
+                                                              tmp, nc);
+                                       break;
+                               }
+                       }
+               }
+       }
 
-       return ncsi_process_next_channel(ndp);
+       /* Leave configured channels active in a multi-channel scenario so
+        * AEN events are still received.
+        */
+       return 0;
 }
 
 static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
index bfc43b2..31359d5 100644 (file)
 LIST_HEAD(ncsi_dev_list);
 DEFINE_SPINLOCK(ncsi_dev_lock);
 
+bool ncsi_channel_has_link(struct ncsi_channel *channel)
+{
+       return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1);
+}
+
+bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
+                         struct ncsi_channel *channel)
+{
+       struct ncsi_package *np;
+       struct ncsi_channel *nc;
+
+       NCSI_FOR_EACH_PACKAGE(ndp, np)
+               NCSI_FOR_EACH_CHANNEL(np, nc) {
+                       if (nc == channel)
+                               continue;
+                       if (nc->state == NCSI_CHANNEL_ACTIVE &&
+                           ncsi_channel_has_link(nc))
+                               return false;
+               }
+
+       return true;
+}
+
 static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
 {
        struct ncsi_dev *nd = &ndp->ndev;
@@ -52,7 +75,7 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
                                continue;
                        }
 
-                       if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
+                       if (ncsi_channel_has_link(nc)) {
                                spin_unlock_irqrestore(&nc->lock, flags);
                                nd->link_up = 1;
                                goto report;
@@ -113,10 +136,8 @@ static void ncsi_channel_monitor(struct timer_list *t)
        default:
                netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n",
                           nc->id);
-               if (!(ndp->flags & NCSI_DEV_HWA)) {
-                       ncsi_report_link(ndp, true);
-                       ndp->flags |= NCSI_DEV_RESHUFFLE;
-               }
+               ncsi_report_link(ndp, true);
+               ndp->flags |= NCSI_DEV_RESHUFFLE;
 
                ncsi_stop_channel_monitor(nc);
 
@@ -269,6 +290,7 @@ struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
        np->ndp = ndp;
        spin_lock_init(&np->lock);
        INIT_LIST_HEAD(&np->channels);
+       np->channel_whitelist = UINT_MAX;
 
        spin_lock_irqsave(&ndp->lock, flags);
        tmp = ncsi_find_package(ndp, id);
@@ -442,12 +464,14 @@ static void ncsi_request_timeout(struct timer_list *t)
 static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
 {
        struct ncsi_dev *nd = &ndp->ndev;
-       struct ncsi_package *np = ndp->active_package;
-       struct ncsi_channel *nc = ndp->active_channel;
+       struct ncsi_package *np;
+       struct ncsi_channel *nc, *tmp;
        struct ncsi_cmd_arg nca;
        unsigned long flags;
        int ret;
 
+       np = ndp->active_package;
+       nc = ndp->active_channel;
        nca.ndp = ndp;
        nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
        switch (nd->state) {
@@ -523,6 +547,15 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
                if (ret)
                        goto error;
 
+               NCSI_FOR_EACH_CHANNEL(np, tmp) {
+                       /* If there is another channel active on this package
+                        * do not deselect the package.
+                        */
+                       if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) {
+                               nd->state = ncsi_dev_state_suspend_done;
+                               break;
+                       }
+               }
                break;
        case ncsi_dev_state_suspend_deselect:
                ndp->pending_req_num = 1;
@@ -541,8 +574,10 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
                spin_lock_irqsave(&nc->lock, flags);
                nc->state = NCSI_CHANNEL_INACTIVE;
                spin_unlock_irqrestore(&nc->lock, flags);
-               ncsi_process_next_channel(ndp);
-
+               if (ndp->flags & NCSI_DEV_RESET)
+                       ncsi_reset_dev(nd);
+               else
+                       ncsi_process_next_channel(ndp);
                break;
        default:
                netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
@@ -675,12 +710,38 @@ static int ncsi_oem_gma_handler_bcm(struct ncsi_cmd_arg *nca)
        return ret;
 }
 
+static int ncsi_oem_gma_handler_mlx(struct ncsi_cmd_arg *nca)
+{
+       union {
+               u8 data_u8[NCSI_OEM_MLX_CMD_GMA_LEN];
+               u32 data_u32[NCSI_OEM_MLX_CMD_GMA_LEN / sizeof(u32)];
+       } u;
+       int ret = 0;
+
+       nca->payload = NCSI_OEM_MLX_CMD_GMA_LEN;
+
+       memset(&u, 0, sizeof(u));
+       u.data_u32[0] = ntohl(NCSI_OEM_MFR_MLX_ID);
+       u.data_u8[5] = NCSI_OEM_MLX_CMD_GMA;
+       u.data_u8[6] = NCSI_OEM_MLX_CMD_GMA_PARAM;
+
+       nca->data = u.data_u8;
+
+       ret = ncsi_xmit_cmd(nca);
+       if (ret)
+               netdev_err(nca->ndp->ndev.dev,
+                          "NCSI: Failed to transmit cmd 0x%x during configure\n",
+                          nca->type);
+       return ret;
+}
+
 /* OEM Command handlers initialization */
 static struct ncsi_oem_gma_handler {
        unsigned int    mfr_id;
        int             (*handler)(struct ncsi_cmd_arg *nca);
 } ncsi_oem_gma_handlers[] = {
-       { NCSI_OEM_MFR_BCM_ID, ncsi_oem_gma_handler_bcm }
+       { NCSI_OEM_MFR_BCM_ID, ncsi_oem_gma_handler_bcm },
+       { NCSI_OEM_MFR_MLX_ID, ncsi_oem_gma_handler_mlx }
 };
 
 static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id)
@@ -717,13 +778,144 @@ static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id)
 
 #endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
 
+/* Determine if a given channel from the channel_queue should be used for Tx */
+static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp,
+                              struct ncsi_channel *nc)
+{
+       struct ncsi_channel_mode *ncm;
+       struct ncsi_channel *channel;
+       struct ncsi_package *np;
+
+       /* Check if any other channel has Tx enabled; a channel may have already
+        * been configured and removed from the channel queue.
+        */
+       NCSI_FOR_EACH_PACKAGE(ndp, np) {
+               if (!ndp->multi_package && np != nc->package)
+                       continue;
+               NCSI_FOR_EACH_CHANNEL(np, channel) {
+                       ncm = &channel->modes[NCSI_MODE_TX_ENABLE];
+                       if (ncm->enable)
+                               return false;
+               }
+       }
+
+       /* This channel is the preferred channel and has link */
+       list_for_each_entry_rcu(channel, &ndp->channel_queue, link) {
+               np = channel->package;
+               if (np->preferred_channel &&
+                   ncsi_channel_has_link(np->preferred_channel)) {
+                       return np->preferred_channel == nc;
+               }
+       }
+
+       /* This channel has link */
+       if (ncsi_channel_has_link(nc))
+               return true;
+
+       list_for_each_entry_rcu(channel, &ndp->channel_queue, link)
+               if (ncsi_channel_has_link(channel))
+                       return false;
+
+       /* No other channel has link; default to this one */
+       return true;
+}
+
+/* Change the active Tx channel in a multi-channel setup */
+int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
+                          struct ncsi_package *package,
+                          struct ncsi_channel *disable,
+                          struct ncsi_channel *enable)
+{
+       struct ncsi_cmd_arg nca;
+       struct ncsi_channel *nc;
+       struct ncsi_package *np;
+       int ret = 0;
+
+       if (!package->multi_channel && !ndp->multi_package)
+               netdev_warn(ndp->ndev.dev,
+                           "NCSI: Trying to update Tx channel in single-channel mode\n");
+       nca.ndp = ndp;
+       nca.req_flags = 0;
+
+       /* Find current channel with Tx enabled */
+       NCSI_FOR_EACH_PACKAGE(ndp, np) {
+               if (disable)
+                       break;
+               if (!ndp->multi_package && np != package)
+                       continue;
+
+               NCSI_FOR_EACH_CHANNEL(np, nc)
+                       if (nc->modes[NCSI_MODE_TX_ENABLE].enable) {
+                               disable = nc;
+                               break;
+                       }
+       }
+
+       /* Find a suitable channel for Tx */
+       NCSI_FOR_EACH_PACKAGE(ndp, np) {
+               if (enable)
+                       break;
+               if (!ndp->multi_package && np != package)
+                       continue;
+               if (!(ndp->package_whitelist & (0x1 << np->id)))
+                       continue;
+
+               if (np->preferred_channel &&
+                   ncsi_channel_has_link(np->preferred_channel)) {
+                       enable = np->preferred_channel;
+                       break;
+               }
+
+               NCSI_FOR_EACH_CHANNEL(np, nc) {
+                       if (!(np->channel_whitelist & 0x1 << nc->id))
+                               continue;
+                       if (nc->state != NCSI_CHANNEL_ACTIVE)
+                               continue;
+                       if (ncsi_channel_has_link(nc)) {
+                               enable = nc;
+                               break;
+                       }
+               }
+       }
+
+       if (disable == enable)
+               return -1;
+
+       if (!enable)
+               return -1;
+
+       if (disable) {
+               nca.channel = disable->id;
+               nca.package = disable->package->id;
+               nca.type = NCSI_PKT_CMD_DCNT;
+               ret = ncsi_xmit_cmd(&nca);
+               if (ret)
+                       netdev_err(ndp->ndev.dev,
+                                  "Error %d sending DCNT\n",
+                                  ret);
+       }
+
+       netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id);
+
+       nca.channel = enable->id;
+       nca.package = enable->package->id;
+       nca.type = NCSI_PKT_CMD_ECNT;
+       ret = ncsi_xmit_cmd(&nca);
+       if (ret)
+               netdev_err(ndp->ndev.dev,
+                          "Error %d sending ECNT\n",
+                          ret);
+
+       return ret;
+}
+
 static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
 {
-       struct ncsi_dev *nd = &ndp->ndev;
-       struct net_device *dev = nd->dev;
        struct ncsi_package *np = ndp->active_package;
        struct ncsi_channel *nc = ndp->active_channel;
        struct ncsi_channel *hot_nc = NULL;
+       struct ncsi_dev *nd = &ndp->ndev;
+       struct net_device *dev = nd->dev;
        struct ncsi_cmd_arg nca;
        unsigned char index;
        unsigned long flags;
@@ -845,20 +1037,29 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
                } else if (nd->state == ncsi_dev_state_config_ebf) {
                        nca.type = NCSI_PKT_CMD_EBF;
                        nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
-                       nd->state = ncsi_dev_state_config_ecnt;
+                       if (ncsi_channel_is_tx(ndp, nc))
+                               nd->state = ncsi_dev_state_config_ecnt;
+                       else
+                               nd->state = ncsi_dev_state_config_ec;
 #if IS_ENABLED(CONFIG_IPV6)
                        if (ndp->inet6_addr_num > 0 &&
                            (nc->caps[NCSI_CAP_GENERIC].cap &
                             NCSI_CAP_GENERIC_MC))
                                nd->state = ncsi_dev_state_config_egmf;
-                       else
-                               nd->state = ncsi_dev_state_config_ecnt;
                } else if (nd->state == ncsi_dev_state_config_egmf) {
                        nca.type = NCSI_PKT_CMD_EGMF;
                        nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
-                       nd->state = ncsi_dev_state_config_ecnt;
+                       if (ncsi_channel_is_tx(ndp, nc))
+                               nd->state = ncsi_dev_state_config_ecnt;
+                       else
+                               nd->state = ncsi_dev_state_config_ec;
 #endif /* CONFIG_IPV6 */
                } else if (nd->state == ncsi_dev_state_config_ecnt) {
+                       if (np->preferred_channel &&
+                           nc != np->preferred_channel)
+                               netdev_info(ndp->ndev.dev,
+                                           "NCSI: Tx failed over to channel %u\n",
+                                           nc->id);
                        nca.type = NCSI_PKT_CMD_ECNT;
                        nd->state = ncsi_dev_state_config_ec;
                } else if (nd->state == ncsi_dev_state_config_ec) {
@@ -889,6 +1090,16 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
                netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n",
                           nc->id);
                spin_lock_irqsave(&nc->lock, flags);
+               nc->state = NCSI_CHANNEL_ACTIVE;
+
+               if (ndp->flags & NCSI_DEV_RESET) {
+                       /* A reset event happened during config, start it now */
+                       nc->reconfigure_needed = false;
+                       spin_unlock_irqrestore(&nc->lock, flags);
+                       ncsi_reset_dev(nd);
+                       break;
+               }
+
                if (nc->reconfigure_needed) {
                        /* This channel's configuration has been updated
                         * part-way during the config state - start the
@@ -909,10 +1120,8 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
 
                if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
                        hot_nc = nc;
-                       nc->state = NCSI_CHANNEL_ACTIVE;
                } else {
                        hot_nc = NULL;
-                       nc->state = NCSI_CHANNEL_INACTIVE;
                        netdev_dbg(ndp->ndev.dev,
                                   "NCSI: channel %u link down after config\n",
                                   nc->id);
@@ -940,43 +1149,35 @@ error:
 
 static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
 {
-       struct ncsi_package *np, *force_package;
-       struct ncsi_channel *nc, *found, *hot_nc, *force_channel;
+       struct ncsi_channel *nc, *found, *hot_nc;
        struct ncsi_channel_mode *ncm;
-       unsigned long flags;
+       unsigned long flags, cflags;
+       struct ncsi_package *np;
+       bool with_link;
 
        spin_lock_irqsave(&ndp->lock, flags);
        hot_nc = ndp->hot_channel;
-       force_channel = ndp->force_channel;
-       force_package = ndp->force_package;
        spin_unlock_irqrestore(&ndp->lock, flags);
 
-       /* Force a specific channel whether or not it has link if we have been
-        * configured to do so
-        */
-       if (force_package && force_channel) {
-               found = force_channel;
-               ncm = &found->modes[NCSI_MODE_LINK];
-               if (!(ncm->data[2] & 0x1))
-                       netdev_info(ndp->ndev.dev,
-                                   "NCSI: Channel %u forced, but it is link down\n",
-                                   found->id);
-               goto out;
-       }
-
-       /* The search is done once an inactive channel with up
-        * link is found.
+       /* By default the search is done once an inactive channel with up
+        * link is found, unless a preferred channel is set.
+        * If multi_package or multi_channel are configured all channels in the
+        * whitelist are added to the channel queue.
         */
        found = NULL;
+       with_link = false;
        NCSI_FOR_EACH_PACKAGE(ndp, np) {
-               if (ndp->force_package && np != ndp->force_package)
+               if (!(ndp->package_whitelist & (0x1 << np->id)))
                        continue;
                NCSI_FOR_EACH_CHANNEL(np, nc) {
-                       spin_lock_irqsave(&nc->lock, flags);
+                       if (!(np->channel_whitelist & (0x1 << nc->id)))
+                               continue;
+
+                       spin_lock_irqsave(&nc->lock, cflags);
 
                        if (!list_empty(&nc->link) ||
                            nc->state != NCSI_CHANNEL_INACTIVE) {
-                               spin_unlock_irqrestore(&nc->lock, flags);
+                               spin_unlock_irqrestore(&nc->lock, cflags);
                                continue;
                        }
 
@@ -988,32 +1189,49 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
 
                        ncm = &nc->modes[NCSI_MODE_LINK];
                        if (ncm->data[2] & 0x1) {
-                               spin_unlock_irqrestore(&nc->lock, flags);
                                found = nc;
-                               goto out;
+                               with_link = true;
                        }
 
-                       spin_unlock_irqrestore(&nc->lock, flags);
+                       /* If multi_channel is enabled configure all valid
+                        * channels whether or not they currently have link
+                        * so they will have AENs enabled.
+                        */
+                       if (with_link || np->multi_channel) {
+                               spin_lock_irqsave(&ndp->lock, flags);
+                               list_add_tail_rcu(&nc->link,
+                                                 &ndp->channel_queue);
+                               spin_unlock_irqrestore(&ndp->lock, flags);
+
+                               netdev_dbg(ndp->ndev.dev,
+                                          "NCSI: Channel %u added to queue (link %s)\n",
+                                          nc->id,
+                                          ncm->data[2] & 0x1 ? "up" : "down");
+                       }
+
+                       spin_unlock_irqrestore(&nc->lock, cflags);
+
+                       if (with_link && !np->multi_channel)
+                               break;
                }
+               if (with_link && !ndp->multi_package)
+                       break;
        }
 
-       if (!found) {
+       if (list_empty(&ndp->channel_queue) && found) {
+               netdev_info(ndp->ndev.dev,
+                           "NCSI: No channel with link found, configuring channel %u\n",
+                           found->id);
+               spin_lock_irqsave(&ndp->lock, flags);
+               list_add_tail_rcu(&found->link, &ndp->channel_queue);
+               spin_unlock_irqrestore(&ndp->lock, flags);
+       } else if (!found) {
                netdev_warn(ndp->ndev.dev,
-                           "NCSI: No channel found with link\n");
+                           "NCSI: No channel found to configure!\n");
                ncsi_report_link(ndp, true);
                return -ENODEV;
        }
 
-       ncm = &found->modes[NCSI_MODE_LINK];
-       netdev_dbg(ndp->ndev.dev,
-                  "NCSI: Channel %u added to queue (link %s)\n",
-                  found->id, ncm->data[2] & 0x1 ? "up" : "down");
-
-out:
-       spin_lock_irqsave(&ndp->lock, flags);
-       list_add_tail_rcu(&found->link, &ndp->channel_queue);
-       spin_unlock_irqrestore(&ndp->lock, flags);
-
        return ncsi_process_next_channel(ndp);
 }
 
@@ -1050,35 +1268,6 @@ static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
        return false;
 }
 
-static int ncsi_enable_hwa(struct ncsi_dev_priv *ndp)
-{
-       struct ncsi_package *np;
-       struct ncsi_channel *nc;
-       unsigned long flags;
-
-       /* Move all available channels to processing queue */
-       spin_lock_irqsave(&ndp->lock, flags);
-       NCSI_FOR_EACH_PACKAGE(ndp, np) {
-               NCSI_FOR_EACH_CHANNEL(np, nc) {
-                       WARN_ON_ONCE(nc->state != NCSI_CHANNEL_INACTIVE ||
-                                    !list_empty(&nc->link));
-                       ncsi_stop_channel_monitor(nc);
-                       list_add_tail_rcu(&nc->link, &ndp->channel_queue);
-               }
-       }
-       spin_unlock_irqrestore(&ndp->lock, flags);
-
-       /* We can have no channels in extremely case */
-       if (list_empty(&ndp->channel_queue)) {
-               netdev_err(ndp->ndev.dev,
-                          "NCSI: No available channels for HWA\n");
-               ncsi_report_link(ndp, false);
-               return -ENOENT;
-       }
-
-       return ncsi_process_next_channel(ndp);
-}
-
 static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
 {
        struct ncsi_dev *nd = &ndp->ndev;
@@ -1110,70 +1299,28 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
                nd->state = ncsi_dev_state_probe_package;
                break;
        case ncsi_dev_state_probe_package:
-               ndp->pending_req_num = 16;
+               ndp->pending_req_num = 1;
 
-               /* Select all possible packages */
                nca.type = NCSI_PKT_CMD_SP;
                nca.bytes[0] = 1;
+               nca.package = ndp->package_probe_id;
                nca.channel = NCSI_RESERVED_CHANNEL;
-               for (index = 0; index < 8; index++) {
-                       nca.package = index;
-                       ret = ncsi_xmit_cmd(&nca);
-                       if (ret)
-                               goto error;
-               }
-
-               /* Disable all possible packages */
-               nca.type = NCSI_PKT_CMD_DP;
-               for (index = 0; index < 8; index++) {
-                       nca.package = index;
-                       ret = ncsi_xmit_cmd(&nca);
-                       if (ret)
-                               goto error;
-               }
-
+               ret = ncsi_xmit_cmd(&nca);
+               if (ret)
+                       goto error;
                nd->state = ncsi_dev_state_probe_channel;
                break;
        case ncsi_dev_state_probe_channel:
-               if (!ndp->active_package)
-                       ndp->active_package = list_first_or_null_rcu(
-                               &ndp->packages, struct ncsi_package, node);
-               else if (list_is_last(&ndp->active_package->node,
-                                     &ndp->packages))
-                       ndp->active_package = NULL;
-               else
-                       ndp->active_package = list_next_entry(
-                               ndp->active_package, node);
-
-               /* All available packages and channels are enumerated. The
-                * enumeration happens for once when the NCSI interface is
-                * started. So we need continue to start the interface after
-                * the enumeration.
-                *
-                * We have to choose an active channel before configuring it.
-                * Note that we possibly don't have active channel in extreme
-                * situation.
-                */
+               ndp->active_package = ncsi_find_package(ndp,
+                                                       ndp->package_probe_id);
                if (!ndp->active_package) {
-                       ndp->flags |= NCSI_DEV_PROBED;
-                       if (ncsi_check_hwa(ndp))
-                               ncsi_enable_hwa(ndp);
-                       else
-                               ncsi_choose_active_channel(ndp);
-                       return;
+                       /* No response */
+                       nd->state = ncsi_dev_state_probe_dp;
+                       schedule_work(&ndp->work);
+                       break;
                }
-
-               /* Select the active package */
-               ndp->pending_req_num = 1;
-               nca.type = NCSI_PKT_CMD_SP;
-               nca.bytes[0] = 1;
-               nca.package = ndp->active_package->id;
-               nca.channel = NCSI_RESERVED_CHANNEL;
-               ret = ncsi_xmit_cmd(&nca);
-               if (ret)
-                       goto error;
-
                nd->state = ncsi_dev_state_probe_cis;
+               schedule_work(&ndp->work);
                break;
        case ncsi_dev_state_probe_cis:
                ndp->pending_req_num = NCSI_RESERVED_CHANNEL;
@@ -1222,22 +1369,35 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
        case ncsi_dev_state_probe_dp:
                ndp->pending_req_num = 1;
 
-               /* Deselect the active package */
+               /* Deselect the current package */
                nca.type = NCSI_PKT_CMD_DP;
-               nca.package = ndp->active_package->id;
+               nca.package = ndp->package_probe_id;
                nca.channel = NCSI_RESERVED_CHANNEL;
                ret = ncsi_xmit_cmd(&nca);
                if (ret)
                        goto error;
 
-               /* Scan channels in next package */
-               nd->state = ncsi_dev_state_probe_channel;
+               /* Probe next package */
+               ndp->package_probe_id++;
+               if (ndp->package_probe_id >= 8) {
+                       /* Probe finished */
+                       ndp->flags |= NCSI_DEV_PROBED;
+                       break;
+               }
+               nd->state = ncsi_dev_state_probe_package;
+               ndp->active_package = NULL;
                break;
        default:
                netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n",
                            nd->state);
        }
 
+       if (ndp->flags & NCSI_DEV_PROBED) {
+               /* Check if all packages have HWA support */
+               ncsi_check_hwa(ndp);
+               ncsi_choose_active_channel(ndp);
+       }
+
        return;
 error:
        netdev_err(ndp->ndev.dev,
@@ -1556,6 +1716,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
        INIT_LIST_HEAD(&ndp->channel_queue);
        INIT_LIST_HEAD(&ndp->vlan_vids);
        INIT_WORK(&ndp->work, ncsi_dev_work);
+       ndp->package_whitelist = UINT_MAX;
 
        /* Initialize private NCSI device */
        spin_lock_init(&ndp->lock);
@@ -1592,26 +1753,19 @@ EXPORT_SYMBOL_GPL(ncsi_register_dev);
 int ncsi_start_dev(struct ncsi_dev *nd)
 {
        struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
-       int ret;
 
        if (nd->state != ncsi_dev_state_registered &&
            nd->state != ncsi_dev_state_functional)
                return -ENOTTY;
 
        if (!(ndp->flags & NCSI_DEV_PROBED)) {
+               ndp->package_probe_id = 0;
                nd->state = ncsi_dev_state_probe;
                schedule_work(&ndp->work);
                return 0;
        }
 
-       if (ndp->flags & NCSI_DEV_HWA) {
-               netdev_info(ndp->ndev.dev, "NCSI: Enabling HWA mode\n");
-               ret = ncsi_enable_hwa(ndp);
-       } else {
-               ret = ncsi_choose_active_channel(ndp);
-       }
-
-       return ret;
+       return ncsi_reset_dev(nd);
 }
 EXPORT_SYMBOL_GPL(ncsi_start_dev);
 
@@ -1624,7 +1778,10 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
        int old_state;
        unsigned long flags;
 
-       /* Stop the channel monitor and reset channel's state */
+       /* Stop the channel monitor on any active channels. Don't reset the
+        * channel state so we know which were active when ncsi_start_dev()
+        * is next called.
+        */
        NCSI_FOR_EACH_PACKAGE(ndp, np) {
                NCSI_FOR_EACH_CHANNEL(np, nc) {
                        ncsi_stop_channel_monitor(nc);
@@ -1632,7 +1789,6 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
                        spin_lock_irqsave(&nc->lock, flags);
                        chained = !list_empty(&nc->link);
                        old_state = nc->state;
-                       nc->state = NCSI_CHANNEL_INACTIVE;
                        spin_unlock_irqrestore(&nc->lock, flags);
 
                        WARN_ON_ONCE(chained ||
@@ -1645,6 +1801,92 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
 }
 EXPORT_SYMBOL_GPL(ncsi_stop_dev);
 
+int ncsi_reset_dev(struct ncsi_dev *nd)
+{
+       struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+       struct ncsi_channel *nc, *active, *tmp;
+       struct ncsi_package *np;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ndp->lock, flags);
+
+       if (!(ndp->flags & NCSI_DEV_RESET)) {
+               /* Haven't been called yet, check states */
+               switch (nd->state & ncsi_dev_state_major) {
+               case ncsi_dev_state_registered:
+               case ncsi_dev_state_probe:
+                       /* Not even probed yet - do nothing */
+                       spin_unlock_irqrestore(&ndp->lock, flags);
+                       return 0;
+               case ncsi_dev_state_suspend:
+               case ncsi_dev_state_config:
+                       /* Wait for the channel to finish its suspend/config
+                        * operation; once it finishes it will check for
+                        * NCSI_DEV_RESET and reset the state.
+                        */
+                       ndp->flags |= NCSI_DEV_RESET;
+                       spin_unlock_irqrestore(&ndp->lock, flags);
+                       return 0;
+               }
+       } else {
+               switch (nd->state) {
+               case ncsi_dev_state_suspend_done:
+               case ncsi_dev_state_config_done:
+               case ncsi_dev_state_functional:
+                       /* Ok */
+                       break;
+               default:
+                       /* Current reset operation happening */
+                       spin_unlock_irqrestore(&ndp->lock, flags);
+                       return 0;
+               }
+       }
+
+       if (!list_empty(&ndp->channel_queue)) {
+               /* Clear any channel queue we may have interrupted */
+               list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link)
+                       list_del_init(&nc->link);
+       }
+       spin_unlock_irqrestore(&ndp->lock, flags);
+
+       active = NULL;
+       NCSI_FOR_EACH_PACKAGE(ndp, np) {
+               NCSI_FOR_EACH_CHANNEL(np, nc) {
+                       spin_lock_irqsave(&nc->lock, flags);
+
+                       if (nc->state == NCSI_CHANNEL_ACTIVE) {
+                               active = nc;
+                               nc->state = NCSI_CHANNEL_INVISIBLE;
+                               spin_unlock_irqrestore(&nc->lock, flags);
+                               ncsi_stop_channel_monitor(nc);
+                               break;
+                       }
+
+                       spin_unlock_irqrestore(&nc->lock, flags);
+               }
+               if (active)
+                       break;
+       }
+
+       if (!active) {
+               /* Done */
+               spin_lock_irqsave(&ndp->lock, flags);
+               ndp->flags &= ~NCSI_DEV_RESET;
+               spin_unlock_irqrestore(&ndp->lock, flags);
+               return ncsi_choose_active_channel(ndp);
+       }
+
+       spin_lock_irqsave(&ndp->lock, flags);
+       ndp->flags |= NCSI_DEV_RESET;
+       ndp->active_channel = active;
+       ndp->active_package = active->package;
+       spin_unlock_irqrestore(&ndp->lock, flags);
+
+       nd->state = ncsi_dev_state_suspend;
+       schedule_work(&ndp->work);
+       return 0;
+}
+
 void ncsi_unregister_dev(struct ncsi_dev *nd)
 {
        struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
index 3331438..5d78244 100644 (file)
@@ -30,6 +30,9 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
        [NCSI_ATTR_PACKAGE_ID] =        { .type = NLA_U32 },
        [NCSI_ATTR_CHANNEL_ID] =        { .type = NLA_U32 },
        [NCSI_ATTR_DATA] =              { .type = NLA_BINARY, .len = 2048 },
+       [NCSI_ATTR_MULTI_FLAG] =        { .type = NLA_FLAG },
+       [NCSI_ATTR_PACKAGE_MASK] =      { .type = NLA_U32 },
+       [NCSI_ATTR_CHANNEL_MASK] =      { .type = NLA_U32 },
 };
 
 static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
@@ -69,7 +72,7 @@ static int ncsi_write_channel_info(struct sk_buff *skb,
        nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
        if (nc->state == NCSI_CHANNEL_ACTIVE)
                nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
-       if (ndp->force_channel == nc)
+       if (nc == nc->package->preferred_channel)
                nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
 
        nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version);
@@ -114,7 +117,7 @@ static int ncsi_write_package_info(struct sk_buff *skb,
                if (!pnest)
                        return -ENOMEM;
                nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
-               if (ndp->force_package == np)
+               if ((0x1 << np->id) == ndp->package_whitelist)
                        nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
                cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
                if (!cnest) {
@@ -290,49 +293,58 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
        package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
        package = NULL;
 
-       spin_lock_irqsave(&ndp->lock, flags);
-
        NCSI_FOR_EACH_PACKAGE(ndp, np)
                if (np->id == package_id)
                        package = np;
        if (!package) {
                /* The user has set a package that does not exist */
-               spin_unlock_irqrestore(&ndp->lock, flags);
                return -ERANGE;
        }
 
        channel = NULL;
-       if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
-               /* Allow any channel */
-               channel_id = NCSI_RESERVED_CHANNEL;
-       } else {
+       if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
                channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
                NCSI_FOR_EACH_CHANNEL(package, nc)
-                       if (nc->id == channel_id)
+                       if (nc->id == channel_id) {
                                channel = nc;
+                               break;
+                       }
+               if (!channel) {
+                       netdev_info(ndp->ndev.dev,
+                                   "NCSI: Channel %u does not exist!\n",
+                                   channel_id);
+                       return -ERANGE;
+               }
        }
 
-       if (channel_id != NCSI_RESERVED_CHANNEL && !channel) {
-               /* The user has set a channel that does not exist on this
-                * package
-                */
-               spin_unlock_irqrestore(&ndp->lock, flags);
-               netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n",
-                           channel_id);
-               return -ERANGE;
-       }
-
-       ndp->force_package = package;
-       ndp->force_channel = channel;
+       spin_lock_irqsave(&ndp->lock, flags);
+       ndp->package_whitelist = 0x1 << package->id;
+       ndp->multi_package = false;
        spin_unlock_irqrestore(&ndp->lock, flags);
 
-       netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n",
-                   package_id, channel_id,
-                   channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : "");
+       spin_lock_irqsave(&package->lock, flags);
+       package->multi_channel = false;
+       if (channel) {
+               package->channel_whitelist = 0x1 << channel->id;
+               package->preferred_channel = channel;
+       } else {
+               /* Allow any channel */
+               package->channel_whitelist = UINT_MAX;
+               package->preferred_channel = NULL;
+       }
+       spin_unlock_irqrestore(&package->lock, flags);
+
+       if (channel)
+               netdev_info(ndp->ndev.dev,
+                           "Set package 0x%x, channel 0x%x as preferred\n",
+                           package_id, channel_id);
+       else
+               netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n",
+                           package_id);
 
-       /* Bounce the NCSI channel to set changes */
-       ncsi_stop_dev(&ndp->ndev);
-       ncsi_start_dev(&ndp->ndev);
+       /* Update channel configuration */
+       if (!(ndp->flags & NCSI_DEV_RESET))
+               ncsi_reset_dev(&ndp->ndev);
 
        return 0;
 }
@@ -340,6 +352,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
 static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
 {
        struct ncsi_dev_priv *ndp;
+       struct ncsi_package *np;
        unsigned long flags;
 
        if (!info || !info->attrs)
@@ -353,16 +366,24 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
        if (!ndp)
                return -ENODEV;
 
-       /* Clear any override */
+       /* Reset any whitelists and disable multi mode */
        spin_lock_irqsave(&ndp->lock, flags);
-       ndp->force_package = NULL;
-       ndp->force_channel = NULL;
+       ndp->package_whitelist = UINT_MAX;
+       ndp->multi_package = false;
        spin_unlock_irqrestore(&ndp->lock, flags);
+
+       NCSI_FOR_EACH_PACKAGE(ndp, np) {
+               spin_lock_irqsave(&np->lock, flags);
+               np->multi_channel = false;
+               np->channel_whitelist = UINT_MAX;
+               np->preferred_channel = NULL;
+               spin_unlock_irqrestore(&np->lock, flags);
+       }
        netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
 
-       /* Bounce the NCSI channel to set changes */
-       ncsi_stop_dev(&ndp->ndev);
-       ncsi_start_dev(&ndp->ndev);
+       /* Update channel configuration */
+       if (!(ndp->flags & NCSI_DEV_RESET))
+               ncsi_reset_dev(&ndp->ndev);
 
        return 0;
 }
@@ -563,6 +584,138 @@ int ncsi_send_netlink_err(struct net_device *dev,
        return nlmsg_unicast(net->genl_sock, skb, snd_portid);
 }
 
+static int ncsi_set_package_mask_nl(struct sk_buff *msg,
+                                   struct genl_info *info)
+{
+       struct ncsi_dev_priv *ndp;
+       unsigned long flags;
+       int rc;
+
+       if (!info || !info->attrs)
+               return -EINVAL;
+
+       if (!info->attrs[NCSI_ATTR_IFINDEX])
+               return -EINVAL;
+
+       if (!info->attrs[NCSI_ATTR_PACKAGE_MASK])
+               return -EINVAL;
+
+       ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
+                              nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
+       if (!ndp)
+               return -ENODEV;
+
+       spin_lock_irqsave(&ndp->lock, flags);
+       if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
+               if (ndp->flags & NCSI_DEV_HWA) {
+                       ndp->multi_package = true;
+                       rc = 0;
+               } else {
+                       netdev_err(ndp->ndev.dev,
+                                  "NCSI: Can't use multiple packages without HWA\n");
+                       rc = -EPERM;
+               }
+       } else {
+               ndp->multi_package = false;
+               rc = 0;
+       }
+
+       if (!rc)
+               ndp->package_whitelist =
+                       nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]);
+       spin_unlock_irqrestore(&ndp->lock, flags);
+
+       if (!rc) {
+               /* Update channel configuration */
+               if (!(ndp->flags & NCSI_DEV_RESET))
+                       ncsi_reset_dev(&ndp->ndev);
+       }
+
+       return rc;
+}
+
+static int ncsi_set_channel_mask_nl(struct sk_buff *msg,
+                                   struct genl_info *info)
+{
+       struct ncsi_package *np, *package;
+       struct ncsi_channel *nc, *channel;
+       u32 package_id, channel_id;
+       struct ncsi_dev_priv *ndp;
+       unsigned long flags;
+
+       if (!info || !info->attrs)
+               return -EINVAL;
+
+       if (!info->attrs[NCSI_ATTR_IFINDEX])
+               return -EINVAL;
+
+       if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
+               return -EINVAL;
+
+       if (!info->attrs[NCSI_ATTR_CHANNEL_MASK])
+               return -EINVAL;
+
+       ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
+                              nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
+       if (!ndp)
+               return -ENODEV;
+
+       package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
+       package = NULL;
+       NCSI_FOR_EACH_PACKAGE(ndp, np)
+               if (np->id == package_id) {
+                       package = np;
+                       break;
+               }
+       if (!package)
+               return -ERANGE;
+
+       spin_lock_irqsave(&package->lock, flags);
+
+       channel = NULL;
+       if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
+               channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
+               NCSI_FOR_EACH_CHANNEL(np, nc)
+                       if (nc->id == channel_id) {
+                               channel = nc;
+                               break;
+                       }
+               if (!channel) {
+                       spin_unlock_irqrestore(&package->lock, flags);
+                       return -ERANGE;
+               }
+               netdev_dbg(ndp->ndev.dev,
+                          "NCSI: Channel %u set as preferred channel\n",
+                          channel->id);
+       }
+
+       package->channel_whitelist =
+               nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]);
+       if (package->channel_whitelist == 0)
+               netdev_dbg(ndp->ndev.dev,
+                          "NCSI: Package %u set to all channels disabled\n",
+                          package->id);
+
+       package->preferred_channel = channel;
+
+       if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
+               package->multi_channel = true;
+               netdev_info(ndp->ndev.dev,
+                           "NCSI: Multi-channel enabled on package %u\n",
+                           package_id);
+       } else {
+               package->multi_channel = false;
+       }
+
+       spin_unlock_irqrestore(&package->lock, flags);
+
+       /* Update channel configuration */
+       if (!(ndp->flags & NCSI_DEV_RESET))
+               ncsi_reset_dev(&ndp->ndev);
+
+       return 0;
+}
+
 static const struct genl_ops ncsi_ops[] = {
        {
                .cmd = NCSI_CMD_PKG_INFO,
@@ -589,6 +742,18 @@ static const struct genl_ops ncsi_ops[] = {
                .doit = ncsi_send_cmd_nl,
                .flags = GENL_ADMIN_PERM,
        },
+       {
+               .cmd = NCSI_CMD_SET_PACKAGE_MASK,
+               .policy = ncsi_genl_policy,
+               .doit = ncsi_set_package_mask_nl,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NCSI_CMD_SET_CHANNEL_MASK,
+               .policy = ncsi_genl_policy,
+               .doit = ncsi_set_channel_mask_nl,
+               .flags = GENL_ADMIN_PERM,
+       },
 };
 
 static struct genl_family ncsi_genl_family __ro_after_init = {
index 4d3f06b..2a6d83a 100644 (file)
@@ -165,6 +165,15 @@ struct ncsi_rsp_oem_pkt {
        unsigned char           data[];      /* Payload data      */
 };
 
+/* Mellanox Response Data */
+struct ncsi_rsp_oem_mlx_pkt {
+       unsigned char           cmd_rev;     /* Command Revision  */
+       unsigned char           cmd;         /* Command ID        */
+       unsigned char           param;       /* Parameter         */
+       unsigned char           optional;    /* Optional data     */
+       unsigned char           data[];      /* Data              */
+};
+
 /* Broadcom Response Data */
 struct ncsi_rsp_oem_bcm_pkt {
        unsigned char           ver;         /* Payload Version   */
index 77e07ba..dc07fcc 100644 (file)
@@ -256,7 +256,7 @@ static int ncsi_rsp_handler_dcnt(struct ncsi_request *nr)
        if (!ncm->enable)
                return 0;
 
-       ncm->enable = 1;
+       ncm->enable = 0;
        return 0;
 }
 
@@ -611,6 +611,45 @@ static int ncsi_rsp_handler_snfc(struct ncsi_request *nr)
        return 0;
 }
 
+/* Response handler for Mellanox command Get Mac Address */
+static int ncsi_rsp_handler_oem_mlx_gma(struct ncsi_request *nr)
+{
+       struct ncsi_dev_priv *ndp = nr->ndp;
+       struct net_device *ndev = ndp->ndev.dev;
+       const struct net_device_ops *ops = ndev->netdev_ops;
+       struct ncsi_rsp_oem_pkt *rsp;
+       struct sockaddr saddr;
+       int ret = 0;
+
+       /* Get the response header */
+       rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp);
+
+       saddr.sa_family = ndev->type;
+       ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+       memcpy(saddr.sa_data, &rsp->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN);
+       ret = ops->ndo_set_mac_address(ndev, &saddr);
+       if (ret < 0)
+               netdev_warn(ndev, "NCSI: 'Writing mac address to device failed\n");
+
+       return ret;
+}
+
+/* Response handler for Mellanox card */
+static int ncsi_rsp_handler_oem_mlx(struct ncsi_request *nr)
+{
+       struct ncsi_rsp_oem_mlx_pkt *mlx;
+       struct ncsi_rsp_oem_pkt *rsp;
+
+       /* Get the response header */
+       rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp);
+       mlx = (struct ncsi_rsp_oem_mlx_pkt *)(rsp->data);
+
+       if (mlx->cmd == NCSI_OEM_MLX_CMD_GMA &&
+           mlx->param == NCSI_OEM_MLX_CMD_GMA_PARAM)
+               return ncsi_rsp_handler_oem_mlx_gma(nr);
+       return 0;
+}
+
 /* Response handler for Broadcom command Get Mac Address */
 static int ncsi_rsp_handler_oem_bcm_gma(struct ncsi_request *nr)
 {
@@ -655,7 +694,7 @@ static struct ncsi_rsp_oem_handler {
        unsigned int    mfr_id;
        int             (*handler)(struct ncsi_request *nr);
 } ncsi_rsp_oem_handlers[] = {
-       { NCSI_OEM_MFR_MLX_ID, NULL },
+       { NCSI_OEM_MFR_MLX_ID, ncsi_rsp_handler_oem_mlx },
        { NCSI_OEM_MFR_BCM_ID, ncsi_rsp_handler_oem_bcm }
 };
 
index 4eef55d..8da228d 100644 (file)
@@ -531,8 +531,8 @@ nla_put_failure:
                ret = -EMSGSIZE;
        } else {
                cb->args[IPSET_CB_ARG0] = i;
+               ipset_nest_end(skb, atd);
        }
-       ipset_nest_end(skb, atd);
 out:
        rcu_read_unlock();
        return ret;
index 83395bf..432141f 100644 (file)
@@ -3980,6 +3980,9 @@ static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs)
 
 static struct notifier_block ip_vs_dst_notifier = {
        .notifier_call = ip_vs_dst_event,
+#ifdef CONFIG_IP_VS_IPV6
+       .priority = ADDRCONF_NOTIFY_PRIORITY + 5,
+#endif
 };
 
 int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs)
index 02ca7df..9cd180b 100644 (file)
@@ -49,6 +49,7 @@ struct nf_conncount_tuple {
        struct nf_conntrack_zone        zone;
        int                             cpu;
        u32                             jiffies32;
+       bool                            dead;
        struct rcu_head                 rcu_head;
 };
 
@@ -106,15 +107,16 @@ nf_conncount_add(struct nf_conncount_list *list,
        conn->zone = *zone;
        conn->cpu = raw_smp_processor_id();
        conn->jiffies32 = (u32)jiffies;
-       spin_lock(&list->list_lock);
+       conn->dead = false;
+       spin_lock_bh(&list->list_lock);
        if (list->dead == true) {
                kmem_cache_free(conncount_conn_cachep, conn);
-               spin_unlock(&list->list_lock);
+               spin_unlock_bh(&list->list_lock);
                return NF_CONNCOUNT_SKIP;
        }
        list_add_tail(&conn->node, &list->head);
        list->count++;
-       spin_unlock(&list->list_lock);
+       spin_unlock_bh(&list->list_lock);
        return NF_CONNCOUNT_ADDED;
 }
 EXPORT_SYMBOL_GPL(nf_conncount_add);
@@ -132,19 +134,22 @@ static bool conn_free(struct nf_conncount_list *list,
 {
        bool free_entry = false;
 
-       spin_lock(&list->list_lock);
+       spin_lock_bh(&list->list_lock);
 
-       if (list->count == 0) {
-               spin_unlock(&list->list_lock);
-                return free_entry;
+       if (conn->dead) {
+               spin_unlock_bh(&list->list_lock);
+               return free_entry;
        }
 
        list->count--;
+       conn->dead = true;
        list_del_rcu(&conn->node);
-       if (list->count == 0)
+       if (list->count == 0) {
+               list->dead = true;
                free_entry = true;
+       }
 
-       spin_unlock(&list->list_lock);
+       spin_unlock_bh(&list->list_lock);
        call_rcu(&conn->rcu_head, __conn_free);
        return free_entry;
 }
@@ -245,7 +250,7 @@ void nf_conncount_list_init(struct nf_conncount_list *list)
 {
        spin_lock_init(&list->list_lock);
        INIT_LIST_HEAD(&list->head);
-       list->count = 1;
+       list->count = 0;
        list->dead = false;
 }
 EXPORT_SYMBOL_GPL(nf_conncount_list_init);
@@ -259,6 +264,7 @@ bool nf_conncount_gc_list(struct net *net,
        struct nf_conn *found_ct;
        unsigned int collected = 0;
        bool free_entry = false;
+       bool ret = false;
 
        list_for_each_entry_safe(conn, conn_n, &list->head, node) {
                found = find_or_evict(net, list, conn, &free_entry);
@@ -288,7 +294,15 @@ bool nf_conncount_gc_list(struct net *net,
                if (collected > CONNCOUNT_GC_MAX_NODES)
                        return false;
        }
-       return false;
+
+       spin_lock_bh(&list->list_lock);
+       if (!list->count) {
+               list->dead = true;
+               ret = true;
+       }
+       spin_unlock_bh(&list->list_lock);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(nf_conncount_gc_list);
 
@@ -309,11 +323,8 @@ static void tree_nodes_free(struct rb_root *root,
        while (gc_count) {
                rbconn = gc_nodes[--gc_count];
                spin_lock(&rbconn->list.list_lock);
-               if (rbconn->list.count == 0 && rbconn->list.dead == false) {
-                       rbconn->list.dead = true;
-                       rb_erase(&rbconn->node, root);
-                       call_rcu(&rbconn->rcu_head, __tree_nodes_free);
-               }
+               rb_erase(&rbconn->node, root);
+               call_rcu(&rbconn->rcu_head, __tree_nodes_free);
                spin_unlock(&rbconn->list.list_lock);
        }
 }
@@ -414,8 +425,9 @@ insert_tree(struct net *net,
        nf_conncount_list_init(&rbconn->list);
        list_add(&conn->node, &rbconn->list.head);
        count = 1;
+       rbconn->list.count = count;
 
-       rb_link_node(&rbconn->node, parent, rbnode);
+       rb_link_node_rcu(&rbconn->node, parent, rbnode);
        rb_insert_color(&rbconn->node, root);
 out_unlock:
        spin_unlock_bh(&nf_conncount_locks[hash % CONNCOUNT_LOCK_SLOTS]);
index d5e76bc..8899b51 100644 (file)
 #include <linux/netfilter/nf_conntrack_proto_gre.h>
 #include <linux/netfilter/nf_conntrack_pptp.h>
 
-enum grep_conntrack {
-       GRE_CT_UNREPLIED,
-       GRE_CT_REPLIED,
-       GRE_CT_MAX
-};
-
 static const unsigned int gre_timeouts[GRE_CT_MAX] = {
        [GRE_CT_UNREPLIED]      = 30*HZ,
        [GRE_CT_REPLIED]        = 180*HZ,
 };
 
 static unsigned int proto_gre_net_id __read_mostly;
-struct netns_proto_gre {
-       struct nf_proto_net     nf;
-       rwlock_t                keymap_lock;
-       struct list_head        keymap_list;
-       unsigned int            gre_timeouts[GRE_CT_MAX];
-};
 
 static inline struct netns_proto_gre *gre_pernet(struct net *net)
 {
@@ -442,6 +430,8 @@ static int __init nf_ct_proto_gre_init(void)
 {
        int ret;
 
+       BUILD_BUG_ON(offsetof(struct netns_proto_gre, nf) != 0);
+
        ret = register_pernet_subsys(&proto_gre_net_ops);
        if (ret < 0)
                goto out_pernet;
index a975efd..9da3034 100644 (file)
@@ -115,12 +115,12 @@ static void nf_ct_sack_block_adjust(struct sk_buff *skb,
 /* TCP SACK sequence number adjustment */
 static unsigned int nf_ct_sack_adjust(struct sk_buff *skb,
                                      unsigned int protoff,
-                                     struct tcphdr *tcph,
                                      struct nf_conn *ct,
                                      enum ip_conntrack_info ctinfo)
 {
-       unsigned int dir, optoff, optend;
+       struct tcphdr *tcph = (void *)skb->data + protoff;
        struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
+       unsigned int dir, optoff, optend;
 
        optoff = protoff + sizeof(struct tcphdr);
        optend = protoff + tcph->doff * 4;
@@ -128,6 +128,7 @@ static unsigned int nf_ct_sack_adjust(struct sk_buff *skb,
        if (!skb_make_writable(skb, optend))
                return 0;
 
+       tcph = (void *)skb->data + protoff;
        dir = CTINFO2DIR(ctinfo);
 
        while (optoff < optend) {
@@ -207,7 +208,7 @@ int nf_ct_seq_adjust(struct sk_buff *skb,
                 ntohl(newack));
        tcph->ack_seq = newack;
 
-       res = nf_ct_sack_adjust(skb, protoff, tcph, ct, ctinfo);
+       res = nf_ct_sack_adjust(skb, protoff, ct, ctinfo);
 out:
        spin_unlock_bh(&ct->lock);
 
index a8c5c84..3a0d688 100644 (file)
@@ -156,22 +156,20 @@ nf_log_dump_packet_common(struct nf_log_buf *m, u_int8_t pf,
                          const struct net_device *out,
                          const struct nf_loginfo *loginfo, const char *prefix)
 {
+       const struct net_device *physoutdev __maybe_unused;
+       const struct net_device *physindev __maybe_unused;
+
        nf_log_buf_add(m, KERN_SOH "%c%sIN=%s OUT=%s ",
               '0' + loginfo->u.log.level, prefix,
               in ? in->name : "",
               out ? out->name : "");
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-       if (skb->nf_bridge) {
-               const struct net_device *physindev;
-               const struct net_device *physoutdev;
-
-               physindev = nf_bridge_get_physindev(skb);
-               if (physindev && in != physindev)
-                       nf_log_buf_add(m, "PHYSIN=%s ", physindev->name);
-               physoutdev = nf_bridge_get_physoutdev(skb);
-               if (physoutdev && out != physoutdev)
-                       nf_log_buf_add(m, "PHYSOUT=%s ", physoutdev->name);
-       }
+       physindev = nf_bridge_get_physindev(skb);
+       if (physindev && in != physindev)
+               nf_log_buf_add(m, "PHYSIN=%s ", physindev->name);
+       physoutdev = nf_bridge_get_physoutdev(skb);
+       if (physoutdev && out != physoutdev)
+               nf_log_buf_add(m, "PHYSOUT=%s ", physoutdev->name);
 #endif
 }
 EXPORT_SYMBOL_GPL(nf_log_dump_packet_common);
index 9935b66..d159e9e 100644 (file)
@@ -107,7 +107,8 @@ int nf_xfrm_me_harder(struct net *net, struct sk_buff *skb, unsigned int family)
        dst = skb_dst(skb);
        if (dst->xfrm)
                dst = ((struct xfrm_dst *)dst)->route;
-       dst_hold(dst);
+       if (!dst_hold_safe(dst))
+               return -EHOSTUNREACH;
 
        if (sk && !net_eq(net, sock_net(sk)))
                sk = NULL;
index d67a96a..a36a77b 100644 (file)
@@ -46,6 +46,24 @@ void nf_unregister_queue_handler(struct net *net)
 }
 EXPORT_SYMBOL(nf_unregister_queue_handler);
 
+static void nf_queue_entry_release_br_nf_refs(struct sk_buff *skb)
+{
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
+
+       if (nf_bridge) {
+               struct net_device *physdev;
+
+               physdev = nf_bridge_get_physindev(skb);
+               if (physdev)
+                       dev_put(physdev);
+               physdev = nf_bridge_get_physoutdev(skb);
+               if (physdev)
+                       dev_put(physdev);
+       }
+#endif
+}
+
 void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
 {
        struct nf_hook_state *state = &entry->state;
@@ -57,20 +75,28 @@ void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
                dev_put(state->out);
        if (state->sk)
                sock_put(state->sk);
+
+       nf_queue_entry_release_br_nf_refs(entry->skb);
+}
+EXPORT_SYMBOL_GPL(nf_queue_entry_release_refs);
+
+static void nf_queue_entry_get_br_nf_refs(struct sk_buff *skb)
+{
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-       if (entry->skb->nf_bridge) {
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
+
+       if (nf_bridge) {
                struct net_device *physdev;
 
-               physdev = nf_bridge_get_physindev(entry->skb);
+               physdev = nf_bridge_get_physindev(skb);
                if (physdev)
-                       dev_put(physdev);
-               physdev = nf_bridge_get_physoutdev(entry->skb);
+                       dev_hold(physdev);
+               physdev = nf_bridge_get_physoutdev(skb);
                if (physdev)
-                       dev_put(physdev);
+                       dev_hold(physdev);
        }
 #endif
 }
-EXPORT_SYMBOL_GPL(nf_queue_entry_release_refs);
 
 /* Bump dev refs so they don't vanish while packet is out */
 void nf_queue_entry_get_refs(struct nf_queue_entry *entry)
@@ -83,18 +109,8 @@ void nf_queue_entry_get_refs(struct nf_queue_entry *entry)
                dev_hold(state->out);
        if (state->sk)
                sock_hold(state->sk);
-#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-       if (entry->skb->nf_bridge) {
-               struct net_device *physdev;
 
-               physdev = nf_bridge_get_physindev(entry->skb);
-               if (physdev)
-                       dev_hold(physdev);
-               physdev = nf_bridge_get_physoutdev(entry->skb);
-               if (physdev)
-                       dev_hold(physdev);
-       }
-#endif
+       nf_queue_entry_get_br_nf_refs(entry->skb);
 }
 EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
 
index 200751a..fec814d 100644 (file)
@@ -1216,7 +1216,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
                if (nla_put_string(skb, NFTA_CHAIN_TYPE, basechain->type->name))
                        goto nla_put_failure;
 
-               if (basechain->stats && nft_dump_stats(skb, basechain->stats))
+               if (rcu_access_pointer(basechain->stats) &&
+                   nft_dump_stats(skb, rcu_dereference(basechain->stats)))
                        goto nla_put_failure;
        }
 
@@ -1392,7 +1393,8 @@ static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
        return newstats;
 }
 
-static void nft_chain_stats_replace(struct nft_base_chain *chain,
+static void nft_chain_stats_replace(struct net *net,
+                                   struct nft_base_chain *chain,
                                    struct nft_stats __percpu *newstats)
 {
        struct nft_stats __percpu *oldstats;
@@ -1400,8 +1402,9 @@ static void nft_chain_stats_replace(struct nft_base_chain *chain,
        if (newstats == NULL)
                return;
 
-       if (chain->stats) {
-               oldstats = nfnl_dereference(chain->stats, NFNL_SUBSYS_NFTABLES);
+       if (rcu_access_pointer(chain->stats)) {
+               oldstats = rcu_dereference_protected(chain->stats,
+                                       lockdep_commit_lock_is_held(net));
                rcu_assign_pointer(chain->stats, newstats);
                synchronize_rcu();
                free_percpu(oldstats);
@@ -1439,9 +1442,10 @@ static void nf_tables_chain_destroy(struct nft_ctx *ctx)
                struct nft_base_chain *basechain = nft_base_chain(chain);
 
                module_put(basechain->type->owner);
-               free_percpu(basechain->stats);
-               if (basechain->stats)
+               if (rcu_access_pointer(basechain->stats)) {
                        static_branch_dec(&nft_counters_enabled);
+                       free_percpu(rcu_dereference_raw(basechain->stats));
+               }
                kfree(chain->name);
                kfree(basechain);
        } else {
@@ -1590,7 +1594,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
                                kfree(basechain);
                                return PTR_ERR(stats);
                        }
-                       basechain->stats = stats;
+                       rcu_assign_pointer(basechain->stats, stats);
                        static_branch_inc(&nft_counters_enabled);
                }
 
@@ -2491,7 +2495,7 @@ err:
 static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
                                   struct nft_rule *rule)
 {
-       struct nft_expr *expr;
+       struct nft_expr *expr, *next;
 
        /*
         * Careful: some expressions might not be initialized in case this
@@ -2499,8 +2503,9 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
         */
        expr = nft_expr_first(rule);
        while (expr != nft_expr_last(rule) && expr->ops) {
+               next = nft_expr_next(expr);
                nf_tables_expr_destroy(ctx, expr);
-               expr = nft_expr_next(expr);
+               expr = next;
        }
        kfree(rule);
 }
@@ -2623,17 +2628,14 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
 
                if (chain->use == UINT_MAX)
                        return -EOVERFLOW;
-       }
-
-       if (nla[NFTA_RULE_POSITION]) {
-               if (!(nlh->nlmsg_flags & NLM_F_CREATE))
-                       return -EOPNOTSUPP;
 
-               pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
-               old_rule = __nft_rule_lookup(chain, pos_handle);
-               if (IS_ERR(old_rule)) {
-                       NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION]);
-                       return PTR_ERR(old_rule);
+               if (nla[NFTA_RULE_POSITION]) {
+                       pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
+                       old_rule = __nft_rule_lookup(chain, pos_handle);
+                       if (IS_ERR(old_rule)) {
+                               NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION]);
+                               return PTR_ERR(old_rule);
+                       }
                }
        }
 
@@ -2703,21 +2705,14 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
        }
 
        if (nlh->nlmsg_flags & NLM_F_REPLACE) {
-               if (!nft_is_active_next(net, old_rule)) {
-                       err = -ENOENT;
-                       goto err2;
-               }
-               trans = nft_trans_rule_add(&ctx, NFT_MSG_DELRULE,
-                                          old_rule);
+               trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule);
                if (trans == NULL) {
                        err = -ENOMEM;
                        goto err2;
                }
-               nft_deactivate_next(net, old_rule);
-               chain->use--;
-
-               if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
-                       err = -ENOMEM;
+               err = nft_delrule(&ctx, old_rule);
+               if (err < 0) {
+                       nft_trans_destroy(trans);
                        goto err2;
                }
 
@@ -6223,7 +6218,8 @@ static void nft_chain_commit_update(struct nft_trans *trans)
                return;
 
        basechain = nft_base_chain(trans->ctx.chain);
-       nft_chain_stats_replace(basechain, nft_trans_chain_stats(trans));
+       nft_chain_stats_replace(trans->ctx.net, basechain,
+                               nft_trans_chain_stats(trans));
 
        switch (nft_trans_chain_policy(trans)) {
        case NF_DROP:
@@ -6358,7 +6354,7 @@ static void nf_tables_commit_chain_free_rules_old(struct nft_rule **rules)
        call_rcu(&old->h, __nf_tables_commit_chain_free_rules_old);
 }
 
-static void nf_tables_commit_chain_active(struct net *net, struct nft_chain *chain)
+static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain)
 {
        struct nft_rule **g0, **g1;
        bool next_genbit;
@@ -6475,11 +6471,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
 
        /* step 2.  Make rules_gen_X visible to packet path */
        list_for_each_entry(table, &net->nft.tables, list) {
-               list_for_each_entry(chain, &table->chains, list) {
-                       if (!nft_is_active_next(net, chain))
-                               continue;
-                       nf_tables_commit_chain_active(net, chain);
-               }
+               list_for_each_entry(chain, &table->chains, list)
+                       nf_tables_commit_chain(net, chain);
        }
 
        /*
index 3fbce3b..a505002 100644 (file)
@@ -101,7 +101,7 @@ static noinline void nft_update_chain_stats(const struct nft_chain *chain,
        struct nft_stats *stats;
 
        base_chain = nft_base_chain(chain);
-       if (!base_chain->stats)
+       if (!rcu_access_pointer(base_chain->stats))
                return;
 
        local_bh_disable();
index a518eb1..109b0d2 100644 (file)
@@ -455,7 +455,8 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl,
        case IPPROTO_TCP:
                timeouts = nf_tcp_pernet(net)->timeouts;
                break;
-       case IPPROTO_UDP:
+       case IPPROTO_UDP: /* fallthrough */
+       case IPPROTO_UDPLITE:
                timeouts = nf_udp_pernet(net)->timeouts;
                break;
        case IPPROTO_DCCP:
@@ -469,13 +470,23 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl,
        case IPPROTO_SCTP:
 #ifdef CONFIG_NF_CT_PROTO_SCTP
                timeouts = nf_sctp_pernet(net)->timeouts;
+#endif
+               break;
+       case IPPROTO_GRE:
+#ifdef CONFIG_NF_CT_PROTO_GRE
+               if (l4proto->net_id) {
+                       struct netns_proto_gre *net_gre;
+
+                       net_gre = net_generic(net, *l4proto->net_id);
+                       timeouts = net_gre->gre_timeouts;
+               }
 #endif
                break;
        case 255:
                timeouts = &nf_generic_pernet(net)->timeout;
                break;
        default:
-               WARN_ON_ONCE(1);
+               WARN_ONCE(1, "Missing timeouts for proto %d", l4proto->l4proto);
                break;
        }
 
index 1ce30ef..0dcc359 100644 (file)
@@ -727,13 +727,13 @@ nf_queue_entry_dup(struct nf_queue_entry *e)
  */
 static void nf_bridge_adjust_skb_data(struct sk_buff *skb)
 {
-       if (skb->nf_bridge)
+       if (nf_bridge_info_get(skb))
                __skb_push(skb, skb->network_header - skb->mac_header);
 }
 
 static void nf_bridge_adjust_segmented_data(struct sk_buff *skb)
 {
-       if (skb->nf_bridge)
+       if (nf_bridge_info_get(skb))
                __skb_pull(skb, skb->network_header - skb->mac_header);
 }
 #else
@@ -904,23 +904,22 @@ nfqnl_set_mode(struct nfqnl_instance *queue,
 static int
 dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)
 {
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+       int physinif, physoutif;
+
+       physinif = nf_bridge_get_physinif(entry->skb);
+       physoutif = nf_bridge_get_physoutif(entry->skb);
+
+       if (physinif == ifindex || physoutif == ifindex)
+               return 1;
+#endif
        if (entry->state.in)
                if (entry->state.in->ifindex == ifindex)
                        return 1;
        if (entry->state.out)
                if (entry->state.out->ifindex == ifindex)
                        return 1;
-#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-       if (entry->skb->nf_bridge) {
-               int physinif, physoutif;
 
-               physinif = nf_bridge_get_physinif(entry->skb);
-               physoutif = nf_bridge_get_physoutif(entry->skb);
-
-               if (physinif == ifindex || physoutif == ifindex)
-                       return 1;
-       }
-#endif
        return 0;
 }
 
index 9d0ede4..7334e0b 100644 (file)
@@ -520,6 +520,7 @@ __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,
                    void *info)
 {
        struct xt_match *match = expr->ops->data;
+       struct module *me = match->me;
        struct xt_mtdtor_param par;
 
        par.net = ctx->net;
@@ -530,7 +531,7 @@ __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,
                par.match->destroy(&par);
 
        if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
-               module_put(match->me);
+               module_put(me);
 }
 
 static void
index e82d9a9..974525e 100644 (file)
@@ -214,7 +214,9 @@ static int __init nft_flow_offload_module_init(void)
 {
        int err;
 
-       register_netdevice_notifier(&flow_offload_netdev_notifier);
+       err = register_netdevice_notifier(&flow_offload_netdev_notifier);
+       if (err)
+               goto err;
 
        err = nft_register_expr(&nft_flow_offload_type);
        if (err < 0)
@@ -224,6 +226,7 @@ static int __init nft_flow_offload_module_init(void)
 
 register_expr:
        unregister_netdevice_notifier(&flow_offload_netdev_notifier);
+err:
        return err;
 }
 
index 6180626..6df486c 100644 (file)
@@ -229,7 +229,7 @@ void nft_meta_get_eval(const struct nft_expr *expr,
        }
 #ifdef CONFIG_XFRM
        case NFT_META_SECPATH:
-               nft_reg_store8(dest, !!skb->sp);
+               nft_reg_store8(dest, secpath_exists(skb));
                break;
 #endif
 #ifdef CONFIG_NF_TABLES_BRIDGE
index 5322609..b08865e 100644 (file)
@@ -161,7 +161,7 @@ static void nft_xfrm_get_eval_in(const struct nft_xfrm *priv,
                                    struct nft_regs *regs,
                                    const struct nft_pktinfo *pkt)
 {
-       const struct sec_path *sp = pkt->skb->sp;
+       const struct sec_path *sp = skb_sec_path(pkt->skb);
        const struct xfrm_state *state;
 
        if (sp == NULL || sp->len <= priv->spnum) {
index dec843c..9e05c86 100644 (file)
@@ -201,18 +201,8 @@ static __net_init int xt_rateest_net_init(struct net *net)
        return 0;
 }
 
-static void __net_exit xt_rateest_net_exit(struct net *net)
-{
-       struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(xn->hash); i++)
-               WARN_ON_ONCE(!hlist_empty(&xn->hash[i]));
-}
-
 static struct pernet_operations xt_rateest_net_ops = {
        .init = xt_rateest_net_init,
-       .exit = xt_rateest_net_exit,
        .id   = &xt_rateest_id,
        .size = sizeof(struct xt_rateest_net),
 };
index 8138b68..28e27a3 100644 (file)
@@ -295,9 +295,10 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
 
        /* copy match config into hashtable config */
        ret = cfg_copy(&hinfo->cfg, (void *)cfg, 3);
-
-       if (ret)
+       if (ret) {
+               vfree(hinfo);
                return ret;
+       }
 
        hinfo->cfg.size = size;
        if (hinfo->cfg.max == 0)
@@ -814,7 +815,6 @@ hashlimit_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
        int ret;
 
        ret = cfg_copy(&cfg, (void *)&info->cfg, 1);
-
        if (ret)
                return ret;
 
@@ -830,7 +830,6 @@ hashlimit_mt_v2(const struct sk_buff *skb, struct xt_action_param *par)
        int ret;
 
        ret = cfg_copy(&cfg, (void *)&info->cfg, 2);
-
        if (ret)
                return ret;
 
@@ -921,7 +920,6 @@ static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par)
                return ret;
 
        ret = cfg_copy(&cfg, (void *)&info->cfg, 1);
-
        if (ret)
                return ret;
 
@@ -940,7 +938,6 @@ static int hashlimit_mt_check_v2(const struct xt_mtchk_param *par)
                return ret;
 
        ret = cfg_copy(&cfg, (void *)&info->cfg, 2);
-
        if (ret)
                return ret;
 
index 9d6d67b..4034d70 100644 (file)
@@ -33,7 +33,7 @@ physdev_mt(const struct sk_buff *skb, struct xt_action_param *par)
        /* Not a bridged IP packet or no info available yet:
         * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
         * the destination device will be a bridge. */
-       if (!skb->nf_bridge) {
+       if (!nf_bridge_info_exists(skb)) {
                /* Return MATCH if the invert flags of the used options are on */
                if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
                    !(info->invert & XT_PHYSDEV_OP_BRIDGED))
index 13f8ccf..aa84e81 100644 (file)
@@ -56,7 +56,7 @@ match_policy_in(const struct sk_buff *skb, const struct xt_policy_info *info,
                unsigned short family)
 {
        const struct xt_policy_elem *e;
-       const struct sec_path *sp = skb->sp;
+       const struct sec_path *sp = skb_sec_path(skb);
        int strict = info->flags & XT_POLICY_MATCH_STRICT;
        int i, pos;
 
index 6bb9f3c..3c023d6 100644 (file)
@@ -1706,7 +1706,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
                        nlk->flags &= ~NETLINK_F_EXT_ACK;
                err = 0;
                break;
-       case NETLINK_DUMP_STRICT_CHK:
+       case NETLINK_GET_STRICT_CHK:
                if (val)
                        nlk->flags |= NETLINK_F_STRICT_CHK;
                else
@@ -1806,7 +1806,7 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname,
                        return -EFAULT;
                err = 0;
                break;
-       case NETLINK_DUMP_STRICT_CHK:
+       case NETLINK_GET_STRICT_CHK:
                if (len < sizeof(int))
                        return -EINVAL;
                len = sizeof(int);
index a4660c4..cd94f92 100644 (file)
@@ -1166,7 +1166,7 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key,
                                &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
                        if (err) {
                                net_warn_ratelimited("openvswitch: zone: %u "
-                                       "execeeds conntrack limit\n",
+                                       "exceeds conntrack limit\n",
                                        info->zone.id);
                                return err;
                        }
index 5aaf3ba..acb6077 100644 (file)
@@ -93,7 +93,7 @@ static struct vport *geneve_tnl_create(const struct vport_parms *parms)
                return ERR_CAST(dev);
        }
 
-       err = dev_change_flags(dev, dev->flags | IFF_UP);
+       err = dev_change_flags(dev, dev->flags | IFF_UP, NULL);
        if (err < 0) {
                rtnl_delete_link(dev);
                rtnl_unlock();
index 0e72d95..c38a624 100644 (file)
@@ -68,7 +68,7 @@ static struct vport *gre_tnl_create(const struct vport_parms *parms)
                return ERR_CAST(dev);
        }
 
-       err = dev_change_flags(dev, dev->flags | IFF_UP);
+       err = dev_change_flags(dev, dev->flags | IFF_UP, NULL);
        if (err < 0) {
                rtnl_delete_link(dev);
                rtnl_unlock();
index 7e6301b..8f16f11 100644 (file)
@@ -131,7 +131,7 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms)
                return ERR_CAST(dev);
        }
 
-       err = dev_change_flags(dev, dev->flags | IFF_UP);
+       err = dev_change_flags(dev, dev->flags | IFF_UP, NULL);
        if (err < 0) {
                rtnl_delete_link(dev);
                rtnl_unlock();
index ec3095f..6655793 100644 (file)
@@ -1965,7 +1965,7 @@ retry:
        skb->mark = sk->sk_mark;
        skb->tstamp = sockc.transmit_time;
 
-       sock_tx_timestamp(sk, sockc.tsflags, &skb_shinfo(skb)->tx_flags);
+       skb_setup_tx_timestamp(skb, sockc.tsflags);
 
        if (unlikely(extra_len == 4))
                skb->no_fcs = 1;
@@ -2394,7 +2394,7 @@ static void tpacket_destruct_skb(struct sk_buff *skb)
                void *ph;
                __u32 ts;
 
-               ph = skb_shinfo(skb)->destructor_arg;
+               ph = skb_zcopy_get_nouarg(skb);
                packet_dec_pending(&po->tx_ring);
 
                ts = __packet_set_timestamp(po, ph, skb);
@@ -2460,8 +2460,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
        skb->priority = po->sk.sk_priority;
        skb->mark = po->sk.sk_mark;
        skb->tstamp = sockc->transmit_time;
-       sock_tx_timestamp(&po->sk, sockc->tsflags, &skb_shinfo(skb)->tx_flags);
-       skb_shinfo(skb)->destructor_arg = ph.raw;
+       skb_setup_tx_timestamp(skb, sockc->tsflags);
+       skb_zcopy_set_nouarg(skb, ph.raw);
 
        skb_reserve(skb, hlen);
        skb_reset_network_header(skb);
@@ -2898,7 +2898,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
                goto out_free;
        }
 
-       sock_tx_timestamp(sk, sockc.tsflags, &skb_shinfo(skb)->tx_flags);
+       skb_setup_tx_timestamp(skb, sockc.tsflags);
 
        if (!vnet_hdr.gso_type && (len > dev->mtu + reserve + extra_len) &&
            !packet_extra_vlan_len_allowed(dev, skb)) {
index 4b00b11..f139420 100644 (file)
@@ -308,16 +308,27 @@ out:
 /*
  * RDS ops use this to grab SG entries from the rm's sg pool.
  */
-struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents)
+struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents,
+                                         int *ret)
 {
        struct scatterlist *sg_first = (struct scatterlist *) &rm[1];
        struct scatterlist *sg_ret;
 
-       WARN_ON(rm->m_used_sgs + nents > rm->m_total_sgs);
-       WARN_ON(!nents);
+       if (WARN_ON(!ret))
+               return NULL;
 
-       if (rm->m_used_sgs + nents > rm->m_total_sgs)
+       if (nents <= 0) {
+               pr_warn("rds: alloc sgs failed! nents <= 0\n");
+               *ret = -EINVAL;
                return NULL;
+       }
+
+       if (rm->m_used_sgs + nents > rm->m_total_sgs) {
+               pr_warn("rds: alloc sgs failed! total %d used %d nents %d\n",
+                       rm->m_total_sgs, rm->m_used_sgs, nents);
+               *ret = -ENOMEM;
+               return NULL;
+       }
 
        sg_ret = &sg_first[rm->m_used_sgs];
        sg_init_table(sg_ret, nents);
@@ -332,6 +343,7 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned in
        unsigned int i;
        int num_sgs = ceil(total_len, PAGE_SIZE);
        int extra_bytes = num_sgs * sizeof(struct scatterlist);
+       int ret;
 
        rm = rds_message_alloc(extra_bytes, GFP_NOWAIT);
        if (!rm)
@@ -340,10 +352,10 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned in
        set_bit(RDS_MSG_PAGEVEC, &rm->m_flags);
        rm->m_inc.i_hdr.h_len = cpu_to_be32(total_len);
        rm->data.op_nents = ceil(total_len, PAGE_SIZE);
-       rm->data.op_sg = rds_message_alloc_sgs(rm, num_sgs);
+       rm->data.op_sg = rds_message_alloc_sgs(rm, num_sgs, &ret);
        if (!rm->data.op_sg) {
                rds_message_put(rm);
-               return ERR_PTR(-ENOMEM);
+               return ERR_PTR(ret);
        }
 
        for (i = 0; i < rm->data.op_nents; ++i) {
index 98237fe..182ab84 100644 (file)
@@ -517,9 +517,10 @@ static int rds_rdma_pages(struct rds_iovec iov[], int nr_iovecs)
        return tot_pages;
 }
 
-int rds_rdma_extra_size(struct rds_rdma_args *args)
+int rds_rdma_extra_size(struct rds_rdma_args *args,
+                       struct rds_iov_vector *iov)
 {
-       struct rds_iovec vec;
+       struct rds_iovec *vec;
        struct rds_iovec __user *local_vec;
        int tot_pages = 0;
        unsigned int nr_pages;
@@ -530,13 +531,23 @@ int rds_rdma_extra_size(struct rds_rdma_args *args)
        if (args->nr_local == 0)
                return -EINVAL;
 
+       iov->iov = kcalloc(args->nr_local,
+                          sizeof(struct rds_iovec),
+                          GFP_KERNEL);
+       if (!iov->iov)
+               return -ENOMEM;
+
+       vec = &iov->iov[0];
+
+       if (copy_from_user(vec, local_vec, args->nr_local *
+                          sizeof(struct rds_iovec)))
+               return -EFAULT;
+       iov->len = args->nr_local;
+
        /* figure out the number of pages in the vector */
-       for (i = 0; i < args->nr_local; i++) {
-               if (copy_from_user(&vec, &local_vec[i],
-                                  sizeof(struct rds_iovec)))
-                       return -EFAULT;
+       for (i = 0; i < args->nr_local; i++, vec++) {
 
-               nr_pages = rds_pages_in_vec(&vec);
+               nr_pages = rds_pages_in_vec(vec);
                if (nr_pages == 0)
                        return -EINVAL;
 
@@ -558,15 +569,15 @@ int rds_rdma_extra_size(struct rds_rdma_args *args)
  * Extract all arguments and set up the rdma_op
  */
 int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
-                         struct cmsghdr *cmsg)
+                      struct cmsghdr *cmsg,
+                      struct rds_iov_vector *vec)
 {
        struct rds_rdma_args *args;
        struct rm_rdma_op *op = &rm->rdma;
        int nr_pages;
        unsigned int nr_bytes;
        struct page **pages = NULL;
-       struct rds_iovec iovstack[UIO_FASTIOV], *iovs = iovstack;
-       int iov_size;
+       struct rds_iovec *iovs;
        unsigned int i, j;
        int ret = 0;
 
@@ -586,31 +597,23 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
                goto out_ret;
        }
 
-       /* Check whether to allocate the iovec area */
-       iov_size = args->nr_local * sizeof(struct rds_iovec);
-       if (args->nr_local > UIO_FASTIOV) {
-               iovs = sock_kmalloc(rds_rs_to_sk(rs), iov_size, GFP_KERNEL);
-               if (!iovs) {
-                       ret = -ENOMEM;
-                       goto out_ret;
-               }
+       if (vec->len != args->nr_local) {
+               ret = -EINVAL;
+               goto out_ret;
        }
 
-       if (copy_from_user(iovs, (struct rds_iovec __user *)(unsigned long) args->local_vec_addr, iov_size)) {
-               ret = -EFAULT;
-               goto out;
-       }
+       iovs = vec->iov;
 
        nr_pages = rds_rdma_pages(iovs, args->nr_local);
        if (nr_pages < 0) {
                ret = -EINVAL;
-               goto out;
+               goto out_ret;
        }
 
        pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL);
        if (!pages) {
                ret = -ENOMEM;
-               goto out;
+               goto out_ret;
        }
 
        op->op_write = !!(args->flags & RDS_RDMA_READWRITE);
@@ -620,11 +623,9 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
        op->op_active = 1;
        op->op_recverr = rs->rs_recverr;
        WARN_ON(!nr_pages);
-       op->op_sg = rds_message_alloc_sgs(rm, nr_pages);
-       if (!op->op_sg) {
-               ret = -ENOMEM;
-               goto out;
-       }
+       op->op_sg = rds_message_alloc_sgs(rm, nr_pages, &ret);
+       if (!op->op_sg)
+               goto out_pages;
 
        if (op->op_notify || op->op_recverr) {
                /* We allocate an uninitialized notifier here, because
@@ -635,7 +636,7 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
                op->op_notifier = kmalloc(sizeof(struct rds_notifier), GFP_KERNEL);
                if (!op->op_notifier) {
                        ret = -ENOMEM;
-                       goto out;
+                       goto out_pages;
                }
                op->op_notifier->n_user_token = args->user_token;
                op->op_notifier->n_status = RDS_RDMA_SUCCESS;
@@ -681,7 +682,7 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
                 */
                ret = rds_pin_pages(iov->addr, nr, pages, !op->op_write);
                if (ret < 0)
-                       goto out;
+                       goto out_pages;
                else
                        ret = 0;
 
@@ -714,13 +715,11 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
                                nr_bytes,
                                (unsigned int) args->remote_vec.bytes);
                ret = -EINVAL;
-               goto out;
+               goto out_pages;
        }
        op->op_bytes = nr_bytes;
 
-out:
-       if (iovs != iovstack)
-               sock_kfree_s(rds_rs_to_sk(rs), iovs, iov_size);
+out_pages:
        kfree(pages);
 out_ret:
        if (ret)
@@ -838,11 +837,9 @@ int rds_cmsg_atomic(struct rds_sock *rs, struct rds_message *rm,
        rm->atomic.op_silent = !!(args->flags & RDS_RDMA_SILENT);
        rm->atomic.op_active = 1;
        rm->atomic.op_recverr = rs->rs_recverr;
-       rm->atomic.op_sg = rds_message_alloc_sgs(rm, 1);
-       if (!rm->atomic.op_sg) {
-               ret = -ENOMEM;
+       rm->atomic.op_sg = rds_message_alloc_sgs(rm, 1, &ret);
+       if (!rm->atomic.op_sg)
                goto err;
-       }
 
        /* verify 8 byte-aligned */
        if (args->local_addr & 0x7) {
index 6bfaf05..02ec4a3 100644 (file)
@@ -386,6 +386,18 @@ static inline void rds_message_zcopy_queue_init(struct rds_msg_zcopy_queue *q)
        INIT_LIST_HEAD(&q->zcookie_head);
 }
 
+struct rds_iov_vector {
+       struct rds_iovec *iov;
+       int               len;
+};
+
+struct rds_iov_vector_arr {
+       struct rds_iov_vector *vec;
+       int                    len;
+       int                    indx;
+       int                    incr;
+};
+
 struct rds_message {
        refcount_t              m_refcount;
        struct list_head        m_sock_item;
@@ -827,7 +839,8 @@ rds_conn_connecting(struct rds_connection *conn)
 
 /* message.c */
 struct rds_message *rds_message_alloc(unsigned int nents, gfp_t gfp);
-struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents);
+struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents,
+                                         int *ret);
 int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from,
                               bool zcopy);
 struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned int total_len);
@@ -904,13 +917,13 @@ int rds_get_mr(struct rds_sock *rs, char __user *optval, int optlen);
 int rds_get_mr_for_dest(struct rds_sock *rs, char __user *optval, int optlen);
 int rds_free_mr(struct rds_sock *rs, char __user *optval, int optlen);
 void rds_rdma_drop_keys(struct rds_sock *rs);
-int rds_rdma_extra_size(struct rds_rdma_args *args);
-int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
-                         struct cmsghdr *cmsg);
+int rds_rdma_extra_size(struct rds_rdma_args *args,
+                       struct rds_iov_vector *iov);
 int rds_cmsg_rdma_dest(struct rds_sock *rs, struct rds_message *rm,
                          struct cmsghdr *cmsg);
 int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
-                         struct cmsghdr *cmsg);
+                         struct cmsghdr *cmsg,
+                         struct rds_iov_vector *vec);
 int rds_cmsg_rdma_map(struct rds_sock *rs, struct rds_message *rm,
                          struct cmsghdr *cmsg);
 void rds_rdma_free_op(struct rm_rdma_op *ro);
index fe785ee..3d822ba 100644 (file)
@@ -876,13 +876,18 @@ out:
  * rds_message is getting to be quite complicated, and we'd like to allocate
  * it all in one go. This figures out how big it needs to be up front.
  */
-static int rds_rm_size(struct msghdr *msg, int num_sgs)
+static int rds_rm_size(struct msghdr *msg, int num_sgs,
+                      struct rds_iov_vector_arr *vct)
 {
        struct cmsghdr *cmsg;
        int size = 0;
        int cmsg_groups = 0;
        int retval;
        bool zcopy_cookie = false;
+       struct rds_iov_vector *iov, *tmp_iov;
+
+       if (num_sgs < 0)
+               return -EINVAL;
 
        for_each_cmsghdr(cmsg, msg) {
                if (!CMSG_OK(msg, cmsg))
@@ -893,8 +898,24 @@ static int rds_rm_size(struct msghdr *msg, int num_sgs)
 
                switch (cmsg->cmsg_type) {
                case RDS_CMSG_RDMA_ARGS:
+                       if (vct->indx >= vct->len) {
+                               vct->len += vct->incr;
+                               tmp_iov =
+                                       krealloc(vct->vec,
+                                                vct->len *
+                                                sizeof(struct rds_iov_vector),
+                                                GFP_KERNEL);
+                               if (!tmp_iov) {
+                                       vct->len -= vct->incr;
+                                       return -ENOMEM;
+                               }
+                               vct->vec = tmp_iov;
+                       }
+                       iov = &vct->vec[vct->indx];
+                       memset(iov, 0, sizeof(struct rds_iov_vector));
+                       vct->indx++;
                        cmsg_groups |= 1;
-                       retval = rds_rdma_extra_size(CMSG_DATA(cmsg));
+                       retval = rds_rdma_extra_size(CMSG_DATA(cmsg), iov);
                        if (retval < 0)
                                return retval;
                        size += retval;
@@ -951,10 +972,11 @@ static int rds_cmsg_zcopy(struct rds_sock *rs, struct rds_message *rm,
 }
 
 static int rds_cmsg_send(struct rds_sock *rs, struct rds_message *rm,
-                        struct msghdr *msg, int *allocated_mr)
+                        struct msghdr *msg, int *allocated_mr,
+                        struct rds_iov_vector_arr *vct)
 {
        struct cmsghdr *cmsg;
-       int ret = 0;
+       int ret = 0, ind = 0;
 
        for_each_cmsghdr(cmsg, msg) {
                if (!CMSG_OK(msg, cmsg))
@@ -968,7 +990,10 @@ static int rds_cmsg_send(struct rds_sock *rs, struct rds_message *rm,
                 */
                switch (cmsg->cmsg_type) {
                case RDS_CMSG_RDMA_ARGS:
-                       ret = rds_cmsg_rdma_args(rs, rm, cmsg);
+                       if (ind >= vct->indx)
+                               return -ENOMEM;
+                       ret = rds_cmsg_rdma_args(rs, rm, cmsg, &vct->vec[ind]);
+                       ind++;
                        break;
 
                case RDS_CMSG_RDMA_DEST:
@@ -1084,6 +1109,13 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
                      sock_flag(rds_rs_to_sk(rs), SOCK_ZEROCOPY));
        int num_sgs = ceil(payload_len, PAGE_SIZE);
        int namelen;
+       struct rds_iov_vector_arr vct;
+       int ind;
+
+       memset(&vct, 0, sizeof(vct));
+
+       /* expect 1 RDMA CMSG per rds_sendmsg. can still grow if more needed. */
+       vct.incr = 1;
 
        /* Mirror Linux UDP mirror of BSD error message compatibility */
        /* XXX: Perhaps MSG_MORE someday */
@@ -1220,7 +1252,7 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
                num_sgs = iov_iter_npages(&msg->msg_iter, INT_MAX);
        }
        /* size of rm including all sgs */
-       ret = rds_rm_size(msg, num_sgs);
+       ret = rds_rm_size(msg, num_sgs, &vct);
        if (ret < 0)
                goto out;
 
@@ -1232,11 +1264,9 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
 
        /* Attach data to the rm */
        if (payload_len) {
-               rm->data.op_sg = rds_message_alloc_sgs(rm, num_sgs);
-               if (!rm->data.op_sg) {
-                       ret = -ENOMEM;
+               rm->data.op_sg = rds_message_alloc_sgs(rm, num_sgs, &ret);
+               if (!rm->data.op_sg)
                        goto out;
-               }
                ret = rds_message_copy_from_user(rm, &msg->msg_iter, zcopy);
                if (ret)
                        goto out;
@@ -1270,7 +1300,7 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
        rm->m_conn_path = cpath;
 
        /* Parse any control messages the user may have included. */
-       ret = rds_cmsg_send(rs, rm, msg, &allocated_mr);
+       ret = rds_cmsg_send(rs, rm, msg, &allocated_mr, &vct);
        if (ret) {
                /* Trigger connection so that its ready for the next retry */
                if (ret ==  -EAGAIN)
@@ -1348,9 +1378,18 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
        if (ret)
                goto out;
        rds_message_put(rm);
+
+       for (ind = 0; ind < vct.indx; ind++)
+               kfree(vct.vec[ind].iov);
+       kfree(vct.vec);
+
        return payload_len;
 
 out:
+       for (ind = 0; ind < vct.indx; ind++)
+               kfree(vct.vec[ind].iov);
+       kfree(vct.vec);
+
        /* If the user included a RDMA_MAP cmsg, we allocated a MR on the fly.
         * If the sendmsg goes through, we keep the MR. If it fails with EAGAIN
         * or in any other way, we need to destroy the MR again */
index 0f84658..41a5cd4 100644 (file)
@@ -16,7 +16,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-#include <linux/gpio.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
index 64362d0..a2522f9 100644 (file)
@@ -375,16 +375,35 @@ EXPORT_SYMBOL(rxrpc_kernel_end_call);
  * getting ACKs from the server.  Returns a number representing the life state
  * which can be compared to that returned by a previous call.
  *
- * If this is a client call, ping ACKs will be sent to the server to find out
- * whether it's still responsive and whether the call is still alive on the
- * server.
+ * If the life state stalls, rxrpc_kernel_probe_life() should be called and
+ * then 2RTT waited.
  */
-u32 rxrpc_kernel_check_life(struct socket *sock, struct rxrpc_call *call)
+u32 rxrpc_kernel_check_life(const struct socket *sock,
+                           const struct rxrpc_call *call)
 {
        return call->acks_latest;
 }
 EXPORT_SYMBOL(rxrpc_kernel_check_life);
 
+/**
+ * rxrpc_kernel_probe_life - Poke the peer to see if it's still alive
+ * @sock: The socket the call is on
+ * @call: The call to check
+ *
+ * In conjunction with rxrpc_kernel_check_life(), allow a kernel service to
+ * find out whether a call is still alive by pinging it.  This should cause the
+ * life state to be bumped in about 2*RTT.
+ *
+ * The must be called in TASK_RUNNING state on pain of might_sleep() objecting.
+ */
+void rxrpc_kernel_probe_life(struct socket *sock, struct rxrpc_call *call)
+{
+       rxrpc_propose_ACK(call, RXRPC_ACK_PING, 0, 0, true, false,
+                         rxrpc_propose_ack_ping_for_check_life);
+       rxrpc_send_ack_packet(call, true, NULL);
+}
+EXPORT_SYMBOL(rxrpc_kernel_probe_life);
+
 /**
  * rxrpc_kernel_get_epoch - Retrieve the epoch value from a call.
  * @sock: The socket the call is on
index 9c1b072..d4b8355 100644 (file)
@@ -21,8 +21,6 @@
 #include <linux/kmod.h>
 #include <linux/err.h>
 #include <linux/module.h>
-#include <linux/rhashtable.h>
-#include <linux/list.h>
 #include <net/net_namespace.h>
 #include <net/sock.h>
 #include <net/sch_generic.h>
@@ -1522,227 +1520,8 @@ out_module_put:
        return skb->len;
 }
 
-struct tcf_action_net {
-       struct rhashtable egdev_ht;
-};
-
-static unsigned int tcf_action_net_id;
-
-struct tcf_action_egdev_cb {
-       struct list_head list;
-       tc_setup_cb_t *cb;
-       void *cb_priv;
-};
-
-struct tcf_action_egdev {
-       struct rhash_head ht_node;
-       const struct net_device *dev;
-       unsigned int refcnt;
-       struct list_head cb_list;
-};
-
-static const struct rhashtable_params tcf_action_egdev_ht_params = {
-       .key_offset = offsetof(struct tcf_action_egdev, dev),
-       .head_offset = offsetof(struct tcf_action_egdev, ht_node),
-       .key_len = sizeof(const struct net_device *),
-};
-
-static struct tcf_action_egdev *
-tcf_action_egdev_lookup(const struct net_device *dev)
-{
-       struct net *net = dev_net(dev);
-       struct tcf_action_net *tan = net_generic(net, tcf_action_net_id);
-
-       return rhashtable_lookup_fast(&tan->egdev_ht, &dev,
-                                     tcf_action_egdev_ht_params);
-}
-
-static struct tcf_action_egdev *
-tcf_action_egdev_get(const struct net_device *dev)
-{
-       struct tcf_action_egdev *egdev;
-       struct tcf_action_net *tan;
-
-       egdev = tcf_action_egdev_lookup(dev);
-       if (egdev)
-               goto inc_ref;
-
-       egdev = kzalloc(sizeof(*egdev), GFP_KERNEL);
-       if (!egdev)
-               return NULL;
-       INIT_LIST_HEAD(&egdev->cb_list);
-       egdev->dev = dev;
-       tan = net_generic(dev_net(dev), tcf_action_net_id);
-       rhashtable_insert_fast(&tan->egdev_ht, &egdev->ht_node,
-                              tcf_action_egdev_ht_params);
-
-inc_ref:
-       egdev->refcnt++;
-       return egdev;
-}
-
-static void tcf_action_egdev_put(struct tcf_action_egdev *egdev)
-{
-       struct tcf_action_net *tan;
-
-       if (--egdev->refcnt)
-               return;
-       tan = net_generic(dev_net(egdev->dev), tcf_action_net_id);
-       rhashtable_remove_fast(&tan->egdev_ht, &egdev->ht_node,
-                              tcf_action_egdev_ht_params);
-       kfree(egdev);
-}
-
-static struct tcf_action_egdev_cb *
-tcf_action_egdev_cb_lookup(struct tcf_action_egdev *egdev,
-                          tc_setup_cb_t *cb, void *cb_priv)
-{
-       struct tcf_action_egdev_cb *egdev_cb;
-
-       list_for_each_entry(egdev_cb, &egdev->cb_list, list)
-               if (egdev_cb->cb == cb && egdev_cb->cb_priv == cb_priv)
-                       return egdev_cb;
-       return NULL;
-}
-
-static int tcf_action_egdev_cb_call(struct tcf_action_egdev *egdev,
-                                   enum tc_setup_type type,
-                                   void *type_data, bool err_stop)
-{
-       struct tcf_action_egdev_cb *egdev_cb;
-       int ok_count = 0;
-       int err;
-
-       list_for_each_entry(egdev_cb, &egdev->cb_list, list) {
-               err = egdev_cb->cb(type, type_data, egdev_cb->cb_priv);
-               if (err) {
-                       if (err_stop)
-                               return err;
-               } else {
-                       ok_count++;
-               }
-       }
-       return ok_count;
-}
-
-static int tcf_action_egdev_cb_add(struct tcf_action_egdev *egdev,
-                                  tc_setup_cb_t *cb, void *cb_priv)
-{
-       struct tcf_action_egdev_cb *egdev_cb;
-
-       egdev_cb = tcf_action_egdev_cb_lookup(egdev, cb, cb_priv);
-       if (WARN_ON(egdev_cb))
-               return -EEXIST;
-       egdev_cb = kzalloc(sizeof(*egdev_cb), GFP_KERNEL);
-       if (!egdev_cb)
-               return -ENOMEM;
-       egdev_cb->cb = cb;
-       egdev_cb->cb_priv = cb_priv;
-       list_add(&egdev_cb->list, &egdev->cb_list);
-       return 0;
-}
-
-static void tcf_action_egdev_cb_del(struct tcf_action_egdev *egdev,
-                                   tc_setup_cb_t *cb, void *cb_priv)
-{
-       struct tcf_action_egdev_cb *egdev_cb;
-
-       egdev_cb = tcf_action_egdev_cb_lookup(egdev, cb, cb_priv);
-       if (WARN_ON(!egdev_cb))
-               return;
-       list_del(&egdev_cb->list);
-       kfree(egdev_cb);
-}
-
-static int __tc_setup_cb_egdev_register(const struct net_device *dev,
-                                       tc_setup_cb_t *cb, void *cb_priv)
-{
-       struct tcf_action_egdev *egdev = tcf_action_egdev_get(dev);
-       int err;
-
-       if (!egdev)
-               return -ENOMEM;
-       err = tcf_action_egdev_cb_add(egdev, cb, cb_priv);
-       if (err)
-               goto err_cb_add;
-       return 0;
-
-err_cb_add:
-       tcf_action_egdev_put(egdev);
-       return err;
-}
-int tc_setup_cb_egdev_register(const struct net_device *dev,
-                              tc_setup_cb_t *cb, void *cb_priv)
-{
-       int err;
-
-       rtnl_lock();
-       err = __tc_setup_cb_egdev_register(dev, cb, cb_priv);
-       rtnl_unlock();
-       return err;
-}
-EXPORT_SYMBOL_GPL(tc_setup_cb_egdev_register);
-
-static void __tc_setup_cb_egdev_unregister(const struct net_device *dev,
-                                          tc_setup_cb_t *cb, void *cb_priv)
-{
-       struct tcf_action_egdev *egdev = tcf_action_egdev_lookup(dev);
-
-       if (WARN_ON(!egdev))
-               return;
-       tcf_action_egdev_cb_del(egdev, cb, cb_priv);
-       tcf_action_egdev_put(egdev);
-}
-void tc_setup_cb_egdev_unregister(const struct net_device *dev,
-                                 tc_setup_cb_t *cb, void *cb_priv)
-{
-       rtnl_lock();
-       __tc_setup_cb_egdev_unregister(dev, cb, cb_priv);
-       rtnl_unlock();
-}
-EXPORT_SYMBOL_GPL(tc_setup_cb_egdev_unregister);
-
-int tc_setup_cb_egdev_call(const struct net_device *dev,
-                          enum tc_setup_type type, void *type_data,
-                          bool err_stop)
-{
-       struct tcf_action_egdev *egdev = tcf_action_egdev_lookup(dev);
-
-       if (!egdev)
-               return 0;
-       return tcf_action_egdev_cb_call(egdev, type, type_data, err_stop);
-}
-EXPORT_SYMBOL_GPL(tc_setup_cb_egdev_call);
-
-static __net_init int tcf_action_net_init(struct net *net)
-{
-       struct tcf_action_net *tan = net_generic(net, tcf_action_net_id);
-
-       return rhashtable_init(&tan->egdev_ht, &tcf_action_egdev_ht_params);
-}
-
-static void __net_exit tcf_action_net_exit(struct net *net)
-{
-       struct tcf_action_net *tan = net_generic(net, tcf_action_net_id);
-
-       rhashtable_destroy(&tan->egdev_ht);
-}
-
-static struct pernet_operations tcf_action_net_ops = {
-       .init = tcf_action_net_init,
-       .exit = tcf_action_net_exit,
-       .id = &tcf_action_net_id,
-       .size = sizeof(struct tcf_action_net),
-};
-
 static int __init tc_action_init(void)
 {
-       int err;
-
-       err = register_pernet_subsys(&tcf_action_net_ops);
-       if (err)
-               return err;
-
        rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, 0);
        rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, 0);
        rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action,
index da3dd0f..2b372a0 100644 (file)
@@ -201,7 +201,8 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
                        goto out_release;
                }
        } else {
-               return err;
+               ret = err;
+               goto out_free;
        }
 
        p = to_pedit(*a);
index 052855d..ec8ec55 100644 (file)
@@ -27,10 +27,7 @@ struct tcf_police_params {
        u32                     tcfp_ewma_rate;
        s64                     tcfp_burst;
        u32                     tcfp_mtu;
-       s64                     tcfp_toks;
-       s64                     tcfp_ptoks;
        s64                     tcfp_mtu_ptoks;
-       s64                     tcfp_t_c;
        struct psched_ratecfg   rate;
        bool                    rate_present;
        struct psched_ratecfg   peak;
@@ -41,6 +38,11 @@ struct tcf_police_params {
 struct tcf_police {
        struct tc_action        common;
        struct tcf_police_params __rcu *params;
+
+       spinlock_t              tcfp_lock ____cacheline_aligned_in_smp;
+       s64                     tcfp_toks;
+       s64                     tcfp_ptoks;
+       s64                     tcfp_t_c;
 };
 
 #define to_police(pc) ((struct tcf_police *)pc)
@@ -83,7 +85,7 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
                               int ovr, int bind, bool rtnl_held,
                               struct netlink_ext_ack *extack)
 {
-       int ret = 0, err;
+       int ret = 0, tcfp_result = TC_ACT_OK, err, size;
        struct nlattr *tb[TCA_POLICE_MAX + 1];
        struct tc_police *parm;
        struct tcf_police *police;
@@ -91,7 +93,6 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
        struct tc_action_net *tn = net_generic(net, police_net_id);
        struct tcf_police_params *new;
        bool exists = false;
-       int size;
 
        if (nla == NULL)
                return -EINVAL;
@@ -122,6 +123,7 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
                        return ret;
                }
                ret = ACT_P_CREATED;
+               spin_lock_init(&(to_police(*a)->tcfp_lock));
        } else if (!ovr) {
                tcf_idr_release(*a, bind);
                return -EEXIST;
@@ -157,6 +159,16 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
                goto failure;
        }
 
+       if (tb[TCA_POLICE_RESULT]) {
+               tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]);
+               if (TC_ACT_EXT_CMP(tcfp_result, TC_ACT_GOTO_CHAIN)) {
+                       NL_SET_ERR_MSG(extack,
+                                      "goto chain not allowed on fallback");
+                       err = -EINVAL;
+                       goto failure;
+               }
+       }
+
        new = kzalloc(sizeof(*new), GFP_KERNEL);
        if (unlikely(!new)) {
                err = -ENOMEM;
@@ -164,6 +176,7 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
        }
 
        /* No failure allowed after this point */
+       new->tcfp_result = tcfp_result;
        new->tcfp_mtu = parm->mtu;
        if (!new->tcfp_mtu) {
                new->tcfp_mtu = ~0;
@@ -186,28 +199,20 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
        }
 
        new->tcfp_burst = PSCHED_TICKS2NS(parm->burst);
-       new->tcfp_toks = new->tcfp_burst;
-       if (new->peak_present) {
+       if (new->peak_present)
                new->tcfp_mtu_ptoks = (s64)psched_l2t_ns(&new->peak,
                                                         new->tcfp_mtu);
-               new->tcfp_ptoks = new->tcfp_mtu_ptoks;
-       }
 
        if (tb[TCA_POLICE_AVRATE])
                new->tcfp_ewma_rate = nla_get_u32(tb[TCA_POLICE_AVRATE]);
 
-       if (tb[TCA_POLICE_RESULT]) {
-               new->tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]);
-               if (TC_ACT_EXT_CMP(new->tcfp_result, TC_ACT_GOTO_CHAIN)) {
-                       NL_SET_ERR_MSG(extack,
-                                      "goto chain not allowed on fallback");
-                       err = -EINVAL;
-                       goto failure;
-               }
-       }
-
        spin_lock_bh(&police->tcf_lock);
-       new->tcfp_t_c = ktime_get_ns();
+       spin_lock_bh(&police->tcfp_lock);
+       police->tcfp_t_c = ktime_get_ns();
+       police->tcfp_toks = new->tcfp_burst;
+       if (new->peak_present)
+               police->tcfp_ptoks = new->tcfp_mtu_ptoks;
+       spin_unlock_bh(&police->tcfp_lock);
        police->tcf_action = parm->action;
        rcu_swap_protected(police->params,
                           new,
@@ -257,25 +262,28 @@ static int tcf_police_act(struct sk_buff *skb, const struct tc_action *a,
                }
 
                now = ktime_get_ns();
-               toks = min_t(s64, now - p->tcfp_t_c, p->tcfp_burst);
+               spin_lock_bh(&police->tcfp_lock);
+               toks = min_t(s64, now - police->tcfp_t_c, p->tcfp_burst);
                if (p->peak_present) {
-                       ptoks = toks + p->tcfp_ptoks;
+                       ptoks = toks + police->tcfp_ptoks;
                        if (ptoks > p->tcfp_mtu_ptoks)
                                ptoks = p->tcfp_mtu_ptoks;
                        ptoks -= (s64)psched_l2t_ns(&p->peak,
                                                    qdisc_pkt_len(skb));
                }
-               toks += p->tcfp_toks;
+               toks += police->tcfp_toks;
                if (toks > p->tcfp_burst)
                        toks = p->tcfp_burst;
                toks -= (s64)psched_l2t_ns(&p->rate, qdisc_pkt_len(skb));
                if ((toks|ptoks) >= 0) {
-                       p->tcfp_t_c = now;
-                       p->tcfp_toks = toks;
-                       p->tcfp_ptoks = ptoks;
+                       police->tcfp_t_c = now;
+                       police->tcfp_toks = toks;
+                       police->tcfp_ptoks = ptoks;
+                       spin_unlock_bh(&police->tcfp_lock);
                        ret = p->tcfp_result;
                        goto inc_drops;
                }
+               spin_unlock_bh(&police->tcfp_lock);
        }
 
 inc_overlimits:
index 4cca8f2..c3b90fa 100644 (file)
@@ -210,9 +210,9 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
        struct tcf_tunnel_key *t;
        bool exists = false;
        __be16 dst_port = 0;
+       __be64 key_id = 0;
        int opts_len = 0;
-       __be64 key_id;
-       __be16 flags;
+       __be16 flags = 0;
        u8 tos, ttl;
        int ret = 0;
        int err;
@@ -246,15 +246,15 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
        case TCA_TUNNEL_KEY_ACT_RELEASE:
                break;
        case TCA_TUNNEL_KEY_ACT_SET:
-               if (!tb[TCA_TUNNEL_KEY_ENC_KEY_ID]) {
-                       NL_SET_ERR_MSG(extack, "Missing tunnel key id");
-                       ret = -EINVAL;
-                       goto err_out;
-               }
+               if (tb[TCA_TUNNEL_KEY_ENC_KEY_ID]) {
+                       __be32 key32;
 
-               key_id = key32_to_tunnel_id(nla_get_be32(tb[TCA_TUNNEL_KEY_ENC_KEY_ID]));
+                       key32 = nla_get_be32(tb[TCA_TUNNEL_KEY_ENC_KEY_ID]);
+                       key_id = key32_to_tunnel_id(key32);
+                       flags = TUNNEL_KEY;
+               }
 
-               flags = TUNNEL_KEY | TUNNEL_CSUM;
+               flags |= TUNNEL_CSUM;
                if (tb[TCA_TUNNEL_KEY_NO_CSUM] &&
                    nla_get_u8(tb[TCA_TUNNEL_KEY_NO_CSUM]))
                        flags &= ~TUNNEL_CSUM;
@@ -508,10 +508,13 @@ static int tunnel_key_dump(struct sk_buff *skb, struct tc_action *a,
                struct ip_tunnel_key *key = &info->key;
                __be32 key_id = tunnel_id_to_key32(key->tun_id);
 
-               if (nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_KEY_ID, key_id) ||
+               if (((key->tun_flags & TUNNEL_KEY) &&
+                    nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_KEY_ID, key_id)) ||
                    tunnel_key_dump_addresses(skb,
                                              &params->tcft_enc_metadata->u.tun_info) ||
-                   nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_DST_PORT, key->tp_dst) ||
+                   (key->tp_dst &&
+                     nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_DST_PORT,
+                                  key->tp_dst)) ||
                    nla_put_u8(skb, TCA_TUNNEL_KEY_NO_CSUM,
                               !(key->tun_flags & TUNNEL_CSUM)) ||
                    tunnel_key_opts_dump(skb, info))
index d92f44a..8ce2a05 100644 (file)
@@ -1270,29 +1270,6 @@ void tcf_block_cb_unregister(struct tcf_block *block,
 }
 EXPORT_SYMBOL(tcf_block_cb_unregister);
 
-static int tcf_block_cb_call(struct tcf_block *block, enum tc_setup_type type,
-                            void *type_data, bool err_stop)
-{
-       struct tcf_block_cb *block_cb;
-       int ok_count = 0;
-       int err;
-
-       /* Make sure all netdevs sharing this block are offload-capable. */
-       if (block->nooffloaddevcnt && err_stop)
-               return -EOPNOTSUPP;
-
-       list_for_each_entry(block_cb, &block->cb_list, list) {
-               err = block_cb->cb(type, type_data, block_cb->cb_priv);
-               if (err) {
-                       if (err_stop)
-                               return err;
-               } else {
-                       ok_count++;
-               }
-       }
-       return ok_count;
-}
-
 /* Main classifier routine: scans classifier chain attached
  * to this qdisc, (optionally) tests for protocol and asks
  * specific classifiers.
@@ -2515,54 +2492,26 @@ int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts)
 }
 EXPORT_SYMBOL(tcf_exts_dump_stats);
 
-static int tc_exts_setup_cb_egdev_call(struct tcf_exts *exts,
-                                      enum tc_setup_type type,
-                                      void *type_data, bool err_stop)
+int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
+                    void *type_data, bool err_stop)
 {
+       struct tcf_block_cb *block_cb;
        int ok_count = 0;
-#ifdef CONFIG_NET_CLS_ACT
-       const struct tc_action *a;
-       struct net_device *dev;
-       int i, ret;
+       int err;
 
-       if (!tcf_exts_has_actions(exts))
-               return 0;
+       /* Make sure all netdevs sharing this block are offload-capable. */
+       if (block->nooffloaddevcnt && err_stop)
+               return -EOPNOTSUPP;
 
-       for (i = 0; i < exts->nr_actions; i++) {
-               a = exts->actions[i];
-               if (!a->ops->get_dev)
-                       continue;
-               dev = a->ops->get_dev(a);
-               if (!dev)
-                       continue;
-               ret = tc_setup_cb_egdev_call(dev, type, type_data, err_stop);
-               a->ops->put_dev(dev);
-               if (ret < 0)
-                       return ret;
-               ok_count += ret;
+       list_for_each_entry(block_cb, &block->cb_list, list) {
+               err = block_cb->cb(type, type_data, block_cb->cb_priv);
+               if (err) {
+                       if (err_stop)
+                               return err;
+               } else {
+                       ok_count++;
+               }
        }
-#endif
-       return ok_count;
-}
-
-int tc_setup_cb_call(struct tcf_block *block, struct tcf_exts *exts,
-                    enum tc_setup_type type, void *type_data, bool err_stop)
-{
-       int ok_count;
-       int ret;
-
-       ret = tcf_block_cb_call(block, type, type_data, err_stop);
-       if (ret < 0)
-               return ret;
-       ok_count = ret;
-
-       if (!exts || ok_count)
-               return ok_count;
-       ret = tc_exts_setup_cb_egdev_call(exts, type, type_data, err_stop);
-       if (ret < 0)
-               return ret;
-       ok_count += ret;
-
        return ok_count;
 }
 EXPORT_SYMBOL(tc_setup_cb_call);
index fa6fe2f..a95cb24 100644 (file)
@@ -169,7 +169,7 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
        if (oldprog)
                tcf_block_offload_dec(block, &oldprog->gen_flags);
 
-       err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSBPF, &cls_bpf, skip_sw);
+       err = tc_setup_cb_call(block, TC_SETUP_CLSBPF, &cls_bpf, skip_sw);
        if (prog) {
                if (err < 0) {
                        cls_bpf_offload_cmd(tp, oldprog, prog, extack);
@@ -234,7 +234,7 @@ static void cls_bpf_offload_update_stats(struct tcf_proto *tp,
        cls_bpf.name = prog->bpf_name;
        cls_bpf.exts_integrated = prog->exts_integrated;
 
-       tc_setup_cb_call(block, NULL, TC_SETUP_CLSBPF, &cls_bpf, false);
+       tc_setup_cb_call(block, TC_SETUP_CLSBPF, &cls_bpf, false);
 }
 
 static int cls_bpf_init(struct tcf_proto *tp)
index c6c3278..dad04e7 100644 (file)
@@ -55,6 +55,8 @@ struct fl_flow_key {
        struct flow_dissector_key_ip ip;
        struct flow_dissector_key_ip enc_ip;
        struct flow_dissector_key_enc_opts enc_opts;
+       struct flow_dissector_key_ports tp_min;
+       struct flow_dissector_key_ports tp_max;
 } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
 
 struct fl_flow_mask_range {
@@ -65,6 +67,7 @@ struct fl_flow_mask_range {
 struct fl_flow_mask {
        struct fl_flow_key key;
        struct fl_flow_mask_range range;
+       u32 flags;
        struct rhash_head ht_node;
        struct rhashtable ht;
        struct rhashtable_params filter_ht_params;
@@ -179,13 +182,89 @@ static void fl_clear_masked_range(struct fl_flow_key *key,
        memset(fl_key_get_start(key, mask), 0, fl_mask_range(mask));
 }
 
-static struct cls_fl_filter *fl_lookup(struct fl_flow_mask *mask,
-                                      struct fl_flow_key *mkey)
+static bool fl_range_port_dst_cmp(struct cls_fl_filter *filter,
+                                 struct fl_flow_key *key,
+                                 struct fl_flow_key *mkey)
+{
+       __be16 min_mask, max_mask, min_val, max_val;
+
+       min_mask = htons(filter->mask->key.tp_min.dst);
+       max_mask = htons(filter->mask->key.tp_max.dst);
+       min_val = htons(filter->key.tp_min.dst);
+       max_val = htons(filter->key.tp_max.dst);
+
+       if (min_mask && max_mask) {
+               if (htons(key->tp.dst) < min_val ||
+                   htons(key->tp.dst) > max_val)
+                       return false;
+
+               /* skb does not have min and max values */
+               mkey->tp_min.dst = filter->mkey.tp_min.dst;
+               mkey->tp_max.dst = filter->mkey.tp_max.dst;
+       }
+       return true;
+}
+
+static bool fl_range_port_src_cmp(struct cls_fl_filter *filter,
+                                 struct fl_flow_key *key,
+                                 struct fl_flow_key *mkey)
+{
+       __be16 min_mask, max_mask, min_val, max_val;
+
+       min_mask = htons(filter->mask->key.tp_min.src);
+       max_mask = htons(filter->mask->key.tp_max.src);
+       min_val = htons(filter->key.tp_min.src);
+       max_val = htons(filter->key.tp_max.src);
+
+       if (min_mask && max_mask) {
+               if (htons(key->tp.src) < min_val ||
+                   htons(key->tp.src) > max_val)
+                       return false;
+
+               /* skb does not have min and max values */
+               mkey->tp_min.src = filter->mkey.tp_min.src;
+               mkey->tp_max.src = filter->mkey.tp_max.src;
+       }
+       return true;
+}
+
+static struct cls_fl_filter *__fl_lookup(struct fl_flow_mask *mask,
+                                        struct fl_flow_key *mkey)
 {
        return rhashtable_lookup_fast(&mask->ht, fl_key_get_start(mkey, mask),
                                      mask->filter_ht_params);
 }
 
+static struct cls_fl_filter *fl_lookup_range(struct fl_flow_mask *mask,
+                                            struct fl_flow_key *mkey,
+                                            struct fl_flow_key *key)
+{
+       struct cls_fl_filter *filter, *f;
+
+       list_for_each_entry_rcu(filter, &mask->filters, list) {
+               if (!fl_range_port_dst_cmp(filter, key, mkey))
+                       continue;
+
+               if (!fl_range_port_src_cmp(filter, key, mkey))
+                       continue;
+
+               f = __fl_lookup(mask, mkey);
+               if (f)
+                       return f;
+       }
+       return NULL;
+}
+
+static struct cls_fl_filter *fl_lookup(struct fl_flow_mask *mask,
+                                      struct fl_flow_key *mkey,
+                                      struct fl_flow_key *key)
+{
+       if ((mask->flags & TCA_FLOWER_MASK_FLAGS_RANGE))
+               return fl_lookup_range(mask, mkey, key);
+
+       return __fl_lookup(mask, mkey);
+}
+
 static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
                       struct tcf_result *res)
 {
@@ -208,7 +287,7 @@ static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
 
                fl_set_masked_key(&skb_mkey, &skb_key, mask);
 
-               f = fl_lookup(mask, &skb_mkey);
+               f = fl_lookup(mask, &skb_mkey, &skb_key);
                if (f && !tc_skip_sw(f->flags)) {
                        *res = f->res;
                        return tcf_exts_exec(skb, &f->exts, res);
@@ -289,8 +368,7 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f,
        cls_flower.command = TC_CLSFLOWER_DESTROY;
        cls_flower.cookie = (unsigned long) f;
 
-       tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER,
-                        &cls_flower, false);
+       tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
        tcf_block_offload_dec(block, &f->flags);
 }
 
@@ -312,8 +390,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
        cls_flower.exts = &f->exts;
        cls_flower.classid = f->res.classid;
 
-       err = tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER,
-                              &cls_flower, skip_sw);
+       err = tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, skip_sw);
        if (err < 0) {
                fl_hw_destroy_filter(tp, f, NULL);
                return err;
@@ -339,8 +416,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f)
        cls_flower.exts = &f->exts;
        cls_flower.classid = f->res.classid;
 
-       tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER,
-                        &cls_flower, false);
+       tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
 }
 
 static bool __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
@@ -514,6 +590,31 @@ static void fl_set_key_val(struct nlattr **tb,
                memcpy(mask, nla_data(tb[mask_type]), len);
 }
 
+static int fl_set_key_port_range(struct nlattr **tb, struct fl_flow_key *key,
+                                struct fl_flow_key *mask)
+{
+       fl_set_key_val(tb, &key->tp_min.dst,
+                      TCA_FLOWER_KEY_PORT_DST_MIN, &mask->tp_min.dst,
+                      TCA_FLOWER_UNSPEC, sizeof(key->tp_min.dst));
+       fl_set_key_val(tb, &key->tp_max.dst,
+                      TCA_FLOWER_KEY_PORT_DST_MAX, &mask->tp_max.dst,
+                      TCA_FLOWER_UNSPEC, sizeof(key->tp_max.dst));
+       fl_set_key_val(tb, &key->tp_min.src,
+                      TCA_FLOWER_KEY_PORT_SRC_MIN, &mask->tp_min.src,
+                      TCA_FLOWER_UNSPEC, sizeof(key->tp_min.src));
+       fl_set_key_val(tb, &key->tp_max.src,
+                      TCA_FLOWER_KEY_PORT_SRC_MAX, &mask->tp_max.src,
+                      TCA_FLOWER_UNSPEC, sizeof(key->tp_max.src));
+
+       if ((mask->tp_min.dst && mask->tp_max.dst &&
+            htons(key->tp_max.dst) <= htons(key->tp_min.dst)) ||
+            (mask->tp_min.src && mask->tp_max.src &&
+             htons(key->tp_max.src) <= htons(key->tp_min.src)))
+               return -EINVAL;
+
+       return 0;
+}
+
 static int fl_set_key_mpls(struct nlattr **tb,
                           struct flow_dissector_key_mpls *key_val,
                           struct flow_dissector_key_mpls *key_mask)
@@ -921,6 +1022,14 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
                               sizeof(key->arp.tha));
        }
 
+       if (key->basic.ip_proto == IPPROTO_TCP ||
+           key->basic.ip_proto == IPPROTO_UDP ||
+           key->basic.ip_proto == IPPROTO_SCTP) {
+               ret = fl_set_key_port_range(tb, key, mask);
+               if (ret)
+                       return ret;
+       }
+
        if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] ||
            tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) {
                key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
@@ -1038,8 +1147,9 @@ static void fl_init_dissector(struct flow_dissector *dissector,
                             FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4);
        FL_KEY_SET_IF_MASKED(mask, keys, cnt,
                             FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6);
-       FL_KEY_SET_IF_MASKED(mask, keys, cnt,
-                            FLOW_DISSECTOR_KEY_PORTS, tp);
+       if (FL_KEY_IS_MASKED(mask, tp) ||
+           FL_KEY_IS_MASKED(mask, tp_min) || FL_KEY_IS_MASKED(mask, tp_max))
+               FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_PORTS, tp);
        FL_KEY_SET_IF_MASKED(mask, keys, cnt,
                             FLOW_DISSECTOR_KEY_IP, ip);
        FL_KEY_SET_IF_MASKED(mask, keys, cnt,
@@ -1086,6 +1196,10 @@ static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head,
 
        fl_mask_copy(newmask, mask);
 
+       if ((newmask->key.tp_min.dst && newmask->key.tp_max.dst) ||
+           (newmask->key.tp_min.src && newmask->key.tp_max.src))
+               newmask->flags |= TCA_FLOWER_MASK_FLAGS_RANGE;
+
        err = fl_init_mask_hashtable(newmask);
        if (err)
                goto errout_free;
@@ -1238,18 +1352,16 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
        if (err)
                goto errout_idr;
 
-       if (!tc_skip_sw(fnew->flags)) {
-               if (!fold && fl_lookup(fnew->mask, &fnew->mkey)) {
-                       err = -EEXIST;
-                       goto errout_mask;
-               }
-
-               err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node,
-                                            fnew->mask->filter_ht_params);
-               if (err)
-                       goto errout_mask;
+       if (!fold && __fl_lookup(fnew->mask, &fnew->mkey)) {
+               err = -EEXIST;
+               goto errout_mask;
        }
 
+       err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node,
+                                    fnew->mask->filter_ht_params);
+       if (err)
+               goto errout_mask;
+
        if (!tc_skip_hw(fnew->flags)) {
                err = fl_hw_replace_filter(tp, fnew, extack);
                if (err)
@@ -1260,10 +1372,9 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
                fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
 
        if (fold) {
-               if (!tc_skip_sw(fold->flags))
-                       rhashtable_remove_fast(&fold->mask->ht,
-                                              &fold->ht_node,
-                                              fold->mask->filter_ht_params);
+               rhashtable_remove_fast(&fold->mask->ht,
+                                      &fold->ht_node,
+                                      fold->mask->filter_ht_params);
                if (!tc_skip_hw(fold->flags))
                        fl_hw_destroy_filter(tp, fold, NULL);
        }
@@ -1303,9 +1414,8 @@ static int fl_delete(struct tcf_proto *tp, void *arg, bool *last,
        struct cls_fl_head *head = rtnl_dereference(tp->root);
        struct cls_fl_filter *f = arg;
 
-       if (!tc_skip_sw(f->flags))
-               rhashtable_remove_fast(&f->mask->ht, &f->ht_node,
-                                      f->mask->filter_ht_params);
+       rhashtable_remove_fast(&f->mask->ht, &f->ht_node,
+                              f->mask->filter_ht_params);
        __fl_delete(tp, f, extack);
        *last = list_empty(&head->masks);
        return 0;
@@ -1388,8 +1498,7 @@ static void fl_hw_create_tmplt(struct tcf_chain *chain,
        /* 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, NULL, TC_SETUP_CLSFLOWER,
-                        &cls_flower, false);
+       tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
 }
 
 static void fl_hw_destroy_tmplt(struct tcf_chain *chain,
@@ -1402,8 +1511,7 @@ static void fl_hw_destroy_tmplt(struct tcf_chain *chain,
        cls_flower.command = TC_CLSFLOWER_TMPLT_DESTROY;
        cls_flower.cookie = (unsigned long) tmplt;
 
-       tc_setup_cb_call(block, NULL, TC_SETUP_CLSFLOWER,
-                        &cls_flower, false);
+       tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
 }
 
 static void *fl_tmplt_create(struct net *net, struct tcf_chain *chain,
@@ -1476,6 +1584,26 @@ static int fl_dump_key_val(struct sk_buff *skb,
        return 0;
 }
 
+static int fl_dump_key_port_range(struct sk_buff *skb, struct fl_flow_key *key,
+                                 struct fl_flow_key *mask)
+{
+       if (fl_dump_key_val(skb, &key->tp_min.dst, TCA_FLOWER_KEY_PORT_DST_MIN,
+                           &mask->tp_min.dst, TCA_FLOWER_UNSPEC,
+                           sizeof(key->tp_min.dst)) ||
+           fl_dump_key_val(skb, &key->tp_max.dst, TCA_FLOWER_KEY_PORT_DST_MAX,
+                           &mask->tp_max.dst, TCA_FLOWER_UNSPEC,
+                           sizeof(key->tp_max.dst)) ||
+           fl_dump_key_val(skb, &key->tp_min.src, TCA_FLOWER_KEY_PORT_SRC_MIN,
+                           &mask->tp_min.src, TCA_FLOWER_UNSPEC,
+                           sizeof(key->tp_min.src)) ||
+           fl_dump_key_val(skb, &key->tp_max.src, TCA_FLOWER_KEY_PORT_SRC_MAX,
+                           &mask->tp_max.src, TCA_FLOWER_UNSPEC,
+                           sizeof(key->tp_max.src)))
+               return -1;
+
+       return 0;
+}
+
 static int fl_dump_key_mpls(struct sk_buff *skb,
                            struct flow_dissector_key_mpls *mpls_key,
                            struct flow_dissector_key_mpls *mpls_mask)
@@ -1812,6 +1940,12 @@ static int fl_dump_key(struct sk_buff *skb, struct net *net,
                                  sizeof(key->arp.tha))))
                goto nla_put_failure;
 
+       if ((key->basic.ip_proto == IPPROTO_TCP ||
+            key->basic.ip_proto == IPPROTO_UDP ||
+            key->basic.ip_proto == IPPROTO_SCTP) &&
+            fl_dump_key_port_range(skb, key, mask))
+               goto nla_put_failure;
+
        if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS &&
            (fl_dump_key_val(skb, &key->enc_ipv4.src,
                            TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src,
index 856fa79..0e408ee 100644 (file)
@@ -71,7 +71,7 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp,
        cls_mall.command = TC_CLSMATCHALL_DESTROY;
        cls_mall.cookie = cookie;
 
-       tc_setup_cb_call(block, NULL, TC_SETUP_CLSMATCHALL, &cls_mall, false);
+       tc_setup_cb_call(block, TC_SETUP_CLSMATCHALL, &cls_mall, false);
        tcf_block_offload_dec(block, &head->flags);
 }
 
@@ -90,8 +90,7 @@ static int mall_replace_hw_filter(struct tcf_proto *tp,
        cls_mall.exts = &head->exts;
        cls_mall.cookie = cookie;
 
-       err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSMATCHALL,
-                              &cls_mall, skip_sw);
+       err = tc_setup_cb_call(block, TC_SETUP_CLSMATCHALL, &cls_mall, skip_sw);
        if (err < 0) {
                mall_destroy_hw_filter(tp, head, cookie, NULL);
                return err;
index 4b28fd4..dcea210 100644 (file)
@@ -491,7 +491,7 @@ static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
        cls_u32.hnode.handle = h->handle;
        cls_u32.hnode.prio = h->prio;
 
-       tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, false);
+       tc_setup_cb_call(block, TC_SETUP_CLSU32, &cls_u32, false);
 }
 
 static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
@@ -509,7 +509,7 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
        cls_u32.hnode.handle = h->handle;
        cls_u32.hnode.prio = h->prio;
 
-       err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, skip_sw);
+       err = tc_setup_cb_call(block, TC_SETUP_CLSU32, &cls_u32, skip_sw);
        if (err < 0) {
                u32_clear_hw_hnode(tp, h, NULL);
                return err;
@@ -533,7 +533,7 @@ static void u32_remove_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
        cls_u32.command = TC_CLSU32_DELETE_KNODE;
        cls_u32.knode.handle = n->handle;
 
-       tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, false);
+       tc_setup_cb_call(block, TC_SETUP_CLSU32, &cls_u32, false);
        tcf_block_offload_dec(block, &n->flags);
 }
 
@@ -558,11 +558,12 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
        cls_u32.knode.mask = 0;
 #endif
        cls_u32.knode.sel = &n->sel;
+       cls_u32.knode.res = &n->res;
        cls_u32.knode.exts = &n->exts;
        if (n->ht_down)
                cls_u32.knode.link_handle = ht->handle;
 
-       err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, skip_sw);
+       err = tc_setup_cb_call(block, TC_SETUP_CLSU32, &cls_u32, skip_sw);
        if (err < 0) {
                u32_remove_hw_knode(tp, n, NULL);
                return err;
@@ -1206,6 +1207,7 @@ static int u32_reoffload_knode(struct tcf_proto *tp, struct tc_u_knode *n,
                cls_u32.knode.mask = 0;
 #endif
                cls_u32.knode.sel = &n->sel;
+               cls_u32.knode.res = &n->res;
                cls_u32.knode.exts = &n->exts;
                if (n->ht_down)
                        cls_u32.knode.link_handle = ht->handle;
index f55bc50..187a57e 100644 (file)
@@ -335,7 +335,6 @@ out:
 static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
 {
        unsigned long cl;
-       struct Qdisc *leaf;
        const struct Qdisc_class_ops *cops = p->ops->cl_ops;
 
        if (cops == NULL)
@@ -344,8 +343,7 @@ static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
 
        if (cl == 0)
                return NULL;
-       leaf = cops->leaf(p, cl);
-       return leaf;
+       return cops->leaf(p, cl);
 }
 
 /* Find queueing discipline by name */
@@ -860,6 +858,21 @@ void qdisc_offload_graft_helper(struct net_device *dev, struct Qdisc *sch,
 }
 EXPORT_SYMBOL(qdisc_offload_graft_helper);
 
+static void qdisc_offload_graft_root(struct net_device *dev,
+                                    struct Qdisc *new, struct Qdisc *old,
+                                    struct netlink_ext_ack *extack)
+{
+       struct tc_root_qopt_offload graft_offload = {
+               .command        = TC_ROOT_GRAFT,
+               .handle         = new ? new->handle : 0,
+               .ingress        = (new && new->flags & TCQ_F_INGRESS) ||
+                                 (old && old->flags & TCQ_F_INGRESS),
+       };
+
+       qdisc_offload_graft_helper(dev, NULL, new, old,
+                                  TC_SETUP_ROOT_QDISC, &graft_offload, extack);
+}
+
 static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
                         u32 portid, u32 seq, u16 flags, int event)
 {
@@ -1026,6 +1039,8 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
                if (dev->flags & IFF_UP)
                        dev_deactivate(dev);
 
+               qdisc_offload_graft_root(dev, new, old, extack);
+
                if (new && new->ops->attach)
                        goto skip;
 
index 1538d6f..1150f22 100644 (file)
@@ -30,7 +30,7 @@ struct etf_sched_data {
        int queue;
        s32 delta; /* in ns */
        ktime_t last; /* The txtime of the last skb sent to the netdevice. */
-       struct rb_root head;
+       struct rb_root_cached head;
        struct qdisc_watchdog watchdog;
        ktime_t (*get_time)(void);
 };
@@ -104,7 +104,7 @@ static struct sk_buff *etf_peek_timesortedlist(struct Qdisc *sch)
        struct etf_sched_data *q = qdisc_priv(sch);
        struct rb_node *p;
 
-       p = rb_first(&q->head);
+       p = rb_first_cached(&q->head);
        if (!p)
                return NULL;
 
@@ -117,8 +117,10 @@ static void reset_watchdog(struct Qdisc *sch)
        struct sk_buff *skb = etf_peek_timesortedlist(sch);
        ktime_t next;
 
-       if (!skb)
+       if (!skb) {
+               qdisc_watchdog_cancel(&q->watchdog);
                return;
+       }
 
        next = ktime_sub_ns(skb->tstamp, q->delta);
        qdisc_watchdog_schedule_ns(&q->watchdog, ktime_to_ns(next));
@@ -154,8 +156,9 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
                                      struct sk_buff **to_free)
 {
        struct etf_sched_data *q = qdisc_priv(sch);
-       struct rb_node **p = &q->head.rb_node, *parent = NULL;
+       struct rb_node **p = &q->head.rb_root.rb_node, *parent = NULL;
        ktime_t txtime = nskb->tstamp;
+       bool leftmost = true;
 
        if (!is_packet_valid(sch, nskb)) {
                report_sock_error(nskb, EINVAL,
@@ -168,13 +171,15 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
 
                parent = *p;
                skb = rb_to_skb(parent);
-               if (ktime_after(txtime, skb->tstamp))
+               if (ktime_after(txtime, skb->tstamp)) {
                        p = &parent->rb_right;
-               else
+                       leftmost = false;
+               } else {
                        p = &parent->rb_left;
+               }
        }
        rb_link_node(&nskb->rbnode, parent, p);
-       rb_insert_color(&nskb->rbnode, &q->head);
+       rb_insert_color_cached(&nskb->rbnode, &q->head, leftmost);
 
        qdisc_qstats_backlog_inc(sch, nskb);
        sch->q.qlen++;
@@ -185,12 +190,42 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
        return NET_XMIT_SUCCESS;
 }
 
-static void timesortedlist_erase(struct Qdisc *sch, struct sk_buff *skb,
-                                bool drop)
+static void timesortedlist_drop(struct Qdisc *sch, struct sk_buff *skb,
+                               ktime_t now)
+{
+       struct etf_sched_data *q = qdisc_priv(sch);
+       struct sk_buff *to_free = NULL;
+       struct sk_buff *tmp = NULL;
+
+       skb_rbtree_walk_from_safe(skb, tmp) {
+               if (ktime_after(skb->tstamp, now))
+                       break;
+
+               rb_erase_cached(&skb->rbnode, &q->head);
+
+               /* The rbnode field in the skb re-uses these fields, now that
+                * we are done with the rbnode, reset them.
+                */
+               skb->next = NULL;
+               skb->prev = NULL;
+               skb->dev = qdisc_dev(sch);
+
+               report_sock_error(skb, ECANCELED, SO_EE_CODE_TXTIME_MISSED);
+
+               qdisc_qstats_backlog_dec(sch, skb);
+               qdisc_drop(skb, sch, &to_free);
+               qdisc_qstats_overlimit(sch);
+               sch->q.qlen--;
+       }
+
+       kfree_skb_list(to_free);
+}
+
+static void timesortedlist_remove(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct etf_sched_data *q = qdisc_priv(sch);
 
-       rb_erase(&skb->rbnode, &q->head);
+       rb_erase_cached(&skb->rbnode, &q->head);
 
        /* The rbnode field in the skb re-uses these fields, now that
         * we are done with the rbnode, reset them.
@@ -201,19 +236,9 @@ static void timesortedlist_erase(struct Qdisc *sch, struct sk_buff *skb,
 
        qdisc_qstats_backlog_dec(sch, skb);
 
-       if (drop) {
-               struct sk_buff *to_free = NULL;
+       qdisc_bstats_update(sch, skb);
 
-               report_sock_error(skb, ECANCELED, SO_EE_CODE_TXTIME_MISSED);
-
-               qdisc_drop(skb, sch, &to_free);
-               kfree_skb_list(to_free);
-               qdisc_qstats_overlimit(sch);
-       } else {
-               qdisc_bstats_update(sch, skb);
-
-               q->last = skb->tstamp;
-       }
+       q->last = skb->tstamp;
 
        sch->q.qlen--;
 }
@@ -232,7 +257,7 @@ static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch)
 
        /* Drop if packet has expired while in queue. */
        if (ktime_before(skb->tstamp, now)) {
-               timesortedlist_erase(sch, skb, true);
+               timesortedlist_drop(sch, skb, now);
                skb = NULL;
                goto out;
        }
@@ -241,7 +266,7 @@ static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch)
         * txtime from deadline to (now + delta).
         */
        if (q->deadline_mode) {
-               timesortedlist_erase(sch, skb, false);
+               timesortedlist_remove(sch, skb);
                skb->tstamp = now;
                goto out;
        }
@@ -250,7 +275,7 @@ static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch)
 
        /* Dequeue only if now is within the [txtime - delta, txtime] range. */
        if (ktime_after(now, next))
-               timesortedlist_erase(sch, skb, false);
+               timesortedlist_remove(sch, skb);
        else
                skb = NULL;
 
@@ -386,14 +411,14 @@ static int etf_init(struct Qdisc *sch, struct nlattr *opt,
 static void timesortedlist_clear(struct Qdisc *sch)
 {
        struct etf_sched_data *q = qdisc_priv(sch);
-       struct rb_node *p = rb_first(&q->head);
+       struct rb_node *p = rb_first_cached(&q->head);
 
        while (p) {
                struct sk_buff *skb = rb_to_skb(p);
 
                p = rb_next(p);
 
-               rb_erase(&skb->rbnode, &q->head);
+               rb_erase_cached(&skb->rbnode, &q->head);
                rtnl_kfree_skbs(skb, skb);
                sch->q.qlen--;
        }
index 3671eab..1a662f2 100644 (file)
@@ -414,16 +414,21 @@ static void fq_check_throttled(struct fq_sched_data *q, u64 now)
 static struct sk_buff *fq_dequeue(struct Qdisc *sch)
 {
        struct fq_sched_data *q = qdisc_priv(sch);
-       u64 now = ktime_get_ns();
        struct fq_flow_head *head;
        struct sk_buff *skb;
        struct fq_flow *f;
        unsigned long rate;
        u32 plen;
+       u64 now;
+
+       if (!sch->q.qlen)
+               return NULL;
 
        skb = fq_dequeue_head(sch, &q->internal);
        if (skb)
                goto out;
+
+       now = ktime_get_ns();
        fq_check_throttled(q, now);
 begin:
        head = &q->new_flows;
@@ -476,22 +481,29 @@ begin:
                goto begin;
        }
        prefetch(&skb->end);
-       f->credit -= qdisc_pkt_len(skb);
+       plen = qdisc_pkt_len(skb);
+       f->credit -= plen;
 
-       if (ktime_to_ns(skb->tstamp) || !q->rate_enable)
+       if (!q->rate_enable)
                goto out;
 
        rate = q->flow_max_rate;
-       if (skb->sk)
-               rate = min(skb->sk->sk_pacing_rate, rate);
-
-       if (rate <= q->low_rate_threshold) {
-               f->credit = 0;
-               plen = qdisc_pkt_len(skb);
-       } else {
-               plen = max(qdisc_pkt_len(skb), q->quantum);
-               if (f->credit > 0)
-                       goto out;
+
+       /* If EDT time was provided for this skb, we need to
+        * update f->time_next_packet only if this qdisc enforces
+        * a flow max rate.
+        */
+       if (!skb->tstamp) {
+               if (skb->sk)
+                       rate = min(skb->sk->sk_pacing_rate, rate);
+
+               if (rate <= q->low_rate_threshold) {
+                       f->credit = 0;
+               } else {
+                       plen = max(plen, q->quantum);
+                       if (f->credit > 0)
+                               goto out;
+               }
        }
        if (rate != ~0UL) {
                u64 len = (u64)plen * NSEC_PER_SEC;
index 4a042ab..234afbf 100644 (file)
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/skbuff.h>
+#include <net/pkt_cls.h>
 #include <net/pkt_sched.h>
 #include <net/red.h>
 
 #define GRED_DEF_PRIO (MAX_DPs / 2)
 #define GRED_VQ_MASK (MAX_DPs - 1)
 
+#define GRED_VQ_RED_FLAGS      (TC_RED_ECN | TC_RED_HARDDROP)
+
 struct gred_sched_data;
 struct gred_sched;
 
 struct gred_sched_data {
        u32             limit;          /* HARD maximal queue length    */
        u32             DP;             /* the drop parameters */
-       u32             bytesin;        /* bytes seen on virtualQ so far*/
+       u32             red_flags;      /* virtualQ version of red_flags */
+       u64             bytesin;        /* bytes seen on virtualQ so far*/
        u32             packetsin;      /* packets seen on virtualQ so far*/
        u32             backlog;        /* bytes on the virtualQ */
        u8              prio;           /* the prio of this vq */
@@ -139,14 +143,27 @@ static inline void gred_store_wred_set(struct gred_sched *table,
        table->wred_set.qidlestart = q->vars.qidlestart;
 }
 
-static inline int gred_use_ecn(struct gred_sched *t)
+static int gred_use_ecn(struct gred_sched_data *q)
+{
+       return q->red_flags & TC_RED_ECN;
+}
+
+static int gred_use_harddrop(struct gred_sched_data *q)
 {
-       return t->red_flags & TC_RED_ECN;
+       return q->red_flags & TC_RED_HARDDROP;
 }
 
-static inline int gred_use_harddrop(struct gred_sched *t)
+static bool gred_per_vq_red_flags_used(struct gred_sched *table)
 {
-       return t->red_flags & TC_RED_HARDDROP;
+       unsigned int i;
+
+       /* Local per-vq flags couldn't have been set unless global are 0 */
+       if (table->red_flags)
+               return false;
+       for (i = 0; i < MAX_DPs; i++)
+               if (table->tab[i] && table->tab[i]->red_flags)
+                       return true;
+       return false;
 }
 
 static int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch,
@@ -212,7 +229,7 @@ static int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 
        case RED_PROB_MARK:
                qdisc_qstats_overlimit(sch);
-               if (!gred_use_ecn(t) || !INET_ECN_set_ce(skb)) {
+               if (!gred_use_ecn(q) || !INET_ECN_set_ce(skb)) {
                        q->stats.prob_drop++;
                        goto congestion_drop;
                }
@@ -222,7 +239,7 @@ static int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 
        case RED_HARD_MARK:
                qdisc_qstats_overlimit(sch);
-               if (gred_use_harddrop(t) || !gred_use_ecn(t) ||
+               if (gred_use_harddrop(q) || !gred_use_ecn(q) ||
                    !INET_ECN_set_ce(skb)) {
                        q->stats.forced_drop++;
                        goto congestion_drop;
@@ -295,15 +312,103 @@ static void gred_reset(struct Qdisc *sch)
        }
 }
 
+static void gred_offload(struct Qdisc *sch, enum tc_gred_command command)
+{
+       struct gred_sched *table = qdisc_priv(sch);
+       struct net_device *dev = qdisc_dev(sch);
+       struct tc_gred_qopt_offload opt = {
+               .command        = command,
+               .handle         = sch->handle,
+               .parent         = sch->parent,
+       };
+
+       if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
+               return;
+
+       if (command == TC_GRED_REPLACE) {
+               unsigned int i;
+
+               opt.set.grio_on = gred_rio_mode(table);
+               opt.set.wred_on = gred_wred_mode(table);
+               opt.set.dp_cnt = table->DPs;
+               opt.set.dp_def = table->def;
+
+               for (i = 0; i < table->DPs; i++) {
+                       struct gred_sched_data *q = table->tab[i];
+
+                       if (!q)
+                               continue;
+                       opt.set.tab[i].present = true;
+                       opt.set.tab[i].limit = q->limit;
+                       opt.set.tab[i].prio = q->prio;
+                       opt.set.tab[i].min = q->parms.qth_min >> q->parms.Wlog;
+                       opt.set.tab[i].max = q->parms.qth_max >> q->parms.Wlog;
+                       opt.set.tab[i].is_ecn = gred_use_ecn(q);
+                       opt.set.tab[i].is_harddrop = gred_use_harddrop(q);
+                       opt.set.tab[i].probability = q->parms.max_P;
+                       opt.set.tab[i].backlog = &q->backlog;
+               }
+               opt.set.qstats = &sch->qstats;
+       }
+
+       dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_GRED, &opt);
+}
+
+static int gred_offload_dump_stats(struct Qdisc *sch)
+{
+       struct gred_sched *table = qdisc_priv(sch);
+       struct tc_gred_qopt_offload *hw_stats;
+       unsigned int i;
+       int ret;
+
+       hw_stats = kzalloc(sizeof(*hw_stats), GFP_KERNEL);
+       if (!hw_stats)
+               return -ENOMEM;
+
+       hw_stats->command = TC_GRED_STATS;
+       hw_stats->handle = sch->handle;
+       hw_stats->parent = sch->parent;
+
+       for (i = 0; i < MAX_DPs; i++)
+               if (table->tab[i])
+                       hw_stats->stats.xstats[i] = &table->tab[i]->stats;
+
+       ret = qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_GRED, hw_stats);
+       /* Even if driver returns failure adjust the stats - in case offload
+        * ended but driver still wants to adjust the values.
+        */
+       for (i = 0; i < MAX_DPs; i++) {
+               if (!table->tab[i])
+                       continue;
+               table->tab[i]->packetsin += hw_stats->stats.bstats[i].packets;
+               table->tab[i]->bytesin += hw_stats->stats.bstats[i].bytes;
+               table->tab[i]->backlog += hw_stats->stats.qstats[i].backlog;
+
+               _bstats_update(&sch->bstats,
+                              hw_stats->stats.bstats[i].bytes,
+                              hw_stats->stats.bstats[i].packets);
+               sch->qstats.qlen += hw_stats->stats.qstats[i].qlen;
+               sch->qstats.backlog += hw_stats->stats.qstats[i].backlog;
+               sch->qstats.drops += hw_stats->stats.qstats[i].drops;
+               sch->qstats.requeues += hw_stats->stats.qstats[i].requeues;
+               sch->qstats.overlimits += hw_stats->stats.qstats[i].overlimits;
+       }
+
+       kfree(hw_stats);
+       return ret;
+}
+
 static inline void gred_destroy_vq(struct gred_sched_data *q)
 {
        kfree(q);
 }
 
-static inline int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps)
+static int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps,
+                                struct netlink_ext_ack *extack)
 {
        struct gred_sched *table = qdisc_priv(sch);
        struct tc_gred_sopt *sopt;
+       bool red_flags_changed;
        int i;
 
        if (!dps)
@@ -311,13 +416,28 @@ static inline int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps)
 
        sopt = nla_data(dps);
 
-       if (sopt->DPs > MAX_DPs || sopt->DPs == 0 ||
-           sopt->def_DP >= sopt->DPs)
+       if (sopt->DPs > MAX_DPs) {
+               NL_SET_ERR_MSG_MOD(extack, "number of virtual queues too high");
+               return -EINVAL;
+       }
+       if (sopt->DPs == 0) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "number of virtual queues can't be 0");
+               return -EINVAL;
+       }
+       if (sopt->def_DP >= sopt->DPs) {
+               NL_SET_ERR_MSG_MOD(extack, "default virtual queue above virtual queue count");
                return -EINVAL;
+       }
+       if (sopt->flags && gred_per_vq_red_flags_used(table)) {
+               NL_SET_ERR_MSG_MOD(extack, "can't set per-Qdisc RED flags when per-virtual queue flags are used");
+               return -EINVAL;
+       }
 
        sch_tree_lock(sch);
        table->DPs = sopt->DPs;
        table->def = sopt->def_DP;
+       red_flags_changed = table->red_flags != sopt->flags;
        table->red_flags = sopt->flags;
 
        /*
@@ -337,6 +457,12 @@ static inline int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps)
                gred_disable_wred_mode(table);
        }
 
+       if (red_flags_changed)
+               for (i = 0; i < table->DPs; i++)
+                       if (table->tab[i])
+                               table->tab[i]->red_flags =
+                                       table->red_flags & GRED_VQ_RED_FLAGS;
+
        for (i = table->DPs; i < MAX_DPs; i++) {
                if (table->tab[i]) {
                        pr_warn("GRED: Warning: Destroying shadowed VQ 0x%x\n",
@@ -346,25 +472,30 @@ static inline int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps)
                }
        }
 
+       gred_offload(sch, TC_GRED_REPLACE);
        return 0;
 }
 
 static inline int gred_change_vq(struct Qdisc *sch, int dp,
                                 struct tc_gred_qopt *ctl, int prio,
                                 u8 *stab, u32 max_P,
-                                struct gred_sched_data **prealloc)
+                                struct gred_sched_data **prealloc,
+                                struct netlink_ext_ack *extack)
 {
        struct gred_sched *table = qdisc_priv(sch);
        struct gred_sched_data *q = table->tab[dp];
 
-       if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog))
+       if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog)) {
+               NL_SET_ERR_MSG_MOD(extack, "invalid RED parameters");
                return -EINVAL;
+       }
 
        if (!q) {
                table->tab[dp] = q = *prealloc;
                *prealloc = NULL;
                if (!q)
                        return -ENOMEM;
+               q->red_flags = table->red_flags & GRED_VQ_RED_FLAGS;
        }
 
        q->DP = dp;
@@ -384,14 +515,127 @@ static inline int gred_change_vq(struct Qdisc *sch, int dp,
        return 0;
 }
 
+static const struct nla_policy gred_vq_policy[TCA_GRED_VQ_MAX + 1] = {
+       [TCA_GRED_VQ_DP]        = { .type = NLA_U32 },
+       [TCA_GRED_VQ_FLAGS]     = { .type = NLA_U32 },
+};
+
+static const struct nla_policy gred_vqe_policy[TCA_GRED_VQ_ENTRY_MAX + 1] = {
+       [TCA_GRED_VQ_ENTRY]     = { .type = NLA_NESTED },
+};
+
 static const struct nla_policy gred_policy[TCA_GRED_MAX + 1] = {
        [TCA_GRED_PARMS]        = { .len = sizeof(struct tc_gred_qopt) },
        [TCA_GRED_STAB]         = { .len = 256 },
        [TCA_GRED_DPS]          = { .len = sizeof(struct tc_gred_sopt) },
        [TCA_GRED_MAX_P]        = { .type = NLA_U32 },
        [TCA_GRED_LIMIT]        = { .type = NLA_U32 },
+       [TCA_GRED_VQ_LIST]      = { .type = NLA_NESTED },
 };
 
+static void gred_vq_apply(struct gred_sched *table, const struct nlattr *entry)
+{
+       struct nlattr *tb[TCA_GRED_VQ_MAX + 1];
+       u32 dp;
+
+       nla_parse_nested(tb, TCA_GRED_VQ_MAX, entry, gred_vq_policy, NULL);
+
+       dp = nla_get_u32(tb[TCA_GRED_VQ_DP]);
+
+       if (tb[TCA_GRED_VQ_FLAGS])
+               table->tab[dp]->red_flags = nla_get_u32(tb[TCA_GRED_VQ_FLAGS]);
+}
+
+static void gred_vqs_apply(struct gred_sched *table, struct nlattr *vqs)
+{
+       const struct nlattr *attr;
+       int rem;
+
+       nla_for_each_nested(attr, vqs, rem) {
+               switch (nla_type(attr)) {
+               case TCA_GRED_VQ_ENTRY:
+                       gred_vq_apply(table, attr);
+                       break;
+               }
+       }
+}
+
+static int gred_vq_validate(struct gred_sched *table, u32 cdp,
+                           const struct nlattr *entry,
+                           struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb[TCA_GRED_VQ_MAX + 1];
+       int err;
+       u32 dp;
+
+       err = nla_parse_nested(tb, TCA_GRED_VQ_MAX, entry, gred_vq_policy,
+                              extack);
+       if (err < 0)
+               return err;
+
+       if (!tb[TCA_GRED_VQ_DP]) {
+               NL_SET_ERR_MSG_MOD(extack, "Virtual queue with no index specified");
+               return -EINVAL;
+       }
+       dp = nla_get_u32(tb[TCA_GRED_VQ_DP]);
+       if (dp >= table->DPs) {
+               NL_SET_ERR_MSG_MOD(extack, "Virtual queue with index out of bounds");
+               return -EINVAL;
+       }
+       if (dp != cdp && !table->tab[dp]) {
+               NL_SET_ERR_MSG_MOD(extack, "Virtual queue not yet instantiated");
+               return -EINVAL;
+       }
+
+       if (tb[TCA_GRED_VQ_FLAGS]) {
+               u32 red_flags = nla_get_u32(tb[TCA_GRED_VQ_FLAGS]);
+
+               if (table->red_flags && table->red_flags != red_flags) {
+                       NL_SET_ERR_MSG_MOD(extack, "can't change per-virtual queue RED flags when per-Qdisc flags are used");
+                       return -EINVAL;
+               }
+               if (red_flags & ~GRED_VQ_RED_FLAGS) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "invalid RED flags specified");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int gred_vqs_validate(struct gred_sched *table, u32 cdp,
+                            struct nlattr *vqs, struct netlink_ext_ack *extack)
+{
+       const struct nlattr *attr;
+       int rem, err;
+
+       err = nla_validate_nested(vqs, TCA_GRED_VQ_ENTRY_MAX,
+                                 gred_vqe_policy, extack);
+       if (err < 0)
+               return err;
+
+       nla_for_each_nested(attr, vqs, rem) {
+               switch (nla_type(attr)) {
+               case TCA_GRED_VQ_ENTRY:
+                       err = gred_vq_validate(table, cdp, attr, extack);
+                       if (err)
+                               return err;
+                       break;
+               default:
+                       NL_SET_ERR_MSG_MOD(extack, "GRED_VQ_LIST can contain only entry attributes");
+                       return -EINVAL;
+               }
+       }
+
+       if (rem > 0) {
+               NL_SET_ERR_MSG_MOD(extack, "Trailing data after parsing virtual queue list");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int gred_change(struct Qdisc *sch, struct nlattr *opt,
                       struct netlink_ext_ack *extack)
 {
@@ -406,29 +650,39 @@ static int gred_change(struct Qdisc *sch, struct nlattr *opt,
        if (opt == NULL)
                return -EINVAL;
 
-       err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy, NULL);
+       err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy, extack);
        if (err < 0)
                return err;
 
        if (tb[TCA_GRED_PARMS] == NULL && tb[TCA_GRED_STAB] == NULL) {
                if (tb[TCA_GRED_LIMIT] != NULL)
                        sch->limit = nla_get_u32(tb[TCA_GRED_LIMIT]);
-               return gred_change_table_def(sch, tb[TCA_GRED_DPS]);
+               return gred_change_table_def(sch, tb[TCA_GRED_DPS], extack);
        }
 
        if (tb[TCA_GRED_PARMS] == NULL ||
            tb[TCA_GRED_STAB] == NULL ||
-           tb[TCA_GRED_LIMIT] != NULL)
+           tb[TCA_GRED_LIMIT] != NULL) {
+               NL_SET_ERR_MSG_MOD(extack, "can't configure Qdisc and virtual queue at the same time");
                return -EINVAL;
+       }
 
        max_P = tb[TCA_GRED_MAX_P] ? nla_get_u32(tb[TCA_GRED_MAX_P]) : 0;
 
-       err = -EINVAL;
        ctl = nla_data(tb[TCA_GRED_PARMS]);
        stab = nla_data(tb[TCA_GRED_STAB]);
 
-       if (ctl->DP >= table->DPs)
-               goto errout;
+       if (ctl->DP >= table->DPs) {
+               NL_SET_ERR_MSG_MOD(extack, "virtual queue index above virtual queue count");
+               return -EINVAL;
+       }
+
+       if (tb[TCA_GRED_VQ_LIST]) {
+               err = gred_vqs_validate(table, ctl->DP, tb[TCA_GRED_VQ_LIST],
+                                       extack);
+               if (err)
+                       return err;
+       }
 
        if (gred_rio_mode(table)) {
                if (ctl->prio == 0) {
@@ -448,9 +702,13 @@ static int gred_change(struct Qdisc *sch, struct nlattr *opt,
        prealloc = kzalloc(sizeof(*prealloc), GFP_KERNEL);
        sch_tree_lock(sch);
 
-       err = gred_change_vq(sch, ctl->DP, ctl, prio, stab, max_P, &prealloc);
+       err = gred_change_vq(sch, ctl->DP, ctl, prio, stab, max_P, &prealloc,
+                            extack);
        if (err < 0)
-               goto errout_locked;
+               goto err_unlock_free;
+
+       if (tb[TCA_GRED_VQ_LIST])
+               gred_vqs_apply(table, tb[TCA_GRED_VQ_LIST]);
 
        if (gred_rio_mode(table)) {
                gred_disable_wred_mode(table);
@@ -458,12 +716,15 @@ static int gred_change(struct Qdisc *sch, struct nlattr *opt,
                        gred_enable_wred_mode(table);
        }
 
-       err = 0;
+       sch_tree_unlock(sch);
+       kfree(prealloc);
+
+       gred_offload(sch, TC_GRED_REPLACE);
+       return 0;
 
-errout_locked:
+err_unlock_free:
        sch_tree_unlock(sch);
        kfree(prealloc);
-errout:
        return err;
 }
 
@@ -476,12 +737,15 @@ static int gred_init(struct Qdisc *sch, struct nlattr *opt,
        if (!opt)
                return -EINVAL;
 
-       err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy, NULL);
+       err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy, extack);
        if (err < 0)
                return err;
 
-       if (tb[TCA_GRED_PARMS] || tb[TCA_GRED_STAB])
+       if (tb[TCA_GRED_PARMS] || tb[TCA_GRED_STAB]) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "virtual queue configuration can't be specified at initialization time");
                return -EINVAL;
+       }
 
        if (tb[TCA_GRED_LIMIT])
                sch->limit = nla_get_u32(tb[TCA_GRED_LIMIT]);
@@ -489,13 +753,13 @@ static int gred_init(struct Qdisc *sch, struct nlattr *opt,
                sch->limit = qdisc_dev(sch)->tx_queue_len
                             * psched_mtu(qdisc_dev(sch));
 
-       return gred_change_table_def(sch, tb[TCA_GRED_DPS]);
+       return gred_change_table_def(sch, tb[TCA_GRED_DPS], extack);
 }
 
 static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct gred_sched *table = qdisc_priv(sch);
-       struct nlattr *parms, *opts = NULL;
+       struct nlattr *parms, *vqs, *opts = NULL;
        int i;
        u32 max_p[MAX_DPs];
        struct tc_gred_sopt sopt = {
@@ -505,6 +769,9 @@ static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
                .flags  = table->red_flags,
        };
 
+       if (gred_offload_dump_stats(sch))
+               goto nla_put_failure;
+
        opts = nla_nest_start(skb, TCA_OPTIONS);
        if (opts == NULL)
                goto nla_put_failure;
@@ -522,6 +789,7 @@ static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
        if (nla_put_u32(skb, TCA_GRED_LIMIT, sch->limit))
                goto nla_put_failure;
 
+       /* Old style all-in-one dump of VQs */
        parms = nla_nest_start(skb, TCA_GRED_PARMS);
        if (parms == NULL)
                goto nla_put_failure;
@@ -572,6 +840,58 @@ append_opt:
 
        nla_nest_end(skb, parms);
 
+       /* Dump the VQs again, in more structured way */
+       vqs = nla_nest_start(skb, TCA_GRED_VQ_LIST);
+       if (!vqs)
+               goto nla_put_failure;
+
+       for (i = 0; i < MAX_DPs; i++) {
+               struct gred_sched_data *q = table->tab[i];
+               struct nlattr *vq;
+
+               if (!q)
+                       continue;
+
+               vq = nla_nest_start(skb, TCA_GRED_VQ_ENTRY);
+               if (!vq)
+                       goto nla_put_failure;
+
+               if (nla_put_u32(skb, TCA_GRED_VQ_DP, q->DP))
+                       goto nla_put_failure;
+
+               if (nla_put_u32(skb, TCA_GRED_VQ_FLAGS, q->red_flags))
+                       goto nla_put_failure;
+
+               /* Stats */
+               if (nla_put_u64_64bit(skb, TCA_GRED_VQ_STAT_BYTES, q->bytesin,
+                                     TCA_GRED_VQ_PAD))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PACKETS, q->packetsin))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_BACKLOG,
+                               gred_backlog(table, q, sch)))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PROB_DROP,
+                               q->stats.prob_drop))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PROB_MARK,
+                               q->stats.prob_mark))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_FORCED_DROP,
+                               q->stats.forced_drop))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_FORCED_MARK,
+                               q->stats.forced_mark))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PDROP, q->stats.pdrop))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_OTHER, q->stats.other))
+                       goto nla_put_failure;
+
+               nla_nest_end(skb, vq);
+       }
+       nla_nest_end(skb, vqs);
+
        return nla_nest_end(skb, opts);
 
 nla_put_failure:
@@ -588,6 +908,7 @@ static void gred_destroy(struct Qdisc *sch)
                if (table->tab[i])
                        gred_destroy_vq(table->tab[i]);
        }
+       gred_offload(sch, TC_GRED_DESTROY);
 }
 
 static struct Qdisc_ops gred_qdisc_ops __read_mostly = {
index 1db5c1b..203659b 100644 (file)
@@ -193,6 +193,7 @@ static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
                    struct Qdisc **old, struct netlink_ext_ack *extack)
 {
        struct netdev_queue *dev_queue = mq_queue_get(sch, cl);
+       struct tc_mq_qopt_offload graft_offload;
        struct net_device *dev = qdisc_dev(sch);
 
        if (dev->flags & IFF_UP)
@@ -203,6 +204,14 @@ static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
                new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
        if (dev->flags & IFF_UP)
                dev_activate(dev);
+
+       graft_offload.handle = sch->handle;
+       graft_offload.graft_params.queue = cl - 1;
+       graft_offload.graft_params.child_handle = new ? new->handle : 0;
+       graft_offload.command = TC_MQ_GRAFT;
+
+       qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, *old,
+                                  TC_SETUP_QDISC_MQ, &graft_offload, extack);
        return 0;
 }
 
index 2c38e3d..75046ec 100644 (file)
@@ -77,6 +77,10 @@ struct netem_sched_data {
        /* internal t(ime)fifo qdisc uses t_root and sch->limit */
        struct rb_root t_root;
 
+       /* a linear queue; reduces rbtree rebalancing when jitter is low */
+       struct sk_buff  *t_head;
+       struct sk_buff  *t_tail;
+
        /* optional qdisc for classful handling (NULL at netem init) */
        struct Qdisc    *qdisc;
 
@@ -369,26 +373,39 @@ static void tfifo_reset(struct Qdisc *sch)
                rb_erase(&skb->rbnode, &q->t_root);
                rtnl_kfree_skbs(skb, skb);
        }
+
+       rtnl_kfree_skbs(q->t_head, q->t_tail);
+       q->t_head = NULL;
+       q->t_tail = NULL;
 }
 
 static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
        u64 tnext = netem_skb_cb(nskb)->time_to_send;
-       struct rb_node **p = &q->t_root.rb_node, *parent = NULL;
 
-       while (*p) {
-               struct sk_buff *skb;
-
-               parent = *p;
-               skb = rb_to_skb(parent);
-               if (tnext >= netem_skb_cb(skb)->time_to_send)
-                       p = &parent->rb_right;
+       if (!q->t_tail || tnext >= netem_skb_cb(q->t_tail)->time_to_send) {
+               if (q->t_tail)
+                       q->t_tail->next = nskb;
                else
-                       p = &parent->rb_left;
+                       q->t_head = nskb;
+               q->t_tail = nskb;
+       } else {
+               struct rb_node **p = &q->t_root.rb_node, *parent = NULL;
+
+               while (*p) {
+                       struct sk_buff *skb;
+
+                       parent = *p;
+                       skb = rb_to_skb(parent);
+                       if (tnext >= netem_skb_cb(skb)->time_to_send)
+                               p = &parent->rb_right;
+                       else
+                               p = &parent->rb_left;
+               }
+               rb_link_node(&nskb->rbnode, parent, p);
+               rb_insert_color(&nskb->rbnode, &q->t_root);
        }
-       rb_link_node(&nskb->rbnode, parent, p);
-       rb_insert_color(&nskb->rbnode, &q->t_root);
        sch->q.qlen++;
 }
 
@@ -431,6 +448,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
        int count = 1;
        int rc = NET_XMIT_SUCCESS;
 
+       /* Do not fool qdisc_drop_all() */
+       skb->prev = NULL;
+
        /* Random duplication */
        if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
                ++count;
@@ -530,9 +550,16 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
                                t_skb = skb_rb_last(&q->t_root);
                                t_last = netem_skb_cb(t_skb);
                                if (!last ||
-                                   t_last->time_to_send > last->time_to_send) {
+                                   t_last->time_to_send > last->time_to_send)
+                                       last = t_last;
+                       }
+                       if (q->t_tail) {
+                               struct netem_skb_cb *t_last =
+                                       netem_skb_cb(q->t_tail);
+
+                               if (!last ||
+                                   t_last->time_to_send > last->time_to_send)
                                        last = t_last;
-                               }
                        }
 
                        if (last) {
@@ -611,11 +638,38 @@ static void get_slot_next(struct netem_sched_data *q, u64 now)
        q->slot.bytes_left = q->slot_config.max_bytes;
 }
 
+static struct sk_buff *netem_peek(struct netem_sched_data *q)
+{
+       struct sk_buff *skb = skb_rb_first(&q->t_root);
+       u64 t1, t2;
+
+       if (!skb)
+               return q->t_head;
+       if (!q->t_head)
+               return skb;
+
+       t1 = netem_skb_cb(skb)->time_to_send;
+       t2 = netem_skb_cb(q->t_head)->time_to_send;
+       if (t1 < t2)
+               return skb;
+       return q->t_head;
+}
+
+static void netem_erase_head(struct netem_sched_data *q, struct sk_buff *skb)
+{
+       if (skb == q->t_head) {
+               q->t_head = skb->next;
+               if (!q->t_head)
+                       q->t_tail = NULL;
+       } else {
+               rb_erase(&skb->rbnode, &q->t_root);
+       }
+}
+
 static struct sk_buff *netem_dequeue(struct Qdisc *sch)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
        struct sk_buff *skb;
-       struct rb_node *p;
 
 tfifo_dequeue:
        skb = __qdisc_dequeue_head(&sch->q);
@@ -625,20 +679,18 @@ deliver:
                qdisc_bstats_update(sch, skb);
                return skb;
        }
-       p = rb_first(&q->t_root);
-       if (p) {
+       skb = netem_peek(q);
+       if (skb) {
                u64 time_to_send;
                u64 now = ktime_get_ns();
 
-               skb = rb_to_skb(p);
-
                /* if more time remaining? */
                time_to_send = netem_skb_cb(skb)->time_to_send;
                if (q->slot.slot_next && q->slot.slot_next < time_to_send)
                        get_slot_next(q, now);
 
-               if (time_to_send <= now &&  q->slot.slot_next <= now) {
-                       rb_erase(p, &q->t_root);
+               if (time_to_send <= now && q->slot.slot_next <= now) {
+                       netem_erase_head(q, skb);
                        sch->q.qlen--;
                        qdisc_qstats_backlog_dec(sch, skb);
                        skb->next = NULL;
index a1d08bd..9df9942 100644 (file)
@@ -166,6 +166,7 @@ static int red_offload(struct Qdisc *sch, bool enable)
                opt.set.min = q->parms.qth_min >> q->parms.Wlog;
                opt.set.max = q->parms.qth_max >> q->parms.Wlog;
                opt.set.probability = q->parms.max_P;
+               opt.set.limit = q->limit;
                opt.set.is_ecn = red_use_ecn(q);
                opt.set.is_harddrop = red_use_harddrop(q);
                opt.set.qstats = &sch->qstats;
@@ -367,6 +368,21 @@ static int red_dump_class(struct Qdisc *sch, unsigned long cl,
        return 0;
 }
 
+static void red_graft_offload(struct Qdisc *sch,
+                             struct Qdisc *new, struct Qdisc *old,
+                             struct netlink_ext_ack *extack)
+{
+       struct tc_red_qopt_offload graft_offload = {
+               .handle         = sch->handle,
+               .parent         = sch->parent,
+               .child_handle   = new->handle,
+               .command        = TC_RED_GRAFT,
+       };
+
+       qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, old,
+                                  TC_SETUP_QDISC_RED, &graft_offload, extack);
+}
+
 static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
                     struct Qdisc **old, struct netlink_ext_ack *extack)
 {
@@ -376,6 +392,8 @@ static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
                new = &noop_qdisc;
 
        *old = qdisc_replace(sch, new, &q->qdisc);
+
+       red_graft_offload(sch, new, *old, extack);
        return 0;
 }
 
index 6a28b96..201c888 100644 (file)
@@ -118,9 +118,6 @@ static struct sctp_association *sctp_association_init(
        asoc->flowlabel = sp->flowlabel;
        asoc->dscp = sp->dscp;
 
-       /* Initialize default path MTU. */
-       asoc->pathmtu = sp->pathmtu;
-
        /* Set association default SACK delay */
        asoc->sackdelay = msecs_to_jiffies(sp->sackdelay);
        asoc->sackfreq = sp->sackfreq;
@@ -135,6 +132,8 @@ static struct sctp_association *sctp_association_init(
         */
        asoc->max_burst = sp->max_burst;
 
+       asoc->subscribe = sp->subscribe;
+
        /* initialize association timers */
        asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] = asoc->rto_initial;
        asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] = asoc->rto_initial;
@@ -252,6 +251,10 @@ static struct sctp_association *sctp_association_init(
                             0, gfp))
                goto fail_init;
 
+       /* Initialize default path MTU. */
+       asoc->pathmtu = sp->pathmtu;
+       sctp_assoc_update_frag_point(asoc);
+
        /* Assume that peer would support both address types unless we are
         * told otherwise.
         */
@@ -434,7 +437,7 @@ static void sctp_association_destroy(struct sctp_association *asoc)
 
        WARN_ON(atomic_read(&asoc->rmem_alloc));
 
-       kfree(asoc);
+       kfree_rcu(asoc, rcu);
        SCTP_DBG_OBJCNT_DEC(assoc);
 }
 
index 7df3704..ebf28ad 100644 (file)
@@ -337,6 +337,34 @@ int sctp_bind_addr_match(struct sctp_bind_addr *bp,
        return match;
 }
 
+int sctp_bind_addrs_check(struct sctp_sock *sp,
+                         struct sctp_sock *sp2, int cnt2)
+{
+       struct sctp_bind_addr *bp2 = &sp2->ep->base.bind_addr;
+       struct sctp_bind_addr *bp = &sp->ep->base.bind_addr;
+       struct sctp_sockaddr_entry *laddr, *laddr2;
+       bool exist = false;
+       int cnt = 0;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(laddr, &bp->address_list, list) {
+               list_for_each_entry_rcu(laddr2, &bp2->address_list, list) {
+                       if (sp->pf->af->cmp_addr(&laddr->a, &laddr2->a) &&
+                           laddr->valid && laddr2->valid) {
+                               exist = true;
+                               goto next;
+                       }
+               }
+               cnt = 0;
+               break;
+next:
+               cnt++;
+       }
+       rcu_read_unlock();
+
+       return (cnt == cnt2) ? 0 : (exist ? -EEXIST : 1);
+}
+
 /* Does the address 'addr' conflict with any addresses in
  * the bp.
  */
index ce80878..64bef31 100644 (file)
@@ -86,11 +86,10 @@ void sctp_datamsg_free(struct sctp_datamsg *msg)
 /* Final destructruction of datamsg memory. */
 static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
 {
+       struct sctp_association *asoc = NULL;
        struct list_head *pos, *temp;
        struct sctp_chunk *chunk;
-       struct sctp_sock *sp;
        struct sctp_ulpevent *ev;
-       struct sctp_association *asoc = NULL;
        int error = 0, notify;
 
        /* If we failed, we may need to notify. */
@@ -108,9 +107,8 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
                        else
                                error = asoc->outqueue.error;
 
-                       sp = sctp_sk(asoc->base.sk);
-                       notify = sctp_ulpevent_type_enabled(SCTP_SEND_FAILED,
-                                                           &sp->subscribe);
+                       notify = sctp_ulpevent_type_enabled(asoc->subscribe,
+                                                           SCTP_SEND_FAILED);
                }
 
                /* Generate a SEND FAILED event only if enabled. */
@@ -191,6 +189,12 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
         * the packet
         */
        max_data = asoc->frag_point;
+       if (unlikely(!max_data)) {
+               max_data = sctp_min_frag_point(sctp_sk(asoc->base.sk),
+                                              sctp_datachk_len(&asoc->stream));
+               pr_warn_ratelimited("%s: asoc:%p frag_point is zero, forcing max_data to default minimum (%Zu)",
+                                   __func__, asoc, max_data);
+       }
 
        /* If the the peer requested that we authenticate DATA chunks
         * we need to account for bundling of the AUTH chunks along with
index 7ab08a5..d7a649d 100644 (file)
@@ -57,6 +57,7 @@
 #include <net/sctp/checksum.h>
 #include <net/net_namespace.h>
 #include <linux/rhashtable.h>
+#include <net/sock_reuseport.h>
 
 /* Forward declarations for internal helpers. */
 static int sctp_rcv_ootb(struct sk_buff *);
@@ -65,8 +66,10 @@ static struct sctp_association *__sctp_rcv_lookup(struct net *net,
                                      const union sctp_addr *paddr,
                                      const union sctp_addr *laddr,
                                      struct sctp_transport **transportp);
-static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
-                                               const union sctp_addr *laddr);
+static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(
+                                       struct net *net, struct sk_buff *skb,
+                                       const union sctp_addr *laddr,
+                                       const union sctp_addr *daddr);
 static struct sctp_association *__sctp_lookup_association(
                                        struct net *net,
                                        const union sctp_addr *local,
@@ -171,7 +174,7 @@ int sctp_rcv(struct sk_buff *skb)
        asoc = __sctp_rcv_lookup(net, skb, &src, &dest, &transport);
 
        if (!asoc)
-               ep = __sctp_rcv_lookup_endpoint(net, &dest);
+               ep = __sctp_rcv_lookup_endpoint(net, skb, &dest, &src);
 
        /* Retrieve the common input handling substructure. */
        rcvr = asoc ? &asoc->base : &ep->base;
@@ -721,43 +724,87 @@ discard:
 }
 
 /* Insert endpoint into the hash table.  */
-static void __sctp_hash_endpoint(struct sctp_endpoint *ep)
+static int __sctp_hash_endpoint(struct sctp_endpoint *ep)
 {
-       struct net *net = sock_net(ep->base.sk);
-       struct sctp_ep_common *epb;
+       struct sock *sk = ep->base.sk;
+       struct net *net = sock_net(sk);
        struct sctp_hashbucket *head;
+       struct sctp_ep_common *epb;
 
        epb = &ep->base;
-
        epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port);
        head = &sctp_ep_hashtable[epb->hashent];
 
+       if (sk->sk_reuseport) {
+               bool any = sctp_is_ep_boundall(sk);
+               struct sctp_ep_common *epb2;
+               struct list_head *list;
+               int cnt = 0, err = 1;
+
+               list_for_each(list, &ep->base.bind_addr.address_list)
+                       cnt++;
+
+               sctp_for_each_hentry(epb2, &head->chain) {
+                       struct sock *sk2 = epb2->sk;
+
+                       if (!net_eq(sock_net(sk2), net) || sk2 == sk ||
+                           !uid_eq(sock_i_uid(sk2), sock_i_uid(sk)) ||
+                           !sk2->sk_reuseport)
+                               continue;
+
+                       err = sctp_bind_addrs_check(sctp_sk(sk2),
+                                                   sctp_sk(sk), cnt);
+                       if (!err) {
+                               err = reuseport_add_sock(sk, sk2, any);
+                               if (err)
+                                       return err;
+                               break;
+                       } else if (err < 0) {
+                               return err;
+                       }
+               }
+
+               if (err) {
+                       err = reuseport_alloc(sk, any);
+                       if (err)
+                               return err;
+               }
+       }
+
        write_lock(&head->lock);
        hlist_add_head(&epb->node, &head->chain);
        write_unlock(&head->lock);
+       return 0;
 }
 
 /* Add an endpoint to the hash. Local BH-safe. */
-void sctp_hash_endpoint(struct sctp_endpoint *ep)
+int sctp_hash_endpoint(struct sctp_endpoint *ep)
 {
+       int err;
+
        local_bh_disable();
-       __sctp_hash_endpoint(ep);
+       err = __sctp_hash_endpoint(ep);
        local_bh_enable();
+
+       return err;
 }
 
 /* Remove endpoint from the hash table.  */
 static void __sctp_unhash_endpoint(struct sctp_endpoint *ep)
 {
-       struct net *net = sock_net(ep->base.sk);
+       struct sock *sk = ep->base.sk;
        struct sctp_hashbucket *head;
        struct sctp_ep_common *epb;
 
        epb = &ep->base;
 
-       epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port);
+       epb->hashent = sctp_ep_hashfn(sock_net(sk), epb->bind_addr.port);
 
        head = &sctp_ep_hashtable[epb->hashent];
 
+       if (rcu_access_pointer(sk->sk_reuseport_cb))
+               reuseport_detach_sock(sk);
+
        write_lock(&head->lock);
        hlist_del_init(&epb->node);
        write_unlock(&head->lock);
@@ -771,16 +818,35 @@ void sctp_unhash_endpoint(struct sctp_endpoint *ep)
        local_bh_enable();
 }
 
+static inline __u32 sctp_hashfn(const struct net *net, __be16 lport,
+                               const union sctp_addr *paddr, __u32 seed)
+{
+       __u32 addr;
+
+       if (paddr->sa.sa_family == AF_INET6)
+               addr = jhash(&paddr->v6.sin6_addr, 16, seed);
+       else
+               addr = (__force __u32)paddr->v4.sin_addr.s_addr;
+
+       return  jhash_3words(addr, ((__force __u32)paddr->v4.sin_port) << 16 |
+                            (__force __u32)lport, net_hash_mix(net), seed);
+}
+
 /* Look up an endpoint. */
-static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
-                                               const union sctp_addr *laddr)
+static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(
+                                       struct net *net, struct sk_buff *skb,
+                                       const union sctp_addr *laddr,
+                                       const union sctp_addr *paddr)
 {
        struct sctp_hashbucket *head;
        struct sctp_ep_common *epb;
        struct sctp_endpoint *ep;
+       struct sock *sk;
+       __be16 lport;
        int hash;
 
-       hash = sctp_ep_hashfn(net, ntohs(laddr->v4.sin_port));
+       lport = laddr->v4.sin_port;
+       hash = sctp_ep_hashfn(net, ntohs(lport));
        head = &sctp_ep_hashtable[hash];
        read_lock(&head->lock);
        sctp_for_each_hentry(epb, &head->chain) {
@@ -792,6 +858,15 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
        ep = sctp_sk(net->sctp.ctl_sock)->ep;
 
 hit:
+       sk = ep->base.sk;
+       if (sk->sk_reuseport) {
+               __u32 phash = sctp_hashfn(net, lport, paddr, 0);
+
+               sk = reuseport_select_sock(sk, phash, skb,
+                                          sizeof(struct sctphdr));
+               if (sk)
+                       ep = sctp_sk(sk)->ep;
+       }
        sctp_endpoint_hold(ep);
        read_unlock(&head->lock);
        return ep;
@@ -830,35 +905,17 @@ out:
 static inline __u32 sctp_hash_obj(const void *data, u32 len, u32 seed)
 {
        const struct sctp_transport *t = data;
-       const union sctp_addr *paddr = &t->ipaddr;
-       const struct net *net = sock_net(t->asoc->base.sk);
-       __be16 lport = htons(t->asoc->base.bind_addr.port);
-       __u32 addr;
 
-       if (paddr->sa.sa_family == AF_INET6)
-               addr = jhash(&paddr->v6.sin6_addr, 16, seed);
-       else
-               addr = (__force __u32)paddr->v4.sin_addr.s_addr;
-
-       return  jhash_3words(addr, ((__force __u32)paddr->v4.sin_port) << 16 |
-                            (__force __u32)lport, net_hash_mix(net), seed);
+       return sctp_hashfn(sock_net(t->asoc->base.sk),
+                          htons(t->asoc->base.bind_addr.port),
+                          &t->ipaddr, seed);
 }
 
 static inline __u32 sctp_hash_key(const void *data, u32 len, u32 seed)
 {
        const struct sctp_hash_cmp_arg *x = data;
-       const union sctp_addr *paddr = x->paddr;
-       const struct net *net = x->net;
-       __be16 lport = x->lport;
-       __u32 addr;
-
-       if (paddr->sa.sa_family == AF_INET6)
-               addr = jhash(&paddr->v6.sin6_addr, 16, seed);
-       else
-               addr = (__force __u32)paddr->v4.sin_addr.s_addr;
 
-       return  jhash_3words(addr, ((__force __u32)paddr->v4.sin_port) << 16 |
-                            (__force __u32)lport, net_hash_mix(net), seed);
+       return sctp_hashfn(x->net, x->lport, x->paddr, seed);
 }
 
 static const struct rhashtable_params sctp_hash_params = {
index 6e27c62..b9ed271 100644 (file)
@@ -101,6 +101,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
                if (addr) {
                        addr->a.v6.sin6_family = AF_INET6;
                        addr->a.v6.sin6_port = 0;
+                       addr->a.v6.sin6_flowinfo = 0;
                        addr->a.v6.sin6_addr = ifa->addr;
                        addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex;
                        addr->valid = 1;
index 67939ad..025f48e 100644 (file)
@@ -118,6 +118,9 @@ void sctp_packet_config(struct sctp_packet *packet, __u32 vtag,
                sctp_transport_route(tp, NULL, sp);
                if (asoc->param_flags & SPP_PMTUD_ENABLE)
                        sctp_assoc_sync_pmtu(asoc);
+       } else if (!sctp_transport_pmtu_check(tp)) {
+               if (asoc->param_flags & SPP_PMTUD_ENABLE)
+                       sctp_assoc_sync_pmtu(asoc);
        }
 
        if (asoc->pmtu_pending) {
@@ -396,25 +399,6 @@ finish:
        return retval;
 }
 
-static void sctp_packet_release_owner(struct sk_buff *skb)
-{
-       sk_free(skb->sk);
-}
-
-static void sctp_packet_set_owner_w(struct sk_buff *skb, struct sock *sk)
-{
-       skb_orphan(skb);
-       skb->sk = sk;
-       skb->destructor = sctp_packet_release_owner;
-
-       /*
-        * The data chunks have already been accounted for in sctp_sendmsg(),
-        * therefore only reserve a single byte to keep socket around until
-        * the packet has been transmitted.
-        */
-       refcount_inc(&sk->sk_wmem_alloc);
-}
-
 static void sctp_packet_gso_append(struct sk_buff *head, struct sk_buff *skb)
 {
        if (SCTP_OUTPUT_CB(head)->last == head)
@@ -426,6 +410,7 @@ static void sctp_packet_gso_append(struct sk_buff *head, struct sk_buff *skb)
        head->truesize += skb->truesize;
        head->data_len += skb->len;
        head->len += skb->len;
+       refcount_add(skb->truesize, &head->sk->sk_wmem_alloc);
 
        __skb_header_release(skb);
 }
@@ -601,7 +586,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
        if (!head)
                goto out;
        skb_reserve(head, packet->overhead + MAX_HEADER);
-       sctp_packet_set_owner_w(head, sk);
+       skb_set_owner_w(head, sk);
 
        /* set sctp header */
        sh = skb_push(head, sizeof(struct sctphdr));
index c0817f7..a8c4c33 100644 (file)
@@ -53,7 +53,7 @@
 int sctp_primitive_ ## name(struct net *net, struct sctp_association *asoc, \
                            void *arg) { \
        int error = 0; \
-       enum sctp_event event_type; union sctp_subtype subtype; \
+       enum sctp_event_type event_type; union sctp_subtype subtype; \
        enum sctp_state state; \
        struct sctp_endpoint *ep; \
        \
index 4a4fd19..f4ac6c5 100644 (file)
@@ -2462,6 +2462,9 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk,
                             asoc->c.sinit_max_instreams, gfp))
                goto clean_up;
 
+       /* Update frag_point when stream_interleave may get changed. */
+       sctp_assoc_update_frag_point(asoc);
+
        if (!asoc->temp && sctp_assoc_set_id(asoc, gfp))
                goto clean_up;
 
index 85d3930..1d143bc 100644 (file)
@@ -52,7 +52,7 @@
 #include <net/sctp/sm.h>
 #include <net/sctp/stream_sched.h>
 
-static int sctp_cmd_interpreter(enum sctp_event event_type,
+static int sctp_cmd_interpreter(enum sctp_event_type event_type,
                                union sctp_subtype subtype,
                                enum sctp_state state,
                                struct sctp_endpoint *ep,
@@ -61,7 +61,7 @@ static int sctp_cmd_interpreter(enum sctp_event event_type,
                                enum sctp_disposition status,
                                struct sctp_cmd_seq *commands,
                                gfp_t gfp);
-static int sctp_side_effects(enum sctp_event event_type,
+static int sctp_side_effects(enum sctp_event_type event_type,
                             union sctp_subtype subtype,
                             enum sctp_state state,
                             struct sctp_endpoint *ep,
@@ -623,7 +623,7 @@ static void sctp_cmd_init_failed(struct sctp_cmd_seq *commands,
 /* Worker routine to handle SCTP_CMD_ASSOC_FAILED.  */
 static void sctp_cmd_assoc_failed(struct sctp_cmd_seq *commands,
                                  struct sctp_association *asoc,
-                                 enum sctp_event event_type,
+                                 enum sctp_event_type event_type,
                                  union sctp_subtype subtype,
                                  struct sctp_chunk *chunk,
                                  unsigned int error)
@@ -1162,7 +1162,7 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc)
  * If you want to understand all of lksctp, this is a
  * good place to start.
  */
-int sctp_do_sm(struct net *net, enum sctp_event event_type,
+int sctp_do_sm(struct net *net, enum sctp_event_type event_type,
               union sctp_subtype subtype, enum sctp_state state,
               struct sctp_endpoint *ep, struct sctp_association *asoc,
               void *event_arg, gfp_t gfp)
@@ -1199,7 +1199,7 @@ int sctp_do_sm(struct net *net, enum sctp_event event_type,
 /*****************************************************************
  * This the master state function side effect processing function.
  *****************************************************************/
-static int sctp_side_effects(enum sctp_event event_type,
+static int sctp_side_effects(enum sctp_event_type event_type,
                             union sctp_subtype subtype,
                             enum sctp_state state,
                             struct sctp_endpoint *ep,
@@ -1285,7 +1285,7 @@ bail:
  ********************************************************************/
 
 /* This is the side-effect interpreter.  */
-static int sctp_cmd_interpreter(enum sctp_event event_type,
+static int sctp_cmd_interpreter(enum sctp_event_type event_type,
                                union sctp_subtype subtype,
                                enum sctp_state state,
                                struct sctp_endpoint *ep,
index 691d9dc..d239b94 100644 (file)
@@ -79,7 +79,7 @@ static const struct sctp_sm_table_entry bug = {
 
 const struct sctp_sm_table_entry *sctp_sm_lookup_event(
                                        struct net *net,
-                                       enum sctp_event event_type,
+                                       enum sctp_event_type event_type,
                                        enum sctp_state state,
                                        union sctp_subtype event_subtype)
 {
index 739f3e5..f93c3cf 100644 (file)
@@ -2230,7 +2230,7 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
        if (sp->recvrcvinfo)
                sctp_ulpevent_read_rcvinfo(event, msg);
        /* Check if we allow SCTP_SNDRCVINFO. */
-       if (sp->subscribe.sctp_data_io_event)
+       if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_DATA_IO_EVENT))
                sctp_ulpevent_read_sndrcvinfo(event, msg);
 
        err = copied;
@@ -2304,22 +2304,33 @@ static int sctp_setsockopt_disable_fragments(struct sock *sk,
 static int sctp_setsockopt_events(struct sock *sk, char __user *optval,
                                  unsigned int optlen)
 {
+       struct sctp_event_subscribe subscribe;
+       __u8 *sn_type = (__u8 *)&subscribe;
+       struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
-       struct sctp_ulpevent *event;
+       int i;
 
        if (optlen > sizeof(struct sctp_event_subscribe))
                return -EINVAL;
-       if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen))
+
+       if (copy_from_user(&subscribe, optval, optlen))
                return -EFAULT;
 
+       for (i = 0; i < optlen; i++)
+               sctp_ulpevent_type_set(&sp->subscribe, SCTP_SN_TYPE_BASE + i,
+                                      sn_type[i]);
+
+       list_for_each_entry(asoc, &sp->ep->asocs, asocs)
+               asoc->subscribe = sctp_sk(sk)->subscribe;
+
        /* At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT,
         * if there is no data to be sent or retransmit, the stack will
         * immediately send up this notification.
         */
-       if (sctp_ulpevent_type_enabled(SCTP_SENDER_DRY_EVENT,
-                                      &sctp_sk(sk)->subscribe)) {
-               asoc = sctp_id2assoc(sk, 0);
+       if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_SENDER_DRY_EVENT)) {
+               struct sctp_ulpevent *event;
 
+               asoc = sctp_id2assoc(sk, 0);
                if (asoc && sctp_outq_is_empty(&asoc->outqueue)) {
                        event = sctp_ulpevent_make_sender_dry_event(asoc,
                                        GFP_USER | __GFP_NOWARN);
@@ -3324,8 +3335,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
                __u16 datasize = asoc ? sctp_datachk_len(&asoc->stream) :
                                 sizeof(struct sctp_data_chunk);
 
-               min_len = sctp_mtu_payload(sp, SCTP_DEFAULT_MINSEGMENT,
-                                          datasize);
+               min_len = sctp_min_frag_point(sp, datasize);
                max_len = SCTP_MAX_CHUNK_LEN - datasize;
 
                if (val < min_len || val > max_len)
@@ -3940,32 +3950,16 @@ static int sctp_setsockopt_pr_supported(struct sock *sk,
                                        unsigned int optlen)
 {
        struct sctp_assoc_value params;
-       struct sctp_association *asoc;
-       int retval = -EINVAL;
 
        if (optlen != sizeof(params))
-               goto out;
-
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
-       asoc = sctp_id2assoc(sk, params.assoc_id);
-       if (asoc) {
-               asoc->prsctp_enable = !!params.assoc_value;
-       } else if (!params.assoc_id) {
-               struct sctp_sock *sp = sctp_sk(sk);
+               return -EINVAL;
 
-               sp->ep->prsctp_enable = !!params.assoc_value;
-       } else {
-               goto out;
-       }
+       if (copy_from_user(&params, optval, optlen))
+               return -EFAULT;
 
-       retval = 0;
+       sctp_sk(sk)->ep->prsctp_enable = !!params.assoc_value;
 
-out:
-       return retval;
+       return 0;
 }
 
 static int sctp_setsockopt_default_prinfo(struct sock *sk,
@@ -4277,6 +4271,57 @@ static int sctp_setsockopt_reuse_port(struct sock *sk, char __user *optval,
        return 0;
 }
 
+static int sctp_setsockopt_event(struct sock *sk, char __user *optval,
+                                unsigned int optlen)
+{
+       struct sctp_association *asoc;
+       struct sctp_ulpevent *event;
+       struct sctp_event param;
+       int retval = 0;
+
+       if (optlen < sizeof(param)) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       optlen = sizeof(param);
+       if (copy_from_user(&param, optval, optlen)) {
+               retval = -EFAULT;
+               goto out;
+       }
+
+       if (param.se_type < SCTP_SN_TYPE_BASE ||
+           param.se_type > SCTP_SN_TYPE_MAX) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       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;
+       }
+
+       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) {
+                               retval = -ENOMEM;
+                               goto out;
+                       }
+
+                       asoc->stream.si->enqueue_event(&asoc->ulpq, event);
+               }
+       }
+
+out:
+       return retval;
+}
+
 /* API 6.2 setsockopt(), getsockopt()
  *
  * Applications use setsockopt() and getsockopt() to set or retrieve
@@ -4474,6 +4519,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
        case SCTP_REUSE_PORT:
                retval = sctp_setsockopt_reuse_port(sk, optval, optlen);
                break;
+       case SCTP_EVENT:
+               retval = sctp_setsockopt_event(sk, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -4722,7 +4770,7 @@ static int sctp_init_sock(struct sock *sk)
        /* Initialize default event subscriptions. By default, all the
         * options are off.
         */
-       memset(&sp->subscribe, 0, sizeof(struct sctp_event_subscribe));
+       sp->subscribe = 0;
 
        /* Default Peer Address Parameters.  These defaults can
         * be modified via SCTP_PEER_ADDR_PARAMS
@@ -5267,14 +5315,24 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
 static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
                                  int __user *optlen)
 {
+       struct sctp_event_subscribe subscribe;
+       __u8 *sn_type = (__u8 *)&subscribe;
+       int i;
+
        if (len == 0)
                return -EINVAL;
        if (len > sizeof(struct sctp_event_subscribe))
                len = sizeof(struct sctp_event_subscribe);
        if (put_user(len, optlen))
                return -EFAULT;
-       if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
+
+       for (i = 0; i < len; i++)
+               sn_type[i] = sctp_ulpevent_type_enabled(sctp_sk(sk)->subscribe,
+                                                       SCTP_SN_TYPE_BASE + i);
+
+       if (copy_to_user(optval, &subscribe, len))
                return -EFAULT;
+
        return 0;
 }
 
@@ -7409,6 +7467,37 @@ static int sctp_getsockopt_reuse_port(struct sock *sk, int len,
        return 0;
 }
 
+static int sctp_getsockopt_event(struct sock *sk, int len, char __user *optval,
+                                int __user *optlen)
+{
+       struct sctp_association *asoc;
+       struct sctp_event param;
+       __u16 subscribe;
+
+       if (len < sizeof(param))
+               return -EINVAL;
+
+       len = sizeof(param);
+       if (copy_from_user(&param, optval, len))
+               return -EFAULT;
+
+       if (param.se_type < SCTP_SN_TYPE_BASE ||
+           param.se_type > SCTP_SN_TYPE_MAX)
+               return -EINVAL;
+
+       asoc = sctp_id2assoc(sk, param.se_assoc_id);
+       subscribe = asoc ? asoc->subscribe : sctp_sk(sk)->subscribe;
+       param.se_on = sctp_ulpevent_type_enabled(subscribe, param.se_type);
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+
+       if (copy_to_user(optval, &param, len))
+               return -EFAULT;
+
+       return 0;
+}
+
 static int sctp_getsockopt(struct sock *sk, int level, int optname,
                           char __user *optval, int __user *optlen)
 {
@@ -7607,6 +7696,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
        case SCTP_REUSE_PORT:
                retval = sctp_getsockopt_reuse_port(sk, len, optval, optlen);
                break;
+       case SCTP_EVENT:
+               retval = sctp_getsockopt_event(sk, len, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -7644,8 +7736,10 @@ static struct sctp_bind_bucket *sctp_bucket_create(
 
 static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
 {
-       bool reuse = (sk->sk_reuse || sctp_sk(sk)->reuse);
+       struct sctp_sock *sp = sctp_sk(sk);
+       bool reuse = (sk->sk_reuse || sp->reuse);
        struct sctp_bind_hashbucket *head; /* hash list */
+       kuid_t uid = sock_i_uid(sk);
        struct sctp_bind_bucket *pp;
        unsigned short snum;
        int ret;
@@ -7721,7 +7815,10 @@ pp_found:
 
                pr_debug("%s: found a possible match\n", __func__);
 
-               if (pp->fastreuse && reuse && sk->sk_state != SCTP_SS_LISTENING)
+               if ((pp->fastreuse && reuse &&
+                    sk->sk_state != SCTP_SS_LISTENING) ||
+                   (pp->fastreuseport && sk->sk_reuseport &&
+                    uid_eq(pp->fastuid, uid)))
                        goto success;
 
                /* Run through the list of sockets bound to the port
@@ -7735,16 +7832,18 @@ pp_found:
                 * in an endpoint.
                 */
                sk_for_each_bound(sk2, &pp->owner) {
-                       struct sctp_endpoint *ep2;
-                       ep2 = sctp_sk(sk2)->ep;
+                       struct sctp_sock *sp2 = sctp_sk(sk2);
+                       struct sctp_endpoint *ep2 = sp2->ep;
 
                        if (sk == sk2 ||
-                           (reuse && (sk2->sk_reuse || sctp_sk(sk2)->reuse) &&
-                            sk2->sk_state != SCTP_SS_LISTENING))
+                           (reuse && (sk2->sk_reuse || sp2->reuse) &&
+                            sk2->sk_state != SCTP_SS_LISTENING) ||
+                           (sk->sk_reuseport && sk2->sk_reuseport &&
+                            uid_eq(uid, sock_i_uid(sk2))))
                                continue;
 
-                       if (sctp_bind_addr_conflict(&ep2->base.bind_addr, addr,
-                                                sctp_sk(sk2), sctp_sk(sk))) {
+                       if (sctp_bind_addr_conflict(&ep2->base.bind_addr,
+                                                   addr, sp2, sp)) {
                                ret = (long)sk2;
                                goto fail_unlock;
                        }
@@ -7767,19 +7866,32 @@ pp_not_found:
                        pp->fastreuse = 1;
                else
                        pp->fastreuse = 0;
-       } else if (pp->fastreuse &&
-                  (!reuse || sk->sk_state == SCTP_SS_LISTENING))
-               pp->fastreuse = 0;
+
+               if (sk->sk_reuseport) {
+                       pp->fastreuseport = 1;
+                       pp->fastuid = uid;
+               } else {
+                       pp->fastreuseport = 0;
+               }
+       } else {
+               if (pp->fastreuse &&
+                   (!reuse || sk->sk_state == SCTP_SS_LISTENING))
+                       pp->fastreuse = 0;
+
+               if (pp->fastreuseport &&
+                   (!sk->sk_reuseport || !uid_eq(pp->fastuid, uid)))
+                       pp->fastreuseport = 0;
+       }
 
        /* We are set, so fill up all the data in the hash table
         * entry, tie the socket list information with the rest of the
         * sockets FIXME: Blurry, NPI (ipg).
         */
 success:
-       if (!sctp_sk(sk)->bind_hash) {
+       if (!sp->bind_hash) {
                inet_sk(sk)->inet_num = snum;
                sk_add_bind_node(sk, &pp->owner);
-               sctp_sk(sk)->bind_hash = pp;
+               sp->bind_hash = pp;
        }
        ret = 0;
 
@@ -7852,8 +7964,7 @@ static int sctp_listen_start(struct sock *sk, int backlog)
        }
 
        sk->sk_max_ack_backlog = backlog;
-       sctp_hash_endpoint(ep);
-       return 0;
+       return sctp_hash_endpoint(ep);
 }
 
 /*
index ffb940d..3892e76 100644 (file)
@@ -535,7 +535,6 @@ int sctp_send_add_streams(struct sctp_association *asoc,
                goto out;
        }
 
-       stream->incnt = incnt;
        stream->outcnt = outcnt;
 
        asoc->strreset_outstanding = !!out + !!in;
index 2b499a8..a6bf215 100644 (file)
@@ -503,7 +503,7 @@ static int sctp_enqueue_event(struct sctp_ulpq *ulpq,
                sk_incoming_cpu_update(sk);
        }
 
-       if (!sctp_ulpevent_is_enabled(event, &sp->subscribe))
+       if (!sctp_ulpevent_is_enabled(event, ulpq->asoc->subscribe))
                goto out_free;
 
        if (skb_list)
@@ -994,17 +994,19 @@ static void sctp_intl_stream_abort_pd(struct sctp_ulpq *ulpq, __u16 sid,
        struct sock *sk = ulpq->asoc->base.sk;
        struct sctp_ulpevent *ev = NULL;
 
-       if (!sctp_ulpevent_type_enabled(SCTP_PARTIAL_DELIVERY_EVENT,
-                                       &sctp_sk(sk)->subscribe))
+       if (!sctp_ulpevent_type_enabled(ulpq->asoc->subscribe,
+                                       SCTP_PARTIAL_DELIVERY_EVENT))
                return;
 
        ev = sctp_ulpevent_make_pdapi(ulpq->asoc, SCTP_PARTIAL_DELIVERY_ABORTED,
                                      sid, mid, flags, gfp);
        if (ev) {
+               struct sctp_sock *sp = sctp_sk(sk);
+
                __skb_queue_tail(&sk->sk_receive_queue, sctp_event2skb(ev));
 
-               if (!sctp_sk(sk)->data_ready_signalled) {
-                       sctp_sk(sk)->data_ready_signalled = 1;
+               if (!sp->data_ready_signalled) {
+                       sp->data_ready_signalled = 1;
                        sk->sk_data_ready(sk);
                }
        }
index 331cc73..5dde921 100644 (file)
@@ -219,7 +219,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
                sk_incoming_cpu_update(sk);
        }
        /* Check if the user wishes to receive this event.  */
-       if (!sctp_ulpevent_is_enabled(event, &sp->subscribe))
+       if (!sctp_ulpevent_is_enabled(event, ulpq->asoc->subscribe))
                goto out_free;
 
        /* If we are in partial delivery mode, post to the lobby until
@@ -1129,16 +1129,16 @@ void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
 void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
 {
        struct sctp_ulpevent *ev = NULL;
-       struct sock *sk;
        struct sctp_sock *sp;
+       struct sock *sk;
 
        if (!ulpq->pd_mode)
                return;
 
        sk = ulpq->asoc->base.sk;
        sp = sctp_sk(sk);
-       if (sctp_ulpevent_type_enabled(SCTP_PARTIAL_DELIVERY_EVENT,
-                                      &sctp_sk(sk)->subscribe))
+       if (sctp_ulpevent_type_enabled(ulpq->asoc->subscribe,
+                                      SCTP_PARTIAL_DELIVERY_EVENT))
                ev = sctp_ulpevent_make_pdapi(ulpq->asoc,
                                              SCTP_PARTIAL_DELIVERY_ABORTED,
                                              0, 0, 0, gfp);
index 80e2119..c4da4a7 100644 (file)
@@ -127,6 +127,8 @@ static int smc_release(struct socket *sock)
        smc = smc_sk(sk);
 
        /* cleanup for a dangling non-blocking connect */
+       if (smc->connect_info && sk->sk_state == SMC_INIT)
+               tcp_abort(smc->clcsock->sk, ECONNABORTED);
        flush_work(&smc->connect_work);
        kfree(smc->connect_info);
        smc->connect_info = NULL;
@@ -145,8 +147,14 @@ static int smc_release(struct socket *sock)
                sk->sk_shutdown |= SHUTDOWN_MASK;
        }
        if (smc->clcsock) {
+               if (smc->use_fallback && 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)
@@ -203,6 +211,7 @@ static struct sock *smc_sock_alloc(struct net *net, struct socket *sock,
        spin_lock_init(&smc->conn.send_lock);
        sk->sk_prot->hash(sk);
        sk_refcnt_debug_inc(sk);
+       mutex_init(&smc->clcsock_release_lock);
 
        return sk;
 }
@@ -299,14 +308,17 @@ static void smc_copy_sock_settings_to_smc(struct smc_sock *smc)
        smc_copy_sock_settings(&smc->sk, smc->clcsock->sk, SK_FLAGS_CLC_TO_SMC);
 }
 
-/* register a new rmb, optionally send confirm_rkey msg to register with peer */
+/* register a new rmb, send confirm_rkey msg to register with peer */
 static int smc_reg_rmb(struct smc_link *link, struct smc_buf_desc *rmb_desc,
                       bool conf_rkey)
 {
-       /* register memory region for new rmb */
-       if (smc_wr_reg_send(link, rmb_desc->mr_rx[SMC_SINGLE_LINK])) {
-               rmb_desc->regerr = 1;
-               return -EFAULT;
+       if (!rmb_desc->wr_reg) {
+               /* register memory region for new rmb */
+               if (smc_wr_reg_send(link, rmb_desc->mr_rx[SMC_SINGLE_LINK])) {
+                       rmb_desc->regerr = 1;
+                       return -EFAULT;
+               }
+               rmb_desc->wr_reg = 1;
        }
        if (!conf_rkey)
                return 0;
@@ -335,8 +347,8 @@ static int smc_clnt_conf_first_link(struct smc_sock *smc)
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
-                                     SMC_CLC_DECLINE);
-               return rc;
+                                     SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
+               return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc;
        }
 
        if (link->llc_confirm_rc)
@@ -363,8 +375,8 @@ static int smc_clnt_conf_first_link(struct smc_sock *smc)
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
-                                     SMC_CLC_DECLINE);
-               return rc;
+                                     SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
+               return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_AL : rc;
        }
 
        /* send add link reject message, only one link supported for now */
@@ -533,7 +545,8 @@ static int smc_connect_clc(struct smc_sock *smc, int smc_type,
        if (rc)
                return rc;
        /* receive SMC Accept CLC message */
-       return smc_clc_wait_msg(smc, aclc, sizeof(*aclc), SMC_CLC_ACCEPT);
+       return smc_clc_wait_msg(smc, aclc, sizeof(*aclc), SMC_CLC_ACCEPT,
+                               CLC_WAIT_TIME);
 }
 
 /* setup for RDMA connection of client */
@@ -547,7 +560,8 @@ static int smc_connect_rdma(struct smc_sock *smc,
 
        mutex_lock(&smc_create_lgr_pending);
        local_contact = smc_conn_create(smc, false, aclc->hdr.flag, ibdev,
-                                       ibport, &aclc->lcl, NULL, 0);
+                                       ibport, ntoh24(aclc->qpn), &aclc->lcl,
+                                       NULL, 0);
        if (local_contact < 0) {
                if (local_contact == -ENOMEM)
                        reason_code = SMC_CLC_DECL_MEM;/* insufficient memory*/
@@ -580,8 +594,7 @@ static int smc_connect_rdma(struct smc_sock *smc,
                        return smc_connect_abort(smc, SMC_CLC_DECL_ERR_RDYLNK,
                                                 local_contact);
        } else {
-               if (!smc->conn.rmb_desc->reused &&
-                   smc_reg_rmb(link, smc->conn.rmb_desc, true))
+               if (smc_reg_rmb(link, smc->conn.rmb_desc, true))
                        return smc_connect_abort(smc, SMC_CLC_DECL_ERR_REGRMB,
                                                 local_contact);
        }
@@ -618,7 +631,7 @@ static int smc_connect_ism(struct smc_sock *smc,
        int rc = 0;
 
        mutex_lock(&smc_create_lgr_pending);
-       local_contact = smc_conn_create(smc, true, aclc->hdr.flag, NULL, 0,
+       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);
@@ -818,7 +831,7 @@ static int smc_clcsock_accept(struct smc_sock *lsmc, struct smc_sock **new_smc)
        struct socket *new_clcsock = NULL;
        struct sock *lsk = &lsmc->sk;
        struct sock *new_sk;
-       int rc;
+       int rc = -EINVAL;
 
        release_sock(lsk);
        new_sk = smc_sock_alloc(sock_net(lsk), NULL, lsk->sk_protocol);
@@ -831,7 +844,10 @@ static int smc_clcsock_accept(struct smc_sock *lsmc, struct smc_sock **new_smc)
        }
        *new_smc = smc_sk(new_sk);
 
-       rc = kernel_accept(lsmc->clcsock, &new_clcsock, 0);
+       mutex_lock(&lsmc->clcsock_release_lock);
+       if (lsmc->clcsock)
+               rc = kernel_accept(lsmc->clcsock, &new_clcsock, 0);
+       mutex_unlock(&lsmc->clcsock_release_lock);
        lock_sock(lsk);
        if  (rc < 0)
                lsk->sk_err = -rc;
@@ -965,8 +981,8 @@ static int smc_serv_conf_first_link(struct smc_sock *smc)
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
-                                     SMC_CLC_DECLINE);
-               return rc;
+                                     SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
+               return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc;
        }
 
        if (link->llc_confirm_resp_rc)
@@ -986,8 +1002,8 @@ static int smc_serv_conf_first_link(struct smc_sock *smc)
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
-                                     SMC_CLC_DECLINE);
-               return rc;
+                                     SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
+               return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_AL : rc;
        }
 
        smc_llc_link_active(link, net->ipv4.sysctl_tcp_keepalive_time);
@@ -1083,7 +1099,7 @@ static int smc_listen_rdma_init(struct smc_sock *new_smc,
                                int *local_contact)
 {
        /* allocate connection / link group */
-       *local_contact = smc_conn_create(new_smc, false, 0, ibdev, ibport,
+       *local_contact = smc_conn_create(new_smc, false, 0, ibdev, ibport, 0,
                                         &pclc->lcl, NULL, 0);
        if (*local_contact < 0) {
                if (*local_contact == -ENOMEM)
@@ -1107,7 +1123,7 @@ static int smc_listen_ism_init(struct smc_sock *new_smc,
        struct smc_clc_msg_smcd *pclc_smcd;
 
        pclc_smcd = smc_get_clc_msg_smcd(pclc);
-       *local_contact = smc_conn_create(new_smc, true, 0, NULL, 0, NULL,
+       *local_contact = smc_conn_create(new_smc, true, 0, NULL, 0, 0, NULL,
                                         ismdev, pclc_smcd->gid);
        if (*local_contact < 0) {
                if (*local_contact == -ENOMEM)
@@ -1142,10 +1158,8 @@ static int smc_listen_rdma_reg(struct smc_sock *new_smc, int local_contact)
        struct smc_link *link = &new_smc->conn.lgr->lnk[SMC_SINGLE_LINK];
 
        if (local_contact != SMC_FIRST_CONTACT) {
-               if (!new_smc->conn.rmb_desc->reused) {
-                       if (smc_reg_rmb(link, new_smc->conn.rmb_desc, true))
-                               return SMC_CLC_DECL_ERR_REGRMB;
-               }
+               if (smc_reg_rmb(link, new_smc->conn.rmb_desc, true))
+                       return SMC_CLC_DECL_ERR_REGRMB;
        }
        smc_rmb_sync_sg_for_device(&new_smc->conn);
 
@@ -1181,7 +1195,6 @@ static int smc_listen_rdma_finish(struct smc_sock *new_smc,
        return 0;
 
 decline:
-       mutex_unlock(&smc_create_lgr_pending);
        smc_listen_decline(new_smc, reason_code, local_contact);
        return reason_code;
 }
@@ -1222,7 +1235,7 @@ static void smc_listen_work(struct work_struct *work)
         */
        pclc = (struct smc_clc_msg_proposal *)&buf;
        reason_code = smc_clc_wait_msg(new_smc, pclc, SMC_CLC_MAX_LEN,
-                                      SMC_CLC_PROPOSAL);
+                                      SMC_CLC_PROPOSAL, CLC_WAIT_TIME);
        if (reason_code) {
                smc_listen_decline(new_smc, reason_code, 0);
                return;
@@ -1272,7 +1285,7 @@ static void smc_listen_work(struct work_struct *work)
 
        /* receive SMC Confirm CLC message */
        reason_code = smc_clc_wait_msg(new_smc, &cclc, sizeof(cclc),
-                                      SMC_CLC_CONFIRM);
+                                      SMC_CLC_CONFIRM, CLC_WAIT_TIME);
        if (reason_code) {
                mutex_unlock(&smc_create_lgr_pending);
                smc_listen_decline(new_smc, reason_code, local_contact);
@@ -1281,8 +1294,10 @@ static void smc_listen_work(struct work_struct *work)
 
        /* finish worker */
        if (!ism_supported) {
-               if (smc_listen_rdma_finish(new_smc, &cclc, local_contact))
+               if (smc_listen_rdma_finish(new_smc, &cclc, local_contact)) {
+                       mutex_unlock(&smc_create_lgr_pending);
                        return;
+               }
        }
        smc_conn_save_peer_info(new_smc, &cclc);
        mutex_unlock(&smc_create_lgr_pending);
@@ -1354,7 +1369,6 @@ static int smc_listen(struct socket *sock, int backlog)
        sk->sk_max_ack_backlog = backlog;
        sk->sk_ack_backlog = 0;
        sk->sk_state = SMC_LISTEN;
-       INIT_WORK(&smc->tcp_listen_work, smc_tcp_listen_work);
        sock_hold(sk); /* sock_hold in tcp_listen_worker */
        if (!schedule_work(&smc->tcp_listen_work))
                sock_put(sk);
index 08786ac..5721416 100644 (file)
@@ -219,6 +219,10 @@ struct smc_sock {                          /* smc sock container */
                                                 * started, waiting for unsent
                                                 * data to be sent
                                                 */
+       struct mutex            clcsock_release_lock;
+                                               /* protects clcsock of a listen
+                                                * socket
+                                                * */
 };
 
 static inline struct smc_sock *smc_sk(const struct sock *sk)
index ed5dcf0..db83332 100644 (file)
@@ -81,7 +81,7 @@ static inline void smc_cdc_add_pending_send(struct smc_connection *conn,
                sizeof(struct smc_cdc_msg) > SMC_WR_BUF_SIZE,
                "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_cdc_msg)");
        BUILD_BUG_ON_MSG(
-               sizeof(struct smc_cdc_msg) != SMC_WR_TX_SIZE,
+               offsetofend(struct smc_cdc_msg, reserved) > SMC_WR_TX_SIZE,
                "must adapt SMC_WR_TX_SIZE to sizeof(struct smc_cdc_msg); if not all smc_wr upper layer protocols use the same message size any more, must start to set link->wr_tx_sges[i].length on each individual smc_wr_tx_send()");
        BUILD_BUG_ON_MSG(
                sizeof(struct smc_cdc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE,
@@ -177,23 +177,24 @@ void smc_cdc_tx_dismiss_slots(struct smc_connection *conn)
 int smcd_cdc_msg_send(struct smc_connection *conn)
 {
        struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
+       union smc_host_cursor curs;
        struct smcd_cdc_msg cdc;
        int rc, diff;
 
        memset(&cdc, 0, sizeof(cdc));
        cdc.common.type = SMC_CDC_MSG_TYPE;
-       cdc.prod_wrap = conn->local_tx_ctrl.prod.wrap;
-       cdc.prod_count = conn->local_tx_ctrl.prod.count;
-
-       cdc.cons_wrap = conn->local_tx_ctrl.cons.wrap;
-       cdc.cons_count = conn->local_tx_ctrl.cons.count;
-       cdc.prod_flags = conn->local_tx_ctrl.prod_flags;
-       cdc.conn_state_flags = conn->local_tx_ctrl.conn_state_flags;
+       curs.acurs.counter = atomic64_read(&conn->local_tx_ctrl.prod.acurs);
+       cdc.prod.wrap = curs.wrap;
+       cdc.prod.count = curs.count;
+       curs.acurs.counter = atomic64_read(&conn->local_tx_ctrl.cons.acurs);
+       cdc.cons.wrap = curs.wrap;
+       cdc.cons.count = curs.count;
+       cdc.cons.prod_flags = conn->local_tx_ctrl.prod_flags;
+       cdc.cons.conn_state_flags = conn->local_tx_ctrl.conn_state_flags;
        rc = smcd_tx_ism_write(conn, &cdc, sizeof(cdc), 0, 1);
        if (rc)
                return rc;
-       smc_curs_copy(&conn->rx_curs_confirmed, &conn->local_tx_ctrl.cons,
-                     conn);
+       smc_curs_copy(&conn->rx_curs_confirmed, &curs, conn);
        /* 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);
@@ -331,13 +332,16 @@ static void smc_cdc_msg_recv(struct smc_sock *smc, struct smc_cdc_msg *cdc)
 static void smcd_cdc_rx_tsklet(unsigned long data)
 {
        struct smc_connection *conn = (struct smc_connection *)data;
+       struct smcd_cdc_msg *data_cdc;
        struct smcd_cdc_msg cdc;
        struct smc_sock *smc;
 
        if (!conn)
                return;
 
-       memcpy(&cdc, conn->rmb_desc->cpu_addr, sizeof(cdc));
+       data_cdc = (struct smcd_cdc_msg *)conn->rmb_desc->cpu_addr;
+       smcd_curs_copy(&cdc.prod, &data_cdc->prod, conn);
+       smcd_curs_copy(&cdc.cons, &data_cdc->cons, conn);
        smc = container_of(conn, struct smc_sock, conn);
        smc_cdc_msg_recv(smc, (struct smc_cdc_msg *)&cdc);
 }
index 934df44..b5bfe38 100644 (file)
@@ -48,21 +48,31 @@ struct smc_cdc_msg {
        struct smc_cdc_producer_flags   prod_flags;
        struct smc_cdc_conn_state_flags conn_state_flags;
        u8                              reserved[18];
-} __packed;                                    /* format defined in RFC7609 */
+};
+
+/* SMC-D cursor format */
+union smcd_cdc_cursor {
+       struct {
+               u16     wrap;
+               u32     count;
+               struct smc_cdc_producer_flags   prod_flags;
+               struct smc_cdc_conn_state_flags conn_state_flags;
+       } __packed;
+#ifdef KERNEL_HAS_ATOMIC64
+       atomic64_t              acurs;          /* for atomic processing */
+#else
+       u64                     acurs;          /* for atomic processing */
+#endif
+} __aligned(8);
 
 /* CDC message for SMC-D */
 struct smcd_cdc_msg {
        struct smc_wr_rx_hdr common;    /* Type = 0xFE */
        u8 res1[7];
-       u16 prod_wrap;
-       u32 prod_count;
-       u8 res2[2];
-       u16 cons_wrap;
-       u32 cons_count;
-       struct smc_cdc_producer_flags   prod_flags;
-       struct smc_cdc_conn_state_flags conn_state_flags;
+       union smcd_cdc_cursor   prod;
+       union smcd_cdc_cursor   cons;
        u8 res3[8];
-} __packed;
+} __aligned(8);
 
 static inline bool smc_cdc_rxed_any_close(struct smc_connection *conn)
 {
@@ -135,6 +145,21 @@ static inline void smc_curs_copy_net(union smc_cdc_cursor *tgt,
 #endif
 }
 
+static inline void smcd_curs_copy(union smcd_cdc_cursor *tgt,
+                                 union smcd_cdc_cursor *src,
+                                 struct smc_connection *conn)
+{
+#ifndef KERNEL_HAS_ATOMIC64
+       unsigned long flags;
+
+       spin_lock_irqsave(&conn->acurs_lock, flags);
+       tgt->acurs = src->acurs;
+       spin_unlock_irqrestore(&conn->acurs_lock, flags);
+#else
+       atomic64_set(&tgt->acurs, atomic64_read(&src->acurs));
+#endif
+}
+
 /* calculate cursor difference between old and new, where old <= new */
 static inline int smc_curs_diff(unsigned int size,
                                union smc_host_cursor *old,
@@ -222,12 +247,17 @@ 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)
 {
-       local->prod.wrap = peer->prod_wrap;
-       local->prod.count = peer->prod_count;
-       local->cons.wrap = peer->cons_wrap;
-       local->cons.count = peer->cons_count;
-       local->prod_flags = peer->prod_flags;
-       local->conn_state_flags = peer->conn_state_flags;
+       union smc_host_cursor temp;
+
+       temp.wrap = peer->prod.wrap;
+       temp.count = peer->prod.count;
+       atomic64_set(&local->prod.acurs, atomic64_read(&temp.acurs));
+
+       temp.wrap = peer->cons.wrap;
+       temp.count = peer->cons.count;
+       atomic64_set(&local->cons.acurs, atomic64_read(&temp.acurs));
+       local->prod_flags = peer->cons.prod_flags;
+       local->conn_state_flags = peer->cons.conn_state_flags;
 }
 
 static inline void smc_cdc_msg_to_host(struct smc_host_cdc_msg *local,
index 89c3a8c..776e9df 100644 (file)
@@ -265,7 +265,7 @@ out:
  * clcsock error, -EINTR, -ECONNRESET, -EPROTO otherwise.
  */
 int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
-                    u8 expected_type)
+                    u8 expected_type, unsigned long timeout)
 {
        long rcvtimeo = smc->clcsock->sk->sk_rcvtimeo;
        struct sock *clc_sk = smc->clcsock->sk;
@@ -285,7 +285,7 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
         * sizeof(struct smc_clc_msg_hdr)
         */
        krflags = MSG_PEEK | MSG_WAITALL;
-       smc->clcsock->sk->sk_rcvtimeo = CLC_WAIT_TIME;
+       clc_sk->sk_rcvtimeo = timeout;
        iov_iter_kvec(&msg.msg_iter, READ, &vec, 1,
                        sizeof(struct smc_clc_msg_hdr));
        len = sock_recvmsg(smc->clcsock, &msg, krflags);
@@ -297,7 +297,11 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
        }
        if (clc_sk->sk_err) {
                reason_code = -clc_sk->sk_err;
-               smc->sk.sk_err = clc_sk->sk_err;
+               if (clc_sk->sk_err == EAGAIN &&
+                   expected_type == SMC_CLC_DECLINE)
+                       clc_sk->sk_err = 0; /* reset for fallback usage */
+               else
+                       smc->sk.sk_err = clc_sk->sk_err;
                goto out;
        }
        if (!len) { /* peer has performed orderly shutdown */
@@ -306,7 +310,8 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
                goto out;
        }
        if (len < 0) {
-               smc->sk.sk_err = -len;
+               if (len != -EAGAIN || expected_type != SMC_CLC_DECLINE)
+                       smc->sk.sk_err = -len;
                reason_code = len;
                goto out;
        }
@@ -346,7 +351,7 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
        }
 
 out:
-       smc->clcsock->sk->sk_rcvtimeo = rcvtimeo;
+       clc_sk->sk_rcvtimeo = rcvtimeo;
        return reason_code;
 }
 
@@ -374,10 +379,8 @@ int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info)
        len = kernel_sendmsg(smc->clcsock, &msg, &vec, 1,
                             sizeof(struct smc_clc_msg_decline));
        if (len < sizeof(struct smc_clc_msg_decline))
-               smc->sk.sk_err = EPROTO;
-       if (len < 0)
-               smc->sk.sk_err = -len;
-       return sock_error(&smc->sk);
+               len = -EPROTO;
+       return len > 0 ? 0 : len;
 }
 
 /* send CLC PROPOSAL message across internal TCP socket */
@@ -536,7 +539,6 @@ int smc_clc_send_accept(struct smc_sock *new_smc, int srv_first_contact)
        struct smc_link *link;
        struct msghdr msg;
        struct kvec vec;
-       int rc = 0;
        int len;
 
        memset(&aclc, 0, sizeof(aclc));
@@ -589,13 +591,8 @@ int smc_clc_send_accept(struct smc_sock *new_smc, int srv_first_contact)
        vec.iov_len = ntohs(aclc.hdr.length);
        len = kernel_sendmsg(new_smc->clcsock, &msg, &vec, 1,
                             ntohs(aclc.hdr.length));
-       if (len < ntohs(aclc.hdr.length)) {
-               if (len >= 0)
-                       new_smc->sk.sk_err = EPROTO;
-               else
-                       new_smc->sk.sk_err = new_smc->clcsock->sk->sk_err;
-               rc = sock_error(&new_smc->sk);
-       }
+       if (len < ntohs(aclc.hdr.length))
+               len = len >= 0 ? -EPROTO : -new_smc->clcsock->sk->sk_err;
 
-       return rc;
+       return len > 0 ? 0 : len;
 }
index 18da89b..24658e8 100644 (file)
@@ -27,6 +27,7 @@
 #define SMC_TYPE_D             1               /* SMC-D only                 */
 #define SMC_TYPE_B             3               /* SMC-R and SMC-D            */
 #define CLC_WAIT_TIME          (6 * HZ)        /* max. wait time on clcsock  */
+#define CLC_WAIT_TIME_SHORT    HZ              /* short wait time on clcsock */
 #define SMC_CLC_DECL_MEM       0x01010000  /* insufficient memory resources  */
 #define SMC_CLC_DECL_TIMEOUT_CL        0x02010000  /* timeout w4 QP confirm link     */
 #define SMC_CLC_DECL_TIMEOUT_AL        0x02020000  /* timeout w4 QP add link         */
@@ -182,7 +183,7 @@ struct smcd_dev;
 int smc_clc_prfx_match(struct socket *clcsock,
                       struct smc_clc_msg_proposal_prefix *prop);
 int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
-                    u8 expected_type);
+                    u8 expected_type, unsigned long timeout);
 int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info);
 int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
                          struct smc_ib_device *smcibdev, u8 ibport, u8 gid[],
index 18daebc..35c1cdc 100644 (file)
@@ -149,6 +149,8 @@ static int smc_link_send_delete(struct smc_link *lnk)
        return -ENOTCONN;
 }
 
+static void smc_lgr_free(struct smc_link_group *lgr);
+
 static void smc_lgr_free_work(struct work_struct *work)
 {
        struct smc_link_group *lgr = container_of(to_delayed_work(work),
@@ -171,8 +173,11 @@ free:
        spin_unlock_bh(&smc_lgr_list.lock);
 
        if (!lgr->is_smcd && !lgr->terminating) {
+               struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];
+
                /* try to send del link msg, on error free lgr immediately */
-               if (!smc_link_send_delete(&lgr->lnk[SMC_SINGLE_LINK])) {
+               if (lnk->state == SMC_LNK_ACTIVE &&
+                   !smc_link_send_delete(lnk)) {
                        /* reschedule in case we never receive a response */
                        smc_lgr_schedule_free_work(lgr);
                        return;
@@ -184,6 +189,8 @@ free:
 
                if (!lgr->is_smcd && lnk->state != SMC_LNK_INACTIVE)
                        smc_llc_link_inactive(lnk);
+               if (lgr->is_smcd)
+                       smc_ism_signal_shutdown(lgr);
                smc_lgr_free(lgr);
        }
 }
@@ -293,8 +300,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->reused = 1;
                        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);
+                       }
                } else {
                        /* buf registration failed, reuse not possible */
                        write_lock_bh(&lgr->rmbs_lock);
@@ -408,7 +420,7 @@ static void smc_lgr_free_bufs(struct smc_link_group *lgr)
 }
 
 /* remove a link group */
-void smc_lgr_free(struct smc_link_group *lgr)
+static void smc_lgr_free(struct smc_link_group *lgr)
 {
        smc_lgr_free_bufs(lgr);
        if (lgr->is_smcd)
@@ -485,7 +497,7 @@ void smc_port_terminate(struct smc_ib_device *smcibdev, u8 ibport)
 }
 
 /* Called when SMC-D device is terminated or peer is lost */
-void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid)
+void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid, unsigned short vlan)
 {
        struct smc_link_group *lgr, *l;
        LIST_HEAD(lgr_free_list);
@@ -495,7 +507,7 @@ void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid)
        list_for_each_entry_safe(lgr, l, &smc_lgr_list.list, list) {
                if (lgr->is_smcd && lgr->smcd == dev &&
                    (!peer_gid || lgr->peer_gid == peer_gid) &&
-                   !list_empty(&lgr->list)) {
+                   (vlan == VLAN_VID_MASK || lgr->vlan_id == vlan)) {
                        __smc_lgr_terminate(lgr);
                        list_move(&lgr->list, &lgr_free_list);
                }
@@ -506,6 +518,8 @@ void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid)
        list_for_each_entry_safe(lgr, l, &lgr_free_list, list) {
                list_del_init(&lgr->list);
                cancel_delayed_work_sync(&lgr->free_work);
+               if (!peer_gid && vlan == VLAN_VID_MASK) /* dev terminated? */
+                       smc_ism_signal_shutdown(lgr);
                smc_lgr_free(lgr);
        }
 }
@@ -559,7 +573,7 @@ out:
 
 static bool smcr_lgr_match(struct smc_link_group *lgr,
                           struct smc_clc_msg_local *lcl,
-                          enum smc_lgr_role role)
+                          enum smc_lgr_role role, u32 clcqpn)
 {
        return !memcmp(lgr->peer_systemid, lcl->id_for_peer,
                       SMC_SYSTEMID_LEN) &&
@@ -567,7 +581,9 @@ static bool smcr_lgr_match(struct smc_link_group *lgr,
                        SMC_GID_SIZE) &&
                !memcmp(lgr->lnk[SMC_SINGLE_LINK].peer_mac, lcl->mac,
                        sizeof(lcl->mac)) &&
-               lgr->role == role;
+               lgr->role == role &&
+               (lgr->role == SMC_SERV ||
+                lgr->lnk[SMC_SINGLE_LINK].peer_qpn == clcqpn);
 }
 
 static bool smcd_lgr_match(struct smc_link_group *lgr,
@@ -578,7 +594,7 @@ static bool smcd_lgr_match(struct smc_link_group *lgr,
 
 /* create a new SMC connection (and a new link group if necessary) */
 int smc_conn_create(struct smc_sock *smc, bool is_smcd, int srv_first_contact,
-                   struct smc_ib_device *smcibdev, u8 ibport,
+                   struct smc_ib_device *smcibdev, u8 ibport, u32 clcqpn,
                    struct smc_clc_msg_local *lcl, struct smcd_dev *smcd,
                    u64 peer_gid)
 {
@@ -603,7 +619,7 @@ int smc_conn_create(struct smc_sock *smc, bool is_smcd, int srv_first_contact,
        list_for_each_entry(lgr, &smc_lgr_list.list, list) {
                write_lock_bh(&lgr->conns_lock);
                if ((is_smcd ? smcd_lgr_match(lgr, smcd, peer_gid) :
-                    smcr_lgr_match(lgr, lcl, role)) &&
+                    smcr_lgr_match(lgr, lcl, role, clcqpn)) &&
                    !lgr->sync_err &&
                    lgr->vlan_id == vlan_id &&
                    (role == SMC_CLNT ||
@@ -1024,6 +1040,8 @@ void smc_core_exit(void)
                        smc_llc_link_inactive(lnk);
                }
                cancel_delayed_work_sync(&lgr->free_work);
+               if (lgr->is_smcd)
+                       smc_ism_signal_shutdown(lgr);
                smc_lgr_free(lgr); /* free link group */
        }
 }
index c156674..b002879 100644 (file)
@@ -109,6 +109,9 @@ struct smc_link {
        int                     llc_testlink_time; /* testlink interval */
        struct completion       llc_confirm_rkey; /* wait 4 rx of cnf rkey */
        int                     llc_confirm_rkey_rc; /* rc from cnf rkey msg */
+       struct completion       llc_delete_rkey; /* wait 4 rx of del rkey */
+       int                     llc_delete_rkey_rc; /* rc from del rkey msg */
+       struct mutex            llc_delete_rkey_mutex; /* serialize usage */
 };
 
 /* For now we just allow one parallel link per link group. The SMC protocol
@@ -127,7 +130,7 @@ struct smc_buf_desc {
        struct page             *pages;
        int                     len;            /* length of buffer */
        u32                     used;           /* currently used / unused */
-       u8                      reused  : 1;    /* new created / reused */
+       u8                      wr_reg  : 1;    /* mem region registered */
        u8                      regerr  : 1;    /* err during registration */
        union {
                struct { /* SMC-R */
@@ -243,11 +246,11 @@ struct smc_sock;
 struct smc_clc_msg_accept_confirm;
 struct smc_clc_msg_local;
 
-void smc_lgr_free(struct smc_link_group *lgr);
 void smc_lgr_forget(struct smc_link_group *lgr);
 void smc_lgr_terminate(struct smc_link_group *lgr);
 void smc_port_terminate(struct smc_ib_device *smcibdev, u8 ibport);
-void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid);
+void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid,
+                       unsigned short vlan);
 int smc_buf_create(struct smc_sock *smc, bool is_smcd);
 int smc_uncompress_bufsize(u8 compressed);
 int smc_rmb_rtoken_handling(struct smc_connection *conn,
@@ -262,7 +265,7 @@ int smc_vlan_by_tcpsk(struct socket *clcsock, unsigned short *vlan_id);
 
 void smc_conn_free(struct smc_connection *conn);
 int smc_conn_create(struct smc_sock *smc, bool is_smcd, int srv_first_contact,
-                   struct smc_ib_device *smcibdev, u8 ibport,
+                   struct smc_ib_device *smcibdev, u8 ibport, u32 clcqpn,
                    struct smc_clc_msg_local *lcl, struct smcd_dev *smcd,
                    u64 peer_gid);
 void smcd_conn_free(struct smc_connection *conn);
index e36f21c..2fff79d 100644 (file)
@@ -187,22 +187,28 @@ struct smc_ism_event_work {
 #define ISM_EVENT_REQUEST              0x0001
 #define ISM_EVENT_RESPONSE             0x0002
 #define ISM_EVENT_REQUEST_IR           0x00000001
+#define ISM_EVENT_CODE_SHUTDOWN                0x80
 #define ISM_EVENT_CODE_TESTLINK                0x83
 
+union smcd_sw_event_info {
+       u64     info;
+       struct {
+               u8              uid[SMC_LGR_ID_SIZE];
+               unsigned short  vlan_id;
+               u16             code;
+       };
+};
+
 static void smcd_handle_sw_event(struct smc_ism_event_work *wrk)
 {
-       union {
-               u64     info;
-               struct {
-                       u32             uid;
-                       unsigned short  vlanid;
-                       u16             code;
-               };
-       } ev_info;
+       union smcd_sw_event_info ev_info;
 
+       ev_info.info = wrk->event.info;
        switch (wrk->event.code) {
+       case ISM_EVENT_CODE_SHUTDOWN:   /* Peer shut down DMBs */
+               smc_smcd_terminate(wrk->smcd, wrk->event.tok, ev_info.vlan_id);
+               break;
        case ISM_EVENT_CODE_TESTLINK:   /* Activity timer */
-               ev_info.info = wrk->event.info;
                if (ev_info.code == ISM_EVENT_REQUEST) {
                        ev_info.code = ISM_EVENT_RESPONSE;
                        wrk->smcd->ops->signal_event(wrk->smcd,
@@ -215,6 +221,21 @@ static void smcd_handle_sw_event(struct smc_ism_event_work *wrk)
        }
 }
 
+int smc_ism_signal_shutdown(struct smc_link_group *lgr)
+{
+       int rc;
+       union smcd_sw_event_info ev_info;
+
+       memcpy(ev_info.uid, lgr->id, SMC_LGR_ID_SIZE);
+       ev_info.vlan_id = lgr->vlan_id;
+       ev_info.code = ISM_EVENT_REQUEST;
+       rc = lgr->smcd->ops->signal_event(lgr->smcd, lgr->peer_gid,
+                                         ISM_EVENT_REQUEST_IR,
+                                         ISM_EVENT_CODE_SHUTDOWN,
+                                         ev_info.info);
+       return rc;
+}
+
 /* worker for SMC-D events */
 static void smc_ism_event_work(struct work_struct *work)
 {
@@ -223,7 +244,7 @@ static void smc_ism_event_work(struct work_struct *work)
 
        switch (wrk->event.type) {
        case ISM_EVENT_GID:     /* GID event, token is peer GID */
-               smc_smcd_terminate(wrk->smcd, wrk->event.tok);
+               smc_smcd_terminate(wrk->smcd, wrk->event.tok, VLAN_VID_MASK);
                break;
        case ISM_EVENT_DMB:
                break;
@@ -289,7 +310,7 @@ void smcd_unregister_dev(struct smcd_dev *smcd)
        spin_unlock(&smcd_dev_list.lock);
        flush_workqueue(smcd->event_wq);
        destroy_workqueue(smcd->event_wq);
-       smc_smcd_terminate(smcd, 0);
+       smc_smcd_terminate(smcd, 0, VLAN_VID_MASK);
 
        device_del(&smcd->dev);
 }
index aee45b8..4da946c 100644 (file)
@@ -45,4 +45,5 @@ int smc_ism_register_dmb(struct smc_link_group *lgr, int buf_size,
 int smc_ism_unregister_dmb(struct smcd_dev *dev, struct smc_buf_desc *dmb_desc);
 int smc_ism_write(struct smcd_dev *dev, const struct smc_ism_position *pos,
                  void *data, size_t len);
+int smc_ism_signal_shutdown(struct smc_link_group *lgr);
 #endif
index 9c916c7..a6d3623 100644 (file)
@@ -238,6 +238,29 @@ static int smc_llc_send_confirm_rkey(struct smc_link *link,
        return rc;
 }
 
+/* send LLC delete rkey request */
+static int smc_llc_send_delete_rkey(struct smc_link *link,
+                                   struct smc_buf_desc *rmb_desc)
+{
+       struct smc_llc_msg_delete_rkey *rkeyllc;
+       struct smc_wr_tx_pend_priv *pend;
+       struct smc_wr_buf *wr_buf;
+       int rc;
+
+       rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
+       if (rc)
+               return rc;
+       rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf;
+       memset(rkeyllc, 0, sizeof(*rkeyllc));
+       rkeyllc->hd.common.type = SMC_LLC_DELETE_RKEY;
+       rkeyllc->hd.length = sizeof(struct smc_llc_msg_delete_rkey);
+       rkeyllc->num_rkeys = 1;
+       rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey);
+       /* send llc message */
+       rc = smc_wr_tx_send(link, pend);
+       return rc;
+}
+
 /* prepare an add link message */
 static void smc_llc_prep_add_link(struct smc_llc_msg_add_link *addllc,
                                  struct smc_link *link, u8 mac[], u8 gid[],
@@ -509,7 +532,9 @@ static void smc_llc_rx_delete_rkey(struct smc_link *link,
        int i, max;
 
        if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
-               /* unused as long as we don't send this type of msg */
+               link->llc_delete_rkey_rc = llc->hd.flags &
+                                           SMC_LLC_FLAG_RKEY_NEG;
+               complete(&link->llc_delete_rkey);
        } else {
                max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
                for (i = 0; i < max; i++) {
@@ -610,6 +635,8 @@ int smc_llc_link_init(struct smc_link *link)
        init_completion(&link->llc_add);
        init_completion(&link->llc_add_resp);
        init_completion(&link->llc_confirm_rkey);
+       init_completion(&link->llc_delete_rkey);
+       mutex_init(&link->llc_delete_rkey_mutex);
        init_completion(&link->llc_testlink_resp);
        INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
        return 0;
@@ -650,8 +677,11 @@ int smc_llc_do_confirm_rkey(struct smc_link *link,
 {
        int rc;
 
+       /* protected by mutex smc_create_lgr_pending */
        reinit_completion(&link->llc_confirm_rkey);
-       smc_llc_send_confirm_rkey(link, rmb_desc);
+       rc = smc_llc_send_confirm_rkey(link, rmb_desc);
+       if (rc)
+               return rc;
        /* receive CONFIRM RKEY response from server over RoCE fabric */
        rc = wait_for_completion_interruptible_timeout(&link->llc_confirm_rkey,
                                                       SMC_LLC_WAIT_TIME);
@@ -660,6 +690,29 @@ int smc_llc_do_confirm_rkey(struct smc_link *link,
        return 0;
 }
 
+/* unregister an rtoken at the remote peer */
+int smc_llc_do_delete_rkey(struct smc_link *link,
+                          struct smc_buf_desc *rmb_desc)
+{
+       int rc;
+
+       mutex_lock(&link->llc_delete_rkey_mutex);
+       reinit_completion(&link->llc_delete_rkey);
+       rc = smc_llc_send_delete_rkey(link, rmb_desc);
+       if (rc)
+               goto out;
+       /* receive DELETE RKEY response from server over RoCE fabric */
+       rc = wait_for_completion_interruptible_timeout(&link->llc_delete_rkey,
+                                                      SMC_LLC_WAIT_TIME);
+       if (rc <= 0 || link->llc_delete_rkey_rc)
+               rc = -EFAULT;
+       else
+               rc = 0;
+out:
+       mutex_unlock(&link->llc_delete_rkey_mutex);
+       return rc;
+}
+
 /***************************** init, exit, misc ******************************/
 
 static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
index 9e2ff08..461c0c3 100644 (file)
@@ -49,6 +49,8 @@ void smc_llc_link_inactive(struct smc_link *link);
 void smc_llc_link_clear(struct smc_link *link);
 int smc_llc_do_confirm_rkey(struct smc_link *link,
                            struct smc_buf_desc *rmb_desc);
+int smc_llc_do_delete_rkey(struct smc_link *link,
+                          struct smc_buf_desc *rmb_desc);
 int smc_llc_init(void) __init;
 
 #endif /* SMC_LLC_H */
index 3c458d2..c269475 100644 (file)
@@ -215,12 +215,14 @@ int smc_wr_tx_put_slot(struct smc_link *link,
 
        pend = container_of(wr_pend_priv, struct smc_wr_tx_pend, priv);
        if (pend->idx < link->wr_tx_cnt) {
+               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]));
-               test_and_clear_bit(pend->idx, link->wr_tx_mask);
+               test_and_clear_bit(idx, link->wr_tx_mask);
                return 1;
        }
 
index 593826e..334fcc6 100644 (file)
@@ -853,7 +853,7 @@ static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
        struct socket *sock = file->private_data;
 
        if (unlikely(!sock->ops->splice_read))
-               return -EINVAL;
+               return generic_file_splice_read(file, ppos, pipe, len, flags);
 
        return sock->ops->splice_read(sock, ppos, pipe, len, flags);
 }
index d8831b9..ab4a3be 100644 (file)
@@ -281,13 +281,7 @@ static bool generic_key_to_expire(struct rpc_cred *cred)
 {
        struct auth_cred *acred = &container_of(cred, struct generic_cred,
                                                gc_base)->acred;
-       bool ret;
-
-       get_rpccred(cred);
-       ret = test_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
-       put_rpccred(cred);
-
-       return ret;
+       return test_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
 }
 
 static const struct rpc_credops generic_credops = {
index 30f970c..ba76547 100644 (file)
@@ -1239,36 +1239,59 @@ gss_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
        return &gss_auth->rpc_auth;
 }
 
+static struct gss_cred *
+gss_dup_cred(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
+{
+       struct gss_cred *new;
+
+       /* Make a copy of the cred so that we can reference count it */
+       new = kzalloc(sizeof(*gss_cred), GFP_NOIO);
+       if (new) {
+               struct auth_cred acred = {
+                       .uid = gss_cred->gc_base.cr_uid,
+               };
+               struct gss_cl_ctx *ctx =
+                       rcu_dereference_protected(gss_cred->gc_ctx, 1);
+
+               rpcauth_init_cred(&new->gc_base, &acred,
+                               &gss_auth->rpc_auth,
+                               &gss_nullops);
+               new->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
+               new->gc_service = gss_cred->gc_service;
+               new->gc_principal = gss_cred->gc_principal;
+               kref_get(&gss_auth->kref);
+               rcu_assign_pointer(new->gc_ctx, ctx);
+               gss_get_ctx(ctx);
+       }
+       return new;
+}
+
 /*
- * gss_destroying_context will cause the RPCSEC_GSS to send a NULL RPC call
+ * gss_send_destroy_context will cause the RPCSEC_GSS to send a NULL RPC call
  * to the server with the GSS control procedure field set to
  * RPC_GSS_PROC_DESTROY. This should normally cause the server to release
  * all RPCSEC_GSS state associated with that context.
  */
-static int
-gss_destroying_context(struct rpc_cred *cred)
+static void
+gss_send_destroy_context(struct rpc_cred *cred)
 {
        struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
        struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
        struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1);
+       struct gss_cred *new;
        struct rpc_task *task;
 
-       if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0)
-               return 0;
+       new = gss_dup_cred(gss_auth, gss_cred);
+       if (new) {
+               ctx->gc_proc = RPC_GSS_PROC_DESTROY;
 
-       ctx->gc_proc = RPC_GSS_PROC_DESTROY;
-       cred->cr_ops = &gss_nullops;
+               task = rpc_call_null(gss_auth->client, &new->gc_base,
+                               RPC_TASK_ASYNC|RPC_TASK_SOFT);
+               if (!IS_ERR(task))
+                       rpc_put_task(task);
 
-       /* Take a reference to ensure the cred will be destroyed either
-        * by the RPC call or by the put_rpccred() below */
-       get_rpccred(cred);
-
-       task = rpc_call_null(gss_auth->client, cred, RPC_TASK_ASYNC|RPC_TASK_SOFT);
-       if (!IS_ERR(task))
-               rpc_put_task(task);
-
-       put_rpccred(cred);
-       return 1;
+               put_rpccred(&new->gc_base);
+       }
 }
 
 /* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure
@@ -1330,8 +1353,8 @@ static void
 gss_destroy_cred(struct rpc_cred *cred)
 {
 
-       if (gss_destroying_context(cred))
-               return;
+       if (test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0)
+               gss_send_destroy_context(cred);
        gss_destroy_nullcred(cred);
 }
 
@@ -1768,6 +1791,7 @@ priv_release_snd_buf(struct rpc_rqst *rqstp)
        for (i=0; i < rqstp->rq_enc_pages_num; i++)
                __free_page(rqstp->rq_enc_pages[i]);
        kfree(rqstp->rq_enc_pages);
+       rqstp->rq_release_snd_buf = NULL;
 }
 
 static int
@@ -1776,6 +1800,9 @@ alloc_enc_pages(struct rpc_rqst *rqstp)
        struct xdr_buf *snd_buf = &rqstp->rq_snd_buf;
        int first, last, i;
 
+       if (rqstp->rq_release_snd_buf)
+               rqstp->rq_release_snd_buf(rqstp);
+
        if (snd_buf->page_len == 0) {
                rqstp->rq_enc_pages_num = 0;
                return 0;
index ae3b814..24cbddc 100644 (file)
@@ -1915,6 +1915,13 @@ call_connect_status(struct rpc_task *task)
        struct rpc_clnt *clnt = task->tk_client;
        int status = task->tk_status;
 
+       /* Check if the task was already transmitted */
+       if (!test_bit(RPC_TASK_NEED_XMIT, &task->tk_runstate)) {
+               xprt_end_transmit(task);
+               task->tk_action = call_transmit_status;
+               return;
+       }
+
        dprint_status(task);
 
        trace_rpc_connect_status(task);
@@ -1945,6 +1952,7 @@ call_connect_status(struct rpc_task *task)
                /* retry with existing socket, after a delay */
                rpc_delay(task, 3*HZ);
                /* fall through */
+       case -ENOTCONN:
        case -EAGAIN:
                /* Check for timeouts before looping back to call_bind */
        case -ETIMEDOUT:
@@ -2302,6 +2310,7 @@ out_retry:
        task->tk_status = 0;
        /* Note: rpc_verify_header() may have freed the RPC slot */
        if (task->tk_rqstp == req) {
+               xdr_free_bvec(&req->rq_rcv_buf);
                req->rq_reply_bytes_recvd = req->rq_rcv_buf.len = 0;
                if (task->tk_client->cl_discrtry)
                        xprt_conditional_disconnect(req->rq_xprt,
index 9062967..7e55cfc 100644 (file)
@@ -175,7 +175,7 @@ int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
                return -1;
        if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
            !skb->csum_complete_sw)
-               netdev_rx_csum_fault(skb->dev);
+               netdev_rx_csum_fault(skb->dev, skb);
        return 0;
 no_checksum:
        if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0)
index 2bbb8d3..f302c6e 100644 (file)
@@ -546,7 +546,7 @@ EXPORT_SYMBOL_GPL(xdr_commit_encode);
 static __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
                size_t nbytes)
 {
-       static __be32 *p;
+       __be32 *p;
        int space_left;
        int frag1bytes, frag2bytes;
 
@@ -673,11 +673,10 @@ void xdr_truncate_encode(struct xdr_stream *xdr, size_t len)
                WARN_ON_ONCE(xdr->iov);
                return;
        }
-       if (fraglen) {
+       if (fraglen)
                xdr->end = head->iov_base + head->iov_len;
-               xdr->page_ptr--;
-       }
        /* (otherwise assume xdr->end is already set) */
+       xdr->page_ptr--;
        head->iov_len = len;
        buf->len = len;
        xdr->p = head->iov_base + head->iov_len;
index 86bea45..73547d1 100644 (file)
@@ -67,7 +67,6 @@
  */
 static void     xprt_init(struct rpc_xprt *xprt, struct net *net);
 static __be32  xprt_alloc_xid(struct rpc_xprt *xprt);
-static void    xprt_connect_status(struct rpc_task *task);
 static void     xprt_destroy(struct rpc_xprt *xprt);
 
 static DEFINE_SPINLOCK(xprt_list_lock);
@@ -680,7 +679,9 @@ void xprt_force_disconnect(struct rpc_xprt *xprt)
        /* Try to schedule an autoclose RPC call */
        if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0)
                queue_work(xprtiod_workqueue, &xprt->task_cleanup);
-       xprt_wake_pending_tasks(xprt, -EAGAIN);
+       else if (xprt->snd_task)
+               rpc_wake_up_queued_task_set_status(&xprt->pending,
+                               xprt->snd_task, -ENOTCONN);
        spin_unlock_bh(&xprt->transport_lock);
 }
 EXPORT_SYMBOL_GPL(xprt_force_disconnect);
@@ -820,46 +821,25 @@ void xprt_connect(struct rpc_task *task)
        if (!xprt_connected(xprt)) {
                task->tk_timeout = task->tk_rqstp->rq_timeout;
                task->tk_rqstp->rq_connect_cookie = xprt->connect_cookie;
-               rpc_sleep_on(&xprt->pending, task, xprt_connect_status);
+               rpc_sleep_on(&xprt->pending, task, NULL);
 
                if (test_bit(XPRT_CLOSING, &xprt->state))
                        return;
                if (xprt_test_and_set_connecting(xprt))
                        return;
-               xprt->stat.connect_start = jiffies;
-               xprt->ops->connect(xprt, task);
+               /* Race breaker */
+               if (!xprt_connected(xprt)) {
+                       xprt->stat.connect_start = jiffies;
+                       xprt->ops->connect(xprt, task);
+               } else {
+                       xprt_clear_connecting(xprt);
+                       task->tk_status = 0;
+                       rpc_wake_up_queued_task(&xprt->pending, task);
+               }
        }
        xprt_release_write(xprt, task);
 }
 
-static void xprt_connect_status(struct rpc_task *task)
-{
-       switch (task->tk_status) {
-       case 0:
-               dprintk("RPC: %5u xprt_connect_status: connection established\n",
-                               task->tk_pid);
-               break;
-       case -ECONNREFUSED:
-       case -ECONNRESET:
-       case -ECONNABORTED:
-       case -ENETUNREACH:
-       case -EHOSTUNREACH:
-       case -EPIPE:
-       case -EAGAIN:
-               dprintk("RPC: %5u xprt_connect_status: retrying\n", task->tk_pid);
-               break;
-       case -ETIMEDOUT:
-               dprintk("RPC: %5u xprt_connect_status: connect attempt timed "
-                               "out\n", task->tk_pid);
-               break;
-       default:
-               dprintk("RPC: %5u xprt_connect_status: error %d connecting to "
-                               "server %s\n", task->tk_pid, -task->tk_status,
-                               task->tk_rqstp->rq_xprt->servername);
-               task->tk_status = -EIO;
-       }
-}
-
 enum xprt_xid_rb_cmp {
        XID_RB_EQUAL,
        XID_RB_LEFT,
@@ -1623,6 +1603,8 @@ xprt_request_init(struct rpc_task *task)
        req->rq_snd_buf.buflen = 0;
        req->rq_rcv_buf.len = 0;
        req->rq_rcv_buf.buflen = 0;
+       req->rq_snd_buf.bvec = NULL;
+       req->rq_rcv_buf.bvec = NULL;
        req->rq_release_snd_buf = NULL;
        xprt_reset_majortimeo(req);
        dprintk("RPC: %5u reserved req %p xid %08x\n", task->tk_pid,
index ae77c71..f0b3700 100644 (file)
@@ -330,18 +330,16 @@ xs_alloc_sparse_pages(struct xdr_buf *buf, size_t want, gfp_t gfp)
 {
        size_t i,n;
 
-       if (!(buf->flags & XDRBUF_SPARSE_PAGES))
+       if (!want || !(buf->flags & XDRBUF_SPARSE_PAGES))
                return want;
-       if (want > buf->page_len)
-               want = buf->page_len;
        n = (buf->page_base + want + PAGE_SIZE - 1) >> PAGE_SHIFT;
        for (i = 0; i < n; i++) {
                if (buf->pages[i])
                        continue;
                buf->bvec[i].bv_page = buf->pages[i] = alloc_page(gfp);
                if (!buf->pages[i]) {
-                       buf->page_len = (i * PAGE_SIZE) - buf->page_base;
-                       return buf->page_len;
+                       i *= PAGE_SIZE;
+                       return i > buf->page_base ? i - buf->page_base : 0;
                }
        }
        return want;
@@ -378,8 +376,8 @@ static ssize_t
 xs_read_discard(struct socket *sock, struct msghdr *msg, int flags,
                size_t count)
 {
-       struct kvec kvec = { 0 };
-       return xs_read_kvec(sock, msg, flags | MSG_TRUNC, &kvec, count, 0);
+       iov_iter_discard(&msg->msg_iter, READ, count);
+       return sock_recvmsg(sock, msg, flags);
 }
 
 static ssize_t
@@ -398,16 +396,17 @@ xs_read_xdr_buf(struct socket *sock, struct msghdr *msg, int flags,
                if (offset == count || msg->msg_flags & (MSG_EOR|MSG_TRUNC))
                        goto out;
                if (ret != want)
-                       goto eagain;
+                       goto out;
                seek = 0;
        } else {
                seek -= buf->head[0].iov_len;
                offset += buf->head[0].iov_len;
        }
-       if (seek < buf->page_len) {
-               want = xs_alloc_sparse_pages(buf,
-                               min_t(size_t, count - offset, buf->page_len),
-                               GFP_NOWAIT);
+
+       want = xs_alloc_sparse_pages(buf,
+                       min_t(size_t, count - offset, buf->page_len),
+                       GFP_NOWAIT);
+       if (seek < want) {
                ret = xs_read_bvec(sock, msg, flags, buf->bvec,
                                xdr_buf_pagecount(buf),
                                want + buf->page_base,
@@ -418,12 +417,13 @@ xs_read_xdr_buf(struct socket *sock, struct msghdr *msg, int flags,
                if (offset == count || msg->msg_flags & (MSG_EOR|MSG_TRUNC))
                        goto out;
                if (ret != want)
-                       goto eagain;
+                       goto out;
                seek = 0;
        } else {
-               seek -= buf->page_len;
-               offset += buf->page_len;
+               seek -= want;
+               offset += want;
        }
+
        if (seek < buf->tail[0].iov_len) {
                want = min_t(size_t, count - offset, buf->tail[0].iov_len);
                ret = xs_read_kvec(sock, msg, flags, &buf->tail[0], want, seek);
@@ -433,17 +433,13 @@ xs_read_xdr_buf(struct socket *sock, struct msghdr *msg, int flags,
                if (offset == count || msg->msg_flags & (MSG_EOR|MSG_TRUNC))
                        goto out;
                if (ret != want)
-                       goto eagain;
+                       goto out;
        } else
                offset += buf->tail[0].iov_len;
        ret = -EMSGSIZE;
-       msg->msg_flags |= MSG_TRUNC;
 out:
        *read = offset - seek_init;
        return ret;
-eagain:
-       ret = -EAGAIN;
-       goto out;
 sock_err:
        offset += seek;
        goto out;
@@ -486,19 +482,20 @@ xs_read_stream_request(struct sock_xprt *transport, struct msghdr *msg,
        if (transport->recv.offset == transport->recv.len) {
                if (xs_read_stream_request_done(transport))
                        msg->msg_flags |= MSG_EOR;
-               return transport->recv.copied;
+               return read;
        }
 
        switch (ret) {
+       default:
+               break;
+       case -EFAULT:
        case -EMSGSIZE:
-               return transport->recv.copied;
+               msg->msg_flags |= MSG_TRUNC;
+               return read;
        case 0:
                return -ESHUTDOWN;
-       default:
-               if (ret < 0)
-                       return ret;
        }
-       return -EAGAIN;
+       return ret < 0 ? ret : read;
 }
 
 static size_t
@@ -537,7 +534,7 @@ xs_read_stream_call(struct sock_xprt *transport, struct msghdr *msg, int flags)
 
        ret = xs_read_stream_request(transport, msg, flags, req);
        if (msg->msg_flags & (MSG_EOR|MSG_TRUNC))
-               xprt_complete_bc_request(req, ret);
+               xprt_complete_bc_request(req, transport->recv.copied);
 
        return ret;
 }
@@ -570,7 +567,7 @@ xs_read_stream_reply(struct sock_xprt *transport, struct msghdr *msg, int flags)
 
        spin_lock(&xprt->queue_lock);
        if (msg->msg_flags & (MSG_EOR|MSG_TRUNC))
-               xprt_complete_rqst(req->rq_task, ret);
+               xprt_complete_rqst(req->rq_task, transport->recv.copied);
        xprt_unpin_rqst(req);
 out:
        spin_unlock(&xprt->queue_lock);
@@ -591,10 +588,8 @@ xs_read_stream(struct sock_xprt *transport, int flags)
                if (ret <= 0)
                        goto out_err;
                transport->recv.offset = ret;
-               if (ret != want) {
-                       ret = -EAGAIN;
-                       goto out_err;
-               }
+               if (transport->recv.offset != want)
+                       return transport->recv.offset;
                transport->recv.len = be32_to_cpu(transport->recv.fraghdr) &
                        RPC_FRAGMENT_SIZE_MASK;
                transport->recv.offset -= sizeof(transport->recv.fraghdr);
@@ -602,6 +597,9 @@ xs_read_stream(struct sock_xprt *transport, int flags)
        }
 
        switch (be32_to_cpu(transport->recv.calldir)) {
+       default:
+               msg.msg_flags |= MSG_TRUNC;
+               break;
        case RPC_CALL:
                ret = xs_read_stream_call(transport, &msg, flags);
                break;
@@ -616,6 +614,9 @@ xs_read_stream(struct sock_xprt *transport, int flags)
                goto out_err;
        read += ret;
        if (transport->recv.offset < transport->recv.len) {
+               if (!(msg.msg_flags & MSG_TRUNC))
+                       return read;
+               msg.msg_flags = 0;
                ret = xs_read_discard(transport->sock, &msg, flags,
                                transport->recv.len - transport->recv.offset);
                if (ret <= 0)
@@ -623,7 +624,7 @@ xs_read_stream(struct sock_xprt *transport, int flags)
                transport->recv.offset += ret;
                read += ret;
                if (transport->recv.offset != transport->recv.len)
-                       return -EAGAIN;
+                       return read;
        }
        if (xs_read_stream_request_done(transport)) {
                trace_xs_stream_read_request(transport);
@@ -633,13 +634,7 @@ xs_read_stream(struct sock_xprt *transport, int flags)
        transport->recv.len = 0;
        return read;
 out_err:
-       switch (ret) {
-       case 0:
-       case -ESHUTDOWN:
-               xprt_force_disconnect(&transport->xprt);
-               return -ESHUTDOWN;
-       }
-       return ret;
+       return ret != 0 ? ret : -ESHUTDOWN;
 }
 
 static void xs_stream_data_receive(struct sock_xprt *transport)
@@ -648,12 +643,12 @@ static void xs_stream_data_receive(struct sock_xprt *transport)
        ssize_t ret = 0;
 
        mutex_lock(&transport->recv_mutex);
+       clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state);
        if (transport->sock == NULL)
                goto out;
-       clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state);
        for (;;) {
                ret = xs_read_stream(transport, MSG_DONTWAIT);
-               if (ret <= 0)
+               if (ret < 0)
                        break;
                read += ret;
                cond_resched();
@@ -1222,6 +1217,8 @@ static void xs_reset_transport(struct sock_xprt *transport)
 
        trace_rpc_socket_close(xprt, sock);
        sock_release(sock);
+
+       xprt_disconnect_done(xprt);
 }
 
 /**
@@ -1242,8 +1239,6 @@ static void xs_close(struct rpc_xprt *xprt)
 
        xs_reset_transport(transport);
        xprt->reestablish_timeout = 0;
-
-       xprt_disconnect_done(xprt);
 }
 
 static void xs_inject_disconnect(struct rpc_xprt *xprt)
@@ -1345,10 +1340,10 @@ static void xs_udp_data_receive(struct sock_xprt *transport)
        int err;
 
        mutex_lock(&transport->recv_mutex);
+       clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state);
        sk = transport->inet;
        if (sk == NULL)
                goto out;
-       clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state);
        for (;;) {
                skb = skb_recv_udp(sk, 0, 1, &err);
                if (skb == NULL)
@@ -1494,8 +1489,6 @@ static void xs_tcp_state_change(struct sock *sk)
                                        &transport->sock_state))
                        xprt_clear_connecting(xprt);
                clear_bit(XPRT_CLOSING, &xprt->state);
-               if (sk->sk_err)
-                       xprt_wake_pending_tasks(xprt, -sk->sk_err);
                /* Trigger the socket release */
                xs_tcp_force_close(xprt);
        }
@@ -2097,8 +2090,8 @@ static void xs_udp_setup_socket(struct work_struct *work)
        trace_rpc_socket_connect(xprt, sock, 0);
        status = 0;
 out:
-       xprt_unlock_connect(xprt, transport);
        xprt_clear_connecting(xprt);
+       xprt_unlock_connect(xprt, transport);
        xprt_wake_pending_tasks(xprt, status);
 }
 
@@ -2334,8 +2327,8 @@ static void xs_tcp_setup_socket(struct work_struct *work)
        }
        status = -EAGAIN;
 out:
-       xprt_unlock_connect(xprt, transport);
        xprt_clear_connecting(xprt);
+       xprt_unlock_connect(xprt, transport);
        xprt_wake_pending_tasks(xprt, status);
 }
 
index 74b9d91..5df9d11 100644 (file)
@@ -353,34 +353,35 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj)
        return 0;
 }
 
-static int __switchdev_port_obj_add(struct net_device *dev,
-                                   const struct switchdev_obj *obj,
-                                   struct switchdev_trans *trans)
+static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
+                                    struct net_device *dev,
+                                    const struct switchdev_obj *obj,
+                                    struct switchdev_trans *trans,
+                                    struct netlink_ext_ack *extack)
 {
-       const struct switchdev_ops *ops = dev->switchdev_ops;
-       struct net_device *lower_dev;
-       struct list_head *iter;
-       int err = -EOPNOTSUPP;
-
-       if (ops && ops->switchdev_port_obj_add)
-               return ops->switchdev_port_obj_add(dev, obj, trans);
+       int rc;
+       int err;
 
-       /* Switch device port(s) may be stacked under
-        * bond/team/vlan dev, so recurse down to add object on
-        * each port.
-        */
+       struct switchdev_notifier_port_obj_info obj_info = {
+               .obj = obj,
+               .trans = trans,
+               .handled = false,
+       };
 
-       netdev_for_each_lower_dev(dev, lower_dev, iter) {
-               err = __switchdev_port_obj_add(lower_dev, obj, trans);
-               if (err)
-                       break;
+       rc = call_switchdev_blocking_notifiers(nt, dev, &obj_info.info, extack);
+       err = notifier_to_errno(rc);
+       if (err) {
+               WARN_ON(!obj_info.handled);
+               return err;
        }
-
-       return err;
+       if (!obj_info.handled)
+               return -EOPNOTSUPP;
+       return 0;
 }
 
 static int switchdev_port_obj_add_now(struct net_device *dev,
-                                     const struct switchdev_obj *obj)
+                                     const struct switchdev_obj *obj,
+                                     struct netlink_ext_ack *extack)
 {
        struct switchdev_trans trans;
        int err;
@@ -397,7 +398,8 @@ static int switchdev_port_obj_add_now(struct net_device *dev,
         */
 
        trans.ph_prepare = true;
-       err = __switchdev_port_obj_add(dev, obj, &trans);
+       err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
+                                       dev, obj, &trans, extack);
        if (err) {
                /* Prepare phase failed: abort the transaction.  Any
                 * resources reserved in the prepare phase are
@@ -416,7 +418,8 @@ static int switchdev_port_obj_add_now(struct net_device *dev,
         */
 
        trans.ph_prepare = false;
-       err = __switchdev_port_obj_add(dev, obj, &trans);
+       err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
+                                       dev, obj, &trans, extack);
        WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id);
        switchdev_trans_items_warn_destroy(dev, &trans);
 
@@ -429,7 +432,7 @@ static void switchdev_port_obj_add_deferred(struct net_device *dev,
        const struct switchdev_obj *obj = data;
        int err;
 
-       err = switchdev_port_obj_add_now(dev, obj);
+       err = switchdev_port_obj_add_now(dev, obj, NULL);
        if (err && err != -EOPNOTSUPP)
                netdev_err(dev, "failed (err=%d) to add object (id=%d)\n",
                           err, obj->id);
@@ -459,38 +462,21 @@ static int switchdev_port_obj_add_defer(struct net_device *dev,
  *     in case SWITCHDEV_F_DEFER flag is not set.
  */
 int switchdev_port_obj_add(struct net_device *dev,
-                          const struct switchdev_obj *obj)
+                          const struct switchdev_obj *obj,
+                          struct netlink_ext_ack *extack)
 {
        if (obj->flags & SWITCHDEV_F_DEFER)
                return switchdev_port_obj_add_defer(dev, obj);
        ASSERT_RTNL();
-       return switchdev_port_obj_add_now(dev, obj);
+       return switchdev_port_obj_add_now(dev, obj, extack);
 }
 EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
 
 static int switchdev_port_obj_del_now(struct net_device *dev,
                                      const struct switchdev_obj *obj)
 {
-       const struct switchdev_ops *ops = dev->switchdev_ops;
-       struct net_device *lower_dev;
-       struct list_head *iter;
-       int err = -EOPNOTSUPP;
-
-       if (ops && ops->switchdev_port_obj_del)
-               return ops->switchdev_port_obj_del(dev, obj);
-
-       /* Switch device port(s) may be stacked under
-        * bond/team/vlan dev, so recurse down to delete object on
-        * each port.
-        */
-
-       netdev_for_each_lower_dev(dev, lower_dev, iter) {
-               err = switchdev_port_obj_del_now(lower_dev, obj);
-               if (err)
-                       break;
-       }
-
-       return err;
+       return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_DEL,
+                                        dev, obj, NULL, NULL);
 }
 
 static void switchdev_port_obj_del_deferred(struct net_device *dev,
@@ -535,6 +521,7 @@ int switchdev_port_obj_del(struct net_device *dev,
 EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
 
 static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
+static BLOCKING_NOTIFIER_HEAD(switchdev_blocking_notif_chain);
 
 /**
  *     register_switchdev_notifier - Register notifier
@@ -572,10 +559,38 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
                             struct switchdev_notifier_info *info)
 {
        info->dev = dev;
+       info->extack = NULL;
        return atomic_notifier_call_chain(&switchdev_notif_chain, val, info);
 }
 EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
 
+int register_switchdev_blocking_notifier(struct notifier_block *nb)
+{
+       struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
+
+       return blocking_notifier_chain_register(chain, nb);
+}
+EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier);
+
+int unregister_switchdev_blocking_notifier(struct notifier_block *nb)
+{
+       struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
+
+       return blocking_notifier_chain_unregister(chain, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
+
+int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
+                                     struct switchdev_notifier_info *info,
+                                     struct netlink_ext_ack *extack)
+{
+       info->dev = dev;
+       info->extack = extack;
+       return blocking_notifier_call_chain(&switchdev_blocking_notif_chain,
+                                           val, info);
+}
+EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
+
 bool switchdev_port_same_parent_id(struct net_device *a,
                                   struct net_device *b)
 {
@@ -595,3 +610,109 @@ bool switchdev_port_same_parent_id(struct net_device *a,
        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),
+                       int (*add_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj,
+                                     struct switchdev_trans *trans,
+                                     struct netlink_ext_ack *extack))
+{
+       struct netlink_ext_ack *extack;
+       struct net_device *lower_dev;
+       struct list_head *iter;
+       int err = -EOPNOTSUPP;
+
+       extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
+
+       if (check_cb(dev)) {
+               /* This flag is only checked if the return value is success. */
+               port_obj_info->handled = true;
+               return add_cb(dev, port_obj_info->obj, port_obj_info->trans,
+                             extack);
+       }
+
+       /* Switch ports might be stacked under e.g. a LAG. Ignore the
+        * unsupported devices, another driver might be able to handle them. But
+        * propagate to the callers any hard errors.
+        *
+        * If the driver does its own bookkeeping of stacked ports, it's not
+        * necessary to go through this helper.
+        */
+       netdev_for_each_lower_dev(dev, lower_dev, iter) {
+               err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info,
+                                                     check_cb, add_cb);
+               if (err && err != -EOPNOTSUPP)
+                       return err;
+       }
+
+       return err;
+}
+
+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),
+                       int (*add_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj,
+                                     struct switchdev_trans *trans,
+                                     struct netlink_ext_ack *extack))
+{
+       int err;
+
+       err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
+                                             add_cb);
+       if (err == -EOPNOTSUPP)
+               err = 0;
+       return err;
+}
+EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add);
+
+static int __switchdev_handle_port_obj_del(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*del_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj))
+{
+       struct net_device *lower_dev;
+       struct list_head *iter;
+       int err = -EOPNOTSUPP;
+
+       if (check_cb(dev)) {
+               /* This flag is only checked if the return value is success. */
+               port_obj_info->handled = true;
+               return del_cb(dev, port_obj_info->obj);
+       }
+
+       /* Switch ports might be stacked under e.g. a LAG. Ignore the
+        * unsupported devices, another driver might be able to handle them. But
+        * propagate to the callers any hard errors.
+        *
+        * If the driver does its own bookkeeping of stacked ports, it's not
+        * necessary to go through this helper.
+        */
+       netdev_for_each_lower_dev(dev, lower_dev, iter) {
+               err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info,
+                                                     check_cb, del_cb);
+               if (err && err != -EOPNOTSUPP)
+                       return err;
+       }
+
+       return err;
+}
+
+int switchdev_handle_port_obj_del(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*del_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj))
+{
+       int err;
+
+       err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
+                                             del_cb);
+       if (err == -EOPNOTSUPP)
+               err = 0;
+       return err;
+}
+EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);
index aca168f..c86aba0 100644 (file)
@@ -9,7 +9,9 @@ tipc-y  += addr.o bcast.o bearer.o \
           core.o link.o discover.o msg.o  \
           name_distr.o  subscr.o monitor.o name_table.o net.o  \
           netlink.o netlink_compat.o node.o socket.o eth_media.o \
-          topsrv.o socket.o group.o
+          topsrv.o socket.o group.o trace.o
+
+CFLAGS_trace.o += -I$(src)
 
 tipc-$(CONFIG_TIPC_MEDIA_UDP)  += udp_media.o
 tipc-$(CONFIG_TIPC_MEDIA_IB)   += ib_media.o
index e65c3a8..fb2c0d8 100644 (file)
@@ -43,6 +43,7 @@
 #include "bcast.h"
 #include "netlink.h"
 #include "udp_media.h"
+#include "trace.h"
 
 #define MAX_ADDR_STR 60
 
@@ -99,7 +100,7 @@ static struct tipc_media *media_find_id(u8 type)
 /**
  * tipc_media_addr_printf - record media address in print buffer
  */
-void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a)
+int tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a)
 {
        char addr_str[MAX_ADDR_STR];
        struct tipc_media *m;
@@ -114,9 +115,10 @@ void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a)
 
                ret = scnprintf(buf, len, "UNKNOWN(%u)", a->media_id);
                for (i = 0; i < sizeof(a->value); i++)
-                       ret += scnprintf(buf - ret, len + ret,
-                                           "-%02x", a->value[i]);
+                       ret += scnprintf(buf + ret, len - ret,
+                                           "-%x", a->value[i]);
        }
+       return ret;
 }
 
 /**
@@ -607,6 +609,7 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
        if (!b)
                return NOTIFY_DONE;
 
+       trace_tipc_l2_device_event(dev, b, evt);
        switch (evt) {
        case NETDEV_CHANGE:
                if (netif_carrier_ok(dev) && netif_oper_up(dev)) {
index 394290c..7f4c569 100644 (file)
@@ -207,7 +207,7 @@ int __tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info);
 
 int tipc_media_set_priority(const char *name, u32 new_value);
 int tipc_media_set_window(const char *name, u32 new_value);
-void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a);
+int tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a);
 int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b,
                         struct nlattr *attrs[]);
 void tipc_disable_l2_media(struct tipc_bearer *b);
index 2830709..c138d68 100644 (file)
@@ -166,7 +166,8 @@ static bool tipc_disc_addr_trial_msg(struct tipc_discoverer *d,
 
        /* Apply trial address if we just left trial period */
        if (!trial && !self) {
-               tipc_net_finalize(net, tn->trial_addr);
+               tipc_sched_net_finalize(net, tn->trial_addr);
+               msg_set_prevnode(buf_msg(d->skb), tn->trial_addr);
                msg_set_type(buf_msg(d->skb), DSC_REQ_MSG);
        }
 
@@ -300,14 +301,12 @@ static void tipc_disc_timeout(struct timer_list *t)
                goto exit;
        }
 
-       /* Trial period over ? */
-       if (!time_before(jiffies, tn->addr_trial_end)) {
-               /* Did we just leave it ? */
-               if (!tipc_own_addr(net))
-                       tipc_net_finalize(net, tn->trial_addr);
-
-               msg_set_type(buf_msg(d->skb), DSC_REQ_MSG);
-               msg_set_prevnode(buf_msg(d->skb), tipc_own_addr(net));
+       /* Did we just leave trial period ? */
+       if (!time_before(jiffies, tn->addr_trial_end) && !tipc_own_addr(net)) {
+               mod_timer(&d->timer, jiffies + TIPC_DISC_INIT);
+               spin_unlock_bh(&d->lock);
+               tipc_sched_net_finalize(net, tn->trial_addr);
+               return;
        }
 
        /* Adjust timeout interval according to discovery phase */
@@ -319,6 +318,8 @@ static void tipc_disc_timeout(struct timer_list *t)
                        d->timer_intv = TIPC_DISC_SLOW;
                else if (!d->num_nodes && d->timer_intv > TIPC_DISC_FAST)
                        d->timer_intv = TIPC_DISC_FAST;
+               msg_set_type(buf_msg(d->skb), DSC_REQ_MSG);
+               msg_set_prevnode(buf_msg(d->skb), tn->trial_addr);
        }
 
        mod_timer(&d->timer, jiffies + d->timer_intv);
index 9e265eb..2792a3c 100644 (file)
@@ -43,6 +43,7 @@
 #include "discover.h"
 #include "netlink.h"
 #include "monitor.h"
+#include "trace.h"
 
 #include <linux/pkt_sched.h>
 
@@ -356,9 +357,11 @@ void tipc_link_remove_bc_peer(struct tipc_link *snd_l,
        rcv_l->bc_peer_is_up = true;
        rcv_l->state = LINK_ESTABLISHED;
        tipc_link_bc_ack_rcv(rcv_l, ack, xmitq);
+       trace_tipc_link_reset(rcv_l, TIPC_DUMP_ALL, "bclink removed!");
        tipc_link_reset(rcv_l);
        rcv_l->state = LINK_RESET;
        if (!snd_l->ackers) {
+               trace_tipc_link_reset(snd_l, TIPC_DUMP_ALL, "zero ackers!");
                tipc_link_reset(snd_l);
                snd_l->state = LINK_RESET;
                __skb_queue_purge(xmitq);
@@ -522,6 +525,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer,
 
        l = *link;
        strcpy(l->name, tipc_bclink_name);
+       trace_tipc_link_reset(l, TIPC_DUMP_ALL, "bclink created!");
        tipc_link_reset(l);
        l->state = LINK_RESET;
        l->ackers = 0;
@@ -546,6 +550,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer,
 int tipc_link_fsm_evt(struct tipc_link *l, int evt)
 {
        int rc = 0;
+       int old_state = l->state;
 
        switch (l->state) {
        case LINK_RESETTING:
@@ -692,10 +697,12 @@ int tipc_link_fsm_evt(struct tipc_link *l, int evt)
        default:
                pr_err("Unknown FSM state %x in %s\n", l->state, l->name);
        }
+       trace_tipc_link_fsm(l->name, old_state, l->state, evt);
        return rc;
 illegal_evt:
        pr_err("Illegal FSM event %x in state %x on link %s\n",
               evt, l->state, l->name);
+       trace_tipc_link_fsm(l->name, old_state, l->state, evt);
        return rc;
 }
 
@@ -740,6 +747,18 @@ static void link_profile_stats(struct tipc_link *l)
                l->stats.msg_length_profile[6]++;
 }
 
+/**
+ * tipc_link_too_silent - check if link is "too silent"
+ * @l: tipc link to be checked
+ *
+ * Returns true if the link 'silent_intv_cnt' is about to reach the
+ * 'abort_limit' value, otherwise false
+ */
+bool tipc_link_too_silent(struct tipc_link *l)
+{
+       return (l->silent_intv_cnt + 2 > l->abort_limit);
+}
+
 /* tipc_link_timeout - perform periodic task as instructed from node timeout
  */
 int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq)
@@ -753,6 +772,8 @@ int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq)
        u16 bc_acked = l->bc_rcvlink->acked;
        struct tipc_mon_state *mstate = &l->mon_state;
 
+       trace_tipc_link_timeout(l, TIPC_DUMP_NONE, " ");
+       trace_tipc_link_too_silent(l, TIPC_DUMP_ALL, " ");
        switch (l->state) {
        case LINK_ESTABLISHED:
        case LINK_SYNCHING:
@@ -815,6 +836,7 @@ static int link_schedule_user(struct tipc_link *l, struct tipc_msg *hdr)
        TIPC_SKB_CB(skb)->chain_imp = msg_importance(hdr);
        skb_queue_tail(&l->wakeupq, skb);
        l->stats.link_congs++;
+       trace_tipc_link_conges(l, TIPC_DUMP_ALL, "wakeup scheduled!");
        return -ELINKCONG;
 }
 
@@ -945,6 +967,10 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
                        }
                        __skb_dequeue(list);
                        __skb_queue_tail(transmq, skb);
+                       /* next retransmit attempt */
+                       if (link_is_bc_sndlink(l))
+                               TIPC_SKB_CB(skb)->nxt_retr =
+                                       jiffies + TIPC_BC_RETR_LIM;
                        __skb_queue_tail(xmitq, _skb);
                        TIPC_SKB_CB(skb)->ackers = l->ackers;
                        l->rcv_unacked = 0;
@@ -992,6 +1018,10 @@ static void tipc_link_advance_backlog(struct tipc_link *l,
                hdr = buf_msg(skb);
                l->backlog[msg_importance(hdr)].len--;
                __skb_queue_tail(&l->transmq, skb);
+               /* next retransmit attempt */
+               if (link_is_bc_sndlink(l))
+                       TIPC_SKB_CB(skb)->nxt_retr = jiffies + TIPC_BC_RETR_LIM;
+
                __skb_queue_tail(xmitq, _skb);
                TIPC_SKB_CB(skb)->ackers = l->ackers;
                msg_set_seqno(hdr, seqno);
@@ -1036,6 +1066,7 @@ static int tipc_link_retrans(struct tipc_link *l, struct tipc_link *r,
        if (less(to, from))
                return 0;
 
+       trace_tipc_link_retrans(r, from, to, &l->transmq);
        /* Detect repeated retransmit failures on same packet */
        if (r->prev_from != from) {
                r->prev_from = from;
@@ -1043,6 +1074,9 @@ static int tipc_link_retrans(struct tipc_link *l, struct tipc_link *r,
                r->stale_cnt = 0;
        } else if (++r->stale_cnt > 99 && time_after(jiffies, r->stale_limit)) {
                link_retransmit_failure(l, skb);
+               trace_tipc_list_dump(&l->transmq, true, "retrans failure!");
+               trace_tipc_link_dump(l, TIPC_DUMP_NONE, "retrans failure!");
+               trace_tipc_link_dump(r, TIPC_DUMP_NONE, "retrans failure!");
                if (link_is_bc_sndlink(l))
                        return TIPC_LINK_DOWN_EVT;
                return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
@@ -1402,6 +1436,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
                l->stats.sent_nacks++;
        skb->priority = TC_PRIO_CONTROL;
        __skb_queue_tail(xmitq, skb);
+       trace_tipc_proto_build(skb, false, l->name);
 }
 
 void tipc_link_create_dummy_tnl_msg(struct tipc_link *l,
@@ -1565,6 +1600,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
        char *if_name;
        int rc = 0;
 
+       trace_tipc_proto_rcv(skb, false, l->name);
        if (tipc_link_is_blocked(l) || !xmitq)
                goto exit;
 
@@ -1575,8 +1611,11 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
        hdr = buf_msg(skb);
        data = msg_data(hdr);
 
-       if (!tipc_link_validate_msg(l, hdr))
+       if (!tipc_link_validate_msg(l, hdr)) {
+               trace_tipc_skb_dump(skb, false, "PROTO invalid (1)!");
+               trace_tipc_link_dump(l, TIPC_DUMP_NONE, "PROTO invalid (1)!");
                goto exit;
+       }
 
        switch (mtyp) {
        case RESET_MSG:
@@ -1819,6 +1858,7 @@ void tipc_link_bc_ack_rcv(struct tipc_link *l, u16 acked,
        if (!more(acked, l->acked))
                return;
 
+       trace_tipc_link_bc_ack(l, l->acked, acked, &snd_l->transmq);
        /* Skip over packets peer has already acked */
        skb_queue_walk(&snd_l->transmq, skb) {
                if (more(buf_seqno(skb), l->acked))
@@ -2222,3 +2262,122 @@ void tipc_link_set_abort_limit(struct tipc_link *l, u32 limit)
 {
        l->abort_limit = limit;
 }
+
+char *tipc_link_name_ext(struct tipc_link *l, char *buf)
+{
+       if (!l)
+               scnprintf(buf, TIPC_MAX_LINK_NAME, "null");
+       else if (link_is_bc_sndlink(l))
+               scnprintf(buf, TIPC_MAX_LINK_NAME, "broadcast-sender");
+       else if (link_is_bc_rcvlink(l))
+               scnprintf(buf, TIPC_MAX_LINK_NAME,
+                         "broadcast-receiver, peer %x", l->addr);
+       else
+               memcpy(buf, l->name, TIPC_MAX_LINK_NAME);
+
+       return buf;
+}
+
+/**
+ * tipc_link_dump - dump TIPC link data
+ * @l: tipc link to be dumped
+ * @dqueues: bitmask to decide if any link queue to be dumped?
+ *           - TIPC_DUMP_NONE: don't dump link queues
+ *           - TIPC_DUMP_TRANSMQ: dump link transmq queue
+ *           - TIPC_DUMP_BACKLOGQ: dump link backlog queue
+ *           - TIPC_DUMP_DEFERDQ: dump link deferd queue
+ *           - TIPC_DUMP_INPUTQ: dump link input queue
+ *           - TIPC_DUMP_WAKEUP: dump link wakeup queue
+ *           - TIPC_DUMP_ALL: dump all the link queues above
+ * @buf: returned buffer of dump data in format
+ */
+int tipc_link_dump(struct tipc_link *l, u16 dqueues, char *buf)
+{
+       int i = 0;
+       size_t sz = (dqueues) ? LINK_LMAX : LINK_LMIN;
+       struct sk_buff_head *list;
+       struct sk_buff *hskb, *tskb;
+       u32 len;
+
+       if (!l) {
+               i += scnprintf(buf, sz, "link data: (null)\n");
+               return i;
+       }
+
+       i += scnprintf(buf, sz, "link data: %x", l->addr);
+       i += scnprintf(buf + i, sz - i, " %x", l->state);
+       i += scnprintf(buf + i, sz - i, " %u", l->in_session);
+       i += scnprintf(buf + i, sz - i, " %u", l->session);
+       i += scnprintf(buf + i, sz - i, " %u", l->peer_session);
+       i += scnprintf(buf + i, sz - i, " %u", l->snd_nxt);
+       i += scnprintf(buf + i, sz - i, " %u", l->rcv_nxt);
+       i += scnprintf(buf + i, sz - i, " %u", l->snd_nxt_state);
+       i += scnprintf(buf + i, sz - i, " %u", l->rcv_nxt_state);
+       i += scnprintf(buf + i, sz - i, " %x", l->peer_caps);
+       i += scnprintf(buf + i, sz - i, " %u", l->silent_intv_cnt);
+       i += scnprintf(buf + i, sz - i, " %u", l->rst_cnt);
+       i += scnprintf(buf + i, sz - i, " %u", l->prev_from);
+       i += scnprintf(buf + i, sz - i, " %u", l->stale_cnt);
+       i += scnprintf(buf + i, sz - i, " %u", l->acked);
+
+       list = &l->transmq;
+       len = skb_queue_len(list);
+       hskb = skb_peek(list);
+       tskb = skb_peek_tail(list);
+       i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
+                      (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
+                      (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
+
+       list = &l->deferdq;
+       len = skb_queue_len(list);
+       hskb = skb_peek(list);
+       tskb = skb_peek_tail(list);
+       i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
+                      (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
+                      (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
+
+       list = &l->backlogq;
+       len = skb_queue_len(list);
+       hskb = skb_peek(list);
+       tskb = skb_peek_tail(list);
+       i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
+                      (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
+                      (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
+
+       list = l->inputq;
+       len = skb_queue_len(list);
+       hskb = skb_peek(list);
+       tskb = skb_peek_tail(list);
+       i += scnprintf(buf + i, sz - i, " | %u %u %u\n", len,
+                      (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
+                      (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
+
+       if (dqueues & TIPC_DUMP_TRANSMQ) {
+               i += scnprintf(buf + i, sz - i, "transmq: ");
+               i += tipc_list_dump(&l->transmq, false, buf + i);
+       }
+       if (dqueues & TIPC_DUMP_BACKLOGQ) {
+               i += scnprintf(buf + i, sz - i,
+                              "backlogq: <%u %u %u %u %u>, ",
+                              l->backlog[TIPC_LOW_IMPORTANCE].len,
+                              l->backlog[TIPC_MEDIUM_IMPORTANCE].len,
+                              l->backlog[TIPC_HIGH_IMPORTANCE].len,
+                              l->backlog[TIPC_CRITICAL_IMPORTANCE].len,
+                              l->backlog[TIPC_SYSTEM_IMPORTANCE].len);
+               i += tipc_list_dump(&l->backlogq, false, buf + i);
+       }
+       if (dqueues & TIPC_DUMP_DEFERDQ) {
+               i += scnprintf(buf + i, sz - i, "deferdq: ");
+               i += tipc_list_dump(&l->deferdq, false, buf + i);
+       }
+       if (dqueues & TIPC_DUMP_INPUTQ) {
+               i += scnprintf(buf + i, sz - i, "inputq: ");
+               i += tipc_list_dump(l->inputq, false, buf + i);
+       }
+       if (dqueues & TIPC_DUMP_WAKEUP) {
+               i += scnprintf(buf + i, sz - i, "wakeup: ");
+               i += tipc_list_dump(&l->wakeupq, false, buf + i);
+       }
+
+       return i;
+}
index 90488c5..8439e0e 100644 (file)
@@ -109,6 +109,7 @@ u16 tipc_link_rcv_nxt(struct tipc_link *l);
 u16 tipc_link_acked(struct tipc_link *l);
 u32 tipc_link_id(struct tipc_link *l);
 char *tipc_link_name(struct tipc_link *l);
+char *tipc_link_name_ext(struct tipc_link *l, char *buf);
 u32 tipc_link_state(struct tipc_link *l);
 char tipc_link_plane(struct tipc_link *l);
 int tipc_link_prio(struct tipc_link *l);
@@ -147,4 +148,5 @@ int tipc_link_bc_sync_rcv(struct tipc_link *l,   struct tipc_msg *hdr,
                          struct sk_buff_head *xmitq);
 int tipc_link_bc_nack_rcv(struct tipc_link *l, struct sk_buff *skb,
                          struct sk_buff_head *xmitq);
+bool tipc_link_too_silent(struct tipc_link *l);
 #endif
index 62199cf..f076edb 100644 (file)
  *     - A local spin_lock protecting the queue of subscriber events.
 */
 
+struct tipc_net_work {
+       struct work_struct work;
+       struct net *net;
+       u32 addr;
+};
+
+static void tipc_net_finalize(struct net *net, u32 addr);
+
 int tipc_net_init(struct net *net, u8 *node_id, u32 addr)
 {
        if (tipc_own_id(net)) {
@@ -119,17 +127,38 @@ int tipc_net_init(struct net *net, u8 *node_id, u32 addr)
        return 0;
 }
 
-void tipc_net_finalize(struct net *net, u32 addr)
+static void tipc_net_finalize(struct net *net, u32 addr)
 {
        struct tipc_net *tn = tipc_net(net);
 
-       if (!cmpxchg(&tn->node_addr, 0, addr)) {
-               tipc_set_node_addr(net, addr);
-               tipc_named_reinit(net);
-               tipc_sk_reinit(net);
-               tipc_nametbl_publish(net, TIPC_CFG_SRV, addr, addr,
-                                    TIPC_CLUSTER_SCOPE, 0, addr);
-       }
+       if (cmpxchg(&tn->node_addr, 0, addr))
+               return;
+       tipc_set_node_addr(net, addr);
+       tipc_named_reinit(net);
+       tipc_sk_reinit(net);
+       tipc_nametbl_publish(net, TIPC_CFG_SRV, addr, addr,
+                            TIPC_CLUSTER_SCOPE, 0, addr);
+}
+
+static void tipc_net_finalize_work(struct work_struct *work)
+{
+       struct tipc_net_work *fwork;
+
+       fwork = container_of(work, struct tipc_net_work, work);
+       tipc_net_finalize(fwork->net, fwork->addr);
+       kfree(fwork);
+}
+
+void tipc_sched_net_finalize(struct net *net, u32 addr)
+{
+       struct tipc_net_work *fwork = kzalloc(sizeof(*fwork), GFP_ATOMIC);
+
+       if (!fwork)
+               return;
+       INIT_WORK(&fwork->work, tipc_net_finalize_work);
+       fwork->net = net;
+       fwork->addr = addr;
+       schedule_work(&fwork->work);
 }
 
 void tipc_net_stop(struct net *net)
index 09ad02b..b7f2e36 100644 (file)
@@ -42,7 +42,7 @@
 extern const struct nla_policy tipc_nl_net_policy[];
 
 int tipc_net_init(struct net *net, u8 *node_id, u32 addr);
-void tipc_net_finalize(struct net *net, u32 addr);
+void tipc_sched_net_finalize(struct net *net, u32 addr);
 void tipc_net_stop(struct net *net);
 int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb);
 int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info);
index 2afc4f8..db2a6c3 100644 (file)
@@ -43,6 +43,7 @@
 #include "monitor.h"
 #include "discover.h"
 #include "netlink.h"
+#include "trace.h"
 
 #define INVALID_NODE_SIG       0x10000
 #define NODE_CLEANUP_AFTER     300000
@@ -432,6 +433,7 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,
                        break;
        }
        list_add_tail_rcu(&n->list, &temp_node->list);
+       trace_tipc_node_create(n, true, " ");
 exit:
        spin_unlock_bh(&tn->node_list_lock);
        return n;
@@ -459,6 +461,7 @@ static void tipc_node_delete_from_list(struct tipc_node *node)
 
 static void tipc_node_delete(struct tipc_node *node)
 {
+       trace_tipc_node_delete(node, true, " ");
        tipc_node_delete_from_list(node);
 
        del_timer_sync(&node->timer);
@@ -584,12 +587,15 @@ static void  tipc_node_clear_links(struct tipc_node *node)
 /* tipc_node_cleanup - delete nodes that does not
  * have active links for NODE_CLEANUP_AFTER time
  */
-static int tipc_node_cleanup(struct tipc_node *peer)
+static bool tipc_node_cleanup(struct tipc_node *peer)
 {
        struct tipc_net *tn = tipc_net(peer->net);
        bool deleted = false;
 
-       spin_lock_bh(&tn->node_list_lock);
+       /* If lock held by tipc_node_stop() the node will be deleted anyway */
+       if (!spin_trylock_bh(&tn->node_list_lock))
+               return false;
+
        tipc_node_write_lock(peer);
 
        if (!node_is_up(peer) && time_after(jiffies, peer->delete_at)) {
@@ -613,6 +619,7 @@ static void tipc_node_timeout(struct timer_list *t)
        int bearer_id;
        int rc = 0;
 
+       trace_tipc_node_timeout(n, false, " ");
        if (!node_is_up(n) && tipc_node_cleanup(n)) {
                /*Removing the reference of Timer*/
                tipc_node_put(n);
@@ -621,6 +628,12 @@ static void tipc_node_timeout(struct timer_list *t)
 
        __skb_queue_head_init(&xmitq);
 
+       /* Initial node interval to value larger (10 seconds), then it will be
+        * recalculated with link lowest tolerance
+        */
+       tipc_node_read_lock(n);
+       n->keepalive_intv = 10000;
+       tipc_node_read_unlock(n);
        for (bearer_id = 0; remains && (bearer_id < MAX_BEARERS); bearer_id++) {
                tipc_node_read_lock(n);
                le = &n->links[bearer_id];
@@ -672,6 +685,7 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
 
        pr_debug("Established link <%s> on network plane %c\n",
                 tipc_link_name(nl), tipc_link_plane(nl));
+       trace_tipc_node_link_up(n, true, " ");
 
        /* Ensure that a STATE message goes first */
        tipc_link_build_state_msg(nl, xmitq);
@@ -774,6 +788,7 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
                if (tipc_link_peer_is_down(l))
                        tipc_node_fsm_evt(n, PEER_LOST_CONTACT_EVT);
                tipc_node_fsm_evt(n, SELF_LOST_CONTACT_EVT);
+               trace_tipc_link_reset(l, TIPC_DUMP_ALL, "link down!");
                tipc_link_fsm_evt(l, LINK_RESET_EVT);
                tipc_link_reset(l);
                tipc_link_build_reset_msg(l, xmitq);
@@ -791,6 +806,7 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
        tipc_node_fsm_evt(n, NODE_SYNCH_END_EVT);
        n->sync_point = tipc_link_rcv_nxt(tnl) + (U16_MAX / 2 - 1);
        tipc_link_tnl_prepare(l, tnl, FAILOVER_MSG, xmitq);
+       trace_tipc_link_reset(l, TIPC_DUMP_ALL, "link down -> failover!");
        tipc_link_reset(l);
        tipc_link_fsm_evt(l, LINK_RESET_EVT);
        tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT);
@@ -823,6 +839,7 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
                /* Defuse pending tipc_node_link_up() */
                tipc_link_fsm_evt(l, LINK_RESET_EVT);
        }
+       trace_tipc_node_link_down(n, true, "node link down or deleted!");
        tipc_node_write_unlock(n);
        if (delete)
                tipc_mon_remove_peer(n->net, n->addr, old_bearer_id);
@@ -1012,6 +1029,7 @@ void tipc_node_check_dest(struct net *net, u32 addr,
                        *respond = false;
                        goto exit;
                }
+               trace_tipc_link_reset(l, TIPC_DUMP_ALL, "link created!");
                tipc_link_reset(l);
                tipc_link_fsm_evt(l, LINK_RESET_EVT);
                if (n->state == NODE_FAILINGOVER)
@@ -1051,6 +1069,7 @@ static void tipc_node_reset_links(struct tipc_node *n)
 
        pr_warn("Resetting all links to %x\n", n->addr);
 
+       trace_tipc_node_reset_links(n, true, " ");
        for (i = 0; i < MAX_BEARERS; i++) {
                tipc_node_link_down(n, i, false);
        }
@@ -1226,11 +1245,13 @@ static void tipc_node_fsm_evt(struct tipc_node *n, int evt)
                pr_err("Unknown node fsm state %x\n", state);
                break;
        }
+       trace_tipc_node_fsm(n->peer_id, n->state, state, evt);
        n->state = state;
        return;
 
 illegal_evt:
        pr_err("Illegal node fsm evt %x in state %x\n", evt, state);
+       trace_tipc_node_fsm(n->peer_id, n->state, state, evt);
 }
 
 static void node_lost_contact(struct tipc_node *n,
@@ -1244,6 +1265,7 @@ static void node_lost_contact(struct tipc_node *n,
 
        pr_debug("Lost contact with %x\n", n->addr);
        n->delete_at = jiffies + msecs_to_jiffies(NODE_CLEANUP_AFTER);
+       trace_tipc_node_lost_contact(n, true, " ");
 
        /* Clean up broadcast state */
        tipc_bcast_remove_peer(n->net, n->bc_entry.link);
@@ -1540,6 +1562,10 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id
        if (!skb_queue_empty(&be->inputq1))
                tipc_node_mcast_rcv(n);
 
+       /* Handle NAME_DISTRIBUTOR messages sent from 1.7 nodes */
+       if (!skb_queue_empty(&n->bc_entry.namedq))
+               tipc_named_rcv(net, &n->bc_entry.namedq);
+
        /* If reassembly or retransmission failure => reset all links to peer */
        if (rc & TIPC_LINK_DOWN_EVT)
                tipc_node_reset_links(n);
@@ -1568,6 +1594,10 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
        struct tipc_media_addr *maddr;
        int pb_id;
 
+       if (trace_tipc_node_check_state_enabled()) {
+               trace_tipc_skb_dump(skb, false, "skb for node state check");
+               trace_tipc_node_check_state(n, true, " ");
+       }
        l = n->links[bearer_id].link;
        if (!l)
                return false;
@@ -1585,8 +1615,11 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
                }
        }
 
-       if (!tipc_link_validate_msg(l, hdr))
+       if (!tipc_link_validate_msg(l, hdr)) {
+               trace_tipc_skb_dump(skb, false, "PROTO invalid (2)!");
+               trace_tipc_link_dump(l, TIPC_DUMP_NONE, "PROTO invalid (2)!");
                return false;
+       }
 
        /* Check and update node accesibility if applicable */
        if (state == SELF_UP_PEER_COMING) {
@@ -1616,6 +1649,8 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
                syncpt = oseqno + exp_pkts - 1;
                if (pl && tipc_link_is_up(pl)) {
                        __tipc_node_link_down(n, &pb_id, xmitq, &maddr);
+                       trace_tipc_node_link_down(n, true,
+                                                 "node link down <- failover!");
                        tipc_skb_queue_splice_tail_init(tipc_link_inputq(pl),
                                                        tipc_link_inputq(l));
                }
@@ -2422,3 +2457,65 @@ int tipc_nl_node_dump_monitor_peer(struct sk_buff *skb,
 
        return skb->len;
 }
+
+u32 tipc_node_get_addr(struct tipc_node *node)
+{
+       return (node) ? node->addr : 0;
+}
+
+/**
+ * tipc_node_dump - dump TIPC node data
+ * @n: tipc node to be dumped
+ * @more: dump more?
+ *        - false: dump only tipc node data
+ *        - true: dump node link data as well
+ * @buf: returned buffer of dump data in format
+ */
+int tipc_node_dump(struct tipc_node *n, bool more, char *buf)
+{
+       int i = 0;
+       size_t sz = (more) ? NODE_LMAX : NODE_LMIN;
+
+       if (!n) {
+               i += scnprintf(buf, sz, "node data: (null)\n");
+               return i;
+       }
+
+       i += scnprintf(buf, sz, "node data: %x", n->addr);
+       i += scnprintf(buf + i, sz - i, " %x", n->state);
+       i += scnprintf(buf + i, sz - i, " %d", n->active_links[0]);
+       i += scnprintf(buf + i, sz - i, " %d", n->active_links[1]);
+       i += scnprintf(buf + i, sz - i, " %x", n->action_flags);
+       i += scnprintf(buf + i, sz - i, " %u", n->failover_sent);
+       i += scnprintf(buf + i, sz - i, " %u", n->sync_point);
+       i += scnprintf(buf + i, sz - i, " %d", n->link_cnt);
+       i += scnprintf(buf + i, sz - i, " %u", n->working_links);
+       i += scnprintf(buf + i, sz - i, " %x", n->capabilities);
+       i += scnprintf(buf + i, sz - i, " %lu\n", n->keepalive_intv);
+
+       if (!more)
+               return i;
+
+       i += scnprintf(buf + i, sz - i, "link_entry[0]:\n");
+       i += scnprintf(buf + i, sz - i, " mtu: %u\n", n->links[0].mtu);
+       i += scnprintf(buf + i, sz - i, " media: ");
+       i += tipc_media_addr_printf(buf + i, sz - i, &n->links[0].maddr);
+       i += scnprintf(buf + i, sz - i, "\n");
+       i += tipc_link_dump(n->links[0].link, TIPC_DUMP_NONE, buf + i);
+       i += scnprintf(buf + i, sz - i, " inputq: ");
+       i += tipc_list_dump(&n->links[0].inputq, false, buf + i);
+
+       i += scnprintf(buf + i, sz - i, "link_entry[1]:\n");
+       i += scnprintf(buf + i, sz - i, " mtu: %u\n", n->links[1].mtu);
+       i += scnprintf(buf + i, sz - i, " media: ");
+       i += tipc_media_addr_printf(buf + i, sz - i, &n->links[1].maddr);
+       i += scnprintf(buf + i, sz - i, "\n");
+       i += tipc_link_dump(n->links[1].link, TIPC_DUMP_NONE, buf + i);
+       i += scnprintf(buf + i, sz - i, " inputq: ");
+       i += tipc_list_dump(&n->links[1].inputq, false, buf + i);
+
+       i += scnprintf(buf + i, sz - i, "bclink:\n ");
+       i += tipc_link_dump(n->bc_entry.link, TIPC_DUMP_NONE, buf + i);
+
+       return i;
+}
index 03f5efb..4f59a30 100644 (file)
@@ -65,6 +65,7 @@ enum {
 
 void tipc_node_stop(struct net *net);
 bool tipc_node_get_id(struct net *net, u32 addr, u8 *id);
+u32 tipc_node_get_addr(struct tipc_node *node);
 u32 tipc_node_try_addr(struct net *net, u8 *id, u32 addr);
 void tipc_node_check_dest(struct net *net, u32 onode, u8 *peer_id128,
                          struct tipc_bearer *bearer,
index 636e613..1217c90 100644 (file)
@@ -46,6 +46,7 @@
 #include "bcast.h"
 #include "netlink.h"
 #include "group.h"
+#include "trace.h"
 
 #define CONN_TIMEOUT_DEFAULT    8000    /* default connect timeout = 8s */
 #define CONN_PROBING_INTV      msecs_to_jiffies(3600000)  /* [ms] => 1 h */
@@ -233,6 +234,7 @@ static u16 tsk_inc(struct tipc_sock *tsk, int msglen)
  */
 static void tsk_advance_rx_queue(struct sock *sk)
 {
+       trace_tipc_sk_advance_rx(sk, NULL, TIPC_DUMP_SK_RCVQ, " ");
        kfree_skb(__skb_dequeue(&sk->sk_receive_queue));
 }
 
@@ -247,6 +249,7 @@ static void tipc_sk_respond(struct sock *sk, struct sk_buff *skb, int err)
        if (!tipc_msg_reverse(onode, &skb, err))
                return;
 
+       trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_NONE, "@sk_respond!");
        dnode = msg_destnode(buf_msg(skb));
        selector = msg_origport(buf_msg(skb));
        tipc_node_xmit_skb(sock_net(sk), skb, dnode, selector);
@@ -482,6 +485,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
                        tsk_set_unreliable(tsk, true);
        }
 
+       trace_tipc_sk_create(sk, NULL, TIPC_DUMP_NONE, " ");
        return 0;
 }
 
@@ -571,6 +575,7 @@ static int tipc_release(struct socket *sock)
        tsk = tipc_sk(sk);
        lock_sock(sk);
 
+       trace_tipc_sk_release(sk, NULL, TIPC_DUMP_ALL, " ");
        __tipc_shutdown(sock, TIPC_ERR_NO_PORT);
        sk->sk_shutdown = SHUTDOWN_MASK;
        tipc_sk_leave(tsk);
@@ -718,6 +723,7 @@ static __poll_t tipc_poll(struct file *file, struct socket *sock,
        __poll_t revents = 0;
 
        sock_poll_wait(file, sock, wait);
+       trace_tipc_sk_poll(sk, NULL, TIPC_DUMP_ALL, " ");
 
        if (sk->sk_shutdown & RCV_SHUTDOWN)
                revents |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM;
@@ -804,9 +810,12 @@ static int tipc_sendmcast(struct  socket *sock, struct tipc_name_seq *seq,
        rc = tipc_msg_build(hdr, msg, 0, dlen, mtu, &pkts);
 
        /* Send message if build was successful */
-       if (unlikely(rc == dlen))
+       if (unlikely(rc == dlen)) {
+               trace_tipc_sk_sendmcast(sk, skb_peek(&pkts),
+                                       TIPC_DUMP_SK_SNDQ, " ");
                rc = tipc_mcast_xmit(net, &pkts, method, &dsts,
                                     &tsk->cong_link_cnt);
+       }
 
        tipc_nlist_purge(&dsts);
 
@@ -880,7 +889,6 @@ static int tipc_send_group_unicast(struct socket *sock, struct msghdr *m,
        DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);
        int blks = tsk_blocks(GROUP_H_SIZE + dlen);
        struct tipc_sock *tsk = tipc_sk(sk);
-       struct tipc_group *grp = tsk->group;
        struct net *net = sock_net(sk);
        struct tipc_member *mb = NULL;
        u32 node, port;
@@ -894,7 +902,9 @@ static int tipc_send_group_unicast(struct socket *sock, struct msghdr *m,
        /* Block or return if destination link or member is congested */
        rc = tipc_wait_for_cond(sock, &timeout,
                                !tipc_dest_find(&tsk->cong_links, node, 0) &&
-                               !tipc_group_cong(grp, node, port, blks, &mb));
+                               tsk->group &&
+                               !tipc_group_cong(tsk->group, node, port, blks,
+                                                &mb));
        if (unlikely(rc))
                return rc;
 
@@ -924,7 +934,6 @@ static int tipc_send_group_anycast(struct socket *sock, struct msghdr *m,
        struct tipc_sock *tsk = tipc_sk(sk);
        struct list_head *cong_links = &tsk->cong_links;
        int blks = tsk_blocks(GROUP_H_SIZE + dlen);
-       struct tipc_group *grp = tsk->group;
        struct tipc_msg *hdr = &tsk->phdr;
        struct tipc_member *first = NULL;
        struct tipc_member *mbr = NULL;
@@ -941,9 +950,10 @@ static int tipc_send_group_anycast(struct socket *sock, struct msghdr *m,
        type = msg_nametype(hdr);
        inst = dest->addr.name.name.instance;
        scope = msg_lookup_scope(hdr);
-       exclude = tipc_group_exclude(grp);
 
        while (++lookups < 4) {
+               exclude = tipc_group_exclude(tsk->group);
+
                first = NULL;
 
                /* Look for a non-congested destination member, if any */
@@ -952,7 +962,8 @@ static int tipc_send_group_anycast(struct socket *sock, struct msghdr *m,
                                                 &dstcnt, exclude, false))
                                return -EHOSTUNREACH;
                        tipc_dest_pop(&dsts, &node, &port);
-                       cong = tipc_group_cong(grp, node, port, blks, &mbr);
+                       cong = tipc_group_cong(tsk->group, node, port, blks,
+                                              &mbr);
                        if (!cong)
                                break;
                        if (mbr == first)
@@ -971,7 +982,8 @@ static int tipc_send_group_anycast(struct socket *sock, struct msghdr *m,
                /* Block or return if destination link or member is congested */
                rc = tipc_wait_for_cond(sock, &timeout,
                                        !tipc_dest_find(cong_links, node, 0) &&
-                                       !tipc_group_cong(grp, node, port,
+                                       tsk->group &&
+                                       !tipc_group_cong(tsk->group, node, port,
                                                         blks, &mbr));
                if (unlikely(rc))
                        return rc;
@@ -1006,8 +1018,7 @@ static int tipc_send_group_bcast(struct socket *sock, struct msghdr *m,
        struct sock *sk = sock->sk;
        struct net *net = sock_net(sk);
        struct tipc_sock *tsk = tipc_sk(sk);
-       struct tipc_group *grp = tsk->group;
-       struct tipc_nlist *dsts = tipc_group_dests(grp);
+       struct tipc_nlist *dsts;
        struct tipc_mc_method *method = &tsk->mc_method;
        bool ack = method->mandatory && method->rcast;
        int blks = tsk_blocks(MCAST_H_SIZE + dlen);
@@ -1016,15 +1027,17 @@ static int tipc_send_group_bcast(struct socket *sock, struct msghdr *m,
        struct sk_buff_head pkts;
        int rc = -EHOSTUNREACH;
 
-       if (!dsts->local && !dsts->remote)
-               return -EHOSTUNREACH;
-
        /* Block or return if any destination link or member is congested */
-       rc = tipc_wait_for_cond(sock, &timeout, !tsk->cong_link_cnt &&
-                               !tipc_group_bc_cong(grp, blks));
+       rc = tipc_wait_for_cond(sock, &timeout,
+                               !tsk->cong_link_cnt && tsk->group &&
+                               !tipc_group_bc_cong(tsk->group, blks));
        if (unlikely(rc))
                return rc;
 
+       dsts = tipc_group_dests(tsk->group);
+       if (!dsts->local && !dsts->remote)
+               return -EHOSTUNREACH;
+
        /* Complete message header */
        if (dest) {
                msg_set_type(hdr, TIPC_GRP_MCAST_MSG);
@@ -1036,7 +1049,7 @@ static int tipc_send_group_bcast(struct socket *sock, struct msghdr *m,
        msg_set_hdr_sz(hdr, GROUP_H_SIZE);
        msg_set_destport(hdr, 0);
        msg_set_destnode(hdr, 0);
-       msg_set_grp_bc_seqno(hdr, tipc_group_bc_snd_nxt(grp));
+       msg_set_grp_bc_seqno(hdr, tipc_group_bc_snd_nxt(tsk->group));
 
        /* Avoid getting stuck with repeated forced replicasts */
        msg_set_grp_bc_ack_req(hdr, ack);
@@ -1208,8 +1221,10 @@ static void tipc_sk_conn_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb,
        bool conn_cong;
 
        /* Ignore if connection cannot be validated: */
-       if (!tsk_peer_msg(tsk, hdr))
+       if (!tsk_peer_msg(tsk, hdr)) {
+               trace_tipc_sk_drop_msg(sk, skb, TIPC_DUMP_NONE, "@proto_rcv!");
                goto exit;
+       }
 
        if (unlikely(msg_errcode(hdr))) {
                tipc_set_sk_state(sk, TIPC_DISCONNECTING);
@@ -1377,6 +1392,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen)
        if (unlikely(syn && !tipc_msg_skb_clone(&pkts, &sk->sk_write_queue)))
                return -ENOMEM;
 
+       trace_tipc_sk_sendmsg(sk, skb_peek(&pkts), TIPC_DUMP_SK_SNDQ, " ");
        rc = tipc_node_xmit(net, &pkts, dnode, tsk->portid);
        if (unlikely(rc == -ELINKCONG)) {
                tipc_dest_push(clinks, dnode, 0);
@@ -1454,6 +1470,8 @@ static int __tipc_sendstream(struct socket *sock, struct msghdr *m, size_t dlen)
                if (unlikely(rc != send))
                        break;
 
+               trace_tipc_sk_sendstream(sk, skb_peek(&pkts),
+                                        TIPC_DUMP_SK_SNDQ, " ");
                rc = tipc_node_xmit(net, &pkts, dnode, tsk->portid);
                if (unlikely(rc == -ELINKCONG)) {
                        tsk->cong_link_cnt = 1;
@@ -1555,16 +1573,17 @@ static void tipc_sk_set_orig_addr(struct msghdr *m, struct sk_buff *skb)
 /**
  * tipc_sk_anc_data_recv - optionally capture ancillary data for received message
  * @m: descriptor for message info
- * @msg: received message header
+ * @skb: received message buffer
  * @tsk: TIPC port associated with message
  *
  * Note: Ancillary data is not captured if not requested by receiver.
  *
  * Returns 0 if successful, otherwise errno
  */
-static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
+static int tipc_sk_anc_data_recv(struct msghdr *m, struct sk_buff *skb,
                                 struct tipc_sock *tsk)
 {
+       struct tipc_msg *msg;
        u32 anc_data[3];
        u32 err;
        u32 dest_type;
@@ -1573,6 +1592,7 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
 
        if (likely(m->msg_controllen == 0))
                return 0;
+       msg = buf_msg(skb);
 
        /* Optionally capture errored message object(s) */
        err = msg ? msg_errcode(msg) : 0;
@@ -1583,6 +1603,9 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
                if (res)
                        return res;
                if (anc_data[1]) {
+                       if (skb_linearize(skb))
+                               return -ENOMEM;
+                       msg = buf_msg(skb);
                        res = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, anc_data[1],
                                       msg_data(msg));
                        if (res)
@@ -1744,9 +1767,10 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m,
 
        /* Collect msg meta data, including error code and rejected data */
        tipc_sk_set_orig_addr(m, skb);
-       rc = tipc_sk_anc_data_recv(m, hdr, tsk);
+       rc = tipc_sk_anc_data_recv(m, skb, tsk);
        if (unlikely(rc))
                goto exit;
+       hdr = buf_msg(skb);
 
        /* Capture data if non-error msg, otherwise just set return value */
        if (likely(!err)) {
@@ -1856,9 +1880,10 @@ static int tipc_recvstream(struct socket *sock, struct msghdr *m,
                /* Collect msg meta data, incl. error code and rejected data */
                if (!copied) {
                        tipc_sk_set_orig_addr(m, skb);
-                       rc = tipc_sk_anc_data_recv(m, hdr, tsk);
+                       rc = tipc_sk_anc_data_recv(m, skb, tsk);
                        if (rc)
                                break;
+                       hdr = buf_msg(skb);
                }
 
                /* Copy data if msg ok, otherwise return error/partial data */
@@ -2121,6 +2146,7 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb,
        struct sk_buff_head inputq;
        int limit, err = TIPC_OK;
 
+       trace_tipc_sk_filter_rcv(sk, skb, TIPC_DUMP_ALL, " ");
        TIPC_SKB_CB(skb)->bytes_read = 0;
        __skb_queue_head_init(&inputq);
        __skb_queue_tail(&inputq, skb);
@@ -2140,17 +2166,25 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb,
                    (!grp && msg_in_group(hdr)))
                        err = TIPC_ERR_NO_PORT;
                else if (sk_rmem_alloc_get(sk) + skb->truesize >= limit) {
+                       trace_tipc_sk_dump(sk, skb, TIPC_DUMP_ALL,
+                                          "err_overload2!");
                        atomic_inc(&sk->sk_drops);
                        err = TIPC_ERR_OVERLOAD;
                }
 
                if (unlikely(err)) {
-                       tipc_skb_reject(net, err, skb, xmitq);
+                       if (tipc_msg_reverse(tipc_own_addr(net), &skb, err)) {
+                               trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_NONE,
+                                                     "@filter_rcv!");
+                               __skb_queue_tail(xmitq, skb);
+                       }
                        err = TIPC_OK;
                        continue;
                }
                __skb_queue_tail(&sk->sk_receive_queue, skb);
                skb_set_owner_r(skb, sk);
+               trace_tipc_sk_overlimit2(sk, skb, TIPC_DUMP_ALL,
+                                        "rcvq >90% allocated!");
                sk->sk_data_ready(sk);
        }
 }
@@ -2216,14 +2250,21 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk,
                if (!sk->sk_backlog.len)
                        atomic_set(dcnt, 0);
                lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt);
-               if (likely(!sk_add_backlog(sk, skb, lim)))
+               if (likely(!sk_add_backlog(sk, skb, lim))) {
+                       trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_ALL,
+                                                "bklg & rcvq >90% allocated!");
                        continue;
+               }
 
+               trace_tipc_sk_dump(sk, skb, TIPC_DUMP_ALL, "err_overload!");
                /* Overload => reject message back to sender */
                onode = tipc_own_addr(sock_net(sk));
                atomic_inc(&sk->sk_drops);
-               if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD))
+               if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD)) {
+                       trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_ALL,
+                                             "@sk_enqueue!");
                        __skb_queue_tail(xmitq, skb);
+               }
                break;
        }
 }
@@ -2272,6 +2313,8 @@ void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq)
                /* Prepare for message rejection */
                if (!tipc_msg_reverse(tipc_own_addr(net), &skb, err))
                        continue;
+
+               trace_tipc_sk_rej_msg(NULL, skb, TIPC_DUMP_NONE, "@sk_rcv!");
 xmit:
                dnode = msg_destnode(buf_msg(skb));
                tipc_node_xmit_skb(net, skb, dnode, dport);
@@ -2545,6 +2588,7 @@ static int tipc_shutdown(struct socket *sock, int how)
 
        lock_sock(sk);
 
+       trace_tipc_sk_shutdown(sk, NULL, TIPC_DUMP_ALL, " ");
        __tipc_shutdown(sock, TIPC_CONN_SHUTDOWN);
        sk->sk_shutdown = SEND_SHUTDOWN;
 
@@ -2717,11 +2761,15 @@ void tipc_sk_reinit(struct net *net)
                rhashtable_walk_start(&iter);
 
                while ((tsk = rhashtable_walk_next(&iter)) && !IS_ERR(tsk)) {
-                       spin_lock_bh(&tsk->sk.sk_lock.slock);
+                       sock_hold(&tsk->sk);
+                       rhashtable_walk_stop(&iter);
+                       lock_sock(&tsk->sk);
                        msg = &tsk->phdr;
                        msg_set_prevnode(msg, tipc_own_addr(net));
                        msg_set_orignode(msg, tipc_own_addr(net));
-                       spin_unlock_bh(&tsk->sk.sk_lock.slock);
+                       release_sock(&tsk->sk);
+                       rhashtable_walk_start(&iter);
+                       sock_put(&tsk->sk);
                }
 
                rhashtable_walk_stop(&iter);
@@ -3557,3 +3605,187 @@ int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb)
 
        return skb->len;
 }
+
+/**
+ * tipc_sk_filtering - check if a socket should be traced
+ * @sk: the socket to be examined
+ * @sysctl_tipc_sk_filter[]: the socket tuple for filtering,
+ *  (portid, sock type, name type, name lower, name upper)
+ *
+ * Returns true if the socket meets the socket tuple data
+ * (value 0 = 'any') or when there is no tuple set (all = 0),
+ * otherwise false
+ */
+bool tipc_sk_filtering(struct sock *sk)
+{
+       struct tipc_sock *tsk;
+       struct publication *p;
+       u32 _port, _sktype, _type, _lower, _upper;
+       u32 type = 0, lower = 0, upper = 0;
+
+       if (!sk)
+               return true;
+
+       tsk = tipc_sk(sk);
+
+       _port = sysctl_tipc_sk_filter[0];
+       _sktype = sysctl_tipc_sk_filter[1];
+       _type = sysctl_tipc_sk_filter[2];
+       _lower = sysctl_tipc_sk_filter[3];
+       _upper = sysctl_tipc_sk_filter[4];
+
+       if (!_port && !_sktype && !_type && !_lower && !_upper)
+               return true;
+
+       if (_port)
+               return (_port == tsk->portid);
+
+       if (_sktype && _sktype != sk->sk_type)
+               return false;
+
+       if (tsk->published) {
+               p = list_first_entry_or_null(&tsk->publications,
+                                            struct publication, binding_sock);
+               if (p) {
+                       type = p->type;
+                       lower = p->lower;
+                       upper = p->upper;
+               }
+       }
+
+       if (!tipc_sk_type_connectionless(sk)) {
+               type = tsk->conn_type;
+               lower = tsk->conn_instance;
+               upper = tsk->conn_instance;
+       }
+
+       if ((_type && _type != type) || (_lower && _lower != lower) ||
+           (_upper && _upper != upper))
+               return false;
+
+       return true;
+}
+
+u32 tipc_sock_get_portid(struct sock *sk)
+{
+       return (sk) ? (tipc_sk(sk))->portid : 0;
+}
+
+/**
+ * tipc_sk_overlimit1 - check if socket rx queue is about to be overloaded,
+ *                     both the rcv and backlog queues are considered
+ * @sk: tipc sk to be checked
+ * @skb: tipc msg to be checked
+ *
+ * Returns true if the socket rx queue allocation is > 90%, otherwise false
+ */
+
+bool tipc_sk_overlimit1(struct sock *sk, struct sk_buff *skb)
+{
+       atomic_t *dcnt = &tipc_sk(sk)->dupl_rcvcnt;
+       unsigned int lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt);
+       unsigned int qsize = sk->sk_backlog.len + sk_rmem_alloc_get(sk);
+
+       return (qsize > lim * 90 / 100);
+}
+
+/**
+ * tipc_sk_overlimit2 - check if socket rx queue is about to be overloaded,
+ *                     only the rcv queue is considered
+ * @sk: tipc sk to be checked
+ * @skb: tipc msg to be checked
+ *
+ * Returns true if the socket rx queue allocation is > 90%, otherwise false
+ */
+
+bool tipc_sk_overlimit2(struct sock *sk, struct sk_buff *skb)
+{
+       unsigned int lim = rcvbuf_limit(sk, skb);
+       unsigned int qsize = sk_rmem_alloc_get(sk);
+
+       return (qsize > lim * 90 / 100);
+}
+
+/**
+ * tipc_sk_dump - dump TIPC socket
+ * @sk: tipc sk to be dumped
+ * @dqueues: bitmask to decide if any socket queue to be dumped?
+ *           - TIPC_DUMP_NONE: don't dump socket queues
+ *           - TIPC_DUMP_SK_SNDQ: dump socket send queue
+ *           - TIPC_DUMP_SK_RCVQ: dump socket rcv queue
+ *           - TIPC_DUMP_SK_BKLGQ: dump socket backlog queue
+ *           - TIPC_DUMP_ALL: dump all the socket queues above
+ * @buf: returned buffer of dump data in format
+ */
+int tipc_sk_dump(struct sock *sk, u16 dqueues, char *buf)
+{
+       int i = 0;
+       size_t sz = (dqueues) ? SK_LMAX : SK_LMIN;
+       struct tipc_sock *tsk;
+       struct publication *p;
+       bool tsk_connected;
+
+       if (!sk) {
+               i += scnprintf(buf, sz, "sk data: (null)\n");
+               return i;
+       }
+
+       tsk = tipc_sk(sk);
+       tsk_connected = !tipc_sk_type_connectionless(sk);
+
+       i += scnprintf(buf, sz, "sk data: %u", sk->sk_type);
+       i += scnprintf(buf + i, sz - i, " %d", sk->sk_state);
+       i += scnprintf(buf + i, sz - i, " %x", tsk_own_node(tsk));
+       i += scnprintf(buf + i, sz - i, " %u", tsk->portid);
+       i += scnprintf(buf + i, sz - i, " | %u", tsk_connected);
+       if (tsk_connected) {
+               i += scnprintf(buf + i, sz - i, " %x", tsk_peer_node(tsk));
+               i += scnprintf(buf + i, sz - i, " %u", tsk_peer_port(tsk));
+               i += scnprintf(buf + i, sz - i, " %u", tsk->conn_type);
+               i += scnprintf(buf + i, sz - i, " %u", tsk->conn_instance);
+       }
+       i += scnprintf(buf + i, sz - i, " | %u", tsk->published);
+       if (tsk->published) {
+               p = list_first_entry_or_null(&tsk->publications,
+                                            struct publication, binding_sock);
+               i += scnprintf(buf + i, sz - i, " %u", (p) ? p->type : 0);
+               i += scnprintf(buf + i, sz - i, " %u", (p) ? p->lower : 0);
+               i += scnprintf(buf + i, sz - i, " %u", (p) ? p->upper : 0);
+       }
+       i += scnprintf(buf + i, sz - i, " | %u", tsk->snd_win);
+       i += scnprintf(buf + i, sz - i, " %u", tsk->rcv_win);
+       i += scnprintf(buf + i, sz - i, " %u", tsk->max_pkt);
+       i += scnprintf(buf + i, sz - i, " %x", tsk->peer_caps);
+       i += scnprintf(buf + i, sz - i, " %u", tsk->cong_link_cnt);
+       i += scnprintf(buf + i, sz - i, " %u", tsk->snt_unacked);
+       i += scnprintf(buf + i, sz - i, " %u", tsk->rcv_unacked);
+       i += scnprintf(buf + i, sz - i, " %u", atomic_read(&tsk->dupl_rcvcnt));
+       i += scnprintf(buf + i, sz - i, " %u", sk->sk_shutdown);
+       i += scnprintf(buf + i, sz - i, " | %d", sk_wmem_alloc_get(sk));
+       i += scnprintf(buf + i, sz - i, " %d", sk->sk_sndbuf);
+       i += scnprintf(buf + i, sz - i, " | %d", sk_rmem_alloc_get(sk));
+       i += scnprintf(buf + i, sz - i, " %d", sk->sk_rcvbuf);
+       i += scnprintf(buf + i, sz - i, " | %d\n", sk->sk_backlog.len);
+
+       if (dqueues & TIPC_DUMP_SK_SNDQ) {
+               i += scnprintf(buf + i, sz - i, "sk_write_queue: ");
+               i += tipc_list_dump(&sk->sk_write_queue, false, buf + i);
+       }
+
+       if (dqueues & TIPC_DUMP_SK_RCVQ) {
+               i += scnprintf(buf + i, sz - i, "sk_receive_queue: ");
+               i += tipc_list_dump(&sk->sk_receive_queue, false, buf + i);
+       }
+
+       if (dqueues & TIPC_DUMP_SK_BKLGQ) {
+               i += scnprintf(buf + i, sz - i, "sk_backlog:\n  head ");
+               i += tipc_skb_dump(sk->sk_backlog.head, false, buf + i);
+               if (sk->sk_backlog.tail != sk->sk_backlog.head) {
+                       i += scnprintf(buf + i, sz - i, "  tail ");
+                       i += tipc_skb_dump(sk->sk_backlog.tail, false,
+                                          buf + i);
+               }
+       }
+
+       return i;
+}
index 5e575f2..235b967 100644 (file)
@@ -71,4 +71,8 @@ int tipc_nl_sk_walk(struct sk_buff *skb, struct netlink_callback *cb,
 int tipc_dump_start(struct netlink_callback *cb);
 int __tipc_dump_start(struct netlink_callback *cb, struct net *net);
 int tipc_dump_done(struct netlink_callback *cb);
+u32 tipc_sock_get_portid(struct sock *sk);
+bool tipc_sk_overlimit1(struct sock *sk, struct sk_buff *skb);
+bool tipc_sk_overlimit2(struct sock *sk, struct sk_buff *skb);
+
 #endif
index 1a779b1..3481e49 100644 (file)
@@ -34,6 +34,7 @@
  */
 
 #include "core.h"
+#include "trace.h"
 
 #include <linux/sysctl.h>
 
@@ -54,6 +55,13 @@ static struct ctl_table tipc_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
+       {
+               .procname       = "sk_filter",
+               .data           = &sysctl_tipc_sk_filter,
+               .maxlen         = sizeof(sysctl_tipc_sk_filter),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
        {}
 };
 
diff --git a/net/tipc/trace.c b/net/tipc/trace.c
new file mode 100644 (file)
index 0000000..9648238
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * net/tipc/trace.c: TIPC tracepoints code
+ *
+ * Copyright (c) 2018, Ericsson AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "ASIS"
+ * 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.
+ */
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+/**
+ * socket tuples for filtering in socket traces:
+ * (portid, sock type, name type, name lower, name upper)
+ */
+unsigned long sysctl_tipc_sk_filter[5] __read_mostly = {0, };
+
+/**
+ * tipc_skb_dump - dump TIPC skb data
+ * @skb: skb to be dumped
+ * @more: dump more?
+ *        - false: dump only tipc msg data
+ *        - true: dump kernel-related skb data and tipc cb[] array as well
+ * @buf: returned buffer of dump data in format
+ */
+int tipc_skb_dump(struct sk_buff *skb, bool more, char *buf)
+{
+       int i = 0;
+       size_t sz = (more) ? SKB_LMAX : SKB_LMIN;
+       struct tipc_msg *hdr;
+       struct tipc_skb_cb *skbcb;
+
+       if (!skb) {
+               i += scnprintf(buf, sz, "msg: (null)\n");
+               return i;
+       }
+
+       hdr = buf_msg(skb);
+       skbcb = TIPC_SKB_CB(skb);
+
+       /* tipc msg data section */
+       i += scnprintf(buf, sz, "msg: %u", msg_user(hdr));
+       i += scnprintf(buf + i, sz - i, " %u", msg_type(hdr));
+       i += scnprintf(buf + i, sz - i, " %u", msg_hdr_sz(hdr));
+       i += scnprintf(buf + i, sz - i, " %u", msg_data_sz(hdr));
+       i += scnprintf(buf + i, sz - i, " %x", msg_orignode(hdr));
+       i += scnprintf(buf + i, sz - i, " %x", msg_destnode(hdr));
+       i += scnprintf(buf + i, sz - i, " %u", msg_seqno(hdr));
+       i += scnprintf(buf + i, sz - i, " %u", msg_ack(hdr));
+       i += scnprintf(buf + i, sz - i, " %u", msg_bcast_ack(hdr));
+       switch (msg_user(hdr)) {
+       case LINK_PROTOCOL:
+               i += scnprintf(buf + i, sz - i, " %c", msg_net_plane(hdr));
+               i += scnprintf(buf + i, sz - i, " %u", msg_probe(hdr));
+               i += scnprintf(buf + i, sz - i, " %u", msg_peer_stopping(hdr));
+               i += scnprintf(buf + i, sz - i, " %u", msg_session(hdr));
+               i += scnprintf(buf + i, sz - i, " %u", msg_next_sent(hdr));
+               i += scnprintf(buf + i, sz - i, " %u", msg_seq_gap(hdr));
+               i += scnprintf(buf + i, sz - i, " %u", msg_bc_snd_nxt(hdr));
+               i += scnprintf(buf + i, sz - i, " %u", msg_bc_gap(hdr));
+               break;
+       case TIPC_LOW_IMPORTANCE:
+       case TIPC_MEDIUM_IMPORTANCE:
+       case TIPC_HIGH_IMPORTANCE:
+       case TIPC_CRITICAL_IMPORTANCE:
+       case CONN_MANAGER:
+       case SOCK_WAKEUP:
+               i += scnprintf(buf + i, sz - i, " | %u", msg_origport(hdr));
+               i += scnprintf(buf + i, sz - i, " %u", msg_destport(hdr));
+               switch (msg_type(hdr)) {
+               case TIPC_NAMED_MSG:
+                       i += scnprintf(buf + i, sz - i, " %u",
+                                      msg_nametype(hdr));
+                       i += scnprintf(buf + i, sz - i, " %u",
+                                      msg_nameinst(hdr));
+                       break;
+               case TIPC_MCAST_MSG:
+                       i += scnprintf(buf + i, sz - i, " %u",
+                                      msg_nametype(hdr));
+                       i += scnprintf(buf + i, sz - i, " %u",
+                                      msg_namelower(hdr));
+                       i += scnprintf(buf + i, sz - i, " %u",
+                                      msg_nameupper(hdr));
+                       break;
+               default:
+                       break;
+               };
+               i += scnprintf(buf + i, sz - i, " | %u",
+                              msg_src_droppable(hdr));
+               i += scnprintf(buf + i, sz - i, " %u",
+                              msg_dest_droppable(hdr));
+               i += scnprintf(buf + i, sz - i, " %u", msg_errcode(hdr));
+               i += scnprintf(buf + i, sz - i, " %u", msg_reroute_cnt(hdr));
+               break;
+       default:
+               /* need more? */
+               break;
+       };
+
+       i += scnprintf(buf + i, sz - i, "\n");
+       if (!more)
+               return i;
+
+       /* kernel-related skb data section */
+       i += scnprintf(buf + i, sz - i, "skb: %s",
+                      (skb->dev) ? skb->dev->name : "n/a");
+       i += scnprintf(buf + i, sz - i, " %u", skb->len);
+       i += scnprintf(buf + i, sz - i, " %u", skb->data_len);
+       i += scnprintf(buf + i, sz - i, " %u", skb->hdr_len);
+       i += scnprintf(buf + i, sz - i, " %u", skb->truesize);
+       i += scnprintf(buf + i, sz - i, " %u", skb_cloned(skb));
+       i += scnprintf(buf + i, sz - i, " %p", skb->sk);
+       i += scnprintf(buf + i, sz - i, " %u", skb_shinfo(skb)->nr_frags);
+       i += scnprintf(buf + i, sz - i, " %llx",
+                      ktime_to_ms(skb_get_ktime(skb)));
+       i += scnprintf(buf + i, sz - i, " %llx\n",
+                      ktime_to_ms(skb_hwtstamps(skb)->hwtstamp));
+
+       /* tipc skb cb[] data section */
+       i += scnprintf(buf + i, sz - i, "cb[]: %u", skbcb->bytes_read);
+       i += scnprintf(buf + i, sz - i, " %u", skbcb->orig_member);
+       i += scnprintf(buf + i, sz - i, " %u",
+                      jiffies_to_msecs(skbcb->nxt_retr));
+       i += scnprintf(buf + i, sz - i, " %u", skbcb->validated);
+       i += scnprintf(buf + i, sz - i, " %u", skbcb->chain_imp);
+       i += scnprintf(buf + i, sz - i, " %u\n", skbcb->ackers);
+
+       return i;
+}
+
+/**
+ * tipc_list_dump - dump TIPC skb list/queue
+ * @list: list of skbs to be dumped
+ * @more: dump more?
+ *        - false: dump only the head & tail skbs
+ *        - true: dump the first & last 5 skbs
+ * @buf: returned buffer of dump data in format
+ */
+int tipc_list_dump(struct sk_buff_head *list, bool more, char *buf)
+{
+       int i = 0;
+       size_t sz = (more) ? LIST_LMAX : LIST_LMIN;
+       u32 count, len;
+       struct sk_buff *hskb, *tskb, *skb, *tmp;
+
+       if (!list) {
+               i += scnprintf(buf, sz, "(null)\n");
+               return i;
+       }
+
+       len = skb_queue_len(list);
+       i += scnprintf(buf, sz, "len = %d\n", len);
+
+       if (!len)
+               return i;
+
+       if (!more) {
+               hskb = skb_peek(list);
+               i += scnprintf(buf + i, sz - i, "  head ");
+               i += tipc_skb_dump(hskb, false, buf + i);
+               if (len > 1) {
+                       tskb = skb_peek_tail(list);
+                       i += scnprintf(buf + i, sz - i, "  tail ");
+                       i += tipc_skb_dump(tskb, false, buf + i);
+               }
+       } else {
+               count = 0;
+               skb_queue_walk_safe(list, skb, tmp) {
+                       count++;
+                       if (count == 6)
+                               i += scnprintf(buf + i, sz - i, "  .\n  .\n");
+                       if (count > 5 && count <= len - 5)
+                               continue;
+                       i += scnprintf(buf + i, sz - i, "  #%d ", count);
+                       i += tipc_skb_dump(skb, false, buf + i);
+               }
+       }
+       return i;
+}
diff --git a/net/tipc/trace.h b/net/tipc/trace.h
new file mode 100644 (file)
index 0000000..4d8e004
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ * net/tipc/trace.h: TIPC tracepoints
+ *
+ * Copyright (c) 2018, Ericsson AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "ASIS"
+ * 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM tipc
+
+#if !defined(_TIPC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TIPC_TRACE_H
+
+#include <linux/tracepoint.h>
+#include "core.h"
+#include "link.h"
+#include "socket.h"
+#include "node.h"
+
+#define SKB_LMIN       (100)
+#define SKB_LMAX       (SKB_LMIN * 2)
+#define LIST_LMIN      (SKB_LMIN * 3)
+#define LIST_LMAX      (SKB_LMIN * 11)
+#define SK_LMIN                (SKB_LMIN * 2)
+#define SK_LMAX                (SKB_LMIN * 11)
+#define LINK_LMIN      (SKB_LMIN)
+#define LINK_LMAX      (SKB_LMIN * 16)
+#define NODE_LMIN      (SKB_LMIN)
+#define NODE_LMAX      (SKB_LMIN * 11)
+
+#ifndef __TIPC_TRACE_ENUM
+#define __TIPC_TRACE_ENUM
+enum {
+       TIPC_DUMP_NONE          = 0,
+
+       TIPC_DUMP_TRANSMQ       = 1,
+       TIPC_DUMP_BACKLOGQ      = (1 << 1),
+       TIPC_DUMP_DEFERDQ       = (1 << 2),
+       TIPC_DUMP_INPUTQ        = (1 << 3),
+       TIPC_DUMP_WAKEUP        = (1 << 4),
+
+       TIPC_DUMP_SK_SNDQ       = (1 << 8),
+       TIPC_DUMP_SK_RCVQ       = (1 << 9),
+       TIPC_DUMP_SK_BKLGQ      = (1 << 10),
+       TIPC_DUMP_ALL           = 0xffffu
+};
+#endif
+
+/* Link & Node FSM states: */
+#define state_sym(val)                                                   \
+       __print_symbolic(val,                                             \
+                       {(0xe),         "ESTABLISHED"                   },\
+                       {(0xe << 4),    "ESTABLISHING"                  },\
+                       {(0x1 << 8),    "RESET"                         },\
+                       {(0x2 << 12),   "RESETTING"                     },\
+                       {(0xd << 16),   "PEER_RESET"                    },\
+                       {(0xf << 20),   "FAILINGOVER"                   },\
+                       {(0xc << 24),   "SYNCHING"                      },\
+                       {(0xdd),        "SELF_DOWN_PEER_DOWN"           },\
+                       {(0xaa),        "SELF_UP_PEER_UP"               },\
+                       {(0xd1),        "SELF_DOWN_PEER_LEAVING"        },\
+                       {(0xac),        "SELF_UP_PEER_COMING"           },\
+                       {(0xca),        "SELF_COMING_PEER_UP"           },\
+                       {(0x1d),        "SELF_LEAVING_PEER_DOWN"        },\
+                       {(0xf0),        "FAILINGOVER"                   },\
+                       {(0xcc),        "SYNCHING"                      })
+
+/* Link & Node FSM events: */
+#define evt_sym(val)                                                     \
+       __print_symbolic(val,                                             \
+                       {(0xec1ab1e),   "ESTABLISH_EVT"                 },\
+                       {(0x9eed0e),    "PEER_RESET_EVT"                },\
+                       {(0xfa110e),    "FAILURE_EVT"                   },\
+                       {(0x10ca1d0e),  "RESET_EVT"                     },\
+                       {(0xfa110bee),  "FAILOVER_BEGIN_EVT"            },\
+                       {(0xfa110ede),  "FAILOVER_END_EVT"              },\
+                       {(0xc1ccbee),   "SYNCH_BEGIN_EVT"               },\
+                       {(0xc1ccede),   "SYNCH_END_EVT"                 },\
+                       {(0xece),       "SELF_ESTABL_CONTACT_EVT"       },\
+                       {(0x1ce),       "SELF_LOST_CONTACT_EVT"         },\
+                       {(0x9ece),      "PEER_ESTABL_CONTACT_EVT"       },\
+                       {(0x91ce),      "PEER_LOST_CONTACT_EVT"         },\
+                       {(0xfbe),       "FAILOVER_BEGIN_EVT"            },\
+                       {(0xfee),       "FAILOVER_END_EVT"              },\
+                       {(0xcbe),       "SYNCH_BEGIN_EVT"               },\
+                       {(0xcee),       "SYNCH_END_EVT"                 })
+
+/* Bearer, net device events: */
+#define dev_evt_sym(val)                                                 \
+       __print_symbolic(val,                                             \
+                       {(NETDEV_CHANGE),       "NETDEV_CHANGE"         },\
+                       {(NETDEV_GOING_DOWN),   "NETDEV_GOING_DOWN"     },\
+                       {(NETDEV_UP),           "NETDEV_UP"             },\
+                       {(NETDEV_CHANGEMTU),    "NETDEV_CHANGEMTU"      },\
+                       {(NETDEV_CHANGEADDR),   "NETDEV_CHANGEADDR"     },\
+                       {(NETDEV_UNREGISTER),   "NETDEV_UNREGISTER"     },\
+                       {(NETDEV_CHANGENAME),   "NETDEV_CHANGENAME"     })
+
+extern unsigned long sysctl_tipc_sk_filter[5] __read_mostly;
+
+int tipc_skb_dump(struct sk_buff *skb, bool more, char *buf);
+int tipc_list_dump(struct sk_buff_head *list, bool more, char *buf);
+int tipc_sk_dump(struct sock *sk, u16 dqueues, char *buf);
+int tipc_link_dump(struct tipc_link *l, u16 dqueues, char *buf);
+int tipc_node_dump(struct tipc_node *n, bool more, char *buf);
+bool tipc_sk_filtering(struct sock *sk);
+
+DECLARE_EVENT_CLASS(tipc_skb_class,
+
+       TP_PROTO(struct sk_buff *skb, bool more, const char *header),
+
+       TP_ARGS(skb, more, header),
+
+       TP_STRUCT__entry(
+               __string(header, header)
+               __dynamic_array(char, buf, (more) ? SKB_LMAX : SKB_LMIN)
+       ),
+
+       TP_fast_assign(
+               __assign_str(header, header);
+               tipc_skb_dump(skb, more, __get_str(buf));
+       ),
+
+       TP_printk("%s\n%s", __get_str(header), __get_str(buf))
+)
+
+#define DEFINE_SKB_EVENT(name) \
+DEFINE_EVENT(tipc_skb_class, name, \
+       TP_PROTO(struct sk_buff *skb, bool more, const char *header), \
+       TP_ARGS(skb, more, header))
+DEFINE_SKB_EVENT(tipc_skb_dump);
+DEFINE_SKB_EVENT(tipc_proto_build);
+DEFINE_SKB_EVENT(tipc_proto_rcv);
+
+DECLARE_EVENT_CLASS(tipc_list_class,
+
+       TP_PROTO(struct sk_buff_head *list, bool more, const char *header),
+
+       TP_ARGS(list, more, header),
+
+       TP_STRUCT__entry(
+               __string(header, header)
+               __dynamic_array(char, buf, (more) ? LIST_LMAX : LIST_LMIN)
+       ),
+
+       TP_fast_assign(
+               __assign_str(header, header);
+               tipc_list_dump(list, more, __get_str(buf));
+       ),
+
+       TP_printk("%s\n%s", __get_str(header), __get_str(buf))
+);
+
+#define DEFINE_LIST_EVENT(name) \
+DEFINE_EVENT(tipc_list_class, name, \
+       TP_PROTO(struct sk_buff_head *list, bool more, const char *header), \
+       TP_ARGS(list, more, header))
+DEFINE_LIST_EVENT(tipc_list_dump);
+
+DECLARE_EVENT_CLASS(tipc_sk_class,
+
+       TP_PROTO(struct sock *sk, struct sk_buff *skb, u16 dqueues,
+                const char *header),
+
+       TP_ARGS(sk, skb, dqueues, header),
+
+       TP_STRUCT__entry(
+               __string(header, header)
+               __field(u32, portid)
+               __dynamic_array(char, buf, (dqueues) ? SK_LMAX : SK_LMIN)
+               __dynamic_array(char, skb_buf, (skb) ? SKB_LMIN : 1)
+       ),
+
+       TP_fast_assign(
+               __assign_str(header, header);
+               __entry->portid = tipc_sock_get_portid(sk);
+               tipc_sk_dump(sk, dqueues, __get_str(buf));
+               if (skb)
+                       tipc_skb_dump(skb, false, __get_str(skb_buf));
+               else
+                       *(__get_str(skb_buf)) = '\0';
+       ),
+
+       TP_printk("<%u> %s\n%s%s", __entry->portid, __get_str(header),
+                 __get_str(skb_buf), __get_str(buf))
+);
+
+#define DEFINE_SK_EVENT_FILTER(name) \
+DEFINE_EVENT_CONDITION(tipc_sk_class, name, \
+       TP_PROTO(struct sock *sk, struct sk_buff *skb, u16 dqueues, \
+                const char *header), \
+       TP_ARGS(sk, skb, dqueues, header), \
+       TP_CONDITION(tipc_sk_filtering(sk)))
+DEFINE_SK_EVENT_FILTER(tipc_sk_dump);
+DEFINE_SK_EVENT_FILTER(tipc_sk_create);
+DEFINE_SK_EVENT_FILTER(tipc_sk_sendmcast);
+DEFINE_SK_EVENT_FILTER(tipc_sk_sendmsg);
+DEFINE_SK_EVENT_FILTER(tipc_sk_sendstream);
+DEFINE_SK_EVENT_FILTER(tipc_sk_poll);
+DEFINE_SK_EVENT_FILTER(tipc_sk_filter_rcv);
+DEFINE_SK_EVENT_FILTER(tipc_sk_advance_rx);
+DEFINE_SK_EVENT_FILTER(tipc_sk_rej_msg);
+DEFINE_SK_EVENT_FILTER(tipc_sk_drop_msg);
+DEFINE_SK_EVENT_FILTER(tipc_sk_release);
+DEFINE_SK_EVENT_FILTER(tipc_sk_shutdown);
+
+#define DEFINE_SK_EVENT_FILTER_COND(name, cond) \
+DEFINE_EVENT_CONDITION(tipc_sk_class, name, \
+       TP_PROTO(struct sock *sk, struct sk_buff *skb, u16 dqueues, \
+                const char *header), \
+       TP_ARGS(sk, skb, dqueues, header), \
+       TP_CONDITION(tipc_sk_filtering(sk) && (cond)))
+DEFINE_SK_EVENT_FILTER_COND(tipc_sk_overlimit1, tipc_sk_overlimit1(sk, skb));
+DEFINE_SK_EVENT_FILTER_COND(tipc_sk_overlimit2, tipc_sk_overlimit2(sk, skb));
+
+DECLARE_EVENT_CLASS(tipc_link_class,
+
+       TP_PROTO(struct tipc_link *l, u16 dqueues, const char *header),
+
+       TP_ARGS(l, dqueues, header),
+
+       TP_STRUCT__entry(
+               __string(header, header)
+               __array(char, name, TIPC_MAX_LINK_NAME)
+               __dynamic_array(char, buf, (dqueues) ? LINK_LMAX : LINK_LMIN)
+       ),
+
+       TP_fast_assign(
+               __assign_str(header, header);
+               tipc_link_name_ext(l, __entry->name);
+               tipc_link_dump(l, dqueues, __get_str(buf));
+       ),
+
+       TP_printk("<%s> %s\n%s", __entry->name, __get_str(header),
+                 __get_str(buf))
+);
+
+#define DEFINE_LINK_EVENT(name) \
+DEFINE_EVENT(tipc_link_class, name, \
+       TP_PROTO(struct tipc_link *l, u16 dqueues, const char *header), \
+       TP_ARGS(l, dqueues, header))
+DEFINE_LINK_EVENT(tipc_link_dump);
+DEFINE_LINK_EVENT(tipc_link_conges);
+DEFINE_LINK_EVENT(tipc_link_timeout);
+DEFINE_LINK_EVENT(tipc_link_reset);
+
+#define DEFINE_LINK_EVENT_COND(name, cond) \
+DEFINE_EVENT_CONDITION(tipc_link_class, name, \
+       TP_PROTO(struct tipc_link *l, u16 dqueues, const char *header), \
+       TP_ARGS(l, dqueues, header), \
+       TP_CONDITION(cond))
+DEFINE_LINK_EVENT_COND(tipc_link_too_silent, tipc_link_too_silent(l));
+
+DECLARE_EVENT_CLASS(tipc_link_transmq_class,
+
+       TP_PROTO(struct tipc_link *r, u16 f, u16 t, struct sk_buff_head *tq),
+
+       TP_ARGS(r, f, t, tq),
+
+       TP_STRUCT__entry(
+               __array(char, name, TIPC_MAX_LINK_NAME)
+               __field(u16, from)
+               __field(u16, to)
+               __field(u32, len)
+               __field(u16, fseqno)
+               __field(u16, lseqno)
+       ),
+
+       TP_fast_assign(
+               tipc_link_name_ext(r, __entry->name);
+               __entry->from = f;
+               __entry->to = t;
+               __entry->len = skb_queue_len(tq);
+               __entry->fseqno = msg_seqno(buf_msg(skb_peek(tq)));
+               __entry->lseqno = msg_seqno(buf_msg(skb_peek_tail(tq)));
+       ),
+
+       TP_printk("<%s> retrans req: [%u-%u] transmq: %u [%u-%u]\n",
+                 __entry->name, __entry->from, __entry->to,
+                 __entry->len, __entry->fseqno, __entry->lseqno)
+);
+
+DEFINE_EVENT(tipc_link_transmq_class, tipc_link_retrans,
+       TP_PROTO(struct tipc_link *r, u16 f, u16 t, struct sk_buff_head *tq),
+       TP_ARGS(r, f, t, tq)
+);
+
+DEFINE_EVENT_PRINT(tipc_link_transmq_class, tipc_link_bc_ack,
+       TP_PROTO(struct tipc_link *r, u16 f, u16 t, struct sk_buff_head *tq),
+       TP_ARGS(r, f, t, tq),
+       TP_printk("<%s> acked: [%u-%u] transmq: %u [%u-%u]\n",
+                 __entry->name, __entry->from, __entry->to,
+                 __entry->len, __entry->fseqno, __entry->lseqno)
+);
+
+DECLARE_EVENT_CLASS(tipc_node_class,
+
+       TP_PROTO(struct tipc_node *n, bool more, const char *header),
+
+       TP_ARGS(n, more, header),
+
+       TP_STRUCT__entry(
+               __string(header, header)
+               __field(u32, addr)
+               __dynamic_array(char, buf, (more) ? NODE_LMAX : NODE_LMIN)
+       ),
+
+       TP_fast_assign(
+               __assign_str(header, header);
+               __entry->addr = tipc_node_get_addr(n);
+               tipc_node_dump(n, more, __get_str(buf));
+       ),
+
+       TP_printk("<%x> %s\n%s", __entry->addr, __get_str(header),
+                 __get_str(buf))
+);
+
+#define DEFINE_NODE_EVENT(name) \
+DEFINE_EVENT(tipc_node_class, name, \
+       TP_PROTO(struct tipc_node *n, bool more, const char *header), \
+       TP_ARGS(n, more, header))
+DEFINE_NODE_EVENT(tipc_node_dump);
+DEFINE_NODE_EVENT(tipc_node_create);
+DEFINE_NODE_EVENT(tipc_node_delete);
+DEFINE_NODE_EVENT(tipc_node_lost_contact);
+DEFINE_NODE_EVENT(tipc_node_timeout);
+DEFINE_NODE_EVENT(tipc_node_link_up);
+DEFINE_NODE_EVENT(tipc_node_link_down);
+DEFINE_NODE_EVENT(tipc_node_reset_links);
+DEFINE_NODE_EVENT(tipc_node_check_state);
+
+DECLARE_EVENT_CLASS(tipc_fsm_class,
+
+       TP_PROTO(const char *name, u32 os, u32 ns, int evt),
+
+       TP_ARGS(name, os, ns, evt),
+
+       TP_STRUCT__entry(
+               __string(name, name)
+               __field(u32, os)
+               __field(u32, ns)
+               __field(u32, evt)
+       ),
+
+       TP_fast_assign(
+               __assign_str(name, name);
+               __entry->os = os;
+               __entry->ns = ns;
+               __entry->evt = evt;
+       ),
+
+       TP_printk("<%s> %s--(%s)->%s\n", __get_str(name),
+                 state_sym(__entry->os), evt_sym(__entry->evt),
+                 state_sym(__entry->ns))
+);
+
+#define DEFINE_FSM_EVENT(fsm_name) \
+DEFINE_EVENT(tipc_fsm_class, fsm_name, \
+       TP_PROTO(const char *name, u32 os, u32 ns, int evt), \
+       TP_ARGS(name, os, ns, evt))
+DEFINE_FSM_EVENT(tipc_link_fsm);
+DEFINE_FSM_EVENT(tipc_node_fsm);
+
+TRACE_EVENT(tipc_l2_device_event,
+
+       TP_PROTO(struct net_device *dev, struct tipc_bearer *b,
+                unsigned long evt),
+
+       TP_ARGS(dev, b, evt),
+
+       TP_STRUCT__entry(
+               __string(dev_name, dev->name)
+               __string(b_name, b->name)
+               __field(unsigned long, evt)
+               __field(u8, b_up)
+               __field(u8, carrier)
+               __field(u8, oper)
+       ),
+
+       TP_fast_assign(
+               __assign_str(dev_name, dev->name);
+               __assign_str(b_name, b->name);
+               __entry->evt = evt;
+               __entry->b_up = test_bit(0, &b->up);
+               __entry->carrier = netif_carrier_ok(dev);
+               __entry->oper = netif_oper_up(dev);
+       ),
+
+       TP_printk("%s on: <%s>/<%s> oper: %s carrier: %s bearer: %s\n",
+                 dev_evt_sym(__entry->evt), __get_str(dev_name),
+                 __get_str(b_name), (__entry->oper) ? "up" : "down",
+                 (__entry->carrier) ? "ok" : "notok",
+                 (__entry->b_up) ? "up" : "down")
+);
+
+#endif /* _TIPC_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+#include <trace/define_trace.h>
index 10dc59c..4d85d71 100644 (file)
@@ -245,10 +245,8 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
                }
 
                err = tipc_udp_xmit(net, _skb, ub, src, &rcast->addr);
-               if (err) {
-                       kfree_skb(_skb);
+               if (err)
                        goto out;
-               }
        }
        err = 0;
 out:
@@ -681,6 +679,11 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
        if (err)
                goto err;
 
+       if (remote.proto != local.proto) {
+               err = -EINVAL;
+               goto err;
+       }
+
        /* Checking remote ip address */
        rmcast = tipc_udp_is_mcast_addr(&remote);
 
index 311cec8..78cb4a5 100644 (file)
@@ -55,8 +55,10 @@ enum {
 
 static struct proto *saved_tcpv6_prot;
 static DEFINE_MUTEX(tcpv6_prot_mutex);
+static struct proto *saved_tcpv4_prot;
+static DEFINE_MUTEX(tcpv4_prot_mutex);
 static LIST_HEAD(device_list);
-static DEFINE_MUTEX(device_mutex);
+static DEFINE_SPINLOCK(device_spinlock);
 static struct proto tls_prots[TLS_NUM_PROTS][TLS_NUM_CONFIG][TLS_NUM_CONFIG];
 static struct proto_ops tls_sw_proto_ops;
 
@@ -538,11 +540,14 @@ static struct tls_context *create_ctx(struct sock *sk)
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct tls_context *ctx;
 
-       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
        if (!ctx)
                return NULL;
 
        icsk->icsk_ulp_data = ctx;
+       ctx->setsockopt = sk->sk_prot->setsockopt;
+       ctx->getsockopt = sk->sk_prot->getsockopt;
+       ctx->sk_proto_close = sk->sk_prot->close;
        return ctx;
 }
 
@@ -552,7 +557,7 @@ static int tls_hw_prot(struct sock *sk)
        struct tls_device *dev;
        int rc = 0;
 
-       mutex_lock(&device_mutex);
+       spin_lock_bh(&device_spinlock);
        list_for_each_entry(dev, &device_list, dev_list) {
                if (dev->feature && dev->feature(dev)) {
                        ctx = create_ctx(sk);
@@ -570,7 +575,7 @@ static int tls_hw_prot(struct sock *sk)
                }
        }
 out:
-       mutex_unlock(&device_mutex);
+       spin_unlock_bh(&device_spinlock);
        return rc;
 }
 
@@ -579,12 +584,17 @@ static void tls_hw_unhash(struct sock *sk)
        struct tls_context *ctx = tls_get_ctx(sk);
        struct tls_device *dev;
 
-       mutex_lock(&device_mutex);
+       spin_lock_bh(&device_spinlock);
        list_for_each_entry(dev, &device_list, dev_list) {
-               if (dev->unhash)
+               if (dev->unhash) {
+                       kref_get(&dev->kref);
+                       spin_unlock_bh(&device_spinlock);
                        dev->unhash(dev, sk);
+                       kref_put(&dev->kref, dev->release);
+                       spin_lock_bh(&device_spinlock);
+               }
        }
-       mutex_unlock(&device_mutex);
+       spin_unlock_bh(&device_spinlock);
        ctx->unhash(sk);
 }
 
@@ -595,12 +605,17 @@ static int tls_hw_hash(struct sock *sk)
        int err;
 
        err = ctx->hash(sk);
-       mutex_lock(&device_mutex);
+       spin_lock_bh(&device_spinlock);
        list_for_each_entry(dev, &device_list, dev_list) {
-               if (dev->hash)
+               if (dev->hash) {
+                       kref_get(&dev->kref);
+                       spin_unlock_bh(&device_spinlock);
                        err |= dev->hash(dev, sk);
+                       kref_put(&dev->kref, dev->release);
+                       spin_lock_bh(&device_spinlock);
+               }
        }
-       mutex_unlock(&device_mutex);
+       spin_unlock_bh(&device_spinlock);
 
        if (err)
                tls_hw_unhash(sk);
@@ -675,9 +690,6 @@ static int tls_init(struct sock *sk)
                rc = -ENOMEM;
                goto out;
        }
-       ctx->setsockopt = sk->sk_prot->setsockopt;
-       ctx->getsockopt = sk->sk_prot->getsockopt;
-       ctx->sk_proto_close = sk->sk_prot->close;
 
        /* Build IPv6 TLS whenever the address of tcpv6 _prot changes */
        if (ip_ver == TLSV6 &&
@@ -690,6 +702,16 @@ static int tls_init(struct sock *sk)
                mutex_unlock(&tcpv6_prot_mutex);
        }
 
+       if (ip_ver == TLSV4 &&
+           unlikely(sk->sk_prot != smp_load_acquire(&saved_tcpv4_prot))) {
+               mutex_lock(&tcpv4_prot_mutex);
+               if (likely(sk->sk_prot != saved_tcpv4_prot)) {
+                       build_protos(tls_prots[TLSV4], sk->sk_prot);
+                       smp_store_release(&saved_tcpv4_prot, sk->sk_prot);
+               }
+               mutex_unlock(&tcpv4_prot_mutex);
+       }
+
        ctx->tx_conf = TLS_BASE;
        ctx->rx_conf = TLS_BASE;
        update_sk_prot(sk, ctx);
@@ -699,17 +721,17 @@ out:
 
 void tls_register_device(struct tls_device *device)
 {
-       mutex_lock(&device_mutex);
+       spin_lock_bh(&device_spinlock);
        list_add_tail(&device->dev_list, &device_list);
-       mutex_unlock(&device_mutex);
+       spin_unlock_bh(&device_spinlock);
 }
 EXPORT_SYMBOL(tls_register_device);
 
 void tls_unregister_device(struct tls_device *device)
 {
-       mutex_lock(&device_mutex);
+       spin_lock_bh(&device_spinlock);
        list_del(&device->dev_list);
-       mutex_unlock(&device_mutex);
+       spin_unlock_bh(&device_spinlock);
 }
 EXPORT_SYMBOL(tls_unregister_device);
 
@@ -721,8 +743,6 @@ static struct tcp_ulp_ops tcp_tls_ulp_ops __read_mostly = {
 
 static int __init tls_register(void)
 {
-       build_protos(tls_prots[TLSV4], &tcp_prot);
-
        tls_sw_proto_ops = inet_stream_ops;
        tls_sw_proto_ops.splice_read = tls_sw_splice_read;
 
index 7b1af8b..5aee9ae 100644 (file)
@@ -686,16 +686,24 @@ static int bpf_exec_tx_verdict(struct sk_msg *msg, struct sock *sk,
        struct sk_psock *psock;
        struct sock *sk_redir;
        struct tls_rec *rec;
+       bool enospc, policy;
        int err = 0, send;
-       bool enospc;
+       u32 delta = 0;
 
+       policy = !(flags & MSG_SENDPAGE_NOPOLICY);
        psock = sk_psock_get(sk);
-       if (!psock)
+       if (!psock || !policy)
                return tls_push_record(sk, flags, record_type);
 more_data:
        enospc = sk_msg_full(msg);
-       if (psock->eval == __SK_NONE)
+       if (psock->eval == __SK_NONE) {
+               delta = msg->sg.size;
                psock->eval = sk_psock_msg_verdict(sk, psock, msg);
+               if (delta < msg->sg.size)
+                       delta -= msg->sg.size;
+               else
+                       delta = 0;
+       }
        if (msg->cork_bytes && msg->cork_bytes > msg->sg.size &&
            !enospc && !full_record) {
                err = -ENOSPC;
@@ -743,7 +751,7 @@ more_data:
                        msg->apply_bytes -= send;
                if (msg->sg.size == 0)
                        tls_free_open_rec(sk);
-               *copied -= send;
+               *copied -= (send + delta);
                err = -EACCES;
        }
 
@@ -1010,8 +1018,8 @@ send_end:
        return copied ? copied : ret;
 }
 
-int tls_sw_sendpage(struct sock *sk, struct page *page,
-                   int offset, size_t size, int flags)
+int tls_sw_do_sendpage(struct sock *sk, struct page *page,
+                      int offset, size_t size, int flags)
 {
        long timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
        struct tls_context *tls_ctx = tls_get_ctx(sk);
@@ -1026,15 +1034,7 @@ int tls_sw_sendpage(struct sock *sk, struct page *page,
        int ret = 0;
        bool eor;
 
-       if (flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL |
-                     MSG_SENDPAGE_NOTLAST))
-               return -ENOTSUPP;
-
-       /* No MSG_EOR from splice, only look at MSG_MORE */
        eor = !(flags & (MSG_MORE | MSG_SENDPAGE_NOTLAST));
-
-       lock_sock(sk);
-
        sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
 
        /* Wait till there is any pending write on socket */
@@ -1138,10 +1138,34 @@ wait_for_memory:
        }
 sendpage_end:
        ret = sk_stream_error(sk, flags, ret);
-       release_sock(sk);
        return copied ? copied : ret;
 }
 
+int tls_sw_sendpage_locked(struct sock *sk, struct page *page,
+                          int offset, size_t size, int flags)
+{
+       if (flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL |
+                     MSG_SENDPAGE_NOTLAST | MSG_SENDPAGE_NOPOLICY))
+               return -ENOTSUPP;
+
+       return tls_sw_do_sendpage(sk, page, offset, size, flags);
+}
+
+int tls_sw_sendpage(struct sock *sk, struct page *page,
+                   int offset, size_t size, int flags)
+{
+       int ret;
+
+       if (flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL |
+                     MSG_SENDPAGE_NOTLAST | MSG_SENDPAGE_NOPOLICY))
+               return -ENOTSUPP;
+
+       lock_sock(sk);
+       ret = tls_sw_do_sendpage(sk, page, offset, size, flags);
+       release_sock(sk);
+       return ret;
+}
+
 static struct sk_buff *tls_wait_data(struct sock *sk, struct sk_psock *psock,
                                     int flags, long timeo, int *err)
 {
index ab27a28..43a1dec 100644 (file)
 #include <linux/mutex.h>
 #include <linux/net.h>
 #include <linux/poll.h>
+#include <linux/random.h>
 #include <linux/skbuff.h>
 #include <linux/smp.h>
 #include <linux/socket.h>
@@ -504,9 +505,13 @@ out:
 static int __vsock_bind_stream(struct vsock_sock *vsk,
                               struct sockaddr_vm *addr)
 {
-       static u32 port = LAST_RESERVED_PORT + 1;
+       static u32 port = 0;
        struct sockaddr_vm new_addr;
 
+       if (!port)
+               port = LAST_RESERVED_PORT + 1 +
+                       prandom_u32_max(U32_MAX - LAST_RESERVED_PORT);
+
        vsock_addr_init(&new_addr, addr->svm_cid, addr->svm_port);
 
        if (addr->svm_port == VMADDR_PORT_ANY) {
index cb332ad..c361ce7 100644 (file)
@@ -263,6 +263,31 @@ vmci_transport_send_control_pkt_bh(struct sockaddr_vm *src,
                                                 false);
 }
 
+static int
+vmci_transport_alloc_send_control_pkt(struct sockaddr_vm *src,
+                                     struct sockaddr_vm *dst,
+                                     enum vmci_transport_packet_type type,
+                                     u64 size,
+                                     u64 mode,
+                                     struct vmci_transport_waiting_info *wait,
+                                     u16 proto,
+                                     struct vmci_handle handle)
+{
+       struct vmci_transport_packet *pkt;
+       int err;
+
+       pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
+       if (!pkt)
+               return -ENOMEM;
+
+       err = __vmci_transport_send_control_pkt(pkt, src, dst, type, size,
+                                               mode, wait, proto, handle,
+                                               true);
+       kfree(pkt);
+
+       return err;
+}
+
 static int
 vmci_transport_send_control_pkt(struct sock *sk,
                                enum vmci_transport_packet_type type,
@@ -272,9 +297,7 @@ vmci_transport_send_control_pkt(struct sock *sk,
                                u16 proto,
                                struct vmci_handle handle)
 {
-       struct vmci_transport_packet *pkt;
        struct vsock_sock *vsk;
-       int err;
 
        vsk = vsock_sk(sk);
 
@@ -284,17 +307,10 @@ vmci_transport_send_control_pkt(struct sock *sk,
        if (!vsock_addr_bound(&vsk->remote_addr))
                return -EINVAL;
 
-       pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
-       if (!pkt)
-               return -ENOMEM;
-
-       err = __vmci_transport_send_control_pkt(pkt, &vsk->local_addr,
-                                               &vsk->remote_addr, type, size,
-                                               mode, wait, proto, handle,
-                                               true);
-       kfree(pkt);
-
-       return err;
+       return vmci_transport_alloc_send_control_pkt(&vsk->local_addr,
+                                                    &vsk->remote_addr,
+                                                    type, size, mode,
+                                                    wait, proto, handle);
 }
 
 static int vmci_transport_send_reset_bh(struct sockaddr_vm *dst,
@@ -312,12 +328,29 @@ static int vmci_transport_send_reset_bh(struct sockaddr_vm *dst,
 static int vmci_transport_send_reset(struct sock *sk,
                                     struct vmci_transport_packet *pkt)
 {
+       struct sockaddr_vm *dst_ptr;
+       struct sockaddr_vm dst;
+       struct vsock_sock *vsk;
+
        if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST)
                return 0;
-       return vmci_transport_send_control_pkt(sk,
-                                       VMCI_TRANSPORT_PACKET_TYPE_RST,
-                                       0, 0, NULL, VSOCK_PROTO_INVALID,
-                                       VMCI_INVALID_HANDLE);
+
+       vsk = vsock_sk(sk);
+
+       if (!vsock_addr_bound(&vsk->local_addr))
+               return -EINVAL;
+
+       if (vsock_addr_bound(&vsk->remote_addr)) {
+               dst_ptr = &vsk->remote_addr;
+       } else {
+               vsock_addr_init(&dst, pkt->dg.src.context,
+                               pkt->src_port);
+               dst_ptr = &dst;
+       }
+       return vmci_transport_alloc_send_control_pkt(&vsk->local_addr, dst_ptr,
+                                            VMCI_TRANSPORT_PACKET_TYPE_RST,
+                                            0, 0, NULL, VSOCK_PROTO_INVALID,
+                                            VMCI_INVALID_HANDLE);
 }
 
 static int vmci_transport_send_negotiate(struct sock *sk, size_t size)
index 1d84f91..72a224c 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
 
 cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
 cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
+cfg80211-y += pmsr.o
 cfg80211-$(CONFIG_OF) += of.o
 cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
 cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
index 2db713d..7dc1bbd 100644 (file)
@@ -6,6 +6,7 @@
  *
  * Copyright 2009      Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright 2018       Intel Corporation
  */
 
 #include <linux/export.h>
@@ -747,6 +748,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
        case NL80211_CHAN_WIDTH_20:
                if (!ht_cap->ht_supported)
                        return false;
+               /* fall through */
        case NL80211_CHAN_WIDTH_20_NOHT:
                prohibited_flags |= IEEE80211_CHAN_NO_20MHZ;
                width = 20;
@@ -769,6 +771,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
                cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
                if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
                        return false;
+               /* fall through */
        case NL80211_CHAN_WIDTH_80:
                if (!vht_cap->vht_supported)
                        return false;
index 5bd0105..623dfe5 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright 2006-2010         Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -190,11 +191,25 @@ int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
                return err;
        }
 
+       list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+               if (!wdev->netdev)
+                       continue;
+               nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
+       }
+       nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
+
        wiphy_net_set(&rdev->wiphy, net);
 
        err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev));
        WARN_ON(err);
 
+       nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
+       list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+               if (!wdev->netdev)
+                       continue;
+               nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
+       }
+
        return 0;
 }
 
@@ -664,6 +679,34 @@ int wiphy_register(struct wiphy *wiphy)
                return -EINVAL;
 #endif
 
+       if (WARN_ON(wiphy->pmsr_capa && !wiphy->pmsr_capa->ftm.supported))
+               return -EINVAL;
+
+       if (wiphy->pmsr_capa && wiphy->pmsr_capa->ftm.supported) {
+               if (WARN_ON(!wiphy->pmsr_capa->ftm.asap &&
+                           !wiphy->pmsr_capa->ftm.non_asap))
+                       return -EINVAL;
+               if (WARN_ON(!wiphy->pmsr_capa->ftm.preambles ||
+                           !wiphy->pmsr_capa->ftm.bandwidths))
+                       return -EINVAL;
+               if (WARN_ON(wiphy->pmsr_capa->ftm.preambles &
+                               ~(BIT(NL80211_PREAMBLE_LEGACY) |
+                                 BIT(NL80211_PREAMBLE_HT) |
+                                 BIT(NL80211_PREAMBLE_VHT) |
+                                 BIT(NL80211_PREAMBLE_DMG))))
+                       return -EINVAL;
+               if (WARN_ON(wiphy->pmsr_capa->ftm.bandwidths &
+                               ~(BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                 BIT(NL80211_CHAN_WIDTH_20) |
+                                 BIT(NL80211_CHAN_WIDTH_40) |
+                                 BIT(NL80211_CHAN_WIDTH_80) |
+                                 BIT(NL80211_CHAN_WIDTH_80P80) |
+                                 BIT(NL80211_CHAN_WIDTH_160) |
+                                 BIT(NL80211_CHAN_WIDTH_5) |
+                                 BIT(NL80211_CHAN_WIDTH_10))))
+                       return -EINVAL;
+       }
+
        /*
         * if a wiphy has unsupported modes for regulatory channel enforcement,
         * opt-out of enforcement checking
@@ -1087,6 +1130,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
        ASSERT_RTNL();
        ASSERT_WDEV_LOCK(wdev);
 
+       cfg80211_pmsr_wdev_down(wdev);
+
        switch (wdev->iftype) {
        case NL80211_IFTYPE_ADHOC:
                __cfg80211_leave_ibss(rdev, dev, true);
@@ -1174,6 +1219,9 @@ void cfg80211_init_wdev(struct cfg80211_registered_device *rdev,
        spin_lock_init(&wdev->event_lock);
        INIT_LIST_HEAD(&wdev->mgmt_registrations);
        spin_lock_init(&wdev->mgmt_registrations_lock);
+       INIT_LIST_HEAD(&wdev->pmsr_list);
+       spin_lock_init(&wdev->pmsr_lock);
+       INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
 
        /*
         * We get here also when the interface changes network namespaces,
index c61dbba..c5d6f34 100644 (file)
@@ -3,6 +3,7 @@
  * Wireless configuration interface internals.
  *
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (C) 2018 Intel Corporation
  */
 #ifndef __NET_WIRELESS_CORE_H
 #define __NET_WIRELESS_CORE_H
@@ -530,4 +531,8 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
 
 void cfg80211_cqm_config_free(struct wireless_dev *wdev);
 
+void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid);
+void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev);
+void cfg80211_pmsr_free_wk(struct work_struct *work);
+
 #endif /* __NET_WIRELESS_CORE_H */
index 12b3edf..1615e50 100644 (file)
@@ -272,11 +272,11 @@ void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
 
        p1 = (u8*)(ht_capa);
        p2 = (u8*)(ht_capa_mask);
-       for (i = 0; i<sizeof(*ht_capa); i++)
+       for (i = 0; i < sizeof(*ht_capa); i++)
                p1[i] &= p2[i];
 }
 
-/*  Do a logical ht_capa &= ht_capa_mask.  */
+/*  Do a logical vht_capa &= vht_capa_mask.  */
 void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
                                const struct ieee80211_vht_cap *vht_capa_mask)
 {
index 744b585..5e49492 100644 (file)
@@ -240,7 +240,63 @@ nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = {
                                             .len = U8_MAX },
 };
 
-static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+static const struct nla_policy
+nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
+       [NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG },
+       [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] =
+               NLA_POLICY_MAX(NLA_U8, 15),
+       [NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 },
+       [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),
+       [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 },
+};
+
+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),
+};
+
+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),
+       [NL80211_PMSR_REQ_ATTR_GET_AP_TSF] = { .type = NLA_FLAG },
+};
+
+static const struct nla_policy
+nl80211_psmr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {
+       [NL80211_PMSR_PEER_ATTR_ADDR] = NLA_POLICY_ETH_ADDR,
+       /*
+        * we could specify this again to be the top-level policy,
+        * but that would open us up to recursion problems ...
+        */
+       [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),
+       [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT },
+};
+
+static const struct nla_policy
+nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = {
+       [NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_REJECT },
+       [NL80211_PMSR_ATTR_REPORT_AP_TSF] = { .type = NLA_REJECT },
+       [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),
+};
+
+const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
        [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
                                      .len = 20-1 },
@@ -497,6 +553,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
                .type = NLA_NESTED,
                .validation_data = nl80211_ftm_responder_policy,
        },
+       [NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1),
+       [NL80211_ATTR_PEER_MEASUREMENTS] =
+               NLA_POLICY_NESTED(NL80211_PMSR_FTM_REQ_ATTR_MAX,
+                                 nl80211_pmsr_attr_policy),
 };
 
 /* policy for the key attributes */
@@ -637,9 +697,9 @@ nl80211_packet_pattern_policy[MAX_NL80211_PKTPAT + 1] = {
        [NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 },
 };
 
-static int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
-                                    struct cfg80211_registered_device **rdev,
-                                    struct wireless_dev **wdev)
+int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
+                             struct cfg80211_registered_device **rdev,
+                             struct wireless_dev **wdev)
 {
        int err;
 
@@ -684,8 +744,8 @@ static int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
 }
 
 /* message building helper */
-static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
-                                  int flags, u8 cmd)
+void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
+                    int flags, u8 cmd)
 {
        /* since there is no private header just add the generic one */
        return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd);
@@ -1615,6 +1675,91 @@ static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev,
        return -ENOBUFS;
 }
 
+static int
+nl80211_send_pmsr_ftm_capa(const struct cfg80211_pmsr_capabilities *cap,
+                          struct sk_buff *msg)
+{
+       struct nlattr *ftm;
+
+       if (!cap->ftm.supported)
+               return 0;
+
+       ftm = nla_nest_start(msg, NL80211_PMSR_TYPE_FTM);
+       if (!ftm)
+               return -ENOBUFS;
+
+       if (cap->ftm.asap && nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_ASAP))
+               return -ENOBUFS;
+       if (cap->ftm.non_asap &&
+           nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP))
+               return -ENOBUFS;
+       if (cap->ftm.request_lci &&
+           nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI))
+               return -ENOBUFS;
+       if (cap->ftm.request_civicloc &&
+           nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC))
+               return -ENOBUFS;
+       if (nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES,
+                       cap->ftm.preambles))
+               return -ENOBUFS;
+       if (nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS,
+                       cap->ftm.bandwidths))
+               return -ENOBUFS;
+       if (cap->ftm.max_bursts_exponent >= 0 &&
+           nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT,
+                       cap->ftm.max_bursts_exponent))
+               return -ENOBUFS;
+       if (cap->ftm.max_ftms_per_burst &&
+           nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST,
+                       cap->ftm.max_ftms_per_burst))
+               return -ENOBUFS;
+
+       nla_nest_end(msg, ftm);
+       return 0;
+}
+
+static int nl80211_send_pmsr_capa(struct cfg80211_registered_device *rdev,
+                                 struct sk_buff *msg)
+{
+       const struct cfg80211_pmsr_capabilities *cap = rdev->wiphy.pmsr_capa;
+       struct nlattr *pmsr, *caps;
+
+       if (!cap)
+               return 0;
+
+       /*
+        * we don't need to clean up anything here since the caller
+        * will genlmsg_cancel() if we fail
+        */
+
+       pmsr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS);
+       if (!pmsr)
+               return -ENOBUFS;
+
+       if (nla_put_u32(msg, NL80211_PMSR_ATTR_MAX_PEERS, cap->max_peers))
+               return -ENOBUFS;
+
+       if (cap->report_ap_tsf &&
+           nla_put_flag(msg, NL80211_PMSR_ATTR_REPORT_AP_TSF))
+               return -ENOBUFS;
+
+       if (cap->randomize_mac_addr &&
+           nla_put_flag(msg, NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR))
+               return -ENOBUFS;
+
+       caps = nla_nest_start(msg, NL80211_PMSR_ATTR_TYPE_CAPA);
+       if (!caps)
+               return -ENOBUFS;
+
+       if (nl80211_send_pmsr_ftm_capa(cap, msg))
+               return -ENOBUFS;
+
+       nla_nest_end(msg, caps);
+       nla_nest_end(msg, pmsr);
+
+       return 0;
+}
+
 struct nl80211_dump_wiphy_state {
        s64 filter_wiphy;
        long start;
@@ -1706,6 +1851,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 1:
                if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
                            sizeof(u32) * rdev->wiphy.n_cipher_suites,
@@ -1752,6 +1898,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 2:
                if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
                                        rdev->wiphy.interface_modes))
@@ -1759,6 +1906,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 3:
                nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
                if (!nl_bands)
@@ -1784,6 +1932,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                                state->chan_start++;
                                if (state->split)
                                        break;
+                               /* fall through */
                        default:
                                /* add frequencies */
                                nl_freqs = nla_nest_start(
@@ -1837,6 +1986,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                        state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 4:
                nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
                if (!nl_cmds)
@@ -1863,6 +2013,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 5:
                if (rdev->ops->remain_on_channel &&
                    (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
@@ -1880,6 +2031,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 6:
 #ifdef CONFIG_PM
                if (nl80211_send_wowlan(msg, rdev, state->split))
@@ -1890,6 +2042,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
 #else
                state->split_start++;
 #endif
+               /* fall through */
        case 7:
                if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
                                        rdev->wiphy.software_iftypes))
@@ -1902,6 +2055,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 8:
                if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
                    nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
@@ -2118,6 +2272,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                                goto nla_put_failure;
                }
 
+               state->split_start++;
+               break;
+       case 14:
+               if (nl80211_send_pmsr_capa(rdev, msg))
+                       goto nla_put_failure;
+
                /* done */
                state->split_start = 0;
                break;
@@ -2318,9 +2478,9 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
                wdev->iftype == NL80211_IFTYPE_P2P_GO;
 }
 
-static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
-                                struct genl_info *info,
-                                struct cfg80211_chan_def *chandef)
+int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
+                         struct genl_info *info,
+                         struct cfg80211_chan_def *chandef)
 {
        struct netlink_ext_ack *extack = info->extack;
        struct nlattr **attrs = info->attrs;
@@ -2794,12 +2954,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        return 0;
 }
 
-static inline u64 wdev_id(struct wireless_dev *wdev)
-{
-       return (u64)wdev->identifier |
-              ((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);
-}
-
 static int nl80211_send_chandef(struct sk_buff *msg,
                                const struct cfg80211_chan_def *chandef)
 {
@@ -2832,14 +2986,15 @@ static int nl80211_send_chandef(struct sk_buff *msg,
 
 static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
                              struct cfg80211_registered_device *rdev,
-                             struct wireless_dev *wdev, bool removal)
+                             struct wireless_dev *wdev,
+                             enum nl80211_commands cmd)
 {
        struct net_device *dev = wdev->netdev;
-       u8 cmd = NL80211_CMD_NEW_INTERFACE;
        void *hdr;
 
-       if (removal)
-               cmd = NL80211_CMD_DEL_INTERFACE;
+       WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE &&
+               cmd != NL80211_CMD_DEL_INTERFACE &&
+               cmd != NL80211_CMD_SET_INTERFACE);
 
        hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
        if (!hdr)
@@ -2987,7 +3142,8 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
                        }
                        if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
                                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                              rdev, wdev, false) < 0) {
+                                              rdev, wdev,
+                                              NL80211_CMD_NEW_INTERFACE) < 0) {
                                goto out;
                        }
                        if_idx++;
@@ -3017,7 +3173,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
                return -ENOMEM;
 
        if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
-                              rdev, wdev, false) < 0) {
+                              rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@ -3207,6 +3363,12 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
        if (!err && params.use_4addr != -1)
                dev->ieee80211_ptr->use_4addr = params.use_4addr;
 
+       if (change && !err) {
+               struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+               nl80211_notify_iface(rdev, wdev, NL80211_CMD_SET_INTERFACE);
+       }
+
        return err;
 }
 
@@ -3298,7 +3460,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
-                              rdev, wdev, false) < 0) {
+                              rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@ -4521,8 +4683,7 @@ static int parse_station_flags(struct genl_info *info,
        return 0;
 }
 
-static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
-                                int attr)
+bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
 {
        struct nlattr *rate;
        u32 bitrate;
@@ -4731,6 +4892,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
        PUT_SINFO(LOCAL_PM, local_pm, u32);
        PUT_SINFO(PEER_PM, peer_pm, u32);
        PUT_SINFO(NONPEER_PM, nonpeer_pm, u32);
+       PUT_SINFO(CONNECTED_TO_GATE, connected_to_gate, u8);
 
        if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) {
                bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
@@ -6122,7 +6284,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
            nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
                        cur_params.dot11MeshAwakeWindowDuration) ||
            nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
-                       cur_params.plink_timeout))
+                       cur_params.plink_timeout) ||
+           nla_put_u8(msg, NL80211_MESHCONF_CONNECTED_TO_GATE,
+                      cur_params.dot11MeshConnectedToMeshGate))
                goto nla_put_failure;
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
@@ -6179,6 +6343,7 @@ nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
                                 NL80211_MESH_POWER_MAX),
        [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
        [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 },
+       [NL80211_MESHCONF_CONNECTED_TO_GATE] = NLA_POLICY_RANGE(NLA_U8, 0, 1),
 };
 
 static const struct nla_policy
@@ -6290,6 +6455,9 @@ do {                                                                      \
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, mask,
                                  NL80211_MESHCONF_RSSI_THRESHOLD,
                                  nla_get_s32);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConnectedToMeshGate, mask,
+                                 NL80211_MESHCONF_CONNECTED_TO_GATE,
+                                 nla_get_u8);
        /*
         * Check HT operation mode based on
         * IEEE 802.11-2016 9.4.2.57 HT Operation element.
@@ -6855,8 +7023,8 @@ static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy,
        return 0;
 }
 
-static int nl80211_parse_random_mac(struct nlattr **attrs,
-                                   u8 *mac_addr, u8 *mac_addr_mask)
+int nl80211_parse_random_mac(struct nlattr **attrs,
+                            u8 *mac_addr, u8 *mac_addr_mask)
 {
        int i;
 
@@ -7822,6 +7990,60 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        return err;
 }
 
+static int nl80211_notify_radar_detection(struct sk_buff *skb,
+                                         struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_chan_def chandef;
+       enum nl80211_dfs_regions dfs_region;
+       int err;
+
+       dfs_region = reg_get_dfs_region(wiphy);
+       if (dfs_region == NL80211_DFS_UNSET) {
+               GENL_SET_ERR_MSG(info,
+                                "DFS Region is not set. Unexpected Radar indication");
+               return -EINVAL;
+       }
+
+       err = nl80211_parse_chandef(rdev, info, &chandef);
+       if (err) {
+               GENL_SET_ERR_MSG(info, "Unable to extract chandef info");
+               return err;
+       }
+
+       err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype);
+       if (err < 0) {
+               GENL_SET_ERR_MSG(info, "chandef is invalid");
+               return err;
+       }
+
+       if (err == 0) {
+               GENL_SET_ERR_MSG(info,
+                                "Unexpected Radar indication for chandef/iftype");
+               return -EINVAL;
+       }
+
+       /* Do not process this notification if radar is already detected
+        * by kernel on this channel, and return success.
+        */
+       if (chandef.chan->dfs_state == NL80211_DFS_UNAVAILABLE)
+               return 0;
+
+       cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_UNAVAILABLE);
+
+       cfg80211_sched_dfs_chan_update(rdev);
+
+       memcpy(&rdev->radar_chandef, &chandef, sizeof(chandef));
+
+       /* Propagate this notification to other radios as well */
+       queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
+
+       return 0;
+}
+
 static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -7870,6 +8092,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        }
 
        memset(&params, 0, sizeof(params));
+       params.beacon_csa.ftm_responder = -1;
 
        if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
            !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT])
@@ -8929,8 +9152,10 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) {
                int r = validate_pae_over_nl80211(rdev, info);
 
-               if (r < 0)
+               if (r < 0) {
+                       kzfree(connkeys);
                        return r;
+               }
 
                ibss.control_port_over_nl80211 = true;
        }
@@ -13898,6 +14123,22 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_PEER_MEASUREMENT_START,
+               .doit = nl80211_pmsr_start,
+               .policy = nl80211_policy,
+               .flags = GENL_UNS_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_NOTIFY_RADAR,
+               .doit = nl80211_notify_radar_detection,
+               .policy = nl80211_policy,
+               .flags = GENL_UNS_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_family nl80211_fam __ro_after_init = {
@@ -13945,15 +14186,11 @@ void nl80211_notify_iface(struct cfg80211_registered_device *rdev,
 {
        struct sk_buff *msg;
 
-       WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE &&
-               cmd != NL80211_CMD_DEL_INTERFACE);
-
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
-       if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev,
-                              cmd == NL80211_CMD_DEL_INTERFACE) < 0) {
+       if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, cmd) < 0) {
                nlmsg_free(msg);
                return;
        }
@@ -14572,7 +14809,8 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
 }
 
 void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
-                                       const u8* ie, u8 ie_len, gfp_t gfp)
+                                       const u8 *ie, u8 ie_len,
+                                       int sig_dbm, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
@@ -14598,7 +14836,9 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
            nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
            (ie_len && ie &&
-            nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
+            nla_put(msg, NL80211_ATTR_IE, ie_len, ie)) ||
+           (sig_dbm &&
+            nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)))
                goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
@@ -15881,6 +16121,8 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
                        } else if (wdev->conn_owner_nlportid == notify->portid) {
                                schedule_work(&wdev->disconnect_wk);
                        }
+
+                       cfg80211_release_pmsr(wdev, notify->portid);
                }
 
                spin_lock_bh(&rdev->beacon_registrations_lock);
index 79e47fe..531c82d 100644 (file)
@@ -1,4 +1,8 @@
 /* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Portions of this file
+ * Copyright (C) 2018 Intel Corporation
+ */
 #ifndef __NET_WIRELESS_NL80211_H
 #define __NET_WIRELESS_NL80211_H
 
@@ -6,6 +10,30 @@
 
 int nl80211_init(void);
 void nl80211_exit(void);
+
+extern const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];
+
+void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
+                    int flags, u8 cmd);
+bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
+                         int attr);
+
+static inline u64 wdev_id(struct wireless_dev *wdev)
+{
+       return (u64)wdev->identifier |
+              ((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);
+}
+
+int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
+                             struct cfg80211_registered_device **rdev,
+                             struct wireless_dev **wdev);
+
+int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
+                         struct genl_info *info,
+                         struct cfg80211_chan_def *chandef);
+int nl80211_parse_random_mac(struct nlattr **attrs,
+                            u8 *mac_addr, u8 *mac_addr_mask);
+
 void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
                          enum nl80211_commands cmd);
 void nl80211_notify_iface(struct cfg80211_registered_device *rdev,
@@ -95,4 +123,8 @@ void nl80211_send_ap_stopped(struct wireless_dev *wdev);
 
 void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
 
+/* peer measurement */
+int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info);
+int nl80211_pmsr_dump_results(struct sk_buff *skb, struct netlink_callback *cb);
+
 #endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c
new file mode 100644 (file)
index 0000000..de92867
--- /dev/null
@@ -0,0 +1,590 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Intel Corporation
+ */
+#ifndef __PMSR_H
+#define __PMSR_H
+#include <net/cfg80211.h>
+#include "core.h"
+#include "nl80211.h"
+#include "rdev-ops.h"
+
+static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
+                         struct nlattr *ftmreq,
+                         struct cfg80211_pmsr_request_peer *out,
+                         struct genl_info *info)
+{
+       const struct cfg80211_pmsr_capabilities *capa = rdev->wiphy.pmsr_capa;
+       struct nlattr *tb[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1];
+       u32 preamble = NL80211_PREAMBLE_DMG; /* only optional in DMG */
+
+       /* validate existing data */
+       if (!(rdev->wiphy.pmsr_capa->ftm.bandwidths & BIT(out->chandef.width))) {
+               NL_SET_ERR_MSG(info->extack, "FTM: unsupported bandwidth");
+               return -EINVAL;
+       }
+
+       /* no validation needed - was already done via nested policy */
+       nla_parse_nested(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq, NULL, NULL);
+
+       if (tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE])
+               preamble = nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]);
+
+       /* set up values - struct is 0-initialized */
+       out->ftm.requested = true;
+
+       switch (out->chandef.chan->band) {
+       case NL80211_BAND_60GHZ:
+               /* optional */
+               break;
+       default:
+               if (!tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) {
+                       NL_SET_ERR_MSG(info->extack,
+                                      "FTM: must specify preamble");
+                       return -EINVAL;
+               }
+       }
+
+       if (!(capa->ftm.preambles & BIT(preamble))) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
+                                   "FTM: invalid preamble");
+               return -EINVAL;
+       }
+
+       out->ftm.preamble = preamble;
+
+       out->ftm.burst_period = 0;
+       if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD])
+               out->ftm.burst_period =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]);
+
+       out->ftm.asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP];
+       if (out->ftm.asap && !capa->ftm.asap) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP],
+                                   "FTM: ASAP mode not supported");
+               return -EINVAL;
+       }
+
+       if (!out->ftm.asap && !capa->ftm.non_asap) {
+               NL_SET_ERR_MSG(info->extack,
+                              "FTM: non-ASAP mode not supported");
+               return -EINVAL;
+       }
+
+       out->ftm.num_bursts_exp = 0;
+       if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP])
+               out->ftm.num_bursts_exp =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]);
+
+       if (capa->ftm.max_bursts_exponent >= 0 &&
+           out->ftm.num_bursts_exp > capa->ftm.max_bursts_exponent) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP],
+                                   "FTM: max NUM_BURSTS_EXP must be set lower than the device limit");
+               return -EINVAL;
+       }
+
+       out->ftm.burst_duration = 15;
+       if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION])
+               out->ftm.burst_duration =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]);
+
+       out->ftm.ftms_per_burst = 0;
+       if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST])
+               out->ftm.ftms_per_burst =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]);
+
+       if (capa->ftm.max_ftms_per_burst &&
+           (out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst ||
+            out->ftm.ftms_per_burst == 0)) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
+                                   "FTM: FTMs per burst must be set lower than the device limit but non-zero");
+               return -EINVAL;
+       }
+
+       out->ftm.ftmr_retries = 3;
+       if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES])
+               out->ftm.ftmr_retries =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]);
+
+       out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI];
+       if (out->ftm.request_lci && !capa->ftm.request_lci) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI],
+                                   "FTM: LCI request not supported");
+       }
+
+       out->ftm.request_civicloc =
+               !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC];
+       if (out->ftm.request_civicloc && !capa->ftm.request_civicloc) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC],
+                           "FTM: civic location request not supported");
+       }
+
+       return 0;
+}
+
+static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
+                          struct nlattr *peer,
+                          struct cfg80211_pmsr_request_peer *out,
+                          struct genl_info *info)
+{
+       struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1];
+       struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1];
+       struct nlattr *treq;
+       int err, rem;
+
+       /* no validation needed - was already done via nested policy */
+       nla_parse_nested(tb, NL80211_PMSR_PEER_ATTR_MAX, peer, NULL, NULL);
+
+       if (!tb[NL80211_PMSR_PEER_ATTR_ADDR] ||
+           !tb[NL80211_PMSR_PEER_ATTR_CHAN] ||
+           !tb[NL80211_PMSR_PEER_ATTR_REQ]) {
+               NL_SET_ERR_MSG_ATTR(info->extack, peer,
+                                   "insufficient peer data");
+               return -EINVAL;
+       }
+
+       memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN);
+
+       /* reuse info->attrs */
+       memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1));
+       /* need to validate here, we don't want to have validation recursion */
+       err = nla_parse_nested(info->attrs, NL80211_ATTR_MAX,
+                              tb[NL80211_PMSR_PEER_ATTR_CHAN],
+                              nl80211_policy, info->extack);
+       if (err)
+               return err;
+
+       err = nl80211_parse_chandef(rdev, info, &out->chandef);
+       if (err)
+               return err;
+
+       /* no validation needed - was already done via nested policy */
+       nla_parse_nested(req, NL80211_PMSR_REQ_ATTR_MAX,
+                        tb[NL80211_PMSR_PEER_ATTR_REQ],
+                        NULL, NULL);
+
+       if (!req[NL80211_PMSR_REQ_ATTR_DATA]) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_PEER_ATTR_REQ],
+                                   "missing request type/data");
+               return -EINVAL;
+       }
+
+       if (req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF])
+               out->report_ap_tsf = true;
+
+       if (out->report_ap_tsf && !rdev->wiphy.pmsr_capa->report_ap_tsf) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF],
+                                   "reporting AP TSF is not supported");
+               return -EINVAL;
+       }
+
+       nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) {
+               switch (nla_type(treq)) {
+               case NL80211_PMSR_TYPE_FTM:
+                       err = pmsr_parse_ftm(rdev, treq, out, info);
+                       break;
+               default:
+                       NL_SET_ERR_MSG_ATTR(info->extack, treq,
+                                           "unsupported measurement type");
+                       err = -EINVAL;
+               }
+       }
+
+       if (err)
+               return err;
+
+       return 0;
+}
+
+int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nlattr *reqattr = info->attrs[NL80211_ATTR_PEER_MEASUREMENTS];
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev = info->user_ptr[1];
+       struct cfg80211_pmsr_request *req;
+       struct nlattr *peers, *peer;
+       int count, rem, err, idx;
+
+       if (!rdev->wiphy.pmsr_capa)
+               return -EOPNOTSUPP;
+
+       if (!reqattr)
+               return -EINVAL;
+
+       peers = nla_find(nla_data(reqattr), nla_len(reqattr),
+                        NL80211_PMSR_ATTR_PEERS);
+       if (!peers)
+               return -EINVAL;
+
+       count = 0;
+       nla_for_each_nested(peer, peers, rem) {
+               count++;
+
+               if (count > rdev->wiphy.pmsr_capa->max_peers) {
+                       NL_SET_ERR_MSG_ATTR(info->extack, peer,
+                                           "Too many peers used");
+                       return -EINVAL;
+               }
+       }
+
+       req = kzalloc(struct_size(req, peers, count), GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       if (info->attrs[NL80211_ATTR_TIMEOUT])
+               req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]);
+
+       if (info->attrs[NL80211_ATTR_MAC]) {
+               if (!rdev->wiphy.pmsr_capa->randomize_mac_addr) {
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           info->attrs[NL80211_ATTR_MAC],
+                                           "device cannot randomize MAC address");
+                       err = -EINVAL;
+                       goto out_err;
+               }
+
+               err = nl80211_parse_random_mac(info->attrs, req->mac_addr,
+                                              req->mac_addr_mask);
+               if (err)
+                       goto out_err;
+       } else {
+               memcpy(req->mac_addr, nla_data(info->attrs[NL80211_ATTR_MAC]),
+                      ETH_ALEN);
+               memset(req->mac_addr_mask, 0xff, ETH_ALEN);
+       }
+
+       idx = 0;
+       nla_for_each_nested(peer, peers, rem) {
+               /* NB: this reuses info->attrs, but we no longer need it */
+               err = pmsr_parse_peer(rdev, peer, &req->peers[idx], info);
+               if (err)
+                       goto out_err;
+               idx++;
+       }
+
+       req->n_peers = count;
+       req->cookie = cfg80211_assign_cookie(rdev);
+
+       err = rdev_start_pmsr(rdev, wdev, req);
+       if (err)
+               goto out_err;
+
+       list_add_tail(&req->list, &wdev->pmsr_list);
+
+       nl_set_extack_cookie_u64(info->extack, req->cookie);
+       return 0;
+out_err:
+       kfree(req);
+       return err;
+}
+
+void cfg80211_pmsr_complete(struct wireless_dev *wdev,
+                           struct cfg80211_pmsr_request *req,
+                           gfp_t gfp)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+
+       trace_cfg80211_pmsr_complete(wdev->wiphy, wdev, req->cookie);
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               goto free_request;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0,
+                            NL80211_CMD_PEER_MEASUREMENT_COMPLETE);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+           nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+                             NL80211_ATTR_PAD))
+               goto free_msg;
+
+       if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie,
+                             NL80211_ATTR_PAD))
+               goto free_msg;
+
+       genlmsg_end(msg, hdr);
+       genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid);
+       goto free_request;
+free_msg:
+       nlmsg_free(msg);
+free_request:
+       spin_lock_bh(&wdev->pmsr_lock);
+       list_del(&req->list);
+       spin_unlock_bh(&wdev->pmsr_lock);
+       kfree(req);
+}
+EXPORT_SYMBOL_GPL(cfg80211_pmsr_complete);
+
+static int nl80211_pmsr_send_ftm_res(struct sk_buff *msg,
+                                    struct cfg80211_pmsr_result *res)
+{
+       if (res->status == NL80211_PMSR_STATUS_FAILURE) {
+               if (nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON,
+                               res->ftm.failure_reason))
+                       goto error;
+
+               if (res->ftm.failure_reason ==
+                       NL80211_PMSR_FTM_FAILURE_PEER_BUSY &&
+                   res->ftm.busy_retry_time &&
+                   nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME,
+                               res->ftm.busy_retry_time))
+                       goto error;
+
+               return 0;
+       }
+
+#define PUT(tp, attr, val)                                             \
+       do {                                                            \
+               if (nla_put_##tp(msg,                                   \
+                                NL80211_PMSR_FTM_RESP_ATTR_##attr,     \
+                                res->ftm.val))                         \
+                       goto error;                                     \
+       } while (0)
+
+#define PUTOPT(tp, attr, val)                                          \
+       do {                                                            \
+               if (res->ftm.val##_valid)                               \
+                       PUT(tp, attr, val);                             \
+       } while (0)
+
+#define PUT_U64(attr, val)                                             \
+       do {                                                            \
+               if (nla_put_u64_64bit(msg,                              \
+                                     NL80211_PMSR_FTM_RESP_ATTR_##attr,\
+                                     res->ftm.val,                     \
+                                     NL80211_PMSR_FTM_RESP_ATTR_PAD))  \
+                       goto error;                                     \
+       } while (0)
+
+#define PUTOPT_U64(attr, val)                                          \
+       do {                                                            \
+               if (res->ftm.val##_valid)                               \
+                       PUT_U64(attr, val);                             \
+       } while (0)
+
+       if (res->ftm.burst_index >= 0)
+               PUT(u32, BURST_INDEX, burst_index);
+       PUTOPT(u32, NUM_FTMR_ATTEMPTS, num_ftmr_attempts);
+       PUTOPT(u32, NUM_FTMR_SUCCESSES, num_ftmr_successes);
+       PUT(u8, NUM_BURSTS_EXP, num_bursts_exp);
+       PUT(u8, BURST_DURATION, burst_duration);
+       PUT(u8, FTMS_PER_BURST, ftms_per_burst);
+       PUTOPT(s32, RSSI_AVG, rssi_avg);
+       PUTOPT(s32, RSSI_SPREAD, rssi_spread);
+       if (res->ftm.tx_rate_valid &&
+           !nl80211_put_sta_rate(msg, &res->ftm.tx_rate,
+                                 NL80211_PMSR_FTM_RESP_ATTR_TX_RATE))
+               goto error;
+       if (res->ftm.rx_rate_valid &&
+           !nl80211_put_sta_rate(msg, &res->ftm.rx_rate,
+                                 NL80211_PMSR_FTM_RESP_ATTR_RX_RATE))
+               goto error;
+       PUTOPT_U64(RTT_AVG, rtt_avg);
+       PUTOPT_U64(RTT_VARIANCE, rtt_variance);
+       PUTOPT_U64(RTT_SPREAD, rtt_spread);
+       PUTOPT_U64(DIST_AVG, dist_avg);
+       PUTOPT_U64(DIST_VARIANCE, dist_variance);
+       PUTOPT_U64(DIST_SPREAD, dist_spread);
+       if (res->ftm.lci && res->ftm.lci_len &&
+           nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI,
+                   res->ftm.lci_len, res->ftm.lci))
+               goto error;
+       if (res->ftm.civicloc && res->ftm.civicloc_len &&
+           nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC,
+                   res->ftm.civicloc_len, res->ftm.civicloc))
+               goto error;
+#undef PUT
+#undef PUTOPT
+#undef PUT_U64
+#undef PUTOPT_U64
+
+       return 0;
+error:
+       return -ENOSPC;
+}
+
+static int nl80211_pmsr_send_result(struct sk_buff *msg,
+                                   struct cfg80211_pmsr_result *res)
+{
+       struct nlattr *pmsr, *peers, *peer, *resp, *data, *typedata;
+
+       pmsr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS);
+       if (!pmsr)
+               goto error;
+
+       peers = nla_nest_start(msg, NL80211_PMSR_ATTR_PEERS);
+       if (!peers)
+               goto error;
+
+       peer = nla_nest_start(msg, 1);
+       if (!peer)
+               goto error;
+
+       if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, res->addr))
+               goto error;
+
+       resp = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_RESP);
+       if (!resp)
+               goto error;
+
+       if (nla_put_u32(msg, NL80211_PMSR_RESP_ATTR_STATUS, res->status) ||
+           nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_HOST_TIME,
+                             res->host_time, NL80211_PMSR_RESP_ATTR_PAD))
+               goto error;
+
+       if (res->ap_tsf_valid &&
+           nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_AP_TSF,
+                             res->host_time, NL80211_PMSR_RESP_ATTR_PAD))
+               goto error;
+
+       if (res->final && nla_put_flag(msg, NL80211_PMSR_RESP_ATTR_FINAL))
+               goto error;
+
+       data = nla_nest_start(msg, NL80211_PMSR_RESP_ATTR_DATA);
+       if (!data)
+               goto error;
+
+       typedata = nla_nest_start(msg, res->type);
+       if (!typedata)
+               goto error;
+
+       switch (res->type) {
+       case NL80211_PMSR_TYPE_FTM:
+               if (nl80211_pmsr_send_ftm_res(msg, res))
+                       goto error;
+               break;
+       default:
+               WARN_ON(1);
+       }
+
+       nla_nest_end(msg, typedata);
+       nla_nest_end(msg, data);
+       nla_nest_end(msg, resp);
+       nla_nest_end(msg, peer);
+       nla_nest_end(msg, peers);
+       nla_nest_end(msg, pmsr);
+
+       return 0;
+error:
+       return -ENOSPC;
+}
+
+void cfg80211_pmsr_report(struct wireless_dev *wdev,
+                         struct cfg80211_pmsr_request *req,
+                         struct cfg80211_pmsr_result *result,
+                         gfp_t gfp)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+       int err;
+
+       trace_cfg80211_pmsr_report(wdev->wiphy, wdev, req->cookie,
+                                  result->addr);
+
+       /*
+        * Currently, only variable items are LCI and civic location,
+        * both of which are reasonably short so we don't need to
+        * worry about them here for the allocation.
+        */
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PEER_MEASUREMENT_RESULT);
+       if (!hdr)
+               goto free;
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+           nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+                             NL80211_ATTR_PAD))
+               goto free;
+
+       if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie,
+                             NL80211_ATTR_PAD))
+               goto free;
+
+       err = nl80211_pmsr_send_result(msg, result);
+       if (err) {
+               pr_err_ratelimited("peer measurement result: message didn't fit!");
+               goto free;
+       }
+
+       genlmsg_end(msg, hdr);
+       genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid);
+       return;
+free:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL_GPL(cfg80211_pmsr_report);
+
+void cfg80211_pmsr_free_wk(struct work_struct *work)
+{
+       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);
+
+       spin_lock_bh(&wdev->pmsr_lock);
+       list_for_each_entry_safe(req, tmp, &wdev->pmsr_list, list) {
+               if (req->nl_portid)
+                       continue;
+               list_move_tail(&req->list, &free_list);
+       }
+       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_wdev_down(struct wireless_dev *wdev)
+{
+       struct cfg80211_pmsr_request *req;
+       bool found = false;
+
+       spin_lock_bh(&wdev->pmsr_lock);
+       list_for_each_entry(req, &wdev->pmsr_list, list) {
+               found = true;
+               req->nl_portid = 0;
+       }
+       spin_unlock_bh(&wdev->pmsr_lock);
+
+       if (found)
+               schedule_work(&wdev->pmsr_free_wk);
+       flush_work(&wdev->pmsr_free_wk);
+       WARN_ON(!list_empty(&wdev->pmsr_list));
+}
+
+void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid)
+{
+       struct cfg80211_pmsr_request *req;
+
+       spin_lock_bh(&wdev->pmsr_lock);
+       list_for_each_entry(req, &wdev->pmsr_list, list) {
+               if (req->nl_portid == portid) {
+                       req->nl_portid = 0;
+                       schedule_work(&wdev->pmsr_free_wk);
+               }
+       }
+       spin_unlock_bh(&wdev->pmsr_lock);
+}
+
+#endif /* __PMSR_H */
index 51380b5..5cb48d1 100644 (file)
@@ -1247,4 +1247,29 @@ rdev_get_ftm_responder_stats(struct cfg80211_registered_device *rdev,
        return ret;
 }
 
+static inline int
+rdev_start_pmsr(struct cfg80211_registered_device *rdev,
+               struct wireless_dev *wdev,
+               struct cfg80211_pmsr_request *request)
+{
+       int ret = -EOPNOTSUPP;
+
+       trace_rdev_start_pmsr(&rdev->wiphy, wdev, request->cookie);
+       if (rdev->ops->start_pmsr)
+               ret = rdev->ops->start_pmsr(&rdev->wiphy, wdev, request);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
+
+static inline void
+rdev_abort_pmsr(struct cfg80211_registered_device *rdev,
+               struct wireless_dev *wdev,
+               struct cfg80211_pmsr_request *request)
+{
+       trace_rdev_abort_pmsr(&rdev->wiphy, wdev, request->cookie);
+       if (rdev->ops->abort_pmsr)
+               rdev->ops->abort_pmsr(&rdev->wiphy, wdev, request);
+       trace_rdev_return_void(&rdev->wiphy);
+}
+
 #endif /* __CFG80211_RDEV_OPS */
index d0e7472..5123667 100644 (file)
@@ -1183,7 +1183,7 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
        switch (ftype) {
        case CFG80211_BSS_FTYPE_BEACON:
                ies->from_beacon = true;
-               /* fall through to assign */
+               /* fall through */
        case CFG80211_BSS_FTYPE_UNKNOWN:
                rcu_assign_pointer(tmp.pub.beacon_ies, ies);
                break;
index d536b07..f741d83 100644 (file)
@@ -642,11 +642,15 @@ static bool cfg80211_is_all_idle(void)
         * All devices must be idle as otherwise if you are actively
         * scanning some new beacon hints could be learned and would
         * count as new regulatory hints.
+        * Also if there is any other active beaconing interface we
+        * need not issue a disconnect hint and reset any info such
+        * as chan dfs state, etc.
         */
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
                        wdev_lock(wdev);
-                       if (wdev->conn || wdev->current_bss)
+                       if (wdev->conn || wdev->current_bss ||
+                           cfg80211_beaconing_iface_active(wdev))
                                is_all_idle = false;
                        wdev_unlock(wdev);
                }
@@ -1171,6 +1175,8 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
 
        cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
                                  rdev->wiphy.ht_capa_mod_mask);
+       cfg80211_oper_and_vht_capa(&connect->vht_capa_mask,
+                                  rdev->wiphy.vht_capa_mod_mask);
 
        if (connkeys && connkeys->def >= 0) {
                int idx;
index c6a9446..44b2ce1 100644 (file)
@@ -361,6 +361,24 @@ DECLARE_EVENT_CLASS(wiphy_wdev_evt,
        TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
 );
 
+DECLARE_EVENT_CLASS(wiphy_wdev_cookie_evt,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
+       TP_ARGS(wiphy, wdev, cookie),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               WDEV_ENTRY
+               __field(u64, cookie)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WDEV_ASSIGN;
+               __entry->cookie = cookie;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %lld",
+                 WIPHY_PR_ARG, WDEV_PR_ARG,
+                 (unsigned long long)__entry->cookie)
+);
+
 DEFINE_EVENT(wiphy_wdev_evt, rdev_return_wdev,
        TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
        TP_ARGS(wiphy, wdev)
@@ -770,9 +788,9 @@ DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_set_wds_peer,
 );
 
 TRACE_EVENT(rdev_dump_station,
-       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx,
                 u8 *mac),
-       TP_ARGS(wiphy, netdev, idx, mac),
+       TP_ARGS(wiphy, netdev, _idx, mac),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                NETDEV_ENTRY
@@ -783,7 +801,7 @@ TRACE_EVENT(rdev_dump_station,
                WIPHY_ASSIGN;
                NETDEV_ASSIGN;
                MAC_ASSIGN(sta_mac, mac);
-               __entry->idx = idx;
+               __entry->idx = _idx;
        ),
        TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d",
                  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
@@ -847,9 +865,9 @@ DEFINE_EVENT(mpath_evt, rdev_get_mpath,
 );
 
 TRACE_EVENT(rdev_dump_mpath,
-       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx,
                 u8 *dst, u8 *next_hop),
-       TP_ARGS(wiphy, netdev, idx, dst, next_hop),
+       TP_ARGS(wiphy, netdev, _idx, dst, next_hop),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                NETDEV_ENTRY
@@ -862,7 +880,7 @@ TRACE_EVENT(rdev_dump_mpath,
                NETDEV_ASSIGN;
                MAC_ASSIGN(dst, dst);
                MAC_ASSIGN(next_hop, next_hop);
-               __entry->idx = idx;
+               __entry->idx = _idx;
        ),
        TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
                  MAC_PR_FMT ", next hop: " MAC_PR_FMT,
@@ -892,9 +910,9 @@ TRACE_EVENT(rdev_get_mpp,
 );
 
 TRACE_EVENT(rdev_dump_mpp,
-       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx,
                 u8 *dst, u8 *mpp),
-       TP_ARGS(wiphy, netdev, idx, mpp, dst),
+       TP_ARGS(wiphy, netdev, _idx, mpp, dst),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                NETDEV_ENTRY
@@ -907,7 +925,7 @@ TRACE_EVENT(rdev_dump_mpp,
                NETDEV_ASSIGN;
                MAC_ASSIGN(dst, dst);
                MAC_ASSIGN(mpp, mpp);
-               __entry->idx = idx;
+               __entry->idx = _idx;
        ),
        TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
                  MAC_PR_FMT ", mpp: " MAC_PR_FMT,
@@ -1673,8 +1691,8 @@ TRACE_EVENT(rdev_tdls_mgmt,
 );
 
 TRACE_EVENT(rdev_dump_survey,
-       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx),
-       TP_ARGS(wiphy, netdev, idx),
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx),
+       TP_ARGS(wiphy, netdev, _idx),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                NETDEV_ENTRY
@@ -1683,7 +1701,7 @@ TRACE_EVENT(rdev_dump_survey,
        TP_fast_assign(
                WIPHY_ASSIGN;
                NETDEV_ASSIGN;
-               __entry->idx = idx;
+               __entry->idx = _idx;
        ),
        TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d",
                  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx)
@@ -2502,6 +2520,16 @@ TRACE_EVENT(rdev_get_ftm_responder_stats,
                __entry->out_of_window)
 );
 
+DEFINE_EVENT(wiphy_wdev_cookie_evt, rdev_start_pmsr,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
+       TP_ARGS(wiphy, wdev, cookie)
+);
+
+DEFINE_EVENT(wiphy_wdev_cookie_evt, rdev_abort_pmsr,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
+       TP_ARGS(wiphy, wdev, cookie)
+);
+
 /*************************************************************
  *          cfg80211 exported functions traces              *
  *************************************************************/
@@ -3294,6 +3322,46 @@ TRACE_EVENT(cfg80211_stop_iface,
        TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT,
                  WIPHY_PR_ARG, WDEV_PR_ARG)
 );
+
+TRACE_EVENT(cfg80211_pmsr_report,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+                u64 cookie, const u8 *addr),
+       TP_ARGS(wiphy, wdev, cookie, addr),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               WDEV_ENTRY
+               __field(u64, cookie)
+               MAC_ENTRY(addr)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WDEV_ASSIGN;
+               __entry->cookie = cookie;
+               MAC_ASSIGN(addr, addr);
+       ),
+       TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie:%lld, " MAC_PR_FMT,
+                 WIPHY_PR_ARG, WDEV_PR_ARG,
+                 (unsigned long long)__entry->cookie,
+                 MAC_PR_ARG(addr))
+);
+
+TRACE_EVENT(cfg80211_pmsr_complete,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
+       TP_ARGS(wiphy, wdev, cookie),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               WDEV_ENTRY
+               __field(u64, cookie)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WDEV_ASSIGN;
+               __entry->cookie = cookie;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie:%lld",
+                 WIPHY_PR_ARG, WDEV_PR_ARG,
+                 (unsigned long long)__entry->cookie)
+);
 #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH
index ef14d80..cd48cdd 100644 (file)
@@ -1421,6 +1421,8 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
                                                          ies[pos + ext],
                                                          ext == 2))
                                        pos = skip_ie(ies, ielen, pos);
+                               else
+                                       break;
                        }
                } else {
                        pos = skip_ie(ies, ielen, pos);
@@ -2013,33 +2015,32 @@ int ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *cap,
        case IEEE80211_VHT_CHANWIDTH_160MHZ:
                if (supp_width == 0 &&
                    (ext_nss_bw == 1 || ext_nss_bw == 2))
-                       return DIV_ROUND_UP(max_vht_nss, 2);
+                       return max_vht_nss / 2;
                if (supp_width == 0 &&
                    ext_nss_bw == 3)
-                       return DIV_ROUND_UP(3 * max_vht_nss, 4);
+                       return (3 * max_vht_nss) / 4;
                if (supp_width == 1 &&
                    ext_nss_bw == 3)
                        return 2 * max_vht_nss;
                break;
        case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
-               if (supp_width == 0 &&
-                   (ext_nss_bw == 1 || ext_nss_bw == 2))
+               if (supp_width == 0 && ext_nss_bw == 1)
                        return 0; /* not possible */
                if (supp_width == 0 &&
                    ext_nss_bw == 2)
-                       return DIV_ROUND_UP(max_vht_nss, 2);
+                       return max_vht_nss / 2;
                if (supp_width == 0 &&
                    ext_nss_bw == 3)
-                       return DIV_ROUND_UP(3 * max_vht_nss, 4);
+                       return (3 * max_vht_nss) / 4;
                if (supp_width == 1 &&
                    ext_nss_bw == 0)
                        return 0; /* not possible */
                if (supp_width == 1 &&
                    ext_nss_bw == 1)
-                       return DIV_ROUND_UP(max_vht_nss, 2);
+                       return max_vht_nss / 2;
                if (supp_width == 1 &&
                    ext_nss_bw == 2)
-                       return DIV_ROUND_UP(3 * max_vht_nss, 4);
+                       return (3 * max_vht_nss) / 4;
                break;
        }
 
index d49aa79..5121729 100644 (file)
@@ -100,7 +100,7 @@ int x25_parse_address_block(struct sk_buff *skb,
        }
 
        len = *skb->data;
-       needed = 1 + (len >> 4) + (len & 0x0f);
+       needed = 1 + ((len >> 4) + (len & 0x0f) + 1) / 2;
 
        if (!pskb_may_pull(skb, needed)) {
                /* packet is too short to hold the addresses it claims
@@ -288,7 +288,7 @@ static struct sock *x25_find_listener(struct x25_address *addr,
        sk_for_each(s, &x25_list)
                if ((!strcmp(addr->x25_addr,
                        x25_sk(s)->source_addr.x25_addr) ||
-                               !strcmp(addr->x25_addr,
+                               !strcmp(x25_sk(s)->source_addr.x25_addr,
                                        null_x25_address.x25_addr)) &&
                                        s->sk_state == TCP_LISTEN) {
                        /*
@@ -688,11 +688,15 @@ static int x25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                goto out;
        }
 
-       len = strlen(addr->sx25_addr.x25_addr);
-       for (i = 0; i < len; i++) {
-               if (!isdigit(addr->sx25_addr.x25_addr[i])) {
-                       rc = -EINVAL;
-                       goto out;
+       /* check for the null_x25_address */
+       if (strcmp(addr->sx25_addr.x25_addr, null_x25_address.x25_addr)) {
+
+               len = strlen(addr->sx25_addr.x25_addr);
+               for (i = 0; i < len; i++) {
+                       if (!isdigit(addr->sx25_addr.x25_addr[i])) {
+                               rc = -EINVAL;
+                               goto out;
+                       }
                }
        }
 
index 3c12cae..afb2622 100644 (file)
@@ -142,6 +142,15 @@ static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametyp
                        sk->sk_state_change(sk);
                break;
        }
+       case X25_CALL_REQUEST:
+               /* call collision */
+               x25->causediag.cause      = 0x01;
+               x25->causediag.diagnostic = 0x48;
+
+               x25_write_internal(sk, X25_CLEAR_REQUEST);
+               x25_disconnect(sk, EISCONN, 0x01, 0x48);
+               break;
+
        case X25_CLEAR_REQUEST:
                if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2))
                        goto out_clear;
index 07156f4..a032684 100644 (file)
@@ -366,6 +366,7 @@ static int xsk_release(struct socket *sock)
 
        xskq_destroy(xs->rx);
        xskq_destroy(xs->tx);
+       xdp_put_umem(xs->umem);
 
        sock_orphan(sk);
        sock->sk = NULL;
@@ -713,18 +714,6 @@ static const struct proto_ops xsk_proto_ops = {
        .sendpage       = sock_no_sendpage,
 };
 
-static void xsk_destruct(struct sock *sk)
-{
-       struct xdp_sock *xs = xdp_sk(sk);
-
-       if (!sock_flag(sk, SOCK_DEAD))
-               return;
-
-       xdp_put_umem(xs->umem);
-
-       sk_refcnt_debug_dec(sk);
-}
-
 static int xsk_create(struct net *net, struct socket *sock, int protocol,
                      int kern)
 {
@@ -751,9 +740,6 @@ static int xsk_create(struct net *net, struct socket *sock, int protocol,
 
        sk->sk_family = PF_XDP;
 
-       sk->sk_destruct = xsk_destruct;
-       sk_refcnt_debug_inc(sk);
-
        sock_set_flag(sk, SOCK_RCU_FREE);
 
        xs = xdp_sk(sk);
index 140270a..5d43aaa 100644 (file)
@@ -5,6 +5,7 @@ config XFRM
        bool
        depends on NET
        select GRO_CELLS
+       select SKB_EXTENSIONS
 
 config XFRM_OFFLOAD
        bool
index 144c137..b8736f5 100644 (file)
@@ -32,6 +32,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
        struct softnet_data *sd;
        netdev_features_t esp_features = features;
        struct xfrm_offload *xo = xfrm_offload(skb);
+       struct sec_path *sp;
 
        if (!xo)
                return skb;
@@ -39,7 +40,8 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
        if (!(features & NETIF_F_HW_ESP))
                esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK);
 
-       x = skb->sp->xvec[skb->sp->len - 1];
+       sp = skb_sec_path(skb);
+       x = sp->xvec[sp->len - 1];
        if (xo->flags & XFRM_GRO || x->xso.flags & XFRM_OFFLOAD_INBOUND)
                return skb;
 
index 684c0bc..b3b6136 100644 (file)
@@ -38,8 +38,6 @@ struct xfrm_trans_cb {
 
 #define XFRM_TRANS_SKB_CB(__skb) ((struct xfrm_trans_cb *)&((__skb)->cb[0]))
 
-static struct kmem_cache *secpath_cachep __ro_after_init;
-
 static DEFINE_SPINLOCK(xfrm_input_afinfo_lock);
 static struct xfrm_input_afinfo const __rcu *xfrm_input_afinfo[AF_INET6 + 1];
 
@@ -111,56 +109,24 @@ static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol,
        return ret;
 }
 
-void __secpath_destroy(struct sec_path *sp)
+struct sec_path *secpath_set(struct sk_buff *skb)
 {
-       int i;
-       for (i = 0; i < sp->len; i++)
-               xfrm_state_put(sp->xvec[i]);
-       kmem_cache_free(secpath_cachep, sp);
-}
-EXPORT_SYMBOL(__secpath_destroy);
+       struct sec_path *sp, *tmp = skb_ext_find(skb, SKB_EXT_SEC_PATH);
 
-struct sec_path *secpath_dup(struct sec_path *src)
-{
-       struct sec_path *sp;
-
-       sp = kmem_cache_alloc(secpath_cachep, GFP_ATOMIC);
+       sp = skb_ext_add(skb, SKB_EXT_SEC_PATH);
        if (!sp)
                return NULL;
 
-       sp->len = 0;
-       sp->olen = 0;
+       if (tmp) /* reused existing one (was COW'd if needed) */
+               return sp;
 
+       /* allocated new secpath */
        memset(sp->ovec, 0, sizeof(sp->ovec));
+       sp->olen = 0;
+       sp->len = 0;
 
-       if (src) {
-               int i;
-
-               memcpy(sp, src, sizeof(*sp));
-               for (i = 0; i < sp->len; i++)
-                       xfrm_state_hold(sp->xvec[i]);
-       }
-       refcount_set(&sp->refcnt, 1);
        return sp;
 }
-EXPORT_SYMBOL(secpath_dup);
-
-int secpath_set(struct sk_buff *skb)
-{
-       struct sec_path *sp;
-
-       /* Allocate new secpath or COW existing one. */
-       if (!skb->sp || refcount_read(&skb->sp->refcnt) != 1) {
-               sp = secpath_dup(skb->sp);
-               if (!sp)
-                       return -ENOMEM;
-
-               if (skb->sp)
-                       secpath_put(skb->sp);
-               skb->sp = sp;
-       }
-       return 0;
-}
 EXPORT_SYMBOL(secpath_set);
 
 /* Fetch spi and seq from ipsec header */
@@ -236,6 +202,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
        bool xfrm_gro = false;
        bool crypto_done = false;
        struct xfrm_offload *xo = xfrm_offload(skb);
+       struct sec_path *sp;
 
        if (encap_type < 0) {
                x = xfrm_input_state(skb);
@@ -312,8 +279,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
                break;
        }
 
-       err = secpath_set(skb);
-       if (err) {
+       sp = secpath_set(skb);
+       if (!sp) {
                XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
                goto drop;
        }
@@ -328,7 +295,9 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
        daddr = (xfrm_address_t *)(skb_network_header(skb) +
                                   XFRM_SPI_SKB_CB(skb)->daddroff);
        do {
-               if (skb->sp->len == XFRM_MAX_DEPTH) {
+               sp = skb_sec_path(skb);
+
+               if (sp->len == XFRM_MAX_DEPTH) {
                        secpath_reset(skb);
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
                        goto drop;
@@ -344,7 +313,13 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
 
                skb->mark = xfrm_smark_get(skb->mark, x);
 
-               skb->sp->xvec[skb->sp->len++] = x;
+               sp->xvec[sp->len++] = x;
+
+               skb_dst_force(skb);
+               if (!skb_dst(skb)) {
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
+                       goto drop;
+               }
 
 lock:
                spin_lock(&x->lock);
@@ -385,7 +360,6 @@ lock:
                XFRM_SKB_CB(skb)->seq.input.low = seq;
                XFRM_SKB_CB(skb)->seq.input.hi = seq_hi;
 
-               skb_dst_force(skb);
                dev_hold(skb->dev);
 
                if (crypto_done)
@@ -468,8 +442,9 @@ resume:
        nf_reset(skb);
 
        if (decaps) {
-               if (skb->sp)
-                       skb->sp->olen = 0;
+               sp = skb_sec_path(skb);
+               if (sp)
+                       sp->olen = 0;
                skb_dst_drop(skb);
                gro_cells_receive(&gro_cells, skb);
                return 0;
@@ -480,8 +455,9 @@ resume:
 
                err = x->inner_mode->afinfo->transport_finish(skb, xfrm_gro || async);
                if (xfrm_gro) {
-                       if (skb->sp)
-                               skb->sp->olen = 0;
+                       sp = skb_sec_path(skb);
+                       if (sp)
+                               sp->olen = 0;
                        skb_dst_drop(skb);
                        gro_cells_receive(&gro_cells, skb);
                        return err;
@@ -546,11 +522,6 @@ void __init xfrm_input_init(void)
        if (err)
                gro_cells.cells = NULL;
 
-       secpath_cachep = kmem_cache_create("secpath_cache",
-                                          sizeof(struct sec_path),
-                                          0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
-                                          NULL);
-
        for_each_possible_cpu(i) {
                struct xfrm_trans_tasklet *trans;
 
index d679fa0..6be8c7d 100644 (file)
@@ -251,7 +251,7 @@ static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
        struct xfrm_if *xi;
        bool xnet;
 
-       if (err && !skb->sp)
+       if (err && !secpath_exists(skb))
                return 0;
 
        x = xfrm_input_state(skb);
index 4ae87c5..9333153 100644 (file)
@@ -102,6 +102,7 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
                skb_dst_force(skb);
                if (!skb_dst(skb)) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
+                       err = -EHOSTUNREACH;
                        goto error_nolock;
                }
 
@@ -218,19 +219,16 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb)
        if (xfrm_dev_offload_ok(skb, x)) {
                struct sec_path *sp;
 
-               sp = secpath_dup(skb->sp);
+               sp = secpath_set(skb);
                if (!sp) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
                        kfree_skb(skb);
                        return -ENOMEM;
                }
-               if (skb->sp)
-                       secpath_put(skb->sp);
-               skb->sp = sp;
                skb->encapsulation = 1;
 
                sp->olen++;
-               sp->xvec[skb->sp->len++] = x;
+               sp->xvec[sp->len++] = x;
                xfrm_state_hold(x);
 
                if (skb_is_gso(skb)) {
index 119a427..934492b 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/cache.h>
 #include <linux/cpu.h>
 #include <linux/audit.h>
+#include <linux/rhashtable.h>
 #include <net/dst.h>
 #include <net/flow.h>
 #include <net/xfrm.h>
@@ -45,6 +46,99 @@ struct xfrm_flo {
        u8 flags;
 };
 
+/* prefixes smaller than this are stored in lists, not trees. */
+#define INEXACT_PREFIXLEN_IPV4 16
+#define INEXACT_PREFIXLEN_IPV6 48
+
+struct xfrm_pol_inexact_node {
+       struct rb_node node;
+       union {
+               xfrm_address_t addr;
+               struct rcu_head rcu;
+       };
+       u8 prefixlen;
+
+       struct rb_root root;
+
+       /* the policies matching this node, can be empty list */
+       struct hlist_head hhead;
+};
+
+/* xfrm inexact policy search tree:
+ * xfrm_pol_inexact_bin = hash(dir,type,family,if_id);
+ *  |
+ * +---- root_d: sorted by daddr:prefix
+ * |                 |
+ * |        xfrm_pol_inexact_node
+ * |                 |
+ * |                 +- root: sorted by saddr/prefix
+ * |                 |              |
+ * |                 |         xfrm_pol_inexact_node
+ * |                 |              |
+ * |                 |              + root: unused
+ * |                 |              |
+ * |                 |              + hhead: saddr:daddr policies
+ * |                 |
+ * |                 +- coarse policies and all any:daddr policies
+ * |
+ * +---- root_s: sorted by saddr:prefix
+ * |                 |
+ * |        xfrm_pol_inexact_node
+ * |                 |
+ * |                 + root: unused
+ * |                 |
+ * |                 + hhead: saddr:any policies
+ * |
+ * +---- coarse policies and all any:any policies
+ *
+ * Lookups return four candidate lists:
+ * 1. any:any list from top-level xfrm_pol_inexact_bin
+ * 2. any:daddr list from daddr tree
+ * 3. saddr:daddr list from 2nd level daddr tree
+ * 4. saddr:any list from saddr tree
+ *
+ * This result set then needs to be searched for the policy with
+ * the lowest priority.  If two results have same prio, youngest one wins.
+ */
+
+struct xfrm_pol_inexact_key {
+       possible_net_t net;
+       u32 if_id;
+       u16 family;
+       u8 dir, type;
+};
+
+struct xfrm_pol_inexact_bin {
+       struct xfrm_pol_inexact_key k;
+       struct rhash_head head;
+       /* list containing '*:*' policies */
+       struct hlist_head hhead;
+
+       seqcount_t count;
+       /* tree sorted by daddr/prefix */
+       struct rb_root root_d;
+
+       /* tree sorted by saddr/prefix */
+       struct rb_root root_s;
+
+       /* slow path below */
+       struct list_head inexact_bins;
+       struct rcu_head rcu;
+};
+
+enum xfrm_pol_inexact_candidate_type {
+       XFRM_POL_CAND_BOTH,
+       XFRM_POL_CAND_SADDR,
+       XFRM_POL_CAND_DADDR,
+       XFRM_POL_CAND_ANY,
+
+       XFRM_POL_CAND_MAX,
+};
+
+struct xfrm_pol_inexact_candidates {
+       struct hlist_head *res[XFRM_POL_CAND_MAX];
+};
+
 static DEFINE_SPINLOCK(xfrm_if_cb_lock);
 static struct xfrm_if_cb const __rcu *xfrm_if_cb __read_mostly;
 
@@ -55,6 +149,9 @@ static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
 static struct kmem_cache *xfrm_dst_cache __ro_after_init;
 static __read_mostly seqcount_t xfrm_policy_hash_generation;
 
+static struct rhashtable xfrm_policy_inexact_table;
+static const struct rhashtable_params xfrm_pol_inexact_params;
+
 static void xfrm_init_pmtu(struct xfrm_dst **bundle, int nr);
 static int stale_bundle(struct dst_entry *dst);
 static int xfrm_bundle_ok(struct xfrm_dst *xdst);
@@ -64,6 +161,25 @@ static void __xfrm_policy_link(struct xfrm_policy *pol, int dir);
 static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
                                                int dir);
 
+static struct xfrm_pol_inexact_bin *
+xfrm_policy_inexact_lookup(struct net *net, u8 type, u16 family, u8 dir,
+                          u32 if_id);
+
+static struct xfrm_pol_inexact_bin *
+xfrm_policy_inexact_lookup_rcu(struct net *net,
+                              u8 type, u16 family, u8 dir, u32 if_id);
+static struct xfrm_policy *
+xfrm_policy_insert_list(struct hlist_head *chain, struct xfrm_policy *policy,
+                       bool excl);
+static void xfrm_policy_insert_inexact_list(struct hlist_head *chain,
+                                           struct xfrm_policy *policy);
+
+static bool
+xfrm_policy_find_inexact_candidates(struct xfrm_pol_inexact_candidates *cand,
+                                   struct xfrm_pol_inexact_bin *b,
+                                   const xfrm_address_t *saddr,
+                                   const xfrm_address_t *daddr);
+
 static inline bool xfrm_pol_hold_rcu(struct xfrm_policy *policy)
 {
        return refcount_inc_not_zero(&policy->refcnt);
@@ -269,6 +385,7 @@ struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp)
        if (policy) {
                write_pnet(&policy->xp_net, net);
                INIT_LIST_HEAD(&policy->walk.all);
+               INIT_HLIST_NODE(&policy->bydst_inexact_list);
                INIT_HLIST_NODE(&policy->bydst);
                INIT_HLIST_NODE(&policy->byidx);
                rwlock_init(&policy->lock);
@@ -365,7 +482,7 @@ static struct hlist_head *policy_hash_bysel(struct net *net,
        hash = __sel_hash(sel, family, hmask, dbits, sbits);
 
        if (hash == hmask + 1)
-               return &net->xfrm.policy_inexact[dir];
+               return NULL;
 
        return rcu_dereference_check(net->xfrm.policy_bydst[dir].table,
                     lockdep_is_held(&net->xfrm.xfrm_policy_lock)) + hash;
@@ -563,6 +680,533 @@ 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).
+ */
+static struct xfrm_pol_inexact_bin *
+xfrm_policy_inexact_alloc_bin(const struct xfrm_policy *pol, u8 dir)
+{
+       struct xfrm_pol_inexact_bin *bin, *prev;
+       struct xfrm_pol_inexact_key k = {
+               .family = pol->family,
+               .type = pol->type,
+               .dir = dir,
+               .if_id = pol->if_id,
+       };
+       struct net *net = xp_net(pol);
+
+       lockdep_assert_held(&net->xfrm.xfrm_policy_lock);
+
+       write_pnet(&k.net, net);
+       bin = rhashtable_lookup_fast(&xfrm_policy_inexact_table, &k,
+                                    xfrm_pol_inexact_params);
+       if (bin)
+               return bin;
+
+       bin = kzalloc(sizeof(*bin), GFP_ATOMIC);
+       if (!bin)
+               return NULL;
+
+       bin->k = k;
+       INIT_HLIST_HEAD(&bin->hhead);
+       bin->root_d = RB_ROOT;
+       bin->root_s = RB_ROOT;
+       seqcount_init(&bin->count);
+
+       prev = rhashtable_lookup_get_insert_key(&xfrm_policy_inexact_table,
+                                               &bin->k, &bin->head,
+                                               xfrm_pol_inexact_params);
+       if (!prev) {
+               list_add(&bin->inexact_bins, &net->xfrm.inexact_bins);
+               return bin;
+       }
+
+       kfree(bin);
+
+       return IS_ERR(prev) ? NULL : prev;
+}
+
+static bool xfrm_pol_inexact_addr_use_any_list(const xfrm_address_t *addr,
+                                              int family, u8 prefixlen)
+{
+       if (xfrm_addr_any(addr, family))
+               return true;
+
+       if (family == AF_INET6 && prefixlen < INEXACT_PREFIXLEN_IPV6)
+               return true;
+
+       if (family == AF_INET && prefixlen < INEXACT_PREFIXLEN_IPV4)
+               return true;
+
+       return false;
+}
+
+static bool
+xfrm_policy_inexact_insert_use_any_list(const struct xfrm_policy *policy)
+{
+       const xfrm_address_t *addr;
+       bool saddr_any, daddr_any;
+       u8 prefixlen;
+
+       addr = &policy->selector.saddr;
+       prefixlen = policy->selector.prefixlen_s;
+
+       saddr_any = xfrm_pol_inexact_addr_use_any_list(addr,
+                                                      policy->family,
+                                                      prefixlen);
+       addr = &policy->selector.daddr;
+       prefixlen = policy->selector.prefixlen_d;
+       daddr_any = xfrm_pol_inexact_addr_use_any_list(addr,
+                                                      policy->family,
+                                                      prefixlen);
+       return saddr_any && daddr_any;
+}
+
+static void xfrm_pol_inexact_node_init(struct xfrm_pol_inexact_node *node,
+                                      const xfrm_address_t *addr, u8 prefixlen)
+{
+       node->addr = *addr;
+       node->prefixlen = prefixlen;
+}
+
+static struct xfrm_pol_inexact_node *
+xfrm_pol_inexact_node_alloc(const xfrm_address_t *addr, u8 prefixlen)
+{
+       struct xfrm_pol_inexact_node *node;
+
+       node = kzalloc(sizeof(*node), GFP_ATOMIC);
+       if (node)
+               xfrm_pol_inexact_node_init(node, addr, prefixlen);
+
+       return node;
+}
+
+static int xfrm_policy_addr_delta(const xfrm_address_t *a,
+                                 const xfrm_address_t *b,
+                                 u8 prefixlen, u16 family)
+{
+       unsigned int pdw, pbi;
+       int delta = 0;
+
+       switch (family) {
+       case AF_INET:
+               if (sizeof(long) == 4 && prefixlen == 0)
+                       return ntohl(a->a4) - ntohl(b->a4);
+               return (ntohl(a->a4) & ((~0UL << (32 - prefixlen)))) -
+                      (ntohl(b->a4) & ((~0UL << (32 - prefixlen))));
+       case AF_INET6:
+               pdw = prefixlen >> 5;
+               pbi = prefixlen & 0x1f;
+
+               if (pdw) {
+                       delta = memcmp(a->a6, b->a6, pdw << 2);
+                       if (delta)
+                               return delta;
+               }
+               if (pbi) {
+                       u32 mask = ~0u << (32 - pbi);
+
+                       delta = (ntohl(a->a6[pdw]) & mask) -
+                               (ntohl(b->a6[pdw]) & mask);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return delta;
+}
+
+static void xfrm_policy_inexact_list_reinsert(struct net *net,
+                                             struct xfrm_pol_inexact_node *n,
+                                             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) {
+               bool matches_s, matches_d;
+
+               if (!policy->bydst_reinsert)
+                       continue;
+
+               WARN_ON_ONCE(policy->family != family);
+
+               policy->bydst_reinsert = false;
+               hlist_for_each_entry(p, &n->hhead, bydst) {
+                       if (policy->priority >= p->priority)
+                               newpos = &p->bydst;
+                       else
+                               break;
+               }
+
+               if (newpos)
+                       hlist_add_behind(&policy->bydst, newpos);
+               else
+                       hlist_add_head(&policy->bydst, &n->hhead);
+
+               /* paranoia checks follow.
+                * Check that the reinserted policy matches at least
+                * saddr or daddr for current node prefix.
+                *
+                * Matching both is fine, matching saddr in one policy
+                * (but not daddr) and then matching only daddr in another
+                * is a bug.
+                */
+               matches_s = xfrm_policy_addr_delta(&policy->selector.saddr,
+                                                  &n->addr,
+                                                  n->prefixlen,
+                                                  family) == 0;
+               matches_d = xfrm_policy_addr_delta(&policy->selector.daddr,
+                                                  &n->addr,
+                                                  n->prefixlen,
+                                                  family) == 0;
+               if (matches_s && matches_d)
+                       continue;
+
+               WARN_ON_ONCE(!matches_s && !matches_d);
+               if (matches_s)
+                       matched_s++;
+               if (matches_d)
+                       matched_d++;
+               WARN_ON_ONCE(matched_s && matched_d);
+       }
+}
+
+static void xfrm_policy_inexact_node_reinsert(struct net *net,
+                                             struct xfrm_pol_inexact_node *n,
+                                             struct rb_root *new,
+                                             u16 family)
+{
+       struct rb_node **p, *parent = NULL;
+       struct xfrm_pol_inexact_node *node;
+
+       /* we should not have another subtree here */
+       WARN_ON_ONCE(!RB_EMPTY_ROOT(&n->root));
+
+       p = &new->rb_node;
+       while (*p) {
+               u8 prefixlen;
+               int delta;
+
+               parent = *p;
+               node = rb_entry(*p, struct xfrm_pol_inexact_node, node);
+
+               prefixlen = min(node->prefixlen, n->prefixlen);
+
+               delta = xfrm_policy_addr_delta(&n->addr, &node->addr,
+                                              prefixlen, family);
+               if (delta < 0) {
+                       p = &parent->rb_left;
+               } else if (delta > 0) {
+                       p = &parent->rb_right;
+               } 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)
+                               tmp->bydst_reinsert = true;
+
+                       INIT_HLIST_HEAD(&node->hhead);
+                       xfrm_policy_inexact_list_reinsert(net, node, family);
+
+                       if (node->prefixlen == n->prefixlen) {
+                               kfree_rcu(n, rcu);
+                               return;
+                       }
+
+                       rb_erase(*p, new);
+                       kfree_rcu(n, rcu);
+                       n = node;
+                       n->prefixlen = prefixlen;
+                       *p = new->rb_node;
+                       parent = NULL;
+               }
+       }
+
+       rb_link_node_rcu(&n->node, parent, p);
+       rb_insert_color(&n->node, new);
+}
+
+/* merge nodes v and n */
+static void xfrm_policy_inexact_node_merge(struct net *net,
+                                          struct xfrm_pol_inexact_node *v,
+                                          struct xfrm_pol_inexact_node *n,
+                                          u16 family)
+{
+       struct xfrm_pol_inexact_node *node;
+       struct xfrm_policy *tmp;
+       struct rb_node *rnode;
+
+       /* To-be-merged node v has a subtree.
+        *
+        * Dismantle it and insert its nodes to n->root.
+        */
+       while ((rnode = rb_first(&v->root)) != NULL) {
+               node = rb_entry(rnode, struct xfrm_pol_inexact_node, node);
+               rb_erase(&node->node, &v->root);
+               xfrm_policy_inexact_node_reinsert(net, node, &n->root,
+                                                 family);
+       }
+
+       hlist_for_each_entry(tmp, &v->hhead, bydst)
+               tmp->bydst_reinsert = true;
+       hlist_for_each_entry(tmp, &n->hhead, bydst)
+               tmp->bydst_reinsert = true;
+
+       INIT_HLIST_HEAD(&n->hhead);
+       xfrm_policy_inexact_list_reinsert(net, n, family);
+}
+
+static struct xfrm_pol_inexact_node *
+xfrm_policy_inexact_insert_node(struct net *net,
+                               struct rb_root *root,
+                               xfrm_address_t *addr,
+                               u16 family, u8 prefixlen, u8 dir)
+{
+       struct xfrm_pol_inexact_node *cached = NULL;
+       struct rb_node **p, *parent = NULL;
+       struct xfrm_pol_inexact_node *node;
+
+       p = &root->rb_node;
+       while (*p) {
+               int delta;
+
+               parent = *p;
+               node = rb_entry(*p, struct xfrm_pol_inexact_node, node);
+
+               delta = xfrm_policy_addr_delta(addr, &node->addr,
+                                              node->prefixlen,
+                                              family);
+               if (delta == 0 && prefixlen >= node->prefixlen) {
+                       WARN_ON_ONCE(cached); /* ipsec policies got lost */
+                       return node;
+               }
+
+               if (delta < 0)
+                       p = &parent->rb_left;
+               else
+                       p = &parent->rb_right;
+
+               if (prefixlen < node->prefixlen) {
+                       delta = xfrm_policy_addr_delta(addr, &node->addr,
+                                                      prefixlen,
+                                                      family);
+                       if (delta)
+                               continue;
+
+                       /* This node is a subnet of the new prefix. It needs
+                        * to be removed and re-inserted with the smaller
+                        * prefix and all nodes that are now also covered
+                        * by the reduced prefixlen.
+                        */
+                       rb_erase(&node->node, root);
+
+                       if (!cached) {
+                               xfrm_pol_inexact_node_init(node, addr,
+                                                          prefixlen);
+                               cached = node;
+                       } else {
+                               /* This node also falls within the new
+                                * prefixlen. Merge the to-be-reinserted
+                                * node and this one.
+                                */
+                               xfrm_policy_inexact_node_merge(net, node,
+                                                              cached, family);
+                               kfree_rcu(node, rcu);
+                       }
+
+                       /* restart */
+                       p = &root->rb_node;
+                       parent = NULL;
+               }
+       }
+
+       node = cached;
+       if (!node) {
+               node = xfrm_pol_inexact_node_alloc(addr, prefixlen);
+               if (!node)
+                       return NULL;
+       }
+
+       rb_link_node_rcu(&node->node, parent, p);
+       rb_insert_color(&node->node, root);
+
+       return node;
+}
+
+static void xfrm_policy_inexact_gc_tree(struct rb_root *r, bool rm)
+{
+       struct xfrm_pol_inexact_node *node;
+       struct rb_node *rn = rb_first(r);
+
+       while (rn) {
+               node = rb_entry(rn, struct xfrm_pol_inexact_node, node);
+
+               xfrm_policy_inexact_gc_tree(&node->root, rm);
+               rn = rb_next(rn);
+
+               if (!hlist_empty(&node->hhead) || !RB_EMPTY_ROOT(&node->root)) {
+                       WARN_ON_ONCE(rm);
+                       continue;
+               }
+
+               rb_erase(&node->node, r);
+               kfree_rcu(node, rcu);
+       }
+}
+
+static void __xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b, bool net_exit)
+{
+       write_seqcount_begin(&b->count);
+       xfrm_policy_inexact_gc_tree(&b->root_d, net_exit);
+       xfrm_policy_inexact_gc_tree(&b->root_s, net_exit);
+       write_seqcount_end(&b->count);
+
+       if (!RB_EMPTY_ROOT(&b->root_d) || !RB_EMPTY_ROOT(&b->root_s) ||
+           !hlist_empty(&b->hhead)) {
+               WARN_ON_ONCE(net_exit);
+               return;
+       }
+
+       if (rhashtable_remove_fast(&xfrm_policy_inexact_table, &b->head,
+                                  xfrm_pol_inexact_params) == 0) {
+               list_del(&b->inexact_bins);
+               kfree_rcu(b, rcu);
+       }
+}
+
+static void xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b)
+{
+       struct net *net = read_pnet(&b->k.net);
+
+       spin_lock_bh(&net->xfrm.xfrm_policy_lock);
+       __xfrm_policy_inexact_prune_bin(b, false);
+       spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
+}
+
+static void __xfrm_policy_inexact_flush(struct net *net)
+{
+       struct xfrm_pol_inexact_bin *bin, *t;
+
+       lockdep_assert_held(&net->xfrm.xfrm_policy_lock);
+
+       list_for_each_entry_safe(bin, t, &net->xfrm.inexact_bins, inexact_bins)
+               __xfrm_policy_inexact_prune_bin(bin, false);
+}
+
+static struct hlist_head *
+xfrm_policy_inexact_alloc_chain(struct xfrm_pol_inexact_bin *bin,
+                               struct xfrm_policy *policy, u8 dir)
+{
+       struct xfrm_pol_inexact_node *n;
+       struct net *net;
+
+       net = xp_net(policy);
+       lockdep_assert_held(&net->xfrm.xfrm_policy_lock);
+
+       if (xfrm_policy_inexact_insert_use_any_list(policy))
+               return &bin->hhead;
+
+       if (xfrm_pol_inexact_addr_use_any_list(&policy->selector.daddr,
+                                              policy->family,
+                                              policy->selector.prefixlen_d)) {
+               write_seqcount_begin(&bin->count);
+               n = xfrm_policy_inexact_insert_node(net,
+                                                   &bin->root_s,
+                                                   &policy->selector.saddr,
+                                                   policy->family,
+                                                   policy->selector.prefixlen_s,
+                                                   dir);
+               write_seqcount_end(&bin->count);
+               if (!n)
+                       return NULL;
+
+               return &n->hhead;
+       }
+
+       /* daddr is fixed */
+       write_seqcount_begin(&bin->count);
+       n = xfrm_policy_inexact_insert_node(net,
+                                           &bin->root_d,
+                                           &policy->selector.daddr,
+                                           policy->family,
+                                           policy->selector.prefixlen_d, dir);
+       write_seqcount_end(&bin->count);
+       if (!n)
+               return NULL;
+
+       /* saddr is wildcard */
+       if (xfrm_pol_inexact_addr_use_any_list(&policy->selector.saddr,
+                                              policy->family,
+                                              policy->selector.prefixlen_s))
+               return &n->hhead;
+
+       write_seqcount_begin(&bin->count);
+       n = xfrm_policy_inexact_insert_node(net,
+                                           &n->root,
+                                           &policy->selector.saddr,
+                                           policy->family,
+                                           policy->selector.prefixlen_s, dir);
+       write_seqcount_end(&bin->count);
+       if (!n)
+               return NULL;
+
+       return &n->hhead;
+}
+
+static struct xfrm_policy *
+xfrm_policy_inexact_insert(struct xfrm_policy *policy, u8 dir, int excl)
+{
+       struct xfrm_pol_inexact_bin *bin;
+       struct xfrm_policy *delpol;
+       struct hlist_head *chain;
+       struct net *net;
+
+       bin = xfrm_policy_inexact_alloc_bin(policy, dir);
+       if (!bin)
+               return ERR_PTR(-ENOMEM);
+
+       net = xp_net(policy);
+       lockdep_assert_held(&net->xfrm.xfrm_policy_lock);
+
+       chain = xfrm_policy_inexact_alloc_chain(bin, policy, dir);
+       if (!chain) {
+               __xfrm_policy_inexact_prune_bin(bin, false);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       delpol = xfrm_policy_insert_list(chain, policy, excl);
+       if (delpol && excl) {
+               __xfrm_policy_inexact_prune_bin(bin, false);
+               return ERR_PTR(-EEXIST);
+       }
+
+       chain = &net->xfrm.policy_inexact[dir];
+       xfrm_policy_insert_inexact_list(chain, policy);
+
+       if (delpol)
+               __xfrm_policy_inexact_prune_bin(bin, false);
+
+       return delpol;
+}
+
 static void xfrm_hash_rebuild(struct work_struct *work)
 {
        struct net *net = container_of(work, struct net,
@@ -592,7 +1236,50 @@ static void xfrm_hash_rebuild(struct work_struct *work)
 
        spin_lock_bh(&net->xfrm.xfrm_policy_lock);
 
+       /* make sure that we can insert the indirect policies again before
+        * we start with destructive action.
+        */
+       list_for_each_entry(policy, &net->xfrm.policy_all, walk.all) {
+               struct xfrm_pol_inexact_bin *bin;
+               u8 dbits, sbits;
+
+               dir = xfrm_policy_id2dir(policy->index);
+               if (policy->walk.dead || dir >= XFRM_POLICY_MAX)
+                       continue;
+
+               if ((dir & XFRM_POLICY_MASK) == XFRM_POLICY_OUT) {
+                       if (policy->family == AF_INET) {
+                               dbits = rbits4;
+                               sbits = lbits4;
+                       } else {
+                               dbits = rbits6;
+                               sbits = lbits6;
+                       }
+               } else {
+                       if (policy->family == AF_INET) {
+                               dbits = lbits4;
+                               sbits = rbits4;
+                       } else {
+                               dbits = lbits6;
+                               sbits = rbits6;
+                       }
+               }
+
+               if (policy->selector.prefixlen_d < dbits ||
+                   policy->selector.prefixlen_s < sbits)
+                       continue;
+
+               bin = xfrm_policy_inexact_alloc_bin(policy, dir);
+               if (!bin)
+                       goto out_unlock;
+
+               if (!xfrm_policy_inexact_alloc_chain(bin, policy, dir))
+                       goto out_unlock;
+       }
+
        /* 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]);
                hmask = net->xfrm.policy_bydst[dir].hmask;
@@ -616,15 +1303,23 @@ static void xfrm_hash_rebuild(struct work_struct *work)
 
        /* re-insert all policies by order of creation */
        list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) {
-               if (policy->walk.dead ||
-                   xfrm_policy_id2dir(policy->index) >= XFRM_POLICY_MAX) {
+               if (policy->walk.dead)
+                       continue;
+               dir = xfrm_policy_id2dir(policy->index);
+               if (dir >= XFRM_POLICY_MAX) {
                        /* skip socket policies */
                        continue;
                }
                newpos = NULL;
                chain = policy_hash_bysel(net, &policy->selector,
-                                         policy->family,
-                                         xfrm_policy_id2dir(policy->index));
+                                         policy->family, dir);
+               if (!chain) {
+                       void *p = xfrm_policy_inexact_insert(policy, dir, 0);
+
+                       WARN_ONCE(IS_ERR(p), "reinsert: %ld\n", PTR_ERR(p));
+                       continue;
+               }
+
                hlist_for_each_entry(pol, chain, bydst) {
                        if (policy->priority >= pol->priority)
                                newpos = &pol->bydst;
@@ -637,6 +1332,8 @@ static void xfrm_hash_rebuild(struct work_struct *work)
                        hlist_add_head_rcu(&policy->bydst, chain);
        }
 
+out_unlock:
+       __xfrm_policy_inexact_flush(net);
        spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
 
        mutex_unlock(&hash_resize_mutex);
@@ -740,18 +1437,97 @@ static bool xfrm_policy_mark_match(struct xfrm_policy *policy,
        return false;
 }
 
-int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
+static u32 xfrm_pol_bin_key(const void *data, u32 len, u32 seed)
 {
-       struct net *net = xp_net(policy);
-       struct xfrm_policy *pol;
-       struct xfrm_policy *delpol;
-       struct hlist_head *chain;
-       struct hlist_node *newpos;
+       const struct xfrm_pol_inexact_key *k = data;
+       u32 a = k->type << 24 | k->dir << 16 | k->family;
+
+       return jhash_3words(a, k->if_id, net_hash_mix(read_pnet(&k->net)),
+                           seed);
+}
+
+static u32 xfrm_pol_bin_obj(const void *data, u32 len, u32 seed)
+{
+       const struct xfrm_pol_inexact_bin *b = data;
+
+       return xfrm_pol_bin_key(&b->k, 0, seed);
+}
+
+static int xfrm_pol_bin_cmp(struct rhashtable_compare_arg *arg,
+                           const void *ptr)
+{
+       const struct xfrm_pol_inexact_key *key = arg->key;
+       const struct xfrm_pol_inexact_bin *b = ptr;
+       int ret;
+
+       if (!net_eq(read_pnet(&b->k.net), read_pnet(&key->net)))
+               return -1;
+
+       ret = b->k.dir ^ key->dir;
+       if (ret)
+               return ret;
+
+       ret = b->k.type ^ key->type;
+       if (ret)
+               return ret;
+
+       ret = b->k.family ^ key->family;
+       if (ret)
+               return ret;
+
+       return b->k.if_id ^ key->if_id;
+}
+
+static const struct rhashtable_params xfrm_pol_inexact_params = {
+       .head_offset            = offsetof(struct xfrm_pol_inexact_bin, head),
+       .hashfn                 = xfrm_pol_bin_key,
+       .obj_hashfn             = xfrm_pol_bin_obj,
+       .obj_cmpfn              = xfrm_pol_bin_cmp,
+       .automatic_shrinking    = true,
+};
+
+static void xfrm_policy_insert_inexact_list(struct hlist_head *chain,
+                                           struct xfrm_policy *policy)
+{
+       struct xfrm_policy *pol, *delpol = NULL;
+       struct hlist_node *newpos = NULL;
+       int i = 0;
+
+       hlist_for_each_entry(pol, chain, bydst_inexact_list) {
+               if (pol->type == policy->type &&
+                   pol->if_id == policy->if_id &&
+                   !selector_cmp(&pol->selector, &policy->selector) &&
+                   xfrm_policy_mark_match(policy, pol) &&
+                   xfrm_sec_ctx_match(pol->security, policy->security) &&
+                   !WARN_ON(delpol)) {
+                       delpol = pol;
+                       if (policy->priority > pol->priority)
+                               continue;
+               } else if (policy->priority >= pol->priority) {
+                       newpos = &pol->bydst_inexact_list;
+                       continue;
+               }
+               if (delpol)
+                       break;
+       }
+
+       if (newpos)
+               hlist_add_behind_rcu(&policy->bydst_inexact_list, newpos);
+       else
+               hlist_add_head_rcu(&policy->bydst_inexact_list, chain);
+
+       hlist_for_each_entry(pol, chain, bydst_inexact_list) {
+               pol->pos = i;
+               i++;
+       }
+}
+
+static struct xfrm_policy *xfrm_policy_insert_list(struct hlist_head *chain,
+                                                  struct xfrm_policy *policy,
+                                                  bool excl)
+{
+       struct xfrm_policy *pol, *newpos = NULL, *delpol = NULL;
 
-       spin_lock_bh(&net->xfrm.xfrm_policy_lock);
-       chain = policy_hash_bysel(net, &policy->selector, policy->family, dir);
-       delpol = NULL;
-       newpos = NULL;
        hlist_for_each_entry(pol, chain, bydst) {
                if (pol->type == policy->type &&
                    pol->if_id == policy->if_id &&
@@ -759,24 +1535,45 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
                    xfrm_policy_mark_match(policy, pol) &&
                    xfrm_sec_ctx_match(pol->security, policy->security) &&
                    !WARN_ON(delpol)) {
-                       if (excl) {
-                               spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
-                               return -EEXIST;
-                       }
+                       if (excl)
+                               return ERR_PTR(-EEXIST);
                        delpol = pol;
                        if (policy->priority > pol->priority)
                                continue;
                } else if (policy->priority >= pol->priority) {
-                       newpos = &pol->bydst;
+                       newpos = pol;
                        continue;
                }
                if (delpol)
                        break;
        }
+
        if (newpos)
-               hlist_add_behind_rcu(&policy->bydst, newpos);
+               hlist_add_behind_rcu(&policy->bydst, &newpos->bydst);
        else
                hlist_add_head_rcu(&policy->bydst, chain);
+
+       return delpol;
+}
+
+int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
+{
+       struct net *net = xp_net(policy);
+       struct xfrm_policy *delpol;
+       struct hlist_head *chain;
+
+       spin_lock_bh(&net->xfrm.xfrm_policy_lock);
+       chain = policy_hash_bysel(net, &policy->selector, policy->family, dir);
+       if (chain)
+               delpol = xfrm_policy_insert_list(chain, policy, excl);
+       else
+               delpol = xfrm_policy_inexact_insert(policy, dir, excl);
+
+       if (IS_ERR(delpol)) {
+               spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
+               return PTR_ERR(delpol);
+       }
+
        __xfrm_policy_link(policy, dir);
 
        /* After previous checking, family can either be AF_INET or AF_INET6 */
@@ -806,43 +1603,96 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
 }
 EXPORT_SYMBOL(xfrm_policy_insert);
 
+static struct xfrm_policy *
+__xfrm_policy_bysel_ctx(struct hlist_head *chain, u32 mark, u32 if_id,
+                       u8 type, int dir,
+                       struct xfrm_selector *sel,
+                       struct xfrm_sec_ctx *ctx)
+{
+       struct xfrm_policy *pol;
+
+       if (!chain)
+               return NULL;
+
+       hlist_for_each_entry(pol, chain, bydst) {
+               if (pol->type == type &&
+                   pol->if_id == if_id &&
+                   (mark & pol->mark.m) == pol->mark.v &&
+                   !selector_cmp(sel, &pol->selector) &&
+                   xfrm_sec_ctx_match(ctx, pol->security))
+                       return pol;
+       }
+
+       return NULL;
+}
+
 struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u32 if_id,
                                          u8 type, int dir,
                                          struct xfrm_selector *sel,
                                          struct xfrm_sec_ctx *ctx, int delete,
                                          int *err)
 {
-       struct xfrm_policy *pol, *ret;
+       struct xfrm_pol_inexact_bin *bin = NULL;
+       struct xfrm_policy *pol, *ret = NULL;
        struct hlist_head *chain;
 
        *err = 0;
        spin_lock_bh(&net->xfrm.xfrm_policy_lock);
        chain = policy_hash_bysel(net, sel, sel->family, dir);
-       ret = NULL;
-       hlist_for_each_entry(pol, chain, bydst) {
-               if (pol->type == type &&
-                   pol->if_id == if_id &&
-                   (mark & pol->mark.m) == pol->mark.v &&
-                   !selector_cmp(sel, &pol->selector) &&
-                   xfrm_sec_ctx_match(ctx, pol->security)) {
-                       xfrm_pol_hold(pol);
-                       if (delete) {
-                               *err = security_xfrm_policy_delete(
-                                                               pol->security);
-                               if (*err) {
-                                       spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
-                                       return pol;
-                               }
-                               __xfrm_policy_unlink(pol, dir);
+       if (!chain) {
+               struct xfrm_pol_inexact_candidates cand;
+               int i;
+
+               bin = xfrm_policy_inexact_lookup(net, type,
+                                                sel->family, dir, if_id);
+               if (!bin) {
+                       spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
+                       return NULL;
+               }
+
+               if (!xfrm_policy_find_inexact_candidates(&cand, bin,
+                                                        &sel->saddr,
+                                                        &sel->daddr)) {
+                       spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
+                       return NULL;
+               }
+
+               pol = NULL;
+               for (i = 0; i < ARRAY_SIZE(cand.res); i++) {
+                       struct xfrm_policy *tmp;
+
+                       tmp = __xfrm_policy_bysel_ctx(cand.res[i], mark,
+                                                     if_id, type, dir,
+                                                     sel, ctx);
+                       if (!tmp)
+                               continue;
+
+                       if (!pol || tmp->pos < pol->pos)
+                               pol = tmp;
+               }
+       } else {
+               pol = __xfrm_policy_bysel_ctx(chain, mark, if_id, type, dir,
+                                             sel, ctx);
+       }
+
+       if (pol) {
+               xfrm_pol_hold(pol);
+               if (delete) {
+                       *err = security_xfrm_policy_delete(pol->security);
+                       if (*err) {
+                               spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
+                               return pol;
                        }
-                       ret = pol;
-                       break;
+                       __xfrm_policy_unlink(pol, dir);
                }
+               ret = pol;
        }
        spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
 
        if (ret && delete)
                xfrm_policy_kill(ret);
+       if (bin && delete)
+               xfrm_policy_inexact_prune_bin(bin);
        return ret;
 }
 EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
@@ -892,36 +1742,19 @@ EXPORT_SYMBOL(xfrm_policy_byid);
 static inline int
 xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
 {
-       int dir, err = 0;
+       struct xfrm_policy *pol;
+       int err = 0;
 
-       for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
-               struct xfrm_policy *pol;
-               int i;
+       list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) {
+               if (pol->walk.dead ||
+                   xfrm_policy_id2dir(pol->index) >= XFRM_POLICY_MAX ||
+                   pol->type != type)
+                       continue;
 
-               hlist_for_each_entry(pol,
-                                    &net->xfrm.policy_inexact[dir], bydst) {
-                       if (pol->type != type)
-                               continue;
-                       err = security_xfrm_policy_delete(pol->security);
-                       if (err) {
-                               xfrm_audit_policy_delete(pol, 0, task_valid);
-                               return err;
-                       }
-               }
-               for (i = net->xfrm.policy_bydst[dir].hmask; i >= 0; i--) {
-                       hlist_for_each_entry(pol,
-                                            net->xfrm.policy_bydst[dir].table + i,
-                                            bydst) {
-                               if (pol->type != type)
-                                       continue;
-                               err = security_xfrm_policy_delete(
-                                                               pol->security);
-                               if (err) {
-                                       xfrm_audit_policy_delete(pol, 0,
-                                                                task_valid);
-                                       return err;
-                               }
-                       }
+               err = security_xfrm_policy_delete(pol->security);
+               if (err) {
+                       xfrm_audit_policy_delete(pol, 0, task_valid);
+                       return err;
                }
        }
        return err;
@@ -937,6 +1770,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
 int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
 {
        int dir, err = 0, cnt = 0;
+       struct xfrm_policy *pol;
 
        spin_lock_bh(&net->xfrm.xfrm_policy_lock);
 
@@ -944,48 +1778,25 @@ int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
        if (err)
                goto out;
 
-       for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
-               struct xfrm_policy *pol;
-               int i;
-
-       again1:
-               hlist_for_each_entry(pol,
-                                    &net->xfrm.policy_inexact[dir], bydst) {
-                       if (pol->type != type)
-                               continue;
-                       __xfrm_policy_unlink(pol, dir);
-                       spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
-                       cnt++;
-
-                       xfrm_audit_policy_delete(pol, 1, task_valid);
-
-                       xfrm_policy_kill(pol);
-
-                       spin_lock_bh(&net->xfrm.xfrm_policy_lock);
-                       goto again1;
-               }
-
-               for (i = net->xfrm.policy_bydst[dir].hmask; i >= 0; i--) {
-       again2:
-                       hlist_for_each_entry(pol,
-                                            net->xfrm.policy_bydst[dir].table + i,
-                                            bydst) {
-                               if (pol->type != type)
-                                       continue;
-                               __xfrm_policy_unlink(pol, dir);
-                               spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
-                               cnt++;
-
-                               xfrm_audit_policy_delete(pol, 1, task_valid);
-                               xfrm_policy_kill(pol);
-
-                               spin_lock_bh(&net->xfrm.xfrm_policy_lock);
-                               goto again2;
-                       }
-               }
+again:
+       list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) {
+               dir = xfrm_policy_id2dir(pol->index);
+               if (pol->walk.dead ||
+                   dir >= XFRM_POLICY_MAX ||
+                   pol->type != type)
+                       continue;
 
+               __xfrm_policy_unlink(pol, dir);
+               spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
+               cnt++;
+               xfrm_audit_policy_delete(pol, 1, task_valid);
+               xfrm_policy_kill(pol);
+               spin_lock_bh(&net->xfrm.xfrm_policy_lock);
+               goto again;
        }
-       if (!cnt)
+       if (cnt)
+               __xfrm_policy_inexact_flush(net);
+       else
                err = -ESRCH;
 out:
        spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
@@ -1084,21 +1895,188 @@ static int xfrm_policy_match(const struct xfrm_policy *pol,
        if (match)
                ret = security_xfrm_policy_lookup(pol->security, fl->flowi_secid,
                                                  dir);
-
        return ret;
 }
 
+static struct xfrm_pol_inexact_node *
+xfrm_policy_lookup_inexact_addr(const struct rb_root *r,
+                               seqcount_t *count,
+                               const xfrm_address_t *addr, u16 family)
+{
+       const struct rb_node *parent;
+       int seq;
+
+again:
+       seq = read_seqcount_begin(count);
+
+       parent = rcu_dereference_raw(r->rb_node);
+       while (parent) {
+               struct xfrm_pol_inexact_node *node;
+               int delta;
+
+               node = rb_entry(parent, struct xfrm_pol_inexact_node, node);
+
+               delta = xfrm_policy_addr_delta(addr, &node->addr,
+                                              node->prefixlen, family);
+               if (delta < 0) {
+                       parent = rcu_dereference_raw(parent->rb_left);
+                       continue;
+               } else if (delta > 0) {
+                       parent = rcu_dereference_raw(parent->rb_right);
+                       continue;
+               }
+
+               return node;
+       }
+
+       if (read_seqcount_retry(count, seq))
+               goto again;
+
+       return NULL;
+}
+
+static bool
+xfrm_policy_find_inexact_candidates(struct xfrm_pol_inexact_candidates *cand,
+                                   struct xfrm_pol_inexact_bin *b,
+                                   const xfrm_address_t *saddr,
+                                   const xfrm_address_t *daddr)
+{
+       struct xfrm_pol_inexact_node *n;
+       u16 family;
+
+       if (!b)
+               return false;
+
+       family = b->k.family;
+       memset(cand, 0, sizeof(*cand));
+       cand->res[XFRM_POL_CAND_ANY] = &b->hhead;
+
+       n = xfrm_policy_lookup_inexact_addr(&b->root_d, &b->count, daddr,
+                                           family);
+       if (n) {
+               cand->res[XFRM_POL_CAND_DADDR] = &n->hhead;
+               n = xfrm_policy_lookup_inexact_addr(&n->root, &b->count, saddr,
+                                                   family);
+               if (n)
+                       cand->res[XFRM_POL_CAND_BOTH] = &n->hhead;
+       }
+
+       n = xfrm_policy_lookup_inexact_addr(&b->root_s, &b->count, saddr,
+                                           family);
+       if (n)
+               cand->res[XFRM_POL_CAND_SADDR] = &n->hhead;
+
+       return true;
+}
+
+static struct xfrm_pol_inexact_bin *
+xfrm_policy_inexact_lookup_rcu(struct net *net, u8 type, u16 family,
+                              u8 dir, u32 if_id)
+{
+       struct xfrm_pol_inexact_key k = {
+               .family = family,
+               .type = type,
+               .dir = dir,
+               .if_id = if_id,
+       };
+
+       write_pnet(&k.net, net);
+
+       return rhashtable_lookup(&xfrm_policy_inexact_table, &k,
+                                xfrm_pol_inexact_params);
+}
+
+static struct xfrm_pol_inexact_bin *
+xfrm_policy_inexact_lookup(struct net *net, u8 type, u16 family,
+                          u8 dir, u32 if_id)
+{
+       struct xfrm_pol_inexact_bin *bin;
+
+       lockdep_assert_held(&net->xfrm.xfrm_policy_lock);
+
+       rcu_read_lock();
+       bin = xfrm_policy_inexact_lookup_rcu(net, type, family, dir, if_id);
+       rcu_read_unlock();
+
+       return bin;
+}
+
+static struct xfrm_policy *
+__xfrm_policy_eval_candidates(struct hlist_head *chain,
+                             struct xfrm_policy *prefer,
+                             const struct flowi *fl,
+                             u8 type, u16 family, int dir, u32 if_id)
+{
+       u32 priority = prefer ? prefer->priority : ~0u;
+       struct xfrm_policy *pol;
+
+       if (!chain)
+               return NULL;
+
+       hlist_for_each_entry_rcu(pol, chain, bydst) {
+               int err;
+
+               if (pol->priority > priority)
+                       break;
+
+               err = xfrm_policy_match(pol, fl, type, family, dir, if_id);
+               if (err) {
+                       if (err != -ESRCH)
+                               return ERR_PTR(err);
+
+                       continue;
+               }
+
+               if (prefer) {
+                       /* matches.  Is it older than *prefer? */
+                       if (pol->priority == priority &&
+                           prefer->pos < pol->pos)
+                               return prefer;
+               }
+
+               return pol;
+       }
+
+       return NULL;
+}
+
+static struct xfrm_policy *
+xfrm_policy_eval_candidates(struct xfrm_pol_inexact_candidates *cand,
+                           struct xfrm_policy *prefer,
+                           const struct flowi *fl,
+                           u8 type, u16 family, int dir, u32 if_id)
+{
+       struct xfrm_policy *tmp;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cand->res); i++) {
+               tmp = __xfrm_policy_eval_candidates(cand->res[i],
+                                                   prefer,
+                                                   fl, type, family, dir,
+                                                   if_id);
+               if (!tmp)
+                       continue;
+
+               if (IS_ERR(tmp))
+                       return tmp;
+               prefer = tmp;
+       }
+
+       return prefer;
+}
+
 static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
                                                     const struct flowi *fl,
                                                     u16 family, u8 dir,
                                                     u32 if_id)
 {
-       int err;
-       struct xfrm_policy *pol, *ret;
+       struct xfrm_pol_inexact_candidates cand;
        const xfrm_address_t *daddr, *saddr;
+       struct xfrm_pol_inexact_bin *bin;
+       struct xfrm_policy *pol, *ret;
        struct hlist_head *chain;
        unsigned int sequence;
-       u32 priority;
+       int err;
 
        daddr = xfrm_flowi_daddr(fl, family);
        saddr = xfrm_flowi_saddr(fl, family);
@@ -1112,7 +2090,6 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
                chain = policy_hash_direct(net, daddr, saddr, family, dir);
        } while (read_seqcount_retry(&xfrm_policy_hash_generation, sequence));
 
-       priority = ~0U;
        ret = NULL;
        hlist_for_each_entry_rcu(pol, chain, bydst) {
                err = xfrm_policy_match(pol, fl, type, family, dir, if_id);
@@ -1125,29 +2102,23 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
                        }
                } else {
                        ret = pol;
-                       priority = ret->priority;
                        break;
                }
        }
-       chain = &net->xfrm.policy_inexact[dir];
-       hlist_for_each_entry_rcu(pol, chain, bydst) {
-               if ((pol->priority >= priority) && ret)
-                       break;
+       bin = xfrm_policy_inexact_lookup_rcu(net, type, family, dir, if_id);
+       if (!bin || !xfrm_policy_find_inexact_candidates(&cand, bin, saddr,
+                                                        daddr))
+               goto skip_inexact;
 
-               err = xfrm_policy_match(pol, fl, type, family, dir, if_id);
-               if (err) {
-                       if (err == -ESRCH)
-                               continue;
-                       else {
-                               ret = ERR_PTR(err);
-                               goto fail;
-                       }
-               } else {
-                       ret = pol;
-                       break;
-               }
+       pol = xfrm_policy_eval_candidates(&cand, ret, fl, type,
+                                         family, dir, if_id);
+       if (pol) {
+               ret = pol;
+               if (IS_ERR(pol))
+                       goto fail;
        }
 
+skip_inexact:
        if (read_seqcount_retry(&xfrm_policy_hash_generation, sequence))
                goto retry;
 
@@ -1239,6 +2210,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
        /* Socket policies are not hashed. */
        if (!hlist_unhashed(&pol->bydst)) {
                hlist_del_rcu(&pol->bydst);
+               hlist_del_init(&pol->bydst_inexact_list);
                hlist_del(&pol->byidx);
        }
 
@@ -1811,7 +2783,7 @@ static void xfrm_policy_queue_process(struct timer_list *t)
                pq->timeout = pq->timeout << 1;
                if (!mod_timer(&pq->hold_timer, jiffies + pq->timeout))
                        xfrm_pol_hold(pol);
-       goto out;
+               goto out;
        }
 
        dst_release(dst);
@@ -2225,11 +3197,12 @@ EXPORT_SYMBOL(xfrm_lookup_route);
 static inline int
 xfrm_secpath_reject(int idx, struct sk_buff *skb, const struct flowi *fl)
 {
+       struct sec_path *sp = skb_sec_path(skb);
        struct xfrm_state *x;
 
-       if (!skb->sp || idx < 0 || idx >= skb->sp->len)
+       if (!sp || idx < 0 || idx >= sp->len)
                return 0;
-       x = skb->sp->xvec[idx];
+       x = sp->xvec[idx];
        if (!x->type->reject)
                return 0;
        return x->type->reject(x, skb, fl);
@@ -2329,6 +3302,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
        struct flowi fl;
        int xerr_idx = -1;
        const struct xfrm_if_cb *ifcb;
+       struct sec_path *sp;
        struct xfrm_if *xi;
        u32 if_id = 0;
 
@@ -2353,11 +3327,12 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
        nf_nat_decode_session(skb, &fl, family);
 
        /* First, check used SA against their selectors. */
-       if (skb->sp) {
+       sp = skb_sec_path(skb);
+       if (sp) {
                int i;
 
-               for (i = skb->sp->len-1; i >= 0; i--) {
-                       struct xfrm_state *x = skb->sp->xvec[i];
+               for (i = sp->len - 1; i >= 0; i--) {
+                       struct xfrm_state *x = sp->xvec[i];
                        if (!xfrm_selector_match(&x->sel, &fl, family)) {
                                XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
                                return 0;
@@ -2384,7 +3359,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
        }
 
        if (!pol) {
-               if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
+               if (sp && secpath_has_nontransport(sp, 0, &xerr_idx)) {
                        xfrm_secpath_reject(xerr_idx, skb, &fl);
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
                        return 0;
@@ -2413,7 +3388,6 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 #endif
 
        if (pol->action == XFRM_POLICY_ALLOW) {
-               struct sec_path *sp;
                static struct sec_path dummy;
                struct xfrm_tmpl *tp[XFRM_MAX_DEPTH];
                struct xfrm_tmpl *stp[XFRM_MAX_DEPTH];
@@ -2421,7 +3395,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
                int ti = 0;
                int i, k;
 
-               if ((sp = skb->sp) == NULL)
+               sp = skb_sec_path(skb);
+               if (!sp)
                        sp = &dummy;
 
                for (pi = 0; pi < npols; pi++) {
@@ -2816,13 +3791,17 @@ static void xfrm_statistics_fini(struct net *net)
 static int __net_init xfrm_policy_init(struct net *net)
 {
        unsigned int hmask, sz;
-       int dir;
+       int dir, err;
 
-       if (net_eq(net, &init_net))
+       if (net_eq(net, &init_net)) {
                xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
                                           sizeof(struct xfrm_dst),
                                           0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
                                           NULL);
+               err = rhashtable_init(&xfrm_policy_inexact_table,
+                                     &xfrm_pol_inexact_params);
+               BUG_ON(err);
+       }
 
        hmask = 8 - 1;
        sz = (hmask+1) * sizeof(struct hlist_head);
@@ -2857,6 +3836,7 @@ static int __net_init xfrm_policy_init(struct net *net)
        seqlock_init(&net->xfrm.policy_hthresh.lock);
 
        INIT_LIST_HEAD(&net->xfrm.policy_all);
+       INIT_LIST_HEAD(&net->xfrm.inexact_bins);
        INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize);
        INIT_WORK(&net->xfrm.policy_hthresh.work, xfrm_hash_rebuild);
        return 0;
@@ -2875,6 +3855,7 @@ out_byidx:
 
 static void xfrm_policy_fini(struct net *net)
 {
+       struct xfrm_pol_inexact_bin *b, *t;
        unsigned int sz;
        int dir;
 
@@ -2900,6 +3881,11 @@ static void xfrm_policy_fini(struct net *net)
        sz = (net->xfrm.policy_idx_hmask + 1) * sizeof(struct hlist_head);
        WARN_ON(!hlist_empty(net->xfrm.policy_byidx));
        xfrm_hash_free(net->xfrm.policy_byidx, sz);
+
+       spin_lock_bh(&net->xfrm.xfrm_policy_lock);
+       list_for_each_entry_safe(b, t, &net->xfrm.inexact_bins, inexact_bins)
+               __xfrm_policy_inexact_prune_bin(b, true);
+       spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
 }
 
 static int __net_init xfrm_net_init(struct net *net)
@@ -3065,7 +4051,7 @@ static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *
                }
        }
        chain = &net->xfrm.policy_inexact[dir];
-       hlist_for_each_entry(pol, chain, bydst) {
+       hlist_for_each_entry(pol, chain, bydst_inexact_list) {
                if ((pol->priority >= priority) && ret)
                        break;
 
index dc4a9f1..23c9289 100644 (file)
@@ -426,6 +426,12 @@ static void xfrm_put_mode(struct xfrm_mode *mode)
        module_put(mode->owner);
 }
 
+void xfrm_state_free(struct xfrm_state *x)
+{
+       kmem_cache_free(xfrm_state_cache, x);
+}
+EXPORT_SYMBOL(xfrm_state_free);
+
 static void xfrm_state_gc_destroy(struct xfrm_state *x)
 {
        tasklet_hrtimer_cancel(&x->mtimer);
@@ -452,7 +458,7 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
        }
        xfrm_dev_state_free(x);
        security_xfrm_state_free(x);
-       kmem_cache_free(xfrm_state_cache, x);
+       xfrm_state_free(x);
 }
 
 static void xfrm_state_gc_task(struct work_struct *work)
@@ -788,7 +794,7 @@ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si)
 {
        spin_lock_bh(&net->xfrm.xfrm_state_lock);
        si->sadcnt = net->xfrm.state_num;
-       si->sadhcnt = net->xfrm.state_hmask;
+       si->sadhcnt = net->xfrm.state_hmask + 1;
        si->sadhmcnt = xfrm_state_hashmax;
        spin_unlock_bh(&net->xfrm.xfrm_state_lock);
 }
index c9a84e2..277c1c4 100644 (file)
@@ -2288,13 +2288,13 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
 
        }
 
-       kfree(x);
+       xfrm_state_free(x);
        kfree(xp);
 
        return 0;
 
 free_state:
-       kfree(x);
+       xfrm_state_free(x);
 nomem:
        return err;
 }
index be0a961..35444f4 100644 (file)
@@ -208,12 +208,20 @@ endif
 BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help 2>&1 | grep dwarfris)
 BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help 2>&1 | grep BTF)
 BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --help 2>&1 | grep -i 'usage.*llvm')
+BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \
+                         $(CLANG) -target bpf -O2 -g -c -x c - -o ./llvm_btf_verify.o; \
+                         readelf -S ./llvm_btf_verify.o | grep BTF; \
+                         /bin/rm -f ./llvm_btf_verify.o)
 
+ifneq ($(BTF_LLVM_PROBE),)
+       EXTRA_CFLAGS += -g
+else
 ifneq ($(and $(BTF_LLC_PROBE),$(BTF_PAHOLE_PROBE),$(BTF_OBJCOPY_PROBE)),)
        EXTRA_CFLAGS += -g
        LLC_FLAGS += -mattr=dwarfris
        DWARF2BTF = y
 endif
+endif
 
 # Trick to allow make to be run from this directory
 all:
index e6d7e0f..eae7b63 100644 (file)
@@ -54,6 +54,25 @@ static int populate_prog_array(const char *event, int prog_fd)
        return 0;
 }
 
+static int write_kprobe_events(const char *val)
+{
+       int fd, ret, flags;
+
+       if (val == NULL)
+               return -1;
+       else if (val[0] == '\0')
+               flags = O_WRONLY | O_TRUNC;
+       else
+               flags = O_WRONLY | O_APPEND;
+
+       fd = open("/sys/kernel/debug/tracing/kprobe_events", flags);
+
+       ret = write(fd, val, strlen(val));
+       close(fd);
+
+       return ret;
+}
+
 static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 {
        bool is_socket = strncmp(event, "socket", 6) == 0;
@@ -165,10 +184,9 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 
 #ifdef __x86_64__
                if (strncmp(event, "sys_", 4) == 0) {
-                       snprintf(buf, sizeof(buf),
-                                "echo '%c:__x64_%s __x64_%s' >> /sys/kernel/debug/tracing/kprobe_events",
-                                is_kprobe ? 'p' : 'r', event, event);
-                       err = system(buf);
+                       snprintf(buf, sizeof(buf), "%c:__x64_%s __x64_%s",
+                               is_kprobe ? 'p' : 'r', event, event);
+                       err = write_kprobe_events(buf);
                        if (err >= 0) {
                                need_normal_check = false;
                                event_prefix = "__x64_";
@@ -176,10 +194,9 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
                }
 #endif
                if (need_normal_check) {
-                       snprintf(buf, sizeof(buf),
-                                "echo '%c:%s %s' >> /sys/kernel/debug/tracing/kprobe_events",
-                                is_kprobe ? 'p' : 'r', event, event);
-                       err = system(buf);
+                       snprintf(buf, sizeof(buf), "%c:%s %s",
+                               is_kprobe ? 'p' : 'r', event, event);
+                       err = write_kprobe_events(buf);
                        if (err < 0) {
                                printf("failed to create kprobe '%s' error '%s'\n",
                                       event, strerror(errno));
@@ -284,8 +301,8 @@ static int load_maps(struct bpf_map_data *maps, int nr_maps,
                                                        numa_node);
                }
                if (map_fd[i] < 0) {
-                       printf("failed to create a map: %d %s\n",
-                              errno, strerror(errno));
+                       printf("failed to create map %d (%s): %d %s\n",
+                              i, maps[i].name, errno, strerror(errno));
                        return 1;
                }
                maps[i].fd = map_fd[i];
@@ -519,7 +536,7 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
                return 1;
 
        /* clear all kprobes */
-       i = system("echo \"\" > /sys/kernel/debug/tracing/kprobe_events");
+       i = write_kprobe_events("");
 
        /* scan over all elf sections to get license and map info */
        for (i = 1; i < ehdr.e_shnum; i++) {
index b02c531..0a197f8 100644 (file)
@@ -15,6 +15,7 @@
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/resource.h>
+#include <net/if.h>
 
 #include "bpf_util.h"
 #include "bpf/bpf.h"
@@ -34,26 +35,24 @@ static void int_exit(int sig)
 static void poll_stats(int map_fd, int interval)
 {
        unsigned int nr_cpus = bpf_num_possible_cpus();
-       const unsigned int nr_keys = 256;
-       __u64 values[nr_cpus], prev[nr_keys][nr_cpus];
-       __u32 key;
+       __u64 values[nr_cpus], prev[UINT8_MAX] = { 0 };
        int i;
 
-       memset(prev, 0, sizeof(prev));
-
        while (1) {
+               __u32 key = UINT32_MAX;
+
                sleep(interval);
 
-               for (key = 0; key < nr_keys; key++) {
+               while (bpf_map_get_next_key(map_fd, &key, &key) != -1) {
                        __u64 sum = 0;
 
                        assert(bpf_map_lookup_elem(map_fd, &key, values) == 0);
                        for (i = 0; i < nr_cpus; i++)
-                               sum += (values[i] - prev[key][i]);
-                       if (sum)
+                               sum += values[i];
+                       if (sum > prev[key])
                                printf("proto %u: %10llu pkt/s\n",
-                                      key, sum / interval);
-                       memcpy(prev[key], values, sizeof(values));
+                                      key, (sum - prev[key]) / interval);
+                       prev[key] = sum;
                }
        }
 }
@@ -61,7 +60,7 @@ static void poll_stats(int map_fd, int interval)
 static void usage(const char *prog)
 {
        fprintf(stderr,
-               "usage: %s [OPTS] IFINDEX\n\n"
+               "usage: %s [OPTS] IFACE\n\n"
                "OPTS:\n"
                "    -S    use skb-mode\n"
                "    -N    enforce native mode\n",
@@ -104,7 +103,11 @@ int main(int argc, char **argv)
                return 1;
        }
 
-       ifindex = strtoul(argv[optind], NULL, 0);
+       ifindex = if_nametoindex(argv[1]);
+       if (!ifindex) {
+               perror("if_nametoindex");
+               return 1;
+       }
 
        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
        prog_load_attr.file = filename;
index a8e7ba9..6a6be9f 100644 (file)
@@ -236,10 +236,8 @@ ifdef CONFIG_GCOV_KERNEL
 objtool_args += --no-unreachable
 endif
 ifdef CONFIG_RETPOLINE
-ifneq ($(RETPOLINE_CFLAGS),)
   objtool_args += --retpoline
 endif
-endif
 
 
 ifdef CONFIG_MODVERSIONS
index 8081b6c..34414c6 100755 (executable)
@@ -47,8 +47,8 @@ my (@stack, $re, $dre, $x, $xs, $funcre);
        $xs     = "[0-9a-f ]";  # hex character or space
        $funcre = qr/^$x* <(.*)>:$/;
        if ($arch eq 'aarch64') {
-               #ffffffc0006325cc:       a9bb7bfd        stp     x29, x30, [sp,#-80]!
-               $re = qr/^.*stp.*sp,\#-([0-9]{1,8})\]\!/o;
+               #ffffffc0006325cc:       a9bb7bfd        stp     x29, x30, [sp, #-80]!
+               $re = qr/^.*stp.*sp, \#-([0-9]{1,8})\]\!/o;
        } elsif ($arch eq 'arm') {
                #c0008ffc:      e24dd064        sub     sp, sp, #100    ; 0x64
                $re = qr/.*sub.*sp, sp, #(([0-9]{2}|[3-9])[0-9]{2})/o;
index a0149db..6c6439f 100755 (executable)
@@ -71,7 +71,7 @@ die() {
 
 # Try to figure out the source directory prefix so we can remove it from the
 # addr2line output.  HACK ALERT: This assumes that start_kernel() is in
-# kernel/init.c!  This only works for vmlinux.  Otherwise it falls back to
+# init/main.c!  This only works for vmlinux.  Otherwise it falls back to
 # printing the absolute path.
 find_dir_prefix() {
        local objfile=$1
index 2f48da9..dbd3746 100644 (file)
@@ -363,10 +363,12 @@ __visible int plugin_init(struct plugin_name_args *plugin_info,
                                                PASS_POS_INSERT_BEFORE);
 
        /*
-        * The stackleak_cleanup pass should be executed after the
-        * "reload" pass, when the stack frame size is final.
+        * The stackleak_cleanup pass should be executed before the "*free_cfg"
+        * pass. It's the moment when the stack frame size is already final,
+        * function prologues and epilogues are generated, and the
+        * machine-dependent code transformations are not done.
         */
-       PASS_INFO(stackleak_cleanup, "reload", 1, PASS_POS_INSERT_AFTER);
+       PASS_INFO(stackleak_cleanup, "*free_cfg", 1, PASS_POS_INSERT_BEFORE);
 
        if (!plugin_default_version_check(version, &gcc_version)) {
                error(G_("incompatible gcc/plugin versions"));
index 839e190..e559c62 100755 (executable)
@@ -250,12 +250,13 @@ if __name__ == '__main__':
 
     try:
         if len(args.path) and args.path[0] == '-':
-            parser.parse_lines(sys.stdin, args.maxlines, '-')
+            stdin = os.fdopen(sys.stdin.fileno(), 'rb')
+            parser.parse_lines(stdin, args.maxlines, '-')
         else:
             if args.path:
                 for p in args.path:
                     if os.path.isfile(p):
-                        parser.parse_lines(open(p), args.maxlines, p)
+                        parser.parse_lines(open(p, 'rb'), args.maxlines, p)
                     elif os.path.isdir(p):
                         scan_git_subtree(repo.head.reference.commit.tree, p)
                     else:
index 7493c0e..db00e3e 100644 (file)
@@ -395,7 +395,7 @@ usage(void)
  * When we have processed a group that starts off with a known-false
  * #if/#elif sequence (which has therefore been deleted) followed by a
  * #elif that we don't understand and therefore must keep, we edit the
- * latter into a #if to keep the nesting correct. We use strncpy() to
+ * latter into a #if to keep the nesting correct. We use memcpy() to
  * overwrite the 4 byte token "elif" with "if  " without a '\0' byte.
  *
  * When we find a true #elif in a group, the following block will
@@ -450,7 +450,7 @@ static void Idrop (void) { Fdrop();  ignoreon(); }
 static void Itrue (void) { Ftrue();  ignoreon(); }
 static void Ifalse(void) { Ffalse(); ignoreon(); }
 /* modify this line */
-static void Mpass (void) { strncpy(keyword, "if  ", 4); Pelif(); }
+static void Mpass (void) { memcpy(keyword, "if  ", 4); Pelif(); }
 static void Mtrue (void) { keywordedit("else");  state(IS_TRUE_MIDDLE); }
 static void Melif (void) { keywordedit("endif"); state(IS_FALSE_TRAILER); }
 static void Melse (void) { keywordedit("endif"); state(IS_FALSE_ELSE); }
index 6dc0751..d775e03 100644 (file)
@@ -106,6 +106,7 @@ int asymmetric_verify(struct key *keyring, const char *sig,
 
        pks.pkey_algo = "rsa";
        pks.hash_algo = hash_algo_name[hdr->hash_algo];
+       pks.encoding = "pkcs1";
        pks.digest = (u8 *)data;
        pks.digest_size = datalen;
        pks.s = hdr->sig;
index 8c94998..7489cb7 100644 (file)
@@ -580,9 +580,9 @@ void ima_update_policy(void)
        ima_update_policy_flag();
 }
 
+/* Keep the enumeration in sync with the policy_tokens! */
 enum {
-       Opt_err = -1,
-       Opt_measure = 1, Opt_dont_measure,
+       Opt_measure, Opt_dont_measure,
        Opt_appraise, Opt_dont_appraise,
        Opt_audit, Opt_hash, Opt_dont_hash,
        Opt_obj_user, Opt_obj_role, Opt_obj_type,
@@ -592,10 +592,10 @@ enum {
        Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
        Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
        Opt_appraise_type, Opt_permit_directio,
-       Opt_pcr
+       Opt_pcr, Opt_err
 };
 
-static match_table_t policy_tokens = {
+static const match_table_t policy_tokens = {
        {Opt_measure, "measure"},
        {Opt_dont_measure, "dont_measure"},
        {Opt_appraise, "appraise"},
@@ -1103,7 +1103,7 @@ void ima_policy_stop(struct seq_file *m, void *v)
 {
 }
 
-#define pt(token)      policy_tokens[token + Opt_err].pattern
+#define pt(token)      policy_tokens[token].pattern
 #define mt(token)      mask_tokens[token]
 
 /*
index 7839788..70e65a2 100644 (file)
@@ -25,7 +25,7 @@ static void keyctl_pkey_params_free(struct kernel_pkey_params *params)
 }
 
 enum {
-       Opt_err = -1,
+       Opt_err,
        Opt_enc,                /* "enc=<encoding>" eg. "enc=oaep" */
        Opt_hash,               /* "hash=<digest-name>" eg. "hash=sha1" */
 };
index ff67893..697bfc6 100644 (file)
@@ -711,7 +711,7 @@ static int key_unseal(struct trusted_key_payload *p,
 }
 
 enum {
-       Opt_err = -1,
+       Opt_err,
        Opt_new, Opt_load, Opt_update,
        Opt_keyhandle, Opt_keyauth, Opt_blobauth,
        Opt_pcrinfo, Opt_pcrlock, Opt_migratable,
index 7ce6832..a67459e 100644 (file)
@@ -5318,6 +5318,9 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname,
        addr_buf = address;
 
        while (walk_size < addrlen) {
+               if (walk_size + sizeof(sa_family_t) > addrlen)
+                       return -EINVAL;
+
                addr = addr_buf;
                switch (addr->sa_family) {
                case AF_UNSPEC:
index 74b951f..9cec812 100644 (file)
@@ -80,6 +80,9 @@ static const struct nlmsg_perm nlmsg_route_perms[] =
        { RTM_NEWSTATS,         NETLINK_ROUTE_SOCKET__NLMSG_READ },
        { RTM_GETSTATS,         NETLINK_ROUTE_SOCKET__NLMSG_READ  },
        { RTM_NEWCACHEREPORT,   NETLINK_ROUTE_SOCKET__NLMSG_READ },
+       { RTM_NEWCHAIN,         NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+       { RTM_DELCHAIN,         NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+       { RTM_GETCHAIN,         NETLINK_ROUTE_SOCKET__NLMSG_READ  },
 };
 
 static const struct nlmsg_perm nlmsg_tcpdiag_perms[] =
@@ -158,7 +161,11 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
 
        switch (sclass) {
        case SECCLASS_NETLINK_ROUTE_SOCKET:
-               /* RTM_MAX always point to RTM_SETxxxx, ie RTM_NEWxxx + 3 */
+               /* RTM_MAX always points to RTM_SETxxxx, ie RTM_NEWxxx + 3.
+                * If the BUILD_BUG_ON() below fails you must update the
+                * structures at the top of this file with the new mappings
+                * before updating the BUILD_BUG_ON() macro!
+                */
                BUILD_BUG_ON(RTM_MAX != (RTM_NEWCHAIN + 3));
                err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
                                 sizeof(nlmsg_route_perms));
@@ -170,6 +177,10 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
                break;
 
        case SECCLASS_NETLINK_XFRM_SOCKET:
+               /* If the BUILD_BUG_ON() below fails you must update the
+                * structures at the top of this file with the new mappings
+                * before updating the BUILD_BUG_ON() macro!
+                */
                BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_MAPPING);
                err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms,
                                 sizeof(nlmsg_xfrm_perms));
index 2fe459d..b7efa22 100644 (file)
@@ -245,9 +245,13 @@ int mls_context_to_sid(struct policydb *pol,
        char *rangep[2];
 
        if (!pol->mls_enabled) {
-               if ((def_sid != SECSID_NULL && oldc) || (*scontext) == '\0')
-                       return 0;
-               return -EINVAL;
+               /*
+                * With no MLS, only return -EINVAL if there is a MLS field
+                * and it did not come from an xattr.
+                */
+               if (oldc && def_sid == SECSID_NULL)
+                       return -EINVAL;
+               return 0;
        }
 
        /*
index 91dc378..bd7d18b 100644 (file)
@@ -230,7 +230,7 @@ static int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb,
                                        u32 *sid, int ckall)
 {
        u32 sid_session = SECSID_NULL;
-       struct sec_path *sp = skb->sp;
+       struct sec_path *sp = skb_sec_path(skb);
 
        if (sp) {
                int i;
@@ -408,7 +408,7 @@ int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
                              struct common_audit_data *ad)
 {
        int i;
-       struct sec_path *sp = skb->sp;
+       struct sec_path *sp = skb_sec_path(skb);
        u32 peer_sid = SECINITSID_UNLABELED;
 
        if (sp) {
index 9aa15bf..649d321 100644 (file)
@@ -348,6 +348,40 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
        return 0;
 }
 
+/* add a new kcontrol object; call with card->controls_rwsem locked */
+static int __snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
+{
+       struct snd_ctl_elem_id id;
+       unsigned int idx;
+       unsigned int count;
+
+       id = kcontrol->id;
+       if (id.index > UINT_MAX - kcontrol->count)
+               return -EINVAL;
+
+       if (snd_ctl_find_id(card, &id)) {
+               dev_err(card->dev,
+                       "control %i:%i:%i:%s:%i is already present\n",
+                       id.iface, id.device, id.subdevice, id.name, id.index);
+               return -EBUSY;
+       }
+
+       if (snd_ctl_find_hole(card, kcontrol->count) < 0)
+               return -ENOMEM;
+
+       list_add_tail(&kcontrol->list, &card->controls);
+       card->controls_count += kcontrol->count;
+       kcontrol->id.numid = card->last_numid + 1;
+       card->last_numid += kcontrol->count;
+
+       id = kcontrol->id;
+       count = kcontrol->count;
+       for (idx = 0; idx < count; idx++, id.index++, id.numid++)
+               snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
+
+       return 0;
+}
+
 /**
  * snd_ctl_add - add the control instance to the card
  * @card: the card instance
@@ -364,45 +398,18 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
  */
 int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
 {
-       struct snd_ctl_elem_id id;
-       unsigned int idx;
-       unsigned int count;
        int err = -EINVAL;
 
        if (! kcontrol)
                return err;
        if (snd_BUG_ON(!card || !kcontrol->info))
                goto error;
-       id = kcontrol->id;
-       if (id.index > UINT_MAX - kcontrol->count)
-               goto error;
 
        down_write(&card->controls_rwsem);
-       if (snd_ctl_find_id(card, &id)) {
-               up_write(&card->controls_rwsem);
-               dev_err(card->dev, "control %i:%i:%i:%s:%i is already present\n",
-                                       id.iface,
-                                       id.device,
-                                       id.subdevice,
-                                       id.name,
-                                       id.index);
-               err = -EBUSY;
-               goto error;
-       }
-       if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
-               up_write(&card->controls_rwsem);
-               err = -ENOMEM;
-               goto error;
-       }
-       list_add_tail(&kcontrol->list, &card->controls);
-       card->controls_count += kcontrol->count;
-       kcontrol->id.numid = card->last_numid + 1;
-       card->last_numid += kcontrol->count;
-       id = kcontrol->id;
-       count = kcontrol->count;
+       err = __snd_ctl_add(card, kcontrol);
        up_write(&card->controls_rwsem);
-       for (idx = 0; idx < count; idx++, id.index++, id.numid++)
-               snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
+       if (err < 0)
+               goto error;
        return 0;
 
  error:
@@ -1361,9 +1368,12 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
                kctl->tlv.c = snd_ctl_elem_user_tlv;
 
        /* This function manage to free the instance on failure. */
-       err = snd_ctl_add(card, kctl);
-       if (err < 0)
-               return err;
+       down_write(&card->controls_rwsem);
+       err = __snd_ctl_add(card, kctl);
+       if (err < 0) {
+               snd_ctl_free_one(kctl);
+               goto unlock;
+       }
        offset = snd_ctl_get_ioff(kctl, &info->id);
        snd_ctl_build_ioff(&info->id, kctl, offset);
        /*
@@ -1374,10 +1384,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
         * which locks the element.
         */
 
-       down_write(&card->controls_rwsem);
        card->user_ctl_count++;
-       up_write(&card->controls_rwsem);
 
+ unlock:
+       up_write(&card->controls_rwsem);
        return 0;
 }
 
index f8d4a41..467039b 100644 (file)
@@ -1062,8 +1062,8 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
        runtime->oss.channels = params_channels(params);
        runtime->oss.rate = params_rate(params);
 
-       vfree(runtime->oss.buffer);
-       runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
+       kvfree(runtime->oss.buffer);
+       runtime->oss.buffer = kvzalloc(runtime->oss.period_bytes, GFP_KERNEL);
        if (!runtime->oss.buffer) {
                err = -ENOMEM;
                goto failure;
@@ -2328,7 +2328,7 @@ static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime;
        runtime = substream->runtime;
-       vfree(runtime->oss.buffer);
+       kvfree(runtime->oss.buffer);
        runtime->oss.buffer = NULL;
 #ifdef CONFIG_SND_PCM_OSS_PLUGINS
        snd_pcm_oss_plugin_clear(substream);
index 141c5f3..31cb2ac 100644 (file)
@@ -66,8 +66,8 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
                return -ENXIO;
        size /= 8;
        if (plugin->buf_frames < frames) {
-               vfree(plugin->buf);
-               plugin->buf = vmalloc(size);
+               kvfree(plugin->buf);
+               plugin->buf = kvzalloc(size, GFP_KERNEL);
                plugin->buf_frames = frames;
        }
        if (!plugin->buf) {
@@ -191,7 +191,7 @@ int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin)
        if (plugin->private_free)
                plugin->private_free(plugin);
        kfree(plugin->buf_channels);
-       vfree(plugin->buf);
+       kvfree(plugin->buf);
        kfree(plugin);
        return 0;
 }
index 66c90f4..818dff1 100644 (file)
@@ -36,6 +36,7 @@
 #include <sound/timer.h>
 #include <sound/minors.h>
 #include <linux/uio.h>
+#include <linux/delay.h>
 
 #include "pcm_local.h"
 
@@ -91,12 +92,12 @@ static DECLARE_RWSEM(snd_pcm_link_rwsem);
  * and this may lead to a deadlock when the code path takes read sem
  * twice (e.g. one in snd_pcm_action_nonatomic() and another in
  * snd_pcm_stream_lock()).  As a (suboptimal) workaround, let writer to
- * spin until it gets the lock.
+ * sleep until all the readers are completed without blocking by writer.
  */
-static inline void down_write_nonblock(struct rw_semaphore *lock)
+static inline void down_write_nonfifo(struct rw_semaphore *lock)
 {
        while (!down_write_trylock(lock))
-               cond_resched();
+               msleep(1);
 }
 
 #define PCM_LOCK_DEFAULT       0
@@ -1967,7 +1968,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
                res = -ENOMEM;
                goto _nolock;
        }
-       down_write_nonblock(&snd_pcm_link_rwsem);
+       down_write_nonfifo(&snd_pcm_link_rwsem);
        write_lock_irq(&snd_pcm_link_rwlock);
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
            substream->runtime->status->state != substream1->runtime->status->state ||
@@ -2014,7 +2015,7 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
        struct snd_pcm_substream *s;
        int res = 0;
 
-       down_write_nonblock(&snd_pcm_link_rwsem);
+       down_write_nonfifo(&snd_pcm_link_rwsem);
        write_lock_irq(&snd_pcm_link_rwlock);
        if (!snd_pcm_stream_linked(substream)) {
                res = -EALREADY;
@@ -2369,7 +2370,8 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
 
 static void pcm_release_private(struct snd_pcm_substream *substream)
 {
-       snd_pcm_unlink(substream);
+       if (snd_pcm_stream_linked(substream))
+               snd_pcm_unlink(substream);
 }
 
 void snd_pcm_release_substream(struct snd_pcm_substream *substream)
index 64c3cb0..654a503 100644 (file)
@@ -30,7 +30,7 @@ static int ff400_get_clock(struct snd_ff *ff, unsigned int *rate,
        int err;
 
        err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
-                                FF400_SYNC_STATUS, &reg, sizeof(reg), 0);
+                                FF400_CLOCK_CONFIG, &reg, sizeof(reg), 0);
        if (err < 0)
                return err;
        data = le32_to_cpu(reg);
index 32453f8..3a50088 100644 (file)
@@ -1531,7 +1531,6 @@ static int snd_wss_playback_open(struct snd_pcm_substream *substream)
        if (err < 0) {
                if (chip->release_dma)
                        chip->release_dma(chip, chip->dma_private_data, chip->dma1);
-               snd_free_pages(runtime->dma_area, runtime->dma_bytes);
                return err;
        }
        chip->playback_substream = substream;
@@ -1572,7 +1571,6 @@ static int snd_wss_capture_open(struct snd_pcm_substream *substream)
        if (err < 0) {
                if (chip->release_dma)
                        chip->release_dma(chip, chip->dma_private_data, chip->dma2);
-               snd_free_pages(runtime->dma_area, runtime->dma_bytes);
                return err;
        }
        chip->capture_substream = substream;
index f4459d1..27b468f 100644 (file)
@@ -824,7 +824,7 @@ static int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
 {
        struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
        int reg = kcontrol->private_value & 0xff;
-       int shift = (kcontrol->private_value >> 8) & 0xff;
+       int shift = (kcontrol->private_value >> 8) & 0x0f;
        int mask = (kcontrol->private_value >> 16) & 0xff;
        // int invert = (kcontrol->private_value >> 24) & 0xff;
        unsigned short value, old, new;
index d8eb2b5..76f03ab 100644 (file)
@@ -2169,6 +2169,8 @@ static struct snd_pci_quirk power_save_blacklist[] = {
        /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
        SND_PCI_QUIRK(0x1849, 0xc892, "Asrock B85M-ITX", 0),
        /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+       SND_PCI_QUIRK(0x1849, 0x0397, "Asrock N68C-S UCC", 0),
+       /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
        SND_PCI_QUIRK(0x1849, 0x7662, "Asrock H81M-HDS", 0),
        /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
        SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0),
@@ -2496,6 +2498,10 @@ static const struct pci_device_id azx_ids[] = {
        /* AMD Hudson */
        { PCI_DEVICE(0x1022, 0x780d),
          .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB },
+       /* AMD Stoney */
+       { PCI_DEVICE(0x1022, 0x157a),
+         .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB |
+                        AZX_DCAPS_PM_RUNTIME },
        /* AMD Raven */
        { PCI_DEVICE(0x1022, 0x15e3),
          .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB |
index 0a24037..0a56763 100644 (file)
@@ -1177,6 +1177,7 @@ static const struct snd_pci_quirk ca0132_quirks[] = {
        SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE),
        SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ),
        SND_PCI_QUIRK(0x1102, 0x0023, "Sound Blaster Z", QUIRK_SBZ),
+       SND_PCI_QUIRK(0x1102, 0x0033, "Sound Blaster ZxR", QUIRK_SBZ),
        SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI),
        SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI),
        SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI),
@@ -8413,7 +8414,7 @@ static void ca0132_free(struct hda_codec *codec)
 
        snd_hda_power_down(codec);
        if (spec->mem_base)
-               iounmap(spec->mem_base);
+               pci_iounmap(codec->bus->pci, spec->mem_base);
        kfree(spec->spec_init_verbs);
        kfree(codec->spec);
 }
@@ -8488,7 +8489,7 @@ static void ca0132_config(struct hda_codec *codec)
                break;
        case QUIRK_AE5:
                codec_dbg(codec, "%s: QUIRK_AE5 applied.\n", __func__);
-               snd_hda_apply_pincfgs(codec, r3di_pincfgs);
+               snd_hda_apply_pincfgs(codec, ae5_pincfgs);
                break;
        }
 
index fa61674..15021c8 100644 (file)
@@ -388,6 +388,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
        case 0x10ec0285:
        case 0x10ec0298:
        case 0x10ec0289:
+       case 0x10ec0300:
                alc_update_coef_idx(codec, 0x10, 1<<9, 0);
                break;
        case 0x10ec0275:
@@ -2830,6 +2831,7 @@ enum {
        ALC269_TYPE_ALC215,
        ALC269_TYPE_ALC225,
        ALC269_TYPE_ALC294,
+       ALC269_TYPE_ALC300,
        ALC269_TYPE_ALC700,
 };
 
@@ -2864,6 +2866,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        case ALC269_TYPE_ALC215:
        case ALC269_TYPE_ALC225:
        case ALC269_TYPE_ALC294:
+       case ALC269_TYPE_ALC300:
        case ALC269_TYPE_ALC700:
                ssids = alc269_ssids;
                break;
@@ -4985,9 +4988,18 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec,
                { 0x19, 0x21a11010 }, /* dock mic */
                { }
        };
+       /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise
+        * the speaker output becomes too low by some reason on Thinkpads with
+        * ALC298 codec
+        */
+       static hda_nid_t preferred_pairs[] = {
+               0x14, 0x03, 0x17, 0x02, 0x21, 0x02,
+               0
+       };
        struct alc_spec *spec = codec->spec;
 
        if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+               spec->gen.preferred_dacs = preferred_pairs;
                spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
                snd_hda_apply_pincfgs(codec, pincfgs);
        } else if (action == HDA_FIXUP_ACT_INIT) {
@@ -5358,6 +5370,16 @@ static void alc274_fixup_bind_dacs(struct hda_codec *codec,
        spec->gen.preferred_dacs = preferred_pairs;
 }
 
+/* The DAC of NID 0x3 will introduce click/pop noise on headphones, so invalidate it */
+static void alc285_fixup_invalidate_dacs(struct hda_codec *codec,
+                             const struct hda_fixup *fix, int action)
+{
+       if (action != HDA_FIXUP_ACT_PRE_PROBE)
+               return;
+
+       snd_hda_override_wcaps(codec, 0x03, 0);
+}
+
 /* for hda_fixup_thinkpad_acpi() */
 #include "thinkpad_helper.c"
 
@@ -5495,6 +5517,12 @@ enum {
        ALC255_FIXUP_DELL_HEADSET_MIC,
        ALC295_FIXUP_HP_X360,
        ALC221_FIXUP_HP_HEADSET_MIC,
+       ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
+       ALC295_FIXUP_HP_AUTO_MUTE,
+       ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE,
+       ALC294_FIXUP_ASUS_MIC,
+       ALC294_FIXUP_ASUS_HEADSET_MIC,
+       ALC294_FIXUP_ASUS_SPK,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -5659,6 +5687,8 @@ static const struct hda_fixup alc269_fixups[] = {
        [ALC269_FIXUP_HP_MUTE_LED_MIC3] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc269_fixup_hp_mute_led_mic3,
+               .chained = true,
+               .chain_id = ALC295_FIXUP_HP_AUTO_MUTE
        },
        [ALC269_FIXUP_HP_GPIO_LED] = {
                .type = HDA_FIXUP_FUNC,
@@ -6362,6 +6392,55 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC269_FIXUP_HEADSET_MIC
        },
+       [ALC285_FIXUP_LENOVO_HEADPHONE_NOISE] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc285_fixup_invalidate_dacs,
+               .chained = true,
+               .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+       },
+       [ALC295_FIXUP_HP_AUTO_MUTE] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_auto_mute_via_amp,
+       },
+       [ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MIC
+       },
+       [ALC294_FIXUP_ASUS_MIC] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x13, 0x90a60160 }, /* use as internal mic */
+                       { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+       },
+       [ALC294_FIXUP_ASUS_HEADSET_MIC] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x19, 0x01a1113c }, /* use as headset mic, without its own jack detect */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+       },
+       [ALC294_FIXUP_ASUS_SPK] = {
+               .type = HDA_FIXUP_VERBS,
+               .v.verbs = (const struct hda_verb[]) {
+                       /* Set EAPD high */
+                       { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 },
+                       { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 },
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -6376,7 +6455,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x0762, "Acer Aspire E1-472", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
        SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
        SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
+       SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK),
+       SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
        SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS),
        SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X),
@@ -6481,6 +6564,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC),
+       SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
        SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
        SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360),
        SND_PCI_QUIRK(0x103c, 0x82bf, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
@@ -6499,6 +6583,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC),
        SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC),
        SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
+       SND_PCI_QUIRK(0x1043, 0x14a1, "ASUS UX533FD", ALC294_FIXUP_ASUS_SPK),
        SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
        SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
@@ -6531,6 +6616,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8),
        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(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),
@@ -7033,6 +7119,15 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
                {0x12, 0x90a60130},
                {0x19, 0x03a11020},
                {0x21, 0x0321101f}),
+       SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
+               {0x12, 0x90a60130},
+               {0x14, 0x90170110},
+               {0x19, 0x04a11040},
+               {0x21, 0x04211020}),
+       SND_HDA_PIN_QUIRK(0x10ec0286, 0x1025, "Acer", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE,
+               {0x12, 0x90a60130},
+               {0x17, 0x90170110},
+               {0x21, 0x02211020}),
        SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
                {0x12, 0x90a60120},
                {0x14, 0x90170110},
@@ -7096,6 +7191,14 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
        SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
                ALC292_STANDARD_PINS,
                {0x13, 0x90a60140}),
+       SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_MIC,
+               {0x14, 0x90170110},
+               {0x1b, 0x90a70130},
+               {0x21, 0x04211020}),
+       SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK,
+               {0x12, 0x90a60130},
+               {0x17, 0x90170110},
+               {0x21, 0x04211020}),
        SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
                ALC295_STANDARD_PINS,
                {0x17, 0x21014020},
@@ -7168,6 +7271,37 @@ 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)
@@ -7293,6 +7427,11 @@ 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);
+               break;
+       case 0x10ec0300:
+               spec->codec_variant = ALC269_TYPE_ALC300;
+               spec->gen.mixer_nid = 0; /* no loopback on ALC300 */
                break;
        case 0x10ec0700:
        case 0x10ec0701:
@@ -7300,6 +7439,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);
                break;
 
        }
@@ -8404,6 +8544,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
        HDA_CODEC_ENTRY(0x10ec0295, "ALC295", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0299, "ALC299", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0300, "ALC300", patch_alc269),
        HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
        HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
        HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861),
index 4e98548..e63d6e3 100644 (file)
@@ -2187,11 +2187,6 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
         */
        snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE,
                                                        AC_PWRST_D3);
-       err = snd_hdac_display_power(bus, false);
-       if (err < 0) {
-               dev_err(dev, "Cannot turn on display power on i915\n");
-               return err;
-       }
 
        hlink = snd_hdac_ext_bus_get_link(bus, dev_name(dev));
        if (!hlink) {
@@ -2201,7 +2196,11 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
 
        snd_hdac_ext_bus_link_put(bus, hlink);
 
-       return 0;
+       err = snd_hdac_display_power(bus, false);
+       if (err < 0)
+               dev_err(dev, "Cannot turn off display power on i915\n");
+
+       return err;
 }
 
 static int hdac_hdmi_runtime_resume(struct device *dev)
index 2c6ba55..bb3f0c4 100644 (file)
@@ -139,7 +139,7 @@ enum pcm186x_type {
 #define PCM186X_MAX_REGISTER           PCM186X_CURR_TRIM_CTRL
 
 /* PCM186X_PAGE */
-#define PCM186X_RESET                  0xff
+#define PCM186X_RESET                  0xfe
 
 /* PCM186X_ADCX_INPUT_SEL_X */
 #define PCM186X_ADC_INPUT_SEL_POL      BIT(7)
index 494d9d6..771b46e 100644 (file)
@@ -198,20 +198,16 @@ static const struct snd_kcontrol_new pcm3060_dapm_controls[] = {
 };
 
 static const struct snd_soc_dapm_widget pcm3060_dapm_widgets[] = {
-       SND_SOC_DAPM_OUTPUT("OUTL+"),
-       SND_SOC_DAPM_OUTPUT("OUTR+"),
-       SND_SOC_DAPM_OUTPUT("OUTL-"),
-       SND_SOC_DAPM_OUTPUT("OUTR-"),
+       SND_SOC_DAPM_OUTPUT("OUTL"),
+       SND_SOC_DAPM_OUTPUT("OUTR"),
 
        SND_SOC_DAPM_INPUT("INL"),
        SND_SOC_DAPM_INPUT("INR"),
 };
 
 static const struct snd_soc_dapm_route pcm3060_dapm_map[] = {
-       { "OUTL+", NULL, "Playback" },
-       { "OUTR+", NULL, "Playback" },
-       { "OUTL-", NULL, "Playback" },
-       { "OUTR-", NULL, "Playback" },
+       { "OUTL", NULL, "Playback" },
+       { "OUTR", NULL, "Playback" },
 
        { "Capture", NULL, "INL" },
        { "Capture", NULL, "INR" },
index a53dc17..66501b8 100644 (file)
@@ -765,38 +765,41 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
 
 static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
 {
-       u16 scratch[4];
+       unsigned int scratch[4];
+       unsigned int addr = dsp->base + ADSP2_SCRATCH0;
+       unsigned int i;
        int ret;
 
-       ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0,
-                               scratch, sizeof(scratch));
-       if (ret) {
-               adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
-               return;
+       for (i = 0; i < ARRAY_SIZE(scratch); ++i) {
+               ret = regmap_read(dsp->regmap, addr + i, &scratch[i]);
+               if (ret) {
+                       adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret);
+                       return;
+               }
        }
 
        adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
-                be16_to_cpu(scratch[0]),
-                be16_to_cpu(scratch[1]),
-                be16_to_cpu(scratch[2]),
-                be16_to_cpu(scratch[3]));
+                scratch[0], scratch[1], scratch[2], scratch[3]);
 }
 
 static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp)
 {
-       u32 scratch[2];
+       unsigned int scratch[2];
        int ret;
 
-       ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1,
-                             scratch, sizeof(scratch));
-
+       ret = regmap_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1,
+                         &scratch[0]);
        if (ret) {
-               adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
+               adsp_err(dsp, "Failed to read SCRATCH0_1: %d\n", ret);
                return;
        }
 
-       scratch[0] = be32_to_cpu(scratch[0]);
-       scratch[1] = be32_to_cpu(scratch[1]);
+       ret = regmap_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH2_3,
+                         &scratch[1]);
+       if (ret) {
+               adsp_err(dsp, "Failed to read SCRATCH2_3: %d\n", ret);
+               return;
+       }
 
        adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
                 scratch[0] & 0xFFFF,
index 0caa1f4..18e7177 100644 (file)
@@ -101,22 +101,42 @@ config SND_SST_ATOM_HIFI2_PLATFORM_ACPI
          codec, then enable this option by saying Y or m. This is a
          recommended option
 
-config SND_SOC_INTEL_SKYLAKE_SSP_CLK
-       tristate
-
 config SND_SOC_INTEL_SKYLAKE
        tristate "SKL/BXT/KBL/GLK/CNL... Platforms"
        depends on PCI && ACPI
+       select SND_SOC_INTEL_SKYLAKE_COMMON
+       help
+         If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/
+         GeminiLake or CannonLake platform with the DSP enabled in the BIOS
+         then enable this option by saying Y or m.
+
+if  SND_SOC_INTEL_SKYLAKE
+
+config SND_SOC_INTEL_SKYLAKE_SSP_CLK
+       tristate
+
+config SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC
+       bool "HDAudio codec support"
+       help
+         If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/
+         GeminiLake or CannonLake platform with an HDaudio codec
+         then enable this option by saying Y
+
+config SND_SOC_INTEL_SKYLAKE_COMMON
+       tristate
        select SND_HDA_EXT_CORE
        select SND_HDA_DSP_LOADER
        select SND_SOC_TOPOLOGY
        select SND_SOC_INTEL_SST
+       select SND_SOC_HDAC_HDA if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC
        select SND_SOC_ACPI_INTEL_MATCH
        help
          If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/
          GeminiLake or CannonLake platform with the DSP enabled in the BIOS
          then enable this option by saying Y or m.
 
+endif ## SND_SOC_INTEL_SKYLAKE
+
 config SND_SOC_ACPI_INTEL_MATCH
        tristate
        select SND_SOC_ACPI if ACPI
index 73ca135..b177db2 100644 (file)
@@ -293,16 +293,6 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH
          Say Y if you have such a device.
          If unsure select "N".
 
-config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH
-       tristate "SKL/KBL/BXT/APL with HDA Codecs"
-       select SND_SOC_HDAC_HDMI
-       select SND_SOC_HDAC_HDA
-       help
-         This adds support for ASoC machine driver for Intel platforms
-         SKL/KBL/BXT/APL with iDisp, HDA audio codecs.
-          Say Y or m if you have such a device. This is a recommended option.
-         If unsure select "N".
-
 config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH
        tristate "GLK with RT5682 and MAX98357A in I2S Mode"
        depends on MFD_INTEL_LPSS && I2C && ACPI
@@ -319,4 +309,18 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH
 
 endif ## SND_SOC_INTEL_SKYLAKE
 
+if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC
+
+config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH
+       tristate "SKL/KBL/BXT/APL with HDA Codecs"
+       select SND_SOC_HDAC_HDMI
+       # SND_SOC_HDAC_HDA is already selected
+       help
+         This adds support for ASoC machine driver for Intel platforms
+         SKL/KBL/BXT/APL with iDisp, HDA audio codecs.
+          Say Y or m if you have such a device. This is a recommended option.
+         If unsure select "N".
+
+endif ## SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC
+
 endif ## SND_SOC_INTEL_MACH
index db6976f..9d9f6e4 100644 (file)
@@ -19,6 +19,7 @@
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
+#include <linux/dmi.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
@@ -35,6 +36,8 @@
 #define CHT_PLAT_CLK_3_HZ      19200000
 #define CHT_CODEC_DAI  "HiFi"
 
+#define QUIRK_PMC_PLT_CLK_0                            0x01
+
 struct cht_mc_private {
        struct clk *mclk;
        struct snd_soc_jack jack;
@@ -385,11 +388,29 @@ static struct snd_soc_card snd_soc_card_cht = {
        .num_controls = ARRAY_SIZE(cht_mc_controls),
 };
 
+static const struct dmi_system_id cht_max98090_quirk_table[] = {
+       {
+               /* Swanky model Chromebook (Toshiba Chromebook 2) */
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Swanky"),
+               },
+               .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+       },
+       {}
+};
+
 static int snd_cht_mc_probe(struct platform_device *pdev)
 {
+       const struct dmi_system_id *dmi_id;
        struct device *dev = &pdev->dev;
        int ret_val = 0;
        struct cht_mc_private *drv;
+       const char *mclk_name;
+       int quirks = 0;
+
+       dmi_id = dmi_first_match(cht_max98090_quirk_table);
+       if (dmi_id)
+               quirks = (unsigned long)dmi_id->driver_data;
 
        drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
        if (!drv)
@@ -411,11 +432,16 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
        snd_soc_card_cht.dev = &pdev->dev;
        snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
 
-       drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+       if (quirks & QUIRK_PMC_PLT_CLK_0)
+               mclk_name = "pmc_plt_clk_0";
+       else
+               mclk_name = "pmc_plt_clk_3";
+
+       drv->mclk = devm_clk_get(&pdev->dev, mclk_name);
        if (IS_ERR(drv->mclk)) {
                dev_err(&pdev->dev,
-                       "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
-                       PTR_ERR(drv->mclk));
+                       "Failed to get MCLK from %s: %ld\n",
+                       mclk_name, PTR_ERR(drv->mclk));
                return PTR_ERR(drv->mclk);
        }
 
index 2922562..7487f38 100644 (file)
@@ -37,7 +37,9 @@
 #include "skl.h"
 #include "skl-sst-dsp.h"
 #include "skl-sst-ipc.h"
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC)
 #include "../../../soc/codecs/hdac_hda.h"
+#endif
 
 /*
  * initialize the PCI registers
@@ -658,6 +660,8 @@ static void skl_clock_device_unregister(struct skl *skl)
                platform_device_unregister(skl->clk_dev);
 }
 
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC)
+
 #define IDISP_INTEL_VENDOR_ID  0x80860000
 
 /*
@@ -676,6 +680,8 @@ static void load_codec_module(struct hda_codec *codec)
 #endif
 }
 
+#endif /* CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC */
+
 /*
  * Probe the given codec address
  */
@@ -685,9 +691,11 @@ static int probe_codec(struct hdac_bus *bus, int addr)
                (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
        unsigned int res = -1;
        struct skl *skl = bus_to_skl(bus);
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC)
        struct hdac_hda_priv *hda_codec;
-       struct hdac_device *hdev;
        int err;
+#endif
+       struct hdac_device *hdev;
 
        mutex_lock(&bus->cmd_mutex);
        snd_hdac_bus_send_cmd(bus, cmd);
@@ -697,6 +705,7 @@ static int probe_codec(struct hdac_bus *bus, int addr)
                return -EIO;
        dev_dbg(bus->dev, "codec #%d probed OK: %x\n", addr, res);
 
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC)
        hda_codec = devm_kzalloc(&skl->pci->dev, sizeof(*hda_codec),
                                 GFP_KERNEL);
        if (!hda_codec)
@@ -715,6 +724,13 @@ static int probe_codec(struct hdac_bus *bus, int addr)
                load_codec_module(&hda_codec->codec);
        }
        return 0;
+#else
+       hdev = devm_kzalloc(&skl->pci->dev, sizeof(*hdev), GFP_KERNEL);
+       if (!hdev)
+               return -ENOMEM;
+
+       return snd_hdac_ext_bus_device_init(bus, addr, hdev);
+#endif /* CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC */
 }
 
 /* Codec initialization */
@@ -815,6 +831,12 @@ static void skl_probe_work(struct work_struct *work)
                }
        }
 
+       /*
+        * we are done probing so decrement link counts
+        */
+       list_for_each_entry(hlink, &bus->hlink_list, list)
+               snd_hdac_ext_bus_link_put(bus, hlink);
+
        if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
                err = snd_hdac_display_power(bus, false);
                if (err < 0) {
@@ -824,12 +846,6 @@ static void skl_probe_work(struct work_struct *work)
                }
        }
 
-       /*
-        * we are done probing so decrement link counts
-        */
-       list_for_each_entry(hlink, &bus->hlink_list, list)
-               snd_hdac_ext_bus_link_put(bus, hlink);
-
        /* configure PM */
        pm_runtime_put_noidle(bus->dev);
        pm_runtime_allow(bus->dev);
@@ -870,7 +886,7 @@ static int skl_create(struct pci_dev *pci,
        hbus = skl_to_hbus(skl);
        bus = skl_to_bus(skl);
 
-#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDA)
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC)
        ext_ops = snd_soc_hdac_hda_get_ops();
 #endif
        snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, ext_ops);
index d5ae9eb..fed45b4 100644 (file)
@@ -36,6 +36,8 @@
 #include "../codecs/twl6040.h"
 
 struct abe_twl6040 {
+       struct snd_soc_card card;
+       struct snd_soc_dai_link dai_links[2];
        int     jack_detection; /* board can detect jack events */
        int     mclk_freq;      /* MCLK frequency speed for twl6040 */
 };
@@ -208,40 +210,10 @@ static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd)
                                ARRAY_SIZE(dmic_audio_map));
 }
 
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link abe_twl6040_dai_links[] = {
-       {
-               .name = "TWL6040",
-               .stream_name = "TWL6040",
-               .codec_dai_name = "twl6040-legacy",
-               .codec_name = "twl6040-codec",
-               .init = omap_abe_twl6040_init,
-               .ops = &omap_abe_ops,
-       },
-       {
-               .name = "DMIC",
-               .stream_name = "DMIC Capture",
-               .codec_dai_name = "dmic-hifi",
-               .codec_name = "dmic-codec",
-               .init = omap_abe_dmic_init,
-               .ops = &omap_abe_dmic_ops,
-       },
-};
-
-/* Audio machine driver */
-static struct snd_soc_card omap_abe_card = {
-       .owner = THIS_MODULE,
-
-       .dapm_widgets = twl6040_dapm_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets),
-       .dapm_routes = audio_map,
-       .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
 static int omap_abe_probe(struct platform_device *pdev)
 {
        struct device_node *node = pdev->dev.of_node;
-       struct snd_soc_card *card = &omap_abe_card;
+       struct snd_soc_card *card;
        struct device_node *dai_node;
        struct abe_twl6040 *priv;
        int num_links = 0;
@@ -252,12 +224,18 @@ static int omap_abe_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       card->dev = &pdev->dev;
-
        priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
        if (priv == NULL)
                return -ENOMEM;
 
+       card = &priv->card;
+       card->dev = &pdev->dev;
+       card->owner = THIS_MODULE;
+       card->dapm_widgets = twl6040_dapm_widgets;
+       card->num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets);
+       card->dapm_routes = audio_map;
+       card->num_dapm_routes = ARRAY_SIZE(audio_map);
+
        if (snd_soc_of_parse_card_name(card, "ti,model")) {
                dev_err(&pdev->dev, "Card name is not provided\n");
                return -ENODEV;
@@ -274,14 +252,27 @@ static int omap_abe_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "McPDM node is not provided\n");
                return -EINVAL;
        }
-       abe_twl6040_dai_links[0].cpu_of_node = dai_node;
-       abe_twl6040_dai_links[0].platform_of_node = dai_node;
+
+       priv->dai_links[0].name = "DMIC";
+       priv->dai_links[0].stream_name = "TWL6040";
+       priv->dai_links[0].cpu_of_node = dai_node;
+       priv->dai_links[0].platform_of_node = dai_node;
+       priv->dai_links[0].codec_dai_name = "twl6040-legacy";
+       priv->dai_links[0].codec_name = "twl6040-codec";
+       priv->dai_links[0].init = omap_abe_twl6040_init;
+       priv->dai_links[0].ops = &omap_abe_ops;
 
        dai_node = of_parse_phandle(node, "ti,dmic", 0);
        if (dai_node) {
                num_links = 2;
-               abe_twl6040_dai_links[1].cpu_of_node = dai_node;
-               abe_twl6040_dai_links[1].platform_of_node = dai_node;
+               priv->dai_links[1].name = "TWL6040";
+               priv->dai_links[1].stream_name = "DMIC Capture";
+               priv->dai_links[1].cpu_of_node = dai_node;
+               priv->dai_links[1].platform_of_node = dai_node;
+               priv->dai_links[1].codec_dai_name = "dmic-hifi";
+               priv->dai_links[1].codec_name = "dmic-codec";
+               priv->dai_links[1].init = omap_abe_dmic_init;
+               priv->dai_links[1].ops = &omap_abe_dmic_ops;
        } else {
                num_links = 1;
        }
@@ -300,7 +291,7 @@ static int omap_abe_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       card->dai_link = abe_twl6040_dai_links;
+       card->dai_link = priv->dai_links;
        card->num_links = num_links;
 
        snd_soc_card_set_drvdata(card, priv);
index fe96627..cba9645 100644 (file)
@@ -48,6 +48,8 @@ struct omap_dmic {
        struct device *dev;
        void __iomem *io_base;
        struct clk *fclk;
+       struct pm_qos_request pm_qos_req;
+       int latency;
        int fclk_freq;
        int out_freq;
        int clk_div;
@@ -124,6 +126,8 @@ static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream,
 
        mutex_lock(&dmic->mutex);
 
+       pm_qos_remove_request(&dmic->pm_qos_req);
+
        if (!dai->active)
                dmic->active = 0;
 
@@ -228,6 +232,8 @@ static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream,
        /* packet size is threshold * channels */
        dma_data = snd_soc_dai_get_dma_data(dai, substream);
        dma_data->maxburst = dmic->threshold * channels;
+       dmic->latency = (OMAP_DMIC_THRES_MAX - dmic->threshold) * USEC_PER_SEC /
+                       params_rate(params);
 
        return 0;
 }
@@ -238,6 +244,9 @@ static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream,
        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
        u32 ctrl;
 
+       if (pm_qos_request_active(&dmic->pm_qos_req))
+               pm_qos_update_request(&dmic->pm_qos_req, dmic->latency);
+
        /* Configure uplink threshold */
        omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold);
 
index d0ebb6b..2d6decb 100644 (file)
@@ -308,9 +308,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
                        pkt_size = channels;
                }
 
-               latency = ((((buffer_size - pkt_size) / channels) * 1000)
-                                / (params->rate_num / params->rate_den));
-
+               latency = (buffer_size - pkt_size) / channels;
+               latency = latency * USEC_PER_SEC /
+                         (params->rate_num / params->rate_den);
                mcbsp->latency[substream->stream] = latency;
 
                omap_mcbsp_set_threshold(substream, pkt_size);
index 4c1be36..7d5bdc5 100644 (file)
@@ -54,6 +54,8 @@ struct omap_mcpdm {
        unsigned long phys_base;
        void __iomem *io_base;
        int irq;
+       struct pm_qos_request pm_qos_req;
+       int latency[2];
 
        struct mutex mutex;
 
@@ -277,6 +279,9 @@ static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+       int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+       int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
 
        mutex_lock(&mcpdm->mutex);
 
@@ -289,6 +294,14 @@ static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
                }
        }
 
+       if (mcpdm->latency[stream2])
+               pm_qos_update_request(&mcpdm->pm_qos_req,
+                                     mcpdm->latency[stream2]);
+       else if (mcpdm->latency[stream1])
+               pm_qos_remove_request(&mcpdm->pm_qos_req);
+
+       mcpdm->latency[stream1] = 0;
+
        mutex_unlock(&mcpdm->mutex);
 }
 
@@ -300,7 +313,7 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
        int stream = substream->stream;
        struct snd_dmaengine_dai_dma_data *dma_data;
        u32 threshold;
-       int channels;
+       int channels, latency;
        int link_mask = 0;
 
        channels = params_channels(params);
@@ -344,14 +357,25 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
 
                dma_data->maxburst =
                                (MCPDM_DN_THRES_MAX - threshold) * channels;
+               latency = threshold;
        } else {
                /* If playback is not running assume a stereo stream to come */
                if (!mcpdm->config[!stream].link_mask)
                        mcpdm->config[!stream].link_mask = (0x3 << 3);
 
                dma_data->maxburst = threshold * channels;
+               latency = (MCPDM_DN_THRES_MAX - threshold);
        }
 
+       /*
+        * The DMA must act to a DMA request within latency time (usec) to avoid
+        * under/overflow
+        */
+       mcpdm->latency[stream] = latency * USEC_PER_SEC / params_rate(params);
+
+       if (!mcpdm->latency[stream])
+               mcpdm->latency[stream] = 10;
+
        /* Check if we need to restart McPDM with this stream */
        if (mcpdm->config[stream].link_mask &&
            mcpdm->config[stream].link_mask != link_mask)
@@ -366,6 +390,20 @@ static int omap_mcpdm_prepare(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+       struct pm_qos_request *pm_qos_req = &mcpdm->pm_qos_req;
+       int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+       int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+       int latency = mcpdm->latency[stream2];
+
+       /* Prevent omap hardware from hitting off between FIFO fills */
+       if (!latency || mcpdm->latency[stream1] < latency)
+               latency = mcpdm->latency[stream1];
+
+       if (pm_qos_request_active(pm_qos_req))
+               pm_qos_update_request(pm_qos_req, latency);
+       else if (latency)
+               pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency);
 
        if (!omap_mcpdm_active(mcpdm)) {
                omap_mcpdm_start(mcpdm);
@@ -427,6 +465,9 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
        free_irq(mcpdm->irq, (void *)mcpdm);
        pm_runtime_disable(mcpdm->dev);
 
+       if (pm_qos_request_active(&mcpdm->pm_qos_req))
+               pm_qos_remove_request(&mcpdm->pm_qos_req);
+
        return 0;
 }
 
index eb1b9da..4715527 100644 (file)
@@ -13,6 +13,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
        struct device_node *cpu = NULL;
        struct device *dev = card->dev;
        struct snd_soc_dai_link *link;
+       struct of_phandle_args args;
        int ret, num_links;
 
        ret = snd_soc_of_parse_card_name(card, "model");
@@ -47,12 +48,14 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
                        goto err;
                }
 
-               link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0);
-               if (!link->cpu_of_node) {
+               ret = of_parse_phandle_with_args(cpu, "sound-dai",
+                                       "#sound-dai-cells", 0, &args);
+               if (ret) {
                        dev_err(card->dev, "error getting cpu phandle\n");
-                       ret = -EINVAL;
                        goto err;
                }
+               link->cpu_of_node = args.np;
+               link->id = args.args[0];
 
                ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name);
                if (ret) {
index 60ff4a2..8f6c8fc 100644 (file)
@@ -1112,204 +1112,204 @@ static int q6afe_of_xlate_dai_name(struct snd_soc_component *component,
 }
 
 static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = {
-       SND_SOC_DAPM_AIF_OUT("HDMI_RX", "HDMI Playback", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SLIMBUS_1_RX", "Slimbus1 Playback", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SLIMBUS_2_RX", "Slimbus2 Playback", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SLIMBUS_3_RX", "Slimbus3 Playback", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_RX", "Slimbus6 Playback", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SLIMBUS_2_TX", "Slimbus2 Capture", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SLIMBUS_5_TX", "Slimbus5 Capture", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SLIMBUS_6_TX", "Slimbus6 Capture", 0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback",
+       SND_SOC_DAPM_AIF_IN("HDMI_RX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_IN("SLIMBUS_0_RX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_IN("SLIMBUS_1_RX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_IN("SLIMBUS_2_RX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_IN("SLIMBUS_3_RX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_IN("SLIMBUS_4_RX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_IN("SLIMBUS_5_RX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_IN("SLIMBUS_6_RX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_TX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("SLIMBUS_1_TX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("SLIMBUS_2_TX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("SLIMBUS_3_TX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_TX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_TX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_TX", NULL, 0, 0, 0, 0),
+       SND_SOC_DAPM_AIF_IN("QUAT_MI2S_RX", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture",
+       SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_TX", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX", "Tertiary MI2S Playback",
+       SND_SOC_DAPM_AIF_IN("TERT_MI2S_RX", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX", "Tertiary MI2S Capture",
+       SND_SOC_DAPM_AIF_OUT("TERT_MI2S_TX", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX", "Secondary MI2S Playback",
+       SND_SOC_DAPM_AIF_IN("SEC_MI2S_RX", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX", "Secondary MI2S Capture",
+       SND_SOC_DAPM_AIF_OUT("SEC_MI2S_TX", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX_SD1",
+       SND_SOC_DAPM_AIF_IN("SEC_MI2S_RX_SD1",
                        "Secondary MI2S Playback SD1",
                        0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("PRI_MI2S_RX", "Primary MI2S Playback",
+       SND_SOC_DAPM_AIF_IN("PRI_MI2S_RX", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("PRI_MI2S_TX", "Primary MI2S Capture",
+       SND_SOC_DAPM_AIF_OUT("PRI_MI2S_TX", NULL,
                                                0, 0, 0, 0),
 
-       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_0", "Primary TDM0 Playback",
+       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_0", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_1", "Primary TDM1 Playback",
+       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_1", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_2", "Primary TDM2 Playback",
+       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_2", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_3", "Primary TDM3 Playback",
+       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_3", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_4", "Primary TDM4 Playback",
+       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_4", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_5", "Primary TDM5 Playback",
+       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_5", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_6", "Primary TDM6 Playback",
+       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_6", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_7", "Primary TDM7 Playback",
+       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_7", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_0", "Primary TDM0 Capture",
+       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_0", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_1", "Primary TDM1 Capture",
+       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_1", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_2", "Primary TDM2 Capture",
+       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_2", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_3", "Primary TDM3 Capture",
+       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_3", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_4", "Primary TDM4 Capture",
+       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_4", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_5", "Primary TDM5 Capture",
+       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_5", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_6", "Primary TDM6 Capture",
+       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_6", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_7", "Primary TDM7 Capture",
+       SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_7", NULL,
                                                0, 0, 0, 0),
 
-       SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_0", "Secondary TDM0 Playback",
+       SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_0", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_1", "Secondary TDM1 Playback",
+       SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_1", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_2", "Secondary TDM2 Playback",
+       SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_2", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_3", "Secondary TDM3 Playback",
+       SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_3", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_4", "Secondary TDM4 Playback",
+       SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_4", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_5", "Secondary TDM5 Playback",
+       SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_5", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_6", "Secondary TDM6 Playback",
+       SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_6", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_7", "Secondary TDM7 Playback",
+       SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_7", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_0", "Secondary TDM0 Capture",
+       SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_0", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_1", "Secondary TDM1 Capture",
+       SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_1", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_2", "Secondary TDM2 Capture",
+       SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_2", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_3", "Secondary TDM3 Capture",
+       SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_3", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_4", "Secondary TDM4 Capture",
+       SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_4", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_5", "Secondary TDM5 Capture",
+       SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_5", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_6", "Secondary TDM6 Capture",
+       SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_6", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_7", "Secondary TDM7 Capture",
+       SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_7", NULL,
                                                0, 0, 0, 0),
 
-       SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_0", "Tertiary TDM0 Playback",
+       SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_0", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_1", "Tertiary TDM1 Playback",
+       SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_1", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_2", "Tertiary TDM2 Playback",
+       SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_2", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_3", "Tertiary TDM3 Playback",
+       SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_3", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_4", "Tertiary TDM4 Playback",
+       SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_4", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_5", "Tertiary TDM5 Playback",
+       SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_5", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_6", "Tertiary TDM6 Playback",
+       SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_6", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_7", "Tertiary TDM7 Playback",
+       SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_7", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_0", "Tertiary TDM0 Capture",
+       SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_0", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_1", "Tertiary TDM1 Capture",
+       SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_1", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_2", "Tertiary TDM2 Capture",
+       SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_2", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_3", "Tertiary TDM3 Capture",
+       SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_3", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_4", "Tertiary TDM4 Capture",
+       SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_4", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_5", "Tertiary TDM5 Capture",
+       SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_5", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_6", "Tertiary TDM6 Capture",
+       SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_6", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_7", "Tertiary TDM7 Capture",
+       SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_7", NULL,
                                                0, 0, 0, 0),
 
-       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_0", "Quaternary TDM0 Playback",
+       SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_0", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_1", "Quaternary TDM1 Playback",
+       SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_1", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_2", "Quaternary TDM2 Playback",
+       SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_2", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_3", "Quaternary TDM3 Playback",
+       SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_3", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_4", "Quaternary TDM4 Playback",
+       SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_4", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_5", "Quaternary TDM5 Playback",
+       SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_5", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_6", "Quaternary TDM6 Playback",
+       SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_6", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_7", "Quaternary TDM7 Playback",
+       SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_7", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_0", "Quaternary TDM0 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_0", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_1", "Quaternary TDM1 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_1", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_2", "Quaternary TDM2 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_2", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_3", "Quaternary TDM3 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_3", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_4", "Quaternary TDM4 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_4", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_5", "Quaternary TDM5 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_5", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_6", "Quaternary TDM6 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_6", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_7", "Quaternary TDM7 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_7", NULL,
                                                0, 0, 0, 0),
 
-       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_0", "Quinary TDM0 Playback",
+       SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_0", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_1", "Quinary TDM1 Playback",
+       SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_1", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_2", "Quinary TDM2 Playback",
+       SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_2", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_3", "Quinary TDM3 Playback",
+       SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_3", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_4", "Quinary TDM4 Playback",
+       SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_4", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_5", "Quinary TDM5 Playback",
+       SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_5", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_6", "Quinary TDM6 Playback",
+       SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_6", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_7", "Quinary TDM7 Playback",
+       SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_7", NULL,
                             0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_0", "Quinary TDM0 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_0", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_1", "Quinary TDM1 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_1", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_2", "Quinary TDM2 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_2", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_3", "Quinary TDM3 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_3", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_4", "Quinary TDM4 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_4", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_5", "Quinary TDM5 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_5", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_6", "Quinary TDM6 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_6", NULL,
                                                0, 0, 0, 0),
-       SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_7", "Quinary TDM7 Capture",
+       SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_7", NULL,
                                                0, 0, 0, 0),
 };
 
index 000775b..829b5e9 100644 (file)
 #define AFE_PORT_I2S_SD1               0x2
 #define AFE_PORT_I2S_SD2               0x3
 #define AFE_PORT_I2S_SD3               0x4
-#define AFE_PORT_I2S_SD0_MASK          BIT(0x1)
-#define AFE_PORT_I2S_SD1_MASK          BIT(0x2)
-#define AFE_PORT_I2S_SD2_MASK          BIT(0x3)
-#define AFE_PORT_I2S_SD3_MASK          BIT(0x4)
-#define AFE_PORT_I2S_SD0_1_MASK                GENMASK(2, 1)
-#define AFE_PORT_I2S_SD2_3_MASK                GENMASK(4, 3)
-#define AFE_PORT_I2S_SD0_1_2_MASK      GENMASK(3, 1)
-#define AFE_PORT_I2S_SD0_1_2_3_MASK    GENMASK(4, 1)
+#define AFE_PORT_I2S_SD0_MASK          BIT(0x0)
+#define AFE_PORT_I2S_SD1_MASK          BIT(0x1)
+#define AFE_PORT_I2S_SD2_MASK          BIT(0x2)
+#define AFE_PORT_I2S_SD3_MASK          BIT(0x3)
+#define AFE_PORT_I2S_SD0_1_MASK                GENMASK(1, 0)
+#define AFE_PORT_I2S_SD2_3_MASK                GENMASK(3, 2)
+#define AFE_PORT_I2S_SD0_1_2_MASK      GENMASK(2, 0)
+#define AFE_PORT_I2S_SD0_1_2_3_MASK    GENMASK(3, 0)
 #define AFE_PORT_I2S_QUAD01            0x5
 #define AFE_PORT_I2S_QUAD23            0x6
 #define AFE_PORT_I2S_6CHS              0x7
index a16c71c..86115de 100644 (file)
@@ -122,7 +122,6 @@ static struct snd_pcm_hardware q6asm_dai_hardware_playback = {
                        .rate_max =     48000,                          \
                },                                                      \
                .name = "MultiMedia"#num,                               \
-               .probe = fe_dai_probe,                                  \
                .id = MSM_FRONTEND_DAI_MULTIMEDIA##num,                 \
        }
 
@@ -511,38 +510,6 @@ static void q6asm_dai_pcm_free(struct snd_pcm *pcm)
        }
 }
 
-static const struct snd_soc_dapm_route afe_pcm_routes[] = {
-       {"MM_DL1",  NULL, "MultiMedia1 Playback" },
-       {"MM_DL2",  NULL, "MultiMedia2 Playback" },
-       {"MM_DL3",  NULL, "MultiMedia3 Playback" },
-       {"MM_DL4",  NULL, "MultiMedia4 Playback" },
-       {"MM_DL5",  NULL, "MultiMedia5 Playback" },
-       {"MM_DL6",  NULL, "MultiMedia6 Playback" },
-       {"MM_DL7",  NULL, "MultiMedia7 Playback" },
-       {"MM_DL7",  NULL, "MultiMedia8 Playback" },
-       {"MultiMedia1 Capture", NULL, "MM_UL1"},
-       {"MultiMedia2 Capture", NULL, "MM_UL2"},
-       {"MultiMedia3 Capture", NULL, "MM_UL3"},
-       {"MultiMedia4 Capture", NULL, "MM_UL4"},
-       {"MultiMedia5 Capture", NULL, "MM_UL5"},
-       {"MultiMedia6 Capture", NULL, "MM_UL6"},
-       {"MultiMedia7 Capture", NULL, "MM_UL7"},
-       {"MultiMedia8 Capture", NULL, "MM_UL8"},
-
-};
-
-static int fe_dai_probe(struct snd_soc_dai *dai)
-{
-       struct snd_soc_dapm_context *dapm;
-
-       dapm = snd_soc_component_get_dapm(dai->component);
-       snd_soc_dapm_add_routes(dapm, afe_pcm_routes,
-                               ARRAY_SIZE(afe_pcm_routes));
-
-       return 0;
-}
-
-
 static const struct snd_soc_component_driver q6asm_fe_dai_component = {
        .name           = DRV_NAME,
        .ops            = &q6asm_dai_ops,
index c6b5157..d61b840 100644 (file)
@@ -909,6 +909,25 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"MM_UL6", NULL, "MultiMedia6 Mixer"},
        {"MM_UL7", NULL, "MultiMedia7 Mixer"},
        {"MM_UL8", NULL, "MultiMedia8 Mixer"},
+
+       {"MM_DL1",  NULL, "MultiMedia1 Playback" },
+       {"MM_DL2",  NULL, "MultiMedia2 Playback" },
+       {"MM_DL3",  NULL, "MultiMedia3 Playback" },
+       {"MM_DL4",  NULL, "MultiMedia4 Playback" },
+       {"MM_DL5",  NULL, "MultiMedia5 Playback" },
+       {"MM_DL6",  NULL, "MultiMedia6 Playback" },
+       {"MM_DL7",  NULL, "MultiMedia7 Playback" },
+       {"MM_DL8",  NULL, "MultiMedia8 Playback" },
+
+       {"MultiMedia1 Capture", NULL, "MM_UL1"},
+       {"MultiMedia2 Capture", NULL, "MM_UL2"},
+       {"MultiMedia3 Capture", NULL, "MM_UL3"},
+       {"MultiMedia4 Capture", NULL, "MM_UL4"},
+       {"MultiMedia5 Capture", NULL, "MM_UL5"},
+       {"MultiMedia6 Capture", NULL, "MM_UL6"},
+       {"MultiMedia7 Capture", NULL, "MM_UL7"},
+       {"MultiMedia8 Capture", NULL, "MM_UL8"},
+
 };
 
 static int routing_hw_params(struct snd_pcm_substream *substream,
index 9e7b5fa..4ac78d7 100644 (file)
@@ -33,6 +33,7 @@ static const struct snd_pcm_hardware snd_rockchip_hardware = {
 
 static const struct snd_dmaengine_pcm_config rk_dmaengine_pcm_config = {
        .pcm_hardware = &snd_rockchip_hardware,
+       .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
        .prealloc_buffer_size = 32 * 1024,
 };
 
index fcb4df2..6ec78f3 100644 (file)
@@ -306,7 +306,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
        if (rsnd_ssi_is_multi_slave(mod, io))
                return 0;
 
-       if (ssi->rate) {
+       if (ssi->usrcnt > 1) {
                if (ssi->rate != rate) {
                        dev_err(dev, "SSI parent/child should use same rate\n");
                        return -EINVAL;
index b8e72b5..4fb29f0 100644 (file)
@@ -10,11 +10,17 @@ struct snd_soc_acpi_mach *
 snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines)
 {
        struct snd_soc_acpi_mach *mach;
+       struct snd_soc_acpi_mach *mach_alt;
 
        for (mach = machines; mach->id[0]; mach++) {
                if (acpi_dev_present(mach->id, NULL, -1)) {
-                       if (mach->machine_quirk)
-                               mach = mach->machine_quirk(mach);
+                       if (mach->machine_quirk) {
+                               mach_alt = mach->machine_quirk(mach);
+                               if (!mach_alt)
+                                       continue; /* not full match, ignore */
+                               mach = mach_alt;
+                       }
+
                        return mach;
                }
        }
index 6ddcf12..b29d0f6 100644 (file)
@@ -2131,6 +2131,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
        }
 
        card->instantiated = 1;
+       dapm_mark_endpoints_dirty(card);
        snd_soc_dapm_sync(&card->dapm);
        mutex_unlock(&card->mutex);
        mutex_unlock(&client_mutex);
index ea05cc9..211589b 100644 (file)
@@ -390,7 +390,7 @@ static int stm32_sai_add_mclk_provider(struct stm32_sai_sub_data *sai)
        char *mclk_name, *p, *s = (char *)pname;
        int ret, i = 0;
 
-       mclk = devm_kzalloc(dev, sizeof(mclk), GFP_KERNEL);
+       mclk = devm_kzalloc(dev, sizeof(*mclk), GFP_KERNEL);
        if (!mclk)
                return -ENOMEM;
 
index 66aad0d..8134c3c 100644 (file)
@@ -31,7 +31,7 @@ config SND_SUN8I_CODEC_ANALOG
 config SND_SUN50I_CODEC_ANALOG
        tristate "Allwinner sun50i Codec Analog Controls Support"
        depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
-       select SND_SUNXI_ADDA_PR_REGMAP
+       select SND_SUN8I_ADDA_PR_REGMAP
        help
          Say Y or M if you want to add support for the analog controls for
          the codec embedded in Allwinner A64 SoC.
index 522a72f..92c5de0 100644 (file)
@@ -481,7 +481,11 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
        { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch",
          "AIF1 Slot 0 Right"},
 
-       /* ADC routes */
+       /* ADC Routes */
+       { "AIF1 Slot 0 Right ADC", NULL, "ADC" },
+       { "AIF1 Slot 0 Left ADC", NULL, "ADC" },
+
+       /* ADC Mixer Routes */
        { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
          "AIF1 Slot 0 Left ADC" },
        { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
@@ -605,16 +609,10 @@ err_pm_disable:
 
 static int sun8i_codec_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-       struct sun8i_codec *scodec = snd_soc_card_get_drvdata(card);
-
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                sun8i_codec_runtime_suspend(&pdev->dev);
 
-       clk_disable_unprepare(scodec->clk_module);
-       clk_disable_unprepare(scodec->clk_bus);
-
        return 0;
 }
 
index e73c962..079063d 100644 (file)
@@ -1146,10 +1146,8 @@ static int snd_cs4231_playback_open(struct snd_pcm_substream *substream)
        runtime->hw = snd_cs4231_playback;
 
        err = snd_cs4231_open(chip, CS4231_MODE_PLAY);
-       if (err < 0) {
-               snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+       if (err < 0)
                return err;
-       }
        chip->playback_substream = substream;
        chip->p_periods_sent = 0;
        snd_pcm_set_sync(substream);
@@ -1167,10 +1165,8 @@ static int snd_cs4231_capture_open(struct snd_pcm_substream *substream)
        runtime->hw = snd_cs4231_capture;
 
        err = snd_cs4231_open(chip, CS4231_MODE_RECORD);
-       if (err < 0) {
-               snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+       if (err < 0)
                return err;
-       }
        chip->capture_substream = substream;
        chip->c_periods_sent = 0;
        snd_pcm_set_sync(substream);
index 2bfe4e8..a105947 100644 (file)
@@ -682,9 +682,12 @@ static int usb_audio_probe(struct usb_interface *intf,
 
  __error:
        if (chip) {
+               /* chip->active is inside the chip->card object,
+                * decrement before memory is possibly returned.
+                */
+               atomic_dec(&chip->active);
                if (!chip->num_interfaces)
                        snd_card_free(chip->card);
-               atomic_dec(&chip->active);
        }
        mutex_unlock(&register_mutex);
        return err;
index 849953e..37fc044 100644 (file)
@@ -3382,5 +3382,15 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
                .ifnum = QUIRK_NO_INTERFACE
        }
 },
+/* Dell WD19 Dock */
+{
+       USB_DEVICE(0x0bda, 0x402e),
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+               .vendor_name = "Dell",
+               .product_name = "WD19 Dock",
+               .profile_name = "Dell-WD15-Dock",
+               .ifnum = QUIRK_NO_INTERFACE
+       }
+},
 
 #undef USB_DEVICE_VENDOR_SPEC
index 8a945ec..6623caf 100644 (file)
@@ -1373,6 +1373,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
                        return SNDRV_PCM_FMTBIT_DSD_U32_BE;
                break;
 
+       case USB_ID(0x152a, 0x85de): /* SMSL D1 DAC */
        case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */
        case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */
        case USB_ID(0x16b0, 0x06b2): /* NuPrime DAC-10 */
index 89a048c..28c4a50 100644 (file)
 #define X86_FEATURE_LA57               (16*32+16) /* 5-level page tables */
 #define X86_FEATURE_RDPID              (16*32+22) /* RDPID instruction */
 #define X86_FEATURE_CLDEMOTE           (16*32+25) /* CLDEMOTE instruction */
+#define X86_FEATURE_MOVDIRI            (16*32+27) /* MOVDIRI instruction */
+#define X86_FEATURE_MOVDIR64B          (16*32+28) /* MOVDIR64B instruction */
 
 /* AMD-defined CPU features, CPUID level 0x80000007 (EBX), word 17 */
 #define X86_FEATURE_OVERFLOW_RECOV     (17*32+ 0) /* MCA overflow recovery support */
index edbe815..d07ccf8 100644 (file)
@@ -137,4 +137,10 @@ EXAMPLES
 
 SEE ALSO
 ========
-       **bpftool**\ (8), **bpftool-prog**\ (8), **bpftool-map**\ (8)
+       **bpf**\ (2),
+       **bpf-helpers**\ (7),
+       **bpftool**\ (8),
+       **bpftool-prog**\ (8),
+       **bpftool-map**\ (8),
+       **bpftool-net**\ (8),
+       **bpftool-perf**\ (8)
index f55a2da..64b001b 100644 (file)
@@ -42,7 +42,8 @@ MAP COMMANDS
 |              | **percpu_array** | **stack_trace** | **cgroup_array** | **lru_hash**
 |              | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | **hash_of_maps**
 |              | **devmap** | **sockmap** | **cpumap** | **xskmap** | **sockhash**
-|              | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage** }
+|              | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage**
+|              | **queue** | **stack** }
 
 DESCRIPTION
 ===========
@@ -127,6 +128,10 @@ OPTIONS
        -f, --bpffs
                  Show file names of pinned maps.
 
+       -n, --nomount
+                 Do not automatically attempt to mount any virtual file system
+                 (such as tracefs or BPF virtual file system) when necessary.
+
 EXAMPLES
 ========
 **# bpftool map show**
@@ -169,6 +174,67 @@ The following three commands are equivalent:
 | **# bpftool map pin id 10 /sys/fs/bpf/map**
 | **# bpftool map del pinned /sys/fs/bpf/map key 13 00 07 00**
 
+Note that map update can also be used in order to change the program references
+hold by a program array map. This can be used, for example, to change the
+programs used for tail-call jumps at runtime, without having to reload the
+entry-point program. Below is an example for this use case: we load a program
+defining a prog array map, and with a main function that contains a tail call
+to other programs that can be used either to "process" packets or to "debug"
+processing. Note that the prog array map MUST be pinned into the BPF virtual
+file system for the map update to work successfully, as kernel flushes prog
+array maps when they have no more references from user space (and the update
+would be lost as soon as bpftool exits).
+
+|
+| **# bpftool prog loadall tail_calls.o /sys/fs/bpf/foo type xdp**
+| **# bpftool prog --bpffs**
+
+::
+
+  545: xdp  name main_func  tag 674b4b5597193dc3  gpl
+          loaded_at 2018-12-12T15:02:58+0000  uid 0
+          xlated 240B  jited 257B  memlock 4096B  map_ids 294
+          pinned /sys/fs/bpf/foo/xdp
+  546: xdp  name bpf_func_process  tag e369a529024751fc  gpl
+          loaded_at 2018-12-12T15:02:58+0000  uid 0
+          xlated 200B  jited 164B  memlock 4096B
+          pinned /sys/fs/bpf/foo/process
+  547: xdp  name bpf_func_debug  tag 0b597868bc7f0976  gpl
+          loaded_at 2018-12-12T15:02:58+0000  uid 0
+          xlated 200B  jited 164B  memlock 4096B
+          pinned /sys/fs/bpf/foo/debug
+
+**# bpftool map**
+
+::
+
+  294: prog_array  name jmp_table  flags 0x0
+          key 4B  value 4B  max_entries 1  memlock 4096B
+          owner_prog_type xdp  owner jited
+
+|
+| **# bpftool map pin id 294 /sys/fs/bpf/bar**
+| **# bpftool map dump pinned /sys/fs/bpf/bar**
+
+::
+
+  Found 0 elements
+
+|
+| **# bpftool map update pinned /sys/fs/bpf/bar key 0 0 0 0 value pinned /sys/fs/bpf/foo/debug**
+| **# bpftool map dump pinned /sys/fs/bpf/bar**
+
+::
+
+  key: 00 00 00 00  value: 22 02 00 00
+  Found 1 element
+
 SEE ALSO
 ========
-       **bpftool**\ (8), **bpftool-prog**\ (8), **bpftool-cgroup**\ (8)
+       **bpf**\ (2),
+       **bpf-helpers**\ (7),
+       **bpftool**\ (8),
+       **bpftool-prog**\ (8),
+       **bpftool-cgroup**\ (8),
+       **bpftool-net**\ (8),
+       **bpftool-perf**\ (8)
index 408ec30..ed87c9b 100644 (file)
@@ -136,4 +136,10 @@ EXAMPLES
 
 SEE ALSO
 ========
-       **bpftool**\ (8), **bpftool-prog**\ (8), **bpftool-map**\ (8)
+       **bpf**\ (2),
+       **bpf-helpers**\ (7),
+       **bpftool**\ (8),
+       **bpftool-prog**\ (8),
+       **bpftool-map**\ (8),
+       **bpftool-cgroup**\ (8),
+       **bpftool-perf**\ (8)
index e3eb0ea..f4c5e55 100644 (file)
@@ -78,4 +78,10 @@ EXAMPLES
 
 SEE ALSO
 ========
-       **bpftool**\ (8), **bpftool-prog**\ (8), **bpftool-map**\ (8)
+       **bpf**\ (2),
+       **bpf-helpers**\ (7),
+       **bpftool**\ (8),
+       **bpftool-prog**\ (8),
+       **bpftool-map**\ (8),
+       **bpftool-cgroup**\ (8),
+       **bpftool-net**\ (8)
index ac4e904..58c8369 100644 (file)
@@ -15,18 +15,20 @@ SYNOPSIS
        *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
 
        *COMMANDS* :=
-       { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** | **help** }
+       { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load**
+       | **loadall** | **help** }
 
 MAP COMMANDS
 =============
 
 |      **bpftool** **prog { show | list }** [*PROG*]
-|      **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
-|      **bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes**}]
+|      **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual** | **linum**}]
+|      **bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes** | **linum**}]
 |      **bpftool** **prog pin** *PROG* *FILE*
-|      **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
-|       **bpftool** **prog attach** *PROG* *ATTACH_TYPE* *MAP*
-|       **bpftool** **prog detach** *PROG* *ATTACH_TYPE* *MAP*
+|      **bpftool** **prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
+|      **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
+|      **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
+|      **bpftool** **prog tracelog**
 |      **bpftool** **prog help**
 |
 |      *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
@@ -39,7 +41,9 @@ MAP COMMANDS
 |              **cgroup/bind4** | **cgroup/bind6** | **cgroup/post_bind4** | **cgroup/post_bind6** |
 |              **cgroup/connect4** | **cgroup/connect6** | **cgroup/sendmsg4** | **cgroup/sendmsg6**
 |      }
-|       *ATTACH_TYPE* := { **msg_verdict** | **skb_verdict** | **skb_parse** }
+|       *ATTACH_TYPE* := {
+|              **msg_verdict** | **skb_verdict** | **skb_parse** | **flow_dissector**
+|      }
 
 
 DESCRIPTION
@@ -52,7 +56,7 @@ DESCRIPTION
                  Output will start with program ID followed by program type and
                  zero or more named attributes (depending on kernel version).
 
-       **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** }]
+       **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** | **linum** }]
                  Dump eBPF instructions of the program from the kernel. By
                  default, eBPF will be disassembled and printed to standard
                  output in human-readable format. In this case, **opcodes**
@@ -65,13 +69,23 @@ DESCRIPTION
                  built instead, and eBPF instructions will be presented with
                  CFG in DOT format, on standard output.
 
-       **bpftool prog dump jited**  *PROG* [{ **file** *FILE* | **opcodes** }]
+                 If the prog has line_info available, the source line will
+                 be displayed by default.  If **linum** is specified,
+                 the filename, line number and line column will also be
+                 displayed on top of the source line.
+
+       **bpftool prog dump jited**  *PROG* [{ **file** *FILE* | **opcodes** | **linum** }]
                  Dump jited image (host machine code) of the program.
                  If *FILE* is specified image will be written to a file,
                  otherwise it will be disassembled and printed to stdout.
 
                  **opcodes** controls if raw opcodes will be printed.
 
+                 If the prog has line_info available, the source line will
+                 be displayed by default.  If **linum** is specified,
+                 the filename, line number and line column will also be
+                 displayed on top of the source line.
+
        **bpftool prog pin** *PROG* *FILE*
                  Pin program *PROG* as *FILE*.
 
@@ -79,8 +93,11 @@ DESCRIPTION
                  contain a dot character ('.'), which is reserved for future
                  extensions of *bpffs*.
 
-       **bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
-                 Load bpf program from binary *OBJ* and pin as *FILE*.
+       **bpftool prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*] [**pinmaps** *MAP_DIR*]
+                 Load bpf program(s) from binary *OBJ* and pin as *PATH*.
+                 **bpftool prog load** pins only the first program from the
+                 *OBJ* as *PATH*. **bpftool prog loadall** pins all programs
+                 from the *OBJ* under *PATH* directory.
                  **type** is optional, if not specified program type will be
                  inferred from section names.
                  By default bpftool will create new maps as declared in the ELF
@@ -92,18 +109,32 @@ DESCRIPTION
                  use, referring to it by **id** or through a **pinned** file.
                  If **dev** *NAME* is specified program will be loaded onto
                  given networking device (offload).
+                 Optional **pinmaps** argument can be provided to pin all
+                 maps under *MAP_DIR* directory.
 
-                 Note: *FILE* must be located in *bpffs* mount. It must not
+                 Note: *PATH* must be located in *bpffs* mount. It must not
                  contain a dot character ('.'), which is reserved for future
                  extensions of *bpffs*.
 
-        **bpftool prog attach** *PROG* *ATTACH_TYPE* *MAP*
-                  Attach bpf program *PROG* (with type specified by *ATTACH_TYPE*)
-                  to the map *MAP*.
-
-        **bpftool prog detach** *PROG* *ATTACH_TYPE* *MAP*
-                  Detach bpf program *PROG* (with type specified by *ATTACH_TYPE*)
-                  from the map *MAP*.
+       **bpftool prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
+                 Attach bpf program *PROG* (with type specified by
+                 *ATTACH_TYPE*). Most *ATTACH_TYPEs* require a *MAP*
+                 parameter, with the exception of *flow_dissector* which is
+                 attached to current networking name space.
+
+       **bpftool prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
+                 Detach bpf program *PROG* (with type specified by
+                 *ATTACH_TYPE*). Most *ATTACH_TYPEs* require a *MAP*
+                 parameter, with the exception of *flow_dissector* which is
+                 detached from the current networking name space.
+
+       **bpftool prog tracelog**
+                 Dump the trace pipe of the system to the console (stdout).
+                 Hit <Ctrl+C> to stop printing. BPF programs can write to this
+                 trace pipe at runtime with the **bpf_trace_printk()** helper.
+                 This should be used only for debugging purposes. For
+                 streaming data from BPF programs to user space, one can use
+                 perf events (see also **bpftool-map**\ (8)).
 
        **bpftool prog help**
                  Print short help message.
@@ -124,86 +155,108 @@ OPTIONS
                  Generate human-readable JSON output. Implies **-j**.
 
        -f, --bpffs
-                 Show file names of pinned programs.
+                 When showing BPF programs, show file names of pinned
+                 programs.
+
+       -m, --mapcompat
+                 Allow loading maps with unknown map definitions.
+
+       -n, --nomount
+                 Do not automatically attempt to mount any virtual file system
+                 (such as tracefs or BPF virtual file system) when necessary.
 
 EXAMPLES
 ========
 **# bpftool prog show**
+
 ::
 
-  10: xdp  name some_prog  tag 005a3d2123620c8b  gpl
-       loaded_at Sep 29/20:11  uid 0
-       xlated 528B  jited 370B  memlock 4096B  map_ids 10
+    10: xdp  name some_prog  tag 005a3d2123620c8b  gpl
+            loaded_at 2017-09-29T20:11:00+0000  uid 0
+            xlated 528B  jited 370B  memlock 4096B  map_ids 10
 
 **# bpftool --json --pretty prog show**
 
 ::
 
-    {
-        "programs": [{
-                "id": 10,
-                "type": "xdp",
-                "tag": "005a3d2123620c8b",
-                "gpl_compatible": true,
-                "loaded_at": "Sep 29/20:11",
-                "uid": 0,
-                "bytes_xlated": 528,
-                "jited": true,
-                "bytes_jited": 370,
-                "bytes_memlock": 4096,
-                "map_ids": [10
-                ]
-            }
-        ]
-    }
+    [{
+            "id": 10,
+            "type": "xdp",
+            "tag": "005a3d2123620c8b",
+            "gpl_compatible": true,
+            "loaded_at": 1506715860,
+            "uid": 0,
+            "bytes_xlated": 528,
+            "jited": true,
+            "bytes_jited": 370,
+            "bytes_memlock": 4096,
+            "map_ids": [10
+            ]
+        }
+    ]
 
 |
 | **# bpftool prog dump xlated id 10 file /tmp/t**
 | **# ls -l /tmp/t**
-|   -rw------- 1 root root 560 Jul 22 01:42 /tmp/t
 
-**# bpftool prog dum jited tag 005a3d2123620c8b**
+::
+
+    -rw------- 1 root root 560 Jul 22 01:42 /tmp/t
+
+**# bpftool prog dump jited tag 005a3d2123620c8b**
 
 ::
 
-    push   %rbp
-    mov    %rsp,%rbp
-    sub    $0x228,%rsp
-    sub    $0x28,%rbp
-    mov    %rbx,0x0(%rbp)
+    0:   push   %rbp
+    1:   mov    %rsp,%rbp
+    2:   sub    $0x228,%rsp
+    3:   sub    $0x28,%rbp
+    4:   mov    %rbx,0x0(%rbp)
 
 |
 | **# mount -t bpf none /sys/fs/bpf/**
 | **# bpftool prog pin id 10 /sys/fs/bpf/prog**
 | **# bpftool prog load ./my_prog.o /sys/fs/bpf/prog2**
 | **# ls -l /sys/fs/bpf/**
-|   -rw------- 1 root root 0 Jul 22 01:43 prog
-|   -rw------- 1 root root 0 Jul 22 01:44 prog2
 
-**# bpftool prog dum jited pinned /sys/fs/bpf/prog opcodes**
+::
+
+    -rw------- 1 root root 0 Jul 22 01:43 prog
+    -rw------- 1 root root 0 Jul 22 01:44 prog2
+
+**# bpftool prog dump jited pinned /sys/fs/bpf/prog opcodes**
 
 ::
 
-    push   %rbp
-    55
-    mov    %rsp,%rbp
-    48 89 e5
-    sub    $0x228,%rsp
-    48 81 ec 28 02 00 00
-    sub    $0x28,%rbp
-    48 83 ed 28
-    mov    %rbx,0x0(%rbp)
-    48 89 5d 00
+   0:   push   %rbp
+        55
+   1:   mov    %rsp,%rbp
+        48 89 e5
+   4:   sub    $0x228,%rsp
+        48 81 ec 28 02 00 00
+   b:   sub    $0x28,%rbp
+        48 83 ed 28
+   f:   mov    %rbx,0x0(%rbp)
+        48 89 5d 00
 
 |
 | **# bpftool prog load xdp1_kern.o /sys/fs/bpf/xdp1 type xdp map name rxcnt id 7**
 | **# bpftool prog show pinned /sys/fs/bpf/xdp1**
-|   9: xdp  name xdp_prog1  tag 539ec6ce11b52f98  gpl
-|      loaded_at 2018-06-25T16:17:31-0700  uid 0
-|      xlated 488B  jited 336B  memlock 4096B  map_ids 7
-| **# rm /sys/fs/bpf/xdp1**
-|
+
+::
+
+    9: xdp  name xdp_prog1  tag 539ec6ce11b52f98  gpl
+            loaded_at 2018-06-25T16:17:31-0700  uid 0
+            xlated 488B  jited 336B  memlock 4096B  map_ids 7
+
+**# rm /sys/fs/bpf/xdp1**
 
 SEE ALSO
 ========
-       **bpftool**\ (8), **bpftool-map**\ (8), **bpftool-cgroup**\ (8)
+       **bpf**\ (2),
+       **bpf-helpers**\ (7),
+       **bpftool**\ (8),
+       **bpftool-map**\ (8),
+       **bpftool-cgroup**\ (8),
+       **bpftool-net**\ (8),
+       **bpftool-perf**\ (8)
index 04cd4f9..e1677e8 100644 (file)
@@ -60,8 +60,17 @@ OPTIONS
        -m, --mapcompat
                  Allow loading maps with unknown map definitions.
 
+       -n, --nomount
+                 Do not automatically attempt to mount any virtual file system
+                 (such as tracefs or BPF virtual file system) when necessary.
+
 
 SEE ALSO
 ========
-       **bpftool-map**\ (8), **bpftool-prog**\ (8), **bpftool-cgroup**\ (8)
-        **bpftool-perf**\ (8), **bpftool-net**\ (8)
+       **bpf**\ (2),
+       **bpf-helpers**\ (7),
+       **bpftool-prog**\ (8),
+       **bpftool-map**\ (8),
+       **bpftool-cgroup**\ (8),
+       **bpftool-net**\ (8),
+       **bpftool-perf**\ (8)
index dac7eff..492f0f2 100644 (file)
@@ -35,8 +35,6 @@ $(LIBBPF)-clean:
 prefix ?= /usr/local
 bash_compdir ?= /usr/share/bash-completion/completions
 
-CC = gcc
-
 CFLAGS += -O2
 CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow -Wno-missing-field-initializers
 CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ \
@@ -53,7 +51,7 @@ ifneq ($(EXTRA_LDFLAGS),)
 LDFLAGS += $(EXTRA_LDFLAGS)
 endif
 
-LIBS = -lelf -lbfd -lopcodes $(LIBBPF)
+LIBS = -lelf $(LIBBPF)
 
 INSTALL ?= install
 RM ?= rm -f
@@ -90,7 +88,16 @@ include $(wildcard $(OUTPUT)*.d)
 
 all: $(OUTPUT)bpftool
 
-SRCS = $(wildcard *.c)
+BFD_SRCS = jit_disasm.c
+
+SRCS = $(filter-out $(BFD_SRCS),$(wildcard *.c))
+
+ifeq ($(feature-libbfd),1)
+CFLAGS += -DHAVE_LIBBFD_SUPPORT
+SRCS += $(BFD_SRCS)
+LIBS += -lbfd -lopcodes
+endif
+
 OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
 
 $(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c
index 3f78e64..e4e4fab 100644 (file)
@@ -1,37 +1,8 @@
 # bpftool(8) bash completion                               -*- shell-script -*-
 #
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 # Copyright (C) 2017-2018 Netronome Systems, Inc.
 #
-# This software is dual licensed under the GNU General License
-# Version 2, June 1991 as shown in the file COPYING in the top-level
-# directory of this source tree or the BSD 2-Clause License provided
-# below.  You have the option to license this software under the
-# complete terms of either license.
-#
-# The BSD 2-Clause License:
-#
-#     Redistribution and use in source and binary forms, with or
-#     without modification, are permitted provided that the following
-#     conditions are met:
-#
-#      1. Redistributions of source code must retain the above
-#         copyright notice, this list of conditions and the following
-#         disclaimer.
-#
-#      2. 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.
-#
 # Author: Quentin Monnet <quentin.monnet@netronome.com>
 
 # Takes a list of words in argument; each one of them is added to COMPREPLY if
@@ -191,7 +162,7 @@ _bpftool()
 
     # Deal with simplest keywords
     case $prev in
-        help|hex|opcodes|visual)
+        help|hex|opcodes|visual|linum)
             return 0
             ;;
         tag)
@@ -243,16 +214,20 @@ _bpftool()
     # Completion depends on object and command in use
     case $object in
         prog)
-            if [[ $command != "load" ]]; then
-                case $prev in
-                    id)
-                        _bpftool_get_prog_ids
-                        return 0
-                        ;;
-                esac
-            fi
+            # Complete id, only for subcommands that use prog (but no map) ids
+            case $command in
+                show|list|dump|pin)
+                    case $prev in
+                        id)
+                            _bpftool_get_prog_ids
+                            return 0
+                            ;;
+                    esac
+                    ;;
+            esac
 
             local PROG_TYPE='id pinned tag'
+            local MAP_TYPE='id pinned'
             case $command in
                 show|list)
                     [[ $prev != "$command" ]] && return 0
@@ -274,10 +249,10 @@ _bpftool()
                     *)
                         _bpftool_once_attr 'file'
                         if _bpftool_search_list 'xlated'; then
-                            COMPREPLY+=( $( compgen -W 'opcodes visual' -- \
+                            COMPREPLY+=( $( compgen -W 'opcodes visual linum' -- \
                                 "$cur" ) )
                         else
-                            COMPREPLY+=( $( compgen -W 'opcodes' -- \
+                            COMPREPLY+=( $( compgen -W 'opcodes linum' -- \
                                 "$cur" ) )
                         fi
                         return 0
@@ -293,23 +268,45 @@ _bpftool()
                     return 0
                     ;;
                 attach|detach)
-                    if [[ ${#words[@]} == 7 ]]; then
-                        COMPREPLY=( $( compgen -W "id pinned" -- "$cur" ) )
-                        return 0
-                    fi
-
-                    if [[ ${#words[@]} == 6 ]]; then
-                        COMPREPLY=( $( compgen -W "msg_verdict skb_verdict skb_parse" -- "$cur" ) )
-                        return 0
-                    fi
-
-                    if [[ $prev == "$command" ]]; then
-                        COMPREPLY=( $( compgen -W "id pinned" -- "$cur" ) )
-                        return 0
-                    fi
-                    return 0
+                    case $cword in
+                        3)
+                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
+                            return 0
+                            ;;
+                        4)
+                            case $prev in
+                                id)
+                                    _bpftool_get_prog_ids
+                                    ;;
+                                pinned)
+                                    _filedir
+                                    ;;
+                            esac
+                            return 0
+                            ;;
+                        5)
+                            COMPREPLY=( $( compgen -W 'msg_verdict skb_verdict \
+                                skb_parse flow_dissector' -- "$cur" ) )
+                            return 0
+                            ;;
+                        6)
+                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
+                            return 0
+                            ;;
+                        7)
+                            case $prev in
+                                id)
+                                    _bpftool_get_map_ids
+                                    ;;
+                                pinned)
+                                    _filedir
+                                    ;;
+                            esac
+                            return 0
+                            ;;
+                    esac
                     ;;
-                load)
+                load|loadall)
                     local obj
 
                     if [[ ${#words[@]} -lt 6 ]]; then
@@ -338,7 +335,16 @@ _bpftool()
 
                     case $prev in
                         type)
-                            COMPREPLY=( $( compgen -W "socket kprobe kretprobe classifier action tracepoint raw_tracepoint xdp perf_event cgroup/skb cgroup/sock cgroup/dev lwt_in lwt_out lwt_xmit lwt_seg6local sockops sk_skb sk_msg lirc_mode2 cgroup/bind4 cgroup/bind6 cgroup/connect4 cgroup/connect6 cgroup/sendmsg4 cgroup/sendmsg6 cgroup/post_bind4 cgroup/post_bind6" -- \
+                            COMPREPLY=( $( compgen -W "socket kprobe \
+                                kretprobe classifier flow_dissector \
+                                action tracepoint raw_tracepoint \
+                                xdp perf_event cgroup/skb cgroup/sock \
+                                cgroup/dev lwt_in lwt_out lwt_xmit \
+                                lwt_seg6local sockops sk_skb sk_msg \
+                                lirc_mode2 cgroup/bind4 cgroup/bind6 \
+                                cgroup/connect4 cgroup/connect6 \
+                                cgroup/sendmsg4 cgroup/sendmsg6 \
+                                cgroup/post_bind4 cgroup/post_bind6" -- \
                                                    "$cur" ) )
                             return 0
                             ;;
@@ -346,7 +352,7 @@ _bpftool()
                             _bpftool_get_map_ids
                             return 0
                             ;;
-                        pinned)
+                        pinned|pinmaps)
                             _filedir
                             return 0
                             ;;
@@ -358,14 +364,18 @@ _bpftool()
                             COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
                             _bpftool_once_attr 'type'
                             _bpftool_once_attr 'dev'
+                            _bpftool_once_attr 'pinmaps'
                             return 0
                             ;;
                     esac
                     ;;
+                tracelog)
+                    return 0
+                    ;;
                 *)
                     [[ $prev == $object ]] && \
                         COMPREPLY=( $( compgen -W 'dump help pin attach detach load \
-                            show list' -- "$cur" ) )
+                            show list tracelog' -- "$cur" ) )
                     ;;
             esac
             ;;
@@ -400,7 +410,7 @@ _bpftool()
                                 lru_percpu_hash lpm_trie array_of_maps \
                                 hash_of_maps devmap sockmap cpumap xskmap \
                                 sockhash cgroup_storage reuseport_sockarray \
-                                percpu_cgroup_storage' -- \
+                                percpu_cgroup_storage queue stack' -- \
                                                    "$cur" ) )
                             return 0
                             ;;
index 55bc512..3f0629e 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 /* Copyright (c) 2018 Facebook */
 
 #include <ctype.h>
@@ -32,7 +32,7 @@ static void btf_dumper_ptr(const void *data, json_writer_t *jw,
 }
 
 static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id,
-                              const void *data)
+                              __u8 bit_offset, const void *data)
 {
        int actual_type_id;
 
@@ -40,7 +40,7 @@ static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id,
        if (actual_type_id < 0)
                return actual_type_id;
 
-       return btf_dumper_do_type(d, actual_type_id, 0, data);
+       return btf_dumper_do_type(d, actual_type_id, bit_offset, data);
 }
 
 static void btf_dumper_enum(const void *data, json_writer_t *jw)
@@ -73,20 +73,17 @@ static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id,
        return ret;
 }
 
-static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
+static void btf_dumper_bitfield(__u32 nr_bits, __u8 bit_offset,
                                const void *data, json_writer_t *jw,
                                bool is_plain_text)
 {
        int left_shift_bits, right_shift_bits;
-       int nr_bits = BTF_INT_BITS(int_type);
-       int total_bits_offset;
        int bytes_to_copy;
        int bits_to_copy;
        __u64 print_num;
 
-       total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
-       data += BITS_ROUNDDOWN_BYTES(total_bits_offset);
-       bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset);
+       data += BITS_ROUNDDOWN_BYTES(bit_offset);
+       bit_offset = BITS_PER_BYTE_MASKED(bit_offset);
        bits_to_copy = bit_offset + nr_bits;
        bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy);
 
@@ -109,6 +106,22 @@ static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
                jsonw_printf(jw, "%llu", print_num);
 }
 
+
+static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
+                               const void *data, json_writer_t *jw,
+                               bool is_plain_text)
+{
+       int nr_bits = BTF_INT_BITS(int_type);
+       int total_bits_offset;
+
+       /* bits_offset is at most 7.
+        * BTF_INT_OFFSET() cannot exceed 64 bits.
+        */
+       total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
+       btf_dumper_bitfield(nr_bits, total_bits_offset, data, jw,
+                           is_plain_text);
+}
+
 static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
                          const void *data, json_writer_t *jw,
                          bool is_plain_text)
@@ -180,6 +193,7 @@ static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
        const struct btf_type *t;
        struct btf_member *m;
        const void *data_off;
+       int kind_flag;
        int ret = 0;
        int i, vlen;
 
@@ -187,18 +201,32 @@ static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
        if (!t)
                return -EINVAL;
 
+       kind_flag = BTF_INFO_KFLAG(t->info);
        vlen = BTF_INFO_VLEN(t->info);
        jsonw_start_object(d->jw);
        m = (struct btf_member *)(t + 1);
 
        for (i = 0; i < vlen; i++) {
-               data_off = data + BITS_ROUNDDOWN_BYTES(m[i].offset);
+               __u32 bit_offset = m[i].offset;
+               __u32 bitfield_size = 0;
+
+               if (kind_flag) {
+                       bitfield_size = BTF_MEMBER_BITFIELD_SIZE(bit_offset);
+                       bit_offset = BTF_MEMBER_BIT_OFFSET(bit_offset);
+               }
+
                jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off));
-               ret = btf_dumper_do_type(d, m[i].type,
-                                        BITS_PER_BYTE_MASKED(m[i].offset),
-                                        data_off);
-               if (ret)
-                       break;
+               if (bitfield_size) {
+                       btf_dumper_bitfield(bitfield_size, bit_offset,
+                                           data, d->jw, d->is_plain_text);
+               } else {
+                       data_off = data + BITS_ROUNDDOWN_BYTES(bit_offset);
+                       ret = btf_dumper_do_type(d, m[i].type,
+                                                BITS_PER_BYTE_MASKED(bit_offset),
+                                                data_off);
+                       if (ret)
+                               break;
+               }
        }
 
        jsonw_end_object(d->jw);
@@ -237,7 +265,7 @@ static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
        case BTF_KIND_VOLATILE:
        case BTF_KIND_CONST:
        case BTF_KIND_RESTRICT:
-               return btf_dumper_modifier(d, type_id, data);
+               return btf_dumper_modifier(d, type_id, bit_offset, data);
        default:
                jsonw_printf(d->jw, "(unsupported-kind");
                return -EINVAL;
@@ -249,3 +277,206 @@ int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
 {
        return btf_dumper_do_type(d, type_id, 0, data);
 }
+
+#define BTF_PRINT_ARG(...)                                             \
+       do {                                                            \
+               pos += snprintf(func_sig + pos, size - pos,             \
+                               __VA_ARGS__);                           \
+               if (pos >= size)                                        \
+                       return -1;                                      \
+       } while (0)
+#define BTF_PRINT_TYPE(type)                                   \
+       do {                                                            \
+               pos = __btf_dumper_type_only(btf, type, func_sig,       \
+                                            pos, size);                \
+               if (pos == -1)                                          \
+                       return -1;                                      \
+       } while (0)
+
+static int btf_dump_func(const struct btf *btf, char *func_sig,
+                        const struct btf_type *func_proto,
+                        const struct btf_type *func, int pos, int size);
+
+static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
+                                 char *func_sig, int pos, int size)
+{
+       const struct btf_type *proto_type;
+       const struct btf_array *array;
+       const struct btf_type *t;
+
+       if (!type_id) {
+               BTF_PRINT_ARG("void ");
+               return pos;
+       }
+
+       t = btf__type_by_id(btf, type_id);
+
+       switch (BTF_INFO_KIND(t->info)) {
+       case BTF_KIND_INT:
+       case BTF_KIND_TYPEDEF:
+               BTF_PRINT_ARG("%s ", btf__name_by_offset(btf, t->name_off));
+               break;
+       case BTF_KIND_STRUCT:
+               BTF_PRINT_ARG("struct %s ",
+                             btf__name_by_offset(btf, t->name_off));
+               break;
+       case BTF_KIND_UNION:
+               BTF_PRINT_ARG("union %s ",
+                             btf__name_by_offset(btf, t->name_off));
+               break;
+       case BTF_KIND_ENUM:
+               BTF_PRINT_ARG("enum %s ",
+                             btf__name_by_offset(btf, t->name_off));
+               break;
+       case BTF_KIND_ARRAY:
+               array = (struct btf_array *)(t + 1);
+               BTF_PRINT_TYPE(array->type);
+               BTF_PRINT_ARG("[%d]", array->nelems);
+               break;
+       case BTF_KIND_PTR:
+               BTF_PRINT_TYPE(t->type);
+               BTF_PRINT_ARG("* ");
+               break;
+       case BTF_KIND_FWD:
+               BTF_PRINT_ARG("%s %s ",
+                             BTF_INFO_KFLAG(t->info) ? "union" : "struct",
+                             btf__name_by_offset(btf, t->name_off));
+               break;
+       case BTF_KIND_VOLATILE:
+               BTF_PRINT_ARG("volatile ");
+               BTF_PRINT_TYPE(t->type);
+               break;
+       case BTF_KIND_CONST:
+               BTF_PRINT_ARG("const ");
+               BTF_PRINT_TYPE(t->type);
+               break;
+       case BTF_KIND_RESTRICT:
+               BTF_PRINT_ARG("restrict ");
+               BTF_PRINT_TYPE(t->type);
+               break;
+       case BTF_KIND_FUNC_PROTO:
+               pos = btf_dump_func(btf, func_sig, t, NULL, pos, size);
+               if (pos == -1)
+                       return -1;
+               break;
+       case BTF_KIND_FUNC:
+               proto_type = btf__type_by_id(btf, t->type);
+               pos = btf_dump_func(btf, func_sig, proto_type, t, pos, size);
+               if (pos == -1)
+                       return -1;
+               break;
+       case BTF_KIND_UNKN:
+       default:
+               return -1;
+       }
+
+       return pos;
+}
+
+static int btf_dump_func(const struct btf *btf, char *func_sig,
+                        const struct btf_type *func_proto,
+                        const struct btf_type *func, int pos, int size)
+{
+       int i, vlen;
+
+       BTF_PRINT_TYPE(func_proto->type);
+       if (func)
+               BTF_PRINT_ARG("%s(", btf__name_by_offset(btf, func->name_off));
+       else
+               BTF_PRINT_ARG("(");
+       vlen = BTF_INFO_VLEN(func_proto->info);
+       for (i = 0; i < vlen; i++) {
+               struct btf_param *arg = &((struct btf_param *)(func_proto + 1))[i];
+
+               if (i)
+                       BTF_PRINT_ARG(", ");
+               if (arg->type) {
+                       BTF_PRINT_TYPE(arg->type);
+                       BTF_PRINT_ARG("%s",
+                                     btf__name_by_offset(btf, arg->name_off));
+               } else {
+                       BTF_PRINT_ARG("...");
+               }
+       }
+       BTF_PRINT_ARG(")");
+
+       return pos;
+}
+
+void btf_dumper_type_only(const struct btf *btf, __u32 type_id, char *func_sig,
+                         int size)
+{
+       int err;
+
+       func_sig[0] = '\0';
+       if (!btf)
+               return;
+
+       err = __btf_dumper_type_only(btf, type_id, func_sig, 0, size);
+       if (err < 0)
+               func_sig[0] = '\0';
+}
+
+static const char *ltrim(const char *s)
+{
+       while (isspace(*s))
+               s++;
+
+       return s;
+}
+
+void btf_dump_linfo_plain(const struct btf *btf,
+                         const struct bpf_line_info *linfo,
+                         const char *prefix, bool linum)
+{
+       const char *line = btf__name_by_offset(btf, linfo->line_off);
+
+       if (!line)
+               return;
+       line = ltrim(line);
+
+       if (!prefix)
+               prefix = "";
+
+       if (linum) {
+               const char *file = btf__name_by_offset(btf, linfo->file_name_off);
+
+               /* More forgiving on file because linum option is
+                * expected to provide more info than the already
+                * available src line.
+                */
+               if (!file)
+                       file = "";
+
+               printf("%s%s [file:%s line_num:%u line_col:%u]\n",
+                      prefix, line, file,
+                      BPF_LINE_INFO_LINE_NUM(linfo->line_col),
+                      BPF_LINE_INFO_LINE_COL(linfo->line_col));
+       } else {
+               printf("%s%s\n", prefix, line);
+       }
+}
+
+void btf_dump_linfo_json(const struct btf *btf,
+                        const struct bpf_line_info *linfo, bool linum)
+{
+       const char *line = btf__name_by_offset(btf, linfo->line_off);
+
+       if (line)
+               jsonw_string_field(json_wtr, "src", ltrim(line));
+
+       if (linum) {
+               const char *file = btf__name_by_offset(btf, linfo->file_name_off);
+
+               if (file)
+                       jsonw_string_field(json_wtr, "file", file);
+
+               if (BPF_LINE_INFO_LINE_NUM(linfo->line_col))
+                       jsonw_int_field(json_wtr, "line_num",
+                                       BPF_LINE_INFO_LINE_NUM(linfo->line_col));
+
+               if (BPF_LINE_INFO_LINE_COL(linfo->line_col))
+                       jsonw_int_field(json_wtr, "line_col",
+                                       BPF_LINE_INFO_LINE_COL(linfo->line_col));
+       }
+}
index f30b3a4..31f0db4 100644 (file)
@@ -1,39 +1,5 @@
 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-/*
- * Copyright (C) 2018 Netronome Systems, Inc.
- *
- * This software is dual licensed under the GNU General License Version 2,
- * June 1991 as shown in the file COPYING in the top-level directory of this
- * source tree or the BSD 2-Clause License provided below.  You have the
- * option to license this software under the complete terms of either license.
- *
- * The BSD 2-Clause License:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *
- *      2. 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.
- *
- * 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 HOLDER 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.
- */
+/* Copyright (C) 2018 Netronome Systems, Inc. */
 
 #include <linux/list.h>
 #include <stdlib.h>
index 2cc9bd9..e144257 100644 (file)
@@ -1,39 +1,5 @@
-// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-/*
- * Copyright (C) 2018 Netronome Systems, Inc.
- *
- * This software is dual licensed under the GNU General License Version 2,
- * June 1991 as shown in the file COPYING in the top-level directory of this
- * source tree or the BSD 2-Clause License provided below.  You have the
- * option to license this software under the complete terms of either license.
- *
- * The BSD 2-Clause License:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *
- *      2. 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.
- *
- * 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 HOLDER 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.
- */
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/* Copyright (C) 2018 Netronome Systems, Inc. */
 
 #ifndef __BPF_TOOL_CFG_H
 #define __BPF_TOOL_CFG_H
index ee7a976..4b5c8da 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 // Copyright (C) 2017 Facebook
 // Author: Roman Gushchin <guro@fb.com>
 
index 25af853..8974834 100644 (file)
@@ -1,35 +1,5 @@
-/*
- * Copyright (C) 2017-2018 Netronome Systems, Inc.
- *
- * This software is dual licensed under the GNU General License Version 2,
- * June 1991 as shown in the file COPYING in the top-level directory of this
- * source tree or the BSD 2-Clause License provided below.  You have the
- * option to license this software under the complete terms of either license.
- *
- * The BSD 2-Clause License:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *
- *      2. 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.
- */
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
 
 #include <ctype.h>
 #include <errno.h>
@@ -46,8 +16,8 @@
 #include <linux/magic.h>
 #include <net/if.h>
 #include <sys/mount.h>
+#include <sys/resource.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <sys/vfs.h>
 
 #include <bpf.h>
@@ -58,7 +28,7 @@
 #define BPF_FS_MAGIC           0xcafe4a11
 #endif
 
-void p_err(const char *fmt, ...)
+void __printf(1, 2) p_err(const char *fmt, ...)
 {
        va_list ap;
 
@@ -76,7 +46,7 @@ void p_err(const char *fmt, ...)
        va_end(ap);
 }
 
-void p_info(const char *fmt, ...)
+void __printf(1, 2) p_info(const char *fmt, ...)
 {
        va_list ap;
 
@@ -99,7 +69,15 @@ static bool is_bpffs(char *path)
        return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
 }
 
-static int mnt_bpffs(const char *target, char *buff, size_t bufflen)
+void set_max_rlimit(void)
+{
+       struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
+
+       setrlimit(RLIMIT_MEMLOCK, &rinf);
+}
+
+static int
+mnt_fs(const char *target, const char *type, char *buff, size_t bufflen)
 {
        bool bind_done = false;
 
@@ -121,25 +99,40 @@ static int mnt_bpffs(const char *target, char *buff, size_t bufflen)
                bind_done = true;
        }
 
-       if (mount("bpf", target, "bpf", 0, "mode=0700")) {
-               snprintf(buff, bufflen, "mount -t bpf bpf %s failed: %s",
-                        target, strerror(errno));
+       if (mount(type, target, type, 0, "mode=0700")) {
+               snprintf(buff, bufflen, "mount -t %s %s %s failed: %s",
+                        type, type, target, strerror(errno));
                return -1;
        }
 
        return 0;
 }
 
-int open_obj_pinned(char *path)
+int mount_tracefs(const char *target)
+{
+       char err_str[ERR_MAX_LEN];
+       int err;
+
+       err = mnt_fs(target, "tracefs", err_str, ERR_MAX_LEN);
+       if (err) {
+               err_str[ERR_MAX_LEN - 1] = '\0';
+               p_err("can't mount tracefs: %s", err_str);
+       }
+
+       return err;
+}
+
+int open_obj_pinned(char *path, bool quiet)
 {
        int fd;
 
        fd = bpf_obj_get(path);
        if (fd < 0) {
-               p_err("bpf obj get (%s): %s", path,
-                     errno == EACCES && !is_bpffs(dirname(path)) ?
-                   "directory not in bpf file system (bpffs)" :
-                   strerror(errno));
+               if (!quiet)
+                       p_err("bpf obj get (%s): %s", path,
+                             errno == EACCES && !is_bpffs(dirname(path)) ?
+                           "directory not in bpf file system (bpffs)" :
+                           strerror(errno));
                return -1;
        }
 
@@ -151,7 +144,7 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
        enum bpf_obj_type type;
        int fd;
 
-       fd = open_obj_pinned(path);
+       fd = open_obj_pinned(path, false);
        if (fd < 0)
                return -1;
 
@@ -169,34 +162,29 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
        return fd;
 }
 
-int do_pin_fd(int fd, const char *name)
+int mount_bpffs_for_pin(const char *name)
 {
        char err_str[ERR_MAX_LEN];
        char *file;
        char *dir;
        int err = 0;
 
-       err = bpf_obj_pin(fd, name);
-       if (!err)
-               goto out;
-
        file = malloc(strlen(name) + 1);
        strcpy(file, name);
        dir = dirname(file);
 
-       if (errno != EPERM || is_bpffs(dir)) {
-               p_err("can't pin the object (%s): %s", name, strerror(errno));
+       if (is_bpffs(dir))
+               /* nothing to do if already mounted */
+               goto out_free;
+
+       if (block_mount) {
+               p_err("no BPF file system found, not mounting it due to --nomount option");
+               err = -1;
                goto out_free;
        }
 
-       /* Attempt to mount bpffs, then retry pinning. */
-       err = mnt_bpffs(dir, err_str, ERR_MAX_LEN);
-       if (!err) {
-               err = bpf_obj_pin(fd, name);
-               if (err)
-                       p_err("can't pin the object (%s): %s", name,
-                             strerror(errno));
-       } else {
+       err = mnt_fs(dir, "bpf", err_str, ERR_MAX_LEN);
+       if (err) {
                err_str[ERR_MAX_LEN - 1] = '\0';
                p_err("can't mount BPF file system to pin the object (%s): %s",
                      name, err_str);
@@ -204,10 +192,20 @@ int do_pin_fd(int fd, const char *name)
 
 out_free:
        free(file);
-out:
        return err;
 }
 
+int do_pin_fd(int fd, const char *name)
+{
+       int err;
+
+       err = mount_bpffs_for_pin(name);
+       if (err)
+               return err;
+
+       return bpf_obj_pin(fd, name);
+}
+
 int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
 {
        unsigned int id;
@@ -268,7 +266,7 @@ int get_fd_type(int fd)
        char buf[512];
        ssize_t n;
 
-       snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd);
+       snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
 
        n = readlink(path, buf, sizeof(buf));
        if (n < 0) {
@@ -296,7 +294,7 @@ char *get_fdinfo(int fd, const char *key)
        ssize_t n;
        FILE *fdi;
 
-       snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd);
+       snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
 
        fdi = fopen(path, "r");
        if (!fdi) {
@@ -304,7 +302,7 @@ char *get_fdinfo(int fd, const char *key)
                return NULL;
        }
 
-       while ((n = getline(&line, &line_n, fdi))) {
+       while ((n = getline(&line, &line_n, fdi)) > 0) {
                char *value;
                int len;
 
@@ -384,7 +382,7 @@ int build_pinned_obj_table(struct pinned_obj_table *tab,
                while ((ftse = fts_read(fts))) {
                        if (!(ftse->fts_info & FTS_F))
                                continue;
-                       fd = open_obj_pinned(ftse->fts_path);
+                       fd = open_obj_pinned(ftse->fts_path, true);
                        if (fd < 0)
                                continue;
 
@@ -597,7 +595,7 @@ void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
        if (!ifindex)
                return;
 
-       printf(" dev ");
+       printf("  offloaded_to ");
        if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
                printf("%s", name);
        else
index c75ffd9..3ef3093 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 /*
  * Based on:
  *
 #include <string.h>
 #include <bfd.h>
 #include <dis-asm.h>
-#include <sys/types.h>
 #include <sys/stat.h>
 #include <limits.h>
+#include <libbpf.h>
 
 #include "json_writer.h"
 #include "main.h"
 
 static void get_exec_path(char *tpath, size_t size)
 {
+       const char *path = "/proc/self/exe";
        ssize_t len;
-       char *path;
-
-       snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
-       tpath[size - 1] = 0;
-
-       path = strdup(tpath);
-       assert(path);
 
        len = readlink(path, tpath, size - 1);
        assert(len > 0);
        tpath[len] = 0;
-
-       free(path);
 }
 
 static int oper_count;
@@ -77,10 +70,16 @@ static int fprintf_json(void *out, const char *fmt, ...)
 }
 
 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
-                      const char *arch, const char *disassembler_options)
+                      const char *arch, const char *disassembler_options,
+                      const struct btf *btf,
+                      const struct bpf_prog_linfo *prog_linfo,
+                      __u64 func_ksym, unsigned int func_idx,
+                      bool linum)
 {
+       const struct bpf_line_info *linfo = NULL;
        disassembler_ftype disassemble;
        struct disassemble_info info;
+       unsigned int nr_skip = 0;
        int count, i, pc = 0;
        char tpath[PATH_MAX];
        bfd *bfdf;
@@ -109,7 +108,7 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
                if (inf) {
                        bfdf->arch_info = inf;
                } else {
-                       p_err("No libfd support for %s", arch);
+                       p_err("No libbfd support for %s", arch);
                        return;
                }
        }
@@ -136,12 +135,26 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
        if (json_output)
                jsonw_start_array(json_wtr);
        do {
+               if (prog_linfo) {
+                       linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
+                                                               func_ksym + pc,
+                                                               func_idx,
+                                                               nr_skip);
+                       if (linfo)
+                               nr_skip++;
+               }
+
                if (json_output) {
                        jsonw_start_object(json_wtr);
                        oper_count = 0;
+                       if (linfo)
+                               btf_dump_linfo_json(btf, linfo, linum);
                        jsonw_name(json_wtr, "pc");
                        jsonw_printf(json_wtr, "\"0x%x\"", pc);
                } else {
+                       if (linfo)
+                               btf_dump_linfo_plain(btf, linfo, "; ",
+                                                    linum);
                        printf("%4x:\t", pc);
                }
 
@@ -183,3 +196,9 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
 
        bfd_close(bfdf);
 }
+
+int disasm_init(void)
+{
+       bfd_init();
+       return 0;
+}
index c6eef76..bff7ee0 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 /*
  * Simple streaming JSON writer
  *
@@ -19,6 +20,7 @@
 #include <malloc.h>
 #include <inttypes.h>
 #include <stdint.h>
+#include <linux/compiler.h>
 
 #include "json_writer.h"
 
@@ -156,7 +158,8 @@ void jsonw_name(json_writer_t *self, const char *name)
                putc(' ', self->out);
 }
 
-void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
+void __printf(2, 0)
+jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
 {
        jsonw_eor(self);
        putc('"', self->out);
@@ -164,7 +167,7 @@ void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
        putc('"', self->out);
 }
 
-void jsonw_printf(json_writer_t *self, const char *fmt, ...)
+void __printf(2, 3) jsonw_printf(json_writer_t *self, const char *fmt, ...)
 {
        va_list ap;
 
index 0fa2fb1..c1ab51a 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
 /*
  * Simple streaming JSON writer
  *
index 75a3296..f44a1c2 100644 (file)
@@ -1,37 +1,6 @@
-/*
- * Copyright (C) 2017-2018 Netronome Systems, Inc.
- *
- * This software is dual licensed under the GNU General License Version 2,
- * June 1991 as shown in the file COPYING in the top-level directory of this
- * source tree or the BSD 2-Clause License provided below.  You have the
- * option to license this software under the complete terms of either license.
- *
- * The BSD 2-Clause License:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *
- *      2. 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 <bfd.h>
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
+
 #include <ctype.h>
 #include <errno.h>
 #include <getopt.h>
@@ -55,6 +24,7 @@ json_writer_t *json_wtr;
 bool pretty_output;
 bool json_output;
 bool show_pinned;
+bool block_mount;
 int bpf_flags;
 struct pinned_obj_table prog_table;
 struct pinned_obj_table map_table;
@@ -344,6 +314,7 @@ int main(int argc, char **argv)
                { "version",    no_argument,    NULL,   'V' },
                { "bpffs",      no_argument,    NULL,   'f' },
                { "mapcompat",  no_argument,    NULL,   'm' },
+               { "nomount",    no_argument,    NULL,   'n' },
                { 0 }
        };
        int opt, ret;
@@ -352,13 +323,14 @@ int main(int argc, char **argv)
        pretty_output = false;
        json_output = false;
        show_pinned = false;
+       block_mount = false;
        bin_name = argv[0];
 
        hash_init(prog_table.table);
        hash_init(map_table.table);
 
        opterr = 0;
-       while ((opt = getopt_long(argc, argv, "Vhpjfm",
+       while ((opt = getopt_long(argc, argv, "Vhpjfmn",
                                  options, NULL)) >= 0) {
                switch (opt) {
                case 'V':
@@ -385,6 +357,9 @@ int main(int argc, char **argv)
                case 'm':
                        bpf_flags = MAPS_RELAX_COMPAT;
                        break;
+               case 'n':
+                       block_mount = true;
+                       break;
                default:
                        p_err("unrecognized option '%s'", argv[optind - 1]);
                        if (json_output)
@@ -399,8 +374,6 @@ int main(int argc, char **argv)
        if (argc < 0)
                usage();
 
-       bfd_init();
-
        ret = cmd_select(cmds, argc, argv, do_help);
 
        if (json_output)
index 28322ac..052c91d 100644 (file)
@@ -1,35 +1,5 @@
-/*
- * Copyright (C) 2017-2018 Netronome Systems, Inc.
- *
- * This software is dual licensed under the GNU General License Version 2,
- * June 1991 as shown in the file COPYING in the top-level directory of this
- * source tree or the BSD 2-Clause License provided below.  You have the
- * option to license this software under the complete terms of either license.
- *
- * The BSD 2-Clause License:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *
- *      2. 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.
- */
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
 
 #ifndef __BPF_TOOL_H
 #define __BPF_TOOL_H
 #define HELP_SPEC_PROGRAM                                              \
        "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }"
 #define HELP_SPEC_OPTIONS                                              \
-       "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} | {-m|--mapcompat}"
+       "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} |\n"   \
+       "\t            {-m|--mapcompat} | {-n|--nomount} }"
 #define HELP_SPEC_MAP                                                  \
        "MAP := { id MAP_ID | pinned FILE }"
 
+static const char * const prog_type_name[] = {
+       [BPF_PROG_TYPE_UNSPEC]                  = "unspec",
+       [BPF_PROG_TYPE_SOCKET_FILTER]           = "socket_filter",
+       [BPF_PROG_TYPE_KPROBE]                  = "kprobe",
+       [BPF_PROG_TYPE_SCHED_CLS]               = "sched_cls",
+       [BPF_PROG_TYPE_SCHED_ACT]               = "sched_act",
+       [BPF_PROG_TYPE_TRACEPOINT]              = "tracepoint",
+       [BPF_PROG_TYPE_XDP]                     = "xdp",
+       [BPF_PROG_TYPE_PERF_EVENT]              = "perf_event",
+       [BPF_PROG_TYPE_CGROUP_SKB]              = "cgroup_skb",
+       [BPF_PROG_TYPE_CGROUP_SOCK]             = "cgroup_sock",
+       [BPF_PROG_TYPE_LWT_IN]                  = "lwt_in",
+       [BPF_PROG_TYPE_LWT_OUT]                 = "lwt_out",
+       [BPF_PROG_TYPE_LWT_XMIT]                = "lwt_xmit",
+       [BPF_PROG_TYPE_SOCK_OPS]                = "sock_ops",
+       [BPF_PROG_TYPE_SK_SKB]                  = "sk_skb",
+       [BPF_PROG_TYPE_CGROUP_DEVICE]           = "cgroup_device",
+       [BPF_PROG_TYPE_SK_MSG]                  = "sk_msg",
+       [BPF_PROG_TYPE_RAW_TRACEPOINT]          = "raw_tracepoint",
+       [BPF_PROG_TYPE_CGROUP_SOCK_ADDR]        = "cgroup_sock_addr",
+       [BPF_PROG_TYPE_LWT_SEG6LOCAL]           = "lwt_seg6local",
+       [BPF_PROG_TYPE_LIRC_MODE2]              = "lirc_mode2",
+       [BPF_PROG_TYPE_SK_REUSEPORT]            = "sk_reuseport",
+       [BPF_PROG_TYPE_FLOW_DISSECTOR]          = "flow_dissector",
+};
+
 enum bpf_obj_type {
        BPF_OBJ_UNKNOWN,
        BPF_OBJ_PROG,
@@ -89,6 +86,7 @@ extern const char *bin_name;
 extern json_writer_t *json_wtr;
 extern bool json_output;
 extern bool show_pinned;
+extern bool block_mount;
 extern int bpf_flags;
 extern struct pinned_obj_table prog_table;
 extern struct pinned_obj_table map_table;
@@ -100,6 +98,10 @@ bool is_prefix(const char *pfx, const char *str);
 void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
 void usage(void) __noreturn;
 
+void set_max_rlimit(void);
+
+int mount_tracefs(const char *target);
+
 struct pinned_obj_table {
        DECLARE_HASHTABLE(table, 16);
 };
@@ -110,6 +112,9 @@ struct pinned_obj {
        struct hlist_node hash;
 };
 
+struct btf;
+struct bpf_line_info;
+
 int build_pinned_obj_table(struct pinned_obj_table *table,
                           enum bpf_obj_type type);
 void delete_pinned_obj_table(struct pinned_obj_table *tab);
@@ -127,8 +132,9 @@ int cmd_select(const struct cmd *cmds, int argc, char **argv,
 int get_fd_type(int fd);
 const char *get_fd_type_name(enum bpf_obj_type type);
 char *get_fdinfo(int fd, const char *key);
-int open_obj_pinned(char *path);
+int open_obj_pinned(char *path, bool quiet);
 int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type);
+int mount_bpffs_for_pin(const char *name);
 int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32));
 int do_pin_fd(int fd, const char *name);
 
@@ -138,14 +144,38 @@ int do_event_pipe(int argc, char **argv);
 int do_cgroup(int argc, char **arg);
 int do_perf(int argc, char **arg);
 int do_net(int argc, char **arg);
+int do_tracelog(int argc, char **arg);
 
 int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what);
 int prog_parse_fd(int *argc, char ***argv);
 int map_parse_fd(int *argc, char ***argv);
 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);
 
+struct bpf_prog_linfo;
+#ifdef HAVE_LIBBFD_SUPPORT
+void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
+                      const char *arch, const char *disassembler_options,
+                      const struct btf *btf,
+                      const struct bpf_prog_linfo *prog_linfo,
+                      __u64 func_ksym, unsigned int func_idx,
+                      bool linum);
+int disasm_init(void);
+#else
+static inline
 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
-                      const char *arch, const char *disassembler_options);
+                      const char *arch, const char *disassembler_options,
+                      const struct btf *btf,
+                      const struct bpf_prog_linfo *prog_linfo,
+                      __u64 func_ksym, unsigned int func_idx,
+                      bool linum)
+{
+}
+static inline int disasm_init(void)
+{
+       p_err("No libbfd support");
+       return -1;
+}
+#endif
 void print_data_json(uint8_t *data, size_t len);
 void print_hex_data_json(uint8_t *data, size_t len);
 
@@ -170,6 +200,14 @@ struct btf_dumper {
  */
 int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
                    const void *data);
+void btf_dumper_type_only(const struct btf *btf, __u32 func_type_id,
+                         char *func_only, int size);
+
+void btf_dump_linfo_plain(const struct btf *btf,
+                         const struct bpf_line_info *linfo,
+                         const char *prefix, bool linum);
+void btf_dump_linfo_json(const struct btf *btf,
+                        const struct bpf_line_info *linfo, bool linum);
 
 struct nlattr;
 struct ifinfomsg;
index 7bf38f0..2037e3d 100644 (file)
@@ -1,35 +1,5 @@
-/*
- * Copyright (C) 2017-2018 Netronome Systems, Inc.
- *
- * This software is dual licensed under the GNU General License Version 2,
- * June 1991 as shown in the file COPYING in the top-level directory of this
- * source tree or the BSD 2-Clause License provided below.  You have the
- * option to license this software under the complete terms of either license.
- *
- * The BSD 2-Clause License:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *
- *      2. 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.
- */
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
 
 #include <assert.h>
 #include <errno.h>
 #include "main.h"
 
 static const char * const map_type_name[] = {
-       [BPF_MAP_TYPE_UNSPEC]           = "unspec",
-       [BPF_MAP_TYPE_HASH]             = "hash",
-       [BPF_MAP_TYPE_ARRAY]            = "array",
-       [BPF_MAP_TYPE_PROG_ARRAY]       = "prog_array",
-       [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf_event_array",
-       [BPF_MAP_TYPE_PERCPU_HASH]      = "percpu_hash",
-       [BPF_MAP_TYPE_PERCPU_ARRAY]     = "percpu_array",
-       [BPF_MAP_TYPE_STACK_TRACE]      = "stack_trace",
-       [BPF_MAP_TYPE_CGROUP_ARRAY]     = "cgroup_array",
-       [BPF_MAP_TYPE_LRU_HASH]         = "lru_hash",
-       [BPF_MAP_TYPE_LRU_PERCPU_HASH]  = "lru_percpu_hash",
-       [BPF_MAP_TYPE_LPM_TRIE]         = "lpm_trie",
-       [BPF_MAP_TYPE_ARRAY_OF_MAPS]    = "array_of_maps",
-       [BPF_MAP_TYPE_HASH_OF_MAPS]     = "hash_of_maps",
-       [BPF_MAP_TYPE_DEVMAP]           = "devmap",
-       [BPF_MAP_TYPE_SOCKMAP]          = "sockmap",
-       [BPF_MAP_TYPE_CPUMAP]           = "cpumap",
-       [BPF_MAP_TYPE_XSKMAP]           = "xskmap",
-       [BPF_MAP_TYPE_SOCKHASH]         = "sockhash",
-       [BPF_MAP_TYPE_CGROUP_STORAGE]   = "cgroup_storage",
-       [BPF_MAP_TYPE_REUSEPORT_SOCKARRAY] = "reuseport_sockarray",
+       [BPF_MAP_TYPE_UNSPEC]                   = "unspec",
+       [BPF_MAP_TYPE_HASH]                     = "hash",
+       [BPF_MAP_TYPE_ARRAY]                    = "array",
+       [BPF_MAP_TYPE_PROG_ARRAY]               = "prog_array",
+       [BPF_MAP_TYPE_PERF_EVENT_ARRAY]         = "perf_event_array",
+       [BPF_MAP_TYPE_PERCPU_HASH]              = "percpu_hash",
+       [BPF_MAP_TYPE_PERCPU_ARRAY]             = "percpu_array",
+       [BPF_MAP_TYPE_STACK_TRACE]              = "stack_trace",
+       [BPF_MAP_TYPE_CGROUP_ARRAY]             = "cgroup_array",
+       [BPF_MAP_TYPE_LRU_HASH]                 = "lru_hash",
+       [BPF_MAP_TYPE_LRU_PERCPU_HASH]          = "lru_percpu_hash",
+       [BPF_MAP_TYPE_LPM_TRIE]                 = "lpm_trie",
+       [BPF_MAP_TYPE_ARRAY_OF_MAPS]            = "array_of_maps",
+       [BPF_MAP_TYPE_HASH_OF_MAPS]             = "hash_of_maps",
+       [BPF_MAP_TYPE_DEVMAP]                   = "devmap",
+       [BPF_MAP_TYPE_SOCKMAP]                  = "sockmap",
+       [BPF_MAP_TYPE_CPUMAP]                   = "cpumap",
+       [BPF_MAP_TYPE_XSKMAP]                   = "xskmap",
+       [BPF_MAP_TYPE_SOCKHASH]                 = "sockhash",
+       [BPF_MAP_TYPE_CGROUP_STORAGE]           = "cgroup_storage",
+       [BPF_MAP_TYPE_REUSEPORT_SOCKARRAY]      = "reuseport_sockarray",
        [BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE]    = "percpu_cgroup_storage",
+       [BPF_MAP_TYPE_QUEUE]                    = "queue",
+       [BPF_MAP_TYPE_STACK]                    = "stack",
 };
 
 static bool map_is_per_cpu(__u32 type)
@@ -215,70 +187,6 @@ err_end_obj:
        return ret;
 }
 
-static int get_btf(struct bpf_map_info *map_info, struct btf **btf)
-{
-       struct bpf_btf_info btf_info = { 0 };
-       __u32 len = sizeof(btf_info);
-       __u32 last_size;
-       int btf_fd;
-       void *ptr;
-       int err;
-
-       err = 0;
-       *btf = NULL;
-       btf_fd = bpf_btf_get_fd_by_id(map_info->btf_id);
-       if (btf_fd < 0)
-               return 0;
-
-       /* we won't know btf_size until we call bpf_obj_get_info_by_fd(). so
-        * let's start with a sane default - 4KiB here - and resize it only if
-        * bpf_obj_get_info_by_fd() needs a bigger buffer.
-        */
-       btf_info.btf_size = 4096;
-       last_size = btf_info.btf_size;
-       ptr = malloc(last_size);
-       if (!ptr) {
-               err = -ENOMEM;
-               goto exit_free;
-       }
-
-       bzero(ptr, last_size);
-       btf_info.btf = ptr_to_u64(ptr);
-       err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
-
-       if (!err && btf_info.btf_size > last_size) {
-               void *temp_ptr;
-
-               last_size = btf_info.btf_size;
-               temp_ptr = realloc(ptr, last_size);
-               if (!temp_ptr) {
-                       err = -ENOMEM;
-                       goto exit_free;
-               }
-               ptr = temp_ptr;
-               bzero(ptr, last_size);
-               btf_info.btf = ptr_to_u64(ptr);
-               err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
-       }
-
-       if (err || btf_info.btf_size > last_size) {
-               err = errno;
-               goto exit_free;
-       }
-
-       *btf = btf__new((__u8 *)btf_info.btf, btf_info.btf_size, NULL);
-       if (IS_ERR(*btf)) {
-               err = PTR_ERR(*btf);
-               *btf = NULL;
-       }
-
-exit_free:
-       close(btf_fd);
-       free(ptr);
-
-       return err;
-}
-
 static json_writer_t *get_btf_writer(void)
 {
        json_writer_t *jw = jsonw_new(stdout);
@@ -383,7 +291,10 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
                printf(single_line ? "  " : "\n");
 
                printf("value:%c", break_names ? '\n' : ' ');
-               fprint_hex(stdout, value, info->value_size, " ");
+               if (value)
+                       fprint_hex(stdout, value, info->value_size, " ");
+               else
+                       printf("<no entry>");
 
                printf("\n");
        } else {
@@ -398,8 +309,11 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
                for (i = 0; i < n; i++) {
                        printf("value (CPU %02d):%c",
                               i, info->value_size > 16 ? '\n' : ' ');
-                       fprint_hex(stdout, value + i * step,
-                                  info->value_size, " ");
+                       if (value)
+                               fprint_hex(stdout, value + i * step,
+                                          info->value_size, " ");
+                       else
+                               printf("<no entry>");
                        printf("\n");
                }
        }
@@ -543,7 +457,6 @@ static int show_map_close_json(int fd, struct bpf_map_info *info)
        char *memlock;
 
        memlock = get_fdinfo(fd, "memlock");
-       close(fd);
 
        jsonw_start_object(json_wtr);
 
@@ -570,6 +483,30 @@ static int show_map_close_json(int fd, struct bpf_map_info *info)
                jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock));
        free(memlock);
 
+       if (info->type == BPF_MAP_TYPE_PROG_ARRAY) {
+               char *owner_prog_type = get_fdinfo(fd, "owner_prog_type");
+               char *owner_jited = get_fdinfo(fd, "owner_jited");
+
+               if (owner_prog_type) {
+                       unsigned int prog_type = atoi(owner_prog_type);
+
+                       if (prog_type < ARRAY_SIZE(prog_type_name))
+                               jsonw_string_field(json_wtr, "owner_prog_type",
+                                                  prog_type_name[prog_type]);
+                       else
+                               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);
+
+               free(owner_prog_type);
+               free(owner_jited);
+       }
+       close(fd);
+
        if (!hash_empty(map_table.table)) {
                struct pinned_obj *obj;
 
@@ -592,7 +529,6 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info)
        char *memlock;
 
        memlock = get_fdinfo(fd, "memlock");
-       close(fd);
 
        printf("%u: ", info->id);
        if (info->type < ARRAY_SIZE(map_type_name))
@@ -613,6 +549,30 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info)
                printf("  memlock %sB", memlock);
        free(memlock);
 
+       if (info->type == BPF_MAP_TYPE_PROG_ARRAY) {
+               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) {
+                       unsigned int prog_type = atoi(owner_prog_type);
+
+                       if (prog_type < ARRAY_SIZE(prog_type_name))
+                               printf("owner_prog_type %s  ",
+                                      prog_type_name[prog_type]);
+                       else
+                               printf("owner_prog_type %d  ", prog_type);
+               }
+               if (atoi(owner_jited))
+                       printf("owner jited");
+               else
+                       printf("owner not jited");
+
+               free(owner_prog_type);
+               free(owner_jited);
+       }
+       close(fd);
+
        printf("\n");
        if (!hash_empty(map_table.table)) {
                struct pinned_obj *obj;
@@ -731,7 +691,11 @@ static int dump_map_elem(int fd, void *key, void *value,
                jsonw_string_field(json_wtr, "error", strerror(lookup_errno));
                jsonw_end_object(json_wtr);
        } else {
-               print_entry_error(map_info, key, strerror(lookup_errno));
+               if (errno == ENOENT)
+                       print_entry_plain(map_info, key, NULL);
+               else
+                       print_entry_error(map_info, key,
+                                         strerror(lookup_errno));
        }
 
        return 0;
@@ -765,7 +729,7 @@ static int do_dump(int argc, char **argv)
 
        prev_key = NULL;
 
-       err = get_btf(&info, &btf);
+       err = btf__get_from_id(info.btf_id, &btf);
        if (err) {
                p_err("failed to get btf");
                goto exit_free;
@@ -909,7 +873,7 @@ static int do_lookup(int argc, char **argv)
        }
 
        /* here means bpf_map_lookup_elem() succeeded */
-       err = get_btf(&info, &btf);
+       err = btf__get_from_id(info.btf_id, &btf);
        if (err) {
                p_err("failed to get btf");
                goto exit_free;
@@ -1140,6 +1104,8 @@ static int do_create(int argc, char **argv)
                return -1;
        }
 
+       set_max_rlimit();
+
        fd = bpf_create_map_xattr(&attr);
        if (fd < 0) {
                p_err("map create failed: %s", strerror(errno));
index bdaf406..0507dfa 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 /* Copyright (C) 2018 Netronome Systems, Inc. */
 /* This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index d441bb7..db0e7de 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 // Copyright (C) 2018 Facebook
 
 #define _GNU_SOURCE
index 4e9f453..550a0f5 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 // Copyright (C) 2018 Facebook
 
 #include <stdlib.h>
index e3516b5..774af6c 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
 // Copyright (C) 2018 Facebook
 
 #ifndef _NETLINK_DUMPER_H_
index b76b77d..f2a545e 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 // Copyright (C) 2018 Facebook
 // Author: Yonghong Song <yhs@fb.com>
 
index 5302ee2..2d1bb7d 100644 (file)
@@ -1,35 +1,5 @@
-/*
- * Copyright (C) 2017-2018 Netronome Systems, Inc.
- *
- * This software is dual licensed under the GNU General License Version 2,
- * June 1991 as shown in the file COPYING in the top-level directory of this
- * source tree or the BSD 2-Clause License provided below.  You have the
- * option to license this software under the complete terms of either license.
- *
- * The BSD 2-Clause License:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *
- *      2. 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.
- */
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
 
 #define _GNU_SOURCE
 #include <errno.h>
 #include <linux/err.h>
 
 #include <bpf.h>
+#include <btf.h>
 #include <libbpf.h>
 
 #include "cfg.h"
 #include "main.h"
 #include "xlated_dumper.h"
 
-static const char * const prog_type_name[] = {
-       [BPF_PROG_TYPE_UNSPEC]          = "unspec",
-       [BPF_PROG_TYPE_SOCKET_FILTER]   = "socket_filter",
-       [BPF_PROG_TYPE_KPROBE]          = "kprobe",
-       [BPF_PROG_TYPE_SCHED_CLS]       = "sched_cls",
-       [BPF_PROG_TYPE_SCHED_ACT]       = "sched_act",
-       [BPF_PROG_TYPE_TRACEPOINT]      = "tracepoint",
-       [BPF_PROG_TYPE_XDP]             = "xdp",
-       [BPF_PROG_TYPE_PERF_EVENT]      = "perf_event",
-       [BPF_PROG_TYPE_CGROUP_SKB]      = "cgroup_skb",
-       [BPF_PROG_TYPE_CGROUP_SOCK]     = "cgroup_sock",
-       [BPF_PROG_TYPE_LWT_IN]          = "lwt_in",
-       [BPF_PROG_TYPE_LWT_OUT]         = "lwt_out",
-       [BPF_PROG_TYPE_LWT_XMIT]        = "lwt_xmit",
-       [BPF_PROG_TYPE_SOCK_OPS]        = "sock_ops",
-       [BPF_PROG_TYPE_SK_SKB]          = "sk_skb",
-       [BPF_PROG_TYPE_CGROUP_DEVICE]   = "cgroup_device",
-       [BPF_PROG_TYPE_SK_MSG]          = "sk_msg",
-       [BPF_PROG_TYPE_RAW_TRACEPOINT]  = "raw_tracepoint",
-       [BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr",
-       [BPF_PROG_TYPE_LIRC_MODE2]      = "lirc_mode2",
-       [BPF_PROG_TYPE_FLOW_DISSECTOR]  = "flow_dissector",
-};
-
 static const char * const attach_type_strings[] = {
        [BPF_SK_SKB_STREAM_PARSER] = "stream_parser",
        [BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict",
        [BPF_SK_MSG_VERDICT] = "msg_verdict",
+       [BPF_FLOW_DISSECTOR] = "flow_dissector",
        [__MAX_BPF_ATTACH_TYPE] = NULL,
 };
 
-enum bpf_attach_type parse_attach_type(const char *str)
+static enum bpf_attach_type parse_attach_type(const char *str)
 {
        enum bpf_attach_type type;
 
@@ -357,10 +305,9 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd)
        if (!hash_empty(prog_table.table)) {
                struct pinned_obj *obj;
 
-               printf("\n");
                hash_for_each_possible(prog_table.table, obj, hash, info->id) {
                        if (obj->id == info->id)
-                               printf("\tpinned %s\n", obj->path);
+                               printf("\n\tpinned %s", obj->path);
                }
        }
 
@@ -446,6 +393,10 @@ static int do_show(int argc, char **argv)
 
 static int do_dump(int argc, char **argv)
 {
+       unsigned int finfo_rec_size, linfo_rec_size, jited_linfo_rec_size;
+       void *func_info = NULL, *linfo = NULL, *jited_linfo = NULL;
+       unsigned int nr_finfo, nr_linfo = 0, nr_jited_linfo = 0;
+       struct bpf_prog_linfo *prog_linfo = NULL;
        unsigned long *func_ksyms = NULL;
        struct bpf_prog_info info = {};
        unsigned int *func_lens = NULL;
@@ -454,11 +405,14 @@ static int do_dump(int argc, char **argv)
        unsigned int nr_func_lens;
        struct dump_data dd = {};
        __u32 len = sizeof(info);
+       struct btf *btf = NULL;
        unsigned int buf_size;
        char *filepath = NULL;
        bool opcodes = false;
        bool visual = false;
+       char func_sig[1024];
        unsigned char *buf;
+       bool linum = false;
        __u32 *member_len;
        __u64 *member_ptr;
        ssize_t n;
@@ -466,6 +420,9 @@ static int do_dump(int argc, char **argv)
        int fd;
 
        if (is_prefix(*argv, "jited")) {
+               if (disasm_init())
+                       return -1;
+
                member_len = &info.jited_prog_len;
                member_ptr = &info.jited_prog_insns;
        } else if (is_prefix(*argv, "xlated")) {
@@ -499,6 +456,9 @@ static int do_dump(int argc, char **argv)
        } else if (is_prefix(*argv, "visual")) {
                visual = true;
                NEXT_ARG();
+       } else if (is_prefix(*argv, "linum")) {
+               linum = true;
+               NEXT_ARG();
        }
 
        if (argc) {
@@ -547,6 +507,43 @@ static int do_dump(int argc, char **argv)
                }
        }
 
+       nr_finfo = info.nr_func_info;
+       finfo_rec_size = info.func_info_rec_size;
+       if (nr_finfo && finfo_rec_size) {
+               func_info = malloc(nr_finfo * finfo_rec_size);
+               if (!func_info) {
+                       p_err("mem alloc failed");
+                       close(fd);
+                       goto err_free;
+               }
+       }
+
+       linfo_rec_size = info.line_info_rec_size;
+       if (info.nr_line_info && linfo_rec_size && info.btf_id) {
+               nr_linfo = info.nr_line_info;
+               linfo = malloc(nr_linfo * linfo_rec_size);
+               if (!linfo) {
+                       p_err("mem alloc failed");
+                       close(fd);
+                       goto err_free;
+               }
+       }
+
+       jited_linfo_rec_size = info.jited_line_info_rec_size;
+       if (info.nr_jited_line_info &&
+           jited_linfo_rec_size &&
+           info.nr_jited_ksyms &&
+           info.nr_jited_func_lens &&
+           info.btf_id) {
+               nr_jited_linfo = info.nr_jited_line_info;
+               jited_linfo = malloc(nr_jited_linfo * jited_linfo_rec_size);
+               if (!jited_linfo) {
+                       p_err("mem alloc failed");
+                       close(fd);
+                       goto err_free;
+               }
+       }
+
        memset(&info, 0, sizeof(info));
 
        *member_ptr = ptr_to_u64(buf);
@@ -555,6 +552,15 @@ static int do_dump(int argc, char **argv)
        info.nr_jited_ksyms = nr_func_ksyms;
        info.jited_func_lens = ptr_to_u64(func_lens);
        info.nr_jited_func_lens = nr_func_lens;
+       info.nr_func_info = nr_finfo;
+       info.func_info_rec_size = finfo_rec_size;
+       info.func_info = ptr_to_u64(func_info);
+       info.nr_line_info = nr_linfo;
+       info.line_info_rec_size = linfo_rec_size;
+       info.line_info = ptr_to_u64(linfo);
+       info.nr_jited_line_info = nr_jited_linfo;
+       info.jited_line_info_rec_size = jited_linfo_rec_size;
+       info.jited_line_info = ptr_to_u64(jited_linfo);
 
        err = bpf_obj_get_info_by_fd(fd, &info, &len);
        close(fd);
@@ -578,6 +584,42 @@ static int do_dump(int argc, char **argv)
                goto err_free;
        }
 
+       if (info.nr_func_info != nr_finfo) {
+               p_err("incorrect nr_func_info %d vs. expected %d",
+                     info.nr_func_info, nr_finfo);
+               goto err_free;
+       }
+
+       if (info.func_info_rec_size != finfo_rec_size) {
+               p_err("incorrect func_info_rec_size %d vs. expected %d",
+                     info.func_info_rec_size, finfo_rec_size);
+               goto err_free;
+       }
+
+       if (linfo && info.nr_line_info != nr_linfo) {
+               p_err("incorrect nr_line_info %u vs. expected %u",
+                     info.nr_line_info, nr_linfo);
+               goto err_free;
+       }
+
+       if (info.line_info_rec_size != linfo_rec_size) {
+               p_err("incorrect line_info_rec_size %u vs. expected %u",
+                     info.line_info_rec_size, linfo_rec_size);
+               goto err_free;
+       }
+
+       if (jited_linfo && info.nr_jited_line_info != nr_jited_linfo) {
+               p_err("incorrect nr_jited_line_info %u vs. expected %u",
+                     info.nr_jited_line_info, nr_jited_linfo);
+               goto err_free;
+       }
+
+       if (info.jited_line_info_rec_size != jited_linfo_rec_size) {
+               p_err("incorrect jited_line_info_rec_size %u vs. expected %u",
+                     info.jited_line_info_rec_size, jited_linfo_rec_size);
+               goto err_free;
+       }
+
        if ((member_len == &info.jited_prog_len &&
             info.jited_prog_insns == 0) ||
            (member_len == &info.xlated_prog_len &&
@@ -586,6 +628,17 @@ static int do_dump(int argc, char **argv)
                goto err_free;
        }
 
+       if (info.btf_id && btf__get_from_id(info.btf_id, &btf)) {
+               p_err("failed to get btf");
+               goto err_free;
+       }
+
+       if (nr_linfo) {
+               prog_linfo = bpf_prog_linfo__new(&info);
+               if (!prog_linfo)
+                       p_info("error in processing bpf_line_info.  continue without it.");
+       }
+
        if (filepath) {
                fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
                if (fd < 0) {
@@ -618,6 +671,7 @@ static int do_dump(int argc, char **argv)
 
                if (info.nr_jited_func_lens && info.jited_func_lens) {
                        struct kernel_sym *sym = NULL;
+                       struct bpf_func_info *record;
                        char sym_name[SYM_MAX_NAME];
                        unsigned char *img = buf;
                        __u64 *ksyms = NULL;
@@ -644,17 +698,33 @@ static int do_dump(int argc, char **argv)
                                        strcpy(sym_name, "unknown");
                                }
 
+                               if (func_info) {
+                                       record = func_info + i * finfo_rec_size;
+                                       btf_dumper_type_only(btf, record->type_id,
+                                                            func_sig,
+                                                            sizeof(func_sig));
+                               }
+
                                if (json_output) {
                                        jsonw_start_object(json_wtr);
+                                       if (func_info && func_sig[0] != '\0') {
+                                               jsonw_name(json_wtr, "proto");
+                                               jsonw_string(json_wtr, func_sig);
+                                       }
                                        jsonw_name(json_wtr, "name");
                                        jsonw_string(json_wtr, sym_name);
                                        jsonw_name(json_wtr, "insns");
                                } else {
+                                       if (func_info && func_sig[0] != '\0')
+                                               printf("%s:\n", func_sig);
                                        printf("%s:\n", sym_name);
                                }
 
-                               disasm_print_insn(img, lens[i], opcodes, name,
-                                                 disasm_opt);
+                               disasm_print_insn(img, lens[i], opcodes,
+                                                 name, disasm_opt, btf,
+                                                 prog_linfo, ksyms[i], i,
+                                                 linum);
+
                                img += lens[i];
 
                                if (json_output)
@@ -667,7 +737,7 @@ static int do_dump(int argc, char **argv)
                                jsonw_end_array(json_wtr);
                } else {
                        disasm_print_insn(buf, *member_len, opcodes, name,
-                                         disasm_opt);
+                                         disasm_opt, btf, NULL, 0, 0, false);
                }
        } else if (visual) {
                if (json_output)
@@ -678,23 +748,37 @@ static int do_dump(int argc, char **argv)
                kernel_syms_load(&dd);
                dd.nr_jited_ksyms = info.nr_jited_ksyms;
                dd.jited_ksyms = (__u64 *) info.jited_ksyms;
+               dd.btf = btf;
+               dd.func_info = func_info;
+               dd.finfo_rec_size = finfo_rec_size;
+               dd.prog_linfo = prog_linfo;
 
                if (json_output)
-                       dump_xlated_json(&dd, buf, *member_len, opcodes);
+                       dump_xlated_json(&dd, buf, *member_len, opcodes,
+                                        linum);
                else
-                       dump_xlated_plain(&dd, buf, *member_len, opcodes);
+                       dump_xlated_plain(&dd, buf, *member_len, opcodes,
+                                         linum);
                kernel_syms_destroy(&dd);
        }
 
        free(buf);
        free(func_ksyms);
        free(func_lens);
+       free(func_info);
+       free(linfo);
+       free(jited_linfo);
+       bpf_prog_linfo__free(prog_linfo);
        return 0;
 
 err_free:
        free(buf);
        free(func_ksyms);
        free(func_lens);
+       free(func_info);
+       free(linfo);
+       free(jited_linfo);
+       bpf_prog_linfo__free(prog_linfo);
        return -1;
 }
 
@@ -714,37 +798,56 @@ struct map_replace {
        char *name;
 };
 
-int map_replace_compar(const void *p1, const void *p2)
+static int map_replace_compar(const void *p1, const void *p2)
 {
        const struct map_replace *a = p1, *b = p2;
 
        return a->idx - b->idx;
 }
 
-static int do_attach(int argc, char **argv)
+static int parse_attach_detach_args(int argc, char **argv, int *progfd,
+                                   enum bpf_attach_type *attach_type,
+                                   int *mapfd)
 {
-       enum bpf_attach_type attach_type;
-       int err, mapfd, progfd;
-
-       if (!REQ_ARGS(5)) {
-               p_err("too few parameters for map attach");
+       if (!REQ_ARGS(3))
                return -EINVAL;
-       }
 
-       progfd = prog_parse_fd(&argc, &argv);
-       if (progfd < 0)
-               return progfd;
+       *progfd = prog_parse_fd(&argc, &argv);
+       if (*progfd < 0)
+               return *progfd;
 
-       attach_type = parse_attach_type(*argv);
-       if (attach_type == __MAX_BPF_ATTACH_TYPE) {
-               p_err("invalid attach type");
+       *attach_type = parse_attach_type(*argv);
+       if (*attach_type == __MAX_BPF_ATTACH_TYPE) {
+               p_err("invalid attach/detach type");
                return -EINVAL;
        }
+
+       if (*attach_type == BPF_FLOW_DISSECTOR) {
+               *mapfd = -1;
+               return 0;
+       }
+
        NEXT_ARG();
+       if (!REQ_ARGS(2))
+               return -EINVAL;
+
+       *mapfd = map_parse_fd(&argc, &argv);
+       if (*mapfd < 0)
+               return *mapfd;
+
+       return 0;
+}
 
-       mapfd = map_parse_fd(&argc, &argv);
-       if (mapfd < 0)
-               return mapfd;
+static int do_attach(int argc, char **argv)
+{
+       enum bpf_attach_type attach_type;
+       int err, progfd;
+       int mapfd;
+
+       err = parse_attach_detach_args(argc, argv,
+                                      &progfd, &attach_type, &mapfd);
+       if (err)
+               return err;
 
        err = bpf_prog_attach(progfd, mapfd, attach_type, 0);
        if (err) {
@@ -760,27 +863,13 @@ static int do_attach(int argc, char **argv)
 static int do_detach(int argc, char **argv)
 {
        enum bpf_attach_type attach_type;
-       int err, mapfd, progfd;
-
-       if (!REQ_ARGS(5)) {
-               p_err("too few parameters for map detach");
-               return -EINVAL;
-       }
+       int err, progfd;
+       int mapfd;
 
-       progfd = prog_parse_fd(&argc, &argv);
-       if (progfd < 0)
-               return progfd;
-
-       attach_type = parse_attach_type(*argv);
-       if (attach_type == __MAX_BPF_ATTACH_TYPE) {
-               p_err("invalid attach type");
-               return -EINVAL;
-       }
-       NEXT_ARG();
-
-       mapfd = map_parse_fd(&argc, &argv);
-       if (mapfd < 0)
-               return mapfd;
+       err = parse_attach_detach_args(argc, argv,
+                                      &progfd, &attach_type, &mapfd);
+       if (err)
+               return err;
 
        err = bpf_prog_detach2(progfd, mapfd, attach_type);
        if (err) {
@@ -792,15 +881,17 @@ static int do_detach(int argc, char **argv)
                jsonw_null(json_wtr);
        return 0;
 }
-static int do_load(int argc, char **argv)
+
+static int load_with_options(int argc, char **argv, bool first_prog_only)
 {
        enum bpf_attach_type expected_attach_type;
        struct bpf_object_open_attr attr = {
                .prog_type      = BPF_PROG_TYPE_UNSPEC,
        };
        struct map_replace *map_replace = NULL;
+       struct bpf_program *prog = NULL, *pos;
        unsigned int old_map_fds = 0;
-       struct bpf_program *prog;
+       const char *pinmaps = NULL;
        struct bpf_object *obj;
        struct bpf_map *map;
        const char *pinfile;
@@ -845,6 +936,7 @@ static int do_load(int argc, char **argv)
                        }
                        NEXT_ARG();
                } else if (is_prefix(*argv, "map")) {
+                       void *new_map_replace;
                        char *endptr, *name;
                        int fd;
 
@@ -878,12 +970,15 @@ static int do_load(int argc, char **argv)
                        if (fd < 0)
                                goto err_free_reuse_maps;
 
-                       map_replace = reallocarray(map_replace, old_map_fds + 1,
-                                                  sizeof(*map_replace));
-                       if (!map_replace) {
+                       new_map_replace = reallocarray(map_replace,
+                                                      old_map_fds + 1,
+                                                      sizeof(*map_replace));
+                       if (!new_map_replace) {
                                p_err("mem alloc failed");
                                goto err_free_reuse_maps;
                        }
+                       map_replace = new_map_replace;
+
                        map_replace[old_map_fds].idx = idx;
                        map_replace[old_map_fds].name = name;
                        map_replace[old_map_fds].fd = fd;
@@ -905,6 +1000,13 @@ static int do_load(int argc, char **argv)
                                goto err_free_reuse_maps;
                        }
                        NEXT_ARG();
+               } else if (is_prefix(*argv, "pinmaps")) {
+                       NEXT_ARG();
+
+                       if (!REQ_ARGS(1))
+                               goto err_free_reuse_maps;
+
+                       pinmaps = GET_ARG();
                } else {
                        p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
                              *argv);
@@ -918,26 +1020,25 @@ static int do_load(int argc, char **argv)
                goto err_free_reuse_maps;
        }
 
-       prog = bpf_program__next(NULL, obj);
-       if (!prog) {
-               p_err("object file doesn't contain any bpf program");
-               goto err_close_obj;
-       }
+       bpf_object__for_each_program(pos, obj) {
+               enum bpf_prog_type prog_type = attr.prog_type;
 
-       bpf_program__set_ifindex(prog, ifindex);
-       if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) {
-               const char *sec_name = bpf_program__title(prog, false);
+               if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) {
+                       const char *sec_name = bpf_program__title(pos, false);
 
-               err = libbpf_prog_type_by_name(sec_name, &attr.prog_type,
-                                              &expected_attach_type);
-               if (err < 0) {
-                       p_err("failed to guess program type based on section name %s\n",
-                             sec_name);
-                       goto err_close_obj;
+                       err = libbpf_prog_type_by_name(sec_name, &prog_type,
+                                                      &expected_attach_type);
+                       if (err < 0) {
+                               p_err("failed to guess program type based on section name %s\n",
+                                     sec_name);
+                               goto err_close_obj;
+                       }
                }
+
+               bpf_program__set_ifindex(pos, ifindex);
+               bpf_program__set_type(pos, prog_type);
+               bpf_program__set_expected_attach_type(pos, expected_attach_type);
        }
-       bpf_program__set_type(prog, attr.prog_type);
-       bpf_program__set_expected_attach_type(prog, expected_attach_type);
 
        qsort(map_replace, old_map_fds, sizeof(*map_replace),
              map_replace_compar);
@@ -995,15 +1096,47 @@ static int do_load(int argc, char **argv)
                goto err_close_obj;
        }
 
+       set_max_rlimit();
+
        err = bpf_object__load(obj);
        if (err) {
                p_err("failed to load object file");
                goto err_close_obj;
        }
 
-       if (do_pin_fd(bpf_program__fd(prog), pinfile))
+       err = mount_bpffs_for_pin(pinfile);
+       if (err)
                goto err_close_obj;
 
+       if (first_prog_only) {
+               prog = bpf_program__next(NULL, obj);
+               if (!prog) {
+                       p_err("object file doesn't contain any bpf program");
+                       goto err_close_obj;
+               }
+
+               err = bpf_obj_pin(bpf_program__fd(prog), pinfile);
+               if (err) {
+                       p_err("failed to pin program %s",
+                             bpf_program__title(prog, false));
+                       goto err_close_obj;
+               }
+       } else {
+               err = bpf_object__pin_programs(obj, pinfile);
+               if (err) {
+                       p_err("failed to pin all programs");
+                       goto err_close_obj;
+               }
+       }
+
+       if (pinmaps) {
+               err = bpf_object__pin_maps(obj, pinmaps);
+               if (err) {
+                       p_err("failed to pin all maps");
+                       goto err_unpin;
+               }
+       }
+
        if (json_output)
                jsonw_null(json_wtr);
 
@@ -1014,6 +1147,11 @@ static int do_load(int argc, char **argv)
 
        return 0;
 
+err_unpin:
+       if (first_prog_only)
+               unlink(pinfile);
+       else
+               bpf_object__unpin_programs(obj, pinfile);
 err_close_obj:
        bpf_object__close(obj);
 err_free_reuse_maps:
@@ -1023,6 +1161,16 @@ err_free_reuse_maps:
        return -1;
 }
 
+static int do_load(int argc, char **argv)
+{
+       return load_with_options(argc, argv, true);
+}
+
+static int do_loadall(int argc, char **argv)
+{
+       return load_with_options(argc, argv, false);
+}
+
 static int do_help(int argc, char **argv)
 {
        if (json_output) {
@@ -1032,13 +1180,16 @@ static int do_help(int argc, char **argv)
 
        fprintf(stderr,
                "Usage: %s %s { show | list } [PROG]\n"
-               "       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
-               "       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
+               "       %s %s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n"
+               "       %s %s dump jited  PROG [{ file FILE | opcodes | linum }]\n"
                "       %s %s pin   PROG FILE\n"
-               "       %s %s load  OBJ  FILE [type TYPE] [dev NAME] \\\n"
-               "                         [map { idx IDX | name NAME } MAP]\n"
-               "       %s %s attach PROG ATTACH_TYPE MAP\n"
-               "       %s %s detach PROG ATTACH_TYPE MAP\n"
+               "       %s %s { load | loadall } OBJ  PATH \\\n"
+               "                         [type TYPE] [dev NAME] \\\n"
+               "                         [map { idx IDX | name NAME } MAP]\\\n"
+               "                         [pinmaps MAP_DIR]\n"
+               "       %s %s attach PROG ATTACH_TYPE [MAP]\n"
+               "       %s %s detach PROG ATTACH_TYPE [MAP]\n"
+               "       %s %s tracelog\n"
                "       %s %s help\n"
                "\n"
                "       " HELP_SPEC_MAP "\n"
@@ -1047,15 +1198,17 @@ static int do_help(int argc, char **argv)
                "                 tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
                "                 cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
                "                 lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
+               "                 sk_reuseport | flow_dissector |\n"
                "                 cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
                "                 cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
                "                 cgroup/sendmsg4 | cgroup/sendmsg6 }\n"
-               "       ATTACH_TYPE := { msg_verdict | skb_verdict | skb_parse }\n"
+               "       ATTACH_TYPE := { msg_verdict | skb_verdict | skb_parse |\n"
+               "                        flow_dissector }\n"
                "       " HELP_SPEC_OPTIONS "\n"
                "",
                bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
                bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
-               bin_name, argv[-2], bin_name, argv[-2]);
+               bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
 
        return 0;
 }
@@ -1067,8 +1220,10 @@ static const struct cmd cmds[] = {
        { "dump",       do_dump },
        { "pin",        do_pin },
        { "load",       do_load },
+       { "loadall",    do_loadall },
        { "attach",     do_attach },
        { "detach",     do_detach },
+       { "tracelog",   do_tracelog },
        { 0 }
 };
 
diff --git a/tools/bpf/bpftool/tracelog.c b/tools/bpf/bpftool/tracelog.c
new file mode 100644 (file)
index 0000000..e80a5c7
--- /dev/null
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (c) 2015-2017 Daniel Borkmann */
+/* Copyright (c) 2018 Netronome Systems, Inc. */
+
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/magic.h>
+#include <sys/fcntl.h>
+#include <sys/vfs.h>
+
+#include "main.h"
+
+#ifndef TRACEFS_MAGIC
+# define TRACEFS_MAGIC 0x74726163
+#endif
+
+#define _textify(x)    #x
+#define textify(x)     _textify(x)
+
+FILE *trace_pipe_fd;
+char *buff;
+
+static int validate_tracefs_mnt(const char *mnt, unsigned long magic)
+{
+       struct statfs st_fs;
+
+       if (statfs(mnt, &st_fs) < 0)
+               return -ENOENT;
+       if ((unsigned long)st_fs.f_type != magic)
+               return -ENOENT;
+
+       return 0;
+}
+
+static bool
+find_tracefs_mnt_single(unsigned long magic, char *mnt, const char *mntpt)
+{
+       size_t src_len;
+
+       if (validate_tracefs_mnt(mntpt, magic))
+               return false;
+
+       src_len = strlen(mntpt);
+       if (src_len + 1 >= PATH_MAX) {
+               p_err("tracefs mount point name too long");
+               return false;
+       }
+
+       strcpy(mnt, mntpt);
+       return true;
+}
+
+static bool get_tracefs_pipe(char *mnt)
+{
+       static const char * const known_mnts[] = {
+               "/sys/kernel/debug/tracing",
+               "/sys/kernel/tracing",
+               "/tracing",
+               "/trace",
+       };
+       const char *pipe_name = "/trace_pipe";
+       const char *fstype = "tracefs";
+       char type[100], format[32];
+       const char * const *ptr;
+       bool found = false;
+       FILE *fp;
+
+       for (ptr = known_mnts; ptr < known_mnts + ARRAY_SIZE(known_mnts); ptr++)
+               if (find_tracefs_mnt_single(TRACEFS_MAGIC, mnt, *ptr))
+                       goto exit_found;
+
+       fp = fopen("/proc/mounts", "r");
+       if (!fp)
+               return false;
+
+       /* Allow room for NULL terminating byte and pipe file name */
+       snprintf(format, sizeof(format), "%%*s %%%zds %%99s %%*s %%*d %%*d\\n",
+                PATH_MAX - strlen(pipe_name) - 1);
+       while (fscanf(fp, format, mnt, type) == 2)
+               if (strcmp(type, fstype) == 0) {
+                       found = true;
+                       break;
+               }
+       fclose(fp);
+
+       /* The string from fscanf() might be truncated, check mnt is valid */
+       if (found && validate_tracefs_mnt(mnt, TRACEFS_MAGIC))
+               goto exit_found;
+
+       if (block_mount)
+               return false;
+
+       p_info("could not find tracefs, attempting to mount it now");
+       /* Most of the time, tracefs is automatically mounted by debugfs at
+        * /sys/kernel/debug/tracing when we try to access it. If we could not
+        * find it, it is likely that debugfs is not mounted. Let's give one
+        * attempt at mounting just tracefs at /sys/kernel/tracing.
+        */
+       strcpy(mnt, known_mnts[1]);
+       if (mount_tracefs(mnt))
+               return false;
+
+exit_found:
+       strcat(mnt, pipe_name);
+       return true;
+}
+
+static void exit_tracelog(int signum)
+{
+       fclose(trace_pipe_fd);
+       free(buff);
+
+       if (json_output) {
+               jsonw_end_array(json_wtr);
+               jsonw_destroy(&json_wtr);
+       }
+
+       exit(0);
+}
+
+int do_tracelog(int argc, char **argv)
+{
+       const struct sigaction act = {
+               .sa_handler = exit_tracelog
+       };
+       char trace_pipe[PATH_MAX];
+       size_t buff_len = 0;
+
+       if (json_output)
+               jsonw_start_array(json_wtr);
+
+       if (!get_tracefs_pipe(trace_pipe))
+               return -1;
+
+       trace_pipe_fd = fopen(trace_pipe, "r");
+       if (!trace_pipe_fd) {
+               p_err("could not open trace pipe: %s", strerror(errno));
+               return -1;
+       }
+
+       sigaction(SIGHUP, &act, NULL);
+       sigaction(SIGINT, &act, NULL);
+       sigaction(SIGTERM, &act, NULL);
+       while (1) {
+               ssize_t ret;
+
+               ret = getline(&buff, &buff_len, trace_pipe_fd);
+               if (ret <= 0) {
+                       p_err("failed to read content from trace pipe: %s",
+                             strerror(errno));
+                       break;
+               }
+               if (json_output)
+                       jsonw_string(json_wtr, buff);
+               else
+                       printf("%s", buff);
+       }
+
+       fclose(trace_pipe_fd);
+       free(buff);
+       return -1;
+}
index 3284759..7073dbe 100644 (file)
@@ -1,39 +1,5 @@
 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-/*
- * Copyright (C) 2018 Netronome Systems, Inc.
- *
- * This software is dual licensed under the GNU General License Version 2,
- * June 1991 as shown in the file COPYING in the top-level directory of this
- * source tree or the BSD 2-Clause License provided below.  You have the
- * option to license this software under the complete terms of either license.
- *
- * The BSD 2-Clause License:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *
- *      2. 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.
- *
- * 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 HOLDER 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.
- */
+/* Copyright (C) 2018 Netronome Systems, Inc. */
 
 #define _GNU_SOURCE
 #include <stdarg.h>
@@ -41,6 +7,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <libbpf.h>
 
 #include "disasm.h"
 #include "json_writer.h"
@@ -114,7 +81,7 @@ struct kernel_sym *kernel_syms_search(struct dump_data *dd,
                       sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
 }
 
-static void print_insn(void *private_data, const char *fmt, ...)
+static void __printf(2, 3) print_insn(void *private_data, const char *fmt, ...)
 {
        va_list args;
 
@@ -123,7 +90,7 @@ static void print_insn(void *private_data, const char *fmt, ...)
        va_end(args);
 }
 
-static void
+static void __printf(2, 3)
 print_insn_for_graph(void *private_data, const char *fmt, ...)
 {
        char buf[64], *p;
@@ -154,7 +121,8 @@ print_insn_for_graph(void *private_data, const char *fmt, ...)
        printf("%s", buf);
 }
 
-static void print_insn_json(void *private_data, const char *fmt, ...)
+static void __printf(2, 3)
+print_insn_json(void *private_data, const char *fmt, ...)
 {
        unsigned int l = strlen(fmt);
        char chomped_fmt[l];
@@ -234,19 +202,25 @@ static const char *print_imm(void *private_data,
 }
 
 void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
-                     bool opcodes)
+                     bool opcodes, bool linum)
 {
+       const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
        const struct bpf_insn_cbs cbs = {
                .cb_print       = print_insn_json,
                .cb_call        = print_call,
                .cb_imm         = print_imm,
                .private_data   = dd,
        };
+       struct bpf_func_info *record;
        struct bpf_insn *insn = buf;
+       struct btf *btf = dd->btf;
        bool double_insn = false;
+       unsigned int nr_skip = 0;
+       char func_sig[1024];
        unsigned int i;
 
        jsonw_start_array(json_wtr);
+       record = dd->func_info;
        for (i = 0; i < len / sizeof(*insn); i++) {
                if (double_insn) {
                        double_insn = false;
@@ -255,6 +229,30 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 
                jsonw_start_object(json_wtr);
+
+               if (btf && record) {
+                       if (record->insn_off == i) {
+                               btf_dumper_type_only(btf, record->type_id,
+                                                    func_sig,
+                                                    sizeof(func_sig));
+                               if (func_sig[0] != '\0') {
+                                       jsonw_name(json_wtr, "proto");
+                                       jsonw_string(json_wtr, func_sig);
+                               }
+                               record = (void *)record + dd->finfo_rec_size;
+                       }
+               }
+
+               if (prog_linfo) {
+                       const struct bpf_line_info *linfo;
+
+                       linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
+                       if (linfo) {
+                               btf_dump_linfo_json(btf, linfo, linum);
+                               nr_skip++;
+                       }
+               }
+
                jsonw_name(json_wtr, "disasm");
                print_bpf_insn(&cbs, insn + i, true);
 
@@ -289,24 +287,52 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
 }
 
 void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
-                      bool opcodes)
+                      bool opcodes, bool linum)
 {
+       const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
        const struct bpf_insn_cbs cbs = {
                .cb_print       = print_insn,
                .cb_call        = print_call,
                .cb_imm         = print_imm,
                .private_data   = dd,
        };
+       struct bpf_func_info *record;
        struct bpf_insn *insn = buf;
+       struct btf *btf = dd->btf;
+       unsigned int nr_skip = 0;
        bool double_insn = false;
+       char func_sig[1024];
        unsigned int i;
 
+       record = dd->func_info;
        for (i = 0; i < len / sizeof(*insn); i++) {
                if (double_insn) {
                        double_insn = false;
                        continue;
                }
 
+               if (btf && record) {
+                       if (record->insn_off == i) {
+                               btf_dumper_type_only(btf, record->type_id,
+                                                    func_sig,
+                                                    sizeof(func_sig));
+                               if (func_sig[0] != '\0')
+                                       printf("%s:\n", func_sig);
+                               record = (void *)record + dd->finfo_rec_size;
+                       }
+               }
+
+               if (prog_linfo) {
+                       const struct bpf_line_info *linfo;
+
+                       linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
+                       if (linfo) {
+                               btf_dump_linfo_plain(btf, linfo, "; ",
+                                                    linum);
+                               nr_skip++;
+                       }
+               }
+
                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 
                printf("% 4d: ", i);
index 33d86e2..54847e1 100644 (file)
@@ -1,45 +1,13 @@
-// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-/*
- * Copyright (C) 2018 Netronome Systems, Inc.
- *
- * This software is dual licensed under the GNU General License Version 2,
- * June 1991 as shown in the file COPYING in the top-level directory of this
- * source tree or the BSD 2-Clause License provided below.  You have the
- * option to license this software under the complete terms of either license.
- *
- * The BSD 2-Clause License:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *
- *      2. 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.
- *
- * 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 HOLDER 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.
- */
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/* Copyright (C) 2018 Netronome Systems, Inc. */
 
 #ifndef __BPF_TOOL_XLATED_DUMPER_H
 #define __BPF_TOOL_XLATED_DUMPER_H
 
 #define SYM_MAX_NAME   256
 
+struct bpf_prog_linfo;
+
 struct kernel_sym {
        unsigned long address;
        char name[SYM_MAX_NAME];
@@ -51,6 +19,10 @@ struct dump_data {
        __u32 sym_count;
        __u64 *jited_ksyms;
        __u32 nr_jited_ksyms;
+       struct btf *btf;
+       void *func_info;
+       __u32 finfo_rec_size;
+       const struct bpf_prog_linfo *prog_linfo;
        char scratch_buff[SYM_MAX_NAME + 8];
 };
 
@@ -58,9 +30,9 @@ void kernel_syms_load(struct dump_data *dd);
 void kernel_syms_destroy(struct dump_data *dd);
 struct kernel_sym *kernel_syms_search(struct dump_data *dd, unsigned long key);
 void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
-                     bool opcodes);
+                      bool opcodes, bool linum);
 void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
-                      bool opcodes);
+                      bool opcodes, bool linum);
 void dump_xlated_for_graph(struct dump_data *dd, void *buf, void *buf_end,
                           unsigned int start_index);
 
index f216b2f..d74bb94 100644 (file)
@@ -33,6 +33,7 @@ FEATURE_TESTS_BASIC :=                  \
         dwarf_getlocations              \
         fortify-source                  \
         sync-compare-and-swap           \
+        get_current_dir_name            \
         glibc                           \
         gtk2                            \
         gtk2-infobar                    \
index 0516259..304b984 100644 (file)
@@ -7,6 +7,7 @@ FILES=                                          \
          test-dwarf_getlocations.bin            \
          test-fortify-source.bin                \
          test-sync-compare-and-swap.bin         \
+         test-get_current_dir_name.bin          \
          test-glibc.bin                         \
          test-gtk2.bin                          \
          test-gtk2-infobar.bin                  \
@@ -101,6 +102,9 @@ $(OUTPUT)test-bionic.bin:
 $(OUTPUT)test-libelf.bin:
        $(BUILD) -lelf
 
+$(OUTPUT)test-get_current_dir_name.bin:
+       $(BUILD)
+
 $(OUTPUT)test-glibc.bin:
        $(BUILD)
 
index 8dc20a6..56722bf 100644 (file)
 # include "test-libelf-mmap.c"
 #undef main
 
+#define main main_test_get_current_dir_name
+# include "test-get_current_dir_name.c"
+#undef main
+
 #define main main_test_glibc
 # include "test-glibc.c"
 #undef main
@@ -174,6 +178,7 @@ int main(int argc, char *argv[])
        main_test_hello();
        main_test_libelf();
        main_test_libelf_mmap();
+       main_test_get_current_dir_name();
        main_test_glibc();
        main_test_dwarf();
        main_test_dwarf_getlocations();
diff --git a/tools/build/feature/test-get_current_dir_name.c b/tools/build/feature/test-get_current_dir_name.c
new file mode 100644 (file)
index 0000000..573000f
--- /dev/null
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdlib.h>
+
+int main(void)
+{
+       free(get_current_dir_name());
+       return 0;
+}
index 0406517..cdc9f4c 100644 (file)
@@ -79,6 +79,8 @@
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 #define TIOCGPTPEER    _IO('T', 0x41) /* Safely open the slave */
+#define TIOCGISO7816   _IOR('T', 0x42, struct serial_iso7816)
+#define TIOCSISO7816   _IOWR('T', 0x43, struct serial_iso7816)
 
 #define FIONCLEX       0x5450
 #define FIOCLEX                0x5451
index 7f5634c..a4446f4 100644 (file)
@@ -529,6 +529,28 @@ typedef struct drm_i915_irq_wait {
  */
 #define I915_PARAM_CS_TIMESTAMP_FREQUENCY 51
 
+/*
+ * Once upon a time we supposed that writes through the GGTT would be
+ * immediately in physical memory (once flushed out of the CPU path). However,
+ * on a few different processors and chipsets, this is not necessarily the case
+ * as the writes appear to be buffered internally. Thus a read of the backing
+ * storage (physical memory) via a different path (with different physical tags
+ * to the indirect write via the GGTT) will see stale values from before
+ * the GGTT write. Inside the kernel, we can for the most part keep track of
+ * the different read/write domains in use (e.g. set-domain), but the assumption
+ * of coherency is baked into the ABI, hence reporting its true state in this
+ * parameter.
+ *
+ * Reports true when writes via mmap_gtt are immediately visible following an
+ * lfence to flush the WCB.
+ *
+ * Reports false when writes via mmap_gtt are indeterminately delayed in an in
+ * internal buffer and are _not_ immediately visible to third parties accessing
+ * directly via mmap_cpu/mmap_wc. Use of mmap_gtt as part of an IPC
+ * communications channel when reporting false is strongly disadvised.
+ */
+#define I915_PARAM_MMAP_GTT_COHERENT   52
+
 typedef struct drm_i915_getparam {
        __s32 param;
        /*
index 852dc17..91c4388 100644 (file)
@@ -133,6 +133,14 @@ enum bpf_map_type {
        BPF_MAP_TYPE_STACK,
 };
 
+/* Note that tracing related programs such as
+ * BPF_PROG_TYPE_{KPROBE,TRACEPOINT,PERF_EVENT,RAW_TRACEPOINT}
+ * are not subject to a stable API since kernel internal data
+ * structures can change from release to release and may
+ * therefore break existing tracing BPF programs. Tracing BPF
+ * programs correspond to /a/ specific kernel which is to be
+ * analyzed, and not /a/ specific kernel /and/ all future ones.
+ */
 enum bpf_prog_type {
        BPF_PROG_TYPE_UNSPEC,
        BPF_PROG_TYPE_SOCKET_FILTER,
@@ -232,6 +240,20 @@ enum bpf_attach_type {
  */
 #define BPF_F_STRICT_ALIGNMENT (1U << 0)
 
+/* If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the
+ * verifier will allow any alignment whatsoever.  On platforms
+ * with strict alignment requirements for loads ands stores (such
+ * as sparc and mips) the verifier validates that all loads and
+ * stores provably follow this requirement.  This flag turns that
+ * checking and enforcement off.
+ *
+ * It is mostly used for testing when we want to validate the
+ * context and memory access aspects of the verifier, but because
+ * of an unaligned access the alignment check would trigger before
+ * the one we are interested in.
+ */
+#define BPF_F_ANY_ALIGNMENT    (1U << 1)
+
 /* when bpf_ldimm64->src_reg == BPF_PSEUDO_MAP_FD, bpf_ldimm64->imm == fd */
 #define BPF_PSEUDO_MAP_FD      1
 
@@ -257,9 +279,6 @@ enum bpf_attach_type {
 /* Specify numa node during map creation */
 #define BPF_F_NUMA_NODE                (1U << 2)
 
-/* flags for BPF_PROG_QUERY */
-#define BPF_F_QUERY_EFFECTIVE  (1U << 0)
-
 #define BPF_OBJ_NAME_LEN 16U
 
 /* Flags for accessing BPF object */
@@ -269,6 +288,12 @@ enum bpf_attach_type {
 /* Flag for stack_map, store build_id+offset instead of pointer */
 #define BPF_F_STACK_BUILD_ID   (1U << 5)
 
+/* Zero-initialize hash function seed. This should only be used for testing. */
+#define BPF_F_ZERO_SEED                (1U << 6)
+
+/* flags for BPF_PROG_QUERY */
+#define BPF_F_QUERY_EFFECTIVE  (1U << 0)
+
 enum bpf_stack_build_id_status {
        /* user space need an empty entry to identify end of a trace */
        BPF_STACK_BUILD_ID_EMPTY = 0,
@@ -326,7 +351,7 @@ union bpf_attr {
                __u32           log_level;      /* verbosity level of verifier */
                __u32           log_size;       /* size of user buffer */
                __aligned_u64   log_buf;        /* user supplied buffer */
-               __u32           kern_version;   /* checked when prog_type=kprobe */
+               __u32           kern_version;   /* not used */
                __u32           prog_flags;
                char            prog_name[BPF_OBJ_NAME_LEN];
                __u32           prog_ifindex;   /* ifindex of netdev to prep for */
@@ -335,6 +360,13 @@ union bpf_attr {
                 * (context accesses, allowed helpers, etc).
                 */
                __u32           expected_attach_type;
+               __u32           prog_btf_fd;    /* fd pointing to BTF type data */
+               __u32           func_info_rec_size;     /* userspace bpf_func_info size */
+               __aligned_u64   func_info;      /* func info */
+               __u32           func_info_cnt;  /* number of bpf_func_info records */
+               __u32           line_info_rec_size;     /* userspace bpf_line_info size */
+               __aligned_u64   line_info;      /* line info */
+               __u32           line_info_cnt;  /* number of bpf_line_info records */
        };
 
        struct { /* anonymous struct used by BPF_OBJ_* commands */
@@ -353,8 +385,11 @@ union bpf_attr {
        struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */
                __u32           prog_fd;
                __u32           retval;
-               __u32           data_size_in;
-               __u32           data_size_out;
+               __u32           data_size_in;   /* input: len of data_in */
+               __u32           data_size_out;  /* input/output: len of data_out
+                                                *   returns ENOSPC if data_out
+                                                *   is too small.
+                                                */
                __aligned_u64   data_in;
                __aligned_u64   data_out;
                __u32           repeat;
@@ -475,18 +510,6 @@ union bpf_attr {
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
- * int bpf_map_pop_elem(struct bpf_map *map, void *value)
- *     Description
- *             Pop an element from *map*.
- * Return
- *             0 on success, or a negative error in case of failure.
- *
- * int bpf_map_peek_elem(struct bpf_map *map, void *value)
- *     Description
- *             Get an element from *map* without removing it.
- * Return
- *             0 on success, or a negative error in case of failure.
- *
  * int bpf_probe_read(void *dst, u32 size, const void *src)
  *     Description
  *             For tracing programs, safely attempt to read *size* bytes from
@@ -1910,9 +1933,9 @@ union bpf_attr {
  *             is set to metric from route (IPv4/IPv6 only), and ifindex
  *             is set to the device index of the nexthop from the FIB lookup.
  *
- *             *plen* argument is the size of the passed in struct.
- *             *flags* argument can be a combination of one or more of the
- *             following values:
+ *             *plen* argument is the size of the passed in struct.
+ *             *flags* argument can be a combination of one or more of the
+ *             following values:
  *
  *             **BPF_FIB_LOOKUP_DIRECT**
  *                     Do a direct table lookup vs full lookup using FIB
@@ -1921,9 +1944,9 @@ union bpf_attr {
  *                     Perform lookup from an egress perspective (default is
  *                     ingress).
  *
- *             *ctx* is either **struct xdp_md** for XDP programs or
- *             **struct sk_buff** tc cls_act programs.
- *     Return
+ *             *ctx* is either **struct xdp_md** for XDP programs or
+ *             **struct sk_buff** tc cls_act programs.
+ *     Return
  *             * < 0 if any input argument is invalid
  *             *   0 on success (packet is forwarded, nexthop neighbor exists)
  *             * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
@@ -2068,8 +2091,8 @@ union bpf_attr {
  *             translated to a keycode using the rc keymap, and reported as
  *             an input key down event. After a period a key up event is
  *             generated. This period can be extended by calling either
- *             **bpf_rc_keydown** () again with the same values, or calling
- *             **bpf_rc_repeat** ().
+ *             **bpf_rc_keydown**\ () again with the same values, or calling
+ *             **bpf_rc_repeat**\ ().
  *
  *             Some protocols include a toggle bit, in case the button was
  *             released and pressed again between consecutive scancodes.
@@ -2152,29 +2175,30 @@ union bpf_attr {
  *             The *flags* meaning is specific for each map type,
  *             and has to be 0 for cgroup local storage.
  *
- *             Depending on the bpf program type, a local storage area
- *             can be shared between multiple instances of the bpf program,
+ *             Depending on the BPF program type, a local storage area
+ *             can be shared between multiple instances of the BPF program,
  *             running simultaneously.
  *
  *             A user should care about the synchronization by himself.
- *             For example, by using the BPF_STX_XADD instruction to alter
+ *             For example, by using the **BPF_STX_XADD** instruction to alter
  *             the shared data.
  *     Return
- *             Pointer to the local storage area.
+ *             A pointer to the local storage area.
  *
  * int bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags)
  *     Description
- *             Select a SO_REUSEPORT sk from a BPF_MAP_TYPE_REUSEPORT_ARRAY map
- *             It checks the selected sk is matching the incoming
- *             request in the skb.
+ *             Select a **SO_REUSEPORT** socket from a
+ *             **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*.
+ *             It checks the selected socket is matching the incoming
+ *             request in the socket buffer.
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
- * struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u32 netns, u64 flags)
+ * struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
  *     Description
  *             Look for TCP socket matching *tuple*, optionally in a child
  *             network namespace *netns*. The return value must be checked,
- *             and if non-NULL, released via **bpf_sk_release**\ ().
+ *             and if non-**NULL**, released via **bpf_sk_release**\ ().
  *
  *             The *ctx* should point to the context of the program, such as
  *             the skb or socket (depending on the hook in use). This is used
@@ -2187,12 +2211,14 @@ union bpf_attr {
  *             **sizeof**\ (*tuple*\ **->ipv6**)
  *                     Look for an IPv6 socket.
  *
- *             If the *netns* is zero, then the socket lookup table in the
- *             netns associated with the *ctx* will be used. For the TC hooks,
- *             this in the netns of the device in the skb. For socket hooks,
- *             this in the netns of the socket. If *netns* is non-zero, then
- *             it specifies the ID of the netns relative to the netns
- *             associated with the *ctx*.
+ *             If the *netns* is a negative signed 32-bit integer, then the
+ *             socket lookup table in the netns associated with the *ctx* will
+ *             will be used. For the TC hooks, this is the netns of the device
+ *             in the skb. For socket hooks, this is the netns of the socket.
+ *             If *netns* is any other signed 32-bit value greater than or
+ *             equal to zero then it specifies the ID of the netns relative to
+ *             the netns associated with the *ctx*. *netns* values beyond the
+ *             range of 32-bit integers are reserved for future use.
  *
  *             All values for *flags* are reserved for future usage, and must
  *             be left at zero.
@@ -2200,13 +2226,15 @@ union bpf_attr {
  *             This helper is available only if the kernel was compiled with
  *             **CONFIG_NET** configuration option.
  *     Return
- *             Pointer to *struct bpf_sock*, or NULL in case of failure.
+ *             Pointer to **struct bpf_sock**, or **NULL** in case of failure.
+ *             For sockets with reuseport option, the **struct bpf_sock**
+ *             result is from **reuse->socks**\ [] using the hash of the tuple.
  *
- * struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u32 netns, u64 flags)
+ * struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
  *     Description
  *             Look for UDP socket matching *tuple*, optionally in a child
  *             network namespace *netns*. The return value must be checked,
- *             and if non-NULL, released via **bpf_sk_release**\ ().
+ *             and if non-**NULL**, released via **bpf_sk_release**\ ().
  *
  *             The *ctx* should point to the context of the program, such as
  *             the skb or socket (depending on the hook in use). This is used
@@ -2219,12 +2247,14 @@ union bpf_attr {
  *             **sizeof**\ (*tuple*\ **->ipv6**)
  *                     Look for an IPv6 socket.
  *
- *             If the *netns* is zero, then the socket lookup table in the
- *             netns associated with the *ctx* will be used. For the TC hooks,
- *             this in the netns of the device in the skb. For socket hooks,
- *             this in the netns of the socket. If *netns* is non-zero, then
- *             it specifies the ID of the netns relative to the netns
- *             associated with the *ctx*.
+ *             If the *netns* is a negative signed 32-bit integer, then the
+ *             socket lookup table in the netns associated with the *ctx* will
+ *             will be used. For the TC hooks, this is the netns of the device
+ *             in the skb. For socket hooks, this is the netns of the socket.
+ *             If *netns* is any other signed 32-bit value greater than or
+ *             equal to zero then it specifies the ID of the netns relative to
+ *             the netns associated with the *ctx*. *netns* values beyond the
+ *             range of 32-bit integers are reserved for future use.
  *
  *             All values for *flags* are reserved for future usage, and must
  *             be left at zero.
@@ -2232,31 +2262,71 @@ union bpf_attr {
  *             This helper is available only if the kernel was compiled with
  *             **CONFIG_NET** configuration option.
  *     Return
- *             Pointer to *struct bpf_sock*, or NULL in case of failure.
+ *             Pointer to **struct bpf_sock**, or **NULL** in case of failure.
+ *             For sockets with reuseport option, the **struct bpf_sock**
+ *             result is from **reuse->socks**\ [] using the hash of the tuple.
  *
- * int bpf_sk_release(struct bpf_sock *sk)
+ * int bpf_sk_release(struct bpf_sock *sock)
  *     Description
- *             Release the reference held by *sock*. *sock* must be a non-NULL
- *             pointer that was returned from bpf_sk_lookup_xxx\ ().
+ *             Release the reference held by *sock*. *sock* must be a
+ *             non-**NULL** pointer that was returned from
+ *             **bpf_sk_lookup_xxx**\ ().
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
+ * int bpf_map_pop_elem(struct bpf_map *map, void *value)
+ *     Description
+ *             Pop an element from *map*.
+ *     Return
+ *             0 on success, or a negative error in case of failure.
+ *
+ * int bpf_map_peek_elem(struct bpf_map *map, void *value)
+ *     Description
+ *             Get an element from *map* without removing it.
+ *     Return
+ *             0 on success, or a negative error in case of failure.
+ *
  * int bpf_msg_push_data(struct sk_buff *skb, u32 start, u32 len, u64 flags)
  *     Description
- *             For socket policies, insert *len* bytes into msg at offset
+ *             For socket policies, insert *len* bytes into *msg* at offset
  *             *start*.
  *
  *             If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a
- *             *msg* it may want to insert metadata or options into the msg.
+ *             *msg* it may want to insert metadata or options into the *msg*.
  *             This can later be read and used by any of the lower layer BPF
  *             hooks.
  *
  *             This helper may fail if under memory pressure (a malloc
  *             fails) in these cases BPF programs will get an appropriate
  *             error and BPF programs will need to handle them.
+ *     Return
+ *             0 on success, or a negative error in case of failure.
  *
+ * int bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 pop, u64 flags)
+ *     Description
+ *             Will remove *pop* bytes from a *msg* starting at byte *start*.
+ *             This may result in **ENOMEM** errors under certain situations if
+ *             an allocation and copy are required due to a full ring buffer.
+ *             However, the helper will try to avoid doing the allocation
+ *             if possible. Other errors can occur if input parameters are
+ *             invalid either due to *start* byte not being valid part of *msg*
+ *             payload and/or *pop* value being to large.
  *     Return
  *             0 on success, or a negative error in case of failure.
+ *
+ * int bpf_rc_pointer_rel(void *ctx, s32 rel_x, s32 rel_y)
+ *     Description
+ *             This helper is used in programs implementing IR decoding, to
+ *             report a successfully decoded pointer movement.
+ *
+ *             The *ctx* should point to the lirc sample as passed into
+ *             the program.
+ *
+ *             This helper is only available is the kernel was compiled with
+ *             the **CONFIG_BPF_LIRC_MODE2** configuration option set to
+ *             "**y**".
+ *     Return
+ *             0
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -2349,7 +2419,9 @@ union bpf_attr {
        FN(map_push_elem),              \
        FN(map_pop_elem),               \
        FN(map_peek_elem),              \
-       FN(msg_push_data),
+       FN(msg_push_data),              \
+       FN(msg_pop_data),               \
+       FN(rc_pointer_rel),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -2405,6 +2477,9 @@ enum bpf_func_id {
 /* BPF_FUNC_perf_event_output for sk_buff input context. */
 #define BPF_F_CTXLEN_MASK              (0xfffffULL << 32)
 
+/* Current network namespace */
+#define BPF_F_CURRENT_NETNS            (-1L)
+
 /* Mode for BPF_FUNC_skb_adjust_room helper. */
 enum bpf_adj_room_mode {
        BPF_ADJ_ROOM_NET,
@@ -2422,6 +2497,12 @@ enum bpf_lwt_encap_mode {
        BPF_LWT_ENCAP_SEG6_INLINE
 };
 
+#define __bpf_md_ptr(type, name)       \
+union {                                        \
+       type name;                      \
+       __u64 :64;                      \
+} __attribute__((aligned(8)))
+
 /* user accessible mirror of in-kernel sk_buff.
  * new fields can only be added to the end of this structure
  */
@@ -2456,7 +2537,9 @@ struct __sk_buff {
        /* ... here. */
 
        __u32 data_meta;
-       struct bpf_flow_keys *flow_keys;
+       __bpf_md_ptr(struct bpf_flow_keys *, flow_keys);
+       __u64 tstamp;
+       __u32 wire_len;
 };
 
 struct bpf_tunnel_key {
@@ -2572,8 +2655,8 @@ enum sk_action {
  * be added to the end of this structure
  */
 struct sk_msg_md {
-       void *data;
-       void *data_end;
+       __bpf_md_ptr(void *, data);
+       __bpf_md_ptr(void *, data_end);
 
        __u32 family;
        __u32 remote_ip4;       /* Stored in network byte order */
@@ -2582,6 +2665,7 @@ struct sk_msg_md {
        __u32 local_ip6[4];     /* Stored in network byte order */
        __u32 remote_port;      /* Stored in network byte order */
        __u32 local_port;       /* stored in host byte order */
+       __u32 size;             /* Total size of sk_msg */
 };
 
 struct sk_reuseport_md {
@@ -2589,8 +2673,9 @@ struct sk_reuseport_md {
         * Start of directly accessible data. It begins from
         * the tcp/udp header.
         */
-       void *data;
-       void *data_end;         /* End of directly accessible data */
+       __bpf_md_ptr(void *, data);
+       /* End of directly accessible data */
+       __bpf_md_ptr(void *, data_end);
        /*
         * Total length of packet (starting from the tcp/udp header).
         * Note that the directly accessible bytes (data_end - data)
@@ -2631,6 +2716,18 @@ struct bpf_prog_info {
        __u32 nr_jited_func_lens;
        __aligned_u64 jited_ksyms;
        __aligned_u64 jited_func_lens;
+       __u32 btf_id;
+       __u32 func_info_rec_size;
+       __aligned_u64 func_info;
+       __u32 nr_func_info;
+       __u32 nr_line_info;
+       __aligned_u64 line_info;
+       __aligned_u64 jited_line_info;
+       __u32 nr_jited_line_info;
+       __u32 line_info_rec_size;
+       __u32 jited_line_info_rec_size;
+       __u32 nr_prog_tags;
+       __aligned_u64 prog_tags;
 } __attribute__((aligned(8)));
 
 struct bpf_map_info {
@@ -2942,4 +3039,19 @@ struct bpf_flow_keys {
        };
 };
 
+struct bpf_func_info {
+       __u32   insn_off;
+       __u32   type_id;
+};
+
+#define BPF_LINE_INFO_LINE_NUM(line_col)       ((line_col) >> 10)
+#define BPF_LINE_INFO_LINE_COL(line_col)       ((line_col) & 0x3ff)
+
+struct bpf_line_info {
+       __u32   insn_off;
+       __u32   file_name_off;
+       __u32   line_off;
+       __u32   line_col;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
index 972265f..7b7475e 100644 (file)
@@ -34,13 +34,16 @@ struct btf_type {
         * bits  0-15: vlen (e.g. # of struct's members)
         * bits 16-23: unused
         * bits 24-27: kind (e.g. int, ptr, array...etc)
-        * bits 28-31: unused
+        * bits 28-30: unused
+        * bit     31: kind_flag, currently used by
+        *             struct, union and fwd
         */
        __u32 info;
        /* "size" is used by INT, ENUM, STRUCT and UNION.
         * "size" tells the size of the type it is describing.
         *
-        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
+        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
+        * FUNC and FUNC_PROTO.
         * "type" is a type_id referring to another type.
         */
        union {
@@ -51,6 +54,7 @@ struct btf_type {
 
 #define BTF_INFO_KIND(info)    (((info) >> 24) & 0x0f)
 #define BTF_INFO_VLEN(info)    ((info) & 0xffff)
+#define BTF_INFO_KFLAG(info)   ((info) >> 31)
 
 #define BTF_KIND_UNKN          0       /* Unknown      */
 #define BTF_KIND_INT           1       /* Integer      */
@@ -64,8 +68,10 @@ struct btf_type {
 #define BTF_KIND_VOLATILE      9       /* Volatile     */
 #define BTF_KIND_CONST         10      /* Const        */
 #define BTF_KIND_RESTRICT      11      /* Restrict     */
-#define BTF_KIND_MAX           11
-#define NR_BTF_KINDS           12
+#define BTF_KIND_FUNC          12      /* Function     */
+#define BTF_KIND_FUNC_PROTO    13      /* Function Proto       */
+#define BTF_KIND_MAX           13
+#define NR_BTF_KINDS           14
 
 /* For some specific BTF_KIND, "struct btf_type" is immediately
  * followed by extra data.
@@ -107,7 +113,29 @@ struct btf_array {
 struct btf_member {
        __u32   name_off;
        __u32   type;
-       __u32   offset; /* offset in bits */
+       /* If the type info kind_flag is set, the btf_member offset
+        * contains both member bitfield size and bit offset. The
+        * bitfield size is set for bitfield members. If the type
+        * info kind_flag is not set, the offset contains only bit
+        * offset.
+        */
+       __u32   offset;
+};
+
+/* If the struct/union type info kind_flag is set, the
+ * following two macros are used to access bitfield_size
+ * and bit_offset from btf_member.offset.
+ */
+#define BTF_MEMBER_BITFIELD_SIZE(val)  ((val) >> 24)
+#define BTF_MEMBER_BIT_OFFSET(val)     ((val) & 0xffffff)
+
+/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
+ * The exact number of btf_param is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_param {
+       __u32   name_off;
+       __u32   type;
 };
 
 #endif /* _UAPI__LINUX_BTF_H__ */
index 486ed1f..0a4d733 100644 (file)
@@ -155,7 +155,7 @@ enum nlmsgerr_attrs {
 #define NETLINK_LIST_MEMBERSHIPS       9
 #define NETLINK_CAP_ACK                        10
 #define NETLINK_EXT_ACK                        11
-#define NETLINK_DUMP_STRICT_CHK                12
+#define NETLINK_GET_STRICT_CHK         12
 
 struct nl_pktinfo {
        __u32   group;
diff --git a/tools/include/uapi/linux/pkt_cls.h b/tools/include/uapi/linux/pkt_cls.h
new file mode 100644 (file)
index 0000000..401d0c1
--- /dev/null
@@ -0,0 +1,612 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_PKT_CLS_H
+#define __LINUX_PKT_CLS_H
+
+#include <linux/types.h>
+#include <linux/pkt_sched.h>
+
+#define TC_COOKIE_MAX_SIZE 16
+
+/* Action attributes */
+enum {
+       TCA_ACT_UNSPEC,
+       TCA_ACT_KIND,
+       TCA_ACT_OPTIONS,
+       TCA_ACT_INDEX,
+       TCA_ACT_STATS,
+       TCA_ACT_PAD,
+       TCA_ACT_COOKIE,
+       __TCA_ACT_MAX
+};
+
+#define TCA_ACT_MAX __TCA_ACT_MAX
+#define TCA_OLD_COMPAT (TCA_ACT_MAX+1)
+#define TCA_ACT_MAX_PRIO 32
+#define TCA_ACT_BIND   1
+#define TCA_ACT_NOBIND 0
+#define TCA_ACT_UNBIND 1
+#define TCA_ACT_NOUNBIND       0
+#define TCA_ACT_REPLACE                1
+#define TCA_ACT_NOREPLACE      0
+
+#define TC_ACT_UNSPEC  (-1)
+#define TC_ACT_OK              0
+#define TC_ACT_RECLASSIFY      1
+#define TC_ACT_SHOT            2
+#define TC_ACT_PIPE            3
+#define TC_ACT_STOLEN          4
+#define TC_ACT_QUEUED          5
+#define TC_ACT_REPEAT          6
+#define TC_ACT_REDIRECT                7
+#define TC_ACT_TRAP            8 /* For hw path, this means "trap to cpu"
+                                  * and don't further process the frame
+                                  * in hardware. For sw path, this is
+                                  * equivalent of TC_ACT_STOLEN - drop
+                                  * the skb and act like everything
+                                  * is alright.
+                                  */
+#define TC_ACT_VALUE_MAX       TC_ACT_TRAP
+
+/* There is a special kind of actions called "extended actions",
+ * which need a value parameter. These have a local opcode located in
+ * the highest nibble, starting from 1. The rest of the bits
+ * are used to carry the value. These two parts together make
+ * a combined opcode.
+ */
+#define __TC_ACT_EXT_SHIFT 28
+#define __TC_ACT_EXT(local) ((local) << __TC_ACT_EXT_SHIFT)
+#define TC_ACT_EXT_VAL_MASK ((1 << __TC_ACT_EXT_SHIFT) - 1)
+#define TC_ACT_EXT_OPCODE(combined) ((combined) & (~TC_ACT_EXT_VAL_MASK))
+#define TC_ACT_EXT_CMP(combined, opcode) (TC_ACT_EXT_OPCODE(combined) == opcode)
+
+#define TC_ACT_JUMP __TC_ACT_EXT(1)
+#define TC_ACT_GOTO_CHAIN __TC_ACT_EXT(2)
+#define TC_ACT_EXT_OPCODE_MAX  TC_ACT_GOTO_CHAIN
+
+/* Action type identifiers*/
+enum {
+       TCA_ID_UNSPEC=0,
+       TCA_ID_POLICE=1,
+       /* other actions go here */
+       __TCA_ID_MAX=255
+};
+
+#define TCA_ID_MAX __TCA_ID_MAX
+
+struct tc_police {
+       __u32                   index;
+       int                     action;
+#define TC_POLICE_UNSPEC       TC_ACT_UNSPEC
+#define TC_POLICE_OK           TC_ACT_OK
+#define TC_POLICE_RECLASSIFY   TC_ACT_RECLASSIFY
+#define TC_POLICE_SHOT         TC_ACT_SHOT
+#define TC_POLICE_PIPE         TC_ACT_PIPE
+
+       __u32                   limit;
+       __u32                   burst;
+       __u32                   mtu;
+       struct tc_ratespec      rate;
+       struct tc_ratespec      peakrate;
+       int                     refcnt;
+       int                     bindcnt;
+       __u32                   capab;
+};
+
+struct tcf_t {
+       __u64   install;
+       __u64   lastuse;
+       __u64   expires;
+       __u64   firstuse;
+};
+
+struct tc_cnt {
+       int                   refcnt;
+       int                   bindcnt;
+};
+
+#define tc_gen \
+       __u32                 index; \
+       __u32                 capab; \
+       int                   action; \
+       int                   refcnt; \
+       int                   bindcnt
+
+enum {
+       TCA_POLICE_UNSPEC,
+       TCA_POLICE_TBF,
+       TCA_POLICE_RATE,
+       TCA_POLICE_PEAKRATE,
+       TCA_POLICE_AVRATE,
+       TCA_POLICE_RESULT,
+       TCA_POLICE_TM,
+       TCA_POLICE_PAD,
+       __TCA_POLICE_MAX
+#define TCA_POLICE_RESULT TCA_POLICE_RESULT
+};
+
+#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1)
+
+/* tca flags definitions */
+#define TCA_CLS_FLAGS_SKIP_HW  (1 << 0) /* don't offload filter to HW */
+#define TCA_CLS_FLAGS_SKIP_SW  (1 << 1) /* don't use filter in SW */
+#define TCA_CLS_FLAGS_IN_HW    (1 << 2) /* filter is offloaded to HW */
+#define TCA_CLS_FLAGS_NOT_IN_HW (1 << 3) /* filter isn't offloaded to HW */
+#define TCA_CLS_FLAGS_VERBOSE  (1 << 4) /* verbose logging */
+
+/* U32 filters */
+
+#define TC_U32_HTID(h) ((h)&0xFFF00000)
+#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20)
+#define TC_U32_HASH(h) (((h)>>12)&0xFF)
+#define TC_U32_NODE(h) ((h)&0xFFF)
+#define TC_U32_KEY(h) ((h)&0xFFFFF)
+#define TC_U32_UNSPEC  0
+#define TC_U32_ROOT    (0xFFF00000)
+
+enum {
+       TCA_U32_UNSPEC,
+       TCA_U32_CLASSID,
+       TCA_U32_HASH,
+       TCA_U32_LINK,
+       TCA_U32_DIVISOR,
+       TCA_U32_SEL,
+       TCA_U32_POLICE,
+       TCA_U32_ACT,
+       TCA_U32_INDEV,
+       TCA_U32_PCNT,
+       TCA_U32_MARK,
+       TCA_U32_FLAGS,
+       TCA_U32_PAD,
+       __TCA_U32_MAX
+};
+
+#define TCA_U32_MAX (__TCA_U32_MAX - 1)
+
+struct tc_u32_key {
+       __be32          mask;
+       __be32          val;
+       int             off;
+       int             offmask;
+};
+
+struct tc_u32_sel {
+       unsigned char           flags;
+       unsigned char           offshift;
+       unsigned char           nkeys;
+
+       __be16                  offmask;
+       __u16                   off;
+       short                   offoff;
+
+       short                   hoff;
+       __be32                  hmask;
+       struct tc_u32_key       keys[0];
+};
+
+struct tc_u32_mark {
+       __u32           val;
+       __u32           mask;
+       __u32           success;
+};
+
+struct tc_u32_pcnt {
+       __u64 rcnt;
+       __u64 rhit;
+       __u64 kcnts[0];
+};
+
+/* Flags */
+
+#define TC_U32_TERMINAL                1
+#define TC_U32_OFFSET          2
+#define TC_U32_VAROFFSET       4
+#define TC_U32_EAT             8
+
+#define TC_U32_MAXDEPTH 8
+
+
+/* RSVP filter */
+
+enum {
+       TCA_RSVP_UNSPEC,
+       TCA_RSVP_CLASSID,
+       TCA_RSVP_DST,
+       TCA_RSVP_SRC,
+       TCA_RSVP_PINFO,
+       TCA_RSVP_POLICE,
+       TCA_RSVP_ACT,
+       __TCA_RSVP_MAX
+};
+
+#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 )
+
+struct tc_rsvp_gpi {
+       __u32   key;
+       __u32   mask;
+       int     offset;
+};
+
+struct tc_rsvp_pinfo {
+       struct tc_rsvp_gpi dpi;
+       struct tc_rsvp_gpi spi;
+       __u8    protocol;
+       __u8    tunnelid;
+       __u8    tunnelhdr;
+       __u8    pad;
+};
+
+/* ROUTE filter */
+
+enum {
+       TCA_ROUTE4_UNSPEC,
+       TCA_ROUTE4_CLASSID,
+       TCA_ROUTE4_TO,
+       TCA_ROUTE4_FROM,
+       TCA_ROUTE4_IIF,
+       TCA_ROUTE4_POLICE,
+       TCA_ROUTE4_ACT,
+       __TCA_ROUTE4_MAX
+};
+
+#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1)
+
+
+/* FW filter */
+
+enum {
+       TCA_FW_UNSPEC,
+       TCA_FW_CLASSID,
+       TCA_FW_POLICE,
+       TCA_FW_INDEV, /*  used by CONFIG_NET_CLS_IND */
+       TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */
+       TCA_FW_MASK,
+       __TCA_FW_MAX
+};
+
+#define TCA_FW_MAX (__TCA_FW_MAX - 1)
+
+/* TC index filter */
+
+enum {
+       TCA_TCINDEX_UNSPEC,
+       TCA_TCINDEX_HASH,
+       TCA_TCINDEX_MASK,
+       TCA_TCINDEX_SHIFT,
+       TCA_TCINDEX_FALL_THROUGH,
+       TCA_TCINDEX_CLASSID,
+       TCA_TCINDEX_POLICE,
+       TCA_TCINDEX_ACT,
+       __TCA_TCINDEX_MAX
+};
+
+#define TCA_TCINDEX_MAX     (__TCA_TCINDEX_MAX - 1)
+
+/* Flow filter */
+
+enum {
+       FLOW_KEY_SRC,
+       FLOW_KEY_DST,
+       FLOW_KEY_PROTO,
+       FLOW_KEY_PROTO_SRC,
+       FLOW_KEY_PROTO_DST,
+       FLOW_KEY_IIF,
+       FLOW_KEY_PRIORITY,
+       FLOW_KEY_MARK,
+       FLOW_KEY_NFCT,
+       FLOW_KEY_NFCT_SRC,
+       FLOW_KEY_NFCT_DST,
+       FLOW_KEY_NFCT_PROTO_SRC,
+       FLOW_KEY_NFCT_PROTO_DST,
+       FLOW_KEY_RTCLASSID,
+       FLOW_KEY_SKUID,
+       FLOW_KEY_SKGID,
+       FLOW_KEY_VLAN_TAG,
+       FLOW_KEY_RXHASH,
+       __FLOW_KEY_MAX,
+};
+
+#define FLOW_KEY_MAX   (__FLOW_KEY_MAX - 1)
+
+enum {
+       FLOW_MODE_MAP,
+       FLOW_MODE_HASH,
+};
+
+enum {
+       TCA_FLOW_UNSPEC,
+       TCA_FLOW_KEYS,
+       TCA_FLOW_MODE,
+       TCA_FLOW_BASECLASS,
+       TCA_FLOW_RSHIFT,
+       TCA_FLOW_ADDEND,
+       TCA_FLOW_MASK,
+       TCA_FLOW_XOR,
+       TCA_FLOW_DIVISOR,
+       TCA_FLOW_ACT,
+       TCA_FLOW_POLICE,
+       TCA_FLOW_EMATCHES,
+       TCA_FLOW_PERTURB,
+       __TCA_FLOW_MAX
+};
+
+#define TCA_FLOW_MAX   (__TCA_FLOW_MAX - 1)
+
+/* Basic filter */
+
+enum {
+       TCA_BASIC_UNSPEC,
+       TCA_BASIC_CLASSID,
+       TCA_BASIC_EMATCHES,
+       TCA_BASIC_ACT,
+       TCA_BASIC_POLICE,
+       __TCA_BASIC_MAX
+};
+
+#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1)
+
+
+/* Cgroup classifier */
+
+enum {
+       TCA_CGROUP_UNSPEC,
+       TCA_CGROUP_ACT,
+       TCA_CGROUP_POLICE,
+       TCA_CGROUP_EMATCHES,
+       __TCA_CGROUP_MAX,
+};
+
+#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1)
+
+/* BPF classifier */
+
+#define TCA_BPF_FLAG_ACT_DIRECT                (1 << 0)
+
+enum {
+       TCA_BPF_UNSPEC,
+       TCA_BPF_ACT,
+       TCA_BPF_POLICE,
+       TCA_BPF_CLASSID,
+       TCA_BPF_OPS_LEN,
+       TCA_BPF_OPS,
+       TCA_BPF_FD,
+       TCA_BPF_NAME,
+       TCA_BPF_FLAGS,
+       TCA_BPF_FLAGS_GEN,
+       TCA_BPF_TAG,
+       TCA_BPF_ID,
+       __TCA_BPF_MAX,
+};
+
+#define TCA_BPF_MAX (__TCA_BPF_MAX - 1)
+
+/* Flower classifier */
+
+enum {
+       TCA_FLOWER_UNSPEC,
+       TCA_FLOWER_CLASSID,
+       TCA_FLOWER_INDEV,
+       TCA_FLOWER_ACT,
+       TCA_FLOWER_KEY_ETH_DST,         /* ETH_ALEN */
+       TCA_FLOWER_KEY_ETH_DST_MASK,    /* ETH_ALEN */
+       TCA_FLOWER_KEY_ETH_SRC,         /* ETH_ALEN */
+       TCA_FLOWER_KEY_ETH_SRC_MASK,    /* ETH_ALEN */
+       TCA_FLOWER_KEY_ETH_TYPE,        /* be16 */
+       TCA_FLOWER_KEY_IP_PROTO,        /* u8 */
+       TCA_FLOWER_KEY_IPV4_SRC,        /* be32 */
+       TCA_FLOWER_KEY_IPV4_SRC_MASK,   /* be32 */
+       TCA_FLOWER_KEY_IPV4_DST,        /* be32 */
+       TCA_FLOWER_KEY_IPV4_DST_MASK,   /* be32 */
+       TCA_FLOWER_KEY_IPV6_SRC,        /* struct in6_addr */
+       TCA_FLOWER_KEY_IPV6_SRC_MASK,   /* struct in6_addr */
+       TCA_FLOWER_KEY_IPV6_DST,        /* struct in6_addr */
+       TCA_FLOWER_KEY_IPV6_DST_MASK,   /* struct in6_addr */
+       TCA_FLOWER_KEY_TCP_SRC,         /* be16 */
+       TCA_FLOWER_KEY_TCP_DST,         /* be16 */
+       TCA_FLOWER_KEY_UDP_SRC,         /* be16 */
+       TCA_FLOWER_KEY_UDP_DST,         /* be16 */
+
+       TCA_FLOWER_FLAGS,
+       TCA_FLOWER_KEY_VLAN_ID,         /* be16 */
+       TCA_FLOWER_KEY_VLAN_PRIO,       /* u8   */
+       TCA_FLOWER_KEY_VLAN_ETH_TYPE,   /* be16 */
+
+       TCA_FLOWER_KEY_ENC_KEY_ID,      /* be32 */
+       TCA_FLOWER_KEY_ENC_IPV4_SRC,    /* be32 */
+       TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,/* be32 */
+       TCA_FLOWER_KEY_ENC_IPV4_DST,    /* be32 */
+       TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,/* be32 */
+       TCA_FLOWER_KEY_ENC_IPV6_SRC,    /* struct in6_addr */
+       TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,/* struct in6_addr */
+       TCA_FLOWER_KEY_ENC_IPV6_DST,    /* struct in6_addr */
+       TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,/* struct in6_addr */
+
+       TCA_FLOWER_KEY_TCP_SRC_MASK,    /* be16 */
+       TCA_FLOWER_KEY_TCP_DST_MASK,    /* be16 */
+       TCA_FLOWER_KEY_UDP_SRC_MASK,    /* be16 */
+       TCA_FLOWER_KEY_UDP_DST_MASK,    /* be16 */
+       TCA_FLOWER_KEY_SCTP_SRC_MASK,   /* be16 */
+       TCA_FLOWER_KEY_SCTP_DST_MASK,   /* be16 */
+
+       TCA_FLOWER_KEY_SCTP_SRC,        /* be16 */
+       TCA_FLOWER_KEY_SCTP_DST,        /* be16 */
+
+       TCA_FLOWER_KEY_ENC_UDP_SRC_PORT,        /* be16 */
+       TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK,   /* be16 */
+       TCA_FLOWER_KEY_ENC_UDP_DST_PORT,        /* be16 */
+       TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK,   /* be16 */
+
+       TCA_FLOWER_KEY_FLAGS,           /* be32 */
+       TCA_FLOWER_KEY_FLAGS_MASK,      /* be32 */
+
+       TCA_FLOWER_KEY_ICMPV4_CODE,     /* u8 */
+       TCA_FLOWER_KEY_ICMPV4_CODE_MASK,/* u8 */
+       TCA_FLOWER_KEY_ICMPV4_TYPE,     /* u8 */
+       TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,/* u8 */
+       TCA_FLOWER_KEY_ICMPV6_CODE,     /* u8 */
+       TCA_FLOWER_KEY_ICMPV6_CODE_MASK,/* u8 */
+       TCA_FLOWER_KEY_ICMPV6_TYPE,     /* u8 */
+       TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,/* u8 */
+
+       TCA_FLOWER_KEY_ARP_SIP,         /* be32 */
+       TCA_FLOWER_KEY_ARP_SIP_MASK,    /* be32 */
+       TCA_FLOWER_KEY_ARP_TIP,         /* be32 */
+       TCA_FLOWER_KEY_ARP_TIP_MASK,    /* be32 */
+       TCA_FLOWER_KEY_ARP_OP,          /* u8 */
+       TCA_FLOWER_KEY_ARP_OP_MASK,     /* u8 */
+       TCA_FLOWER_KEY_ARP_SHA,         /* ETH_ALEN */
+       TCA_FLOWER_KEY_ARP_SHA_MASK,    /* ETH_ALEN */
+       TCA_FLOWER_KEY_ARP_THA,         /* ETH_ALEN */
+       TCA_FLOWER_KEY_ARP_THA_MASK,    /* ETH_ALEN */
+
+       TCA_FLOWER_KEY_MPLS_TTL,        /* u8 - 8 bits */
+       TCA_FLOWER_KEY_MPLS_BOS,        /* u8 - 1 bit */
+       TCA_FLOWER_KEY_MPLS_TC,         /* u8 - 3 bits */
+       TCA_FLOWER_KEY_MPLS_LABEL,      /* be32 - 20 bits */
+
+       TCA_FLOWER_KEY_TCP_FLAGS,       /* be16 */
+       TCA_FLOWER_KEY_TCP_FLAGS_MASK,  /* be16 */
+
+       TCA_FLOWER_KEY_IP_TOS,          /* u8 */
+       TCA_FLOWER_KEY_IP_TOS_MASK,     /* u8 */
+       TCA_FLOWER_KEY_IP_TTL,          /* u8 */
+       TCA_FLOWER_KEY_IP_TTL_MASK,     /* u8 */
+
+       TCA_FLOWER_KEY_CVLAN_ID,        /* be16 */
+       TCA_FLOWER_KEY_CVLAN_PRIO,      /* u8   */
+       TCA_FLOWER_KEY_CVLAN_ETH_TYPE,  /* be16 */
+
+       TCA_FLOWER_KEY_ENC_IP_TOS,      /* u8 */
+       TCA_FLOWER_KEY_ENC_IP_TOS_MASK, /* u8 */
+       TCA_FLOWER_KEY_ENC_IP_TTL,      /* u8 */
+       TCA_FLOWER_KEY_ENC_IP_TTL_MASK, /* u8 */
+
+       TCA_FLOWER_KEY_ENC_OPTS,
+       TCA_FLOWER_KEY_ENC_OPTS_MASK,
+
+       TCA_FLOWER_IN_HW_COUNT,
+
+       __TCA_FLOWER_MAX,
+};
+
+#define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1)
+
+enum {
+       TCA_FLOWER_KEY_ENC_OPTS_UNSPEC,
+       TCA_FLOWER_KEY_ENC_OPTS_GENEVE, /* Nested
+                                        * TCA_FLOWER_KEY_ENC_OPT_GENEVE_
+                                        * attributes
+                                        */
+       __TCA_FLOWER_KEY_ENC_OPTS_MAX,
+};
+
+#define TCA_FLOWER_KEY_ENC_OPTS_MAX (__TCA_FLOWER_KEY_ENC_OPTS_MAX - 1)
+
+enum {
+       TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC,
+       TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS,            /* u16 */
+       TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE,             /* u8 */
+       TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA,             /* 4 to 128 bytes */
+
+       __TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX,
+};
+
+#define TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX \
+               (__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX - 1)
+
+enum {
+       TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0),
+       TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
+};
+
+/* Match-all classifier */
+
+enum {
+       TCA_MATCHALL_UNSPEC,
+       TCA_MATCHALL_CLASSID,
+       TCA_MATCHALL_ACT,
+       TCA_MATCHALL_FLAGS,
+       __TCA_MATCHALL_MAX,
+};
+
+#define TCA_MATCHALL_MAX (__TCA_MATCHALL_MAX - 1)
+
+/* Extended Matches */
+
+struct tcf_ematch_tree_hdr {
+       __u16           nmatches;
+       __u16           progid;
+};
+
+enum {
+       TCA_EMATCH_TREE_UNSPEC,
+       TCA_EMATCH_TREE_HDR,
+       TCA_EMATCH_TREE_LIST,
+       __TCA_EMATCH_TREE_MAX
+};
+#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1)
+
+struct tcf_ematch_hdr {
+       __u16           matchid;
+       __u16           kind;
+       __u16           flags;
+       __u16           pad; /* currently unused */
+};
+
+/*  0                   1
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 
+ * +-----------------------+-+-+---+
+ * |         Unused        |S|I| R |
+ * +-----------------------+-+-+---+
+ *
+ * R(2) ::= relation to next ematch
+ *          where: 0 0 END (last ematch)
+ *                 0 1 AND
+ *                 1 0 OR
+ *                 1 1 Unused (invalid)
+ * I(1) ::= invert result
+ * S(1) ::= simple payload
+ */
+#define TCF_EM_REL_END 0
+#define TCF_EM_REL_AND (1<<0)
+#define TCF_EM_REL_OR  (1<<1)
+#define TCF_EM_INVERT  (1<<2)
+#define TCF_EM_SIMPLE  (1<<3)
+
+#define TCF_EM_REL_MASK        3
+#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK)
+
+enum {
+       TCF_LAYER_LINK,
+       TCF_LAYER_NETWORK,
+       TCF_LAYER_TRANSPORT,
+       __TCF_LAYER_MAX
+};
+#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1)
+
+/* Ematch type assignments
+ *   1..32767          Reserved for ematches inside kernel tree
+ *   32768..65535      Free to use, not reliable
+ */
+#define        TCF_EM_CONTAINER        0
+#define        TCF_EM_CMP              1
+#define        TCF_EM_NBYTE            2
+#define        TCF_EM_U32              3
+#define        TCF_EM_META             4
+#define        TCF_EM_TEXT             5
+#define        TCF_EM_VLAN             6
+#define        TCF_EM_CANID            7
+#define        TCF_EM_IPSET            8
+#define        TCF_EM_IPT              9
+#define        TCF_EM_MAX              9
+
+enum {
+       TCF_EM_PROG_TC
+};
+
+enum {
+       TCF_EM_OPND_EQ,
+       TCF_EM_OPND_GT,
+       TCF_EM_OPND_LT
+};
+
+#endif
index c0d7ea0..b17201e 100644 (file)
@@ -212,6 +212,7 @@ struct prctl_mm_map {
 #define PR_SET_SPECULATION_CTRL                53
 /* Speculation control variants */
 # define PR_SPEC_STORE_BYPASS          0
+# define PR_SPEC_INDIRECT_BRANCH       1
 /* Return and control values for PR_SET/GET_SPECULATION_CTRL */
 # define PR_SPEC_NOT_AFFECTED          0
 # define PR_SPEC_PRCTL                 (1UL << 0)
diff --git a/tools/include/uapi/linux/tc_act/tc_bpf.h b/tools/include/uapi/linux/tc_act/tc_bpf.h
new file mode 100644 (file)
index 0000000..6e89a5d
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us>
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_TC_BPF_H
+#define __LINUX_TC_BPF_H
+
+#include <linux/pkt_cls.h>
+
+#define TCA_ACT_BPF 13
+
+struct tc_act_bpf {
+       tc_gen;
+};
+
+enum {
+       TCA_ACT_BPF_UNSPEC,
+       TCA_ACT_BPF_TM,
+       TCA_ACT_BPF_PARMS,
+       TCA_ACT_BPF_OPS_LEN,
+       TCA_ACT_BPF_OPS,
+       TCA_ACT_BPF_FD,
+       TCA_ACT_BPF_NAME,
+       TCA_ACT_BPF_PAD,
+       TCA_ACT_BPF_TAG,
+       TCA_ACT_BPF_ID,
+       __TCA_ACT_BPF_MAX,
+};
+#define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1)
+
+#endif
index 7bc31c9..197b40f 100644 (file)
@@ -1 +1 @@
-libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o
+libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o
index 425b480..34d9c36 100644 (file)
@@ -66,7 +66,7 @@ ifndef VERBOSE
 endif
 
 FEATURE_USER = .libbpf
-FEATURE_TESTS = libelf libelf-mmap bpf reallocarray
+FEATURE_TESTS = libelf libelf-mmap bpf reallocarray cxx
 FEATURE_DISPLAY = libelf bpf
 
 INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi
@@ -145,14 +145,26 @@ include $(srctree)/tools/build/Makefile.include
 
 BPF_IN    := $(OUTPUT)libbpf-in.o
 LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE))
+VERSION_SCRIPT := libbpf.map
+
+GLOBAL_SYM_COUNT = $(shell readelf -s $(BPF_IN) | \
+                          awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {s++} END{print s}')
+VERSIONED_SYM_COUNT = $(shell readelf -s $(OUTPUT)libbpf.so | \
+                             grep -Eo '[^ ]+@LIBBPF_' | cut -d@ -f1 | sort -u | wc -l)
 
 CMD_TARGETS = $(LIB_FILE)
 
+CXX_TEST_TARGET = $(OUTPUT)test_libbpf
+
+ifeq ($(feature-cxx), 1)
+       CMD_TARGETS += $(CXX_TEST_TARGET)
+endif
+
 TARGETS = $(CMD_TARGETS)
 
 all: fixdep all_cmd
 
-all_cmd: $(CMD_TARGETS)
+all_cmd: $(CMD_TARGETS) check
 
 $(BPF_IN): force elfdep bpfdep
        @(test -f ../../include/uapi/linux/bpf.h -a -f ../../../include/uapi/linux/bpf.h && ( \
@@ -170,11 +182,27 @@ $(BPF_IN): force elfdep bpfdep
        $(Q)$(MAKE) $(build)=libbpf
 
 $(OUTPUT)libbpf.so: $(BPF_IN)
-       $(QUIET_LINK)$(CC) --shared $^ -o $@
+       $(QUIET_LINK)$(CC) --shared -Wl,--version-script=$(VERSION_SCRIPT) \
+               $^ -o $@
 
 $(OUTPUT)libbpf.a: $(BPF_IN)
        $(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^
 
+$(OUTPUT)test_libbpf: test_libbpf.cpp $(OUTPUT)libbpf.a
+       $(QUIET_LINK)$(CXX) $^ -lelf -o $@
+
+check: check_abi
+
+check_abi: $(OUTPUT)libbpf.so
+       @if [ "$(GLOBAL_SYM_COUNT)" != "$(VERSIONED_SYM_COUNT)" ]; then  \
+               echo "Warning: Num of global symbols in $(BPF_IN)"       \
+                    "($(GLOBAL_SYM_COUNT)) does NOT match with num of"  \
+                    "versioned symbols in $^ ($(VERSIONED_SYM_COUNT))." \
+                    "Please make sure all LIBBPF_API symbols are"       \
+                    "versioned in $(VERSION_SCRIPT)." >&2;              \
+               exit 1;                                                  \
+       fi
+
 define do_install
        if [ ! -d '$(DESTDIR_SQ)$2' ]; then             \
                $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
@@ -201,8 +229,8 @@ config-clean:
        $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
 
 clean:
-       $(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so .*.d .*.cmd \
-               $(RM) LIBBPF-CFLAGS
+       $(call QUIET_CLEAN, libbpf) $(RM) $(TARGETS) $(CXX_TEST_TARGET) \
+               *.o *~ *.a *.so .*.d .*.cmd LIBBPF-CFLAGS
        $(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP.libbpf
 
 
diff --git a/tools/lib/bpf/README.rst b/tools/lib/bpf/README.rst
new file mode 100644 (file)
index 0000000..056f383
--- /dev/null
@@ -0,0 +1,139 @@
+.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+libbpf API naming convention
+============================
+
+libbpf API provides access to a few logically separated groups of
+functions and types. Every group has its own naming convention
+described here. It's recommended to follow these conventions whenever a
+new function or type is added to keep libbpf API clean and consistent.
+
+All types and functions provided by libbpf API should have one of the
+following prefixes: ``bpf_``, ``btf_``, ``libbpf_``.
+
+System call wrappers
+--------------------
+
+System call wrappers are simple wrappers for commands supported by
+sys_bpf system call. These wrappers should go to ``bpf.h`` header file
+and map one-on-one to corresponding commands.
+
+For example ``bpf_map_lookup_elem`` wraps ``BPF_MAP_LOOKUP_ELEM``
+command of sys_bpf, ``bpf_prog_attach`` wraps ``BPF_PROG_ATTACH``, etc.
+
+Objects
+-------
+
+Another class of types and functions provided by libbpf API is "objects"
+and functions to work with them. Objects are high-level abstractions
+such as BPF program or BPF map. They're represented by corresponding
+structures such as ``struct bpf_object``, ``struct bpf_program``,
+``struct bpf_map``, etc.
+
+Structures are forward declared and access to their fields should be
+provided via corresponding getters and setters rather than directly.
+
+These objects are associated with corresponding parts of ELF object that
+contains compiled BPF programs.
+
+For example ``struct bpf_object`` represents ELF object itself created
+from an ELF file or from a buffer, ``struct bpf_program`` represents a
+program in ELF object and ``struct bpf_map`` is a map.
+
+Functions that work with an object have names built from object name,
+double underscore and part that describes function purpose.
+
+For example ``bpf_object__open`` consists of the name of corresponding
+object, ``bpf_object``, double underscore and ``open`` that defines the
+purpose of the function to open ELF file and create ``bpf_object`` from
+it.
+
+Another example: ``bpf_program__load`` is named for corresponding
+object, ``bpf_program``, that is separated from other part of the name
+by double underscore.
+
+All objects and corresponding functions other than BTF related should go
+to ``libbpf.h``. BTF types and functions should go to ``btf.h``.
+
+Auxiliary functions
+-------------------
+
+Auxiliary functions and types that don't fit well in any of categories
+described above should have ``libbpf_`` prefix, e.g.
+``libbpf_get_error`` or ``libbpf_prog_type_by_name``.
+
+libbpf ABI
+==========
+
+libbpf can be both linked statically or used as DSO. To avoid possible
+conflicts with other libraries an application is linked with, all
+non-static libbpf symbols should have one of the prefixes mentioned in
+API documentation above. See API naming convention to choose the right
+name for a new symbol.
+
+Symbol visibility
+-----------------
+
+libbpf follow the model when all global symbols have visibility "hidden"
+by default and to make a symbol visible it has to be explicitly
+attributed with ``LIBBPF_API`` macro. For example:
+
+.. code-block:: c
+
+        LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id);
+
+This prevents from accidentally exporting a symbol, that is not supposed
+to be a part of ABI what, in turn, improves both libbpf developer- and
+user-experiences.
+
+ABI versionning
+---------------
+
+To make future ABI extensions possible libbpf ABI is versioned.
+Versioning is implemented by ``libbpf.map`` version script that is
+passed to linker.
+
+Version name is ``LIBBPF_`` prefix + three-component numeric version,
+starting from ``0.0.1``.
+
+Every time ABI is being changed, e.g. because a new symbol is added or
+semantic of existing symbol is changed, ABI version should be bumped.
+
+For example, if current state of ``libbpf.map`` is:
+
+.. code-block::
+        LIBBPF_0.0.1 {
+               global:
+                        bpf_func_a;
+                        bpf_func_b;
+               local:
+                       \*;
+        };
+
+, and a new symbol ``bpf_func_c`` is being introduced, then
+``libbpf.map`` should be changed like this:
+
+.. code-block::
+        LIBBPF_0.0.1 {
+               global:
+                        bpf_func_a;
+                        bpf_func_b;
+               local:
+                       \*;
+        };
+        LIBBPF_0.0.2 {
+                global:
+                        bpf_func_c;
+        } LIBBPF_0.0.1;
+
+, where new version ``LIBBPF_0.0.2`` depends on the previous
+``LIBBPF_0.0.1``.
+
+Format of version script and ways to handle ABI changes, including
+incompatible ones, described in details in [1].
+
+Links
+=====
+
+[1] https://www.akkadia.org/drepper/dsohowto.pdf
+    (Chapter 3. Maintaining APIs and ABIs).
index 03f9bcc..3caaa34 100644 (file)
@@ -173,9 +173,35 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
                                          -1);
 }
 
+static void *
+alloc_zero_tailing_info(const void *orecord, __u32 cnt,
+                       __u32 actual_rec_size, __u32 expected_rec_size)
+{
+       __u64 info_len = actual_rec_size * cnt;
+       void *info, *nrecord;
+       int i;
+
+       info = malloc(info_len);
+       if (!info)
+               return NULL;
+
+       /* zero out bytes kernel does not understand */
+       nrecord = info;
+       for (i = 0; i < cnt; i++) {
+               memcpy(nrecord, orecord, expected_rec_size);
+               memset(nrecord + expected_rec_size, 0,
+                      actual_rec_size - expected_rec_size);
+               orecord += actual_rec_size;
+               nrecord += actual_rec_size;
+       }
+
+       return info;
+}
+
 int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
                           char *log_buf, size_t log_buf_sz)
 {
+       void *finfo = NULL, *linfo = NULL;
        union bpf_attr attr;
        __u32 name_len;
        int fd;
@@ -196,19 +222,72 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
        attr.log_level = 0;
        attr.kern_version = load_attr->kern_version;
        attr.prog_ifindex = load_attr->prog_ifindex;
+       attr.prog_btf_fd = load_attr->prog_btf_fd;
+       attr.func_info_rec_size = load_attr->func_info_rec_size;
+       attr.func_info_cnt = load_attr->func_info_cnt;
+       attr.func_info = ptr_to_u64(load_attr->func_info);
+       attr.line_info_rec_size = load_attr->line_info_rec_size;
+       attr.line_info_cnt = load_attr->line_info_cnt;
+       attr.line_info = ptr_to_u64(load_attr->line_info);
        memcpy(attr.prog_name, load_attr->name,
               min(name_len, BPF_OBJ_NAME_LEN - 1));
 
        fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
-       if (fd >= 0 || !log_buf || !log_buf_sz)
+       if (fd >= 0)
                return fd;
 
+       /* After bpf_prog_load, the kernel may modify certain attributes
+        * to give user space a hint how to deal with loading failure.
+        * Check to see whether we can make some changes and load again.
+        */
+       while (errno == E2BIG && (!finfo || !linfo)) {
+               if (!finfo && attr.func_info_cnt &&
+                   attr.func_info_rec_size < load_attr->func_info_rec_size) {
+                       /* try with corrected func info records */
+                       finfo = alloc_zero_tailing_info(load_attr->func_info,
+                                                       load_attr->func_info_cnt,
+                                                       load_attr->func_info_rec_size,
+                                                       attr.func_info_rec_size);
+                       if (!finfo)
+                               goto done;
+
+                       attr.func_info = ptr_to_u64(finfo);
+                       attr.func_info_rec_size = load_attr->func_info_rec_size;
+               } else if (!linfo && attr.line_info_cnt &&
+                          attr.line_info_rec_size <
+                          load_attr->line_info_rec_size) {
+                       linfo = alloc_zero_tailing_info(load_attr->line_info,
+                                                       load_attr->line_info_cnt,
+                                                       load_attr->line_info_rec_size,
+                                                       attr.line_info_rec_size);
+                       if (!linfo)
+                               goto done;
+
+                       attr.line_info = ptr_to_u64(linfo);
+                       attr.line_info_rec_size = load_attr->line_info_rec_size;
+               } else {
+                       break;
+               }
+
+               fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+
+               if (fd >= 0)
+                       goto done;
+       }
+
+       if (!log_buf || !log_buf_sz)
+               goto done;
+
        /* Try again with log */
        attr.log_buf = ptr_to_u64(log_buf);
        attr.log_size = log_buf_sz;
        attr.log_level = 1;
        log_buf[0] = 0;
-       return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+       fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+done:
+       free(finfo);
+       free(linfo);
+       return fd;
 }
 
 int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
@@ -231,9 +310,9 @@ int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
 }
 
 int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
-                      size_t insns_cnt, int strict_alignment,
-                      const char *license, __u32 kern_version,
-                      char *log_buf, size_t log_buf_sz, int log_level)
+                      size_t insns_cnt, __u32 prog_flags, const char *license,
+                      __u32 kern_version, char *log_buf, size_t log_buf_sz,
+                      int log_level)
 {
        union bpf_attr attr;
 
@@ -247,7 +326,7 @@ int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
        attr.log_level = log_level;
        log_buf[0] = 0;
        attr.kern_version = kern_version;
-       attr.prog_flags = strict_alignment ? BPF_F_STRICT_ALIGNMENT : 0;
+       attr.prog_flags = prog_flags;
 
        return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
 }
@@ -415,6 +494,29 @@ int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
        return ret;
 }
 
+int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr)
+{
+       union bpf_attr attr;
+       int ret;
+
+       if (!test_attr->data_out && test_attr->data_size_out > 0)
+               return -EINVAL;
+
+       bzero(&attr, 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);
+       attr.test.data_size_in = test_attr->data_size_in;
+       attr.test.data_size_out = test_attr->data_size_out;
+       attr.test.repeat = test_attr->repeat;
+
+       ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
+       test_attr->data_size_out = attr.test.data_size_out;
+       test_attr->retval = attr.test.retval;
+       test_attr->duration = attr.test.duration;
+       return ret;
+}
+
 int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
 {
        union bpf_attr attr;
index 26a5153..8f09de4 100644 (file)
 #include <stdbool.h>
 #include <stddef.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifndef LIBBPF_API
 #define LIBBPF_API __attribute__((visibility("default")))
 #endif
@@ -74,6 +78,13 @@ struct bpf_load_program_attr {
        const char *license;
        __u32 kern_version;
        __u32 prog_ifindex;
+       __u32 prog_btf_fd;
+       __u32 func_info_rec_size;
+       const void *func_info;
+       __u32 func_info_cnt;
+       __u32 line_info_rec_size;
+       const void *line_info;
+       __u32 line_info_cnt;
 };
 
 /* Flags to direct loading requirements */
@@ -90,7 +101,7 @@ LIBBPF_API int bpf_load_program(enum bpf_prog_type type,
                                char *log_buf, size_t log_buf_sz);
 LIBBPF_API int bpf_verify_program(enum bpf_prog_type type,
                                  const struct bpf_insn *insns,
-                                 size_t insns_cnt, int strict_alignment,
+                                 size_t insns_cnt, __u32 prog_flags,
                                  const char *license, __u32 kern_version,
                                  char *log_buf, size_t log_buf_sz,
                                  int log_level);
@@ -110,6 +121,25 @@ LIBBPF_API int bpf_prog_attach(int prog_fd, int attachable_fd,
 LIBBPF_API int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type);
 LIBBPF_API int bpf_prog_detach2(int prog_fd, int attachable_fd,
                                enum bpf_attach_type type);
+
+struct bpf_prog_test_run_attr {
+       int prog_fd;
+       int repeat;
+       const void *data_in;
+       __u32 data_size_in;
+       void *data_out;      /* optional */
+       __u32 data_size_out; /* in: max length of data_out
+                             * out: length of data_out */
+       __u32 retval;        /* out: return code of the BPF program */
+       __u32 duration;      /* out: average per repetition in ns */
+};
+
+LIBBPF_API int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr);
+
+/*
+ * bpf_prog_test_run does not check that data_out is large enough. Consider
+ * using bpf_prog_test_run_xattr instead.
+ */
 LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data,
                                 __u32 size, void *data_out, __u32 *size_out,
                                 __u32 *retval, __u32 *duration);
@@ -128,4 +158,9 @@ LIBBPF_API int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf,
 LIBBPF_API int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf,
                                 __u32 *buf_len, __u32 *prog_id, __u32 *fd_type,
                                 __u64 *probe_offset, __u64 *probe_addr);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* __LIBBPF_BPF_H */
diff --git a/tools/lib/bpf/bpf_prog_linfo.c b/tools/lib/bpf/bpf_prog_linfo.c
new file mode 100644 (file)
index 0000000..6978314
--- /dev/null
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+/* Copyright (c) 2018 Facebook */
+
+#include <string.h>
+#include <stdlib.h>
+#include <linux/err.h>
+#include <linux/bpf.h>
+#include "libbpf.h"
+
+#ifndef min
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+struct bpf_prog_linfo {
+       void *raw_linfo;
+       void *raw_jited_linfo;
+       __u32 *nr_jited_linfo_per_func;
+       __u32 *jited_linfo_func_idx;
+       __u32 nr_linfo;
+       __u32 nr_jited_func;
+       __u32 rec_size;
+       __u32 jited_rec_size;
+};
+
+static int dissect_jited_func(struct bpf_prog_linfo *prog_linfo,
+                             const __u64 *ksym_func, const __u32 *ksym_len)
+{
+       __u32 nr_jited_func, nr_linfo;
+       const void *raw_jited_linfo;
+       const __u64 *jited_linfo;
+       __u64 last_jited_linfo;
+       /*
+        * Index to raw_jited_linfo:
+        *      i: Index for searching the next ksym_func
+        * prev_i: Index to the last found ksym_func
+        */
+       __u32 i, prev_i;
+       __u32 f; /* Index to ksym_func */
+
+       raw_jited_linfo = prog_linfo->raw_jited_linfo;
+       jited_linfo = raw_jited_linfo;
+       if (ksym_func[0] != *jited_linfo)
+               goto errout;
+
+       prog_linfo->jited_linfo_func_idx[0] = 0;
+       nr_jited_func = prog_linfo->nr_jited_func;
+       nr_linfo = prog_linfo->nr_linfo;
+
+       for (prev_i = 0, i = 1, f = 1;
+            i < nr_linfo && f < nr_jited_func;
+            i++) {
+               raw_jited_linfo += prog_linfo->jited_rec_size;
+               last_jited_linfo = *jited_linfo;
+               jited_linfo = raw_jited_linfo;
+
+               if (ksym_func[f] == *jited_linfo) {
+                       prog_linfo->jited_linfo_func_idx[f] = i;
+
+                       /* Sanity check */
+                       if (last_jited_linfo - ksym_func[f - 1] + 1 >
+                           ksym_len[f - 1])
+                               goto errout;
+
+                       prog_linfo->nr_jited_linfo_per_func[f - 1] =
+                               i - prev_i;
+                       prev_i = i;
+
+                       /*
+                        * The ksym_func[f] is found in jited_linfo.
+                        * Look for the next one.
+                        */
+                       f++;
+               } else if (*jited_linfo <= last_jited_linfo) {
+                       /* Ensure the addr is increasing _within_ a func */
+                       goto errout;
+               }
+       }
+
+       if (f != nr_jited_func)
+               goto errout;
+
+       prog_linfo->nr_jited_linfo_per_func[nr_jited_func - 1] =
+               nr_linfo - prev_i;
+
+       return 0;
+
+errout:
+       return -EINVAL;
+}
+
+void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo)
+{
+       if (!prog_linfo)
+               return;
+
+       free(prog_linfo->raw_linfo);
+       free(prog_linfo->raw_jited_linfo);
+       free(prog_linfo->nr_jited_linfo_per_func);
+       free(prog_linfo->jited_linfo_func_idx);
+       free(prog_linfo);
+}
+
+struct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info)
+{
+       struct bpf_prog_linfo *prog_linfo;
+       __u32 nr_linfo, nr_jited_func;
+
+       nr_linfo = info->nr_line_info;
+
+       if (!nr_linfo)
+               return NULL;
+
+       /*
+        * The min size that bpf_prog_linfo has to access for
+        * searching purpose.
+        */
+       if (info->line_info_rec_size <
+           offsetof(struct bpf_line_info, file_name_off))
+               return NULL;
+
+       prog_linfo = calloc(1, sizeof(*prog_linfo));
+       if (!prog_linfo)
+               return NULL;
+
+       /* Copy xlated line_info */
+       prog_linfo->nr_linfo = nr_linfo;
+       prog_linfo->rec_size = info->line_info_rec_size;
+       prog_linfo->raw_linfo = malloc(nr_linfo * prog_linfo->rec_size);
+       if (!prog_linfo->raw_linfo)
+               goto err_free;
+       memcpy(prog_linfo->raw_linfo, (void *)(long)info->line_info,
+              nr_linfo * prog_linfo->rec_size);
+
+       nr_jited_func = info->nr_jited_ksyms;
+       if (!nr_jited_func ||
+           !info->jited_line_info ||
+           info->nr_jited_line_info != nr_linfo ||
+           info->jited_line_info_rec_size < sizeof(__u64) ||
+           info->nr_jited_func_lens != nr_jited_func ||
+           !info->jited_ksyms ||
+           !info->jited_func_lens)
+               /* Not enough info to provide jited_line_info */
+               return prog_linfo;
+
+       /* Copy jited_line_info */
+       prog_linfo->nr_jited_func = nr_jited_func;
+       prog_linfo->jited_rec_size = info->jited_line_info_rec_size;
+       prog_linfo->raw_jited_linfo = malloc(nr_linfo *
+                                            prog_linfo->jited_rec_size);
+       if (!prog_linfo->raw_jited_linfo)
+               goto err_free;
+       memcpy(prog_linfo->raw_jited_linfo,
+              (void *)(long)info->jited_line_info,
+              nr_linfo * prog_linfo->jited_rec_size);
+
+       /* Number of jited_line_info per jited func */
+       prog_linfo->nr_jited_linfo_per_func = malloc(nr_jited_func *
+                                                    sizeof(__u32));
+       if (!prog_linfo->nr_jited_linfo_per_func)
+               goto err_free;
+
+       /*
+        * For each jited func,
+        * the start idx to the "linfo" and "jited_linfo" array,
+        */
+       prog_linfo->jited_linfo_func_idx = malloc(nr_jited_func *
+                                                 sizeof(__u32));
+       if (!prog_linfo->jited_linfo_func_idx)
+               goto err_free;
+
+       if (dissect_jited_func(prog_linfo,
+                              (__u64 *)(long)info->jited_ksyms,
+                              (__u32 *)(long)info->jited_func_lens))
+               goto err_free;
+
+       return prog_linfo;
+
+err_free:
+       bpf_prog_linfo__free(prog_linfo);
+       return NULL;
+}
+
+const struct bpf_line_info *
+bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
+                               __u64 addr, __u32 func_idx, __u32 nr_skip)
+{
+       __u32 jited_rec_size, rec_size, nr_linfo, start, i;
+       const void *raw_jited_linfo, *raw_linfo;
+       const __u64 *jited_linfo;
+
+       if (func_idx >= prog_linfo->nr_jited_func)
+               return NULL;
+
+       nr_linfo = prog_linfo->nr_jited_linfo_per_func[func_idx];
+       if (nr_skip >= nr_linfo)
+               return NULL;
+
+       start = prog_linfo->jited_linfo_func_idx[func_idx] + nr_skip;
+       jited_rec_size = prog_linfo->jited_rec_size;
+       raw_jited_linfo = prog_linfo->raw_jited_linfo +
+               (start * jited_rec_size);
+       jited_linfo = raw_jited_linfo;
+       if (addr < *jited_linfo)
+               return NULL;
+
+       nr_linfo -= nr_skip;
+       rec_size = prog_linfo->rec_size;
+       raw_linfo = prog_linfo->raw_linfo + (start * rec_size);
+       for (i = 0; i < nr_linfo; i++) {
+               if (addr < *jited_linfo)
+                       break;
+
+               raw_linfo += rec_size;
+               raw_jited_linfo += jited_rec_size;
+               jited_linfo = raw_jited_linfo;
+       }
+
+       return raw_linfo - rec_size;
+}
+
+const struct bpf_line_info *
+bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
+                     __u32 insn_off, __u32 nr_skip)
+{
+       const struct bpf_line_info *linfo;
+       __u32 rec_size, nr_linfo, i;
+       const void *raw_linfo;
+
+       nr_linfo = prog_linfo->nr_linfo;
+       if (nr_skip >= nr_linfo)
+               return NULL;
+
+       rec_size = prog_linfo->rec_size;
+       raw_linfo = prog_linfo->raw_linfo + (nr_skip * rec_size);
+       linfo = raw_linfo;
+       if (insn_off < linfo->insn_off)
+               return NULL;
+
+       nr_linfo -= nr_skip;
+       for (i = 0; i < nr_linfo; i++) {
+               if (insn_off < linfo->insn_off)
+                       break;
+
+               raw_linfo += rec_size;
+               linfo = raw_linfo;
+       }
+
+       return raw_linfo - rec_size;
+}
index 449591a..d682d3b 100644 (file)
@@ -37,6 +37,48 @@ struct btf {
        int fd;
 };
 
+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.
+        */
+       void *info;
+       __u32 rec_size;
+       __u32 len;
+};
+
+struct btf_ext {
+       struct btf_ext_info func_info;
+       struct btf_ext_info line_info;
+};
+
+struct btf_ext_info_sec {
+       __u32   sec_name_off;
+       __u32   num_info;
+       /* Followed by num_info * record_size number of bytes */
+       __u8    data[0];
+};
+
+/* The minimum bpf_func_info checked by the loader */
+struct bpf_func_info_min {
+       __u32   insn_off;
+       __u32   type_id;
+};
+
+/* The minimum bpf_line_info checked by the loader */
+struct bpf_line_info_min {
+       __u32   insn_off;
+       __u32   file_name_off;
+       __u32   line_off;
+       __u32   line_col;
+};
+
+static inline __u64 ptr_to_u64(const void *ptr)
+{
+       return (__u64) (unsigned long) ptr;
+}
+
 static int btf_add_type(struct btf *btf, struct btf_type *t)
 {
        if (btf->types_size - btf->nr_types < 2) {
@@ -165,6 +207,10 @@ static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log)
                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:
@@ -393,3 +439,350 @@ const char *btf__name_by_offset(const struct btf *btf, __u32 offset)
        else
                return NULL;
 }
+
+int btf__get_from_id(__u32 id, struct btf **btf)
+{
+       struct bpf_btf_info btf_info = { 0 };
+       __u32 len = sizeof(btf_info);
+       __u32 last_size;
+       int btf_fd;
+       void *ptr;
+       int err;
+
+       err = 0;
+       *btf = NULL;
+       btf_fd = bpf_btf_get_fd_by_id(id);
+       if (btf_fd < 0)
+               return 0;
+
+       /* we won't know btf_size until we call bpf_obj_get_info_by_fd(). so
+        * let's start with a sane default - 4KiB here - and resize it only if
+        * bpf_obj_get_info_by_fd() needs a bigger buffer.
+        */
+       btf_info.btf_size = 4096;
+       last_size = btf_info.btf_size;
+       ptr = malloc(last_size);
+       if (!ptr) {
+               err = -ENOMEM;
+               goto exit_free;
+       }
+
+       bzero(ptr, last_size);
+       btf_info.btf = ptr_to_u64(ptr);
+       err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
+
+       if (!err && btf_info.btf_size > last_size) {
+               void *temp_ptr;
+
+               last_size = btf_info.btf_size;
+               temp_ptr = realloc(ptr, last_size);
+               if (!temp_ptr) {
+                       err = -ENOMEM;
+                       goto exit_free;
+               }
+               ptr = temp_ptr;
+               bzero(ptr, last_size);
+               btf_info.btf = ptr_to_u64(ptr);
+               err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
+       }
+
+       if (err || btf_info.btf_size > last_size) {
+               err = errno;
+               goto exit_free;
+       }
+
+       *btf = btf__new((__u8 *)(long)btf_info.btf, btf_info.btf_size, NULL);
+       if (IS_ERR(*btf)) {
+               err = PTR_ERR(*btf);
+               *btf = NULL;
+       }
+
+exit_free:
+       close(btf_fd);
+       free(ptr);
+
+       return err;
+}
+
+struct btf_ext_sec_copy_param {
+       __u32 off;
+       __u32 len;
+       __u32 min_rec_size;
+       struct btf_ext_info *ext_info;
+       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)
+{
+       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;
+
+       if (ext_sec->off & 0x03) {
+               elog(".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);
+               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);
+               return -EINVAL;
+       }
+
+       /* The record size needs to meet the minimum standard */
+       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);
+               return -EINVAL;
+       }
+
+       sinfo = info + sizeof(__u32);
+       info_left -= sizeof(__u32);
+
+       /* 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);
+               return -EINVAL;
+       }
+
+       while (info_left) {
+               unsigned int sec_hdrlen = sizeof(struct btf_ext_info_sec);
+               __u64 total_record_size;
+               __u32 num_records;
+
+               if (info_left < sec_hdrlen) {
+                       elog("%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",
+                            ext_sec->desc);
+                       return -EINVAL;
+               }
+
+               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",
+                            ext_sec->desc);
+                       return -EINVAL;
+               }
+
+               info_left -= total_record_size;
+               sinfo = (void *)sinfo + total_record_size;
+       }
+
+       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);
+
+       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)
+{
+       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,
+               .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);
+}
+
+static int btf_ext_copy_line_info(struct btf_ext *btf_ext,
+                                 __u8 *data, __u32 data_size,
+                                 btf_print_fn_t err_log)
+{
+       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,
+               .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);
+}
+
+static int btf_ext_parse_hdr(__u8 *data, __u32 data_size,
+                            btf_print_fn_t err_log)
+{
+       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");
+               return -EINVAL;
+       }
+
+       if (hdr->magic != BTF_MAGIC) {
+               elog("Invalid BTF.ext magic:%x\n", hdr->magic);
+               return -EINVAL;
+       }
+
+       if (hdr->version != BTF_VERSION) {
+               elog("Unsupported BTF.ext version:%u\n", hdr->version);
+               return -ENOTSUP;
+       }
+
+       if (hdr->flags) {
+               elog("Unsupported BTF.ext flags:%x\n", hdr->flags);
+               return -ENOTSUP;
+       }
+
+       if (data_size == hdr->hdr_len) {
+               elog("BTF.ext has no data\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+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);
+}
+
+struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log)
+{
+       struct btf_ext *btf_ext;
+       int err;
+
+       err = btf_ext_parse_hdr(data, size, err_log);
+       if (err)
+               return ERR_PTR(err);
+
+       btf_ext = calloc(1, sizeof(struct btf_ext));
+       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);
+       }
+
+       err = btf_ext_copy_line_info(btf_ext, data, size, err_log);
+       if (err) {
+               btf_ext__free(btf_ext);
+               return ERR_PTR(err);
+       }
+
+       return btf_ext;
+}
+
+static int btf_ext_reloc_info(const struct btf *btf,
+                             const struct btf_ext_info *ext_info,
+                             const char *sec_name, __u32 insns_cnt,
+                             void **info, __u32 *cnt)
+{
+       __u32 sec_hdrlen = sizeof(struct btf_ext_info_sec);
+       __u32 i, record_size, existing_len, records_len;
+       struct btf_ext_info_sec *sinfo;
+       const char *info_sec_name;
+       __u64 remain_len;
+       void *data;
+
+       record_size = ext_info->rec_size;
+       sinfo = ext_info->info;
+       remain_len = ext_info->len;
+       while (remain_len > 0) {
+               records_len = sinfo->num_info * record_size;
+               info_sec_name = btf__name_by_offset(btf, sinfo->sec_name_off);
+               if (strcmp(info_sec_name, sec_name)) {
+                       remain_len -= sec_hdrlen + records_len;
+                       sinfo = (void *)sinfo + sec_hdrlen + records_len;
+                       continue;
+               }
+
+               existing_len = (*cnt) * record_size;
+               data = realloc(*info, existing_len + records_len);
+               if (!data)
+                       return -ENOMEM;
+
+               memcpy(data + existing_len, sinfo->data, records_len);
+               /* adjust insn_off only, the rest data will be passed
+                * to the kernel.
+                */
+               for (i = 0; i < sinfo->num_info; i++) {
+                       __u32 *insn_off;
+
+                       insn_off = data + existing_len + (i * record_size);
+                       *insn_off = *insn_off / sizeof(struct bpf_insn) +
+                               insns_cnt;
+               }
+               *info = data;
+               *cnt += sinfo->num_info;
+               return 0;
+       }
+
+       return -ENOENT;
+}
+
+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)
+{
+       return btf_ext_reloc_info(btf, &btf_ext->func_info, sec_name,
+                                 insns_cnt, func_info, cnt);
+}
+
+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)
+{
+       return btf_ext_reloc_info(btf, &btf_ext->line_info, sec_name,
+                                 insns_cnt, line_info, cnt);
+}
+
+__u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext)
+{
+       return btf_ext->func_info.rec_size;
+}
+
+__u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext)
+{
+       return btf_ext->line_info.rec_size;
+}
index b77e708..b0610dc 100644 (file)
@@ -6,15 +6,55 @@
 
 #include <linux/types.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifndef LIBBPF_API
 #define LIBBPF_API __attribute__((visibility("default")))
 #endif
 
 #define BTF_ELF_SEC ".BTF"
+#define BTF_EXT_ELF_SEC ".BTF.ext"
 
 struct btf;
+struct btf_ext;
 struct btf_type;
 
+/*
+ * The .BTF.ext ELF section layout defined as
+ *   struct btf_ext_header
+ *   func_info subsection
+ *
+ * The func_info subsection layout:
+ *   record size for struct bpf_func_info in the func_info subsection
+ *   struct btf_sec_func_info for section #1
+ *   a list of bpf_func_info records for section #1
+ *     where struct bpf_func_info mimics one in include/uapi/linux/bpf.h
+ *     but may not be identical
+ *   struct btf_sec_func_info for section #2
+ *   a list of bpf_func_info records for section #2
+ *   ......
+ *
+ * Note that the bpf_func_info record size in .BTF.ext may not
+ * be the same as the one defined in include/uapi/linux/bpf.h.
+ * The loader should ensure that record_size meets minimum
+ * requirement and pass the record as is to the kernel. The
+ * kernel will handle the func_info properly based on its contents.
+ */
+struct btf_ext_header {
+       __u16   magic;
+       __u8    version;
+       __u8    flags;
+       __u32   hdr_len;
+
+       /* All offsets are in bytes relative to the end of this header */
+       __u32   func_info_off;
+       __u32   func_info_len;
+       __u32   line_info_off;
+       __u32   line_info_len;
+};
+
 typedef int (*btf_print_fn_t)(const char *, ...)
        __attribute__((format(printf, 1, 2)));
 
@@ -28,5 +68,23 @@ 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 char *btf__name_by_offset(const struct btf *btf, __u32 offset);
+LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf);
+
+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);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
 
 #endif /* __LIBBPF_BTF_H */
index d6e62e9..169e347 100644 (file)
@@ -9,7 +9,9 @@
  * Copyright (C) 2017 Nicira, Inc.
  */
 
+#ifndef _GNU_SOURCE
 #define _GNU_SOURCE
+#endif
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdarg.h>
@@ -24,6 +26,7 @@
 #include <linux/kernel.h>
 #include <linux/bpf.h>
 #include <linux/btf.h>
+#include <linux/filter.h>
 #include <linux/list.h>
 #include <linux/limits.h>
 #include <linux/perf_event.h>
@@ -114,6 +117,11 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
 #endif
 
+struct bpf_capabilities {
+       /* v4.14: kernel support for program & map names. */
+       __u32 name:1;
+};
+
 /*
  * bpf_prog should be a better name but it has been used in
  * linux/filter.h.
@@ -124,6 +132,10 @@ struct bpf_program {
        char *name;
        int prog_ifindex;
        char *section_name;
+       /* section_name with / replaced by _; makes recursive pinning
+        * in bpf_object__pin_programs easier
+        */
+       char *pin_name;
        struct bpf_insn *insns;
        size_t insns_cnt, main_prog_cnt;
        enum bpf_prog_type type;
@@ -152,6 +164,16 @@ struct bpf_program {
        bpf_program_clear_priv_t clear_priv;
 
        enum bpf_attach_type expected_attach_type;
+       int btf_fd;
+       void *func_info;
+       __u32 func_info_rec_size;
+       __u32 func_info_cnt;
+
+       struct bpf_capabilities *caps;
+
+       void *line_info;
+       __u32 line_info_rec_size;
+       __u32 line_info_cnt;
 };
 
 struct bpf_map {
@@ -159,6 +181,7 @@ struct bpf_map {
        char *name;
        size_t offset;
        int map_ifindex;
+       int inner_map_fd;
        struct bpf_map_def def;
        __u32 btf_key_type_id;
        __u32 btf_value_type_id;
@@ -208,10 +231,13 @@ struct bpf_object {
        struct list_head list;
 
        struct btf *btf;
+       struct btf_ext *btf_ext;
 
        void *priv;
        bpf_object_clear_priv_t clear_priv;
 
+       struct bpf_capabilities caps;
+
        char path[];
 };
 #define obj_elf_valid(o)       ((o)->efile.elf)
@@ -237,6 +263,10 @@ void bpf_program__unload(struct bpf_program *prog)
 
        prog->instances.nr = -1;
        zfree(&prog->instances.fds);
+
+       zclose(prog->btf_fd);
+       zfree(&prog->func_info);
+       zfree(&prog->line_info);
 }
 
 static void bpf_program__exit(struct bpf_program *prog)
@@ -253,6 +283,7 @@ static void bpf_program__exit(struct bpf_program *prog)
        bpf_program__unload(prog);
        zfree(&prog->name);
        zfree(&prog->section_name);
+       zfree(&prog->pin_name);
        zfree(&prog->insns);
        zfree(&prog->reloc_desc);
 
@@ -261,6 +292,17 @@ static void bpf_program__exit(struct bpf_program *prog)
        prog->idx = -1;
 }
 
+static char *__bpf_program__pin_name(struct bpf_program *prog)
+{
+       char *name, *p;
+
+       name = p = strdup(prog->section_name);
+       while ((p = strchr(p, '/')))
+               *p = '_';
+
+       return name;
+}
+
 static int
 bpf_program__init(void *data, size_t size, char *section_name, int idx,
                  struct bpf_program *prog)
@@ -279,6 +321,13 @@ bpf_program__init(void *data, size_t size, char *section_name, int idx,
                goto errout;
        }
 
+       prog->pin_name = __bpf_program__pin_name(prog);
+       if (!prog->pin_name) {
+               pr_warning("failed to alloc pin name for prog under section(%d) %s\n",
+                          idx, section_name);
+               goto errout;
+       }
+
        prog->insns = malloc(size);
        if (!prog->insns) {
                pr_warning("failed to alloc insns for prog under section %s\n",
@@ -291,7 +340,8 @@ bpf_program__init(void *data, size_t size, char *section_name, int idx,
        prog->idx = idx;
        prog->instances.fds = NULL;
        prog->instances.nr = -1;
-       prog->type = BPF_PROG_TYPE_KPROBE;
+       prog->type = BPF_PROG_TYPE_UNSPEC;
+       prog->btf_fd = -1;
 
        return 0;
 errout:
@@ -310,6 +360,7 @@ bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
        if (err)
                return err;
 
+       prog.caps = &obj->caps;
        progs = obj->programs;
        nr_progs = obj->nr_programs;
 
@@ -562,6 +613,14 @@ static int compare_bpf_map(const void *_a, const void *_b)
        return a->offset - b->offset;
 }
 
+static bool bpf_map_type__is_map_in_map(enum bpf_map_type type)
+{
+       if (type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
+           type == BPF_MAP_TYPE_HASH_OF_MAPS)
+               return true;
+       return false;
+}
+
 static int
 bpf_object__init_maps(struct bpf_object *obj, int flags)
 {
@@ -625,13 +684,15 @@ bpf_object__init_maps(struct bpf_object *obj, int flags)
        }
        obj->nr_maps = nr_maps;
 
-       /*
-        * fill all fd with -1 so won't close incorrect
-        * fd (fd=0 is stdin) when failure (zclose won't close
-        * negative fd)).
-        */
-       for (i = 0; i < nr_maps; i++)
+       for (i = 0; i < nr_maps; i++) {
+               /*
+                * fill all fd with -1 so won't close incorrect
+                * fd (fd=0 is stdin) when failure (zclose won't close
+                * negative fd)).
+                */
                obj->maps[i].fd = -1;
+               obj->maps[i].inner_map_fd = -1;
+       }
 
        /*
         * Fill obj->maps using data in "maps" section.
@@ -723,6 +784,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 {
        Elf *elf = obj->efile.elf;
        GElf_Ehdr *ep = &obj->efile.ehdr;
+       Elf_Data *btf_ext_data = NULL;
        Elf_Scn *scn = NULL;
        int idx = 0, err = 0;
 
@@ -784,6 +846,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
                                           BTF_ELF_SEC, PTR_ERR(obj->btf));
                                obj->btf = NULL;
                        }
+               } else if (strcmp(name, BTF_EXT_ELF_SEC) == 0) {
+                       btf_ext_data = data;
                } else if (sh.sh_type == SHT_SYMTAB) {
                        if (obj->efile.symbols) {
                                pr_warning("bpf: multiple SYMTAB in %s\n",
@@ -845,6 +909,22 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
                pr_warning("Corrupted ELF file: index of strtab invalid\n");
                return LIBBPF_ERRNO__FORMAT;
        }
+       if (btf_ext_data) {
+               if (!obj->btf) {
+                       pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
+                                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);
+                       if (IS_ERR(obj->btf_ext)) {
+                               pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
+                                          BTF_EXT_ELF_SEC,
+                                          PTR_ERR(obj->btf_ext));
+                               obj->btf_ext = NULL;
+                       }
+               }
+       }
        if (obj->efile.maps_shndx >= 0) {
                err = bpf_object__init_maps(obj, flags);
                if (err)
@@ -1094,6 +1174,52 @@ err_free_new_name:
        return -errno;
 }
 
+static int
+bpf_object__probe_name(struct bpf_object *obj)
+{
+       struct bpf_load_program_attr attr;
+       char *cp, errmsg[STRERR_BUFSIZE];
+       struct bpf_insn insns[] = {
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       };
+       int ret;
+
+       /* make sure basic loading works */
+
+       memset(&attr, 0, sizeof(attr));
+       attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+       attr.insns = insns;
+       attr.insns_cnt = ARRAY_SIZE(insns);
+       attr.license = "GPL";
+
+       ret = bpf_load_program_xattr(&attr, NULL, 0);
+       if (ret < 0) {
+               cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
+               pr_warning("Error in %s():%s(%d). Couldn't load basic 'r0 = 0' BPF program.\n",
+                          __func__, cp, errno);
+               return -errno;
+       }
+       close(ret);
+
+       /* now try the same program, but with the name */
+
+       attr.name = "test";
+       ret = bpf_load_program_xattr(&attr, NULL, 0);
+       if (ret >= 0) {
+               obj->caps.name = 1;
+               close(ret);
+       }
+
+       return 0;
+}
+
+static int
+bpf_object__probe_caps(struct bpf_object *obj)
+{
+       return bpf_object__probe_name(obj);
+}
+
 static int
 bpf_object__create_maps(struct bpf_object *obj)
 {
@@ -1113,7 +1239,8 @@ bpf_object__create_maps(struct bpf_object *obj)
                        continue;
                }
 
-               create_attr.name = map->name;
+               if (obj->caps.name)
+                       create_attr.name = map->name;
                create_attr.map_ifindex = map->map_ifindex;
                create_attr.map_type = def->type;
                create_attr.map_flags = def->map_flags;
@@ -1123,6 +1250,9 @@ bpf_object__create_maps(struct bpf_object *obj)
                create_attr.btf_fd = 0;
                create_attr.btf_key_type_id = 0;
                create_attr.btf_value_type_id = 0;
+               if (bpf_map_type__is_map_in_map(def->type) &&
+                   map->inner_map_fd >= 0)
+                       create_attr.inner_map_fd = map->inner_map_fd;
 
                if (obj->btf && !bpf_map_find_btf_info(map, obj->btf)) {
                        create_attr.btf_fd = btf__fd(obj->btf);
@@ -1160,6 +1290,82 @@ bpf_object__create_maps(struct bpf_object *obj)
        return 0;
 }
 
+static int
+check_btf_ext_reloc_err(struct bpf_program *prog, int err,
+                       void *btf_prog_info, const char *info_name)
+{
+       if (err != -ENOENT) {
+               pr_warning("Error in loading %s for sec %s.\n",
+                          info_name, prog->section_name);
+               return err;
+       }
+
+       /* err == -ENOENT (i.e. prog->section_name not found in btf_ext) */
+
+       if (btf_prog_info) {
+               /*
+                * Some info has already been found but has problem
+                * in the last btf_ext reloc.  Must have to error
+                * out.
+                */
+               pr_warning("Error in relocating %s for sec %s.\n",
+                          info_name, prog->section_name);
+               return err;
+       }
+
+       /*
+        * Have problem loading the very first info.  Ignore
+        * the rest.
+        */
+       pr_warning("Cannot find %s for main program sec %s. Ignore all %s.\n",
+                  info_name, prog->section_name, info_name);
+       return 0;
+}
+
+static int
+bpf_program_reloc_btf_ext(struct bpf_program *prog, struct bpf_object *obj,
+                         const char *section_name,  __u32 insn_offset)
+{
+       int err;
+
+       if (!insn_offset || prog->func_info) {
+               /*
+                * !insn_offset => main program
+                *
+                * For sub prog, the main program's func_info has to
+                * be loaded first (i.e. prog->func_info != NULL)
+                */
+               err = btf_ext__reloc_func_info(obj->btf, obj->btf_ext,
+                                              section_name, insn_offset,
+                                              &prog->func_info,
+                                              &prog->func_info_cnt);
+               if (err)
+                       return check_btf_ext_reloc_err(prog, err,
+                                                      prog->func_info,
+                                                      "bpf_func_info");
+
+               prog->func_info_rec_size = btf_ext__func_info_rec_size(obj->btf_ext);
+       }
+
+       if (!insn_offset || prog->line_info) {
+               err = btf_ext__reloc_line_info(obj->btf, obj->btf_ext,
+                                              section_name, insn_offset,
+                                              &prog->line_info,
+                                              &prog->line_info_cnt);
+               if (err)
+                       return check_btf_ext_reloc_err(prog, err,
+                                                      prog->line_info,
+                                                      "bpf_line_info");
+
+               prog->line_info_rec_size = btf_ext__line_info_rec_size(obj->btf_ext);
+       }
+
+       if (!insn_offset)
+               prog->btf_fd = btf__fd(obj->btf);
+
+       return 0;
+}
+
 static int
 bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
                        struct reloc_desc *relo)
@@ -1167,6 +1373,7 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
        struct bpf_insn *insn, *new_insn;
        struct bpf_program *text;
        size_t new_cnt;
+       int err;
 
        if (relo->type != RELO_CALL)
                return -LIBBPF_ERRNO__RELOC;
@@ -1189,6 +1396,15 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
                        pr_warning("oom in prog realloc\n");
                        return -ENOMEM;
                }
+
+               if (obj->btf_ext) {
+                       err = bpf_program_reloc_btf_ext(prog, obj,
+                                                       text->section_name,
+                                                       prog->insns_cnt);
+                       if (err)
+                               return err;
+               }
+
                memcpy(new_insn + prog->insns_cnt, text->insns,
                       text->insns_cnt * sizeof(*insn));
                prog->insns = new_insn;
@@ -1208,7 +1424,17 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
 {
        int i, err;
 
-       if (!prog || !prog->reloc_desc)
+       if (!prog)
+               return 0;
+
+       if (obj->btf_ext) {
+               err = bpf_program_reloc_btf_ext(prog, obj,
+                                               prog->section_name, 0);
+               if (err)
+                       return err;
+       }
+
+       if (!prog->reloc_desc)
                return 0;
 
        for (i = 0; i < prog->nr_reloc; i++) {
@@ -1296,9 +1522,8 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
 }
 
 static int
-load_program(enum bpf_prog_type type, enum bpf_attach_type expected_attach_type,
-            const char *name, struct bpf_insn *insns, int insns_cnt,
-            char *license, __u32 kern_version, int *pfd, int prog_ifindex)
+load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
+            char *license, __u32 kern_version, int *pfd)
 {
        struct bpf_load_program_attr load_attr;
        char *cp, errmsg[STRERR_BUFSIZE];
@@ -1306,15 +1531,22 @@ load_program(enum bpf_prog_type type, enum bpf_attach_type expected_attach_type,
        int ret;
 
        memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
-       load_attr.prog_type = type;
-       load_attr.expected_attach_type = expected_attach_type;
-       load_attr.name = name;
+       load_attr.prog_type = prog->type;
+       load_attr.expected_attach_type = prog->expected_attach_type;
+       if (prog->caps->name)
+               load_attr.name = prog->name;
        load_attr.insns = insns;
        load_attr.insns_cnt = insns_cnt;
        load_attr.license = license;
        load_attr.kern_version = kern_version;
-       load_attr.prog_ifindex = prog_ifindex;
-
+       load_attr.prog_ifindex = prog->prog_ifindex;
+       load_attr.prog_btf_fd = prog->btf_fd >= 0 ? prog->btf_fd : 0;
+       load_attr.func_info = prog->func_info;
+       load_attr.func_info_rec_size = prog->func_info_rec_size;
+       load_attr.func_info_cnt = prog->func_info_cnt;
+       load_attr.line_info = prog->line_info;
+       load_attr.line_info_rec_size = prog->line_info_rec_size;
+       load_attr.line_info_cnt = prog->line_info_cnt;
        if (!load_attr.insns || !load_attr.insns_cnt)
                return -EINVAL;
 
@@ -1394,10 +1626,8 @@ bpf_program__load(struct bpf_program *prog,
                        pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
                                   prog->section_name, prog->instances.nr);
                }
-               err = load_program(prog->type, prog->expected_attach_type,
-                                  prog->name, prog->insns, prog->insns_cnt,
-                                  license, kern_version, &fd,
-                                  prog->prog_ifindex);
+               err = load_program(prog, prog->insns, prog->insns_cnt,
+                                  license, kern_version, &fd);
                if (!err)
                        prog->instances.fds[0] = fd;
                goto out;
@@ -1425,11 +1655,9 @@ bpf_program__load(struct bpf_program *prog,
                        continue;
                }
 
-               err = load_program(prog->type, prog->expected_attach_type,
-                                  prog->name, result.new_insn_ptr,
+               err = load_program(prog, result.new_insn_ptr,
                                   result.new_insn_cnt,
-                                  license, kern_version, &fd,
-                                  prog->prog_ifindex);
+                                  license, kern_version, &fd);
 
                if (err) {
                        pr_warning("Loading the %dth instance of program '%s' failed\n",
@@ -1495,12 +1723,12 @@ static bool bpf_prog_type__needs_kver(enum bpf_prog_type type)
        case BPF_PROG_TYPE_LIRC_MODE2:
        case BPF_PROG_TYPE_SK_REUSEPORT:
        case BPF_PROG_TYPE_FLOW_DISSECTOR:
-               return false;
        case BPF_PROG_TYPE_UNSPEC:
-       case BPF_PROG_TYPE_KPROBE:
        case BPF_PROG_TYPE_TRACEPOINT:
-       case BPF_PROG_TYPE_PERF_EVENT:
        case BPF_PROG_TYPE_RAW_TRACEPOINT:
+       case BPF_PROG_TYPE_PERF_EVENT:
+               return false;
+       case BPF_PROG_TYPE_KPROBE:
        default:
                return true;
        }
@@ -1627,6 +1855,7 @@ int bpf_object__load(struct bpf_object *obj)
 
        obj->loaded = true;
 
+       CHECK_ERR(bpf_object__probe_caps(obj), err, out);
        CHECK_ERR(bpf_object__create_maps(obj), err, out);
        CHECK_ERR(bpf_object__relocate(obj), err, out);
        CHECK_ERR(bpf_object__load_progs(obj), err, out);
@@ -1699,6 +1928,34 @@ int bpf_program__pin_instance(struct bpf_program *prog, const char *path,
        return 0;
 }
 
+int bpf_program__unpin_instance(struct bpf_program *prog, const char *path,
+                               int instance)
+{
+       int err;
+
+       err = check_path(path);
+       if (err)
+               return err;
+
+       if (prog == NULL) {
+               pr_warning("invalid program pointer\n");
+               return -EINVAL;
+       }
+
+       if (instance < 0 || instance >= prog->instances.nr) {
+               pr_warning("invalid prog instance %d of prog %s (max %d)\n",
+                          instance, prog->section_name, prog->instances.nr);
+               return -EINVAL;
+       }
+
+       err = unlink(path);
+       if (err != 0)
+               return -errno;
+       pr_debug("unpinned program '%s'\n", path);
+
+       return 0;
+}
+
 static int make_dir(const char *path)
 {
        char *cp, errmsg[STRERR_BUFSIZE];
@@ -1733,10 +1990,78 @@ int bpf_program__pin(struct bpf_program *prog, const char *path)
                return -EINVAL;
        }
 
+       if (prog->instances.nr == 1) {
+               /* don't create subdirs when pinning single instance */
+               return bpf_program__pin_instance(prog, path, 0);
+       }
+
        err = make_dir(path);
        if (err)
                return err;
 
+       for (i = 0; i < prog->instances.nr; i++) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
+               if (len < 0) {
+                       err = -EINVAL;
+                       goto err_unpin;
+               } else if (len >= PATH_MAX) {
+                       err = -ENAMETOOLONG;
+                       goto err_unpin;
+               }
+
+               err = bpf_program__pin_instance(prog, buf, i);
+               if (err)
+                       goto err_unpin;
+       }
+
+       return 0;
+
+err_unpin:
+       for (i = i - 1; i >= 0; i--) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
+               if (len < 0)
+                       continue;
+               else if (len >= PATH_MAX)
+                       continue;
+
+               bpf_program__unpin_instance(prog, buf, i);
+       }
+
+       rmdir(path);
+
+       return err;
+}
+
+int bpf_program__unpin(struct bpf_program *prog, const char *path)
+{
+       int i, err;
+
+       err = check_path(path);
+       if (err)
+               return err;
+
+       if (prog == NULL) {
+               pr_warning("invalid program pointer\n");
+               return -EINVAL;
+       }
+
+       if (prog->instances.nr <= 0) {
+               pr_warning("no instances of prog %s to pin\n",
+                          prog->section_name);
+               return -EINVAL;
+       }
+
+       if (prog->instances.nr == 1) {
+               /* don't create subdirs when pinning single instance */
+               return bpf_program__unpin_instance(prog, path, 0);
+       }
+
        for (i = 0; i < prog->instances.nr; i++) {
                char buf[PATH_MAX];
                int len;
@@ -1747,11 +2072,15 @@ int bpf_program__pin(struct bpf_program *prog, const char *path)
                else if (len >= PATH_MAX)
                        return -ENAMETOOLONG;
 
-               err = bpf_program__pin_instance(prog, buf, i);
+               err = bpf_program__unpin_instance(prog, buf, i);
                if (err)
                        return err;
        }
 
+       err = rmdir(path);
+       if (err)
+               return -errno;
+
        return 0;
 }
 
@@ -1776,12 +2105,33 @@ int bpf_map__pin(struct bpf_map *map, const char *path)
        }
 
        pr_debug("pinned map '%s'\n", path);
+
        return 0;
 }
 
-int bpf_object__pin(struct bpf_object *obj, const char *path)
+int bpf_map__unpin(struct bpf_map *map, const char *path)
+{
+       int err;
+
+       err = check_path(path);
+       if (err)
+               return err;
+
+       if (map == NULL) {
+               pr_warning("invalid map pointer\n");
+               return -EINVAL;
+       }
+
+       err = unlink(path);
+       if (err != 0)
+               return -errno;
+       pr_debug("unpinned map '%s'\n", path);
+
+       return 0;
+}
+
+int bpf_object__pin_maps(struct bpf_object *obj, const char *path)
 {
-       struct bpf_program *prog;
        struct bpf_map *map;
        int err;
 
@@ -1797,6 +2147,53 @@ int bpf_object__pin(struct bpf_object *obj, const char *path)
        if (err)
                return err;
 
+       bpf_map__for_each(map, obj) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%s", path,
+                              bpf_map__name(map));
+               if (len < 0) {
+                       err = -EINVAL;
+                       goto err_unpin_maps;
+               } else if (len >= PATH_MAX) {
+                       err = -ENAMETOOLONG;
+                       goto err_unpin_maps;
+               }
+
+               err = bpf_map__pin(map, buf);
+               if (err)
+                       goto err_unpin_maps;
+       }
+
+       return 0;
+
+err_unpin_maps:
+       while ((map = bpf_map__prev(map, obj))) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%s", path,
+                              bpf_map__name(map));
+               if (len < 0)
+                       continue;
+               else if (len >= PATH_MAX)
+                       continue;
+
+               bpf_map__unpin(map, buf);
+       }
+
+       return err;
+}
+
+int bpf_object__unpin_maps(struct bpf_object *obj, const char *path)
+{
+       struct bpf_map *map;
+       int err;
+
+       if (!obj)
+               return -ENOENT;
+
        bpf_map__for_each(map, obj) {
                char buf[PATH_MAX];
                int len;
@@ -1808,23 +2205,90 @@ int bpf_object__pin(struct bpf_object *obj, const char *path)
                else if (len >= PATH_MAX)
                        return -ENAMETOOLONG;
 
-               err = bpf_map__pin(map, buf);
+               err = bpf_map__unpin(map, buf);
                if (err)
                        return err;
        }
 
+       return 0;
+}
+
+int bpf_object__pin_programs(struct bpf_object *obj, const char *path)
+{
+       struct bpf_program *prog;
+       int err;
+
+       if (!obj)
+               return -ENOENT;
+
+       if (!obj->loaded) {
+               pr_warning("object not yet loaded; load it first\n");
+               return -ENOENT;
+       }
+
+       err = make_dir(path);
+       if (err)
+               return err;
+
        bpf_object__for_each_program(prog, obj) {
                char buf[PATH_MAX];
                int len;
 
                len = snprintf(buf, PATH_MAX, "%s/%s", path,
-                              prog->section_name);
+                              prog->pin_name);
+               if (len < 0) {
+                       err = -EINVAL;
+                       goto err_unpin_programs;
+               } else if (len >= PATH_MAX) {
+                       err = -ENAMETOOLONG;
+                       goto err_unpin_programs;
+               }
+
+               err = bpf_program__pin(prog, buf);
+               if (err)
+                       goto err_unpin_programs;
+       }
+
+       return 0;
+
+err_unpin_programs:
+       while ((prog = bpf_program__prev(prog, obj))) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%s", path,
+                              prog->pin_name);
+               if (len < 0)
+                       continue;
+               else if (len >= PATH_MAX)
+                       continue;
+
+               bpf_program__unpin(prog, buf);
+       }
+
+       return err;
+}
+
+int bpf_object__unpin_programs(struct bpf_object *obj, const char *path)
+{
+       struct bpf_program *prog;
+       int err;
+
+       if (!obj)
+               return -ENOENT;
+
+       bpf_object__for_each_program(prog, obj) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%s", path,
+                              prog->pin_name);
                if (len < 0)
                        return -EINVAL;
                else if (len >= PATH_MAX)
                        return -ENAMETOOLONG;
 
-               err = bpf_program__pin(prog, buf);
+               err = bpf_program__unpin(prog, buf);
                if (err)
                        return err;
        }
@@ -1832,6 +2296,23 @@ int bpf_object__pin(struct bpf_object *obj, const char *path)
        return 0;
 }
 
+int bpf_object__pin(struct bpf_object *obj, const char *path)
+{
+       int err;
+
+       err = bpf_object__pin_maps(obj, path);
+       if (err)
+               return err;
+
+       err = bpf_object__pin_programs(obj, path);
+       if (err) {
+               bpf_object__unpin_maps(obj, path);
+               return err;
+       }
+
+       return 0;
+}
+
 void bpf_object__close(struct bpf_object *obj)
 {
        size_t i;
@@ -1845,6 +2326,7 @@ void bpf_object__close(struct bpf_object *obj)
        bpf_object__elf_finish(obj);
        bpf_object__unload(obj);
        btf__free(obj->btf);
+       btf_ext__free(obj->btf_ext);
 
        for (i = 0; i < obj->nr_maps; i++) {
                zfree(&obj->maps[i].name);
@@ -1918,23 +2400,26 @@ void *bpf_object__priv(struct bpf_object *obj)
 }
 
 static struct bpf_program *
-__bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
+__bpf_program__iter(struct bpf_program *p, struct bpf_object *obj, bool forward)
 {
-       size_t idx;
+       size_t nr_programs = obj->nr_programs;
+       ssize_t idx;
 
-       if (!obj->programs)
+       if (!nr_programs)
                return NULL;
-       /* First handler */
-       if (prev == NULL)
-               return &obj->programs[0];
 
-       if (prev->obj != obj) {
+       if (!p)
+               /* Iter from the beginning */
+               return forward ? &obj->programs[0] :
+                       &obj->programs[nr_programs - 1];
+
+       if (p->obj != obj) {
                pr_warning("error: program handler doesn't match object\n");
                return NULL;
        }
 
-       idx = (prev - obj->programs) + 1;
-       if (idx >= obj->nr_programs)
+       idx = (p - obj->programs) + (forward ? 1 : -1);
+       if (idx >= obj->nr_programs || idx < 0)
                return NULL;
        return &obj->programs[idx];
 }
@@ -1945,7 +2430,19 @@ bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
        struct bpf_program *prog = prev;
 
        do {
-               prog = __bpf_program__next(prog, obj);
+               prog = __bpf_program__iter(prog, obj, true);
+       } while (prog && bpf_program__is_function_storage(prog, obj));
+
+       return prog;
+}
+
+struct bpf_program *
+bpf_program__prev(struct bpf_program *next, struct bpf_object *obj)
+{
+       struct bpf_program *prog = next;
+
+       do {
+               prog = __bpf_program__iter(prog, obj, false);
        } while (prog && bpf_program__is_function_storage(prog, obj));
 
        return prog;
@@ -2272,10 +2769,24 @@ void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex)
        map->map_ifindex = ifindex;
 }
 
-struct bpf_map *
-bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
+int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd)
 {
-       size_t idx;
+       if (!bpf_map_type__is_map_in_map(map->def.type)) {
+               pr_warning("error: unsupported map type\n");
+               return -EINVAL;
+       }
+       if (map->inner_map_fd != -1) {
+               pr_warning("error: inner_map_fd already specified\n");
+               return -EINVAL;
+       }
+       map->inner_map_fd = fd;
+       return 0;
+}
+
+static struct bpf_map *
+__bpf_map__iter(struct bpf_map *m, struct bpf_object *obj, int i)
+{
+       ssize_t idx;
        struct bpf_map *s, *e;
 
        if (!obj || !obj->maps)
@@ -2284,21 +2795,39 @@ bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
        s = obj->maps;
        e = obj->maps + obj->nr_maps;
 
-       if (prev == NULL)
-               return s;
-
-       if ((prev < s) || (prev >= e)) {
+       if ((m < s) || (m >= e)) {
                pr_warning("error in %s: map handler doesn't belong to object\n",
                           __func__);
                return NULL;
        }
 
-       idx = (prev - obj->maps) + 1;
-       if (idx >= obj->nr_maps)
+       idx = (m - obj->maps) + i;
+       if (idx >= obj->nr_maps || idx < 0)
                return NULL;
        return &obj->maps[idx];
 }
 
+struct bpf_map *
+bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
+{
+       if (prev == NULL)
+               return obj->maps;
+
+       return __bpf_map__iter(prev, obj, 1);
+}
+
+struct bpf_map *
+bpf_map__prev(struct bpf_map *next, struct bpf_object *obj)
+{
+       if (next == NULL) {
+               if (!obj->nr_maps)
+                       return NULL;
+               return obj->maps + obj->nr_maps - 1;
+       }
+
+       return __bpf_map__iter(next, obj, -1);
+}
+
 struct bpf_map *
 bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
 {
index 1f3468d..5f68d7b 100644 (file)
 #include <sys/types.h>  // for size_t
 #include <linux/bpf.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifndef LIBBPF_API
 #define LIBBPF_API __attribute__((visibility("default")))
 #endif
@@ -71,6 +75,13 @@ struct bpf_object *__bpf_object__open_xattr(struct bpf_object_open_attr *attr,
 LIBBPF_API struct bpf_object *bpf_object__open_buffer(void *obj_buf,
                                                      size_t obj_buf_sz,
                                                      const char *name);
+LIBBPF_API int bpf_object__pin_maps(struct bpf_object *obj, const char *path);
+LIBBPF_API int bpf_object__unpin_maps(struct bpf_object *obj,
+                                     const char *path);
+LIBBPF_API int bpf_object__pin_programs(struct bpf_object *obj,
+                                       const char *path);
+LIBBPF_API int bpf_object__unpin_programs(struct bpf_object *obj,
+                                         const char *path);
 LIBBPF_API int bpf_object__pin(struct bpf_object *object, const char *path);
 LIBBPF_API void bpf_object__close(struct bpf_object *object);
 
@@ -112,6 +123,9 @@ LIBBPF_API struct bpf_program *bpf_program__next(struct bpf_program *prog,
             (pos) != NULL;                             \
             (pos) = bpf_program__next((pos), (obj)))
 
+LIBBPF_API struct bpf_program *bpf_program__prev(struct bpf_program *prog,
+                                                struct bpf_object *obj);
+
 typedef void (*bpf_program_clear_priv_t)(struct bpf_program *,
                                         void *);
 
@@ -131,7 +145,11 @@ LIBBPF_API int bpf_program__fd(struct bpf_program *prog);
 LIBBPF_API int bpf_program__pin_instance(struct bpf_program *prog,
                                         const char *path,
                                         int instance);
+LIBBPF_API int bpf_program__unpin_instance(struct bpf_program *prog,
+                                          const char *path,
+                                          int instance);
 LIBBPF_API int bpf_program__pin(struct bpf_program *prog, const char *path);
+LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path);
 LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
 
 struct bpf_insn;
@@ -260,6 +278,9 @@ bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
             (pos) != NULL;                             \
             (pos) = bpf_map__next((pos), (obj)))
 
+LIBBPF_API struct bpf_map *
+bpf_map__prev(struct bpf_map *map, struct bpf_object *obj);
+
 LIBBPF_API int bpf_map__fd(struct bpf_map *map);
 LIBBPF_API const struct bpf_map_def *bpf_map__def(struct bpf_map *map);
 LIBBPF_API const char *bpf_map__name(struct bpf_map *map);
@@ -274,6 +295,9 @@ LIBBPF_API int bpf_map__reuse_fd(struct bpf_map *map, int fd);
 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);
+LIBBPF_API int bpf_map__unpin(struct bpf_map *map, const char *path);
+
+LIBBPF_API int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd);
 
 LIBBPF_API long libbpf_get_error(const void *ptr);
 
@@ -317,4 +341,22 @@ int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
                        libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie);
 int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
                         libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie);
+
+struct bpf_prog_linfo;
+struct bpf_prog_info;
+
+LIBBPF_API void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo);
+LIBBPF_API struct bpf_prog_linfo *
+bpf_prog_linfo__new(const struct bpf_prog_info *info);
+LIBBPF_API const struct bpf_line_info *
+bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
+                               __u64 addr, __u32 func_idx, __u32 nr_skip);
+LIBBPF_API const struct bpf_line_info *
+bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
+                     __u32 insn_off, __u32 nr_skip);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* __LIBBPF_LIBBPF_H */
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
new file mode 100644 (file)
index 0000000..cd02cd4
--- /dev/null
@@ -0,0 +1,126 @@
+LIBBPF_0.0.1 {
+       global:
+               bpf_btf_get_fd_by_id;
+               bpf_create_map;
+               bpf_create_map_in_map;
+               bpf_create_map_in_map_node;
+               bpf_create_map_name;
+               bpf_create_map_node;
+               bpf_create_map_xattr;
+               bpf_load_btf;
+               bpf_load_program;
+               bpf_load_program_xattr;
+               bpf_map__btf_key_type_id;
+               bpf_map__btf_value_type_id;
+               bpf_map__def;
+               bpf_map__fd;
+               bpf_map__is_offload_neutral;
+               bpf_map__name;
+               bpf_map__next;
+               bpf_map__pin;
+               bpf_map__prev;
+               bpf_map__priv;
+               bpf_map__reuse_fd;
+               bpf_map__set_ifindex;
+               bpf_map__set_inner_map_fd;
+               bpf_map__set_priv;
+               bpf_map__unpin;
+               bpf_map_delete_elem;
+               bpf_map_get_fd_by_id;
+               bpf_map_get_next_id;
+               bpf_map_get_next_key;
+               bpf_map_lookup_and_delete_elem;
+               bpf_map_lookup_elem;
+               bpf_map_update_elem;
+               bpf_obj_get;
+               bpf_obj_get_info_by_fd;
+               bpf_obj_pin;
+               bpf_object__btf_fd;
+               bpf_object__close;
+               bpf_object__find_map_by_name;
+               bpf_object__find_map_by_offset;
+               bpf_object__find_program_by_title;
+               bpf_object__kversion;
+               bpf_object__load;
+               bpf_object__name;
+               bpf_object__next;
+               bpf_object__open;
+               bpf_object__open_buffer;
+               bpf_object__open_xattr;
+               bpf_object__pin;
+               bpf_object__pin_maps;
+               bpf_object__pin_programs;
+               bpf_object__priv;
+               bpf_object__set_priv;
+               bpf_object__unload;
+               bpf_object__unpin_maps;
+               bpf_object__unpin_programs;
+               bpf_perf_event_read_simple;
+               bpf_prog_attach;
+               bpf_prog_detach;
+               bpf_prog_detach2;
+               bpf_prog_get_fd_by_id;
+               bpf_prog_get_next_id;
+               bpf_prog_load;
+               bpf_prog_load_xattr;
+               bpf_prog_query;
+               bpf_prog_test_run;
+               bpf_prog_test_run_xattr;
+               bpf_program__fd;
+               bpf_program__is_kprobe;
+               bpf_program__is_perf_event;
+               bpf_program__is_raw_tracepoint;
+               bpf_program__is_sched_act;
+               bpf_program__is_sched_cls;
+               bpf_program__is_socket_filter;
+               bpf_program__is_tracepoint;
+               bpf_program__is_xdp;
+               bpf_program__load;
+               bpf_program__next;
+               bpf_program__nth_fd;
+               bpf_program__pin;
+               bpf_program__pin_instance;
+               bpf_program__prev;
+               bpf_program__priv;
+               bpf_program__set_expected_attach_type;
+               bpf_program__set_ifindex;
+               bpf_program__set_kprobe;
+               bpf_program__set_perf_event;
+               bpf_program__set_prep;
+               bpf_program__set_priv;
+               bpf_program__set_raw_tracepoint;
+               bpf_program__set_sched_act;
+               bpf_program__set_sched_cls;
+               bpf_program__set_socket_filter;
+               bpf_program__set_tracepoint;
+               bpf_program__set_type;
+               bpf_program__set_xdp;
+               bpf_program__title;
+               bpf_program__unload;
+               bpf_program__unpin;
+               bpf_program__unpin_instance;
+               bpf_prog_linfo__free;
+               bpf_prog_linfo__new;
+               bpf_prog_linfo__lfind_addr_func;
+               bpf_prog_linfo__lfind;
+               bpf_raw_tracepoint_open;
+               bpf_set_link_xdp_fd;
+               bpf_task_fd_query;
+               bpf_verify_program;
+               btf__fd;
+               btf__find_by_name;
+               btf__free;
+               btf__get_from_id;
+               btf__name_by_offset;
+               btf__new;
+               btf__resolve_size;
+               btf__resolve_type;
+               btf__type_by_id;
+               libbpf_attach_type_by_name;
+               libbpf_get_error;
+               libbpf_prog_type_by_name;
+               libbpf_set_print;
+               libbpf_strerror;
+       local:
+               *;
+};
index d83b17f..4343e40 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 2017 Nicira, Inc.
  */
 
+#undef _GNU_SOURCE
 #include <stdio.h>
 #include <string.h>
 
diff --git a/tools/lib/bpf/test_libbpf.cpp b/tools/lib/bpf/test_libbpf.cpp
new file mode 100644 (file)
index 0000000..abf3fc2
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#include "libbpf.h"
+#include "bpf.h"
+#include "btf.h"
+
+/* do nothing, just make sure we can link successfully */
+
+int main(int argc, char *argv[])
+{
+    /* libbpf.h */
+    libbpf_set_print(NULL, NULL, NULL);
+
+    /* bpf.h */
+    bpf_prog_get_fd_by_id(0);
+
+    /* btf.h */
+    btf__new(NULL, 0, NULL);
+}
index 6dbb9fa..b8f3cca 100644 (file)
@@ -31,6 +31,8 @@
 #include "elf.h"
 #include "warn.h"
 
+#define MAX_NAME_LEN 128
+
 struct section *find_section_by_name(struct elf *elf, const char *name)
 {
        struct section *sec;
@@ -298,6 +300,8 @@ static int read_symbols(struct elf *elf)
        /* Create parent/child links for any cold subfunctions */
        list_for_each_entry(sec, &elf->sections, list) {
                list_for_each_entry(sym, &sec->symbol_list, list) {
+                       char pname[MAX_NAME_LEN + 1];
+                       size_t pnamelen;
                        if (sym->type != STT_FUNC)
                                continue;
                        sym->pfunc = sym->cfunc = sym;
@@ -305,14 +309,21 @@ static int read_symbols(struct elf *elf)
                        if (!coldstr)
                                continue;
 
-                       coldstr[0] = '\0';
-                       pfunc = find_symbol_by_name(elf, sym->name);
-                       coldstr[0] = '.';
+                       pnamelen = coldstr - sym->name;
+                       if (pnamelen > MAX_NAME_LEN) {
+                               WARN("%s(): parent function name exceeds maximum length of %d characters",
+                                    sym->name, MAX_NAME_LEN);
+                               return -1;
+                       }
+
+                       strncpy(pname, sym->name, pnamelen);
+                       pname[pnamelen] = '\0';
+                       pfunc = find_symbol_by_name(elf, pname);
 
                        if (!pfunc) {
                                WARN("%s(): can't find parent function",
                                     sym->name);
-                               goto err;
+                               return -1;
                        }
 
                        sym->pfunc = pfunc;
index e30d20f..a0e8c23 100644 (file)
@@ -299,6 +299,11 @@ ifndef NO_BIONIC
   endif
 endif
 
+ifeq ($(feature-get_current_dir_name), 1)
+  CFLAGS += -DHAVE_GET_CURRENT_DIR_NAME
+endif
+
+
 ifdef NO_LIBELF
   NO_DWARF := 1
   NO_DEMANGLE := 1
index 3794066..efd0157 100644 (file)
@@ -9,7 +9,7 @@ size=112
 config=0
 sample_period=*
 sample_type=263
-read_format=0
+read_format=0|4
 disabled=1
 inherit=1
 pinned=0
index 5d2a7fd..eae59ad 100644 (file)
@@ -31,6 +31,7 @@ static size_t ioctl__scnprintf_tty_cmd(int nr, int dir, char *bf, size_t size)
        "TCSETSW2", "TCSETSF2", "TIOCGRS48", "TIOCSRS485", "TIOCGPTN", "TIOCSPTLCK",
        "TIOCGDEV", "TCSETX", "TCSETXF", "TCSETXW", "TIOCSIG", "TIOCVHANGUP", "TIOCGPKT",
        "TIOCGPTLCK", [_IOC_NR(TIOCGEXCL)] = "TIOCGEXCL", "TIOCGPTPEER",
+       "TIOCGISO7816", "TIOCSISO7816",
        [_IOC_NR(FIONCLEX)] = "FIONCLEX", "FIOCLEX", "FIOASYNC", "TIOCSERCONFIG",
        "TIOCSERGWILD", "TIOCSERSWILD", "TIOCGLCKTRMIOS", "TIOCSLCKTRMIOS",
        "TIOCSERGSTRUCT", "TIOCSERGETLSR", "TIOCSERGETMULTI", "TIOCSERSETMULTI",
index ecd9f9c..b7bf201 100644 (file)
@@ -10,6 +10,7 @@ libperf-y += evlist.o
 libperf-y += evsel.o
 libperf-y += evsel_fprintf.o
 libperf-y += find_bit.o
+libperf-y += get_current_dir_name.o
 libperf-y += kallsyms.o
 libperf-y += levenshtein.o
 libperf-y += llvm-utils.o
index d37bb15..dbc0466 100644 (file)
@@ -1092,7 +1092,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
                attr->exclude_user   = 1;
        }
 
-       if (evsel->own_cpus)
+       if (evsel->own_cpus || evsel->unit)
                evsel->attr.read_format |= PERF_FORMAT_ID;
 
        /*
diff --git a/tools/perf/util/get_current_dir_name.c b/tools/perf/util/get_current_dir_name.c
new file mode 100644 (file)
index 0000000..267aa60
--- /dev/null
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+//
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+#include "util.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdlib.h>
+
+/* Android's 'bionic' library, for one, doesn't have this */
+
+char *get_current_dir_name(void)
+{
+       char pwd[PATH_MAX];
+
+       return getcwd(pwd, sizeof(pwd)) == NULL ? NULL : strdup(pwd);
+}
+#endif // HAVE_GET_CURRENT_DIR_NAME
index cf8bd12..aed170b 100644 (file)
@@ -18,6 +18,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <asm/bug.h>
 
 struct namespaces *namespaces__new(struct namespaces_event *event)
 {
@@ -186,6 +187,7 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
        char curpath[PATH_MAX];
        int oldns = -1;
        int newns = -1;
+       char *oldcwd = NULL;
 
        if (nc == NULL)
                return;
@@ -199,9 +201,13 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
        if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
                return;
 
+       oldcwd = get_current_dir_name();
+       if (!oldcwd)
+               return;
+
        oldns = open(curpath, O_RDONLY);
        if (oldns < 0)
-               return;
+               goto errout;
 
        newns = open(nsi->mntns_path, O_RDONLY);
        if (newns < 0)
@@ -210,11 +216,13 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
        if (setns(newns, CLONE_NEWNS) < 0)
                goto errout;
 
+       nc->oldcwd = oldcwd;
        nc->oldns = oldns;
        nc->newns = newns;
        return;
 
 errout:
+       free(oldcwd);
        if (oldns > -1)
                close(oldns);
        if (newns > -1)
@@ -223,11 +231,16 @@ errout:
 
 void nsinfo__mountns_exit(struct nscookie *nc)
 {
-       if (nc == NULL || nc->oldns == -1 || nc->newns == -1)
+       if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd)
                return;
 
        setns(nc->oldns, CLONE_NEWNS);
 
+       if (nc->oldcwd) {
+               WARN_ON_ONCE(chdir(nc->oldcwd));
+               zfree(&nc->oldcwd);
+       }
+
        if (nc->oldns > -1) {
                close(nc->oldns);
                nc->oldns = -1;
index cae1a9a..d5f46c0 100644 (file)
@@ -38,6 +38,7 @@ struct nsinfo {
 struct nscookie {
        int                     oldns;
        int                     newns;
+       char                    *oldcwd;
 };
 
 int nsinfo__init(struct nsinfo *nsi);
index 14508ee..ece040b 100644 (file)
@@ -59,6 +59,10 @@ int fetch_kernel_version(unsigned int *puint,
 
 const char *perf_tip(const char *dirpath);
 
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+char *get_current_dir_name(void);
+#endif
+
 #ifndef HAVE_SCHED_GETCPU_SUPPORT
 int sched_getcpu(void);
 #endif
index 1dd5f4f..db66a95 100644 (file)
@@ -129,7 +129,7 @@ WARNINGS += $(call cc-supports,-Wno-pointer-sign)
 WARNINGS += $(call cc-supports,-Wdeclaration-after-statement)
 WARNINGS += -Wshadow
 
-CFLAGS += -DVERSION=\"$(VERSION)\" -DPACKAGE=\"$(PACKAGE)\" \
+override CFLAGS += -DVERSION=\"$(VERSION)\" -DPACKAGE=\"$(PACKAGE)\" \
                -DPACKAGE_BUGREPORT=\"$(PACKAGE_BUGREPORT)\" -D_GNU_SOURCE
 
 UTIL_OBJS =  utils/helpers/amd.o utils/helpers/msr.o \
@@ -156,12 +156,12 @@ LIB_SRC =         lib/cpufreq.c lib/cpupower.c lib/cpuidle.c
 LIB_OBJS =     lib/cpufreq.o lib/cpupower.o lib/cpuidle.o
 LIB_OBJS :=    $(addprefix $(OUTPUT),$(LIB_OBJS))
 
-CFLAGS +=      -pipe
+override CFLAGS +=     -pipe
 
 ifeq ($(strip $(NLS)),true)
        INSTALL_NLS += install-gmo
        COMPILE_NLS += create-gmo
-       CFLAGS += -DNLS
+       override CFLAGS += -DNLS
 endif
 
 ifeq ($(strip $(CPUFREQ_BENCH)),true)
@@ -175,7 +175,7 @@ ifeq ($(strip $(STATIC)),true)
         UTIL_SRC += $(LIB_SRC)
 endif
 
-CFLAGS += $(WARNINGS)
+override CFLAGS += $(WARNINGS)
 
 ifeq ($(strip $(V)),false)
        QUIET=@
@@ -188,10 +188,10 @@ export QUIET ECHO
 
 # if DEBUG is enabled, then we do not strip or optimize
 ifeq ($(strip $(DEBUG)),true)
-       CFLAGS += -O1 -g -DDEBUG
+       override CFLAGS += -O1 -g -DDEBUG
        STRIPCMD = /bin/true -Since_we_are_debugging
 else
-       CFLAGS += $(OPTIMIZATION) -fomit-frame-pointer
+       override CFLAGS += $(OPTIMIZATION) -fomit-frame-pointer
        STRIPCMD = $(STRIP) -s --remove-section=.note --remove-section=.comment
 endif
 
index d79ab16..f68b4bc 100644 (file)
@@ -9,7 +9,7 @@ endif
 ifeq ($(strip $(STATIC)),true)
 LIBS = -L../ -L$(OUTPUT) -lm
 OBJS = $(OUTPUT)main.o $(OUTPUT)parse.o $(OUTPUT)system.o $(OUTPUT)benchmark.o \
-       $(OUTPUT)../lib/cpufreq.o $(OUTPUT)../lib/sysfs.o
+       $(OUTPUT)../lib/cpufreq.o $(OUTPUT)../lib/cpupower.o
 else
 LIBS = -L../ -L$(OUTPUT) -lm -lcpupower
 OBJS = $(OUTPUT)main.o $(OUTPUT)parse.o $(OUTPUT)system.o $(OUTPUT)benchmark.o
index 59af84b..b1b6c43 100644 (file)
@@ -13,10 +13,10 @@ INSTALL = /usr/bin/install
 default: all
 
 $(OUTPUT)centrino-decode: ../i386/centrino-decode.c
-       $(CC) $(CFLAGS) -o $@ $<
+       $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $<
 
 $(OUTPUT)powernow-k8-decode: ../i386/powernow-k8-decode.c
-       $(CC) $(CFLAGS) -o $@ $<
+       $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $<
 
 all: $(OUTPUT)centrino-decode $(OUTPUT)powernow-k8-decode
 
index 1b993fe..0c0f3e3 100644 (file)
@@ -28,7 +28,7 @@ static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
 
        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
                         cpu, fname);
-       return sysfs_read_file(path, buf, buflen);
+       return cpupower_read_sysfs(path, buf, buflen);
 }
 
 /* helper function to write a new value to a /sys file */
index 9bd4c76..852d254 100644 (file)
@@ -319,7 +319,7 @@ static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
 
        snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
 
-       return sysfs_read_file(path, buf, buflen);
+       return cpupower_read_sysfs(path, buf, buflen);
 }
 
 
index 9c395ec..9711d62 100644 (file)
@@ -15,7 +15,7 @@
 #include "cpupower.h"
 #include "cpupower_intern.h"
 
-unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
+unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen)
 {
        int fd;
        ssize_t numread;
@@ -95,7 +95,7 @@ static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *re
 
        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
                         cpu, fname);
-       if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0)
+       if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
                return -1;
        *result = strtol(linebuf, &endp, 0);
        if (endp == linebuf || errno == ERANGE)
index 92affdf..4887c76 100644 (file)
@@ -3,4 +3,4 @@
 #define MAX_LINE_LEN 4096
 #define SYSFS_PATH_MAX 255
 
-unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen);
+unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen);
index 9527d47..6c16ac3 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/workqueue.h>
 #include <linux/libnvdimm.h>
+#include <linux/genalloc.h>
 #include <linux/vmalloc.h>
 #include <linux/device.h>
 #include <linux/module.h>
@@ -140,8 +141,8 @@ static u32 handle[] = {
        [6] = NFIT_DIMM_HANDLE(1, 0, 0, 0, 1),
 };
 
-static unsigned long dimm_fail_cmd_flags[NUM_DCR];
-static int dimm_fail_cmd_code[NUM_DCR];
+static unsigned long dimm_fail_cmd_flags[ARRAY_SIZE(handle)];
+static int dimm_fail_cmd_code[ARRAY_SIZE(handle)];
 
 static const struct nd_intel_smart smart_def = {
        .flags = ND_INTEL_SMART_HEALTH_VALID
@@ -205,7 +206,7 @@ struct nfit_test {
                unsigned long deadline;
                spinlock_t lock;
        } ars_state;
-       struct device *dimm_dev[NUM_DCR];
+       struct device *dimm_dev[ARRAY_SIZE(handle)];
        struct nd_intel_smart *smart;
        struct nd_intel_smart_threshold *smart_threshold;
        struct badrange badrange;
@@ -215,6 +216,8 @@ struct nfit_test {
 
 static struct workqueue_struct *nfit_wq;
 
+static struct gen_pool *nfit_pool;
+
 static struct nfit_test *to_nfit_test(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -1132,6 +1135,9 @@ static void release_nfit_res(void *data)
        list_del(&nfit_res->list);
        spin_unlock(&nfit_test_lock);
 
+       if (resource_size(&nfit_res->res) >= DIMM_SIZE)
+               gen_pool_free(nfit_pool, nfit_res->res.start,
+                               resource_size(&nfit_res->res));
        vfree(nfit_res->buf);
        kfree(nfit_res);
 }
@@ -1144,7 +1150,7 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma,
                        GFP_KERNEL);
        int rc;
 
-       if (!buf || !nfit_res)
+       if (!buf || !nfit_res || !*dma)
                goto err;
        rc = devm_add_action(dev, release_nfit_res, nfit_res);
        if (rc)
@@ -1164,6 +1170,8 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma,
 
        return nfit_res->buf;
  err:
+       if (*dma && size >= DIMM_SIZE)
+               gen_pool_free(nfit_pool, *dma, size);
        if (buf)
                vfree(buf);
        kfree(nfit_res);
@@ -1172,9 +1180,16 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma,
 
 static void *test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma)
 {
+       struct genpool_data_align data = {
+               .align = SZ_128M,
+       };
        void *buf = vmalloc(size);
 
-       *dma = (unsigned long) buf;
+       if (size >= DIMM_SIZE)
+               *dma = gen_pool_alloc_algo(nfit_pool, size,
+                               gen_pool_first_fit_align, &data);
+       else
+               *dma = (unsigned long) buf;
        return __test_alloc(t, size, dma, buf);
 }
 
@@ -2680,7 +2695,7 @@ static int nfit_test_probe(struct platform_device *pdev)
                u32 nfit_handle = __to_nfit_memdev(nfit_mem)->device_handle;
                int i;
 
-               for (i = 0; i < NUM_DCR; i++)
+               for (i = 0; i < ARRAY_SIZE(handle); i++)
                        if (nfit_handle == handle[i])
                                dev_set_drvdata(nfit_test->dimm_dev[i],
                                                nfit_mem);
@@ -2839,6 +2854,17 @@ static __init int nfit_test_init(void)
                goto err_register;
        }
 
+       nfit_pool = gen_pool_create(ilog2(SZ_4M), NUMA_NO_NODE);
+       if (!nfit_pool) {
+               rc = -ENOMEM;
+               goto err_register;
+       }
+
+       if (gen_pool_add(nfit_pool, SZ_4G, SZ_4G, NUMA_NO_NODE)) {
+               rc = -ENOMEM;
+               goto err_register;
+       }
+
        for (i = 0; i < NUM_NFITS; i++) {
                struct nfit_test *nfit_test;
                struct platform_device *pdev;
@@ -2894,6 +2920,9 @@ static __init int nfit_test_init(void)
        return 0;
 
  err_register:
+       if (nfit_pool)
+               gen_pool_destroy(nfit_pool);
+
        destroy_workqueue(nfit_wq);
        for (i = 0; i < NUM_NFITS; i++)
                if (instances[i])
@@ -2917,6 +2946,8 @@ static __exit void nfit_test_exit(void)
        platform_driver_unregister(&nfit_test_driver);
        nfit_test_teardown();
 
+       gen_pool_destroy(nfit_pool);
+
        for (i = 0; i < NUM_NFITS; i++)
                put_device(&instances[i]->pdev.dev);
        class_destroy(nfit_test_dimm);
index acf1afa..397d6b6 100644 (file)
@@ -7,6 +7,7 @@ LDLIBS+= -lpthread -lurcu
 TARGETS = main idr-test multiorder xarray
 CORE_OFILES := xarray.o radix-tree.o idr.o linux.o test.o find_bit.o bitmap.o
 OFILES = main.o $(CORE_OFILES) regression1.o regression2.o regression3.o \
+        regression4.o \
         tag_check.o multiorder.o idr-test.o iteration_check.o benchmark.o
 
 ifndef SHIFT
index 77a44c5..7a22d6e 100644 (file)
@@ -308,6 +308,7 @@ int main(int argc, char **argv)
        regression1_test();
        regression2_test();
        regression3_test();
+       regression4_test();
        iteration_test(0, 10 + 90 * long_run);
        iteration_test(7, 10 + 90 * long_run);
        single_thread_tests(long_run);
index 3c8a158..135145a 100644 (file)
@@ -5,5 +5,6 @@
 void regression1_test(void);
 void regression2_test(void);
 void regression3_test(void);
+void regression4_test(void);
 
 #endif
diff --git a/tools/testing/radix-tree/regression4.c b/tools/testing/radix-tree/regression4.c
new file mode 100644 (file)
index 0000000..cf4e5ab
--- /dev/null
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/slab.h>
+#include <linux/radix-tree.h>
+#include <linux/rcupdate.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "regression.h"
+
+static pthread_barrier_t worker_barrier;
+static int obj0, obj1;
+static RADIX_TREE(mt_tree, GFP_KERNEL);
+
+static void *reader_fn(void *arg)
+{
+       int i;
+       void *entry;
+
+       rcu_register_thread();
+       pthread_barrier_wait(&worker_barrier);
+
+       for (i = 0; i < 1000000; i++) {
+               rcu_read_lock();
+               entry = radix_tree_lookup(&mt_tree, 0);
+               rcu_read_unlock();
+               if (entry != &obj0) {
+                       printf("iteration %d bad entry = %p\n", i, entry);
+                       abort();
+               }
+       }
+
+       rcu_unregister_thread();
+
+       return NULL;
+}
+
+static void *writer_fn(void *arg)
+{
+       int i;
+
+       rcu_register_thread();
+       pthread_barrier_wait(&worker_barrier);
+
+       for (i = 0; i < 1000000; i++) {
+               radix_tree_insert(&mt_tree, 1, &obj1);
+               radix_tree_delete(&mt_tree, 1);
+       }
+
+       rcu_unregister_thread();
+
+       return NULL;
+}
+
+void regression4_test(void)
+{
+       pthread_t reader, writer;
+
+       printv(1, "regression test 4 starting\n");
+
+       radix_tree_insert(&mt_tree, 0, &obj0);
+       pthread_barrier_init(&worker_barrier, NULL, 2);
+
+       if (pthread_create(&reader, NULL, reader_fn, NULL) ||
+           pthread_create(&writer, NULL, writer_fn, NULL)) {
+               perror("pthread_create");
+               exit(1);
+       }
+
+       if (pthread_join(reader, NULL) || pthread_join(writer, NULL)) {
+               perror("pthread_join");
+               exit(1);
+       }
+
+       printv(1, "regression test 4 passed\n");
+}
index f1fe492..24b9934 100644 (file)
@@ -24,6 +24,8 @@ TARGETS += memory-hotplug
 TARGETS += mount
 TARGETS += mqueue
 TARGETS += net
+TARGETS += netfilter
+TARGETS += networking/timestamping
 TARGETS += nsfs
 TARGETS += powerpc
 TARGETS += proc
index 1b799e3..4a97850 100644 (file)
@@ -27,3 +27,4 @@ test_flow_dissector
 flow_dissector_load
 test_netcnt
 test_section_names
+test_tcpnotify_user
index 4a54236..73aa6d8 100644 (file)
@@ -24,12 +24,13 @@ 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_netcnt test_tcpnotify_user
 
 TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
        test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o     \
        sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o \
        test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \
+       test_tcpnotify_kern.o \
        sample_map_ret0.o test_tcpbpf_kern.o test_stacktrace_build_id.o \
        sockmap_tcp_msg_prog.o connect4_prog.o connect6_prog.o test_adjust_tail.o \
        test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o \
@@ -38,7 +39,7 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test
        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_sk_lookup_kern.o test_xdp_vlan.o test_queue_map.o test_stack_map.o \
-       xdp_dummy.o
+       xdp_dummy.o test_map_in_map.o
 
 # Order correspond to 'make run_tests' order
 TEST_PROGS := test_kmod.sh \
@@ -75,6 +76,7 @@ $(OUTPUT)/test_sock_addr: cgroup_helpers.c
 $(OUTPUT)/test_socket_cookie: cgroup_helpers.c
 $(OUTPUT)/test_sockmap: cgroup_helpers.c
 $(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c
+$(OUTPUT)/test_tcpnotify_user: cgroup_helpers.c trace_helpers.c
 $(OUTPUT)/test_progs: trace_helpers.c
 $(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c
 $(OUTPUT)/test_cgroup_storage: cgroup_helpers.c
@@ -125,7 +127,14 @@ $(OUTPUT)/test_stack_map.o: test_queue_stack_map.h
 BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help 2>&1 | grep dwarfris)
 BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help 2>&1 | grep BTF)
 BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --help 2>&1 | grep -i 'usage.*llvm')
+BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \
+                         $(CLANG) -target bpf -O2 -g -c -x c - -o ./llvm_btf_verify.o; \
+                         readelf -S ./llvm_btf_verify.o | grep BTF; \
+                         /bin/rm -f ./llvm_btf_verify.o)
 
+ifneq ($(BTF_LLVM_PROBE),)
+       CLANG_FLAGS += -g
+else
 ifneq ($(BTF_LLC_PROBE),)
 ifneq ($(BTF_PAHOLE_PROBE),)
 ifneq ($(BTF_OBJCOPY_PROBE),)
@@ -135,6 +144,17 @@ ifneq ($(BTF_OBJCOPY_PROBE),)
 endif
 endif
 endif
+endif
+
+# Have one program compiled without "-target bpf" to test whether libbpf loads
+# it successfully
+$(OUTPUT)/test_xdp.o: test_xdp.c
+       $(CLANG) $(CLANG_FLAGS) \
+               -O2 -emit-llvm -c $< -o - | \
+       $(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
+ifeq ($(DWARF2BTF),y)
+       $(BTF_PAHOLE) -J $@
+endif
 
 $(OUTPUT)/%.o: %.c
        $(CLANG) $(CLANG_FLAGS) \
index 107350a..284660f 100644 (file)
@@ -70,18 +70,18 @@ static __always_inline void *bpf_flow_dissect_get_header(struct __sk_buff *skb,
 {
        void *data_end = (void *)(long)skb->data_end;
        void *data = (void *)(long)skb->data;
-       __u16 nhoff = skb->flow_keys->nhoff;
+       __u16 thoff = skb->flow_keys->thoff;
        __u8 *hdr;
 
        /* Verifies this variable offset does not overflow */
-       if (nhoff > (USHRT_MAX - hdr_size))
+       if (thoff > (USHRT_MAX - hdr_size))
                return NULL;
 
-       hdr = data + nhoff;
+       hdr = data + thoff;
        if (hdr + hdr_size <= data_end)
                return hdr;
 
-       if (bpf_skb_load_bytes(skb, nhoff, buffer, hdr_size))
+       if (bpf_skb_load_bytes(skb, thoff, buffer, hdr_size))
                return NULL;
 
        return buffer;
@@ -116,7 +116,7 @@ static __always_inline int parse_eth_proto(struct __sk_buff *skb, __be16 proto)
        return BPF_DROP;
 }
 
-SEC("dissect")
+SEC("flow_dissector")
 int _dissect(struct __sk_buff *skb)
 {
        if (!skb->vlan_present)
@@ -158,13 +158,13 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
                        /* Only inspect standard GRE packets with version 0 */
                        return BPF_OK;
 
-               keys->nhoff += sizeof(*gre); /* Step over GRE Flags and Proto */
+               keys->thoff += sizeof(*gre); /* Step over GRE Flags and Proto */
                if (GRE_IS_CSUM(gre->flags))
-                       keys->nhoff += 4; /* Step over chksum and Padding */
+                       keys->thoff += 4; /* Step over chksum and Padding */
                if (GRE_IS_KEY(gre->flags))
-                       keys->nhoff += 4; /* Step over key */
+                       keys->thoff += 4; /* Step over key */
                if (GRE_IS_SEQ(gre->flags))
-                       keys->nhoff += 4; /* Step over sequence number */
+                       keys->thoff += 4; /* Step over sequence number */
 
                keys->is_encap = true;
 
@@ -174,7 +174,7 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
                        if (!eth)
                                return BPF_DROP;
 
-                       keys->nhoff += sizeof(*eth);
+                       keys->thoff += sizeof(*eth);
 
                        return parse_eth_proto(skb, eth->h_proto);
                } else {
@@ -191,7 +191,6 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
                if ((__u8 *)tcp + (tcp->doff << 2) > data_end)
                        return BPF_DROP;
 
-               keys->thoff = keys->nhoff;
                keys->sport = tcp->source;
                keys->dport = tcp->dest;
                return BPF_OK;
@@ -201,7 +200,6 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
                if (!udp)
                        return BPF_DROP;
 
-               keys->thoff = keys->nhoff;
                keys->sport = udp->source;
                keys->dport = udp->dest;
                return BPF_OK;
@@ -252,8 +250,8 @@ PROG(IP)(struct __sk_buff *skb)
        keys->ipv4_src = iph->saddr;
        keys->ipv4_dst = iph->daddr;
 
-       keys->nhoff += iph->ihl << 2;
-       if (data + keys->nhoff > data_end)
+       keys->thoff += iph->ihl << 2;
+       if (data + keys->thoff > data_end)
                return BPF_DROP;
 
        if (iph->frag_off & bpf_htons(IP_MF | IP_OFFSET)) {
@@ -285,7 +283,7 @@ PROG(IPV6)(struct __sk_buff *skb)
        keys->addr_proto = ETH_P_IPV6;
        memcpy(&keys->ipv6_src, &ip6h->saddr, 2*sizeof(ip6h->saddr));
 
-       keys->nhoff += sizeof(struct ipv6hdr);
+       keys->thoff += sizeof(struct ipv6hdr);
 
        return parse_ipv6_proto(skb, ip6h->nexthdr);
 }
@@ -301,7 +299,7 @@ PROG(IPV6OP)(struct __sk_buff *skb)
        /* hlen is in 8-octets and does not include the first 8 bytes
         * of the header
         */
-       skb->flow_keys->nhoff += (1 + ip6h->hdrlen) << 3;
+       skb->flow_keys->thoff += (1 + ip6h->hdrlen) << 3;
 
        return parse_ipv6_proto(skb, ip6h->nexthdr);
 }
@@ -315,7 +313,7 @@ PROG(IPV6FR)(struct __sk_buff *skb)
        if (!fragh)
                return BPF_DROP;
 
-       keys->nhoff += sizeof(*fragh);
+       keys->thoff += sizeof(*fragh);
        keys->is_frag = true;
        if (!(fragh->frag_off & bpf_htons(IP6_OFFSET)))
                keys->is_first_frag = true;
@@ -341,7 +339,7 @@ PROG(VLAN)(struct __sk_buff *skb)
        __be16 proto;
 
        /* Peek back to see if single or double-tagging */
-       if (bpf_skb_load_bytes(skb, keys->nhoff - sizeof(proto), &proto,
+       if (bpf_skb_load_bytes(skb, keys->thoff - sizeof(proto), &proto,
                               sizeof(proto)))
                return BPF_DROP;
 
@@ -354,14 +352,14 @@ PROG(VLAN)(struct __sk_buff *skb)
                if (vlan->h_vlan_encapsulated_proto != bpf_htons(ETH_P_8021Q))
                        return BPF_DROP;
 
-               keys->nhoff += sizeof(*vlan);
+               keys->thoff += sizeof(*vlan);
        }
 
        vlan = bpf_flow_dissect_get_header(skb, sizeof(*vlan), &_vlan);
        if (!vlan)
                return BPF_DROP;
 
-       keys->nhoff += sizeof(*vlan);
+       keys->thoff += sizeof(*vlan);
        /* Only allow 8021AD + 8021Q double tagging and no triple tagging.*/
        if (vlan->h_vlan_encapsulated_proto == bpf_htons(ETH_P_8021AD) ||
            vlan->h_vlan_encapsulated_proto == bpf_htons(ETH_P_8021Q))
index 686e57c..6c77cf7 100644 (file)
@@ -113,6 +113,8 @@ static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) =
        (void *) BPF_FUNC_msg_pull_data;
 static int (*bpf_msg_push_data)(void *ctx, int start, int end, int flags) =
        (void *) BPF_FUNC_msg_push_data;
+static int (*bpf_msg_pop_data)(void *ctx, int start, int cut, int flags) =
+       (void *) BPF_FUNC_msg_pop_data;
 static int (*bpf_bind)(void *ctx, void *addr, int addr_len) =
        (void *) BPF_FUNC_bind;
 static int (*bpf_xdp_adjust_tail)(void *ctx, int offset) =
@@ -154,12 +156,12 @@ static unsigned long long (*bpf_skb_ancestor_cgroup_id)(void *ctx, int level) =
        (void *) BPF_FUNC_skb_ancestor_cgroup_id;
 static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx,
                                             struct bpf_sock_tuple *tuple,
-                                            int size, unsigned int netns_id,
+                                            int size, unsigned long long netns_id,
                                             unsigned long long flags) =
        (void *) BPF_FUNC_sk_lookup_tcp;
 static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx,
                                             struct bpf_sock_tuple *tuple,
-                                            int size, unsigned int netns_id,
+                                            int size, unsigned long long netns_id,
                                             unsigned long long flags) =
        (void *) BPF_FUNC_sk_lookup_udp;
 static int (*bpf_sk_release)(struct bpf_sock *sk) =
@@ -168,6 +170,8 @@ static int (*bpf_skb_vlan_push)(void *ctx, __be16 vlan_proto, __u16 vlan_tci) =
        (void *) BPF_FUNC_skb_vlan_push;
 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;
 
 /* llvm builtin functions that eBPF C program may use to
  * emit BPF_LD_ABS and BPF_LD_IND instructions
index 7f90d36..37f947e 100644 (file)
@@ -22,3 +22,4 @@ CONFIG_NET_CLS_FLOWER=m
 CONFIG_LWTUNNEL=y
 CONFIG_BPF_STREAM_PARSER=y
 CONFIG_XDP_SOCKETS=y
+CONFIG_FTRACE_SYSCALLS=y
index 5a88a68..1fd244d 100644 (file)
@@ -21,23 +21,50 @@ int _version SEC("version") = 1;
 SEC("cgroup/connect4")
 int connect_v4_prog(struct bpf_sock_addr *ctx)
 {
+       struct bpf_sock_tuple tuple = {};
        struct sockaddr_in sa;
+       struct bpf_sock *sk;
+
+       /* Verify that new destination is available. */
+       memset(&tuple.ipv4.saddr, 0, sizeof(tuple.ipv4.saddr));
+       memset(&tuple.ipv4.sport, 0, sizeof(tuple.ipv4.sport));
+
+       tuple.ipv4.daddr = bpf_htonl(DST_REWRITE_IP4);
+       tuple.ipv4.dport = bpf_htons(DST_REWRITE_PORT4);
+
+       if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
+               return 0;
+       else if (ctx->type == SOCK_STREAM)
+               sk = bpf_sk_lookup_tcp(ctx, &tuple, sizeof(tuple.ipv4),
+                                      BPF_F_CURRENT_NETNS, 0);
+       else
+               sk = bpf_sk_lookup_udp(ctx, &tuple, sizeof(tuple.ipv4),
+                                      BPF_F_CURRENT_NETNS, 0);
+
+       if (!sk)
+               return 0;
+
+       if (sk->src_ip4 != tuple.ipv4.daddr ||
+           sk->src_port != DST_REWRITE_PORT4) {
+               bpf_sk_release(sk);
+               return 0;
+       }
+
+       bpf_sk_release(sk);
 
        /* Rewrite destination. */
        ctx->user_ip4 = bpf_htonl(DST_REWRITE_IP4);
        ctx->user_port = bpf_htons(DST_REWRITE_PORT4);
 
-       if (ctx->type == SOCK_DGRAM || ctx->type == SOCK_STREAM) {
-               ///* Rewrite source. */
-               memset(&sa, 0, sizeof(sa));
+       /* Rewrite source. */
+       memset(&sa, 0, sizeof(sa));
 
-               sa.sin_family = AF_INET;
-               sa.sin_port = bpf_htons(0);
-               sa.sin_addr.s_addr = bpf_htonl(SRC_REWRITE_IP4);
+       sa.sin_family = AF_INET;
+       sa.sin_port = bpf_htons(0);
+       sa.sin_addr.s_addr = bpf_htonl(SRC_REWRITE_IP4);
 
-               if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
-                       return 0;
-       }
+       if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
+               return 0;
 
        return 1;
 }
index 8ea3f7d..26397ab 100644 (file)
@@ -29,7 +29,43 @@ int _version SEC("version") = 1;
 SEC("cgroup/connect6")
 int connect_v6_prog(struct bpf_sock_addr *ctx)
 {
+       struct bpf_sock_tuple tuple = {};
        struct sockaddr_in6 sa;
+       struct bpf_sock *sk;
+
+       /* Verify that new destination is available. */
+       memset(&tuple.ipv6.saddr, 0, sizeof(tuple.ipv6.saddr));
+       memset(&tuple.ipv6.sport, 0, sizeof(tuple.ipv6.sport));
+
+       tuple.ipv6.daddr[0] = bpf_htonl(DST_REWRITE_IP6_0);
+       tuple.ipv6.daddr[1] = bpf_htonl(DST_REWRITE_IP6_1);
+       tuple.ipv6.daddr[2] = bpf_htonl(DST_REWRITE_IP6_2);
+       tuple.ipv6.daddr[3] = bpf_htonl(DST_REWRITE_IP6_3);
+
+       tuple.ipv6.dport = bpf_htons(DST_REWRITE_PORT6);
+
+       if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
+               return 0;
+       else if (ctx->type == SOCK_STREAM)
+               sk = bpf_sk_lookup_tcp(ctx, &tuple, sizeof(tuple.ipv6),
+                                      BPF_F_CURRENT_NETNS, 0);
+       else
+               sk = bpf_sk_lookup_udp(ctx, &tuple, sizeof(tuple.ipv6),
+                                      BPF_F_CURRENT_NETNS, 0);
+
+       if (!sk)
+               return 0;
+
+       if (sk->src_ip6[0] != tuple.ipv6.daddr[0] ||
+           sk->src_ip6[1] != tuple.ipv6.daddr[1] ||
+           sk->src_ip6[2] != tuple.ipv6.daddr[2] ||
+           sk->src_ip6[3] != tuple.ipv6.daddr[3] ||
+           sk->src_port != DST_REWRITE_PORT6) {
+               bpf_sk_release(sk);
+               return 0;
+       }
+
+       bpf_sk_release(sk);
 
        /* Rewrite destination. */
        ctx->user_ip6[0] = bpf_htonl(DST_REWRITE_IP6_0);
@@ -39,21 +75,19 @@ int connect_v6_prog(struct bpf_sock_addr *ctx)
 
        ctx->user_port = bpf_htons(DST_REWRITE_PORT6);
 
-       if (ctx->type == SOCK_DGRAM || ctx->type == SOCK_STREAM) {
-               /* Rewrite source. */
-               memset(&sa, 0, sizeof(sa));
+       /* Rewrite source. */
+       memset(&sa, 0, sizeof(sa));
 
-               sa.sin6_family = AF_INET6;
-               sa.sin6_port = bpf_htons(0);
+       sa.sin6_family = AF_INET6;
+       sa.sin6_port = bpf_htons(0);
 
-               sa.sin6_addr.s6_addr32[0] = bpf_htonl(SRC_REWRITE_IP6_0);
-               sa.sin6_addr.s6_addr32[1] = bpf_htonl(SRC_REWRITE_IP6_1);
-               sa.sin6_addr.s6_addr32[2] = bpf_htonl(SRC_REWRITE_IP6_2);
-               sa.sin6_addr.s6_addr32[3] = bpf_htonl(SRC_REWRITE_IP6_3);
+       sa.sin6_addr.s6_addr32[0] = bpf_htonl(SRC_REWRITE_IP6_0);
+       sa.sin6_addr.s6_addr32[1] = bpf_htonl(SRC_REWRITE_IP6_1);
+       sa.sin6_addr.s6_addr32[2] = bpf_htonl(SRC_REWRITE_IP6_2);
+       sa.sin6_addr.s6_addr32[3] = bpf_htonl(SRC_REWRITE_IP6_3);
 
-               if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
-                       return 0;
-       }
+       if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
+               return 0;
 
        return 1;
 }
index 1198abc..9f741e6 100644 (file)
@@ -16,12 +16,18 @@ struct bpf_map_def SEC("maps") percpu_netcnt = {
        .value_size = sizeof(struct percpu_net_cnt),
 };
 
+BPF_ANNOTATE_KV_PAIR(percpu_netcnt, struct bpf_cgroup_storage_key,
+                    struct percpu_net_cnt);
+
 struct bpf_map_def SEC("maps") netcnt = {
        .type = BPF_MAP_TYPE_CGROUP_STORAGE,
        .key_size = sizeof(struct bpf_cgroup_storage_key),
        .value_size = sizeof(struct net_cnt),
 };
 
+BPF_ANNOTATE_KV_PAIR(netcnt, struct bpf_cgroup_storage_key,
+                    struct net_cnt);
+
 SEC("cgroup/skb")
 int bpf_nextcnt(struct __sk_buff *skb)
 {
index 5f377ec..3c789d0 100644 (file)
@@ -620,8 +620,8 @@ static int do_test_single(struct bpf_align_test *test)
 
        prog_len = probe_filter_length(prog);
        fd_prog = bpf_verify_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,
-                                    prog, prog_len, 1, "GPL", 0,
-                                    bpf_vlog, sizeof(bpf_vlog), 2);
+                                    prog, prog_len, BPF_F_STRICT_ALIGNMENT,
+                                    "GPL", 0, bpf_vlog, sizeof(bpf_vlog), 2);
        if (fd_prog < 0 && test->result != REJECT) {
                printf("Failed to load program.\n");
                printf("%s", bpf_vlog);
index f42b339..8bcd380 100644 (file)
@@ -5,6 +5,8 @@
 #include <linux/btf.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
+#include <linux/filter.h>
+#include <linux/unistd.h>
 #include <bpf/bpf.h>
 #include <sys/resource.h>
 #include <libelf.h>
@@ -22,6 +24,9 @@
 #include "bpf_rlimit.h"
 #include "bpf_util.h"
 
+#define MAX_INSNS      512
+#define MAX_SUBPROGS   16
+
 static uint32_t pass_cnt;
 static uint32_t error_cnt;
 static uint32_t skip_cnt;
@@ -60,8 +65,8 @@ static int __base_pr(const char *format, ...)
        return err;
 }
 
-#define BTF_INFO_ENC(kind, root, vlen)                 \
-       ((!!(root) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
+#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)
@@ -81,28 +86,44 @@ static int __base_pr(const char *format, ...)
 #define BTF_MEMBER_ENC(name, type, bits_offset)        \
        (name), (type), (bits_offset)
 #define BTF_ENUM_ENC(name, val) (name), (val)
+#define BTF_MEMBER_OFFSET(bitfield_size, bits_offset) \
+       ((bitfield_size) << 24 | (bits_offset))
 
 #define BTF_TYPEDEF_ENC(name, type) \
        BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0), type)
 
-#define BTF_PTR_ENC(name, type) \
-       BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), type)
+#define BTF_PTR_ENC(type) \
+       BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), type)
+
+#define BTF_CONST_ENC(type) \
+       BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 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)
+
+#define BTF_FUNC_PROTO_ARG_ENC(name, type) \
+       (name), (type)
+
+#define BTF_FUNC_ENC(name, func_proto) \
+       BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0), func_proto)
 
 #define BTF_END_RAW 0xdeadbeef
 #define NAME_TBD 0xdeadb33f
 
-#define MAX_NR_RAW_TYPES 1024
+#define MAX_NR_RAW_U32 1024
 #define BTF_LOG_BUF_SIZE 65535
 
 static struct args {
        unsigned int raw_test_num;
        unsigned int file_test_num;
        unsigned int get_info_test_num;
+       unsigned int info_raw_test_num;
        bool raw_test;
        bool file_test;
        bool get_info_test;
        bool pprint_test;
        bool always_log;
+       bool info_raw_test;
 } args;
 
 static char btf_log_buf[BTF_LOG_BUF_SIZE];
@@ -118,7 +139,7 @@ struct btf_raw_test {
        const char *str_sec;
        const char *map_name;
        const char *err_str;
-       __u32 raw_types[MAX_NR_RAW_TYPES];
+       __u32 raw_types[MAX_NR_RAW_U32];
        __u32 str_sec_size;
        enum bpf_map_type map_type;
        __u32 key_size;
@@ -137,6 +158,9 @@ struct btf_raw_test {
        int str_len_delta;
 };
 
+#define BTF_STR_SEC(str) \
+       .str_sec = str, .str_sec_size = sizeof(str)
+
 static struct btf_raw_test raw_tests[] = {
 /* enum E {
  *     E0,
@@ -432,11 +456,11 @@ static struct btf_raw_test raw_tests[] = {
                /* const void* */       /* [3] */
                BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 2),
                /* typedef const void * const_void_ptr */
-               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 3),
-               /* struct A { */        /* [4] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 3),   /* [4] */
+               /* struct A { */        /* [5] */
                BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), sizeof(void *)),
                /* const_void_ptr m; */
-               BTF_MEMBER_ENC(NAME_TBD, 3, 0),
+               BTF_MEMBER_ENC(NAME_TBD, 4, 0),
                /* } */
                BTF_END_RAW,
        },
@@ -494,10 +518,10 @@ static struct btf_raw_test raw_tests[] = {
                BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), 0),
                /* const void* */       /* [3] */
                BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 2),
-               /* typedef const void * const_void_ptr */       /* [4] */
-               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 3),
-               /* const_void_ptr[4] */ /* [5] */
-               BTF_TYPE_ARRAY_ENC(3, 1, 4),
+               /* typedef const void * const_void_ptr */
+               BTF_TYPEDEF_ENC(NAME_TBD, 3),   /* [4] */
+               /* const_void_ptr[4] */
+               BTF_TYPE_ARRAY_ENC(4, 1, 4),    /* [5] */
                BTF_END_RAW,
        },
        .str_sec = "\0const_void_ptr",
@@ -1292,6 +1316,367 @@ static struct btf_raw_test raw_tests[] = {
        .err_str = "type != 0",
 },
 
+{
+       .descr = "typedef (invalid name, name_off = 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPEDEF_ENC(0, 1),                          /* [2] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0__int",
+       .str_sec_size = sizeof("\0__int"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "typedef_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "typedef (invalid name, invalid identifier)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 1),                   /* [2] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0__!int",
+       .str_sec_size = sizeof("\0__!int"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "typedef_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "ptr type (invalid name, name_off <> 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 1),      /* [2] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0__int",
+       .str_sec_size = sizeof("\0__int"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "ptr_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "volatile type (invalid name, name_off <> 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_VOLATILE, 0, 0), 1), /* [2] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0__int",
+       .str_sec_size = sizeof("\0__int"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "volatile_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "const type (invalid name, name_off <> 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), 1),    /* [2] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0__int",
+       .str_sec_size = sizeof("\0__int"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "const_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "restrict type (invalid name, name_off <> 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 1),   /* [2] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_RESTRICT, 0, 0), 2), /* [3] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0__int",
+       .str_sec_size = sizeof("\0__int"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "restrict_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "fwd type (invalid name, name_off = 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FWD, 0, 0), 0),   /* [2] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0__skb",
+       .str_sec_size = sizeof("\0__skb"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "fwd_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "fwd type (invalid name, invalid identifier)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_FWD, 0, 0), 0),      /* [2] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0__!skb",
+       .str_sec_size = sizeof("\0__!skb"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "fwd_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "array type (invalid name, name_off <> 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_ARRAY, 0, 0), 0),    /* [2] */
+               BTF_ARRAY_ENC(1, 1, 4),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0__skb",
+       .str_sec_size = sizeof("\0__skb"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "array_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "struct type (name_off = 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0,
+                            BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), 4),   /* [2] */
+               BTF_MEMBER_ENC(NAME_TBD, 1, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0A",
+       .str_sec_size = sizeof("\0A"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "struct type (invalid name, invalid identifier)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), 4),   /* [2] */
+               BTF_MEMBER_ENC(NAME_TBD, 1, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0A!\0B",
+       .str_sec_size = sizeof("\0A!\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "struct member (name_off = 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0,
+                            BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), 4),   /* [2] */
+               BTF_MEMBER_ENC(NAME_TBD, 1, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0A",
+       .str_sec_size = sizeof("\0A"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "struct member (invalid name, invalid identifier)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), 4),   /* [2] */
+               BTF_MEMBER_ENC(NAME_TBD, 1, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0A\0B*",
+       .str_sec_size = sizeof("\0A\0B*"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "enum type (name_off = 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0,
+                            BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1),
+                            sizeof(int)),                              /* [2] */
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0A\0B",
+       .str_sec_size = sizeof("\0A\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "enum_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "enum type (invalid name, invalid identifier)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1),
+                            sizeof(int)),                              /* [2] */
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0A!\0B",
+       .str_sec_size = sizeof("\0A!\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "enum_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "enum member (invalid name, name_off = 0)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0,
+                            BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1),
+                            sizeof(int)),                              /* [2] */
+               BTF_ENUM_ENC(0, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "enum_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "enum member (invalid name, invalid identifier)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0,
+                            BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1),
+                            sizeof(int)),                              /* [2] */
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0A!",
+       .str_sec_size = sizeof("\0A!"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "enum_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
 {
        .descr = "arraymap invalid btf key (a bit field)",
        .raw_types = {
@@ -1374,1119 +1759,3207 @@ static struct btf_raw_test raw_tests[] = {
        .map_create_err = true,
 },
 
-}; /* struct btf_raw_test raw_tests[] */
-
-static const char *get_next_str(const char *start, const char *end)
 {
-       return start < end - 1 ? start + 1 : NULL;
-}
+       .descr = "func proto (int (*)(int, unsigned int))",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* int (*)(int, unsigned int) */
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
 
-static int get_type_sec_size(const __u32 *raw_types)
 {
-       int i;
-
-       for (i = MAX_NR_RAW_TYPES - 1;
-            i >= 0 && raw_types[i] != BTF_END_RAW;
-            i--)
-               ;
-
-       return i < 0 ? i : i * sizeof(raw_types[0]);
-}
+       .descr = "func proto (vararg)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int, unsigned int, ...) */
+               BTF_FUNC_PROTO_ENC(0, 3),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
 
-static void *btf_raw_create(const struct btf_header *hdr,
-                           const __u32 *raw_types,
-                           const char *str,
-                           unsigned int str_sec_size,
-                           unsigned int *btf_size)
 {
-       const char *next_str = str, *end_str = str + str_sec_size;
-       unsigned int size_needed, offset;
-       struct btf_header *ret_hdr;
-       int i, type_sec_size;
-       uint32_t *ret_types;
-       void *raw_btf;
-
-       type_sec_size = get_type_sec_size(raw_types);
-       if (CHECK(type_sec_size < 0, "Cannot get nr_raw_types"))
-               return NULL;
-
-       size_needed = sizeof(*hdr) + type_sec_size + str_sec_size;
-       raw_btf = malloc(size_needed);
-       if (CHECK(!raw_btf, "Cannot allocate memory for raw_btf"))
-               return NULL;
-
-       /* Copy header */
-       memcpy(raw_btf, hdr, sizeof(*hdr));
-       offset = sizeof(*hdr);
-
-       /* 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;
-                       }
-                       ret_types[i] = next_str - str;
-                       next_str += strlen(next_str);
-               } else {
-                       ret_types[i] = raw_types[i];
-               }
-       }
-       offset += type_sec_size;
-
-       /* Copy string section */
-       memcpy(raw_btf + offset, str, str_sec_size);
-
-       ret_hdr = (struct btf_header *)raw_btf;
-       ret_hdr->type_len = type_sec_size;
-       ret_hdr->str_off = type_sec_size;
-       ret_hdr->str_len = str_sec_size;
-
-       *btf_size = size_needed;
-
-       return raw_btf;
-}
+       .descr = "func proto (vararg with name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int b, ... c) */
+               BTF_FUNC_PROTO_ENC(0, 3),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b\0c",
+       .str_sec_size = sizeof("\0a\0b\0c"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#3",
+},
 
-static int do_test_raw(unsigned int test_num)
 {
-       struct btf_raw_test *test = &raw_tests[test_num - 1];
-       struct bpf_create_map_attr create_attr = {};
-       int map_fd = -1, btf_fd = -1;
-       unsigned int raw_btf_size;
-       struct btf_header *hdr;
-       void *raw_btf;
-       int err;
-
-       fprintf(stderr, "BTF raw test[%u] (%s): ", test_num, test->descr);
-       raw_btf = btf_raw_create(&hdr_tmpl,
-                                test->raw_types,
-                                test->str_sec,
-                                test->str_sec_size,
-                                &raw_btf_size);
-
-       if (!raw_btf)
-               return -1;
-
-       hdr = raw_btf;
-
-       hdr->hdr_len = (int)hdr->hdr_len + test->hdr_len_delta;
-       hdr->type_off = (int)hdr->type_off + test->type_off_delta;
-       hdr->str_off = (int)hdr->str_off + test->str_off_delta;
-       hdr->str_len = (int)hdr->str_len + test->str_len_delta;
-
-       *btf_log_buf = '\0';
-       btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
-                             btf_log_buf, BTF_LOG_BUF_SIZE,
-                             args.always_log);
-       free(raw_btf);
-
-       err = ((btf_fd == -1) != test->btf_load_err);
-       if (CHECK(err, "btf_fd:%d test->btf_load_err:%u",
-                 btf_fd, test->btf_load_err) ||
-           CHECK(test->err_str && !strstr(btf_log_buf, test->err_str),
-                 "expected err_str:%s", test->err_str)) {
-               err = -1;
-               goto done;
-       }
-
-       if (err || btf_fd == -1)
-               goto done;
-
-       create_attr.name = test->map_name;
-       create_attr.map_type = test->map_type;
-       create_attr.key_size = test->key_size;
-       create_attr.value_size = test->value_size;
-       create_attr.max_entries = test->max_entries;
-       create_attr.btf_fd = btf_fd;
-       create_attr.btf_key_type_id = test->key_type_id;
-       create_attr.btf_value_type_id = test->value_type_id;
-
-       map_fd = bpf_create_map_xattr(&create_attr);
-
-       err = ((map_fd == -1) != test->map_create_err);
-       CHECK(err, "map_fd:%d test->map_create_err:%u",
-             map_fd, test->map_create_err);
-
-done:
-       if (!err)
-               fprintf(stderr, "OK");
-
-       if (*btf_log_buf && (err || args.always_log))
-               fprintf(stderr, "\n%s", btf_log_buf);
-
-       if (btf_fd != -1)
-               close(btf_fd);
-       if (map_fd != -1)
-               close(map_fd);
-
-       return err;
-}
+       .descr = "func proto (arg after vararg)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, ..., unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 3),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 0),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b",
+       .str_sec_size = sizeof("\0a\0b"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#2",
+},
 
-static int test_raw(void)
 {
-       unsigned int i;
-       int err = 0;
-
-       if (args.raw_test_num)
-               return count_result(do_test_raw(args.raw_test_num));
-
-       for (i = 1; i <= ARRAY_SIZE(raw_tests); i++)
-               err |= count_result(do_test_raw(i));
-
-       return err;
-}
-
-struct btf_get_info_test {
-       const char *descr;
-       const char *str_sec;
-       __u32 raw_types[MAX_NR_RAW_TYPES];
-       __u32 str_sec_size;
-       int btf_size_delta;
-       int (*special_test)(unsigned int test_num);
-};
-
-static int test_big_btf_info(unsigned int test_num);
-static int test_btf_id(unsigned int test_num);
+       .descr = "func proto (CONST=>TYPEDEF=>PTR=>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] */
+               /* typedef void (*func_ptr)(int, unsigned int) */
+               BTF_TYPEDEF_ENC(NAME_TBD, 5),                   /* [3] */
+               /* const func_ptr */
+               BTF_CONST_ENC(3),                               /* [4] */
+               BTF_PTR_ENC(6),                                 /* [5] */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [6] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0func_ptr",
+       .str_sec_size = sizeof("\0func_ptr"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
 
-const struct btf_get_info_test get_info_tests[] = {
 {
-       .descr = "== raw_btf_size+1",
+       .descr = "func proto (CONST=>TYPEDEF=>FUNC_PROTO)",
        .raw_types = {
-               /* int */                               /* [1] */
-               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
+               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_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
                BTF_END_RAW,
        },
-       .str_sec = "",
-       .str_sec_size = sizeof(""),
-       .btf_size_delta = 1,
+       .str_sec = "\0func_typedef",
+       .str_sec_size = sizeof("\0func_typedef"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid type_id",
 },
+
 {
-       .descr = "== raw_btf_size-3",
+       .descr = "func proto (btf_resolve(arg))",
        .raw_types = {
-               /* int */                               /* [1] */
-               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               /* void (*)(const void *) */
+               BTF_FUNC_PROTO_ENC(0, 1),                       /* [2] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 3),
+               BTF_CONST_ENC(4),                               /* [3] */
+               BTF_PTR_ENC(0),                                 /* [4] */
                BTF_END_RAW,
        },
        .str_sec = "",
        .str_sec_size = sizeof(""),
-       .btf_size_delta = -3,
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
 },
+
 {
-       .descr = "Large bpf_btf_info",
+       .descr = "func proto (Not all arg has name)",
        .raw_types = {
-               /* int */                               /* [1] */
-               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int, unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
                BTF_END_RAW,
        },
-       .str_sec = "",
-       .str_sec_size = sizeof(""),
-       .special_test = test_big_btf_info,
+       .str_sec = "\0b",
+       .str_sec_size = sizeof("\0b"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
 },
+
 {
-       .descr = "BTF ID",
+       .descr = "func proto (Bad arg name_off)",
        .raw_types = {
-               /* int */                               /* [1] */
-               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
-               /* unsigned int */                      /* [2] */
-               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* 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_END_RAW,
        },
-       .str_sec = "",
-       .str_sec_size = sizeof(""),
-       .special_test = test_btf_id,
+       .str_sec = "\0a",
+       .str_sec_size = sizeof("\0a"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#2",
 },
-};
+
+{
+       .descr = "func proto (Bad arg name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int !!!) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0!!!",
+       .str_sec_size = sizeof("\0a\0!!!"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#2",
+},
+
+{
+       .descr = "func proto (Invalid return type)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* <bad_ret_type> (*)(int, unsigned int) */
+               BTF_FUNC_PROTO_ENC(100, 2),                     /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid return type",
+},
+
+{
+       .descr = "func proto (with func name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void func_proto(int, unsigned int) */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 2), 0),     /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0func_proto",
+       .str_sec_size = sizeof("\0func_proto"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "func proto (const void arg)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(const void) */
+               BTF_FUNC_PROTO_ENC(0, 1),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 4),
+               BTF_CONST_ENC(0),                               /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#1",
+},
+
+{
+       .descr = "func (void func(int a, unsigned int b))",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               /* void func(int a, unsigned int b) */
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b\0func",
+       .str_sec_size = sizeof("\0a\0b\0func"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "func (No func name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               /* void <no_name>(int a, unsigned int b) */
+               BTF_FUNC_ENC(0, 3),                             /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b",
+       .str_sec_size = sizeof("\0a\0b"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "func (Invalid func name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               /* void !!!(int a, unsigned int b) */
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b\0!!!",
+       .str_sec_size = sizeof("\0a\0b\0!!!"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "func (Some arg has no name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               /* void func(int a, unsigned int) */
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0func",
+       .str_sec_size = sizeof("\0a\0func"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#2",
+},
+
+{
+       .descr = "func (Non zero vlen)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               /* void func(int a, unsigned int b) */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 2), 3),   /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b\0func",
+       .str_sec_size = sizeof("\0a\0b\0func"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "vlen != 0",
+},
+
+{
+       .descr = "func (Not referring to FUNC_PROTO)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_FUNC_ENC(NAME_TBD, 1),                      /* [2] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0func",
+       .str_sec_size = sizeof("\0func"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid type_id",
+},
+
+{
+       .descr = "invalid int kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_INT, 1, 0), 4),   /* [2] */
+               BTF_INT_ENC(0, 0, 32),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "int_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid btf_info kind_flag",
+},
+
+{
+       .descr = "invalid ptr kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 1, 0), 1),   /* [2] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "ptr_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid btf_info kind_flag",
+},
+
+{
+       .descr = "invalid array kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ARRAY, 1, 0), 0), /* [2] */
+               BTF_ARRAY_ENC(1, 1, 1),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "array_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid btf_info kind_flag",
+},
+
+{
+       .descr = "invalid enum kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 1, 1), 4),  /* [2] */
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "enum_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid btf_info kind_flag",
+},
+
+{
+       .descr = "valid fwd kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_FWD, 1, 0), 0),      /* [2] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "fwd_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "invalid typedef kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(NAME_TBD,
+                            BTF_INFO_ENC(BTF_KIND_TYPEDEF, 1, 0), 1),  /* [2] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "typedef_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid btf_info kind_flag",
+},
+
+{
+       .descr = "invalid volatile kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_VOLATILE, 1, 0), 1),      /* [2] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "volatile_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid btf_info kind_flag",
+},
+
+{
+       .descr = "invalid const kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 1, 0), 1), /* [2] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "const_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid btf_info kind_flag",
+},
+
+{
+       .descr = "invalid restrict kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_RESTRICT, 1, 0), 1),      /* [2] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "restrict_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid btf_info kind_flag",
+},
+
+{
+       .descr = "invalid func kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 0), 0),    /* [2] */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FUNC, 1, 0), 2),   /* [3] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid btf_info kind_flag",
+},
+
+{
+       .descr = "invalid func_proto kind_flag",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 1, 0), 0),    /* [2] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid btf_info kind_flag",
+},
+
+{
+       .descr = "valid struct, kind_flag, bitfield_size = 0",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 8),        /* [2] */
+               BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(0, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(0, 32)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "valid struct, kind_flag, int member, bitfield_size != 0",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4),        /* [2] */
+               BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(4, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(4, 4)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "valid union, kind_flag, int member, bitfield_size != 0",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 1, 2), 4), /* [2] */
+               BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(4, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(4, 0)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "union_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "valid struct, kind_flag, enum member, bitfield_size != 0",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4),  /* [2] */
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4),/* [3] */
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(4, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(4, 4)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B\0C"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "valid union, kind_flag, enum member, bitfield_size != 0",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4),  /* [2] */
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 1, 2), 4), /* [3] */
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(4, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(4, 0)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B\0C"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "union_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "valid struct, kind_flag, typedef member, bitfield_size != 0",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4),  /* [2] */
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4),/* [3] */
+               BTF_MEMBER_ENC(NAME_TBD, 4, BTF_MEMBER_OFFSET(4, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 5, BTF_MEMBER_OFFSET(4, 4)),
+               BTF_TYPEDEF_ENC(NAME_TBD, 1),                           /* [4] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 2),                           /* [5] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B\0C\0D\0E"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "valid union, kind_flag, typedef member, bitfield_size != 0",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4),  /* [2] */
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 1, 2), 4), /* [3] */
+               BTF_MEMBER_ENC(NAME_TBD, 4, BTF_MEMBER_OFFSET(4, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 5, BTF_MEMBER_OFFSET(4, 0)),
+               BTF_TYPEDEF_ENC(NAME_TBD, 1),                           /* [4] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 2),                           /* [5] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B\0C\0D\0E"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "union_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "invalid struct, kind_flag, bitfield_size greater than struct size",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4),        /* [2] */
+               BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(20, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(20, 20)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Member exceeds struct_size",
+},
+
+{
+       .descr = "invalid struct, kind_flag, bitfield base_type int not regular",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 20, 4),                  /* [2] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4),        /* [3] */
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(20, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(20, 20)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid member base type",
+},
+
+{
+       .descr = "invalid struct, kind_flag, base_type int not regular",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 12, 4),                  /* [2] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4),        /* [3] */
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(8, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(8, 8)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid member base type",
+},
+
+{
+       .descr = "invalid union, kind_flag, bitfield_size greater than struct size",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 1, 2), 2), /* [2] */
+               BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(8, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(20, 0)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "union_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Member exceeds struct_size",
+},
+
+{
+       .descr = "invalid struct, kind_flag, int member, bitfield_size = 0, wrong byte alignment",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [2] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 12),       /* [3] */
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 36)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid member offset",
+},
+
+{
+       .descr = "invalid struct, kind_flag, enum member, bitfield_size = 0, wrong byte alignment",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [1] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),                  /* [2] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4),  /* [2] */
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 12),       /* [3] */
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 0)),
+               BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 36)),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0A\0B\0C"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid member offset",
+},
+
+}; /* struct btf_raw_test raw_tests[] */
+
+static const char *get_next_str(const char *start, const char *end)
+{
+       return start < end - 1 ? start + 1 : NULL;
+}
+
+static int get_raw_sec_size(const __u32 *raw_types)
+{
+       int i;
+
+       for (i = MAX_NR_RAW_U32 - 1;
+            i >= 0 && raw_types[i] != BTF_END_RAW;
+            i--)
+               ;
+
+       return i < 0 ? i : i * sizeof(raw_types[0]);
+}
+
+static void *btf_raw_create(const struct btf_header *hdr,
+                           const __u32 *raw_types,
+                           const char *str,
+                           unsigned int str_sec_size,
+                           unsigned int *btf_size,
+                           const char **ret_next_str)
+{
+       const char *next_str = str, *end_str = str + str_sec_size;
+       unsigned int size_needed, offset;
+       struct btf_header *ret_hdr;
+       int i, type_sec_size;
+       uint32_t *ret_types;
+       void *raw_btf;
+
+       type_sec_size = get_raw_sec_size(raw_types);
+       if (CHECK(type_sec_size < 0, "Cannot get nr_raw_types"))
+               return NULL;
+
+       size_needed = sizeof(*hdr) + type_sec_size + str_sec_size;
+       raw_btf = malloc(size_needed);
+       if (CHECK(!raw_btf, "Cannot allocate memory for raw_btf"))
+               return NULL;
+
+       /* Copy header */
+       memcpy(raw_btf, hdr, sizeof(*hdr));
+       offset = sizeof(*hdr);
+
+       /* 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;
+                       }
+                       ret_types[i] = next_str - str;
+                       next_str += strlen(next_str);
+               } else {
+                       ret_types[i] = raw_types[i];
+               }
+       }
+       offset += type_sec_size;
+
+       /* Copy string section */
+       memcpy(raw_btf + offset, str, str_sec_size);
+
+       ret_hdr = (struct btf_header *)raw_btf;
+       ret_hdr->type_len = type_sec_size;
+       ret_hdr->str_off = type_sec_size;
+       ret_hdr->str_len = str_sec_size;
+
+       *btf_size = size_needed;
+       if (ret_next_str)
+               *ret_next_str = next_str;
+
+       return raw_btf;
+}
+
+static int do_test_raw(unsigned int test_num)
+{
+       struct btf_raw_test *test = &raw_tests[test_num - 1];
+       struct bpf_create_map_attr create_attr = {};
+       int map_fd = -1, btf_fd = -1;
+       unsigned int raw_btf_size;
+       struct btf_header *hdr;
+       void *raw_btf;
+       int err;
+
+       fprintf(stderr, "BTF raw test[%u] (%s): ", test_num, test->descr);
+       raw_btf = btf_raw_create(&hdr_tmpl,
+                                test->raw_types,
+                                test->str_sec,
+                                test->str_sec_size,
+                                &raw_btf_size, NULL);
+
+       if (!raw_btf)
+               return -1;
+
+       hdr = raw_btf;
+
+       hdr->hdr_len = (int)hdr->hdr_len + test->hdr_len_delta;
+       hdr->type_off = (int)hdr->type_off + test->type_off_delta;
+       hdr->str_off = (int)hdr->str_off + test->str_off_delta;
+       hdr->str_len = (int)hdr->str_len + test->str_len_delta;
+
+       *btf_log_buf = '\0';
+       btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
+                             btf_log_buf, BTF_LOG_BUF_SIZE,
+                             args.always_log);
+       free(raw_btf);
+
+       err = ((btf_fd == -1) != test->btf_load_err);
+       if (CHECK(err, "btf_fd:%d test->btf_load_err:%u",
+                 btf_fd, test->btf_load_err) ||
+           CHECK(test->err_str && !strstr(btf_log_buf, test->err_str),
+                 "expected err_str:%s", test->err_str)) {
+               err = -1;
+               goto done;
+       }
+
+       if (err || btf_fd == -1)
+               goto done;
+
+       create_attr.name = test->map_name;
+       create_attr.map_type = test->map_type;
+       create_attr.key_size = test->key_size;
+       create_attr.value_size = test->value_size;
+       create_attr.max_entries = test->max_entries;
+       create_attr.btf_fd = btf_fd;
+       create_attr.btf_key_type_id = test->key_type_id;
+       create_attr.btf_value_type_id = test->value_type_id;
+
+       map_fd = bpf_create_map_xattr(&create_attr);
+
+       err = ((map_fd == -1) != test->map_create_err);
+       CHECK(err, "map_fd:%d test->map_create_err:%u",
+             map_fd, test->map_create_err);
+
+done:
+       if (!err)
+               fprintf(stderr, "OK");
+
+       if (*btf_log_buf && (err || args.always_log))
+               fprintf(stderr, "\n%s", btf_log_buf);
+
+       if (btf_fd != -1)
+               close(btf_fd);
+       if (map_fd != -1)
+               close(map_fd);
+
+       return err;
+}
+
+static int test_raw(void)
+{
+       unsigned int i;
+       int err = 0;
+
+       if (args.raw_test_num)
+               return count_result(do_test_raw(args.raw_test_num));
+
+       for (i = 1; i <= ARRAY_SIZE(raw_tests); i++)
+               err |= count_result(do_test_raw(i));
+
+       return err;
+}
+
+struct btf_get_info_test {
+       const char *descr;
+       const char *str_sec;
+       __u32 raw_types[MAX_NR_RAW_U32];
+       __u32 str_sec_size;
+       int btf_size_delta;
+       int (*special_test)(unsigned int test_num);
+};
+
+static int test_big_btf_info(unsigned int test_num);
+static int test_btf_id(unsigned int test_num);
+
+const struct btf_get_info_test get_info_tests[] = {
+{
+       .descr = "== raw_btf_size+1",
+       .raw_types = {
+               /* int */                               /* [1] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .btf_size_delta = 1,
+},
+{
+       .descr = "== raw_btf_size-3",
+       .raw_types = {
+               /* int */                               /* [1] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .btf_size_delta = -3,
+},
+{
+       .descr = "Large bpf_btf_info",
+       .raw_types = {
+               /* int */                               /* [1] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .special_test = test_big_btf_info,
+},
+{
+       .descr = "BTF ID",
+       .raw_types = {
+               /* int */                               /* [1] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
+               /* unsigned int */                      /* [2] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .special_test = test_btf_id,
+},
+};
 
 static inline __u64 ptr_to_u64(const void *ptr)
 {
        return (__u64)(unsigned long)ptr;
 }
 
-static int test_big_btf_info(unsigned int test_num)
+static int test_big_btf_info(unsigned int test_num)
+{
+       const struct btf_get_info_test *test = &get_info_tests[test_num - 1];
+       uint8_t *raw_btf = NULL, *user_btf = NULL;
+       unsigned int raw_btf_size;
+       struct {
+               struct bpf_btf_info info;
+               uint64_t garbage;
+       } info_garbage;
+       struct bpf_btf_info *info;
+       int btf_fd = -1, err;
+       uint32_t info_len;
+
+       raw_btf = btf_raw_create(&hdr_tmpl,
+                                test->raw_types,
+                                test->str_sec,
+                                test->str_sec_size,
+                                &raw_btf_size, NULL);
+
+       if (!raw_btf)
+               return -1;
+
+       *btf_log_buf = '\0';
+
+       user_btf = malloc(raw_btf_size);
+       if (CHECK(!user_btf, "!user_btf")) {
+               err = -1;
+               goto done;
+       }
+
+       btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
+                             btf_log_buf, BTF_LOG_BUF_SIZE,
+                             args.always_log);
+       if (CHECK(btf_fd == -1, "errno:%d", errno)) {
+               err = -1;
+               goto done;
+       }
+
+       /*
+        * GET_INFO should error out if the userspace info
+        * has non zero tailing bytes.
+        */
+       info = &info_garbage.info;
+       memset(info, 0, sizeof(*info));
+       info_garbage.garbage = 0xdeadbeef;
+       info_len = sizeof(info_garbage);
+       info->btf = ptr_to_u64(user_btf);
+       info->btf_size = raw_btf_size;
+
+       err = bpf_obj_get_info_by_fd(btf_fd, info, &info_len);
+       if (CHECK(!err, "!err")) {
+               err = -1;
+               goto done;
+       }
+
+       /*
+        * GET_INFO should succeed even info_len is larger than
+        * the kernel supported as long as tailing bytes are zero.
+        * The kernel supported info len should also be returned
+        * to userspace.
+        */
+       info_garbage.garbage = 0;
+       err = bpf_obj_get_info_by_fd(btf_fd, info, &info_len);
+       if (CHECK(err || info_len != sizeof(*info),
+                 "err:%d errno:%d info_len:%u sizeof(*info):%lu",
+                 err, errno, info_len, sizeof(*info))) {
+               err = -1;
+               goto done;
+       }
+
+       fprintf(stderr, "OK");
+
+done:
+       if (*btf_log_buf && (err || args.always_log))
+               fprintf(stderr, "\n%s", btf_log_buf);
+
+       free(raw_btf);
+       free(user_btf);
+
+       if (btf_fd != -1)
+               close(btf_fd);
+
+       return err;
+}
+
+static int test_btf_id(unsigned int test_num)
+{
+       const struct btf_get_info_test *test = &get_info_tests[test_num - 1];
+       struct bpf_create_map_attr create_attr = {};
+       uint8_t *raw_btf = NULL, *user_btf[2] = {};
+       int btf_fd[2] = {-1, -1}, map_fd = -1;
+       struct bpf_map_info map_info = {};
+       struct bpf_btf_info info[2] = {};
+       unsigned int raw_btf_size;
+       uint32_t info_len;
+       int err, i, ret;
+
+       raw_btf = btf_raw_create(&hdr_tmpl,
+                                test->raw_types,
+                                test->str_sec,
+                                test->str_sec_size,
+                                &raw_btf_size, NULL);
+
+       if (!raw_btf)
+               return -1;
+
+       *btf_log_buf = '\0';
+
+       for (i = 0; i < 2; i++) {
+               user_btf[i] = malloc(raw_btf_size);
+               if (CHECK(!user_btf[i], "!user_btf[%d]", i)) {
+                       err = -1;
+                       goto done;
+               }
+               info[i].btf = ptr_to_u64(user_btf[i]);
+               info[i].btf_size = raw_btf_size;
+       }
+
+       btf_fd[0] = bpf_load_btf(raw_btf, raw_btf_size,
+                                btf_log_buf, BTF_LOG_BUF_SIZE,
+                                args.always_log);
+       if (CHECK(btf_fd[0] == -1, "errno:%d", errno)) {
+               err = -1;
+               goto done;
+       }
+
+       /* Test BPF_OBJ_GET_INFO_BY_ID on btf_id */
+       info_len = sizeof(info[0]);
+       err = bpf_obj_get_info_by_fd(btf_fd[0], &info[0], &info_len);
+       if (CHECK(err, "errno:%d", errno)) {
+               err = -1;
+               goto done;
+       }
+
+       btf_fd[1] = bpf_btf_get_fd_by_id(info[0].id);
+       if (CHECK(btf_fd[1] == -1, "errno:%d", errno)) {
+               err = -1;
+               goto done;
+       }
+
+       ret = 0;
+       err = bpf_obj_get_info_by_fd(btf_fd[1], &info[1], &info_len);
+       if (CHECK(err || info[0].id != info[1].id ||
+                 info[0].btf_size != info[1].btf_size ||
+                 (ret = memcmp(user_btf[0], user_btf[1], info[0].btf_size)),
+                 "err:%d errno:%d id0:%u id1:%u btf_size0:%u btf_size1:%u memcmp:%d",
+                 err, errno, info[0].id, info[1].id,
+                 info[0].btf_size, info[1].btf_size, ret)) {
+               err = -1;
+               goto done;
+       }
+
+       /* Test btf members in struct bpf_map_info */
+       create_attr.name = "test_btf_id";
+       create_attr.map_type = BPF_MAP_TYPE_ARRAY;
+       create_attr.key_size = sizeof(int);
+       create_attr.value_size = sizeof(unsigned int);
+       create_attr.max_entries = 4;
+       create_attr.btf_fd = btf_fd[0];
+       create_attr.btf_key_type_id = 1;
+       create_attr.btf_value_type_id = 2;
+
+       map_fd = bpf_create_map_xattr(&create_attr);
+       if (CHECK(map_fd == -1, "errno:%d", errno)) {
+               err = -1;
+               goto done;
+       }
+
+       info_len = sizeof(map_info);
+       err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len);
+       if (CHECK(err || map_info.btf_id != info[0].id ||
+                 map_info.btf_key_type_id != 1 || map_info.btf_value_type_id != 2,
+                 "err:%d errno:%d info.id:%u btf_id:%u btf_key_type_id:%u btf_value_type_id:%u",
+                 err, errno, info[0].id, map_info.btf_id, map_info.btf_key_type_id,
+                 map_info.btf_value_type_id)) {
+               err = -1;
+               goto done;
+       }
+
+       for (i = 0; i < 2; i++) {
+               close(btf_fd[i]);
+               btf_fd[i] = -1;
+       }
+
+       /* Test BTF ID is removed from the kernel */
+       btf_fd[0] = bpf_btf_get_fd_by_id(map_info.btf_id);
+       if (CHECK(btf_fd[0] == -1, "errno:%d", errno)) {
+               err = -1;
+               goto done;
+       }
+       close(btf_fd[0]);
+       btf_fd[0] = -1;
+
+       /* The map holds the last ref to BTF and its btf_id */
+       close(map_fd);
+       map_fd = -1;
+       btf_fd[0] = bpf_btf_get_fd_by_id(map_info.btf_id);
+       if (CHECK(btf_fd[0] != -1, "BTF lingers")) {
+               err = -1;
+               goto done;
+       }
+
+       fprintf(stderr, "OK");
+
+done:
+       if (*btf_log_buf && (err || args.always_log))
+               fprintf(stderr, "\n%s", btf_log_buf);
+
+       free(raw_btf);
+       if (map_fd != -1)
+               close(map_fd);
+       for (i = 0; i < 2; i++) {
+               free(user_btf[i]);
+               if (btf_fd[i] != -1)
+                       close(btf_fd[i]);
+       }
+
+       return err;
+}
+
+static int do_test_get_info(unsigned int test_num)
 {
        const struct btf_get_info_test *test = &get_info_tests[test_num - 1];
+       unsigned int raw_btf_size, user_btf_size, expected_nbytes;
        uint8_t *raw_btf = NULL, *user_btf = NULL;
-       unsigned int raw_btf_size;
-       struct {
-               struct bpf_btf_info info;
-               uint64_t garbage;
-       } info_garbage;
-       struct bpf_btf_info *info;
-       int btf_fd = -1, err;
+       struct bpf_btf_info info = {};
+       int btf_fd = -1, err, ret;
        uint32_t info_len;
 
+       fprintf(stderr, "BTF GET_INFO test[%u] (%s): ",
+               test_num, test->descr);
+
+       if (test->special_test)
+               return test->special_test(test_num);
+
        raw_btf = btf_raw_create(&hdr_tmpl,
                                 test->raw_types,
                                 test->str_sec,
                                 test->str_sec_size,
-                                &raw_btf_size);
+                                &raw_btf_size, NULL);
 
        if (!raw_btf)
                return -1;
 
        *btf_log_buf = '\0';
 
-       user_btf = malloc(raw_btf_size);
-       if (CHECK(!user_btf, "!user_btf")) {
-               err = -1;
+       user_btf = malloc(raw_btf_size);
+       if (CHECK(!user_btf, "!user_btf")) {
+               err = -1;
+               goto done;
+       }
+
+       btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
+                             btf_log_buf, BTF_LOG_BUF_SIZE,
+                             args.always_log);
+       if (CHECK(btf_fd == -1, "errno:%d", errno)) {
+               err = -1;
+               goto done;
+       }
+
+       user_btf_size = (int)raw_btf_size + test->btf_size_delta;
+       expected_nbytes = min(raw_btf_size, user_btf_size);
+       if (raw_btf_size > expected_nbytes)
+               memset(user_btf + expected_nbytes, 0xff,
+                      raw_btf_size - expected_nbytes);
+
+       info_len = sizeof(info);
+       info.btf = ptr_to_u64(user_btf);
+       info.btf_size = user_btf_size;
+
+       ret = 0;
+       err = bpf_obj_get_info_by_fd(btf_fd, &info, &info_len);
+       if (CHECK(err || !info.id || info_len != sizeof(info) ||
+                 info.btf_size != raw_btf_size ||
+                 (ret = memcmp(raw_btf, user_btf, expected_nbytes)),
+                 "err:%d errno:%d info.id:%u info_len:%u sizeof(info):%lu raw_btf_size:%u info.btf_size:%u expected_nbytes:%u memcmp:%d",
+                 err, errno, info.id, info_len, sizeof(info),
+                 raw_btf_size, info.btf_size, expected_nbytes, ret)) {
+               err = -1;
+               goto done;
+       }
+
+       while (expected_nbytes < raw_btf_size) {
+               fprintf(stderr, "%u...", expected_nbytes);
+               if (CHECK(user_btf[expected_nbytes++] != 0xff,
+                         "user_btf[%u]:%x != 0xff", expected_nbytes - 1,
+                         user_btf[expected_nbytes - 1])) {
+                       err = -1;
+                       goto done;
+               }
+       }
+
+       fprintf(stderr, "OK");
+
+done:
+       if (*btf_log_buf && (err || args.always_log))
+               fprintf(stderr, "\n%s", btf_log_buf);
+
+       free(raw_btf);
+       free(user_btf);
+
+       if (btf_fd != -1)
+               close(btf_fd);
+
+       return err;
+}
+
+static int test_get_info(void)
+{
+       unsigned int i;
+       int err = 0;
+
+       if (args.get_info_test_num)
+               return count_result(do_test_get_info(args.get_info_test_num));
+
+       for (i = 1; i <= ARRAY_SIZE(get_info_tests); i++)
+               err |= count_result(do_test_get_info(i));
+
+       return err;
+}
+
+struct btf_file_test {
+       const char *file;
+       bool btf_kv_notfound;
+};
+
+static struct btf_file_test file_tests[] = {
+{
+       .file = "test_btf_haskv.o",
+},
+{
+       .file = "test_btf_nokv.o",
+       .btf_kv_notfound = true,
+},
+};
+
+static int file_has_btf_elf(const char *fn, bool *has_btf_ext)
+{
+       Elf_Scn *scn = NULL;
+       GElf_Ehdr ehdr;
+       int ret = 0;
+       int elf_fd;
+       Elf *elf;
+
+       if (CHECK(elf_version(EV_CURRENT) == EV_NONE,
+                 "elf_version(EV_CURRENT) == EV_NONE"))
+               return -1;
+
+       elf_fd = open(fn, O_RDONLY);
+       if (CHECK(elf_fd == -1, "open(%s): errno:%d", fn, errno))
+               return -1;
+
+       elf = elf_begin(elf_fd, ELF_C_READ, NULL);
+       if (CHECK(!elf, "elf_begin(%s): %s", fn, elf_errmsg(elf_errno()))) {
+               ret = -1;
+               goto done;
+       }
+
+       if (CHECK(!gelf_getehdr(elf, &ehdr), "!gelf_getehdr(%s)", fn)) {
+               ret = -1;
+               goto done;
+       }
+
+       while ((scn = elf_nextscn(elf, scn))) {
+               const char *sh_name;
+               GElf_Shdr sh;
+
+               if (CHECK(gelf_getshdr(scn, &sh) != &sh,
+                         "file:%s gelf_getshdr != &sh", fn)) {
+                       ret = -1;
+                       goto done;
+               }
+
+               sh_name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
+               if (!strcmp(sh_name, BTF_ELF_SEC))
+                       ret = 1;
+               if (!strcmp(sh_name, BTF_EXT_ELF_SEC))
+                       *has_btf_ext = true;
+       }
+
+done:
+       close(elf_fd);
+       elf_end(elf);
+       return ret;
+}
+
+static int do_test_file(unsigned int test_num)
+{
+       const struct btf_file_test *test = &file_tests[test_num - 1];
+       const char *expected_fnames[] = {"_dummy_tracepoint",
+                                        "test_long_fname_1",
+                                        "test_long_fname_2"};
+       struct bpf_prog_info info = {};
+       struct bpf_object *obj = NULL;
+       struct bpf_func_info *finfo;
+       struct bpf_program *prog;
+       __u32 info_len, rec_size;
+       bool has_btf_ext = false;
+       struct btf *btf = NULL;
+       void *func_info = NULL;
+       struct bpf_map *map;
+       int i, err, prog_fd;
+
+       fprintf(stderr, "BTF libbpf test[%u] (%s): ", test_num,
+               test->file);
+
+       err = file_has_btf_elf(test->file, &has_btf_ext);
+       if (err == -1)
+               return err;
+
+       if (err == 0) {
+               fprintf(stderr, "SKIP. No ELF %s found", BTF_ELF_SEC);
+               skip_cnt++;
+               return 0;
+       }
+
+       obj = bpf_object__open(test->file);
+       if (CHECK(IS_ERR(obj), "obj: %ld", PTR_ERR(obj)))
+               return PTR_ERR(obj);
+
+       err = bpf_object__btf_fd(obj);
+       if (CHECK(err == -1, "bpf_object__btf_fd: -1"))
                goto done;
-       }
 
-       btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
-                             btf_log_buf, BTF_LOG_BUF_SIZE,
-                             args.always_log);
-       if (CHECK(btf_fd == -1, "errno:%d", errno)) {
+       prog = bpf_program__next(NULL, obj);
+       if (CHECK(!prog, "Cannot find bpf_prog")) {
                err = -1;
                goto done;
        }
 
-       /*
-        * GET_INFO should error out if the userspace info
-        * has non zero tailing bytes.
-        */
-       info = &info_garbage.info;
-       memset(info, 0, sizeof(*info));
-       info_garbage.garbage = 0xdeadbeef;
-       info_len = sizeof(info_garbage);
-       info->btf = ptr_to_u64(user_btf);
-       info->btf_size = raw_btf_size;
-
-       err = bpf_obj_get_info_by_fd(btf_fd, info, &info_len);
-       if (CHECK(!err, "!err")) {
-               err = -1;
+       bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT);
+       err = bpf_object__load(obj);
+       if (CHECK(err < 0, "bpf_object__load: %d", err))
                goto done;
-       }
+       prog_fd = bpf_program__fd(prog);
 
-       /*
-        * GET_INFO should succeed even info_len is larger than
-        * the kernel supported as long as tailing bytes are zero.
-        * The kernel supported info len should also be returned
-        * to userspace.
-        */
-       info_garbage.garbage = 0;
-       err = bpf_obj_get_info_by_fd(btf_fd, info, &info_len);
-       if (CHECK(err || info_len != sizeof(*info),
-                 "err:%d errno:%d info_len:%u sizeof(*info):%lu",
-                 err, errno, info_len, sizeof(*info))) {
+       map = bpf_object__find_map_by_name(obj, "btf_map");
+       if (CHECK(!map, "btf_map not found")) {
                err = -1;
                goto done;
        }
 
-       fprintf(stderr, "OK");
-
-done:
-       if (*btf_log_buf && (err || args.always_log))
-               fprintf(stderr, "\n%s", btf_log_buf);
-
-       free(raw_btf);
-       free(user_btf);
-
-       if (btf_fd != -1)
-               close(btf_fd);
-
-       return err;
-}
-
-static int test_btf_id(unsigned int test_num)
-{
-       const struct btf_get_info_test *test = &get_info_tests[test_num - 1];
-       struct bpf_create_map_attr create_attr = {};
-       uint8_t *raw_btf = NULL, *user_btf[2] = {};
-       int btf_fd[2] = {-1, -1}, map_fd = -1;
-       struct bpf_map_info map_info = {};
-       struct bpf_btf_info info[2] = {};
-       unsigned int raw_btf_size;
-       uint32_t info_len;
-       int err, i, ret;
-
-       raw_btf = btf_raw_create(&hdr_tmpl,
-                                test->raw_types,
-                                test->str_sec,
-                                test->str_sec_size,
-                                &raw_btf_size);
-
-       if (!raw_btf)
-               return -1;
+       err = (bpf_map__btf_key_type_id(map) == 0 || bpf_map__btf_value_type_id(map) == 0)
+               != test->btf_kv_notfound;
+       if (CHECK(err, "btf_key_type_id:%u btf_value_type_id:%u test->btf_kv_notfound:%u",
+                 bpf_map__btf_key_type_id(map), bpf_map__btf_value_type_id(map),
+                 test->btf_kv_notfound))
+               goto done;
 
-       *btf_log_buf = '\0';
+       if (!has_btf_ext)
+               goto skip;
 
-       for (i = 0; i < 2; i++) {
-               user_btf[i] = malloc(raw_btf_size);
-               if (CHECK(!user_btf[i], "!user_btf[%d]", i)) {
-                       err = -1;
-                       goto done;
-               }
-               info[i].btf = ptr_to_u64(user_btf[i]);
-               info[i].btf_size = raw_btf_size;
-       }
+       /* get necessary program info */
+       info_len = sizeof(struct bpf_prog_info);
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
 
-       btf_fd[0] = bpf_load_btf(raw_btf, raw_btf_size,
-                                btf_log_buf, BTF_LOG_BUF_SIZE,
-                                args.always_log);
-       if (CHECK(btf_fd[0] == -1, "errno:%d", errno)) {
+       if (CHECK(err == -1, "invalid get info (1st) errno:%d", errno)) {
+               fprintf(stderr, "%s\n", btf_log_buf);
                err = -1;
                goto done;
        }
-
-       /* Test BPF_OBJ_GET_INFO_BY_ID on btf_id */
-       info_len = sizeof(info[0]);
-       err = bpf_obj_get_info_by_fd(btf_fd[0], &info[0], &info_len);
-       if (CHECK(err, "errno:%d", errno)) {
+       if (CHECK(info.nr_func_info != 3,
+                 "incorrect info.nr_func_info (1st) %d",
+                 info.nr_func_info)) {
                err = -1;
                goto done;
        }
-
-       btf_fd[1] = bpf_btf_get_fd_by_id(info[0].id);
-       if (CHECK(btf_fd[1] == -1, "errno:%d", errno)) {
+       rec_size = info.func_info_rec_size;
+       if (CHECK(rec_size != sizeof(struct bpf_func_info),
+                 "incorrect info.func_info_rec_size (1st) %d\n", rec_size)) {
                err = -1;
                goto done;
        }
 
-       ret = 0;
-       err = bpf_obj_get_info_by_fd(btf_fd[1], &info[1], &info_len);
-       if (CHECK(err || info[0].id != info[1].id ||
-                 info[0].btf_size != info[1].btf_size ||
-                 (ret = memcmp(user_btf[0], user_btf[1], info[0].btf_size)),
-                 "err:%d errno:%d id0:%u id1:%u btf_size0:%u btf_size1:%u memcmp:%d",
-                 err, errno, info[0].id, info[1].id,
-                 info[0].btf_size, info[1].btf_size, ret)) {
+       func_info = malloc(info.nr_func_info * rec_size);
+       if (CHECK(!func_info, "out of memory")) {
                err = -1;
                goto done;
        }
 
-       /* Test btf members in struct bpf_map_info */
-       create_attr.name = "test_btf_id";
-       create_attr.map_type = BPF_MAP_TYPE_ARRAY;
-       create_attr.key_size = sizeof(int);
-       create_attr.value_size = sizeof(unsigned int);
-       create_attr.max_entries = 4;
-       create_attr.btf_fd = btf_fd[0];
-       create_attr.btf_key_type_id = 1;
-       create_attr.btf_value_type_id = 2;
+       /* reset info to only retrieve func_info related data */
+       memset(&info, 0, sizeof(info));
+       info.nr_func_info = 3;
+       info.func_info_rec_size = rec_size;
+       info.func_info = ptr_to_u64(func_info);
 
-       map_fd = bpf_create_map_xattr(&create_attr);
-       if (CHECK(map_fd == -1, "errno:%d", errno)) {
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+
+       if (CHECK(err == -1, "invalid get info (2nd) errno:%d", errno)) {
+               fprintf(stderr, "%s\n", btf_log_buf);
                err = -1;
                goto done;
        }
-
-       info_len = sizeof(map_info);
-       err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len);
-       if (CHECK(err || map_info.btf_id != info[0].id ||
-                 map_info.btf_key_type_id != 1 || map_info.btf_value_type_id != 2,
-                 "err:%d errno:%d info.id:%u btf_id:%u btf_key_type_id:%u btf_value_type_id:%u",
-                 err, errno, info[0].id, map_info.btf_id, map_info.btf_key_type_id,
-                 map_info.btf_value_type_id)) {
+       if (CHECK(info.nr_func_info != 3,
+                 "incorrect info.nr_func_info (2nd) %d",
+                 info.nr_func_info)) {
                err = -1;
                goto done;
        }
-
-       for (i = 0; i < 2; i++) {
-               close(btf_fd[i]);
-               btf_fd[i] = -1;
-       }
-
-       /* Test BTF ID is removed from the kernel */
-       btf_fd[0] = bpf_btf_get_fd_by_id(map_info.btf_id);
-       if (CHECK(btf_fd[0] == -1, "errno:%d", errno)) {
+       if (CHECK(info.func_info_rec_size != rec_size,
+                 "incorrect info.func_info_rec_size (2nd) %d",
+                 info.func_info_rec_size)) {
                err = -1;
                goto done;
        }
-       close(btf_fd[0]);
-       btf_fd[0] = -1;
 
-       /* The map holds the last ref to BTF and its btf_id */
-       close(map_fd);
-       map_fd = -1;
-       btf_fd[0] = bpf_btf_get_fd_by_id(map_info.btf_id);
-       if (CHECK(btf_fd[0] != -1, "BTF lingers")) {
-               err = -1;
+       err = btf__get_from_id(info.btf_id, &btf);
+       if (CHECK(err, "cannot get btf from kernel, err: %d", err))
                goto done;
+
+       /* check three functions */
+       finfo = func_info;
+       for (i = 0; i < 3; i++) {
+               const struct btf_type *t;
+               const char *fname;
+
+               t = btf__type_by_id(btf, finfo->type_id);
+               if (CHECK(!t, "btf__type_by_id failure: id %u",
+                         finfo->type_id)) {
+                       err = -1;
+                       goto done;
+               }
+
+               fname = btf__name_by_offset(btf, t->name_off);
+               err = strcmp(fname, expected_fnames[i]);
+               /* for the second and third functions in .text section,
+                * the compiler may order them either way.
+                */
+               if (i && err)
+                       err = strcmp(fname, expected_fnames[3 - i]);
+               if (CHECK(err, "incorrect fname %s", fname ? : "")) {
+                       err = -1;
+                       goto done;
+               }
+
+               finfo = (void *)finfo + rec_size;
        }
 
+skip:
        fprintf(stderr, "OK");
 
 done:
-       if (*btf_log_buf && (err || args.always_log))
-               fprintf(stderr, "\n%s", btf_log_buf);
+       free(func_info);
+       bpf_object__close(obj);
+       return err;
+}
 
-       free(raw_btf);
-       if (map_fd != -1)
-               close(map_fd);
-       for (i = 0; i < 2; i++) {
-               free(user_btf[i]);
-               if (btf_fd[i] != -1)
-                       close(btf_fd[i]);
-       }
+static int test_file(void)
+{
+       unsigned int i;
+       int err = 0;
+
+       if (args.file_test_num)
+               return count_result(do_test_file(args.file_test_num));
+
+       for (i = 1; i <= ARRAY_SIZE(file_tests); i++)
+               err |= count_result(do_test_file(i));
+
+       return err;
+}
+
+const char *pprint_enum_str[] = {
+       "ENUM_ZERO",
+       "ENUM_ONE",
+       "ENUM_TWO",
+       "ENUM_THREE",
+};
+
+struct pprint_mapv {
+       uint32_t ui32;
+       uint16_t ui16;
+       /* 2 bytes hole */
+       int32_t si32;
+       uint32_t unused_bits2a:2,
+               bits28:28,
+               unused_bits2b:2;
+       union {
+               uint64_t ui64;
+               uint8_t ui8a[8];
+       };
+       enum {
+               ENUM_ZERO,
+               ENUM_ONE,
+               ENUM_TWO,
+               ENUM_THREE,
+       } aenum;
+};
+
+static struct btf_raw_test pprint_test_template[] = {
+{
+       .raw_types = {
+               /* unsighed char */                     /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1),
+               /* unsigned short */                    /* [2] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2),
+               /* unsigned int */                      /* [3] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
+               /* int */                               /* [4] */
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
+               /* unsigned long long */                /* [5] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),
+               /* 2 bits */                            /* [6] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 2, 2),
+               /* 28 bits */                           /* [7] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 28, 4),
+               /* uint8_t[8] */                        /* [8] */
+               BTF_TYPE_ARRAY_ENC(9, 1, 8),
+               /* typedef unsigned char uint8_t */     /* [9] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 1),
+               /* typedef unsigned short uint16_t */   /* [10] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 2),
+               /* typedef unsigned int uint32_t */     /* [11] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 3),
+               /* typedef int int32_t */               /* [12] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 4),
+               /* typedef unsigned long long uint64_t *//* [13] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 5),
+               /* union (anon) */                      /* [14] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8),
+               BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */
+               BTF_MEMBER_ENC(NAME_TBD, 8, 0), /* uint8_t ui8a[8]; */
+               /* enum (anon) */                       /* [15] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4),
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_ENUM_ENC(NAME_TBD, 1),
+               BTF_ENUM_ENC(NAME_TBD, 2),
+               BTF_ENUM_ENC(NAME_TBD, 3),
+               /* struct pprint_mapv */                /* [16] */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 8), 32),
+               BTF_MEMBER_ENC(NAME_TBD, 11, 0),        /* uint32_t ui32 */
+               BTF_MEMBER_ENC(NAME_TBD, 10, 32),       /* uint16_t ui16 */
+               BTF_MEMBER_ENC(NAME_TBD, 12, 64),       /* int32_t si32 */
+               BTF_MEMBER_ENC(NAME_TBD, 6, 96),        /* unused_bits2a */
+               BTF_MEMBER_ENC(NAME_TBD, 7, 98),        /* bits28 */
+               BTF_MEMBER_ENC(NAME_TBD, 6, 126),       /* unused_bits2b */
+               BTF_MEMBER_ENC(0, 14, 128),             /* union (anon) */
+               BTF_MEMBER_ENC(NAME_TBD, 15, 192),      /* aenum */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum"),
+       .key_size = sizeof(unsigned int),
+       .value_size = sizeof(struct pprint_mapv),
+       .key_type_id = 3,       /* unsigned int */
+       .value_type_id = 16,    /* struct pprint_mapv */
+       .max_entries = 128 * 1024,
+},
 
-       return err;
-}
+{
+       /* this type will have the same type as the
+        * first .raw_types definition, but struct type will
+        * be encoded with kind_flag set.
+        */
+       .raw_types = {
+               /* unsighed char */                     /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1),
+               /* unsigned short */                    /* [2] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2),
+               /* unsigned int */                      /* [3] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
+               /* int */                               /* [4] */
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
+               /* unsigned long long */                /* [5] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),       /* [6] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),       /* [7] */
+               /* uint8_t[8] */                        /* [8] */
+               BTF_TYPE_ARRAY_ENC(9, 1, 8),
+               /* typedef unsigned char uint8_t */     /* [9] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 1),
+               /* typedef unsigned short uint16_t */   /* [10] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 2),
+               /* typedef unsigned int uint32_t */     /* [11] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 3),
+               /* typedef int int32_t */               /* [12] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 4),
+               /* typedef unsigned long long uint64_t *//* [13] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 5),
+               /* union (anon) */                      /* [14] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8),
+               BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */
+               BTF_MEMBER_ENC(NAME_TBD, 8, 0), /* uint8_t ui8a[8]; */
+               /* enum (anon) */                       /* [15] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4),
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_ENUM_ENC(NAME_TBD, 1),
+               BTF_ENUM_ENC(NAME_TBD, 2),
+               BTF_ENUM_ENC(NAME_TBD, 3),
+               /* struct pprint_mapv */                /* [16] */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 8), 32),
+               BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 0)),  /* uint32_t ui32 */
+               BTF_MEMBER_ENC(NAME_TBD, 10, BTF_MEMBER_OFFSET(0, 32)), /* uint16_t ui16 */
+               BTF_MEMBER_ENC(NAME_TBD, 12, BTF_MEMBER_OFFSET(0, 64)), /* int32_t si32 */
+               BTF_MEMBER_ENC(NAME_TBD, 6, BTF_MEMBER_OFFSET(2, 96)),  /* unused_bits2a */
+               BTF_MEMBER_ENC(NAME_TBD, 7, BTF_MEMBER_OFFSET(28, 98)), /* bits28 */
+               BTF_MEMBER_ENC(NAME_TBD, 6, BTF_MEMBER_OFFSET(2, 126)), /* unused_bits2b */
+               BTF_MEMBER_ENC(0, 14, BTF_MEMBER_OFFSET(0, 128)),       /* union (anon) */
+               BTF_MEMBER_ENC(NAME_TBD, 15, BTF_MEMBER_OFFSET(0, 192)),        /* aenum */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum"),
+       .key_size = sizeof(unsigned int),
+       .value_size = sizeof(struct pprint_mapv),
+       .key_type_id = 3,       /* unsigned int */
+       .value_type_id = 16,    /* struct pprint_mapv */
+       .max_entries = 128 * 1024,
+},
 
-static int do_test_get_info(unsigned int test_num)
 {
-       const struct btf_get_info_test *test = &get_info_tests[test_num - 1];
-       unsigned int raw_btf_size, user_btf_size, expected_nbytes;
-       uint8_t *raw_btf = NULL, *user_btf = NULL;
-       struct bpf_btf_info info = {};
-       int btf_fd = -1, err, ret;
-       uint32_t info_len;
+       /* this type will have the same layout as the
+        * first .raw_types definition. The struct type will
+        * be encoded with kind_flag set, bitfield members
+        * are added typedef/const/volatile, and bitfield members
+        * will have both int and enum types.
+        */
+       .raw_types = {
+               /* unsighed char */                     /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1),
+               /* unsigned short */                    /* [2] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2),
+               /* unsigned int */                      /* [3] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
+               /* int */                               /* [4] */
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
+               /* unsigned long long */                /* [5] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),       /* [6] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),       /* [7] */
+               /* uint8_t[8] */                        /* [8] */
+               BTF_TYPE_ARRAY_ENC(9, 1, 8),
+               /* typedef unsigned char uint8_t */     /* [9] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 1),
+               /* typedef unsigned short uint16_t */   /* [10] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 2),
+               /* typedef unsigned int uint32_t */     /* [11] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 3),
+               /* typedef int int32_t */               /* [12] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 4),
+               /* typedef unsigned long long uint64_t *//* [13] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 5),
+               /* union (anon) */                      /* [14] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8),
+               BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */
+               BTF_MEMBER_ENC(NAME_TBD, 8, 0), /* uint8_t ui8a[8]; */
+               /* enum (anon) */                       /* [15] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4),
+               BTF_ENUM_ENC(NAME_TBD, 0),
+               BTF_ENUM_ENC(NAME_TBD, 1),
+               BTF_ENUM_ENC(NAME_TBD, 2),
+               BTF_ENUM_ENC(NAME_TBD, 3),
+               /* struct pprint_mapv */                /* [16] */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 8), 32),
+               BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 0)),  /* uint32_t ui32 */
+               BTF_MEMBER_ENC(NAME_TBD, 10, BTF_MEMBER_OFFSET(0, 32)), /* uint16_t ui16 */
+               BTF_MEMBER_ENC(NAME_TBD, 12, BTF_MEMBER_OFFSET(0, 64)), /* int32_t si32 */
+               BTF_MEMBER_ENC(NAME_TBD, 17, BTF_MEMBER_OFFSET(2, 96)), /* unused_bits2a */
+               BTF_MEMBER_ENC(NAME_TBD, 7, BTF_MEMBER_OFFSET(28, 98)), /* bits28 */
+               BTF_MEMBER_ENC(NAME_TBD, 19, BTF_MEMBER_OFFSET(2, 126)),/* unused_bits2b */
+               BTF_MEMBER_ENC(0, 14, BTF_MEMBER_OFFSET(0, 128)),       /* union (anon) */
+               BTF_MEMBER_ENC(NAME_TBD, 15, BTF_MEMBER_OFFSET(0, 192)),        /* aenum */
+               /* typedef unsigned int ___int */       /* [17] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 18),
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_VOLATILE, 0, 0), 6),      /* [18] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), 15),        /* [19] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum\0___int"),
+       .key_size = sizeof(unsigned int),
+       .value_size = sizeof(struct pprint_mapv),
+       .key_type_id = 3,       /* unsigned int */
+       .value_type_id = 16,    /* struct pprint_mapv */
+       .max_entries = 128 * 1024,
+},
 
-       fprintf(stderr, "BTF GET_INFO test[%u] (%s): ",
-               test_num, test->descr);
+};
 
-       if (test->special_test)
-               return test->special_test(test_num);
+static struct btf_pprint_test_meta {
+       const char *descr;
+       enum bpf_map_type map_type;
+       const char *map_name;
+       bool ordered_map;
+       bool lossless_map;
+       bool percpu_map;
+} pprint_tests_meta[] = {
+{
+       .descr = "BTF pretty print array",
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "pprint_test_array",
+       .ordered_map = true,
+       .lossless_map = true,
+       .percpu_map = false,
+},
 
-       raw_btf = btf_raw_create(&hdr_tmpl,
-                                test->raw_types,
-                                test->str_sec,
-                                test->str_sec_size,
-                                &raw_btf_size);
+{
+       .descr = "BTF pretty print hash",
+       .map_type = BPF_MAP_TYPE_HASH,
+       .map_name = "pprint_test_hash",
+       .ordered_map = false,
+       .lossless_map = true,
+       .percpu_map = false,
+},
 
-       if (!raw_btf)
-               return -1;
+{
+       .descr = "BTF pretty print lru hash",
+       .map_type = BPF_MAP_TYPE_LRU_HASH,
+       .map_name = "pprint_test_lru_hash",
+       .ordered_map = false,
+       .lossless_map = false,
+       .percpu_map = false,
+},
 
-       *btf_log_buf = '\0';
+{
+       .descr = "BTF pretty print percpu array",
+       .map_type = BPF_MAP_TYPE_PERCPU_ARRAY,
+       .map_name = "pprint_test_percpu_array",
+       .ordered_map = true,
+       .lossless_map = true,
+       .percpu_map = true,
+},
 
-       user_btf = malloc(raw_btf_size);
-       if (CHECK(!user_btf, "!user_btf")) {
-               err = -1;
-               goto done;
-       }
+{
+       .descr = "BTF pretty print percpu hash",
+       .map_type = BPF_MAP_TYPE_PERCPU_HASH,
+       .map_name = "pprint_test_percpu_hash",
+       .ordered_map = false,
+       .lossless_map = true,
+       .percpu_map = true,
+},
 
-       btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
-                             btf_log_buf, BTF_LOG_BUF_SIZE,
-                             args.always_log);
-       if (CHECK(btf_fd == -1, "errno:%d", errno)) {
-               err = -1;
-               goto done;
-       }
+{
+       .descr = "BTF pretty print lru percpu hash",
+       .map_type = BPF_MAP_TYPE_LRU_PERCPU_HASH,
+       .map_name = "pprint_test_lru_percpu_hash",
+       .ordered_map = false,
+       .lossless_map = false,
+       .percpu_map = true,
+},
 
-       user_btf_size = (int)raw_btf_size + test->btf_size_delta;
-       expected_nbytes = min(raw_btf_size, user_btf_size);
-       if (raw_btf_size > expected_nbytes)
-               memset(user_btf + expected_nbytes, 0xff,
-                      raw_btf_size - expected_nbytes);
+};
 
-       info_len = sizeof(info);
-       info.btf = ptr_to_u64(user_btf);
-       info.btf_size = user_btf_size;
 
-       ret = 0;
-       err = bpf_obj_get_info_by_fd(btf_fd, &info, &info_len);
-       if (CHECK(err || !info.id || info_len != sizeof(info) ||
-                 info.btf_size != raw_btf_size ||
-                 (ret = memcmp(raw_btf, user_btf, expected_nbytes)),
-                 "err:%d errno:%d info.id:%u info_len:%u sizeof(info):%lu raw_btf_size:%u info.btf_size:%u expected_nbytes:%u memcmp:%d",
-                 err, errno, info.id, info_len, sizeof(info),
-                 raw_btf_size, info.btf_size, expected_nbytes, ret)) {
-               err = -1;
-               goto done;
-       }
+static void set_pprint_mapv(struct pprint_mapv *v, uint32_t i,
+                           int num_cpus, int rounded_value_size)
+{
+       int cpu;
 
-       while (expected_nbytes < raw_btf_size) {
-               fprintf(stderr, "%u...", expected_nbytes);
-               if (CHECK(user_btf[expected_nbytes++] != 0xff,
-                         "user_btf[%u]:%x != 0xff", expected_nbytes - 1,
-                         user_btf[expected_nbytes - 1])) {
-                       err = -1;
-                       goto done;
-               }
+       for (cpu = 0; cpu < num_cpus; cpu++) {
+               v->ui32 = i + cpu;
+               v->si32 = -i;
+               v->unused_bits2a = 3;
+               v->bits28 = i;
+               v->unused_bits2b = 3;
+               v->ui64 = i;
+               v->aenum = i & 0x03;
+               v = (void *)v + rounded_value_size;
        }
-
-       fprintf(stderr, "OK");
-
-done:
-       if (*btf_log_buf && (err || args.always_log))
-               fprintf(stderr, "\n%s", btf_log_buf);
-
-       free(raw_btf);
-       free(user_btf);
-
-       if (btf_fd != -1)
-               close(btf_fd);
-
-       return err;
 }
 
-static int test_get_info(void)
+static int check_line(const char *expected_line, int nexpected_line,
+                     int expected_line_len, const char *line)
 {
-       unsigned int i;
-       int err = 0;
-
-       if (args.get_info_test_num)
-               return count_result(do_test_get_info(args.get_info_test_num));
+       if (CHECK(nexpected_line == expected_line_len,
+                 "expected_line is too long"))
+               return -1;
 
-       for (i = 1; i <= ARRAY_SIZE(get_info_tests); i++)
-               err |= count_result(do_test_get_info(i));
+       if (strcmp(expected_line, line)) {
+               fprintf(stderr, "unexpected pprint output\n");
+               fprintf(stderr, "expected: %s", expected_line);
+               fprintf(stderr, "    read: %s", line);
+               return -1;
+       }
 
-       return err;
+       return 0;
 }
 
-struct btf_file_test {
-       const char *file;
-       bool btf_kv_notfound;
-};
 
-static struct btf_file_test file_tests[] = {
-{
-       .file = "test_btf_haskv.o",
-},
+static int do_test_pprint(int test_num)
 {
-       .file = "test_btf_nokv.o",
-       .btf_kv_notfound = true,
-},
-};
+       const struct btf_raw_test *test = &pprint_test_template[test_num];
+       struct bpf_create_map_attr create_attr = {};
+       bool ordered_map, lossless_map, percpu_map;
+       int err, ret, num_cpus, rounded_value_size;
+       struct pprint_mapv *mapv = NULL;
+       unsigned int key, nr_read_elems;
+       int map_fd = -1, btf_fd = -1;
+       unsigned int raw_btf_size;
+       char expected_line[255];
+       FILE *pin_file = NULL;
+       char pin_path[255];
+       size_t line_len = 0;
+       char *line = NULL;
+       uint8_t *raw_btf;
+       ssize_t nread;
 
-static int file_has_btf_elf(const char *fn)
-{
-       Elf_Scn *scn = NULL;
-       GElf_Ehdr ehdr;
-       int elf_fd;
-       Elf *elf;
-       int ret;
+       fprintf(stderr, "%s(#%d)......", test->descr, test_num);
+       raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
+                                test->str_sec, test->str_sec_size,
+                                &raw_btf_size, NULL);
 
-       if (CHECK(elf_version(EV_CURRENT) == EV_NONE,
-                 "elf_version(EV_CURRENT) == EV_NONE"))
+       if (!raw_btf)
                return -1;
 
-       elf_fd = open(fn, O_RDONLY);
-       if (CHECK(elf_fd == -1, "open(%s): errno:%d", fn, errno))
-               return -1;
+       *btf_log_buf = '\0';
+       btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
+                             btf_log_buf, BTF_LOG_BUF_SIZE,
+                             args.always_log);
+       free(raw_btf);
 
-       elf = elf_begin(elf_fd, ELF_C_READ, NULL);
-       if (CHECK(!elf, "elf_begin(%s): %s", fn, elf_errmsg(elf_errno()))) {
-               ret = -1;
+       if (CHECK(btf_fd == -1, "errno:%d", errno)) {
+               err = -1;
                goto done;
        }
 
-       if (CHECK(!gelf_getehdr(elf, &ehdr), "!gelf_getehdr(%s)", fn)) {
-               ret = -1;
+       create_attr.name = test->map_name;
+       create_attr.map_type = test->map_type;
+       create_attr.key_size = test->key_size;
+       create_attr.value_size = test->value_size;
+       create_attr.max_entries = test->max_entries;
+       create_attr.btf_fd = btf_fd;
+       create_attr.btf_key_type_id = test->key_type_id;
+       create_attr.btf_value_type_id = test->value_type_id;
+
+       map_fd = bpf_create_map_xattr(&create_attr);
+       if (CHECK(map_fd == -1, "errno:%d", errno)) {
+               err = -1;
                goto done;
        }
 
-       while ((scn = elf_nextscn(elf, scn))) {
-               const char *sh_name;
-               GElf_Shdr sh;
-
-               if (CHECK(gelf_getshdr(scn, &sh) != &sh,
-                         "file:%s gelf_getshdr != &sh", fn)) {
-                       ret = -1;
-                       goto done;
-               }
+       ret = snprintf(pin_path, sizeof(pin_path), "%s/%s",
+                      "/sys/fs/bpf", test->map_name);
 
-               sh_name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
-               if (!strcmp(sh_name, BTF_ELF_SEC)) {
-                       ret = 1;
-                       goto done;
-               }
+       if (CHECK(ret == sizeof(pin_path), "pin_path %s/%s is too long",
+                 "/sys/fs/bpf", test->map_name)) {
+               err = -1;
+               goto done;
        }
 
-       ret = 0;
+       err = bpf_obj_pin(map_fd, pin_path);
+       if (CHECK(err, "bpf_obj_pin(%s): errno:%d.", pin_path, errno))
+               goto done;
 
-done:
-       close(elf_fd);
-       elf_end(elf);
-       return ret;
-}
+       percpu_map = test->percpu_map;
+       num_cpus = percpu_map ? bpf_num_possible_cpus() : 1;
+       rounded_value_size = round_up(sizeof(struct pprint_mapv), 8);
+       mapv = calloc(num_cpus, rounded_value_size);
+       if (CHECK(!mapv, "mapv allocation failure")) {
+               err = -1;
+               goto done;
+       }
 
-static int do_test_file(unsigned int test_num)
-{
-       const struct btf_file_test *test = &file_tests[test_num - 1];
-       struct bpf_object *obj = NULL;
-       struct bpf_program *prog;
-       struct bpf_map *map;
-       int err;
+       for (key = 0; key < test->max_entries; key++) {
+               set_pprint_mapv(mapv, key, num_cpus, rounded_value_size);
+               bpf_map_update_elem(map_fd, &key, mapv, 0);
+       }
 
-       fprintf(stderr, "BTF libbpf test[%u] (%s): ", test_num,
-               test->file);
+       pin_file = fopen(pin_path, "r");
+       if (CHECK(!pin_file, "fopen(%s): errno:%d", pin_path, errno)) {
+               err = -1;
+               goto done;
+       }
 
-       err = file_has_btf_elf(test->file);
-       if (err == -1)
-               return err;
+       /* Skip lines start with '#' */
+       while ((nread = getline(&line, &line_len, pin_file)) > 0 &&
+              *line == '#')
+               ;
 
-       if (err == 0) {
-               fprintf(stderr, "SKIP. No ELF %s found", BTF_ELF_SEC);
-               skip_cnt++;
-               return 0;
+       if (CHECK(nread <= 0, "Unexpected EOF")) {
+               err = -1;
+               goto done;
        }
 
-       obj = bpf_object__open(test->file);
-       if (CHECK(IS_ERR(obj), "obj: %ld", PTR_ERR(obj)))
-               return PTR_ERR(obj);
+       nr_read_elems = 0;
+       ordered_map = test->ordered_map;
+       lossless_map = test->lossless_map;
+       do {
+               struct pprint_mapv *cmapv;
+               ssize_t nexpected_line;
+               unsigned int next_key;
+               int cpu;
 
-       err = bpf_object__btf_fd(obj);
-       if (CHECK(err == -1, "bpf_object__btf_fd: -1"))
-               goto done;
+               next_key = ordered_map ? nr_read_elems : atoi(line);
+               set_pprint_mapv(mapv, next_key, num_cpus, rounded_value_size);
+               cmapv = mapv;
 
-       prog = bpf_program__next(NULL, obj);
-       if (CHECK(!prog, "Cannot find bpf_prog")) {
+               for (cpu = 0; cpu < num_cpus; cpu++) {
+                       if (percpu_map) {
+                               /* for percpu map, the format looks like:
+                                * <key>: {
+                                *      cpu0: <value_on_cpu0>
+                                *      cpu1: <value_on_cpu1>
+                                *      ...
+                                *      cpun: <value_on_cpun>
+                                * }
+                                *
+                                * let us verify the line containing the key here.
+                                */
+                               if (cpu == 0) {
+                                       nexpected_line = snprintf(expected_line,
+                                                                 sizeof(expected_line),
+                                                                 "%u: {\n",
+                                                                 next_key);
+
+                                       err = check_line(expected_line, nexpected_line,
+                                                        sizeof(expected_line), line);
+                                       if (err == -1)
+                                               goto done;
+                               }
+
+                               /* read value@cpu */
+                               nread = getline(&line, &line_len, pin_file);
+                               if (nread < 0)
+                                       break;
+                       }
+
+                       nexpected_line = snprintf(expected_line, sizeof(expected_line),
+                                                 "%s%u: {%u,0,%d,0x%x,0x%x,0x%x,"
+                                                 "{%lu|[%u,%u,%u,%u,%u,%u,%u,%u]},%s}\n",
+                                                 percpu_map ? "\tcpu" : "",
+                                                 percpu_map ? cpu : next_key,
+                                                 cmapv->ui32, cmapv->si32,
+                                                 cmapv->unused_bits2a,
+                                                 cmapv->bits28,
+                                                 cmapv->unused_bits2b,
+                                                 cmapv->ui64,
+                                                 cmapv->ui8a[0], cmapv->ui8a[1],
+                                                 cmapv->ui8a[2], cmapv->ui8a[3],
+                                                 cmapv->ui8a[4], cmapv->ui8a[5],
+                                                 cmapv->ui8a[6], cmapv->ui8a[7],
+                                                 pprint_enum_str[cmapv->aenum]);
+
+                       err = check_line(expected_line, nexpected_line,
+                                        sizeof(expected_line), line);
+                       if (err == -1)
+                               goto done;
+
+                       cmapv = (void *)cmapv + rounded_value_size;
+               }
+
+               if (percpu_map) {
+                       /* skip the last bracket for the percpu map */
+                       nread = getline(&line, &line_len, pin_file);
+                       if (nread < 0)
+                               break;
+               }
+
+               nread = getline(&line, &line_len, pin_file);
+       } while (++nr_read_elems < test->max_entries && nread > 0);
+
+       if (lossless_map &&
+           CHECK(nr_read_elems < test->max_entries,
+                 "Unexpected EOF. nr_read_elems:%u test->max_entries:%u",
+                 nr_read_elems, test->max_entries)) {
                err = -1;
                goto done;
        }
 
-       bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT);
-       err = bpf_object__load(obj);
-       if (CHECK(err < 0, "bpf_object__load: %d", err))
-               goto done;
-
-       map = bpf_object__find_map_by_name(obj, "btf_map");
-       if (CHECK(!map, "btf_map not found")) {
+       if (CHECK(nread > 0, "Unexpected extra pprint output: %s", line)) {
                err = -1;
                goto done;
        }
 
-       err = (bpf_map__btf_key_type_id(map) == 0 || bpf_map__btf_value_type_id(map) == 0)
-               != test->btf_kv_notfound;
-       if (CHECK(err, "btf_key_type_id:%u btf_value_type_id:%u test->btf_kv_notfound:%u",
-                 bpf_map__btf_key_type_id(map), bpf_map__btf_value_type_id(map),
-                 test->btf_kv_notfound))
-               goto done;
+       err = 0;
 
-       fprintf(stderr, "OK");
+done:
+       if (mapv)
+               free(mapv);
+       if (!err)
+               fprintf(stderr, "OK");
+       if (*btf_log_buf && (err || args.always_log))
+               fprintf(stderr, "\n%s", btf_log_buf);
+       if (btf_fd != -1)
+               close(btf_fd);
+       if (map_fd != -1)
+               close(map_fd);
+       if (pin_file)
+               fclose(pin_file);
+       unlink(pin_path);
+       free(line);
 
-done:
-       bpf_object__close(obj);
        return err;
 }
 
-static int test_file(void)
+static int test_pprint(void)
 {
        unsigned int i;
        int err = 0;
 
-       if (args.file_test_num)
-               return count_result(do_test_file(args.file_test_num));
+       /* test various maps with the first test template */
+       for (i = 0; i < ARRAY_SIZE(pprint_tests_meta); i++) {
+               pprint_test_template[0].descr = pprint_tests_meta[i].descr;
+               pprint_test_template[0].map_type = pprint_tests_meta[i].map_type;
+               pprint_test_template[0].map_name = pprint_tests_meta[i].map_name;
+               pprint_test_template[0].ordered_map = pprint_tests_meta[i].ordered_map;
+               pprint_test_template[0].lossless_map = pprint_tests_meta[i].lossless_map;
+               pprint_test_template[0].percpu_map = pprint_tests_meta[i].percpu_map;
+
+               err |= count_result(do_test_pprint(0));
+       }
 
-       for (i = 1; i <= ARRAY_SIZE(file_tests); i++)
-               err |= count_result(do_test_file(i));
+       /* test rest test templates with the first map */
+       for (i = 1; i < ARRAY_SIZE(pprint_test_template); i++) {
+               pprint_test_template[i].descr = pprint_tests_meta[0].descr;
+               pprint_test_template[i].map_type = pprint_tests_meta[0].map_type;
+               pprint_test_template[i].map_name = pprint_tests_meta[0].map_name;
+               pprint_test_template[i].ordered_map = pprint_tests_meta[0].ordered_map;
+               pprint_test_template[i].lossless_map = pprint_tests_meta[0].lossless_map;
+               pprint_test_template[i].percpu_map = pprint_tests_meta[0].percpu_map;
+               err |= count_result(do_test_pprint(i));
+       }
 
        return err;
 }
 
-const char *pprint_enum_str[] = {
-       "ENUM_ZERO",
-       "ENUM_ONE",
-       "ENUM_TWO",
-       "ENUM_THREE",
-};
+#define BPF_LINE_INFO_ENC(insn_off, file_off, line_off, line_num, line_col) \
+       (insn_off), (file_off), (line_off), ((line_num) << 10 | ((line_col) & 0x3ff))
 
-struct pprint_mapv {
-       uint32_t ui32;
-       uint16_t ui16;
-       /* 2 bytes hole */
-       int32_t si32;
-       uint32_t unused_bits2a:2,
-               bits28:28,
-               unused_bits2b:2;
-       union {
-               uint64_t ui64;
-               uint8_t ui8a[8];
-       };
-       enum {
-               ENUM_ZERO,
-               ENUM_ONE,
-               ENUM_TWO,
-               ENUM_THREE,
-       } aenum;
-};
+static struct prog_info_raw_test {
+       const char *descr;
+       const char *str_sec;
+       const char *err_str;
+       __u32 raw_types[MAX_NR_RAW_U32];
+       __u32 str_sec_size;
+       struct bpf_insn insns[MAX_INSNS];
+       __u32 prog_type;
+       __u32 func_info[MAX_SUBPROGS][2];
+       __u32 func_info_rec_size;
+       __u32 func_info_cnt;
+       __u32 line_info[MAX_NR_RAW_U32];
+       __u32 line_info_rec_size;
+       __u32 nr_jited_ksyms;
+       bool expected_prog_load_failure;
+} info_raw_tests[] = {
+{
+       .descr = "func_type (main func + one sub)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),        /* [2] */
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [4] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [5] */
+               BTF_FUNC_ENC(NAME_TBD, 4),                      /* [6] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB",
+       .str_sec_size = sizeof("\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB"),
+       .insns = {
+               BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info = { {0, 5}, {3, 6} },
+       .func_info_rec_size = 8,
+       .func_info_cnt = 2,
+       .line_info = { BTF_END_RAW },
+},
 
-static struct btf_raw_test pprint_test_template = {
+{
+       .descr = "func_type (Incorrect func_info_rec_size)",
        .raw_types = {
-               /* unsighed char */                     /* [1] */
-               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1),
-               /* unsigned short */                    /* [2] */
-               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2),
-               /* unsigned int */                      /* [3] */
-               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
-               /* int */                               /* [4] */
-               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
-               /* unsigned long long */                /* [5] */
-               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),
-               /* 2 bits */                            /* [6] */
-               BTF_TYPE_INT_ENC(0, 0, 0, 2, 2),
-               /* 28 bits */                           /* [7] */
-               BTF_TYPE_INT_ENC(0, 0, 0, 28, 4),
-               /* uint8_t[8] */                        /* [8] */
-               BTF_TYPE_ARRAY_ENC(9, 1, 8),
-               /* typedef unsigned char uint8_t */     /* [9] */
-               BTF_TYPEDEF_ENC(NAME_TBD, 1),
-               /* typedef unsigned short uint16_t */   /* [10] */
-               BTF_TYPEDEF_ENC(NAME_TBD, 2),
-               /* typedef unsigned int uint32_t */     /* [11] */
-               BTF_TYPEDEF_ENC(NAME_TBD, 3),
-               /* typedef int int32_t */               /* [12] */
-               BTF_TYPEDEF_ENC(NAME_TBD, 4),
-               /* typedef unsigned long long uint64_t *//* [13] */
-               BTF_TYPEDEF_ENC(NAME_TBD, 5),
-               /* union (anon) */                      /* [14] */
-               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8),
-               BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */
-               BTF_MEMBER_ENC(NAME_TBD, 8, 0), /* uint8_t ui8a[8]; */
-               /* enum (anon) */                       /* [15] */
-               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4),
-               BTF_ENUM_ENC(NAME_TBD, 0),
-               BTF_ENUM_ENC(NAME_TBD, 1),
-               BTF_ENUM_ENC(NAME_TBD, 2),
-               BTF_ENUM_ENC(NAME_TBD, 3),
-               /* struct pprint_mapv */                /* [16] */
-               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 8), 32),
-               BTF_MEMBER_ENC(NAME_TBD, 11, 0),        /* uint32_t ui32 */
-               BTF_MEMBER_ENC(NAME_TBD, 10, 32),       /* uint16_t ui16 */
-               BTF_MEMBER_ENC(NAME_TBD, 12, 64),       /* int32_t si32 */
-               BTF_MEMBER_ENC(NAME_TBD, 6, 96),        /* unused_bits2a */
-               BTF_MEMBER_ENC(NAME_TBD, 7, 98),        /* bits28 */
-               BTF_MEMBER_ENC(NAME_TBD, 6, 126),       /* unused_bits2b */
-               BTF_MEMBER_ENC(0, 14, 128),             /* union (anon) */
-               BTF_MEMBER_ENC(NAME_TBD, 15, 192),      /* aenum */
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),        /* [2] */
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [4] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [5] */
+               BTF_FUNC_ENC(NAME_TBD, 4),                      /* [6] */
                BTF_END_RAW,
        },
-       .str_sec = "\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum",
-       .str_sec_size = sizeof("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum"),
-       .key_size = sizeof(unsigned int),
-       .value_size = sizeof(struct pprint_mapv),
-       .key_type_id = 3,       /* unsigned int */
-       .value_type_id = 16,    /* struct pprint_mapv */
-       .max_entries = 128 * 1024,
-};
+       .str_sec = "\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB",
+       .str_sec_size = sizeof("\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB"),
+       .insns = {
+               BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info = { {0, 5}, {3, 6} },
+       .func_info_rec_size = 4,
+       .func_info_cnt = 2,
+       .line_info = { BTF_END_RAW },
+       .expected_prog_load_failure = true,
+},
 
-static struct btf_pprint_test_meta {
-       const char *descr;
-       enum bpf_map_type map_type;
-       const char *map_name;
-       bool ordered_map;
-       bool lossless_map;
-       bool percpu_map;
-} pprint_tests_meta[] = {
 {
-       .descr = "BTF pretty print array",
-       .map_type = BPF_MAP_TYPE_ARRAY,
-       .map_name = "pprint_test_array",
-       .ordered_map = true,
-       .lossless_map = true,
-       .percpu_map = false,
+       .descr = "func_type (Incorrect func_info_cnt)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),        /* [2] */
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [4] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [5] */
+               BTF_FUNC_ENC(NAME_TBD, 4),                      /* [6] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB",
+       .str_sec_size = sizeof("\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB"),
+       .insns = {
+               BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info = { {0, 5}, {3, 6} },
+       .func_info_rec_size = 8,
+       .func_info_cnt = 1,
+       .line_info = { BTF_END_RAW },
+       .expected_prog_load_failure = true,
 },
 
 {
-       .descr = "BTF pretty print hash",
-       .map_type = BPF_MAP_TYPE_HASH,
-       .map_name = "pprint_test_hash",
-       .ordered_map = false,
-       .lossless_map = true,
-       .percpu_map = false,
+       .descr = "func_type (Incorrect bpf_func_info.insn_off)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),        /* [2] */
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [4] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [5] */
+               BTF_FUNC_ENC(NAME_TBD, 4),                      /* [6] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB",
+       .str_sec_size = sizeof("\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB"),
+       .insns = {
+               BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info = { {0, 5}, {2, 6} },
+       .func_info_rec_size = 8,
+       .func_info_cnt = 2,
+       .line_info = { BTF_END_RAW },
+       .expected_prog_load_failure = true,
 },
 
 {
-       .descr = "BTF pretty print lru hash",
-       .map_type = BPF_MAP_TYPE_LRU_HASH,
-       .map_name = "pprint_test_lru_hash",
-       .ordered_map = false,
-       .lossless_map = false,
-       .percpu_map = false,
+       .descr = "line_info (No subprog)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1;\0int b=2;\0return a + b;\0return a + b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(1, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(3, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 1,
 },
 
 {
-       .descr = "BTF pretty print percpu array",
-       .map_type = BPF_MAP_TYPE_PERCPU_ARRAY,
-       .map_name = "pprint_test_percpu_array",
-       .ordered_map = true,
-       .lossless_map = true,
-       .percpu_map = true,
+       .descr = "line_info (No subprog. insn_off >= prog->len)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1;\0int b=2;\0return a + b;\0return a + b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(1, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(3, 0, NAME_TBD, 4, 7),
+               BPF_LINE_INFO_ENC(4, 0, 0, 5, 6),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 1,
+       .err_str = "line_info[4].insn_off",
+       .expected_prog_load_failure = true,
 },
 
 {
-       .descr = "BTF pretty print percpu hash",
-       .map_type = BPF_MAP_TYPE_PERCPU_HASH,
-       .map_name = "pprint_test_percpu_hash",
-       .ordered_map = false,
-       .lossless_map = true,
-       .percpu_map = true,
+       .descr = "line_info (Zero bpf insn code)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),        /* [2] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 2),                   /* [3] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0unsigned long\0u64\0u64 a=1;\0return a;"),
+       .insns = {
+               BPF_LD_IMM64(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(1, 0, 0, 2, 9),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 1,
+       .err_str = "Invalid insn code at line_info[1]",
+       .expected_prog_load_failure = true,
 },
 
 {
-       .descr = "BTF pretty print lru percpu hash",
-       .map_type = BPF_MAP_TYPE_LRU_PERCPU_HASH,
-       .map_name = "pprint_test_lru_percpu_hash",
-       .ordered_map = false,
-       .lossless_map = false,
-       .percpu_map = true,
+       .descr = "line_info (No subprog. zero tailing line_info",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1;\0int b=2;\0return a + b;\0return a + b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10), 0,
+               BPF_LINE_INFO_ENC(1, 0, NAME_TBD, 2, 9), 0,
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8), 0,
+               BPF_LINE_INFO_ENC(3, 0, NAME_TBD, 4, 7), 0,
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info) + sizeof(__u32),
+       .nr_jited_ksyms = 1,
+},
+
+{
+       .descr = "line_info (No subprog. nonzero tailing line_info)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1;\0int b=2;\0return a + b;\0return a + b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10), 0,
+               BPF_LINE_INFO_ENC(1, 0, NAME_TBD, 2, 9), 0,
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8), 0,
+               BPF_LINE_INFO_ENC(3, 0, NAME_TBD, 4, 7), 1,
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info) + sizeof(__u32),
+       .nr_jited_ksyms = 1,
+       .err_str = "nonzero tailing record in line_info",
+       .expected_prog_load_failure = true,
+},
+
+{
+       .descr = "line_info (subprog)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1+1;\0return func(a);\0b+=1;\0return b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_2, 1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
+               BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+               BPF_CALL_REL(1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(5, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(7, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 2,
+},
+
+{
+       .descr = "line_info (subprog + func_info)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_FUNC_PROTO_ENC(1, 1),                       /* [2] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+               BTF_FUNC_ENC(NAME_TBD, 2),                      /* [3] */
+               BTF_FUNC_ENC(NAME_TBD, 2),                      /* [4] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0x\0sub\0main\0int a=1+1;\0return func(a);\0b+=1;\0return b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_2, 1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
+               BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+               BPF_CALL_REL(1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 2,
+       .func_info_rec_size = 8,
+       .func_info = { {0, 4}, {5, 3} },
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(5, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(7, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 2,
+},
+
+{
+       .descr = "line_info (subprog. missing 1st func line info)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1+1;\0return func(a);\0b+=1;\0return b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_2, 1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
+               BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+               BPF_CALL_REL(1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(1, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(5, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(7, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 2,
+       .err_str = "missing bpf_line_info for func#0",
+       .expected_prog_load_failure = true,
+},
+
+{
+       .descr = "line_info (subprog. missing 2nd func line info)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1+1;\0return func(a);\0b+=1;\0return b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_2, 1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
+               BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+               BPF_CALL_REL(1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(6, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(7, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 2,
+       .err_str = "missing bpf_line_info for func#1",
+       .expected_prog_load_failure = true,
+},
+
+{
+       .descr = "line_info (subprog. unordered insn offset)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1+1;\0return func(a);\0b+=1;\0return b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_2, 1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
+               BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+               BPF_CALL_REL(1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(5, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(7, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 2,
+       .err_str = "Invalid line_info[2].insn_off",
+       .expected_prog_load_failure = true,
 },
 
 };
 
+static size_t probe_prog_length(const struct bpf_insn *fp)
+{
+       size_t len;
 
-static void set_pprint_mapv(struct pprint_mapv *v, uint32_t i,
-                           int num_cpus, int rounded_value_size)
+       for (len = MAX_INSNS - 1; len > 0; --len)
+               if (fp[len].code != 0 || fp[len].imm != 0)
+                       break;
+       return len + 1;
+}
+
+static __u32 *patch_name_tbd(const __u32 *raw_u32,
+                            const char *str, __u32 str_off,
+                            unsigned int str_sec_size,
+                            unsigned int *ret_size)
 {
-       int cpu;
+       int i, raw_u32_size = get_raw_sec_size(raw_u32);
+       const char *end_str = str + str_sec_size;
+       const char *next_str = str + str_off;
+       __u32 *new_u32 = NULL;
 
-       for (cpu = 0; cpu < num_cpus; cpu++) {
-               v->ui32 = i + cpu;
-               v->si32 = -i;
-               v->unused_bits2a = 3;
-               v->bits28 = i;
-               v->unused_bits2b = 3;
-               v->ui64 = i;
-               v->aenum = i & 0x03;
-               v = (void *)v + rounded_value_size;
+       if (raw_u32_size == -1)
+               return ERR_PTR(-EINVAL);
+
+       if (!raw_u32_size) {
+               *ret_size = 0;
+               return NULL;
+       }
+
+       new_u32 = malloc(raw_u32_size);
+       if (!new_u32)
+               return ERR_PTR(-ENOMEM);
+
+       for (i = 0; i < raw_u32_size / sizeof(raw_u32[0]); i++) {
+               if (raw_u32[i] == NAME_TBD) {
+                       next_str = get_next_str(next_str, end_str);
+                       if (CHECK(!next_str, "Error in getting next_str\n")) {
+                               free(new_u32);
+                               return ERR_PTR(-EINVAL);
+                       }
+                       new_u32[i] = next_str - str;
+                       next_str += strlen(next_str);
+               } else {
+                       new_u32[i] = raw_u32[i];
+               }
        }
+
+       *ret_size = raw_u32_size;
+       return new_u32;
 }
 
-static int check_line(const char *expected_line, int nexpected_line,
-                     int expected_line_len, const char *line)
+static int test_get_finfo(const struct prog_info_raw_test *test,
+                         int prog_fd)
 {
-       if (CHECK(nexpected_line == expected_line_len,
-                 "expected_line is too long"))
+       struct bpf_prog_info info = {};
+       struct bpf_func_info *finfo;
+       __u32 info_len, rec_size, i;
+       void *func_info = NULL;
+       int err;
+
+       /* get necessary lens */
+       info_len = sizeof(struct bpf_prog_info);
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (CHECK(err == -1, "invalid get info (1st) errno:%d", errno)) {
+               fprintf(stderr, "%s\n", btf_log_buf);
+               return -1;
+       }
+       if (CHECK(info.nr_func_info != test->func_info_cnt,
+                 "incorrect info.nr_func_info (1st) %d",
+                 info.nr_func_info)) {
                return -1;
+       }
 
-       if (strcmp(expected_line, line)) {
-               fprintf(stderr, "unexpected pprint output\n");
-               fprintf(stderr, "expected: %s", expected_line);
-               fprintf(stderr, "    read: %s", line);
+       rec_size = info.func_info_rec_size;
+       if (CHECK(rec_size != sizeof(struct bpf_func_info),
+                 "incorrect info.func_info_rec_size (1st) %d", rec_size)) {
                return -1;
        }
 
-       return 0;
-}
+       if (!info.nr_func_info)
+               return 0;
 
+       func_info = malloc(info.nr_func_info * rec_size);
+       if (CHECK(!func_info, "out of memory"))
+               return -1;
 
-static int do_test_pprint(void)
-{
-       const struct btf_raw_test *test = &pprint_test_template;
-       struct bpf_create_map_attr create_attr = {};
-       bool ordered_map, lossless_map, percpu_map;
-       int err, ret, num_cpus, rounded_value_size;
-       struct pprint_mapv *mapv = NULL;
-       unsigned int key, nr_read_elems;
-       int map_fd = -1, btf_fd = -1;
-       unsigned int raw_btf_size;
-       char expected_line[255];
-       FILE *pin_file = NULL;
-       char pin_path[255];
-       size_t line_len = 0;
-       char *line = NULL;
-       uint8_t *raw_btf;
-       ssize_t nread;
+       /* reset info to only retrieve func_info related data */
+       memset(&info, 0, sizeof(info));
+       info.nr_func_info = test->func_info_cnt;
+       info.func_info_rec_size = rec_size;
+       info.func_info = ptr_to_u64(func_info);
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (CHECK(err == -1, "invalid get info (2nd) errno:%d", errno)) {
+               fprintf(stderr, "%s\n", btf_log_buf);
+               err = -1;
+               goto done;
+       }
+       if (CHECK(info.nr_func_info != test->func_info_cnt,
+                 "incorrect info.nr_func_info (2nd) %d",
+                 info.nr_func_info)) {
+               err = -1;
+               goto done;
+       }
+       if (CHECK(info.func_info_rec_size != rec_size,
+                 "incorrect info.func_info_rec_size (2nd) %d",
+                 info.func_info_rec_size)) {
+               err = -1;
+               goto done;
+       }
 
-       fprintf(stderr, "%s......", test->descr);
-       raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
-                                test->str_sec, test->str_sec_size,
-                                &raw_btf_size);
+       finfo = func_info;
+       for (i = 0; i < test->func_info_cnt; i++) {
+               if (CHECK(finfo->type_id != test->func_info[i][1],
+                         "incorrect func_type %u expected %u",
+                         finfo->type_id, test->func_info[i][1])) {
+                       err = -1;
+                       goto done;
+               }
+               finfo = (void *)finfo + rec_size;
+       }
 
-       if (!raw_btf)
-               return -1;
+       err = 0;
 
-       *btf_log_buf = '\0';
-       btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
-                             btf_log_buf, BTF_LOG_BUF_SIZE,
-                             args.always_log);
-       free(raw_btf);
+done:
+       free(func_info);
+       return err;
+}
 
-       if (CHECK(btf_fd == -1, "errno:%d", errno)) {
+static int test_get_linfo(const struct prog_info_raw_test *test,
+                         const void *patched_linfo,
+                         __u32 cnt, int prog_fd)
+{
+       __u32 i, info_len, nr_jited_ksyms, nr_jited_func_lens;
+       __u64 *jited_linfo = NULL, *jited_ksyms = NULL;
+       __u32 rec_size, jited_rec_size, jited_cnt;
+       struct bpf_line_info *linfo = NULL;
+       __u32 cur_func_len, ksyms_found;
+       struct bpf_prog_info info = {};
+       __u32 *jited_func_lens = NULL;
+       __u64 cur_func_ksyms;
+       int err;
+
+       jited_cnt = cnt;
+       rec_size = sizeof(*linfo);
+       jited_rec_size = sizeof(*jited_linfo);
+       if (test->nr_jited_ksyms)
+               nr_jited_ksyms = test->nr_jited_ksyms;
+       else
+               nr_jited_ksyms = test->func_info_cnt;
+       nr_jited_func_lens = nr_jited_ksyms;
+
+       info_len = sizeof(struct bpf_prog_info);
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (CHECK(err == -1, "err:%d errno:%d", err, errno)) {
                err = -1;
                goto done;
        }
 
-       create_attr.name = test->map_name;
-       create_attr.map_type = test->map_type;
-       create_attr.key_size = test->key_size;
-       create_attr.value_size = test->value_size;
-       create_attr.max_entries = test->max_entries;
-       create_attr.btf_fd = btf_fd;
-       create_attr.btf_key_type_id = test->key_type_id;
-       create_attr.btf_value_type_id = test->value_type_id;
+       if (!info.jited_prog_len) {
+               /* prog is not jited */
+               jited_cnt = 0;
+               nr_jited_ksyms = 1;
+               nr_jited_func_lens = 1;
+       }
 
-       map_fd = bpf_create_map_xattr(&create_attr);
-       if (CHECK(map_fd == -1, "errno:%d", errno)) {
+       if (CHECK(info.nr_line_info != cnt ||
+                 info.nr_jited_line_info != jited_cnt ||
+                 info.nr_jited_ksyms != nr_jited_ksyms ||
+                 info.nr_jited_func_lens != nr_jited_func_lens ||
+                 (!info.nr_line_info && info.nr_jited_line_info),
+                 "info: nr_line_info:%u(expected:%u) nr_jited_line_info:%u(expected:%u) nr_jited_ksyms:%u(expected:%u) nr_jited_func_lens:%u(expected:%u)",
+                 info.nr_line_info, cnt,
+                 info.nr_jited_line_info, jited_cnt,
+                 info.nr_jited_ksyms, nr_jited_ksyms,
+                 info.nr_jited_func_lens, nr_jited_func_lens)) {
                err = -1;
                goto done;
        }
 
-       ret = snprintf(pin_path, sizeof(pin_path), "%s/%s",
-                      "/sys/fs/bpf", test->map_name);
-
-       if (CHECK(ret == sizeof(pin_path), "pin_path %s/%s is too long",
-                 "/sys/fs/bpf", test->map_name)) {
+       if (CHECK(info.line_info_rec_size != sizeof(struct bpf_line_info) ||
+                 info.jited_line_info_rec_size != sizeof(__u64),
+                 "info: line_info_rec_size:%u(userspace expected:%u) jited_line_info_rec_size:%u(userspace expected:%u)",
+                 info.line_info_rec_size, rec_size,
+                 info.jited_line_info_rec_size, jited_rec_size)) {
                err = -1;
                goto done;
        }
 
-       err = bpf_obj_pin(map_fd, pin_path);
-       if (CHECK(err, "bpf_obj_pin(%s): errno:%d.", pin_path, errno))
-               goto done;
+       if (!cnt)
+               return 0;
 
-       percpu_map = test->percpu_map;
-       num_cpus = percpu_map ? bpf_num_possible_cpus() : 1;
-       rounded_value_size = round_up(sizeof(struct pprint_mapv), 8);
-       mapv = calloc(num_cpus, rounded_value_size);
-       if (CHECK(!mapv, "mapv allocation failure")) {
+       rec_size = info.line_info_rec_size;
+       jited_rec_size = info.jited_line_info_rec_size;
+
+       memset(&info, 0, sizeof(info));
+
+       linfo = calloc(cnt, rec_size);
+       if (CHECK(!linfo, "!linfo")) {
                err = -1;
                goto done;
        }
+       info.nr_line_info = cnt;
+       info.line_info_rec_size = rec_size;
+       info.line_info = ptr_to_u64(linfo);
+
+       if (jited_cnt) {
+               jited_linfo = calloc(jited_cnt, jited_rec_size);
+               jited_ksyms = calloc(nr_jited_ksyms, sizeof(*jited_ksyms));
+               jited_func_lens = calloc(nr_jited_func_lens,
+                                        sizeof(*jited_func_lens));
+               if (CHECK(!jited_linfo || !jited_ksyms || !jited_func_lens,
+                         "jited_linfo:%p jited_ksyms:%p jited_func_lens:%p",
+                         jited_linfo, jited_ksyms, jited_func_lens)) {
+                       err = -1;
+                       goto done;
+               }
 
-       for (key = 0; key < test->max_entries; key++) {
-               set_pprint_mapv(mapv, key, num_cpus, rounded_value_size);
-               bpf_map_update_elem(map_fd, &key, mapv, 0);
+               info.nr_jited_line_info = jited_cnt;
+               info.jited_line_info_rec_size = jited_rec_size;
+               info.jited_line_info = ptr_to_u64(jited_linfo);
+               info.nr_jited_ksyms = nr_jited_ksyms;
+               info.jited_ksyms = ptr_to_u64(jited_ksyms);
+               info.nr_jited_func_lens = nr_jited_func_lens;
+               info.jited_func_lens = ptr_to_u64(jited_func_lens);
        }
 
-       pin_file = fopen(pin_path, "r");
-       if (CHECK(!pin_file, "fopen(%s): errno:%d", pin_path, errno)) {
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+
+       /*
+        * Only recheck the info.*line_info* fields.
+        * Other fields are not the concern of this test.
+        */
+       if (CHECK(err == -1 ||
+                 info.nr_line_info != cnt ||
+                 (jited_cnt && !info.jited_line_info) ||
+                 info.nr_jited_line_info != jited_cnt ||
+                 info.line_info_rec_size != rec_size ||
+                 info.jited_line_info_rec_size != jited_rec_size,
+                 "err:%d errno:%d info: nr_line_info:%u(expected:%u) nr_jited_line_info:%u(expected:%u) line_info_rec_size:%u(expected:%u) jited_linfo_rec_size:%u(expected:%u) line_info:%p jited_line_info:%p",
+                 err, errno,
+                 info.nr_line_info, cnt,
+                 info.nr_jited_line_info, jited_cnt,
+                 info.line_info_rec_size, rec_size,
+                 info.jited_line_info_rec_size, jited_rec_size,
+                 (void *)(long)info.line_info,
+                 (void *)(long)info.jited_line_info)) {
                err = -1;
                goto done;
        }
 
-       /* Skip lines start with '#' */
-       while ((nread = getline(&line, &line_len, pin_file)) > 0 &&
-              *line == '#')
-               ;
+       CHECK(linfo[0].insn_off, "linfo[0].insn_off:%u",
+             linfo[0].insn_off);
+       for (i = 1; i < cnt; i++) {
+               const struct bpf_line_info *expected_linfo;
 
-       if (CHECK(nread <= 0, "Unexpected EOF")) {
+               expected_linfo = patched_linfo + (i * test->line_info_rec_size);
+               if (CHECK(linfo[i].insn_off <= linfo[i - 1].insn_off,
+                         "linfo[%u].insn_off:%u <= linfo[%u].insn_off:%u",
+                         i, linfo[i].insn_off,
+                         i - 1, linfo[i - 1].insn_off)) {
+                       err = -1;
+                       goto done;
+               }
+               if (CHECK(linfo[i].file_name_off != expected_linfo->file_name_off ||
+                         linfo[i].line_off != expected_linfo->line_off ||
+                         linfo[i].line_col != expected_linfo->line_col,
+                         "linfo[%u] (%u, %u, %u) != (%u, %u, %u)", i,
+                         linfo[i].file_name_off,
+                         linfo[i].line_off,
+                         linfo[i].line_col,
+                         expected_linfo->file_name_off,
+                         expected_linfo->line_off,
+                         expected_linfo->line_col)) {
+                       err = -1;
+                       goto done;
+               }
+       }
+
+       if (!jited_cnt) {
+               fprintf(stderr, "not jited. skipping jited_line_info check. ");
+               err = 0;
+               goto done;
+       }
+
+       if (CHECK(jited_linfo[0] != jited_ksyms[0],
+                 "jited_linfo[0]:%lx != jited_ksyms[0]:%lx",
+                 (long)(jited_linfo[0]), (long)(jited_ksyms[0]))) {
                err = -1;
                goto done;
        }
 
-       nr_read_elems = 0;
-       ordered_map = test->ordered_map;
-       lossless_map = test->lossless_map;
-       do {
-               struct pprint_mapv *cmapv;
-               ssize_t nexpected_line;
-               unsigned int next_key;
-               int cpu;
+       ksyms_found = 1;
+       cur_func_len = jited_func_lens[0];
+       cur_func_ksyms = jited_ksyms[0];
+       for (i = 1; i < jited_cnt; i++) {
+               if (ksyms_found < nr_jited_ksyms &&
+                   jited_linfo[i] == jited_ksyms[ksyms_found]) {
+                       cur_func_ksyms = jited_ksyms[ksyms_found];
+                       cur_func_len = jited_ksyms[ksyms_found];
+                       ksyms_found++;
+                       continue;
+               }
 
-               next_key = ordered_map ? nr_read_elems : atoi(line);
-               set_pprint_mapv(mapv, next_key, num_cpus, rounded_value_size);
-               cmapv = mapv;
+               if (CHECK(jited_linfo[i] <= jited_linfo[i - 1],
+                         "jited_linfo[%u]:%lx <= jited_linfo[%u]:%lx",
+                         i, (long)jited_linfo[i],
+                         i - 1, (long)(jited_linfo[i - 1]))) {
+                       err = -1;
+                       goto done;
+               }
 
-               for (cpu = 0; cpu < num_cpus; cpu++) {
-                       if (percpu_map) {
-                               /* for percpu map, the format looks like:
-                                * <key>: {
-                                *      cpu0: <value_on_cpu0>
-                                *      cpu1: <value_on_cpu1>
-                                *      ...
-                                *      cpun: <value_on_cpun>
-                                * }
-                                *
-                                * let us verify the line containing the key here.
-                                */
-                               if (cpu == 0) {
-                                       nexpected_line = snprintf(expected_line,
-                                                                 sizeof(expected_line),
-                                                                 "%u: {\n",
-                                                                 next_key);
+               if (CHECK(jited_linfo[i] - cur_func_ksyms > cur_func_len,
+                         "jited_linfo[%u]:%lx - %lx > %u",
+                         i, (long)jited_linfo[i], (long)cur_func_ksyms,
+                         cur_func_len)) {
+                       err = -1;
+                       goto done;
+               }
+       }
 
-                                       err = check_line(expected_line, nexpected_line,
-                                                        sizeof(expected_line), line);
-                                       if (err == -1)
-                                               goto done;
-                               }
+       if (CHECK(ksyms_found != nr_jited_ksyms,
+                 "ksyms_found:%u != nr_jited_ksyms:%u",
+                 ksyms_found, nr_jited_ksyms)) {
+               err = -1;
+               goto done;
+       }
 
-                               /* read value@cpu */
-                               nread = getline(&line, &line_len, pin_file);
-                               if (nread < 0)
-                                       break;
-                       }
+       err = 0;
 
-                       nexpected_line = snprintf(expected_line, sizeof(expected_line),
-                                                 "%s%u: {%u,0,%d,0x%x,0x%x,0x%x,"
-                                                 "{%lu|[%u,%u,%u,%u,%u,%u,%u,%u]},%s}\n",
-                                                 percpu_map ? "\tcpu" : "",
-                                                 percpu_map ? cpu : next_key,
-                                                 cmapv->ui32, cmapv->si32,
-                                                 cmapv->unused_bits2a,
-                                                 cmapv->bits28,
-                                                 cmapv->unused_bits2b,
-                                                 cmapv->ui64,
-                                                 cmapv->ui8a[0], cmapv->ui8a[1],
-                                                 cmapv->ui8a[2], cmapv->ui8a[3],
-                                                 cmapv->ui8a[4], cmapv->ui8a[5],
-                                                 cmapv->ui8a[6], cmapv->ui8a[7],
-                                                 pprint_enum_str[cmapv->aenum]);
+done:
+       free(linfo);
+       free(jited_linfo);
+       free(jited_ksyms);
+       free(jited_func_lens);
+       return err;
+}
 
-                       err = check_line(expected_line, nexpected_line,
-                                        sizeof(expected_line), line);
-                       if (err == -1)
-                               goto done;
+static int do_test_info_raw(unsigned int test_num)
+{
+       const struct prog_info_raw_test *test = &info_raw_tests[test_num - 1];
+       unsigned int raw_btf_size, linfo_str_off, linfo_size;
+       int btf_fd = -1, prog_fd = -1, err = 0;
+       void *raw_btf, *patched_linfo = NULL;
+       const char *ret_next_str;
+       union bpf_attr attr = {};
+
+       fprintf(stderr, "BTF prog info raw test[%u] (%s): ", test_num, test->descr);
+       raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
+                                test->str_sec, test->str_sec_size,
+                                &raw_btf_size, &ret_next_str);
 
-                       cmapv = (void *)cmapv + rounded_value_size;
-               }
+       if (!raw_btf)
+               return -1;
 
-               if (percpu_map) {
-                       /* skip the last bracket for the percpu map */
-                       nread = getline(&line, &line_len, pin_file);
-                       if (nread < 0)
-                               break;
-               }
+       *btf_log_buf = '\0';
+       btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
+                             btf_log_buf, BTF_LOG_BUF_SIZE,
+                             args.always_log);
+       free(raw_btf);
 
-               nread = getline(&line, &line_len, pin_file);
-       } while (++nr_read_elems < test->max_entries && nread > 0);
+       if (CHECK(btf_fd == -1, "invalid btf_fd errno:%d", errno)) {
+               err = -1;
+               goto done;
+       }
 
-       if (lossless_map &&
-           CHECK(nr_read_elems < test->max_entries,
-                 "Unexpected EOF. nr_read_elems:%u test->max_entries:%u",
-                 nr_read_elems, test->max_entries)) {
+       if (*btf_log_buf && args.always_log)
+               fprintf(stderr, "\n%s", btf_log_buf);
+       *btf_log_buf = '\0';
+
+       linfo_str_off = ret_next_str - test->str_sec;
+       patched_linfo = patch_name_tbd(test->line_info,
+                                      test->str_sec, linfo_str_off,
+                                      test->str_sec_size, &linfo_size);
+       if (IS_ERR(patched_linfo)) {
+               fprintf(stderr, "error in creating raw bpf_line_info");
                err = -1;
                goto done;
        }
 
-       if (CHECK(nread > 0, "Unexpected extra pprint output: %s", line)) {
+       attr.prog_type = test->prog_type;
+       attr.insns = ptr_to_u64(test->insns);
+       attr.insn_cnt = probe_prog_length(test->insns);
+       attr.license = ptr_to_u64("GPL");
+       attr.prog_btf_fd = btf_fd;
+       attr.func_info_rec_size = test->func_info_rec_size;
+       attr.func_info_cnt = test->func_info_cnt;
+       attr.func_info = ptr_to_u64(test->func_info);
+       attr.log_buf = ptr_to_u64(btf_log_buf);
+       attr.log_size = BTF_LOG_BUF_SIZE;
+       attr.log_level = 1;
+       if (linfo_size) {
+               attr.line_info_rec_size = test->line_info_rec_size;
+               attr.line_info = ptr_to_u64(patched_linfo);
+               attr.line_info_cnt = linfo_size / attr.line_info_rec_size;
+       }
+
+       prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
+       err = ((prog_fd == -1) != test->expected_prog_load_failure);
+       if (CHECK(err, "prog_fd:%d expected_prog_load_failure:%u errno:%d",
+                 prog_fd, test->expected_prog_load_failure, errno) ||
+           CHECK(test->err_str && !strstr(btf_log_buf, test->err_str),
+                 "expected err_str:%s", test->err_str)) {
                err = -1;
                goto done;
        }
 
-       err = 0;
+       if (prog_fd == -1)
+               goto done;
+
+       err = test_get_finfo(test, prog_fd);
+       if (err)
+               goto done;
+
+       err = test_get_linfo(test, patched_linfo, attr.line_info_cnt, prog_fd);
+       if (err)
+               goto done;
 
 done:
-       if (mapv)
-               free(mapv);
        if (!err)
                fprintf(stderr, "OK");
+
        if (*btf_log_buf && (err || args.always_log))
                fprintf(stderr, "\n%s", btf_log_buf);
+
        if (btf_fd != -1)
                close(btf_fd);
-       if (map_fd != -1)
-               close(map_fd);
-       if (pin_file)
-               fclose(pin_file);
-       unlink(pin_path);
-       free(line);
+       if (prog_fd != -1)
+               close(prog_fd);
+
+       if (!IS_ERR(patched_linfo))
+               free(patched_linfo);
 
        return err;
 }
 
-static int test_pprint(void)
+static int test_info_raw(void)
 {
        unsigned int i;
        int err = 0;
 
-       for (i = 0; i < ARRAY_SIZE(pprint_tests_meta); i++) {
-               pprint_test_template.descr = pprint_tests_meta[i].descr;
-               pprint_test_template.map_type = pprint_tests_meta[i].map_type;
-               pprint_test_template.map_name = pprint_tests_meta[i].map_name;
-               pprint_test_template.ordered_map = pprint_tests_meta[i].ordered_map;
-               pprint_test_template.lossless_map = pprint_tests_meta[i].lossless_map;
-               pprint_test_template.percpu_map = pprint_tests_meta[i].percpu_map;
+       if (args.info_raw_test_num)
+               return count_result(do_test_info_raw(args.info_raw_test_num));
 
-               err |= count_result(do_test_pprint());
-       }
+       for (i = 1; i <= ARRAY_SIZE(info_raw_tests); i++)
+               err |= count_result(do_test_info_raw(i));
 
        return err;
 }
 
 static void usage(const char *cmd)
 {
-       fprintf(stderr, "Usage: %s [-l] [[-r test_num (1 - %zu)] | [-g test_num (1 - %zu)] | [-f test_num (1 - %zu)] | [-p]]\n",
+       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",
                cmd, ARRAY_SIZE(raw_tests), ARRAY_SIZE(get_info_tests),
-               ARRAY_SIZE(file_tests));
+               ARRAY_SIZE(file_tests), ARRAY_SIZE(info_raw_tests));
 }
 
 static int parse_args(int argc, char **argv)
 {
-       const char *optstr = "lpf:r:g:";
+       const char *optstr = "lpk:f:r:g:";
        int opt;
 
        while ((opt = getopt(argc, argv, optstr)) != -1) {
@@ -2509,6 +4982,10 @@ static int parse_args(int argc, char **argv)
                case 'p':
                        args.pprint_test = true;
                        break;
+               case 'k':
+                       args.info_raw_test_num = atoi(optarg);
+                       args.info_raw_test = true;
+                       break;
                case 'h':
                        usage(argv[0]);
                        exit(0);
@@ -2542,6 +5019,14 @@ static int parse_args(int argc, char **argv)
                return -1;
        }
 
+       if (args.info_raw_test_num &&
+           (args.info_raw_test_num < 1 ||
+            args.info_raw_test_num > ARRAY_SIZE(info_raw_tests))) {
+               fprintf(stderr, "BTF prog info raw test number must be [1 - %zu]\n",
+                       ARRAY_SIZE(info_raw_tests));
+               return -1;
+       }
+
        return 0;
 }
 
@@ -2574,13 +5059,17 @@ int main(int argc, char **argv)
        if (args.pprint_test)
                err |= test_pprint();
 
+       if (args.info_raw_test)
+               err |= test_info_raw();
+
        if (args.raw_test || args.get_info_test || args.file_test ||
-           args.pprint_test)
+           args.pprint_test || args.info_raw_test)
                goto done;
 
        err |= test_raw();
        err |= test_get_info();
        err |= test_file();
+       err |= test_info_raw();
 
 done:
        print_summary();
index b21b876..e5c79fe 100644 (file)
@@ -24,8 +24,8 @@ struct dummy_tracepoint_args {
        struct sock *sock;
 };
 
-SEC("dummy_tracepoint")
-int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+__attribute__((noinline))
+static int test_long_fname_2(struct dummy_tracepoint_args *arg)
 {
        struct ipv_counts *counts;
        int key = 0;
@@ -42,4 +42,16 @@ int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
        return 0;
 }
 
+__attribute__((noinline))
+static int test_long_fname_1(struct dummy_tracepoint_args *arg)
+{
+       return test_long_fname_2(arg);
+}
+
+SEC("dummy_tracepoint")
+int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+{
+       return test_long_fname_1(arg);
+}
+
 char _license[] SEC("license") = "GPL";
index 0ed8e08..434188c 100644 (file)
@@ -22,8 +22,8 @@ struct dummy_tracepoint_args {
        struct sock *sock;
 };
 
-SEC("dummy_tracepoint")
-int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+__attribute__((noinline))
+static int test_long_fname_2(struct dummy_tracepoint_args *arg)
 {
        struct ipv_counts *counts;
        int key = 0;
@@ -40,4 +40,16 @@ int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
        return 0;
 }
 
+__attribute__((noinline))
+static int test_long_fname_1(struct dummy_tracepoint_args *arg)
+{
+       return test_long_fname_2(arg);
+}
+
+SEC("dummy_tracepoint")
+int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+{
+       return test_long_fname_1(arg);
+}
+
 char _license[] SEC("license") = "GPL";
index c0fb073..d23d4da 100755 (executable)
@@ -59,7 +59,7 @@ else
 fi
 
 # Attach BPF program
-./flow_dissector_load -p bpf_flow.o -s dissect
+./flow_dissector_load -p bpf_flow.o -s flow_dissector
 
 # Setup
 tc qdisc add dev lo ingress
index 156d89f..2989b2e 100755 (executable)
@@ -33,17 +33,11 @@ trap exit_handler 0 2 3 6 9
 
 libbpf_open_file test_l4lb.o
 
-# TODO: fix libbpf to load noinline functions
-# [warning] libbpf: incorrect bpf_call opcode
-#libbpf_open_file test_l4lb_noinline.o
+# Load a program with BPF-to-BPF calls
+libbpf_open_file test_l4lb_noinline.o
 
-# TODO: fix test_xdp_meta.c to load with libbpf
-# [warning] libbpf: test_xdp_meta.o doesn't provide kernel version
-#libbpf_open_file test_xdp_meta.o
-
-# TODO: fix libbpf to handle .eh_frame
-# [warning] libbpf: relocation failed: no section(10)
-#libbpf_open_file ../../../../samples/bpf/tracex3_kern.o
+# Load a program compiled without the "-target bpf" flag
+libbpf_open_file test_xdp.o
 
 # Success
 exit 0
index 6776861..ec4e159 100755 (executable)
@@ -21,13 +21,14 @@ do
        if grep -q DRV_NAME=rc-loopback $i/uevent
        then
                LIRCDEV=$(grep DEVNAME= $i/lirc*/uevent | sed sQDEVNAME=Q/dev/Q)
+               INPUTDEV=$(grep DEVNAME= $i/input*/event*/uevent | sed sQDEVNAME=Q/dev/Q)
        fi
 done
 
 if [ -n $LIRCDEV ];
 then
        TYPE=lirc_mode2
-       ./test_lirc_mode2_user $LIRCDEV
+       ./test_lirc_mode2_user $LIRCDEV $INPUTDEV
        ret=$?
        if [ $ret -ne 0 ]; then
                echo -e ${RED}"FAIL: $TYPE"${NC}
index ba26855..4147130 100644 (file)
@@ -15,6 +15,9 @@ int bpf_decoder(unsigned int *sample)
 
                if (duration & 0x10000)
                        bpf_rc_keydown(sample, 0x40, duration & 0xffff, 0);
+               if (duration & 0x20000)
+                       bpf_rc_pointer_rel(sample, (duration >> 8) & 0xff,
+                                          duration & 0xff);
        }
 
        return 0;
index d470d63..fb5fd68 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <linux/bpf.h>
 #include <linux/lirc.h>
+#include <linux/input.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 int main(int argc, char **argv)
 {
        struct bpf_object *obj;
-       int ret, lircfd, progfd, mode;
-       int testir = 0x1dead;
+       int ret, lircfd, progfd, inputfd;
+       int testir1 = 0x1dead;
+       int testir2 = 0x20101;
        u32 prog_ids[10], prog_flags[10], prog_cnt;
 
-       if (argc != 2) {
-               printf("Usage: %s /dev/lircN\n", argv[0]);
+       if (argc != 3) {
+               printf("Usage: %s /dev/lircN /dev/input/eventM\n", argv[0]);
                return 2;
        }
 
@@ -76,9 +78,9 @@ int main(int argc, char **argv)
                return 1;
        }
 
-       mode = LIRC_MODE_SCANCODE;
-       if (ioctl(lircfd, LIRC_SET_REC_MODE, &mode)) {
-               printf("failed to set rec mode: %m\n");
+       inputfd = open(argv[2], O_RDONLY | O_NONBLOCK);
+       if (inputfd == -1) {
+               printf("failed to open input device %s: %m\n", argv[1]);
                return 1;
        }
 
@@ -102,29 +104,54 @@ int main(int argc, char **argv)
        }
 
        /* Write raw IR */
-       ret = write(lircfd, &testir, sizeof(testir));
-       if (ret != sizeof(testir)) {
+       ret = write(lircfd, &testir1, sizeof(testir1));
+       if (ret != sizeof(testir1)) {
                printf("Failed to send test IR message: %m\n");
                return 1;
        }
 
-       struct pollfd pfd = { .fd = lircfd, .events = POLLIN };
-       struct lirc_scancode lsc;
+       struct pollfd pfd = { .fd = inputfd, .events = POLLIN };
+       struct input_event event;
 
-       poll(&pfd, 1, 100);
+       for (;;) {
+               poll(&pfd, 1, 100);
 
-       /* Read decoded IR */
-       ret = read(lircfd, &lsc, sizeof(lsc));
-       if (ret != sizeof(lsc)) {
-               printf("Failed to read decoded IR: %m\n");
-               return 1;
+               /* Read decoded IR */
+               ret = read(inputfd, &event, sizeof(event));
+               if (ret != sizeof(event)) {
+                       printf("Failed to read decoded IR: %m\n");
+                       return 1;
+               }
+
+               if (event.type == EV_MSC && event.code == MSC_SCAN &&
+                   event.value == 0xdead) {
+                       break;
+               }
        }
 
-       if (lsc.scancode != 0xdead || lsc.rc_proto != 64) {
-               printf("Incorrect scancode decoded\n");
+       /* Write raw IR */
+       ret = write(lircfd, &testir2, sizeof(testir2));
+       if (ret != sizeof(testir2)) {
+               printf("Failed to send test IR message: %m\n");
                return 1;
        }
 
+       for (;;) {
+               poll(&pfd, 1, 100);
+
+               /* Read decoded IR */
+               ret = read(inputfd, &event, sizeof(event));
+               if (ret != sizeof(event)) {
+                       printf("Failed to read decoded IR: %m\n");
+                       return 1;
+               }
+
+               if (event.type == EV_REL && event.code == REL_Y &&
+                   event.value == 1 ) {
+                       break;
+               }
+       }
+
        prog_cnt = 10;
        ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
                             &prog_cnt);
diff --git a/tools/testing/selftests/bpf/test_map_in_map.c b/tools/testing/selftests/bpf/test_map_in_map.c
new file mode 100644 (file)
index 0000000..ce923e6
--- /dev/null
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018 Facebook */
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <linux/types.h>
+#include "bpf_helpers.h"
+
+struct bpf_map_def SEC("maps") mim_array = {
+       .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
+       .key_size = sizeof(int),
+       /* must be sizeof(__u32) for map in map */
+       .value_size = sizeof(__u32),
+       .max_entries = 1,
+       .map_flags = 0,
+};
+
+struct bpf_map_def SEC("maps") mim_hash = {
+       .type = BPF_MAP_TYPE_HASH_OF_MAPS,
+       .key_size = sizeof(int),
+       /* must be sizeof(__u32) for map in map */
+       .value_size = sizeof(__u32),
+       .max_entries = 1,
+       .map_flags = 0,
+};
+
+SEC("xdp_mimtest")
+int xdp_mimtest0(struct xdp_md *ctx)
+{
+       int value = 123;
+       int key = 0;
+       void *map;
+
+       map = bpf_map_lookup_elem(&mim_array, &key);
+       if (!map)
+               return XDP_DROP;
+
+       bpf_map_update_elem(map, &key, &value, 0);
+
+       map = bpf_map_lookup_elem(&mim_hash, &key);
+       if (!map)
+               return XDP_DROP;
+
+       bpf_map_update_elem(map, &key, &value, 0);
+
+       return XDP_PASS;
+}
+
+int _version SEC("version") = 1;
+char _license[] SEC("license") = "GPL";
index 4db2116..9c79ee0 100644 (file)
@@ -258,24 +258,36 @@ static void test_hashmap_percpu(int task, void *data)
        close(fd);
 }
 
-static void test_hashmap_walk(int task, void *data)
+static int helper_fill_hashmap(int max_entries)
 {
-       int fd, i, max_entries = 1000;
-       long long key, value, next_key;
-       bool next_key_valid = true;
+       int i, fd, ret;
+       long long key, value;
 
        fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
                            max_entries, map_flags);
-       if (fd < 0) {
-               printf("Failed to create hashmap '%s'!\n", strerror(errno));
-               exit(1);
-       }
+       CHECK(fd < 0,
+             "failed to create hashmap",
+             "err: %s, flags: 0x%x\n", strerror(errno), map_flags);
 
        for (i = 0; i < max_entries; i++) {
                key = i; value = key;
-               assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0);
+               ret = bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST);
+               CHECK(ret != 0,
+                     "can't update hashmap",
+                     "err: %s\n", strerror(ret));
        }
 
+       return fd;
+}
+
+static void test_hashmap_walk(int task, void *data)
+{
+       int fd, i, max_entries = 1000;
+       long long key, value, next_key;
+       bool next_key_valid = true;
+
+       fd = helper_fill_hashmap(max_entries);
+
        for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key,
                                         &next_key) == 0; i++) {
                key = next_key;
@@ -306,6 +318,39 @@ static void test_hashmap_walk(int task, void *data)
        close(fd);
 }
 
+static void test_hashmap_zero_seed(void)
+{
+       int i, first, second, old_flags;
+       long long key, next_first, next_second;
+
+       old_flags = map_flags;
+       map_flags |= BPF_F_ZERO_SEED;
+
+       first = helper_fill_hashmap(3);
+       second = helper_fill_hashmap(3);
+
+       for (i = 0; ; i++) {
+               void *key_ptr = !i ? NULL : &key;
+
+               if (bpf_map_get_next_key(first, key_ptr, &next_first) != 0)
+                       break;
+
+               CHECK(bpf_map_get_next_key(second, key_ptr, &next_second) != 0,
+                     "next_key for second map must succeed",
+                     "key_ptr: %p", key_ptr);
+               CHECK(next_first != next_second,
+                     "keys must match",
+                     "i: %d first: %lld second: %lld\n", i,
+                     next_first, next_second);
+
+               key = next_first;
+       }
+
+       map_flags = old_flags;
+       close(first);
+       close(second);
+}
+
 static void test_arraymap(int task, void *data)
 {
        int key, next_key, fd;
@@ -1080,6 +1125,94 @@ out_sockmap:
        exit(1);
 }
 
+#define MAPINMAP_PROG "./test_map_in_map.o"
+static void test_map_in_map(void)
+{
+       struct bpf_program *prog;
+       struct bpf_object *obj;
+       struct bpf_map *map;
+       int mim_fd, fd, err;
+       int pos = 0;
+
+       obj = bpf_object__open(MAPINMAP_PROG);
+
+       fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), sizeof(int),
+                           2, 0);
+       if (fd < 0) {
+               printf("Failed to create hashmap '%s'!\n", strerror(errno));
+               exit(1);
+       }
+
+       map = bpf_object__find_map_by_name(obj, "mim_array");
+       if (IS_ERR(map)) {
+               printf("Failed to load array of maps from test prog\n");
+               goto out_map_in_map;
+       }
+       err = bpf_map__set_inner_map_fd(map, fd);
+       if (err) {
+               printf("Failed to set inner_map_fd for array of maps\n");
+               goto out_map_in_map;
+       }
+
+       map = bpf_object__find_map_by_name(obj, "mim_hash");
+       if (IS_ERR(map)) {
+               printf("Failed to load hash of maps from test prog\n");
+               goto out_map_in_map;
+       }
+       err = bpf_map__set_inner_map_fd(map, fd);
+       if (err) {
+               printf("Failed to set inner_map_fd for hash of maps\n");
+               goto out_map_in_map;
+       }
+
+       bpf_object__for_each_program(prog, obj) {
+               bpf_program__set_xdp(prog);
+       }
+       bpf_object__load(obj);
+
+       map = bpf_object__find_map_by_name(obj, "mim_array");
+       if (IS_ERR(map)) {
+               printf("Failed to load array of maps from test prog\n");
+               goto out_map_in_map;
+       }
+       mim_fd = bpf_map__fd(map);
+       if (mim_fd < 0) {
+               printf("Failed to get descriptor for array of maps\n");
+               goto out_map_in_map;
+       }
+
+       err = bpf_map_update_elem(mim_fd, &pos, &fd, 0);
+       if (err) {
+               printf("Failed to update array of maps\n");
+               goto out_map_in_map;
+       }
+
+       map = bpf_object__find_map_by_name(obj, "mim_hash");
+       if (IS_ERR(map)) {
+               printf("Failed to load hash of maps from test prog\n");
+               goto out_map_in_map;
+       }
+       mim_fd = bpf_map__fd(map);
+       if (mim_fd < 0) {
+               printf("Failed to get descriptor for hash of maps\n");
+               goto out_map_in_map;
+       }
+
+       err = bpf_map_update_elem(mim_fd, &pos, &fd, 0);
+       if (err) {
+               printf("Failed to update hash of maps\n");
+               goto out_map_in_map;
+       }
+
+       close(fd);
+       bpf_object__close(obj);
+       return;
+
+out_map_in_map:
+       close(fd);
+       exit(1);
+}
+
 #define MAP_SIZE (32 * 1024)
 
 static void test_map_large(void)
@@ -1534,6 +1667,7 @@ static void run_all_tests(void)
        test_hashmap(0, NULL);
        test_hashmap_percpu(0, NULL);
        test_hashmap_walk(0, NULL);
+       test_hashmap_zero_seed();
 
        test_arraymap(0, NULL);
        test_arraymap_percpu(0, NULL);
@@ -1554,6 +1688,8 @@ static void run_all_tests(void)
 
        test_queuemap(0, NULL);
        test_stackmap(0, NULL);
+
+       test_map_in_map();
 }
 
 int main(void)
index 7887df6..44ed7f2 100644 (file)
@@ -81,7 +81,10 @@ int main(int argc, char **argv)
                goto err;
        }
 
-       assert(system("ping localhost -6 -c 10000 -f -q > /dev/null") == 0);
+       if (system("which ping6 &>/dev/null") == 0)
+               assert(!system("ping6 localhost -c 10000 -f -q > /dev/null"));
+       else
+               assert(!system("ping -6 localhost -c 10000 -f -q > /dev/null"));
 
        if (bpf_prog_query(cgroup_fd, BPF_CGROUP_INET_EGRESS, 0, NULL, NULL,
                           &prog_cnt)) {
index 2d3c04f..126fc62 100644 (file)
@@ -51,10 +51,10 @@ static struct {
        struct iphdr iph;
        struct tcphdr tcp;
 } __packed pkt_v4 = {
-       .eth.h_proto = bpf_htons(ETH_P_IP),
+       .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
        .iph.ihl = 5,
        .iph.protocol = 6,
-       .iph.tot_len = bpf_htons(MAGIC_BYTES),
+       .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
        .tcp.urg_ptr = 123,
 };
 
@@ -64,13 +64,13 @@ static struct {
        struct ipv6hdr iph;
        struct tcphdr tcp;
 } __packed pkt_v6 = {
-       .eth.h_proto = bpf_htons(ETH_P_IPV6),
+       .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
        .iph.nexthdr = 6,
-       .iph.payload_len = bpf_htons(MAGIC_BYTES),
+       .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
        .tcp.urg_ptr = 123,
 };
 
-#define CHECK(condition, tag, format...) ({                            \
+#define _CHECK(condition, tag, duration, format...) ({                 \
        int __ret = !!(condition);                                      \
        if (__ret) {                                                    \
                error_cnt++;                                            \
@@ -83,6 +83,11 @@ static struct {
        __ret;                                                          \
 })
 
+#define CHECK(condition, tag, format...) \
+       _CHECK(condition, tag, duration, format)
+#define CHECK_ATTR(condition, tag, format...) \
+       _CHECK(condition, tag, tattr.duration, format)
+
 static int bpf_find_map(const char *test, struct bpf_object *obj,
                        const char *name)
 {
@@ -124,6 +129,53 @@ static void test_pkt_access(void)
        bpf_object__close(obj);
 }
 
+static void test_prog_run_xattr(void)
+{
+       const char *file = "./test_pkt_access.o";
+       struct bpf_object *obj;
+       char buf[10];
+       int err;
+       struct bpf_prog_test_run_attr tattr = {
+               .repeat = 1,
+               .data_in = &pkt_v4,
+               .data_size_in = sizeof(pkt_v4),
+               .data_out = buf,
+               .data_size_out = 5,
+       };
+
+       err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj,
+                           &tattr.prog_fd);
+       if (CHECK_ATTR(err, "load", "err %d errno %d\n", err, errno))
+               return;
+
+       memset(buf, 0, sizeof(buf));
+
+       err = bpf_prog_test_run_xattr(&tattr);
+       CHECK_ATTR(err != -1 || errno != ENOSPC || tattr.retval, "run",
+             "err %d errno %d retval %d\n", err, errno, tattr.retval);
+
+       CHECK_ATTR(tattr.data_size_out != sizeof(pkt_v4), "data_size_out",
+             "incorrect output size, want %lu have %u\n",
+             sizeof(pkt_v4), tattr.data_size_out);
+
+       CHECK_ATTR(buf[5] != 0, "overflow",
+             "BPF_PROG_TEST_RUN ignored size hint\n");
+
+       tattr.data_out = NULL;
+       tattr.data_size_out = 0;
+       errno = 0;
+
+       err = bpf_prog_test_run_xattr(&tattr);
+       CHECK_ATTR(err || errno || tattr.retval, "run_no_output",
+             "err %d errno %d retval %d\n", err, errno, tattr.retval);
+
+       tattr.data_size_out = 1;
+       err = bpf_prog_test_run_xattr(&tattr);
+       CHECK_ATTR(err != -EINVAL, "run_wrong_size_out", "err %d\n", err);
+
+       bpf_object__close(obj);
+}
+
 static void test_xdp(void)
 {
        struct vip key4 = {.protocol = 6, .family = AF_INET};
@@ -524,7 +576,7 @@ static void test_bpf_obj_id(void)
                          load_time < now - 60 || load_time > now + 60 ||
                          prog_infos[i].created_by_uid != my_uid ||
                          prog_infos[i].nr_map_ids != 1 ||
-                         *(int *)prog_infos[i].map_ids != map_infos[i].id ||
+                         *(int *)(long)prog_infos[i].map_ids != map_infos[i].id ||
                          strcmp((char *)prog_infos[i].name, expected_prog_name),
                          "get-prog-info(fd)",
                          "err %d errno %d i %d type %d(%d) info_len %u(%Zu) jit_enabled %d jited_prog_len %u xlated_prog_len %u jited_prog %d xlated_prog %d load_time %lu(%lu) uid %u(%u) nr_map_ids %u(%u) map_id %u(%u) name %s(%s)\n",
@@ -539,7 +591,7 @@ static void test_bpf_obj_id(void)
                          load_time, now,
                          prog_infos[i].created_by_uid, my_uid,
                          prog_infos[i].nr_map_ids, 1,
-                         *(int *)prog_infos[i].map_ids, map_infos[i].id,
+                         *(int *)(long)prog_infos[i].map_ids, map_infos[i].id,
                          prog_infos[i].name, expected_prog_name))
                        goto done;
        }
@@ -585,7 +637,7 @@ static void test_bpf_obj_id(void)
                bzero(&prog_info, sizeof(prog_info));
                info_len = sizeof(prog_info);
 
-               saved_map_id = *(int *)(prog_infos[i].map_ids);
+               saved_map_id = *(int *)((long)prog_infos[i].map_ids);
                prog_info.map_ids = prog_infos[i].map_ids;
                prog_info.nr_map_ids = 2;
                err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len);
@@ -593,12 +645,12 @@ static void test_bpf_obj_id(void)
                prog_infos[i].xlated_prog_insns = 0;
                CHECK(err || info_len != sizeof(struct bpf_prog_info) ||
                      memcmp(&prog_info, &prog_infos[i], info_len) ||
-                     *(int *)prog_info.map_ids != saved_map_id,
+                     *(int *)(long)prog_info.map_ids != saved_map_id,
                      "get-prog-info(next_id->fd)",
                      "err %d errno %d info_len %u(%Zu) memcmp %d map_id %u(%u)\n",
                      err, errno, info_len, sizeof(struct bpf_prog_info),
                      memcmp(&prog_info, &prog_infos[i], info_len),
-                     *(int *)prog_info.map_ids, saved_map_id);
+                     *(int *)(long)prog_info.map_ids, saved_map_id);
                close(prog_fd);
        }
        CHECK(nr_id_found != nr_iters,
@@ -1703,7 +1755,7 @@ static void test_reference_tracking()
        const char *file = "./test_sk_lookup_kern.o";
        struct bpf_object *obj;
        struct bpf_program *prog;
-       __u32 duration;
+       __u32 duration = 0;
        int err = 0;
 
        obj = bpf_object__open(file);
@@ -1837,6 +1889,7 @@ int main(void)
        jit_enabled = is_jit_enabled();
 
        test_pkt_access();
+       test_prog_run_xattr();
        test_xdp();
        test_xdp_adjust_tail();
        test_l4lb_all();
index b745bdc..e21cd73 100644 (file)
@@ -72,7 +72,7 @@ int bpf_sk_lookup_test0(struct __sk_buff *skb)
                return TC_ACT_SHOT;
 
        tuple_len = ipv4 ? sizeof(tuple->ipv4) : sizeof(tuple->ipv6);
-       sk = bpf_sk_lookup_tcp(skb, tuple, tuple_len, 0, 0);
+       sk = bpf_sk_lookup_tcp(skb, tuple, tuple_len, BPF_F_CURRENT_NETNS, 0);
        if (sk)
                bpf_sk_release(sk);
        return sk ? TC_ACT_OK : TC_ACT_UNSPEC;
@@ -84,7 +84,7 @@ int bpf_sk_lookup_test1(struct __sk_buff *skb)
        struct bpf_sock_tuple tuple = {};
        struct bpf_sock *sk;
 
-       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), 0, 0);
+       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), BPF_F_CURRENT_NETNS, 0);
        if (sk)
                bpf_sk_release(sk);
        return 0;
@@ -97,7 +97,7 @@ int bpf_sk_lookup_uaf(struct __sk_buff *skb)
        struct bpf_sock *sk;
        __u32 family = 0;
 
-       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), 0, 0);
+       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), BPF_F_CURRENT_NETNS, 0);
        if (sk) {
                bpf_sk_release(sk);
                family = sk->family;
@@ -112,7 +112,7 @@ int bpf_sk_lookup_modptr(struct __sk_buff *skb)
        struct bpf_sock *sk;
        __u32 family;
 
-       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), 0, 0);
+       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), BPF_F_CURRENT_NETNS, 0);
        if (sk) {
                sk += 1;
                bpf_sk_release(sk);
@@ -127,7 +127,7 @@ int bpf_sk_lookup_modptr_or_null(struct __sk_buff *skb)
        struct bpf_sock *sk;
        __u32 family;
 
-       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), 0, 0);
+       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), BPF_F_CURRENT_NETNS, 0);
        sk += 1;
        if (sk)
                bpf_sk_release(sk);
@@ -139,7 +139,7 @@ int bpf_sk_lookup_test2(struct __sk_buff *skb)
 {
        struct bpf_sock_tuple tuple = {};
 
-       bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), 0, 0);
+       bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), BPF_F_CURRENT_NETNS, 0);
        return 0;
 }
 
@@ -149,7 +149,7 @@ int bpf_sk_lookup_test3(struct __sk_buff *skb)
        struct bpf_sock_tuple tuple = {};
        struct bpf_sock *sk;
 
-       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), 0, 0);
+       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), BPF_F_CURRENT_NETNS, 0);
        bpf_sk_release(sk);
        bpf_sk_release(sk);
        return 0;
@@ -161,7 +161,7 @@ int bpf_sk_lookup_test4(struct __sk_buff *skb)
        struct bpf_sock_tuple tuple = {};
        struct bpf_sock *sk;
 
-       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), 0, 0);
+       sk = bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), BPF_F_CURRENT_NETNS, 0);
        bpf_sk_release(sk);
        return 0;
 }
@@ -169,7 +169,7 @@ int bpf_sk_lookup_test4(struct __sk_buff *skb)
 void lookup_no_release(struct __sk_buff *skb)
 {
        struct bpf_sock_tuple tuple = {};
-       bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), 0, 0);
+       bpf_sk_lookup_tcp(skb, &tuple, sizeof(tuple), BPF_F_CURRENT_NETNS, 0);
 }
 
 SEC("fail_no_release_subcall")
index aeeb76a..73b7493 100644 (file)
@@ -574,24 +574,44 @@ static int bind4_prog_load(const struct sock_addr_test *test)
                /* if (sk.family == AF_INET && */
                BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
                            offsetof(struct bpf_sock_addr, family)),
-               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET, 16),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET, 24),
 
                /*     (sk.type == SOCK_DGRAM || sk.type == SOCK_STREAM) && */
                BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
                            offsetof(struct bpf_sock_addr, type)),
                BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_DGRAM, 1),
                BPF_JMP_A(1),
-               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_STREAM, 12),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_STREAM, 20),
 
                /*     1st_byte_of_user_ip4 == expected && */
                BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6,
                            offsetof(struct bpf_sock_addr, user_ip4)),
-               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[0], 10),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[0], 18),
+
+               /*     2nd_byte_of_user_ip4 == expected && */
+               BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, user_ip4) + 1),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[1], 16),
+
+               /*     3rd_byte_of_user_ip4 == expected && */
+               BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, user_ip4) + 2),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[2], 14),
+
+               /*     4th_byte_of_user_ip4 == expected && */
+               BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, user_ip4) + 3),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[3], 12),
 
                /*     1st_half_of_user_ip4 == expected && */
                BPF_LDX_MEM(BPF_H, BPF_REG_7, BPF_REG_6,
                            offsetof(struct bpf_sock_addr, user_ip4)),
-               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr16[0], 8),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr16[0], 10),
+
+               /*     2nd_half_of_user_ip4 == expected && */
+               BPF_LDX_MEM(BPF_H, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, user_ip4) + 2),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr16[1], 8),
 
                /*     whole_user_ip4 == expected) { */
                BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
index 622ade0..e85a771 100644 (file)
@@ -79,6 +79,8 @@ int txmsg_start;
 int txmsg_end;
 int txmsg_start_push;
 int txmsg_end_push;
+int txmsg_start_pop;
+int txmsg_pop;
 int txmsg_ingress;
 int txmsg_skb;
 int ktls;
@@ -104,6 +106,8 @@ static const struct option long_options[] = {
        {"txmsg_end",   required_argument,      NULL, 'e'},
        {"txmsg_start_push", required_argument, NULL, 'p'},
        {"txmsg_end_push",   required_argument, NULL, 'q'},
+       {"txmsg_start_pop",  required_argument, NULL, 'w'},
+       {"txmsg_pop",        required_argument, NULL, 'x'},
        {"txmsg_ingress", no_argument,          &txmsg_ingress, 1 },
        {"txmsg_skb", no_argument,              &txmsg_skb, 1 },
        {"ktls", no_argument,                   &ktls, 1 },
@@ -473,13 +477,27 @@ static int msg_loop(int fd, int iov_count, int iov_length, int cnt,
                clock_gettime(CLOCK_MONOTONIC, &s->end);
        } else {
                int slct, recvp = 0, recv, max_fd = fd;
+               float total_bytes, txmsg_pop_total;
                int fd_flags = O_NONBLOCK;
                struct timeval timeout;
-               float total_bytes;
                fd_set w;
 
                fcntl(fd, fd_flags);
+               /* Account for pop bytes noting each iteration of apply will
+                * call msg_pop_data helper so we need to account for this
+                * by calculating the number of apply iterations. Note user
+                * of the tool can create cases where no data is sent by
+                * manipulating pop/push/pull/etc. For example txmsg_apply 1
+                * with txmsg_pop 1 will try to apply 1B at a time but each
+                * iteration will then pop 1B so no data will ever be sent.
+                * This is really only useful for testing edge cases in code
+                * paths.
+                */
                total_bytes = (float)iov_count * (float)iov_length * (float)cnt;
+               txmsg_pop_total = txmsg_pop;
+               if (txmsg_apply)
+                       txmsg_pop_total *= (total_bytes / txmsg_apply);
+               total_bytes -= txmsg_pop_total;
                err = clock_gettime(CLOCK_MONOTONIC, &s->start);
                if (err < 0)
                        perror("recv start time: ");
@@ -488,7 +506,7 @@ static int msg_loop(int fd, int iov_count, int iov_length, int cnt,
                                timeout.tv_sec = 0;
                                timeout.tv_usec = 300000;
                        } else {
-                               timeout.tv_sec = 1;
+                               timeout.tv_sec = 3;
                                timeout.tv_usec = 0;
                        }
 
@@ -503,7 +521,7 @@ static int msg_loop(int fd, int iov_count, int iov_length, int cnt,
                                goto out_errno;
                        } else if (!slct) {
                                if (opt->verbose)
-                                       fprintf(stderr, "unexpected timeout\n");
+                                       fprintf(stderr, "unexpected timeout: recved %zu/%f pop_total %f\n", s->bytes_recvd, total_bytes, txmsg_pop_total);
                                errno = -EIO;
                                clock_gettime(CLOCK_MONOTONIC, &s->end);
                                goto out_errno;
@@ -619,7 +637,7 @@ static int sendmsg_test(struct sockmap_options *opt)
                        iov_count = 1;
                err = msg_loop(rx_fd, iov_count, iov_buf,
                               cnt, &s, false, opt);
-               if (err && opt->verbose)
+               if (opt->verbose)
                        fprintf(stderr,
                                "msg_loop_rx: iov_count %i iov_buf %i cnt %i err %i\n",
                                iov_count, iov_buf, cnt, err);
@@ -931,6 +949,39 @@ run:
                        }
                }
 
+               if (txmsg_start_pop) {
+                       i = 4;
+                       err = bpf_map_update_elem(map_fd[5],
+                                                 &i, &txmsg_start_pop, BPF_ANY);
+                       if (err) {
+                               fprintf(stderr,
+                                       "ERROR: bpf_map_update_elem %i@%i (txmsg_start_pop):  %d (%s)\n",
+                                       txmsg_start_pop, i, err, strerror(errno));
+                               goto out;
+                       }
+               } else {
+                       i = 4;
+                       bpf_map_update_elem(map_fd[5],
+                                                 &i, &txmsg_start_pop, BPF_ANY);
+               }
+
+               if (txmsg_pop) {
+                       i = 5;
+                       err = bpf_map_update_elem(map_fd[5],
+                                                 &i, &txmsg_pop, BPF_ANY);
+                       if (err) {
+                               fprintf(stderr,
+                                       "ERROR: bpf_map_update_elem %i@%i (txmsg_pop):  %d (%s)\n",
+                                       txmsg_pop, i, err, strerror(errno));
+                               goto out;
+                       }
+               } else {
+                       i = 5;
+                       bpf_map_update_elem(map_fd[5],
+                                           &i, &txmsg_pop, BPF_ANY);
+
+               }
+
                if (txmsg_ingress) {
                        int in = BPF_F_INGRESS;
 
@@ -1082,6 +1133,11 @@ static void test_options(char *options)
                snprintf(tstr, OPTSTRING, "end %d,", txmsg_end);
                strncat(options, tstr, OPTSTRING);
        }
+       if (txmsg_start_pop) {
+               snprintf(tstr, OPTSTRING, "pop (%d,%d),",
+                        txmsg_start_pop, txmsg_start_pop + txmsg_pop);
+               strncat(options, tstr, OPTSTRING);
+       }
        if (txmsg_ingress)
                strncat(options, "ingress,", OPTSTRING);
        if (txmsg_skb)
@@ -1264,6 +1320,7 @@ static int test_mixed(int cgrp)
        txmsg_apply = txmsg_cork = 0;
        txmsg_start = txmsg_end = 0;
        txmsg_start_push = txmsg_end_push = 0;
+       txmsg_start_pop = txmsg_pop = 0;
 
        /* Test small and large iov_count values with pass/redir/apply/cork */
        txmsg_pass = 1;
@@ -1383,6 +1440,19 @@ static int test_start_end(int cgrp)
        txmsg_end = 2;
        txmsg_start_push = 1;
        txmsg_end_push = 2;
+       txmsg_start_pop = 1;
+       txmsg_pop = 1;
+       err = test_txmsg(cgrp);
+       if (err)
+               goto out;
+
+       /* Cut a byte of pushed data but leave reamining in place */
+       txmsg_start = 1;
+       txmsg_end = 2;
+       txmsg_start_push = 1;
+       txmsg_end_push = 3;
+       txmsg_start_pop = 1;
+       txmsg_pop = 1;
        err = test_txmsg(cgrp);
        if (err)
                goto out;
@@ -1393,6 +1463,9 @@ static int test_start_end(int cgrp)
        opt.iov_length = 100;
        txmsg_cork = 1600;
 
+       txmsg_start_pop = 0;
+       txmsg_pop = 0;
+
        for (i = 99; i <= 1600; i += 500) {
                txmsg_start = 0;
                txmsg_end = i;
@@ -1403,6 +1476,17 @@ static int test_start_end(int cgrp)
                        goto out;
        }
 
+       /* Test pop data in middle of cork */
+       for (i = 99; i <= 1600; i += 500) {
+               txmsg_start_pop = 10;
+               txmsg_pop = i;
+               err = test_exec(cgrp, &opt);
+               if (err)
+                       goto out;
+       }
+       txmsg_start_pop = 0;
+       txmsg_pop = 0;
+
        /* Test start/end with cork but pull data in middle */
        for (i = 199; i <= 1600; i += 500) {
                txmsg_start = 100;
@@ -1423,6 +1507,15 @@ static int test_start_end(int cgrp)
        if (err)
                goto out;
 
+       /* Test pop with cork pulling last sg entry */
+       txmsg_start_pop = 1500;
+       txmsg_pop = 1600;
+       err = test_exec(cgrp, &opt);
+       if (err)
+               goto out;
+       txmsg_start_pop = 0;
+       txmsg_pop = 0;
+
        /* Test start/end pull of single byte in last page */
        txmsg_start = 1111;
        txmsg_end = 1112;
@@ -1432,6 +1525,13 @@ static int test_start_end(int cgrp)
        if (err)
                goto out;
 
+       /* Test pop of single byte in last page */
+       txmsg_start_pop = 1111;
+       txmsg_pop = 1112;
+       err = test_exec(cgrp, &opt);
+       if (err)
+               goto out;
+
        /* Test start/end with end < start */
        txmsg_start = 1111;
        txmsg_end = 0;
@@ -1456,7 +1556,20 @@ static int test_start_end(int cgrp)
        txmsg_start_push = 1601;
        txmsg_end_push = 1600;
        err = test_exec(cgrp, &opt);
+       if (err)
+               goto out;
+
+       /* Test pop with start > data */
+       txmsg_start_pop = 1601;
+       txmsg_pop = 1;
+       err = test_exec(cgrp, &opt);
+       if (err)
+               goto out;
 
+       /* Test pop with pop range > data */
+       txmsg_start_pop = 1599;
+       txmsg_pop = 10;
+       err = test_exec(cgrp, &opt);
 out:
        txmsg_start = 0;
        txmsg_end = 0;
@@ -1641,6 +1754,12 @@ int main(int argc, char **argv)
                case 'q':
                        txmsg_end_push = atoi(optarg);
                        break;
+               case 'w':
+                       txmsg_start_pop = atoi(optarg);
+                       break;
+               case 'x':
+                       txmsg_pop = atoi(optarg);
+                       break;
                case 'a':
                        txmsg_apply = atoi(optarg);
                        break;
index 14b8bba..e7639f6 100644 (file)
@@ -74,7 +74,7 @@ struct bpf_map_def SEC("maps") sock_bytes = {
        .type = BPF_MAP_TYPE_ARRAY,
        .key_size = sizeof(int),
        .value_size = sizeof(int),
-       .max_entries = 4
+       .max_entries = 6
 };
 
 struct bpf_map_def SEC("maps") sock_redir_flags = {
@@ -181,8 +181,8 @@ int bpf_sockmap(struct bpf_sock_ops *skops)
 SEC("sk_msg1")
 int bpf_prog4(struct sk_msg_md *msg)
 {
-       int *bytes, zero = 0, one = 1, two = 2, three = 3;
-       int *start, *end, *start_push, *end_push;
+       int *bytes, zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5;
+       int *start, *end, *start_push, *end_push, *start_pop, *pop;
 
        bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
        if (bytes)
@@ -198,15 +198,19 @@ int bpf_prog4(struct sk_msg_md *msg)
        end_push = bpf_map_lookup_elem(&sock_bytes, &three);
        if (start_push && end_push)
                bpf_msg_push_data(msg, *start_push, *end_push, 0);
+       start_pop = bpf_map_lookup_elem(&sock_bytes, &four);
+       pop = bpf_map_lookup_elem(&sock_bytes, &five);
+       if (start_pop && pop)
+               bpf_msg_pop_data(msg, *start_pop, *pop, 0);
        return SK_PASS;
 }
 
 SEC("sk_msg2")
 int bpf_prog5(struct sk_msg_md *msg)
 {
-       int zero = 0, one = 1, two = 2, three = 3;
-       int *start, *end, *start_push, *end_push;
-       int *bytes, len1, len2 = 0, len3;
+       int zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5;
+       int *start, *end, *start_push, *end_push, *start_pop, *pop;
+       int *bytes, len1, len2 = 0, len3, len4;
        int err1 = -1, err2 = -1;
 
        bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
@@ -247,6 +251,20 @@ int bpf_prog5(struct sk_msg_md *msg)
                bpf_printk("sk_msg2: length push_update %i->%i\n",
                           len2 ? len2 : len1, len3);
        }
+       start_pop = bpf_map_lookup_elem(&sock_bytes, &four);
+       pop = bpf_map_lookup_elem(&sock_bytes, &five);
+       if (start_pop && pop) {
+               int err;
+
+               bpf_printk("sk_msg2: pop(%i@%i)\n",
+                          start_pop, pop);
+               err = bpf_msg_pop_data(msg, *start_pop, *pop, 0);
+               if (err)
+                       bpf_printk("sk_msg2: pop_data err %i\n", err);
+               len4 = (__u64)msg->data_end - (__u64)msg->data;
+               bpf_printk("sk_msg2: length pop_data %i->%i\n",
+                          len1 ? len1 : 0,  len4);
+       }
 
        bpf_printk("sk_msg2: data length %i err1 %i err2 %i\n",
                   len1, err1, err2);
@@ -256,8 +274,8 @@ int bpf_prog5(struct sk_msg_md *msg)
 SEC("sk_msg3")
 int bpf_prog6(struct sk_msg_md *msg)
 {
-       int *bytes, *start, *end, *start_push, *end_push, *f;
-       int zero = 0, one = 1, two = 2, three = 3, key = 0;
+       int zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5, key = 0;
+       int *bytes, *start, *end, *start_push, *end_push, *start_pop, *pop, *f;
        __u64 flags = 0;
 
        bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
@@ -277,6 +295,11 @@ int bpf_prog6(struct sk_msg_md *msg)
        if (start_push && end_push)
                bpf_msg_push_data(msg, *start_push, *end_push, 0);
 
+       start_pop = bpf_map_lookup_elem(&sock_bytes, &four);
+       pop = bpf_map_lookup_elem(&sock_bytes, &five);
+       if (start_pop && pop)
+               bpf_msg_pop_data(msg, *start_pop, *pop, 0);
+
        f = bpf_map_lookup_elem(&sock_redir_flags, &zero);
        if (f && *f) {
                key = 2;
@@ -292,8 +315,9 @@ int bpf_prog6(struct sk_msg_md *msg)
 SEC("sk_msg4")
 int bpf_prog7(struct sk_msg_md *msg)
 {
-       int zero = 0, one = 1, two = 2, three = 3, len1, len2 = 0, len3;
-       int *bytes, *start, *end, *start_push, *end_push, *f;
+       int *bytes, *start, *end, *start_push, *end_push, *start_pop, *pop, *f;
+       int zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5;
+       int len1, len2 = 0, len3, len4;
        int err1 = 0, err2 = 0, key = 0;
        __u64 flags = 0;
 
@@ -335,6 +359,22 @@ int bpf_prog7(struct sk_msg_md *msg)
                           len2 ? len2 : len1, len3);
        }
 
+       start_pop = bpf_map_lookup_elem(&sock_bytes, &four);
+       pop = bpf_map_lookup_elem(&sock_bytes, &five);
+       if (start_pop && pop) {
+               int err;
+
+               bpf_printk("sk_msg4: pop(%i@%i)\n",
+                          start_pop, pop);
+               err = bpf_msg_pop_data(msg, *start_pop, *pop, 0);
+               if (err)
+                       bpf_printk("sk_msg4: pop_data err %i\n", err);
+               len4 = (__u64)msg->data_end - (__u64)msg->data;
+               bpf_printk("sk_msg4: length pop_data %i->%i\n",
+                          len1 ? len1 : 0,  len4);
+       }
+
+
        f = bpf_map_lookup_elem(&sock_redir_flags, &zero);
        if (f && *f) {
                key = 2;
@@ -389,8 +429,8 @@ int bpf_prog9(struct sk_msg_md *msg)
 SEC("sk_msg7")
 int bpf_prog10(struct sk_msg_md *msg)
 {
-       int *bytes, *start, *end, *start_push, *end_push;
-       int zero = 0, one = 1, two = 2, three = 3;
+       int *bytes, *start, *end, *start_push, *end_push, *start_pop, *pop;
+       int zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5;
 
        bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
        if (bytes)
@@ -406,7 +446,11 @@ int bpf_prog10(struct sk_msg_md *msg)
        end_push = bpf_map_lookup_elem(&sock_bytes, &three);
        if (start_push && end_push)
                bpf_msg_push_data(msg, *start_push, *end_push, 0);
-
+       start_pop = bpf_map_lookup_elem(&sock_bytes, &four);
+       pop = bpf_map_lookup_elem(&sock_bytes, &five);
+       if (start_pop && pop)
+               bpf_msg_pop_data(msg, *start_pop, *pop, 0);
+       bpf_printk("return sk drop\n");
        return SK_DROP;
 }
 
diff --git a/tools/testing/selftests/bpf/test_tcpnotify.h b/tools/testing/selftests/bpf/test_tcpnotify.h
new file mode 100644 (file)
index 0000000..8b6cea0
--- /dev/null
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef _TEST_TCPBPF_H
+#define _TEST_TCPBPF_H
+
+struct tcpnotify_globals {
+       __u32 total_retrans;
+       __u32 ncalls;
+};
+
+struct tcp_notifier {
+       __u8    type;
+       __u8    subtype;
+       __u8    source;
+       __u8    hash;
+};
+
+#define        TESTPORT        12877
+#endif
diff --git a/tools/testing/selftests/bpf/test_tcpnotify_kern.c b/tools/testing/selftests/bpf/test_tcpnotify_kern.c
new file mode 100644 (file)
index 0000000..edbca20
--- /dev/null
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stddef.h>
+#include <string.h>
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/tcp.h>
+#include <netinet/in.h>
+#include "bpf_helpers.h"
+#include "bpf_endian.h"
+#include "test_tcpnotify.h"
+
+struct bpf_map_def SEC("maps") global_map = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(__u32),
+       .value_size = sizeof(struct tcpnotify_globals),
+       .max_entries = 4,
+};
+
+struct bpf_map_def SEC("maps") perf_event_map = {
+       .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(__u32),
+       .max_entries = 2,
+};
+
+int _version SEC("version") = 1;
+
+SEC("sockops")
+int bpf_testcb(struct bpf_sock_ops *skops)
+{
+       int rv = -1;
+       int op;
+
+       op = (int) skops->op;
+
+       if (bpf_ntohl(skops->remote_port) != TESTPORT) {
+               skops->reply = -1;
+               return 0;
+       }
+
+       switch (op) {
+       case BPF_SOCK_OPS_TIMEOUT_INIT:
+       case BPF_SOCK_OPS_RWND_INIT:
+       case BPF_SOCK_OPS_NEEDS_ECN:
+       case BPF_SOCK_OPS_BASE_RTT:
+       case BPF_SOCK_OPS_RTO_CB:
+               rv = 1;
+               break;
+
+       case BPF_SOCK_OPS_TCP_CONNECT_CB:
+       case BPF_SOCK_OPS_TCP_LISTEN_CB:
+       case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
+       case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
+               bpf_sock_ops_cb_flags_set(skops, (BPF_SOCK_OPS_RETRANS_CB_FLAG|
+                                         BPF_SOCK_OPS_RTO_CB_FLAG));
+               rv = 1;
+               break;
+       case BPF_SOCK_OPS_RETRANS_CB: {
+                       __u32 key = 0;
+                       struct tcpnotify_globals g, *gp;
+                       struct tcp_notifier msg = {
+                               .type = 0xde,
+                               .subtype = 0xad,
+                               .source = 0xbe,
+                               .hash = 0xef,
+                       };
+
+                       rv = 1;
+
+                       /* Update results */
+                       gp = bpf_map_lookup_elem(&global_map, &key);
+                       if (!gp)
+                               break;
+                       g = *gp;
+                       g.total_retrans = skops->total_retrans;
+                       g.ncalls++;
+                       bpf_map_update_elem(&global_map, &key, &g,
+                                           BPF_ANY);
+                       bpf_perf_event_output(skops, &perf_event_map,
+                                             BPF_F_CURRENT_CPU,
+                                             &msg, sizeof(msg));
+               }
+               break;
+       default:
+               rv = -1;
+       }
+       skops->reply = rv;
+       return 1;
+}
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_tcpnotify_user.c b/tools/testing/selftests/bpf/test_tcpnotify_user.c
new file mode 100644 (file)
index 0000000..ff3c452
--- /dev/null
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <asm/types.h>
+#include <sys/syscall.h>
+#include <errno.h>
+#include <string.h>
+#include <linux/bpf.h>
+#include <sys/socket.h>
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#include <sys/ioctl.h>
+#include <linux/rtnetlink.h>
+#include <signal.h>
+#include <linux/perf_event.h>
+
+#include "bpf_rlimit.h"
+#include "bpf_util.h"
+#include "cgroup_helpers.h"
+
+#include "test_tcpnotify.h"
+#include "trace_helpers.h"
+
+#define SOCKET_BUFFER_SIZE (getpagesize() < 8192L ? getpagesize() : 8192L)
+
+pthread_t tid;
+int rx_callbacks;
+
+static int dummyfn(void *data, int size)
+{
+       struct tcp_notifier *t = data;
+
+       if (t->type != 0xde || t->subtype != 0xad ||
+           t->source != 0xbe || t->hash != 0xef)
+               return 1;
+       rx_callbacks++;
+       return 0;
+}
+
+void tcp_notifier_poller(int fd)
+{
+       while (1)
+               perf_event_poller(fd, dummyfn);
+}
+
+static void *poller_thread(void *arg)
+{
+       int fd = *(int *)arg;
+
+       tcp_notifier_poller(fd);
+       return arg;
+}
+
+int verify_result(const struct tcpnotify_globals *result)
+{
+       return (result->ncalls > 0 && result->ncalls == rx_callbacks ? 0 : 1);
+}
+
+static int bpf_find_map(const char *test, struct bpf_object *obj,
+                       const char *name)
+{
+       struct bpf_map *map;
+
+       map = bpf_object__find_map_by_name(obj, name);
+       if (!map) {
+               printf("%s:FAIL:map '%s' not found\n", test, name);
+               return -1;
+       }
+       return bpf_map__fd(map);
+}
+
+static int setup_bpf_perf_event(int mapfd)
+{
+       struct perf_event_attr attr = {
+               .sample_type = PERF_SAMPLE_RAW,
+               .type = PERF_TYPE_SOFTWARE,
+               .config = PERF_COUNT_SW_BPF_OUTPUT,
+       };
+       int key = 0;
+       int pmu_fd;
+
+       pmu_fd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, 0);
+       if (pmu_fd < 0)
+               return pmu_fd;
+       bpf_map_update_elem(mapfd, &key, &pmu_fd, BPF_ANY);
+
+       ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
+       return pmu_fd;
+}
+
+int main(int argc, char **argv)
+{
+       const char *file = "test_tcpnotify_kern.o";
+       int prog_fd, map_fd, perf_event_fd;
+       struct tcpnotify_globals g = {0};
+       const char *cg_path = "/foo";
+       int error = EXIT_FAILURE;
+       struct bpf_object *obj;
+       int cg_fd = -1;
+       __u32 key = 0;
+       int rv;
+       char test_script[80];
+       int pmu_fd;
+       cpu_set_t cpuset;
+
+       CPU_ZERO(&cpuset);
+       CPU_SET(0, &cpuset);
+       pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
+
+       if (setup_cgroup_environment())
+               goto err;
+
+       cg_fd = create_and_get_cgroup(cg_path);
+       if (!cg_fd)
+               goto err;
+
+       if (join_cgroup(cg_path))
+               goto err;
+
+       if (bpf_prog_load(file, BPF_PROG_TYPE_SOCK_OPS, &obj, &prog_fd)) {
+               printf("FAILED: load_bpf_file failed for: %s\n", file);
+               goto err;
+       }
+
+       rv = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_SOCK_OPS, 0);
+       if (rv) {
+               printf("FAILED: bpf_prog_attach: %d (%s)\n",
+                      error, strerror(errno));
+               goto err;
+       }
+
+       perf_event_fd = bpf_find_map(__func__, obj, "perf_event_map");
+       if (perf_event_fd < 0)
+               goto err;
+
+       map_fd = bpf_find_map(__func__, obj, "global_map");
+       if (map_fd < 0)
+               goto err;
+
+       pmu_fd = setup_bpf_perf_event(perf_event_fd);
+       if (pmu_fd < 0 || perf_event_mmap(pmu_fd) < 0)
+               goto err;
+
+       pthread_create(&tid, NULL, poller_thread, (void *)&pmu_fd);
+
+       sprintf(test_script,
+               "/usr/sbin/iptables -A INPUT -p tcp --dport %d -j DROP",
+               TESTPORT);
+       system(test_script);
+
+       sprintf(test_script,
+               "/usr/bin/nc 127.0.0.1 %d < /etc/passwd > /dev/null 2>&1 ",
+               TESTPORT);
+       system(test_script);
+
+       sprintf(test_script,
+               "/usr/sbin/iptables -D INPUT -p tcp --dport %d -j DROP",
+               TESTPORT);
+       system(test_script);
+
+       rv = bpf_map_lookup_elem(map_fd, &key, &g);
+       if (rv != 0) {
+               printf("FAILED: bpf_map_lookup_elem returns %d\n", rv);
+               goto err;
+       }
+
+       sleep(10);
+
+       if (verify_result(&g)) {
+               printf("FAILED: Wrong stats Expected %d calls, got %d\n",
+                       g.ncalls, rx_callbacks);
+               goto err;
+       }
+
+       printf("PASSED!\n");
+       error = 0;
+err:
+       bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS);
+       close(cg_fd);
+       cleanup_cgroup_environment();
+       return error;
+}
index 6f61df6..baafe5c 100644 (file)
@@ -49,6 +49,7 @@
 #define MAX_INSNS      BPF_MAXINSNS
 #define MAX_FIXUPS     8
 #define MAX_NR_MAPS    13
+#define MAX_TEST_RUNS  8
 #define POINTER_VALUE  0xcafe4all
 #define TEST_DATA_LEN  64
 
@@ -76,7 +77,7 @@ struct bpf_test {
        int fixup_percpu_cgroup_storage[MAX_FIXUPS];
        const char *errstr;
        const char *errstr_unpriv;
-       uint32_t retval, retval_unpriv;
+       uint32_t retval, retval_unpriv, insn_processed;
        enum {
                UNDEF,
                ACCEPT,
@@ -86,6 +87,14 @@ struct bpf_test {
        uint8_t flags;
        __u8 data[TEST_DATA_LEN];
        void (*fill_helper)(struct bpf_test *self);
+       uint8_t runs;
+       struct {
+               uint32_t retval, retval_unpriv;
+               union {
+                       __u8 data[TEST_DATA_LEN];
+                       __u64 data64[TEST_DATA_LEN / 8];
+               };
+       } retvals[MAX_TEST_RUNS];
 };
 
 /* Note we want this to be 64 bit aligned so that the end of our array is
@@ -721,8 +730,18 @@ static struct bpf_test tests[] = {
                        BPF_ALU32_IMM(BPF_ARSH, BPF_REG_0, 5),
                        BPF_EXIT_INSN(),
                },
-               .result = REJECT,
-               .errstr = "unknown opcode c4",
+               .result = ACCEPT,
+               .retval = 0,
+       },
+       {
+               "arsh32 on imm 2",
+               .insns = {
+                       BPF_LD_IMM64(BPF_REG_0, 0x1122334485667788),
+                       BPF_ALU32_IMM(BPF_ARSH, BPF_REG_0, 7),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .retval = -16069393,
        },
        {
                "arsh32 on reg",
@@ -732,8 +751,19 @@ static struct bpf_test tests[] = {
                        BPF_ALU32_REG(BPF_ARSH, BPF_REG_0, BPF_REG_1),
                        BPF_EXIT_INSN(),
                },
-               .result = REJECT,
-               .errstr = "unknown opcode cc",
+               .result = ACCEPT,
+               .retval = 0,
+       },
+       {
+               "arsh32 on reg 2",
+               .insns = {
+                       BPF_LD_IMM64(BPF_REG_0, 0xffff55667788),
+                       BPF_MOV64_IMM(BPF_REG_1, 15),
+                       BPF_ALU32_REG(BPF_ARSH, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .retval = 43724,
        },
        {
                "arsh64 on imm",
@@ -980,14 +1010,44 @@ static struct bpf_test tests[] = {
                        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
                        /* mess up with R1 pointer on stack */
                        BPF_ST_MEM(BPF_B, BPF_REG_10, -7, 0x23),
-                       /* fill back into R0 should fail */
+                       /* fill back into R0 is fine for priv.
+                        * R0 now becomes SCALAR_VALUE.
+                        */
                        BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
+                       /* Load from R0 should fail. */
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 8),
                        BPF_EXIT_INSN(),
                },
                .errstr_unpriv = "attempt to corrupt spilled",
-               .errstr = "corrupted spill",
+               .errstr = "R0 invalid mem access 'inv",
                .result = REJECT,
        },
+       {
+               "check corrupted spill/fill, LSB",
+               .insns = {
+                       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
+                       BPF_ST_MEM(BPF_H, BPF_REG_10, -8, 0xcafe),
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "attempt to corrupt spilled",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+               .retval = POINTER_VALUE,
+       },
+       {
+               "check corrupted spill/fill, MSB",
+               .insns = {
+                       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
+                       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0x12345678),
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "attempt to corrupt spilled",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+               .retval = POINTER_VALUE,
+       },
        {
                "invalid src register in STX",
                .insns = {
@@ -1792,10 +1852,20 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SK_SKB,
        },
        {
-               "invalid 64B read of family in SK_MSG",
+               "valid access size in SK_MSG",
+               .insns = {
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct sk_msg_md, size)),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .prog_type = BPF_PROG_TYPE_SK_MSG,
+       },
+       {
+               "invalid 64B read of size in SK_MSG",
                .insns = {
                        BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1,
-                                   offsetof(struct sk_msg_md, family)),
+                                   offsetof(struct sk_msg_md, size)),
                        BPF_EXIT_INSN(),
                },
                .errstr = "invalid bpf_context access",
@@ -1806,10 +1876,10 @@ static struct bpf_test tests[] = {
                "invalid read past end of SK_MSG",
                .insns = {
                        BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
-                                   offsetof(struct sk_msg_md, local_port) + 4),
+                                   offsetof(struct sk_msg_md, size) + 4),
                        BPF_EXIT_INSN(),
                },
-               .errstr = "R0 !read_ok",
+               .errstr = "invalid bpf_context access",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_SK_MSG,
        },
@@ -1823,6 +1893,7 @@ static struct bpf_test tests[] = {
                .errstr = "invalid bpf_context access",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_SK_MSG,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "direct packet read for SK_MSG",
@@ -2026,29 +2097,27 @@ static struct bpf_test tests[] = {
                .result = ACCEPT,
        },
        {
-               "check skb->hash byte load not permitted 1",
+               "check skb->hash byte load permitted 1",
                .insns = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
                        BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1,
                                    offsetof(struct __sk_buff, hash) + 1),
                        BPF_EXIT_INSN(),
                },
-               .errstr = "invalid bpf_context access",
-               .result = REJECT,
+               .result = ACCEPT,
        },
        {
-               "check skb->hash byte load not permitted 2",
+               "check skb->hash byte load permitted 2",
                .insns = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
                        BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1,
                                    offsetof(struct __sk_buff, hash) + 2),
                        BPF_EXIT_INSN(),
                },
-               .errstr = "invalid bpf_context access",
-               .result = REJECT,
+               .result = ACCEPT,
        },
        {
-               "check skb->hash byte load not permitted 3",
+               "check skb->hash byte load permitted 3",
                .insns = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
 #if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -2060,8 +2129,7 @@ static struct bpf_test tests[] = {
 #endif
                        BPF_EXIT_INSN(),
                },
-               .errstr = "invalid bpf_context access",
-               .result = REJECT,
+               .result = ACCEPT,
        },
        {
                "check cb access: byte, wrong type",
@@ -2173,7 +2241,7 @@ static struct bpf_test tests[] = {
                .result = ACCEPT,
        },
        {
-               "check skb->hash half load not permitted",
+               "check skb->hash half load permitted 2",
                .insns = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
 #if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -2182,12 +2250,45 @@ static struct bpf_test tests[] = {
 #else
                        BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,
                                    offsetof(struct __sk_buff, hash)),
+#endif
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+       },
+       {
+               "check skb->hash half load not permitted, unaligned 1",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+                       BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, hash) + 1),
+#else
+                       BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, hash) + 3),
 #endif
                        BPF_EXIT_INSN(),
                },
                .errstr = "invalid bpf_context access",
                .result = REJECT,
        },
+       {
+               "check skb->hash half load not permitted, unaligned 3",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+                       BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, hash) + 3),
+#else
+                       BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, hash) + 1),
+#endif
+                       BPF_EXIT_INSN(),
+               },
+               .errstr = "invalid bpf_context access",
+               .result = REJECT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
+       },
        {
                "check cb access: half, wrong type",
                .insns = {
@@ -2418,6 +2519,10 @@ static struct bpf_test tests[] = {
                                    offsetof(struct __sk_buff, tc_index)),
                        BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
                                    offsetof(struct __sk_buff, cb[3])),
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, tstamp)),
+                       BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
+                                   offsetof(struct __sk_buff, tstamp)),
                        BPF_EXIT_INSN(),
                },
                .errstr_unpriv = "",
@@ -2903,6 +3008,19 @@ static struct bpf_test tests[] = {
                .result_unpriv = REJECT,
                .result = ACCEPT,
        },
+       {
+               "alu32: mov u32 const",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_7, 0),
+                       BPF_ALU32_IMM(BPF_AND, BPF_REG_7, 1),
+                       BPF_MOV32_REG(BPF_REG_0, BPF_REG_7),
+                       BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_7, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .retval = 0,
+       },
        {
                "unpriv: partial copy of pointer",
                .insns = {
@@ -3249,6 +3367,7 @@ static struct bpf_test tests[] = {
                .result = REJECT,
                .errstr = "R0 invalid mem access 'inv'",
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "raw_stack: skb_load_bytes, spilled regs corruption 2",
@@ -3279,6 +3398,7 @@ static struct bpf_test tests[] = {
                .result = REJECT,
                .errstr = "R3 invalid mem access 'inv'",
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "raw_stack: skb_load_bytes, spilled regs + data",
@@ -3778,6 +3898,7 @@ static struct bpf_test tests[] = {
                .errstr = "R2 invalid mem access 'inv'",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "direct packet access: test16 (arith on data_end)",
@@ -3880,6 +4001,7 @@ static struct bpf_test tests[] = {
                },
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "direct packet access: test21 (x += pkt_ptr, 2)",
@@ -3905,6 +4027,7 @@ static struct bpf_test tests[] = {
                },
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "direct packet access: test22 (x += pkt_ptr, 3)",
@@ -3935,6 +4058,7 @@ static struct bpf_test tests[] = {
                },
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "direct packet access: test23 (x += pkt_ptr, 4)",
@@ -3961,6 +4085,7 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = REJECT,
                .errstr = "invalid access to packet, off=0 size=8, R5(id=1,off=0,r=0)",
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "direct packet access: test24 (x += pkt_ptr, 5)",
@@ -3986,6 +4111,7 @@ static struct bpf_test tests[] = {
                },
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "direct packet access: test25 (marking on <, good access)",
@@ -5117,6 +5243,7 @@ static struct bpf_test tests[] = {
                .result = REJECT,
                .errstr = "invalid access to map value, value_size=64 off=-2 size=4",
                .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "invalid cgroup storage access 5",
@@ -5233,6 +5360,7 @@ static struct bpf_test tests[] = {
                .result = REJECT,
                .errstr = "invalid access to map value, value_size=64 off=-2 size=4",
                .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "invalid per-cpu cgroup storage access 5",
@@ -5269,6 +5397,31 @@ static struct bpf_test tests[] = {
                .errstr_unpriv = "R2 leaks addr into helper function",
                .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
        },
+       {
+               "write tstamp from CGROUP_SKB",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
+                                   offsetof(struct __sk_buff, tstamp)),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .result_unpriv = REJECT,
+               .errstr_unpriv = "invalid bpf_context access off=152 size=8",
+               .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       },
+       {
+               "read tstamp from CGROUP_SKB",
+               .insns = {
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, tstamp)),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       },
        {
                "multiple registers share map_lookup_elem result",
                .insns = {
@@ -7149,6 +7302,7 @@ static struct bpf_test tests[] = {
                .errstr = "invalid mem access 'inv'",
                .result = REJECT,
                .result_unpriv = REJECT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "map element value illegal alu op, 5",
@@ -7171,6 +7325,7 @@ static struct bpf_test tests[] = {
                .fixup_map_hash_48b = { 3 },
                .errstr = "R0 invalid mem access 'inv'",
                .result = REJECT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "map element value is preserved across register spilling",
@@ -7664,6 +7819,7 @@ static struct bpf_test tests[] = {
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .retval = 0 /* csum_diff of 64-byte packet */,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "helper access to variable memory: size = 0 not allowed on NULL (!ARG_PTR_TO_MEM_OR_NULL)",
@@ -8576,7 +8732,7 @@ static struct bpf_test tests[] = {
                        BPF_JMP_IMM(BPF_JA, 0, 0, -7),
                },
                .fixup_map_hash_8b = { 4 },
-               .errstr = "R0 invalid mem access 'inv'",
+               .errstr = "unbounded min value",
                .result = REJECT,
        },
        {
@@ -9626,6 +9782,7 @@ static struct bpf_test tests[] = {
                },
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data' > pkt_end, bad access 1",
@@ -9663,6 +9820,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_end > pkt_data', good access",
@@ -9701,6 +9859,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_end > pkt_data', bad access 2",
@@ -9719,6 +9878,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data' < pkt_end, good access",
@@ -9757,6 +9917,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data' < pkt_end, bad access 2",
@@ -9775,6 +9936,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_end < pkt_data', good access",
@@ -9792,6 +9954,7 @@ static struct bpf_test tests[] = {
                },
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_end < pkt_data', bad access 1",
@@ -9829,6 +9992,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data' >= pkt_end, good access",
@@ -9865,6 +10029,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data' >= pkt_end, bad access 2",
@@ -9902,6 +10067,7 @@ static struct bpf_test tests[] = {
                },
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_end >= pkt_data', bad access 1",
@@ -9940,6 +10106,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data' <= pkt_end, good access",
@@ -9958,6 +10125,7 @@ static struct bpf_test tests[] = {
                },
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data' <= pkt_end, bad access 1",
@@ -9996,6 +10164,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_end <= pkt_data', good access",
@@ -10032,6 +10201,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_end <= pkt_data', bad access 2",
@@ -10068,6 +10238,7 @@ static struct bpf_test tests[] = {
                },
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_meta' > pkt_data, bad access 1",
@@ -10105,6 +10276,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data > pkt_meta', good access",
@@ -10143,6 +10315,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data > pkt_meta', bad access 2",
@@ -10161,6 +10334,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_meta' < pkt_data, good access",
@@ -10199,6 +10373,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_meta' < pkt_data, bad access 2",
@@ -10217,6 +10392,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data < pkt_meta', good access",
@@ -10234,6 +10410,7 @@ static struct bpf_test tests[] = {
                },
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data < pkt_meta', bad access 1",
@@ -10271,6 +10448,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_meta' >= pkt_data, good access",
@@ -10307,6 +10485,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_meta' >= pkt_data, bad access 2",
@@ -10344,6 +10523,7 @@ static struct bpf_test tests[] = {
                },
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data >= pkt_meta', bad access 1",
@@ -10382,6 +10562,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_meta' <= pkt_data, good access",
@@ -10400,6 +10581,7 @@ static struct bpf_test tests[] = {
                },
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_meta' <= pkt_data, bad access 1",
@@ -10438,6 +10620,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data <= pkt_meta', good access",
@@ -10474,6 +10657,7 @@ static struct bpf_test tests[] = {
                .errstr = "R1 offset is outside of the packet",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "XDP pkt read, pkt_data <= pkt_meta', bad access 2",
@@ -10547,7 +10731,7 @@ static struct bpf_test tests[] = {
                "check deducing bounds from const, 5",
                .insns = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
-                       BPF_JMP_IMM(BPF_JSGE, BPF_REG_0, 0, 1),
+                       BPF_JMP_IMM(BPF_JSGE, BPF_REG_0, 1, 1),
                        BPF_ALU64_REG(BPF_SUB, BPF_REG_0, BPF_REG_1),
                        BPF_EXIT_INSN(),
                },
@@ -10578,6 +10762,7 @@ static struct bpf_test tests[] = {
                },
                .result = REJECT,
                .errstr = "dereference of modified ctx ptr",
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "check deducing bounds from const, 8",
@@ -10591,6 +10776,7 @@ static struct bpf_test tests[] = {
                },
                .result = REJECT,
                .errstr = "dereference of modified ctx ptr",
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "check deducing bounds from const, 9",
@@ -11065,6 +11251,7 @@ static struct bpf_test tests[] = {
                .result = REJECT,
                .errstr = "R6 invalid mem access 'inv'",
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: two calls with args",
@@ -11930,6 +12117,7 @@ static struct bpf_test tests[] = {
                .fixup_map_hash_8b = { 12, 22 },
                .result = REJECT,
                .errstr = "invalid access to map value, value_size=8 off=2 size=8",
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: two calls that receive map_value via arg=ptr_stack_of_caller. test2",
@@ -12073,6 +12261,7 @@ static struct bpf_test tests[] = {
                .fixup_map_hash_8b = { 12, 22 },
                .result = REJECT,
                .errstr = "invalid access to map value, value_size=8 off=2 size=8",
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: two calls that receive map_value_ptr_or_null via arg. test1",
@@ -12244,6 +12433,7 @@ static struct bpf_test tests[] = {
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .retval = POINTER_VALUE,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: pkt_ptr spill into caller stack 2",
@@ -12275,6 +12465,7 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .errstr = "invalid access to packet",
                .result = REJECT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: pkt_ptr spill into caller stack 3",
@@ -12310,6 +12501,7 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
                .retval = 1,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: pkt_ptr spill into caller stack 4",
@@ -12344,6 +12536,7 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
                .retval = 1,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: pkt_ptr spill into caller stack 5",
@@ -12377,6 +12570,7 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .errstr = "same insn cannot be used with different",
                .result = REJECT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: pkt_ptr spill into caller stack 6",
@@ -12412,6 +12606,7 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .errstr = "R4 invalid mem access",
                .result = REJECT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: pkt_ptr spill into caller stack 7",
@@ -12446,6 +12641,7 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .errstr = "R4 invalid mem access",
                .result = REJECT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: pkt_ptr spill into caller stack 8",
@@ -12486,6 +12682,7 @@ static struct bpf_test tests[] = {
                },
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: pkt_ptr spill into caller stack 9",
@@ -12527,6 +12724,7 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .errstr = "invalid access to packet",
                .result = REJECT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "calls: caller stack init to zero or map_value_or_null",
@@ -12892,6 +13090,7 @@ static struct bpf_test tests[] = {
                .result = REJECT,
                .errstr = "BPF_XADD stores into R2 pkt is not allowed",
                .prog_type = BPF_PROG_TYPE_XDP,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "xadd/w check whether src/dst got mangled, 1",
@@ -13378,6 +13577,7 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .errstr = "Unreleased reference",
                .result = REJECT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "reference tracking: alloc, check, free in both subbranches",
@@ -13406,6 +13606,7 @@ static struct bpf_test tests[] = {
                },
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
+               .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
        },
        {
                "reference tracking in call: free reference in subprog",
@@ -13495,6 +13696,28 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
        },
+       {
+               "allocated_stack",
+               .insns = {
+                       BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_1),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+                       BPF_ALU64_REG(BPF_MOV, BPF_REG_7, BPF_REG_0),
+                       BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, -8),
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, -8),
+                       BPF_STX_MEM(BPF_B, BPF_REG_10, BPF_REG_7, -9),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_10, -9),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .result_unpriv = ACCEPT,
+               .insn_processed = 15,
+       },
        {
                "reference tracking in call: free reference in subprog and outside",
                .insns = {
@@ -13896,6 +14119,274 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
        },
+       {
+               "calls: ctx read at start of subprog",
+               .insns = {
+                       BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 5),
+                       BPF_JMP_REG(BPF_JSGT, BPF_REG_0, BPF_REG_0, 0),
+                       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
+                       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+                       BPF_EXIT_INSN(),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_1, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .errstr_unpriv = "function calls to other bpf functions are allowed for root only",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "check wire_len is not readable by sockets",
+               .insns = {
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, wire_len)),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr = "invalid bpf_context access",
+               .result = REJECT,
+       },
+       {
+               "check wire_len is readable by tc classifier",
+               .insns = {
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, wire_len)),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+       },
+       {
+               "check wire_len is not writable by tc classifier",
+               .insns = {
+                       BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
+                                   offsetof(struct __sk_buff, wire_len)),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .errstr = "invalid bpf_context access",
+               .errstr_unpriv = "R1 leaks addr",
+               .result = REJECT,
+       },
+       {
+               "calls: cross frame pruning",
+               .insns = {
+                       /* r8 = !!random();
+                        * call pruner()
+                        * if (r8)
+                        *     do something bad;
+                        */
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_get_prandom_u32),
+                       BPF_MOV64_IMM(BPF_REG_8, 0),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+                       BPF_MOV64_IMM(BPF_REG_8, 1),
+                       BPF_MOV64_REG(BPF_REG_1, BPF_REG_8),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4),
+                       BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, 1, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_1, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+                       BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .errstr_unpriv = "function calls to other bpf functions are allowed for root only",
+                .result = REJECT,
+       },
+       {
+               "jset: functional",
+               .insns = {
+                       /* r0 = 0 */
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       /* prep for direct packet access via r2 */
+                       BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data)),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data_end)),
+                       BPF_MOV64_REG(BPF_REG_4, BPF_REG_2),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 8),
+                       BPF_JMP_REG(BPF_JLE, BPF_REG_4, BPF_REG_3, 1),
+                       BPF_EXIT_INSN(),
+
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_2, 0),
+
+                       /* reg, bit 63 or bit 0 set, taken */
+                       BPF_LD_IMM64(BPF_REG_8, 0x8000000000000001),
+                       BPF_JMP_REG(BPF_JSET, BPF_REG_7, BPF_REG_8, 1),
+                       BPF_EXIT_INSN(),
+
+                       /* reg, bit 62, not taken */
+                       BPF_LD_IMM64(BPF_REG_8, 0x4000000000000000),
+                       BPF_JMP_REG(BPF_JSET, BPF_REG_7, BPF_REG_8, 1),
+                       BPF_JMP_IMM(BPF_JA, 0, 0, 1),
+                       BPF_EXIT_INSN(),
+
+                       /* imm, any bit set, taken */
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_7, -1, 1),
+                       BPF_EXIT_INSN(),
+
+                       /* imm, bit 31 set, taken */
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_7, 0x80000000, 1),
+                       BPF_EXIT_INSN(),
+
+                       /* all good - return r0 == 2 */
+                       BPF_MOV64_IMM(BPF_REG_0, 2),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .runs = 7,
+               .retvals = {
+                       { .retval = 2,
+                         .data64 = { (1ULL << 63) | (1U << 31) | (1U << 0), }
+                       },
+                       { .retval = 2,
+                         .data64 = { (1ULL << 63) | (1U << 31), }
+                       },
+                       { .retval = 2,
+                         .data64 = { (1ULL << 31) | (1U << 0), }
+                       },
+                       { .retval = 2,
+                         .data64 = { (__u32)-1, }
+                       },
+                       { .retval = 2,
+                         .data64 = { ~0x4000000000000000ULL, }
+                       },
+                       { .retval = 0,
+                         .data64 = { 0, }
+                       },
+                       { .retval = 0,
+                         .data64 = { ~0ULL, }
+                       },
+               },
+       },
+       {
+               "jset: sign-extend",
+               .insns = {
+                       /* r0 = 0 */
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       /* prep for direct packet access via r2 */
+                       BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data)),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data_end)),
+                       BPF_MOV64_REG(BPF_REG_4, BPF_REG_2),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 8),
+                       BPF_JMP_REG(BPF_JLE, BPF_REG_4, BPF_REG_3, 1),
+                       BPF_EXIT_INSN(),
+
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_2, 0),
+
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_7, 0x80000000, 1),
+                       BPF_EXIT_INSN(),
+
+                       BPF_MOV64_IMM(BPF_REG_0, 2),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 2,
+               .data = { 1, 0, 0, 0, 0, 0, 0, 1, },
+       },
+       {
+               "jset: known const compare",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 1),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 1, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .retval_unpriv = 1,
+               .result_unpriv = ACCEPT,
+               .retval = 1,
+               .result = ACCEPT,
+       },
+       {
+               "jset: known const compare bad",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 1, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .errstr_unpriv = "!read_ok",
+               .result_unpriv = REJECT,
+               .errstr = "!read_ok",
+               .result = REJECT,
+       },
+       {
+               "jset: unknown const compare taken",
+               .insns = {
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_get_prandom_u32),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 1, 1),
+                       BPF_JMP_IMM(BPF_JA, 0, 0, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .errstr_unpriv = "!read_ok",
+               .result_unpriv = REJECT,
+               .errstr = "!read_ok",
+               .result = REJECT,
+       },
+       {
+               "jset: unknown const compare not taken",
+               .insns = {
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_get_prandom_u32),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 1, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .errstr_unpriv = "!read_ok",
+               .result_unpriv = REJECT,
+               .errstr = "!read_ok",
+               .result = REJECT,
+       },
+       {
+               "jset: half-known const compare",
+               .insns = {
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_get_prandom_u32),
+                       BPF_ALU64_IMM(BPF_OR, BPF_REG_0, 2),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 3, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .result_unpriv = ACCEPT,
+               .result = ACCEPT,
+       },
+       {
+               "jset: range",
+               .insns = {
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_get_prandom_u32),
+                       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0xff),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_1, 0xf0, 3),
+                       BPF_JMP_IMM(BPF_JLT, BPF_REG_1, 0x10, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_1, 0x10, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0x10, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .result_unpriv = ACCEPT,
+               .result = ACCEPT,
+       },
 };
 
 static int probe_filter_length(const struct bpf_insn *fp)
@@ -13921,7 +14412,7 @@ static int create_map(uint32_t type, uint32_t size_key,
        return fd;
 }
 
-static int create_prog_dummy1(enum bpf_map_type prog_type)
+static int create_prog_dummy1(enum bpf_prog_type prog_type)
 {
        struct bpf_insn prog[] = {
                BPF_MOV64_IMM(BPF_REG_0, 42),
@@ -13932,7 +14423,7 @@ static int create_prog_dummy1(enum bpf_map_type prog_type)
                                ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
 }
 
-static int create_prog_dummy2(enum bpf_map_type prog_type, int mfd, int idx)
+static int create_prog_dummy2(enum bpf_prog_type prog_type, int mfd, int idx)
 {
        struct bpf_insn prog[] = {
                BPF_MOV64_IMM(BPF_REG_3, idx),
@@ -13947,7 +14438,7 @@ static int create_prog_dummy2(enum bpf_map_type prog_type, int mfd, int idx)
                                ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
 }
 
-static int create_prog_array(enum bpf_map_type prog_type, uint32_t max_elem,
+static int create_prog_array(enum bpf_prog_type prog_type, uint32_t max_elem,
                             int p1key)
 {
        int p2key = 1;
@@ -14018,7 +14509,7 @@ static int create_cgroup_storage(bool percpu)
 
 static char bpf_vlog[UINT_MAX >> 8];
 
-static void do_test_fixup(struct bpf_test *test, enum bpf_map_type prog_type,
+static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
                          struct bpf_insn *prog, int *map_fds)
 {
        int *fixup_map_hash_8b = test->fixup_map_hash_8b;
@@ -14147,7 +14638,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_map_type prog_type,
                do {
                        prog[*fixup_map_stacktrace].imm = map_fds[12];
                        fixup_map_stacktrace++;
-               } while (fixup_map_stacktrace);
+               } while (*fixup_map_stacktrace);
        }
 }
 
@@ -14178,16 +14669,43 @@ out:
        return ret;
 }
 
+static int do_prog_test_run(int fd_prog, bool unpriv, uint32_t expected_val,
+                           void *data, size_t size_data)
+{
+       __u8 tmp[TEST_DATA_LEN << 2];
+       __u32 size_tmp = sizeof(tmp);
+       uint32_t retval;
+       int err;
+
+       if (unpriv)
+               set_admin(true);
+       err = bpf_prog_test_run(fd_prog, 1, data, size_data,
+                               tmp, &size_tmp, &retval, NULL);
+       if (unpriv)
+               set_admin(false);
+       if (err && errno != 524/*ENOTSUPP*/ && errno != EPERM) {
+               printf("Unexpected bpf_prog_test_run error ");
+               return err;
+       }
+       if (!err && retval != expected_val &&
+           expected_val != POINTER_VALUE) {
+               printf("FAIL retval %d != %d ", retval, expected_val);
+               return 1;
+       }
+
+       return 0;
+}
+
 static void do_test_single(struct bpf_test *test, bool unpriv,
                           int *passes, int *errors)
 {
-       int fd_prog, expected_ret, reject_from_alignment;
+       int fd_prog, expected_ret, alignment_prevented_execution;
        int prog_len, prog_type = test->prog_type;
        struct bpf_insn *prog = test->insns;
+       int run_errs, run_successes;
        int map_fds[MAX_NR_MAPS];
        const char *expected_err;
-       uint32_t expected_val;
-       uint32_t retval;
+       __u32 pflags;
        int i, err;
 
        for (i = 0; i < MAX_NR_MAPS; i++)
@@ -14198,69 +14716,105 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
        do_test_fixup(test, prog_type, prog, map_fds);
        prog_len = probe_filter_length(prog);
 
-       fd_prog = bpf_verify_program(prog_type, prog, prog_len,
-                                    test->flags & F_LOAD_WITH_STRICT_ALIGNMENT,
+       pflags = 0;
+       if (test->flags & F_LOAD_WITH_STRICT_ALIGNMENT)
+               pflags |= BPF_F_STRICT_ALIGNMENT;
+       if (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS)
+               pflags |= BPF_F_ANY_ALIGNMENT;
+       fd_prog = bpf_verify_program(prog_type, prog, prog_len, pflags,
                                     "GPL", 0, bpf_vlog, sizeof(bpf_vlog), 1);
 
        expected_ret = unpriv && test->result_unpriv != UNDEF ?
                       test->result_unpriv : test->result;
        expected_err = unpriv && test->errstr_unpriv ?
                       test->errstr_unpriv : test->errstr;
-       expected_val = unpriv && test->retval_unpriv ?
-                      test->retval_unpriv : test->retval;
-
-       reject_from_alignment = fd_prog < 0 &&
-                               (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS) &&
-                               strstr(bpf_vlog, "Unknown alignment.");
-#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
-       if (reject_from_alignment) {
-               printf("FAIL\nFailed due to alignment despite having efficient unaligned access: '%s'!\n",
-                      strerror(errno));
-               goto fail_log;
-       }
-#endif
+
+       alignment_prevented_execution = 0;
+
        if (expected_ret == ACCEPT) {
-               if (fd_prog < 0 && !reject_from_alignment) {
+               if (fd_prog < 0) {
                        printf("FAIL\nFailed to load prog '%s'!\n",
                               strerror(errno));
                        goto fail_log;
                }
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+               if (fd_prog >= 0 &&
+                   (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS))
+                       alignment_prevented_execution = 1;
+#endif
        } else {
                if (fd_prog >= 0) {
                        printf("FAIL\nUnexpected success to load!\n");
                        goto fail_log;
                }
-               if (!strstr(bpf_vlog, expected_err) && !reject_from_alignment) {
+               if (!strstr(bpf_vlog, expected_err)) {
                        printf("FAIL\nUnexpected error message!\n\tEXP: %s\n\tRES: %s\n",
                              expected_err, bpf_vlog);
                        goto fail_log;
                }
        }
 
-       if (fd_prog >= 0) {
-               __u8 tmp[TEST_DATA_LEN << 2];
-               __u32 size_tmp = sizeof(tmp);
-
-               if (unpriv)
-                       set_admin(true);
-               err = bpf_prog_test_run(fd_prog, 1, test->data,
-                                       sizeof(test->data), tmp, &size_tmp,
-                                       &retval, NULL);
-               if (unpriv)
-                       set_admin(false);
-               if (err && errno != 524/*ENOTSUPP*/ && errno != EPERM) {
-                       printf("Unexpected bpf_prog_test_run error\n");
+       if (test->insn_processed) {
+               uint32_t insn_processed;
+               char *proc;
+
+               proc = strstr(bpf_vlog, "processed ");
+               insn_processed = atoi(proc + 10);
+               if (test->insn_processed != insn_processed) {
+                       printf("FAIL\nUnexpected insn_processed %u vs %u\n",
+                              insn_processed, test->insn_processed);
                        goto fail_log;
                }
-               if (!err && retval != expected_val &&
-                   expected_val != POINTER_VALUE) {
-                       printf("FAIL retval %d != %d\n", retval, expected_val);
-                       goto fail_log;
+       }
+
+       run_errs = 0;
+       run_successes = 0;
+       if (!alignment_prevented_execution && fd_prog >= 0) {
+               uint32_t expected_val;
+               int i;
+
+               if (!test->runs) {
+                       expected_val = unpriv && test->retval_unpriv ?
+                               test->retval_unpriv : test->retval;
+
+                       err = do_prog_test_run(fd_prog, unpriv, expected_val,
+                                              test->data, sizeof(test->data));
+                       if (err)
+                               run_errs++;
+                       else
+                               run_successes++;
                }
+
+               for (i = 0; i < test->runs; i++) {
+                       if (unpriv && test->retvals[i].retval_unpriv)
+                               expected_val = test->retvals[i].retval_unpriv;
+                       else
+                               expected_val = test->retvals[i].retval;
+
+                       err = do_prog_test_run(fd_prog, unpriv, expected_val,
+                                              test->retvals[i].data,
+                                              sizeof(test->retvals[i].data));
+                       if (err) {
+                               printf("(run %d/%d) ", i + 1, test->runs);
+                               run_errs++;
+                       } else {
+                               run_successes++;
+                       }
+               }
+       }
+
+       if (!run_errs) {
+               (*passes)++;
+               if (run_successes > 1)
+                       printf("%d cases ", run_successes);
+               printf("OK");
+               if (alignment_prevented_execution)
+                       printf(" (NOTE: not executed due to unknown alignment)");
+               printf("\n");
+       } else {
+               printf("\n");
+               goto fail_log;
        }
-       (*passes)++;
-       printf("OK%s\n", reject_from_alignment ?
-              " (NOTE: reject due to unknown alignment)" : "");
 close_fds:
        close(fd_prog);
        for (i = 0; i < MAX_NR_MAPS; i++)
diff --git a/tools/testing/selftests/drivers/net/mlxsw/extack.sh b/tools/testing/selftests/drivers/net/mlxsw/extack.sh
new file mode 100755 (executable)
index 0000000..d72d848
--- /dev/null
@@ -0,0 +1,145 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test operations that we expect to report extended ack.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="
+       netdev_pre_up_test
+       vxlan_vlan_add_test
+       port_vlan_add_test
+"
+NUM_NETIFS=2
+source $lib_dir/lib.sh
+
+setup_prepare()
+{
+       swp1=${NETIFS[p1]}
+       swp2=${NETIFS[p2]}
+
+       ip link set dev $swp1 up
+       ip link set dev $swp2 up
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       ip link set dev $swp2 down
+       ip link set dev $swp1 down
+}
+
+netdev_pre_up_test()
+{
+       RET=0
+
+       ip link add name br1 up type bridge vlan_filtering 0 mcast_snooping 0
+       ip link add name vx1 up type vxlan id 1000 \
+               local 192.0.2.17 remote 192.0.2.18 \
+               dstport 4789 nolearning noudpcsum tos inherit ttl 100
+
+       ip link set dev vx1 master br1
+       check_err $?
+
+       ip link set dev $swp1 master br1
+       check_err $?
+
+       ip link add name br2 up type bridge vlan_filtering 0 mcast_snooping 0
+       ip link add name vx2 up type vxlan id 2000 \
+               local 192.0.2.17 remote 192.0.2.18 \
+               dstport 4789 nolearning noudpcsum tos inherit ttl 100
+
+       ip link set dev vx2 master br2
+       check_err $?
+
+       ip link set dev $swp2 master br2
+       check_err $?
+
+       # Unsupported configuration: mlxsw demands that all offloaded VXLAN
+       # devices have the same TTL.
+       ip link set dev vx2 down
+       ip link set dev vx2 type vxlan ttl 200
+
+       ip link set dev vx2 up &>/dev/null
+       check_fail $?
+
+       ip link set dev vx2 up 2>&1 >/dev/null | grep -q mlxsw_spectrum
+       check_err $?
+
+       log_test "extack - NETDEV_PRE_UP"
+
+       ip link del dev vx2
+       ip link del dev br2
+
+       ip link del dev vx1
+       ip link del dev br1
+}
+
+vxlan_vlan_add_test()
+{
+       RET=0
+
+       ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 0
+
+       # Unsupported configuration: mlxsw demands VXLAN with "noudpcsum".
+       ip link add name vx1 up type vxlan id 1000 \
+               local 192.0.2.17 remote 192.0.2.18 \
+               dstport 4789 tos inherit ttl 100
+
+       ip link set dev vx1 master br1
+       check_err $?
+
+       bridge vlan add dev vx1 vid 1
+       check_err $?
+
+       ip link set dev $swp1 master br1
+       check_err $?
+
+       bridge vlan add dev vx1 vid 1 pvid untagged 2>&1 >/dev/null \
+               | grep -q mlxsw_spectrum
+       check_err $?
+
+       log_test "extack - map VLAN at VXLAN device"
+
+       ip link del dev vx1
+       ip link del dev br1
+}
+
+port_vlan_add_test()
+{
+       RET=0
+
+       ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 0
+
+       # Unsupported configuration: mlxsw demands VXLAN with "noudpcsum".
+       ip link add name vx1 up type vxlan id 1000 \
+               local 192.0.2.17 remote 192.0.2.18 \
+               dstport 4789 tos inherit ttl 100
+
+       ip link set dev $swp1 master br1
+       check_err $?
+
+       bridge vlan del dev $swp1 vid 1
+
+       ip link set dev vx1 master br1
+       check_err $?
+
+       bridge vlan add dev $swp1 vid 1 pvid untagged 2>&1 >/dev/null \
+               | grep -q mlxsw_spectrum
+       check_err $?
+
+       log_test "extack - map VLAN at port"
+
+       ip link del dev vx1
+       ip link del dev br1
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/mlxsw/one_armed_router.sh b/tools/testing/selftests/drivers/net/mlxsw/one_armed_router.sh
new file mode 100755 (executable)
index 0000000..f02d83e
--- /dev/null
@@ -0,0 +1,259 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test a "one-armed router" [1] scenario. Packets forwarded between H1 and H2
+# should be forwarded by the ASIC, but also trapped so that ICMP redirect
+# packets could be potentially generated.
+#
+# 1. https://en.wikipedia.org/wiki/One-armed_router
+#
+# +---------------------------------+
+# | 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 |                                                                      |
+# | +--|--------------------------------------------------------------------+ |
+# | |  + $swp1                   BR0 (802.1d)                               | |
+# | |                                                                       | |
+# | |                            192.0.2.2/24                               | |
+# | |                          2001:db8:1::2/64                             | |
+# | |                           198.51.100.2/24                             | |
+# | |                          2001:db8:2::2/64                             | |
+# | |                                                                       | |
+# | |  + $swp2                                                              | |
+# | +--|--------------------------------------------------------------------+ |
+# |    |                                                                      |
+# +----|----------------------------------------------------------------------+
+#      |
+# +----|----------------------------+
+# |    |  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 fwd_mark_ipv4 fwd_mark_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
+}
+
+switch_create()
+{
+       ip link add name br0 type bridge mcast_snooping 0
+       ip link set dev br0 up
+
+       ip link set dev $swp1 master br0
+       ip link set dev $swp1 up
+       ip link set dev $swp2 master br0
+       ip link set dev $swp2 up
+
+       tc qdisc add dev $swp1 clsact
+       tc qdisc add dev $swp2 clsact
+
+       __addr_add_del br0 add 192.0.2.2/24 2001:db8:1::2/64
+       __addr_add_del br0 add 198.51.100.2/24 2001:db8:2::2/64
+}
+
+switch_destroy()
+{
+       __addr_add_del br0 del 198.51.100.2/24 2001:db8:2::2/64
+       __addr_add_del br0 del 192.0.2.2/24 2001:db8:1::2/64
+
+       tc qdisc del dev $swp2 clsact
+       tc qdisc del dev $swp1 clsact
+
+       ip link set dev $swp2 down
+       ip link set dev $swp2 nomaster
+       ip link set dev $swp1 down
+       ip link set dev $swp1 nomaster
+
+       ip link set dev br0 down
+       ip link del dev br0
+}
+
+ping_ipv4()
+{
+       ping_test $h1 198.51.100.1 ": h1->h2"
+}
+
+ping_ipv6()
+{
+       ping6_test $h1 2001:db8:2::1 ": h1->h2"
+}
+
+fwd_mark_ipv4()
+{
+       # Transmit packets from H1 to H2 and make sure they are trapped at
+       # swp1 due to loopback error, but only forwarded by the ASIC through
+       # swp2
+
+       tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
+               skip_hw dst_ip 198.51.100.1 ip_proto udp dst_port 52768 \
+               action pass
+
+       tc filter add dev $swp2 egress protocol ip pref 1 handle 101 flower \
+               skip_hw dst_ip 198.51.100.1 ip_proto udp dst_port 52768 \
+               action pass
+
+       tc filter add dev $swp2 egress protocol ip pref 2 handle 102 flower \
+               skip_sw dst_ip 198.51.100.1 ip_proto udp dst_port 52768 \
+               action pass
+
+       ip vrf exec v$h1 $MZ $h1 -c 10 -d 100msec -p 64 -A 192.0.2.1 \
+               -B 198.51.100.1 -t udp dp=52768,sp=42768 -q
+
+       RET=0
+
+       tc_check_packets "dev $swp1 ingress" 101 10
+       check_err $?
+
+       log_test "fwd mark: trapping IPv4 packets due to LBERROR"
+
+       RET=0
+
+       tc_check_packets "dev $swp2 egress" 101 0
+       check_err $?
+
+       log_test "fwd mark: forwarding IPv4 packets in software"
+
+       RET=0
+
+       tc_check_packets "dev $swp2 egress" 102 10
+       check_err $?
+
+       log_test "fwd mark: forwarding IPv4 packets in hardware"
+
+       tc filter del dev $swp2 egress protocol ip pref 2 handle 102 flower
+       tc filter del dev $swp2 egress protocol ip pref 1 handle 101 flower
+       tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower
+}
+
+fwd_mark_ipv6()
+{
+       tc filter add dev $swp1 ingress protocol ipv6 pref 1 handle 101 flower \
+               skip_hw dst_ip 2001:db8:2::1 ip_proto udp dst_port 52768 \
+               action pass
+
+       tc filter add dev $swp2 egress protocol ipv6 pref 1 handle 101 flower \
+               skip_hw dst_ip 2001:db8:2::1 ip_proto udp dst_port 52768 \
+               action pass
+
+       tc filter add dev $swp2 egress protocol ipv6 pref 2 handle 102 flower \
+               skip_sw dst_ip 2001:db8:2::1 ip_proto udp dst_port 52768 \
+               action pass
+
+       ip vrf exec v$h1 $MZ $h1 -6 -c 10 -d 100msec -p 64 -A 2001:db8:1::1 \
+               -B 2001:db8:2::1 -t udp dp=52768,sp=42768 -q
+
+       RET=0
+
+       tc_check_packets "dev $swp1 ingress" 101 10
+       check_err $?
+
+       log_test "fwd mark: trapping IPv6 packets due to LBERROR"
+
+       RET=0
+
+       tc_check_packets "dev $swp2 egress" 101 0
+       check_err $?
+
+       log_test "fwd mark: forwarding IPv6 packets in software"
+
+       RET=0
+
+       tc_check_packets "dev $swp2 egress" 102 10
+       check_err $?
+
+       log_test "fwd mark: forwarding IPv6 packets in hardware"
+
+       tc filter del dev $swp2 egress protocol ipv6 pref 2 handle 102 flower
+       tc filter del dev $swp2 egress protocol ipv6 pref 1 handle 101 flower
+       tc filter del dev $swp1 ingress protocol ipv6 pref 1 handle 101 flower
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       swp1=${NETIFS[p2]}
+
+       swp2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       vrf_prepare
+       forwarding_enable
+
+       sysctl_set net.ipv4.conf.all.accept_redirects 0
+       sysctl_set net.ipv6.conf.all.accept_redirects 0
+
+       h1_create
+       h2_create
+       switch_create
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       switch_destroy
+       h2_destroy
+       h1_destroy
+
+       sysctl_restore net.ipv6.conf.all.accept_redirects
+       sysctl_restore net.ipv4.conf.all.accept_redirects
+
+       forwarding_restore
+       vrf_cleanup
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh b/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh
new file mode 100755 (executable)
index 0000000..94fdbf2
--- /dev/null
@@ -0,0 +1,565 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test various interface configuration scenarios. Observe that configurations
+# deemed valid by mlxsw succeed, invalid configurations fail and that no traces
+# are produced. To prevent the test from passing in case traces are produced,
+# the user can set the 'kernel.panic_on_warn' and 'kernel.panic_on_oops'
+# sysctls in its environment.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="
+       rif_set_addr_test
+       rif_inherit_bridge_addr_test
+       rif_non_inherit_bridge_addr_test
+       vlan_interface_deletion_test
+       bridge_deletion_test
+       bridge_vlan_flags_test
+       vlan_1_test
+       lag_bridge_upper_test
+       duplicate_vlans_test
+       vlan_rif_refcount_test
+       subport_rif_refcount_test
+       vlan_dev_deletion_test
+       lag_unlink_slaves_test
+       lag_dev_deletion_test
+       vlan_interface_uppers_test
+       devlink_reload_test
+"
+NUM_NETIFS=2
+source $lib_dir/lib.sh
+source $lib_dir/devlink_lib.sh
+
+setup_prepare()
+{
+       swp1=${NETIFS[p1]}
+       swp2=${NETIFS[p2]}
+
+       ip link set dev $swp1 up
+       ip link set dev $swp2 up
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       ip link set dev $swp2 down
+       ip link set dev $swp1 down
+}
+
+rif_set_addr_test()
+{
+       local swp1_mac=$(mac_get $swp1)
+       local swp2_mac=$(mac_get $swp2)
+
+       RET=0
+
+       # $swp1 and $swp2 likely got their IPv6 local addresses already, but
+       # here we need to test the transition to RIF.
+       ip addr flush dev $swp1
+       ip addr flush dev $swp2
+       sleep .1
+
+       ip addr add dev $swp1 192.0.2.1/28
+       check_err $?
+
+       ip link set dev $swp1 addr 00:11:22:33:44:55
+       check_err $?
+
+       # IP address enablement should be rejected if the MAC address prefix
+       # doesn't match other RIFs.
+       ip addr add dev $swp2 192.0.2.2/28 &>/dev/null
+       check_fail $? "IP address addition passed for a device with a wrong MAC"
+       ip addr add dev $swp2 192.0.2.2/28 2>&1 >/dev/null \
+           | grep -q mlxsw_spectrum
+       check_err $? "no extack for IP address addition"
+
+       ip link set dev $swp2 addr 00:11:22:33:44:66
+       check_err $?
+       ip addr add dev $swp2 192.0.2.2/28 &>/dev/null
+       check_err $?
+
+       # Change of MAC address of a RIF should be forbidden if the new MAC
+       # doesn't share the prefix with other MAC addresses.
+       ip link set dev $swp2 addr 00:11:22:33:00:66 &>/dev/null
+       check_fail $? "change of MAC address passed for a wrong MAC"
+       ip link set dev $swp2 addr 00:11:22:33:00:66 2>&1 >/dev/null \
+           | grep -q mlxsw_spectrum
+       check_err $? "no extack for MAC address change"
+
+       log_test "RIF - bad MAC change"
+
+       ip addr del dev $swp2 192.0.2.2/28
+       ip addr del dev $swp1 192.0.2.1/28
+
+       ip link set dev $swp2 addr $swp2_mac
+       ip link set dev $swp1 addr $swp1_mac
+}
+
+rif_inherit_bridge_addr_test()
+{
+       RET=0
+
+       # Create first RIF
+       ip addr add dev $swp1 192.0.2.1/28
+       check_err $?
+
+       # Create a FID RIF
+       ip link add name br1 up type bridge vlan_filtering 0
+       ip link set dev $swp2 master br1
+       ip addr add dev br1 192.0.2.17/28
+       check_err $?
+
+       # Prepare a device with a low MAC address
+       ip link add name d up type dummy
+       ip link set dev d addr 00:11:22:33:44:55
+
+       # Attach the device to br1. That prompts bridge address change, which
+       # should be vetoed, thus preventing the attachment.
+       ip link set dev d master br1 &>/dev/null
+       check_fail $? "Device with low MAC was permitted to attach a bridge with RIF"
+       ip link set dev d master br1 2>&1 >/dev/null \
+           | grep -q mlxsw_spectrum
+       check_err $? "no extack for bridge attach rejection"
+
+       ip link set dev $swp2 addr 00:11:22:33:44:55 &>/dev/null
+       check_fail $? "Changing swp2's MAC address permitted"
+       ip link set dev $swp2 addr 00:11:22:33:44:55 2>&1 >/dev/null \
+           | grep -q mlxsw_spectrum
+       check_err $? "no extack for bridge port MAC address change rejection"
+
+       log_test "RIF - attach port with bad MAC to bridge"
+
+       ip link del dev d
+       ip link del dev br1
+       ip addr del dev $swp1 192.0.2.1/28
+}
+
+rif_non_inherit_bridge_addr_test()
+{
+       local swp2_mac=$(mac_get $swp2)
+
+       RET=0
+
+       # Create first RIF
+       ip addr add dev $swp1 192.0.2.1/28
+       check_err $?
+
+       # Create a FID RIF
+       ip link add name br1 up type bridge vlan_filtering 0
+       ip link set dev br1 addr $swp2_mac
+       ip link set dev $swp2 master br1
+       ip addr add dev br1 192.0.2.17/28
+       check_err $?
+
+       # Prepare a device with a low MAC address
+       ip link add name d up type dummy
+       ip link set dev d addr 00:11:22:33:44:55
+
+       # Attach the device to br1. Since the bridge address was set, it should
+       # work.
+       ip link set dev d master br1 &>/dev/null
+       check_err $? "Could not attach a device with low MAC to a bridge with RIF"
+
+       # Port MAC address change should be allowed for a bridge with set MAC.
+       ip link set dev $swp2 addr 00:11:22:33:44:55
+       check_err $? "Changing swp2's MAC address not permitted"
+
+       log_test "RIF - attach port with bad MAC to bridge with set MAC"
+
+       ip link set dev $swp2 addr $swp2_mac
+       ip link del dev d
+       ip link del dev br1
+       ip addr del dev $swp1 192.0.2.1/28
+}
+
+vlan_interface_deletion_test()
+{
+       # Test that when a VLAN interface is deleted, its associated router
+       # interface (RIF) is correctly deleted and not leaked. See commit
+       # c360867ec46a ("mlxsw: spectrum: Delete RIF when VLAN device is
+       # removed") for more details
+       RET=0
+
+       ip link add name br0 type bridge vlan_filtering 1
+       ip link set dev $swp1 master br0
+
+       ip link add link br0 name br0.10 type vlan id 10
+       ip -6 address add 2001:db8:1::1/64 dev br0.10
+       ip link del dev br0.10
+
+       # If we leaked the previous RIF, then this should produce a trace
+       ip link add link br0 name br0.20 type vlan id 20
+       ip -6 address add 2001:db8:1::1/64 dev br0.20
+       ip link del dev br0.20
+
+       log_test "vlan interface deletion"
+
+       ip link del dev br0
+}
+
+bridge_deletion_test()
+{
+       # Test that when a bridge with VLAN interfaces is deleted, we correctly
+       # delete the associated RIFs. See commit 602b74eda813 ("mlxsw:
+       # spectrum_switchdev: Do not leak RIFs when removing bridge") for more
+       # details
+       RET=0
+
+       ip link add name br0 type bridge vlan_filtering 1
+       ip link set dev $swp1 master br0
+       ip -6 address add 2001:db8::1/64 dev br0
+
+       ip link add link br0 name br0.10 type vlan id 10
+       ip -6 address add 2001:db8:1::1/64 dev br0.10
+
+       ip link add link br0 name br0.20 type vlan id 20
+       ip -6 address add 2001:db8:2::1/64 dev br0.20
+
+       ip link del dev br0
+
+       # If we leaked previous RIFs, then this should produce a trace
+       ip -6 address add 2001:db8:1::1/64 dev $swp1
+       ip -6 address del 2001:db8:1::1/64 dev $swp1
+
+       log_test "bridge deletion"
+}
+
+bridge_vlan_flags_test()
+{
+       # Test that when bridge VLAN flags are toggled, we do not take
+       # unnecessary references on related structs. See commit 9e25826ffc94
+       # ("mlxsw: spectrum_switchdev: Fix port_vlan refcounting") for more
+       # details
+       RET=0
+
+       ip link add name br0 type bridge vlan_filtering 1
+       ip link set dev $swp1 master br0
+
+       bridge vlan add vid 10 dev $swp1 pvid untagged
+       bridge vlan add vid 10 dev $swp1 untagged
+       bridge vlan add vid 10 dev $swp1 pvid
+       bridge vlan add vid 10 dev $swp1
+       ip link del dev br0
+
+       # If we did not handle references correctly, then this should produce a
+       # trace
+       devlink dev reload "$DEVLINK_DEV"
+
+       # Allow netdevices to be re-created following the reload
+       sleep 20
+
+       log_test "bridge vlan flags"
+}
+
+vlan_1_test()
+{
+       # Test that VLAN 1 can be configured over mlxsw ports. In the past it
+       # was used internally for untagged traffic. See commit 47bf9df2e820
+       # ("mlxsw: spectrum: Forbid creation of VLAN 1 over port/LAG") for more
+       # details
+       RET=0
+
+       ip link add link $swp1 name $swp1.1 type vlan id 1
+       check_err $? "did not manage to create vlan 1 when should"
+
+       log_test "vlan 1"
+
+       ip link del dev $swp1.1
+}
+
+lag_bridge_upper_test()
+{
+       # Test that ports cannot be enslaved to LAG devices that have uppers
+       # and that failure is handled gracefully. See commit b3529af6bb0d
+       # ("spectrum: Reference count VLAN entries") for more details
+       RET=0
+
+       ip link add name bond1 type bond mode 802.3ad
+
+       ip link add name br0 type bridge vlan_filtering 1
+       ip link set dev bond1 master br0
+
+       ip link set dev $swp1 down
+       ip link set dev $swp1 master bond1 &> /dev/null
+       check_fail $? "managed to enslave port to lag when should not"
+
+       # This might generate a trace, if we did not handle the failure
+       # correctly
+       ip -6 address add 2001:db8:1::1/64 dev $swp1
+       ip -6 address del 2001:db8:1::1/64 dev $swp1
+
+       log_test "lag with bridge upper"
+
+       ip link del dev br0
+       ip link del dev bond1
+}
+
+duplicate_vlans_test()
+{
+       # Test that on a given port a VLAN is only used once. Either as VLAN
+       # in a VLAN-aware bridge or as a VLAN device
+       RET=0
+
+       ip link add name br0 type bridge vlan_filtering 1
+       ip link set dev $swp1 master br0
+       bridge vlan add vid 10 dev $swp1
+
+       ip link add link $swp1 name $swp1.10 type vlan id 10 &> /dev/null
+       check_fail $? "managed to create vlan device when should not"
+
+       bridge vlan del vid 10 dev $swp1
+       ip link add link $swp1 name $swp1.10 type vlan id 10
+       check_err $? "did not manage to create vlan device when should"
+       bridge vlan add vid 10 dev $swp1 &> /dev/null
+       check_fail $? "managed to add bridge vlan when should not"
+
+       log_test "duplicate vlans"
+
+       ip link del dev $swp1.10
+       ip link del dev br0
+}
+
+vlan_rif_refcount_test()
+{
+       # Test that RIFs representing VLAN interfaces are not affected from
+       # ports member in the VLAN. We use the offload indication on routes
+       # configured on the RIF to understand if it was created / destroyed
+       RET=0
+
+       ip link add name br0 type bridge vlan_filtering 1
+       ip link set dev $swp1 master br0
+
+       ip link set dev $swp1 up
+       ip link set dev br0 up
+
+       ip link add link br0 name br0.10 up type vlan id 10
+       ip -6 address add 2001:db8:1::1/64 dev br0.10
+
+       ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
+       check_err $? "vlan rif was not created before adding port to vlan"
+
+       bridge vlan add vid 10 dev $swp1
+       ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
+       check_err $? "vlan rif was destroyed after adding port to vlan"
+
+       bridge vlan del vid 10 dev $swp1
+       ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
+       check_err $? "vlan rif was destroyed after removing port from vlan"
+
+       ip link set dev $swp1 nomaster
+       ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
+       check_fail $? "vlan rif was not destroyed after unlinking port from bridge"
+
+       log_test "vlan rif refcount"
+
+       ip link del dev br0.10
+       ip link set dev $swp1 down
+       ip link del dev br0
+}
+
+subport_rif_refcount_test()
+{
+       # Test that RIFs representing upper devices of physical ports are
+       # reference counted correctly and destroyed when should. We use the
+       # offload indication on routes configured on the RIF to understand if
+       # it was created / destroyed
+       RET=0
+
+       ip link add name bond1 type bond mode 802.3ad
+       ip link set dev $swp1 down
+       ip link set dev $swp2 down
+       ip link set dev $swp1 master bond1
+       ip link set dev $swp2 master bond1
+
+       ip link set dev bond1 up
+       ip link add link bond1 name bond1.10 up type vlan id 10
+       ip -6 address add 2001:db8:1::1/64 dev bond1
+       ip -6 address add 2001:db8:2::1/64 dev bond1.10
+
+       ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
+       check_err $? "subport rif was not created on lag device"
+       ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
+       check_err $? "subport rif was not created on vlan device"
+
+       ip link set dev $swp1 nomaster
+       ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
+       check_err $? "subport rif of lag device was destroyed when should not"
+       ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
+       check_err $? "subport rif of vlan device was destroyed when should not"
+
+       ip link set dev $swp2 nomaster
+       ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
+       check_fail $? "subport rif of lag device was not destroyed when should"
+       ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
+       check_fail $? "subport rif of vlan device was not destroyed when should"
+
+       log_test "subport rif refcount"
+
+       ip link del dev bond1.10
+       ip link del dev bond1
+}
+
+vlan_dev_deletion_test()
+{
+       # Test that VLAN devices are correctly deleted / unlinked when enslaved
+       # to bridge
+       RET=0
+
+       ip link add name br10 type bridge
+       ip link add name br20 type bridge
+       ip link add name br30 type bridge
+       ip link add link $swp1 name $swp1.10 type vlan id 10
+       ip link add link $swp1 name $swp1.20 type vlan id 20
+       ip link add link $swp1 name $swp1.30 type vlan id 30
+       ip link set dev $swp1.10 master br10
+       ip link set dev $swp1.20 master br20
+       ip link set dev $swp1.30 master br30
+
+       # If we did not handle the situation correctly, then these operations
+       # might produce a trace
+       ip link set dev $swp1.30 nomaster
+       ip link del dev $swp1.20
+       # Deletion via ioctl uses different code paths from netlink
+       vconfig rem $swp1.10 &> /dev/null
+
+       log_test "vlan device deletion"
+
+       ip link del dev $swp1.30
+       ip link del dev br30
+       ip link del dev br20
+       ip link del dev br10
+}
+
+lag_create()
+{
+       ip link add name bond1 type bond mode 802.3ad
+       ip link set dev $swp1 down
+       ip link set dev $swp2 down
+       ip link set dev $swp1 master bond1
+       ip link set dev $swp2 master bond1
+
+       ip link add link bond1 name bond1.10 type vlan id 10
+       ip link add link bond1 name bond1.20 type vlan id 20
+
+       ip link add name br0 type bridge vlan_filtering 1
+       ip link set dev bond1 master br0
+
+       ip link add name br10 type bridge
+       ip link set dev bond1.10 master br10
+
+       ip link add name br20 type bridge
+       ip link set dev bond1.20 master br20
+}
+
+lag_unlink_slaves_test()
+{
+       # Test that ports are correctly unlinked from their LAG master, when
+       # the LAG and its VLAN uppers are enslaved to bridges
+       RET=0
+
+       lag_create
+
+       ip link set dev $swp1 nomaster
+       check_err $? "lag slave $swp1 was not unlinked from master"
+       ip link set dev $swp2 nomaster
+       check_err $? "lag slave $swp2 was not unlinked from master"
+
+       # Try to configure corresponding VLANs as router interfaces
+       ip -6 address add 2001:db8:1::1/64 dev $swp1
+       check_err $? "failed to configure ip address on $swp1"
+
+       ip link add link $swp1 name $swp1.10 type vlan id 10
+       ip -6 address add 2001:db8:10::1/64 dev $swp1.10
+       check_err $? "failed to configure ip address on $swp1.10"
+
+       ip link add link $swp1 name $swp1.20 type vlan id 20
+       ip -6 address add 2001:db8:20::1/64 dev $swp1.20
+       check_err $? "failed to configure ip address on $swp1.20"
+
+       log_test "lag slaves unlinking"
+
+       ip link del dev $swp1.20
+       ip link del dev $swp1.10
+       ip address flush dev $swp1
+
+       ip link del dev br20
+       ip link del dev br10
+       ip link del dev br0
+       ip link del dev bond1
+}
+
+lag_dev_deletion_test()
+{
+       # Test that LAG device is correctly deleted, when the LAG and its VLAN
+       # uppers are enslaved to bridges
+       RET=0
+
+       lag_create
+
+       ip link del dev bond1
+
+       log_test "lag device deletion"
+
+       ip link del dev br20
+       ip link del dev br10
+       ip link del dev br0
+}
+
+vlan_interface_uppers_test()
+{
+       # Test that uppers of a VLAN interface are correctly sanitized
+       RET=0
+
+       ip link add name br0 type bridge vlan_filtering 1
+       ip link set dev $swp1 master br0
+
+       ip link add link br0 name br0.10 type vlan id 10
+       ip link add link br0.10 name macvlan0 \
+               type macvlan mode private &> /dev/null
+       check_fail $? "managed to create a macvlan when should not"
+
+       ip -6 address add 2001:db8:1::1/64 dev br0.10
+       ip link add link br0.10 name macvlan0 type macvlan mode private
+       check_err $? "did not manage to create a macvlan when should"
+
+       ip link del dev macvlan0
+
+       ip link add name vrf-test type vrf table 10
+       ip link set dev br0.10 master vrf-test
+       check_err $? "did not manage to enslave vlan interface to vrf"
+       ip link del dev vrf-test
+
+       ip link add name br-test type bridge
+       ip link set dev br0.10 master br-test &> /dev/null
+       check_fail $? "managed to enslave vlan interface to bridge when should not"
+       ip link del dev br-test
+
+       log_test "vlan interface uppers"
+
+       ip link del dev br0
+}
+
+devlink_reload_test()
+{
+       # Test that after executing all the above configuration tests, a
+       # devlink reload can be performed without errors
+       RET=0
+
+       devlink dev reload "$DEVLINK_DEV"
+       check_err $? "devlink reload failed"
+
+       log_test "devlink reload - last test"
+
+       sleep 20
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
index 3b75180..b41d625 100755 (executable)
@@ -8,7 +8,8 @@
 lib_dir=$(dirname $0)/../../../../net/forwarding
 
 ALL_TESTS="single_mask_test identical_filters_test two_masks_test \
-       multiple_masks_test ctcam_edge_cases_test"
+       multiple_masks_test ctcam_edge_cases_test delta_simple_test \
+       bloom_simple_test bloom_complex_test bloom_delta_test"
 NUM_NETIFS=2
 source $lib_dir/tc_common.sh
 source $lib_dir/lib.sh
@@ -142,7 +143,7 @@ two_masks_test()
        tc filter add dev $h2 ingress protocol ip pref 1 handle 101 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.0.0/16 action drop
+               $tcflags dst_ip 192.0.0.0/8 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
@@ -235,7 +236,7 @@ ctcam_two_atcam_masks_test()
                $tcflags dst_ip 192.0.2.2 action drop
        # Filter goes into A-TCAM
        tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
-               $tcflags dst_ip 192.0.2.0/24 action drop
+               $tcflags dst_ip 192.0.0.0/16 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
@@ -324,6 +325,258 @@ 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
+       # the first eRP with delta. Remove the first rule then and check that
+        # the eRP stays (referenced by the second filter).
+
+       RET=0
+
+       if [[ "$tcflags" != "skip_sw" ]]; then
+               return 0;
+       fi
+
+       tp_record "objagg:*" "tc filter add dev $h2 ingress protocol ip \
+                  pref 1 handle 101 flower $tcflags dst_ip 192.0.0.0/24 \
+                  action drop"
+       tp_check_hits "objagg:objagg_obj_root_create" 1
+       check_err $? "eRP was not created"
+
+       tp_record "objagg:*" "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 "objagg:objagg_obj_root_create" 0
+       check_err $? "eRP was incorrectly created"
+       tp_check_hits "objagg:objagg_obj_parent_assign" 1
+       check_err $? "delta was not created"
+
+       $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" 102 1
+       check_err $? "Did not match on correct filter"
+
+       tp_record "objagg:*" "tc filter del dev $h2 ingress protocol ip \
+                  pref 1 handle 101 flower"
+       tp_check_hits "objagg:objagg_obj_root_destroy" 0
+       check_err $? "eRP was incorrectly destroyed"
+       tp_check_hits "objagg:objagg_obj_parent_unassign" 0
+       check_err $? "delta was incorrectly destroyed"
+
+       $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 2
+       check_err $? "Did not match on correct filter after the first was removed"
+
+       tp_record "objagg:*" "tc filter del dev $h2 ingress protocol ip \
+                  pref 2 handle 102 flower"
+       tp_check_hits "objagg:objagg_obj_parent_unassign" 1
+       check_err $? "delta was not destroyed"
+       tp_check_hits "objagg:objagg_obj_root_destroy" 1
+       check_err $? "eRP was not destroyed"
+
+       log_test "delta simple test ($tcflags)"
+}
+
+bloom_simple_test()
+{
+       # Bloom filter requires that the eRP table is used. This test
+       # verifies that Bloom filter is not harming correctness of ACLs.
+       # First, make sure that eRP table is used and then set rule patterns
+       # which are distant enough and will result skipping a lookup after
+       # consulting the Bloom filter. Although some eRP lookups are skipped,
+       # the correct filter should be hit.
+
+       RET=0
+
+       tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
+               $tcflags dst_ip 192.0.2.2 action drop
+       tc filter add dev $h2 ingress protocol ip pref 5 handle 104 flower \
+               $tcflags dst_ip 198.51.100.2 action drop
+       tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
+               $tcflags dst_ip 192.0.0.0/8 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_err $? "Two filters - did not match highest priority"
+
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \
+               -t ip -q
+
+       tc_check_packets "dev $h2 ingress" 104 1
+       check_err $? "Single filter - did not match"
+
+       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" 103 1
+       check_err $? "Low prio filter - did not match"
+
+       tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
+               $tcflags dst_ip 198.0.0.0/8 action drop
+
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \
+               -t ip -q
+
+       tc_check_packets "dev $h2 ingress" 102 1
+       check_err $? "Two filters - did not match highest priority after add"
+
+       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 5 handle 104 flower
+
+       log_test "bloom simple test ($tcflags)"
+}
+
+bloom_complex_test()
+{
+       # Bloom filter index computation is affected from region ID, eRP
+       # ID and from the region key size. In order to excercise those parts
+       # of the Bloom filter code, use a series of regions, each with a
+       # different key size and send packet that should hit all of them.
+       local index
+
+       RET=0
+       NUM_CHAINS=4
+       BASE_INDEX=100
+
+       # Create chain with up to 2 key blocks (ip_proto only)
+       tc chain add dev $h2 ingress chain 1 protocol ip flower \
+               ip_proto tcp &> /dev/null
+       # Create chain with 2-4 key blocks (ip_proto, src MAC)
+       tc chain add dev $h2 ingress chain 2 protocol ip flower \
+               ip_proto tcp \
+               src_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF &> /dev/null
+       # Create chain with 4-8 key blocks (ip_proto, src & dst MAC, IPv4 dest)
+       tc chain add dev $h2 ingress chain 3 protocol ip flower \
+               ip_proto tcp \
+               dst_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF \
+               src_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF \
+               dst_ip 0.0.0.0/32 &> /dev/null
+       # Default chain contains all fields and therefore is 8-12 key blocks
+       tc chain add dev $h2 ingress chain 4
+
+       # We need at least 2 rules in every region to have eRP table active
+       # so create a dummy rule per chain using a different pattern
+       for i in $(eval echo {0..$NUM_CHAINS}); do
+               index=$((BASE_INDEX - 1 - i))
+               tc filter add dev $h2 ingress chain $i protocol ip \
+                       pref 2 handle $index flower \
+                       $tcflags ip_proto tcp action drop
+       done
+
+       # Add rules to test Bloom filter, each in a different chain
+       index=$BASE_INDEX
+       tc filter add dev $h2 ingress protocol ip \
+               pref 1 handle $((++index)) flower \
+               $tcflags dst_ip 192.0.0.0/16 action goto chain 1
+       tc filter add dev $h2 ingress chain 1 protocol ip \
+               pref 1 handle $((++index)) flower \
+               $tcflags action goto chain 2
+       tc filter add dev $h2 ingress chain 2 protocol ip \
+               pref 1 handle $((++index)) flower \
+               $tcflags src_mac $h1mac action goto chain 3
+       tc filter add dev $h2 ingress chain 3 protocol ip \
+               pref 1 handle $((++index)) flower \
+               $tcflags dst_ip 192.0.0.0/8 action goto chain 4
+       tc filter add dev $h2 ingress chain 4 protocol ip \
+               pref 1 handle $((++index)) flower \
+               $tcflags src_ip 192.0.2.0/24 action drop
+
+       # Send a packet that is supposed to hit all chains
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+               -t ip -q
+
+       for i in $(eval echo {0..$NUM_CHAINS}); do
+               index=$((BASE_INDEX + i + 1))
+               tc_check_packets "dev $h2 ingress" $index 1
+               check_err $? "Did not match chain $i"
+       done
+
+       # Rules cleanup
+       for i in $(eval echo {$NUM_CHAINS..0}); do
+               index=$((BASE_INDEX - i - 1))
+               tc filter del dev $h2 ingress chain $i \
+                       pref 2 handle $index flower
+               index=$((BASE_INDEX + i + 1))
+               tc filter del dev $h2 ingress chain $i \
+                       pref 1 handle $index flower
+       done
+
+       # Chains cleanup
+       for i in $(eval echo {$NUM_CHAINS..1}); do
+               tc chain del dev $h2 ingress chain $i
+       done
+
+       log_test "bloom complex test ($tcflags)"
+}
+
+
+bloom_delta_test()
+{
+       # When multiple masks are used, the eRP table is activated. When
+       # masks are close enough (delta) the masks reside on the same
+       # eRP table. This test verifies that the eRP table is correctly
+       # allocated and used in delta condition and that Bloom filter is
+       # still functional with delta.
+
+       RET=0
+
+       tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
+               $tcflags dst_ip 192.1.0.0/16 action drop
+
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.1.2.1 -B 192.1.2.2 \
+               -t ip -q
+
+       tc_check_packets "dev $h2 ingress" 103 1
+       check_err $? "Single filter - did not match"
+
+       tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
+               $tcflags dst_ip 192.2.1.0/24 action drop
+
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.2.1.1 -B 192.2.1.2 \
+               -t ip -q
+
+       tc_check_packets "dev $h2 ingress" 102 1
+       check_err $? "Delta filters - did not match second filter"
+
+       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
+
+       log_test "bloom delta test ($tcflags)"
+}
+
 setup_prepare()
 {
        h1=${NETIFS[p1]}
diff --git a/tools/testing/selftests/drivers/net/mlxsw/vxlan.sh b/tools/testing/selftests/drivers/net/mlxsw/vxlan.sh
new file mode 100755 (executable)
index 0000000..dcf9f4e
--- /dev/null
@@ -0,0 +1,1103 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test various aspects of VxLAN offloading which are specific to mlxsw, such
+# as sanitization of invalid configurations and offload indication.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="sanitization_test offload_indication_test \
+       sanitization_vlan_aware_test offload_indication_vlan_aware_test"
+NUM_NETIFS=2
+source $lib_dir/lib.sh
+
+setup_prepare()
+{
+       swp1=${NETIFS[p1]}
+       swp2=${NETIFS[p2]}
+
+       ip link set dev $swp1 up
+       ip link set dev $swp2 up
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       ip link set dev $swp2 down
+       ip link set dev $swp1 down
+}
+
+sanitization_single_dev_test_pass()
+{
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev vxlan0 master br0
+       check_err $?
+
+       ip link set dev $swp1 nomaster
+
+       ip link set dev $swp1 master br0
+       check_err $?
+}
+
+sanitization_single_dev_test_fail()
+{
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev vxlan0 master br0 &> /dev/null
+       check_fail $?
+
+       ip link set dev $swp1 nomaster
+
+       ip link set dev vxlan0 master br0
+       check_err $?
+       ip link set dev $swp1 master br0 &> /dev/null
+       check_fail $?
+}
+
+sanitization_single_dev_valid_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_pass
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device - valid configuration"
+}
+
+sanitization_single_dev_vlan_aware_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0 vlan_filtering 1
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_pass
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with a vlan-aware bridge"
+}
+
+sanitization_single_dev_mcast_enabled_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with a multicast enabled bridge"
+}
+
+sanitization_single_dev_mcast_group_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789 \
+               dev $swp2 group 239.0.0.1
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with a multicast group"
+}
+
+sanitization_single_dev_no_local_ip_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with no local ip"
+}
+
+sanitization_single_dev_local_ipv6_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 2001:db8::1 dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with local ipv6 address"
+}
+
+sanitization_single_dev_learning_enabled_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 learning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_pass
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with learning enabled"
+}
+
+sanitization_single_dev_local_interface_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789 dev $swp2
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with local interface"
+}
+
+sanitization_single_dev_port_range_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789 \
+               srcport 4000 5000
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with udp source port range"
+}
+
+sanitization_single_dev_tos_static_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos 20 local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with static tos"
+}
+
+sanitization_single_dev_ttl_inherit_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl inherit tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with inherit ttl"
+}
+
+sanitization_single_dev_udp_checksum_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning udpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with udp checksum"
+}
+
+sanitization_single_dev_test()
+{
+       # These tests make sure that we correctly sanitize VxLAN device
+       # configurations we do not support
+       sanitization_single_dev_valid_test
+       sanitization_single_dev_vlan_aware_test
+       sanitization_single_dev_mcast_enabled_test
+       sanitization_single_dev_mcast_group_test
+       sanitization_single_dev_no_local_ip_test
+       sanitization_single_dev_local_ipv6_test
+       sanitization_single_dev_learning_enabled_test
+       sanitization_single_dev_local_interface_test
+       sanitization_single_dev_port_range_test
+       sanitization_single_dev_tos_static_test
+       sanitization_single_dev_ttl_inherit_test
+       sanitization_single_dev_udp_checksum_test
+}
+
+sanitization_multi_devs_test_pass()
+{
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev vxlan0 master br0
+       check_err $?
+       ip link set dev $swp2 master br1
+       check_err $?
+       ip link set dev vxlan1 master br1
+       check_err $?
+
+       ip link set dev $swp2 nomaster
+       ip link set dev $swp1 nomaster
+
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev $swp2 master br1
+       check_err $?
+}
+
+sanitization_multi_devs_test_fail()
+{
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev vxlan0 master br0
+       check_err $?
+       ip link set dev $swp2 master br1
+       check_err $?
+       ip link set dev vxlan1 master br1 &> /dev/null
+       check_fail $?
+
+       ip link set dev $swp2 nomaster
+       ip link set dev $swp1 nomaster
+
+       ip link set dev vxlan1 master br1
+       check_err $?
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev $swp2 master br1 &> /dev/null
+       check_fail $?
+}
+
+sanitization_multi_devs_valid_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+       ip link add dev br1 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up type vxlan id 20 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_multi_devs_test_pass
+
+       ip link del dev vxlan1
+       ip link del dev vxlan0
+       ip link del dev br1
+       ip link del dev br0
+
+       log_test "multiple vxlan devices - valid configuration"
+}
+
+sanitization_multi_devs_ttl_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+       ip link add dev br1 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up type vxlan id 20 nolearning noudpcsum \
+               ttl 40 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_multi_devs_test_fail
+
+       ip link del dev vxlan1
+       ip link del dev vxlan0
+       ip link del dev br1
+       ip link del dev br0
+
+       log_test "multiple vxlan devices with different ttl"
+}
+
+sanitization_multi_devs_udp_dstport_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+       ip link add dev br1 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up type vxlan id 20 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 5789
+
+       sanitization_multi_devs_test_fail
+
+       ip link del dev vxlan1
+       ip link del dev vxlan0
+       ip link del dev br1
+       ip link del dev br0
+
+       log_test "multiple vxlan devices with different udp destination port"
+}
+
+sanitization_multi_devs_local_ip_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+       ip link add dev br1 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up type vxlan id 20 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.2 dstport 4789
+
+       sanitization_multi_devs_test_fail
+
+       ip link del dev vxlan1
+       ip link del dev vxlan0
+       ip link del dev br1
+       ip link del dev br0
+
+       log_test "multiple vxlan devices with different local ip"
+}
+
+sanitization_multi_devs_test()
+{
+       # The device has a single VTEP, which means all the VxLAN devices
+       # we offload must share certain properties such as source IP and
+       # UDP destination port. These tests make sure that we forbid
+       # configurations that violate this limitation
+       sanitization_multi_devs_valid_test
+       sanitization_multi_devs_ttl_test
+       sanitization_multi_devs_udp_dstport_test
+       sanitization_multi_devs_local_ip_test
+}
+
+sanitization_test()
+{
+       sanitization_single_dev_test
+       sanitization_multi_devs_test
+}
+
+offload_indication_setup_create()
+{
+       # Create a simple setup with two bridges, each with a VxLAN device
+       # and one local port
+       ip link add name br0 up type bridge mcast_snooping 0
+       ip link add name br1 up type bridge mcast_snooping 0
+
+       ip link set dev $swp1 master br0
+       ip link set dev $swp2 master br1
+
+       ip address add 198.51.100.1/32 dev lo
+
+       ip link add name vxlan0 up master br0 type vxlan id 10 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up master br1 type vxlan id 20 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+}
+
+offload_indication_setup_destroy()
+{
+       ip link del dev vxlan1
+       ip link del dev vxlan0
+
+       ip address del 198.51.100.1/32 dev lo
+
+       ip link set dev $swp2 nomaster
+       ip link set dev $swp1 nomaster
+
+       ip link del dev br1
+       ip link del dev br0
+}
+
+offload_indication_fdb_flood_test()
+{
+       RET=0
+
+       bridge fdb append 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.2
+
+       bridge fdb show brport vxlan0 | grep 00:00:00:00:00:00 \
+               | grep -q offload
+       check_err $?
+
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self
+
+       log_test "vxlan flood entry offload indication"
+}
+
+offload_indication_fdb_bridge_test()
+{
+       RET=0
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan0 self master static \
+               dst 198.51.100.2
+
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - initial state"
+
+       # Remove FDB entry from the bridge driver and check that corresponding
+       # entry in the VxLAN driver is not marked as offloaded
+       RET=0
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan0 master
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_fail $?
+
+       log_test "vxlan entry offload indication - after removal from bridge"
+
+       # Add the FDB entry back to the bridge driver and make sure it is
+       # marked as offloaded in both drivers
+       RET=0
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan0 master static
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - after re-add to bridge"
+
+       # Remove FDB entry from the VxLAN driver and check that corresponding
+       # entry in the bridge driver is not marked as offloaded
+       RET=0
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan0 self
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_fail $?
+
+       log_test "vxlan entry offload indication - after removal from vxlan"
+
+       # Add the FDB entry back to the VxLAN driver and make sure it is
+       # marked as offloaded in both drivers
+       RET=0
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan0 self dst 198.51.100.2
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - after re-add to vxlan"
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan0 self master
+}
+
+offload_indication_fdb_test()
+{
+       offload_indication_fdb_flood_test
+       offload_indication_fdb_bridge_test
+}
+
+offload_indication_decap_route_test()
+{
+       RET=0
+
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link set dev vxlan0 down
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link set dev vxlan1 down
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       log_test "vxlan decap route - vxlan device down"
+
+       RET=0
+
+       ip link set dev vxlan1 up
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link set dev vxlan0 up
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       log_test "vxlan decap route - vxlan device up"
+
+       RET=0
+
+       ip address delete 198.51.100.1/32 dev lo
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       ip address add 198.51.100.1/32 dev lo
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       log_test "vxlan decap route - add local route"
+
+       RET=0
+
+       ip link set dev $swp1 nomaster
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link set dev $swp2 nomaster
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       ip link set dev $swp1 master br0
+       ip link set dev $swp2 master br1
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       log_test "vxlan decap route - local ports enslavement"
+
+       RET=0
+
+       ip link del dev br0
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link del dev br1
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       log_test "vxlan decap route - bridge device deletion"
+
+       RET=0
+
+       ip link add name br0 up type bridge mcast_snooping 0
+       ip link add name br1 up type bridge mcast_snooping 0
+       ip link set dev $swp1 master br0
+       ip link set dev $swp2 master br1
+       ip link set dev vxlan0 master br0
+       ip link set dev vxlan1 master br1
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link del dev vxlan0
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link del dev vxlan1
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       log_test "vxlan decap route - vxlan device deletion"
+
+       ip link add name vxlan0 up master br0 type vxlan id 10 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up master br1 type vxlan id 20 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+}
+
+check_fdb_offloaded()
+{
+       local mac=00:11:22:33:44:55
+       local zmac=00:00:00:00:00:00
+
+       bridge fdb show dev vxlan0 | grep $mac | grep self | grep -q offload
+       check_err $?
+       bridge fdb show dev vxlan0 | grep $mac | grep master | grep -q offload
+       check_err $?
+
+       bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
+       check_err $?
+}
+
+check_vxlan_fdb_not_offloaded()
+{
+       local mac=00:11:22:33:44:55
+       local zmac=00:00:00:00:00:00
+
+       bridge fdb show dev vxlan0 | grep $mac | grep -q self
+       check_err $?
+       bridge fdb show dev vxlan0 | grep $mac | grep self | grep -q offload
+       check_fail $?
+
+       bridge fdb show dev vxlan0 | grep $zmac | grep -q self
+       check_err $?
+       bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
+       check_fail $?
+}
+
+check_bridge_fdb_not_offloaded()
+{
+       local mac=00:11:22:33:44:55
+       local zmac=00:00:00:00:00:00
+
+       bridge fdb show dev vxlan0 | grep $mac | grep -q master
+       check_err $?
+       bridge fdb show dev vxlan0 | grep $mac | grep master | grep -q offload
+       check_fail $?
+}
+
+__offload_indication_join_vxlan_first()
+{
+       local vid=$1; shift
+
+       local mac=00:11:22:33:44:55
+       local zmac=00:00:00:00:00:00
+
+       bridge fdb append $zmac dev vxlan0 self dst 198.51.100.2
+
+       ip link set dev vxlan0 master br0
+       bridge fdb add dev vxlan0 $mac self master static dst 198.51.100.2
+
+       RET=0
+       check_vxlan_fdb_not_offloaded
+       ip link set dev $swp1 master br0
+       sleep .1
+       check_fdb_offloaded
+       log_test "offload indication - attach vxlan first"
+
+       RET=0
+       ip link set dev vxlan0 down
+       check_vxlan_fdb_not_offloaded
+       check_bridge_fdb_not_offloaded
+       log_test "offload indication - set vxlan down"
+
+       RET=0
+       ip link set dev vxlan0 up
+       sleep .1
+       check_fdb_offloaded
+       log_test "offload indication - set vxlan up"
+
+       if [[ ! -z $vid ]]; then
+               RET=0
+               bridge vlan del dev vxlan0 vid $vid
+               check_vxlan_fdb_not_offloaded
+               check_bridge_fdb_not_offloaded
+               log_test "offload indication - delete VLAN"
+
+               RET=0
+               bridge vlan add dev vxlan0 vid $vid
+               check_vxlan_fdb_not_offloaded
+               check_bridge_fdb_not_offloaded
+               log_test "offload indication - add tagged VLAN"
+
+               RET=0
+               bridge vlan add dev vxlan0 vid $vid pvid untagged
+               sleep .1
+               check_fdb_offloaded
+               log_test "offload indication - add pvid/untagged VLAN"
+       fi
+
+       RET=0
+       ip link set dev $swp1 nomaster
+       check_vxlan_fdb_not_offloaded
+       log_test "offload indication - detach port"
+}
+
+offload_indication_join_vxlan_first()
+{
+       ip link add dev br0 up type bridge mcast_snooping 0
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       __offload_indication_join_vxlan_first
+
+       ip link del dev vxlan0
+       ip link del dev br0
+}
+
+__offload_indication_join_vxlan_last()
+{
+       local zmac=00:00:00:00:00:00
+
+       RET=0
+
+       bridge fdb append $zmac dev vxlan0 self dst 198.51.100.2
+
+       ip link set dev $swp1 master br0
+
+       bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
+       check_fail $?
+
+       ip link set dev vxlan0 master br0
+
+       bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
+       check_err $?
+
+       log_test "offload indication - attach vxlan last"
+}
+
+offload_indication_join_vxlan_last()
+{
+       ip link add dev br0 up type bridge mcast_snooping 0
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       __offload_indication_join_vxlan_last
+
+       ip link del dev vxlan0
+       ip link del dev br0
+}
+
+offload_indication_test()
+{
+       offload_indication_setup_create
+       offload_indication_fdb_test
+       offload_indication_decap_route_test
+       offload_indication_setup_destroy
+
+       log_info "offload indication - replay & cleanup"
+       offload_indication_join_vxlan_first
+       offload_indication_join_vxlan_last
+}
+
+sanitization_vlan_aware_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0 vlan_filtering 1
+
+       ip link add name vxlan10 up master br0 type vxlan id 10 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       ip link add name vxlan20 up master br0 type vxlan id 20 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       # Test that when each VNI is mapped to a different VLAN we can enslave
+       # a port to the bridge
+       bridge vlan add vid 10 dev vxlan10 pvid untagged
+       bridge vlan add vid 20 dev vxlan20 pvid untagged
+
+       ip link set dev $swp1 master br0
+       check_err $?
+
+       log_test "vlan-aware - enslavement to vlan-aware bridge"
+
+       # Try to map both VNIs to the same VLAN and make sure configuration
+       # fails
+       RET=0
+
+       bridge vlan add vid 10 dev vxlan20 pvid untagged &> /dev/null
+       check_fail $?
+
+       log_test "vlan-aware - two vnis mapped to the same vlan"
+
+       # Test that enslavement of a port to a bridge fails when two VNIs
+       # are mapped to the same VLAN
+       RET=0
+
+       ip link set dev $swp1 nomaster
+
+       bridge vlan del vid 20 dev vxlan20 pvid untagged
+       bridge vlan add vid 10 dev vxlan20 pvid untagged
+
+       ip link set dev $swp1 master br0 &> /dev/null
+       check_fail $?
+
+       log_test "vlan-aware - failed enslavement to vlan-aware bridge"
+
+       ip link del dev vxlan20
+       ip link del dev vxlan10
+       ip link del dev br0
+}
+
+offload_indication_vlan_aware_setup_create()
+{
+       # Create a simple setup with two VxLAN devices and a single VLAN-aware
+       # bridge
+       ip link add name br0 up type bridge mcast_snooping 0 vlan_filtering 1 \
+               vlan_default_pvid 0
+
+       ip link set dev $swp1 master br0
+
+       bridge vlan add vid 10 dev $swp1
+       bridge vlan add vid 20 dev $swp1
+
+       ip address add 198.51.100.1/32 dev lo
+
+       ip link add name vxlan10 up master br0 type vxlan id 10 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan20 up master br0 type vxlan id 20 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       bridge vlan add vid 10 dev vxlan10 pvid untagged
+       bridge vlan add vid 20 dev vxlan20 pvid untagged
+}
+
+offload_indication_vlan_aware_setup_destroy()
+{
+       bridge vlan del vid 20 dev vxlan20
+       bridge vlan del vid 10 dev vxlan10
+
+       ip link del dev vxlan20
+       ip link del dev vxlan10
+
+       ip address del 198.51.100.1/32 dev lo
+
+       bridge vlan del vid 20 dev $swp1
+       bridge vlan del vid 10 dev $swp1
+
+       ip link set dev $swp1 nomaster
+
+       ip link del dev br0
+}
+
+offload_indication_vlan_aware_fdb_test()
+{
+       RET=0
+
+       log_info "vxlan entry offload indication - vlan-aware"
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan10 self master static \
+               dst 198.51.100.2 vlan 10
+
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - initial state"
+
+       # Remove FDB entry from the bridge driver and check that corresponding
+       # entry in the VxLAN driver is not marked as offloaded
+       RET=0
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan10 master vlan 10
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_fail $?
+
+       log_test "vxlan entry offload indication - after removal from bridge"
+
+       # Add the FDB entry back to the bridge driver and make sure it is
+       # marked as offloaded in both drivers
+       RET=0
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan10 master static vlan 10
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - after re-add to bridge"
+
+       # Remove FDB entry from the VxLAN driver and check that corresponding
+       # entry in the bridge driver is not marked as offloaded
+       RET=0
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan10 self
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_fail $?
+
+       log_test "vxlan entry offload indication - after removal from vxlan"
+
+       # Add the FDB entry back to the VxLAN driver and make sure it is
+       # marked as offloaded in both drivers
+       RET=0
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan10 self dst 198.51.100.2
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - after re-add to vxlan"
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan10 self master vlan 10
+}
+
+offload_indication_vlan_aware_decap_route_test()
+{
+       RET=0
+
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       # Toggle PVID flag on one VxLAN device and make sure route is still
+       # marked as offloaded
+       bridge vlan add vid 10 dev vxlan10 untagged
+
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       # Toggle PVID flag on second VxLAN device and make sure route is no
+       # longer marked as offloaded
+       bridge vlan add vid 20 dev vxlan20 untagged
+
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       # Toggle PVID flag back and make sure route is marked as offloaded
+       bridge vlan add vid 10 dev vxlan10 pvid untagged
+       bridge vlan add vid 20 dev vxlan20 pvid untagged
+
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       log_test "vxlan decap route - vni map/unmap"
+}
+
+offload_indication_vlan_aware_join_vxlan_first()
+{
+       ip link add dev br0 up type bridge mcast_snooping 0 \
+               vlan_filtering 1 vlan_default_pvid 1
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       __offload_indication_join_vxlan_first 1
+
+       ip link del dev vxlan0
+       ip link del dev br0
+}
+
+offload_indication_vlan_aware_join_vxlan_last()
+{
+       ip link add dev br0 up type bridge mcast_snooping 0 \
+               vlan_filtering 1 vlan_default_pvid 1
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       __offload_indication_join_vxlan_last
+
+       ip link del dev vxlan0
+       ip link del dev br0
+}
+
+offload_indication_vlan_aware_l3vni_test()
+{
+       local zmac=00:00:00:00:00:00
+
+       RET=0
+
+       sysctl_set net.ipv6.conf.default.disable_ipv6 1
+       ip link add dev br0 up type bridge mcast_snooping 0 \
+               vlan_filtering 1 vlan_default_pvid 0
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       ip link set dev $swp1 master br0
+
+       # The test will use the offload indication on the FDB entry to
+       # understand if the tunnel is offloaded or not
+       bridge fdb append $zmac dev vxlan0 self dst 192.0.2.1
+
+       ip link set dev vxlan0 master br0
+       bridge vlan add dev vxlan0 vid 10 pvid untagged
+
+       # No local port or router port is member in the VLAN, so tunnel should
+       # not be offloaded
+       bridge fdb show brport vxlan0 | grep $zmac | grep self \
+               | grep -q offload
+       check_fail $? "vxlan tunnel offloaded when should not"
+
+       # Configure a VLAN interface and make sure tunnel is offloaded
+       ip link add link br0 name br10 up type vlan id 10
+       sysctl_set net.ipv6.conf.br10.disable_ipv6 0
+       ip -6 address add 2001:db8:1::1/64 dev br10
+       bridge fdb show brport vxlan0 | grep $zmac | grep self \
+               | grep -q offload
+       check_err $? "vxlan tunnel not offloaded when should"
+
+       # Unlink the VXLAN device, make sure tunnel is no longer offloaded,
+       # then add it back to the bridge and make sure it is offloaded
+       ip link set dev vxlan0 nomaster
+       bridge fdb show brport vxlan0 | grep $zmac | grep self \
+               | grep -q offload
+       check_fail $? "vxlan tunnel offloaded after unlinked from bridge"
+
+       ip link set dev vxlan0 master br0
+       bridge fdb show brport vxlan0 | grep $zmac | grep self \
+               | grep -q offload
+       check_fail $? "vxlan tunnel offloaded despite no matching vid"
+
+       bridge vlan add dev vxlan0 vid 10 pvid untagged
+       bridge fdb show brport vxlan0 | grep $zmac | grep self \
+               | grep -q offload
+       check_err $? "vxlan tunnel not offloaded after adding vid"
+
+       log_test "vxlan - l3 vni"
+
+       ip link del dev vxlan0
+       ip link del dev br0
+       sysctl_restore net.ipv6.conf.default.disable_ipv6
+}
+
+offload_indication_vlan_aware_test()
+{
+       offload_indication_vlan_aware_setup_create
+       offload_indication_vlan_aware_fdb_test
+       offload_indication_vlan_aware_decap_route_test
+       offload_indication_vlan_aware_setup_destroy
+
+       log_info "offload indication - replay & cleanup - vlan aware"
+       offload_indication_vlan_aware_join_vxlan_first
+       offload_indication_vlan_aware_join_vxlan_last
+       offload_indication_vlan_aware_l3vni_test
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/mlxsw/vxlan_flooding.sh b/tools/testing/selftests/drivers/net/mlxsw/vxlan_flooding.sh
new file mode 100755 (executable)
index 0000000..fedcb7b
--- /dev/null
@@ -0,0 +1,309 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test VxLAN flooding. The device stores flood records in a singly linked list
+# where each record stores up to three IPv4 addresses of remote VTEPs. The test
+# verifies that packets are correctly flooded in various cases such as deletion
+# of a record in the middle of the list.
+#
+# +--------------------+
+# | H1 (vrf)           |
+# |    + $h1           |
+# |    | 203.0.113.1/24|
+# +----|---------------+
+#      |
+# +----|----------------------------------------------------------------------+
+# | SW |                                                                      |
+# | +--|--------------------------------------------------------------------+ |
+# | |  + $swp1                   BR0 (802.1d)                               | |
+# | |                                                                       | |
+# | |  + vxlan0 (vxlan)                                                     | |
+# | |    local 198.51.100.1                                                 | |
+# | |    remote 198.51.100.{2..13}                                          | |
+# | |    id 10 dstport 4789                                                 | |
+# | +-----------------------------------------------------------------------+ |
+# |                                                                           |
+# |  198.51.100.0/24 via 192.0.2.2                                            |
+# |                                                                           |
+# |    + $rp1                                                                 |
+# |    | 192.0.2.1/24                                                         |
+# +----|----------------------------------------------------------------------+
+#      |
+# +----|--------------------------------------------------------+
+# |    |                                               R2 (vrf) |
+# |    + $rp2                                                   |
+# |      192.0.2.2/24                                           |
+# |                                                             |
+# +-------------------------------------------------------------+
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="flooding_test"
+NUM_NETIFS=4
+source $lib_dir/tc_common.sh
+source $lib_dir/lib.sh
+
+h1_create()
+{
+       simple_if_init $h1 203.0.113.1/24
+}
+
+h1_destroy()
+{
+       simple_if_fini $h1 203.0.113.1/24
+}
+
+switch_create()
+{
+       # Make sure the bridge uses the MAC address of the local port and
+       # not that of the VxLAN's device
+       ip link add dev br0 type bridge mcast_snooping 0
+       ip link set dev br0 address $(mac_get $swp1)
+
+       ip link add name vxlan0 type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       ip address add 198.51.100.1/32 dev lo
+
+       ip link set dev $swp1 master br0
+       ip link set dev vxlan0 master br0
+
+       ip link set dev br0 up
+       ip link set dev $swp1 up
+       ip link set dev vxlan0 up
+}
+
+switch_destroy()
+{
+       ip link set dev vxlan0 down
+       ip link set dev $swp1 down
+       ip link set dev br0 down
+
+       ip link set dev vxlan0 nomaster
+       ip link set dev $swp1 nomaster
+
+       ip address del 198.51.100.1/32 dev lo
+
+       ip link del dev vxlan0
+
+       ip link del dev br0
+}
+
+router1_create()
+{
+       # This router is in the default VRF, where the VxLAN device is
+       # performing the L3 lookup
+       ip link set dev $rp1 up
+       ip address add 192.0.2.1/24 dev $rp1
+       ip route add 198.51.100.0/24 via 192.0.2.2
+}
+
+router1_destroy()
+{
+       ip route del 198.51.100.0/24 via 192.0.2.2
+       ip address del 192.0.2.1/24 dev $rp1
+       ip link set dev $rp1 down
+}
+
+router2_create()
+{
+       # This router is not in the default VRF, so use simple_if_init()
+       simple_if_init $rp2 192.0.2.2/24
+}
+
+router2_destroy()
+{
+       simple_if_fini $rp2 192.0.2.2/24
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       swp1=${NETIFS[p2]}
+
+       rp1=${NETIFS[p3]}
+       rp2=${NETIFS[p4]}
+
+       vrf_prepare
+
+       h1_create
+
+       switch_create
+
+       router1_create
+       router2_create
+
+       forwarding_enable
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       forwarding_restore
+
+       router2_destroy
+       router1_destroy
+
+       switch_destroy
+
+       h1_destroy
+
+       vrf_cleanup
+}
+
+flooding_remotes_add()
+{
+       local num_remotes=$1
+       local lsb
+       local i
+
+       for i in $(eval echo {1..$num_remotes}); do
+               lsb=$((i + 1))
+
+               bridge fdb append 00:00:00:00:00:00 dev vxlan0 self \
+                       dst 198.51.100.$lsb
+       done
+}
+
+flooding_filters_add()
+{
+       local num_remotes=$1
+       local lsb
+       local i
+
+       tc qdisc add dev $rp2 clsact
+
+       for i in $(eval echo {1..$num_remotes}); do
+               lsb=$((i + 1))
+
+               tc filter add dev $rp2 ingress protocol ip pref $i handle $i \
+                       flower ip_proto udp dst_ip 198.51.100.$lsb \
+                       dst_port 4789 skip_sw action drop
+       done
+}
+
+flooding_filters_del()
+{
+       local num_remotes=$1
+       local i
+
+       for i in $(eval echo {1..$num_remotes}); do
+               tc filter del dev $rp2 ingress protocol ip pref $i \
+                       handle $i flower
+       done
+
+       tc qdisc del dev $rp2 clsact
+}
+
+flooding_check_packets()
+{
+       local packets=("$@")
+       local num_remotes=${#packets[@]}
+       local i
+
+       for i in $(eval echo {1..$num_remotes}); do
+               tc_check_packets "dev $rp2 ingress" $i ${packets[i - 1]}
+               check_err $? "remote $i - did not get expected number of packets"
+       done
+}
+
+flooding_test()
+{
+       # Use 12 remote VTEPs that will be stored in 4 records. The array
+       # 'packets' will store how many packets are expected to be received
+       # by each remote VTEP at each stage of the test
+       declare -a packets=(1 1 1 1 1 1 1 1 1 1 1 1)
+       local num_remotes=12
+
+       RET=0
+
+       # Add FDB entries for remote VTEPs and corresponding tc filters on the
+       # ingress of the nexthop router. These filters will count how many
+       # packets were flooded to each remote VTEP
+       flooding_remotes_add $num_remotes
+       flooding_filters_add $num_remotes
+
+       # Send one packet and make sure it is flooded to all the remote VTEPs
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 1 packet"
+
+       # Delete the third record which corresponds to VTEPs with LSB 8..10
+       # and check that packet is flooded correctly when we remove a record
+       # from the middle of the list
+       RET=0
+
+       packets=(2 2 2 2 2 2 1 1 1 2 2 2)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.8
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.9
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.10
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 2 packets"
+
+       # Delete the first record and make sure the packet is flooded correctly
+       RET=0
+
+       packets=(2 2 2 3 3 3 1 1 1 3 3 3)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.2
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.3
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.4
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 3 packets"
+
+       # Delete the last record and make sure the packet is flooded correctly
+       RET=0
+
+       packets=(2 2 2 4 4 4 1 1 1 3 3 3)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.11
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.12
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.13
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 4 packets"
+
+       # Delete the last record, one entry at a time and make sure single
+       # entries are correctly removed
+       RET=0
+
+       packets=(2 2 2 4 5 5 1 1 1 3 3 3)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.5
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 5 packets"
+
+       RET=0
+
+       packets=(2 2 2 4 5 6 1 1 1 3 3 3)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.6
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 6 packets"
+
+       RET=0
+
+       packets=(2 2 2 4 5 6 1 1 1 3 3 3)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.7
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 7 packets"
+
+       flooding_filters_del $num_remotes
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
index 8cf22b3..6f81130 100644 (file)
@@ -3,6 +3,7 @@ socket
 psock_fanout
 psock_snd
 psock_tpacket
+reuseport_addr_any
 reuseport_bpf
 reuseport_bpf_cpu
 reuseport_bpf_numa
@@ -14,4 +15,5 @@ udpgso_bench_rx
 udpgso_bench_tx
 tcp_inq
 tls
+txring_overwrite
 ip_defrag
index eec3598..f8f3e90 100644 (file)
@@ -4,14 +4,16 @@
 CFLAGS =  -Wall -Wl,--no-as-needed -O2 -g
 CFLAGS += -I../../../../usr/include/
 
-TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh
+TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh \
+             rtnetlink.sh xfrm_policy.sh
 TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh udpgso.sh ip_defrag.sh
 TEST_PROGS += udpgso_bench.sh fib_rule_tests.sh msg_zerocopy.sh psock_snd.sh
-TEST_PROGS += udpgro_bench.sh udpgro.sh
+TEST_PROGS += udpgro_bench.sh udpgro.sh test_vxlan_under_vrf.sh reuseport_addr_any.sh
+TEST_PROGS += test_vxlan_fdb_changelink.sh
 TEST_PROGS_EXTENDED := in_netns.sh
 TEST_GEN_FILES =  socket
-TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy
-TEST_GEN_FILES += tcp_mmap tcp_inq psock_snd
+TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any
+TEST_GEN_FILES += tcp_mmap tcp_inq psock_snd txring_overwrite
 TEST_GEN_FILES += udpgso udpgso_bench_tx udpgso_bench_rx ip_defrag
 TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
 TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls
index cd3a2f1..5821bdd 100644 (file)
@@ -14,3 +14,17 @@ CONFIG_IPV6_VTI=y
 CONFIG_DUMMY=y
 CONFIG_BRIDGE=y
 CONFIG_VLAN_8021Q=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_ADVANCED=y
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_NAT_IPV6=m
+CONFIG_NF_NAT_IPV4=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_IP_NF_NAT=m
+CONFIG_NF_TABLES=m
+CONFIG_NF_TABLES_IPV6=y
+CONFIG_NF_TABLES_IPV4=y
+CONFIG_NFT_CHAIN_NAT_IPV6=m
+CONFIG_NFT_CHAIN_NAT_IPV4=m
index 85d2535..3f248d1 100644 (file)
@@ -15,6 +15,8 @@ PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
 PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no}
 NETIF_TYPE=${NETIF_TYPE:=veth}
 NETIF_CREATE=${NETIF_CREATE:=yes}
+MCD=${MCD:=smcrouted}
+MC_CLI=${MC_CLI:=smcroutectl}
 
 relative_path="${BASH_SOURCE%/*}"
 if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
@@ -104,7 +106,7 @@ create_netif_veth()
 {
        local i
 
-       for i in $(eval echo {1..$NUM_NETIFS}); do
+       for ((i = 1; i <= NUM_NETIFS; ++i)); do
                local j=$((i+1))
 
                ip link show dev ${NETIFS[p$i]} &> /dev/null
@@ -135,7 +137,7 @@ if [[ "$NETIF_CREATE" = "yes" ]]; then
        create_netif
 fi
 
-for i in $(eval echo {1..$NUM_NETIFS}); do
+for ((i = 1; i <= NUM_NETIFS; ++i)); do
        ip link show dev ${NETIFS[p$i]} &> /dev/null
        if [[ $? -ne 0 ]]; then
                echo "SKIP: could not find all required interfaces"
@@ -477,11 +479,24 @@ master_name_get()
        ip -j link show dev $if_name | jq -r '.[]["master"]'
 }
 
+link_stats_get()
+{
+       local if_name=$1; shift
+       local dir=$1; shift
+       local stat=$1; shift
+
+       ip -j -s link show dev $if_name \
+               | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
+}
+
 link_stats_tx_packets_get()
 {
-       local if_name=$1
+       link_stats_get $1 tx packets
+}
 
-       ip -j -s link show dev $if_name | jq '.[]["stats64"]["tx"]["packets"]'
+link_stats_rx_errors_get()
+{
+       link_stats_get $1 rx errors
 }
 
 tc_rule_stats_get()
@@ -783,6 +798,17 @@ multipath_eval()
        log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
 }
 
+in_ns()
+{
+       local name=$1; shift
+
+       ip netns exec $name bash <<-EOF
+               NUM_NETIFS=0
+               source lib.sh
+               $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
+       EOF
+}
+
 ##############################################################################
 # Tests
 
@@ -790,10 +816,11 @@ ping_do()
 {
        local if_name=$1
        local dip=$2
+       local args=$3
        local vrf_name
 
        vrf_name=$(master_name_get $if_name)
-       ip vrf exec $vrf_name $PING $dip -c 10 -i 0.1 -w 2 &> /dev/null
+       ip vrf exec $vrf_name $PING $args $dip -c 10 -i 0.1 -w 2 &> /dev/null
 }
 
 ping_test()
@@ -802,17 +829,18 @@ ping_test()
 
        ping_do $1 $2
        check_err $?
-       log_test "ping"
+       log_test "ping$3"
 }
 
 ping6_do()
 {
        local if_name=$1
        local dip=$2
+       local args=$3
        local vrf_name
 
        vrf_name=$(master_name_get $if_name)
-       ip vrf exec $vrf_name $PING6 $dip -c 10 -i 0.1 -w 2 &> /dev/null
+       ip vrf exec $vrf_name $PING6 $args $dip -c 10 -i 0.1 -w 2 &> /dev/null
 }
 
 ping6_test()
@@ -821,7 +849,7 @@ ping6_test()
 
        ping6_do $1 $2
        check_err $?
-       log_test "ping6"
+       log_test "ping6$3"
 }
 
 learning_test()
diff --git a/tools/testing/selftests/net/forwarding/router_multicast.sh b/tools/testing/selftests/net/forwarding/router_multicast.sh
new file mode 100755 (executable)
index 0000000..109e6d7
--- /dev/null
@@ -0,0 +1,311 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# +------------------+
+# | H1 (v$h1)        |
+# | 2001:db8:1::2/64 |
+# | 198.51.100.2/28  |
+# |         $h1 +    |
+# +-------------|----+
+#               |
+# +-------------|-------------------------------+
+# | SW1         |                               |
+# |        $rp1 +                               |
+# | 198.51.100.1/28                             |
+# | 2001:db8:1::1/64                            |
+# |                                             |
+# | 2001:db8:2::1/64           2001:db8:3::1/64 |
+# | 198.51.100.17/28           198.51.100.33/28 |
+# |         $rp2 +                     $rp3 +   |
+# +--------------|--------------------------|---+
+#                |                          |
+#                |                          |
+# +--------------|---+       +--------------|---+
+# | H2 (v$h2)    |   |       | H3 (v$h3)    |   |
+# |          $h2 +   |       |          $h3 +   |
+# | 198.51.100.18/28 |       | 198.51.100.34/28 |
+# | 2001:db8:2::2/64 |       | 2001:db8:3::2/64 |
+# +------------------+       +------------------+
+#
+
+ALL_TESTS="mcast_v4 mcast_v6"
+NUM_NETIFS=6
+source lib.sh
+source tc_common.sh
+
+require_command $MCD
+require_command $MC_CLI
+table_name=selftests
+
+h1_create()
+{
+       simple_if_init $h1 198.51.100.2/28 2001:db8:1::2/64
+
+       ip route add 198.51.100.16/28 vrf v$h1 nexthop via 198.51.100.1
+       ip route add 198.51.100.32/28 vrf v$h1 nexthop via 198.51.100.1
+
+       ip route add 2001:db8:2::/64 vrf v$h1 nexthop via 2001:db8:1::1
+       ip route add 2001:db8:3::/64 vrf v$h1 nexthop via 2001:db8:1::1
+}
+
+h1_destroy()
+{
+       ip route del 2001:db8:3::/64 vrf v$h1
+       ip route del 2001:db8:2::/64 vrf v$h1
+
+       ip route del 198.51.100.32/28 vrf v$h1
+       ip route del 198.51.100.16/28 vrf v$h1
+
+       simple_if_fini $h1 198.51.100.2/28 2001:db8:1::2/64
+}
+
+h2_create()
+{
+       simple_if_init $h2 198.51.100.18/28 2001:db8:2::2/64
+
+       ip route add 198.51.100.0/28 vrf v$h2 nexthop via 198.51.100.17
+       ip route add 198.51.100.32/28 vrf v$h2 nexthop via 198.51.100.17
+
+       ip route add 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:2::1
+       ip route add 2001:db8:3::/64 vrf v$h2 nexthop via 2001:db8:2::1
+
+       tc qdisc add dev $h2 ingress
+}
+
+h2_destroy()
+{
+       tc qdisc del dev $h2 ingress
+
+       ip route del 2001:db8:3::/64 vrf v$h2
+       ip route del 2001:db8:1::/64 vrf v$h2
+
+       ip route del 198.51.100.32/28 vrf v$h2
+       ip route del 198.51.100.0/28 vrf v$h2
+
+       simple_if_fini $h2 198.51.100.18/28 2001:db8:2::2/64
+}
+
+h3_create()
+{
+       simple_if_init $h3 198.51.100.34/28 2001:db8:3::2/64
+
+       ip route add 198.51.100.0/28 vrf v$h3 nexthop via 198.51.100.33
+       ip route add 198.51.100.16/28 vrf v$h3 nexthop via 198.51.100.33
+
+       ip route add 2001:db8:1::/64 vrf v$h3 nexthop via 2001:db8:3::1
+       ip route add 2001:db8:2::/64 vrf v$h3 nexthop via 2001:db8:3::1
+
+       tc qdisc add dev $h3 ingress
+}
+
+h3_destroy()
+{
+       tc qdisc del dev $h3 ingress
+
+       ip route del 2001:db8:2::/64 vrf v$h3
+       ip route del 2001:db8:1::/64 vrf v$h3
+
+       ip route del 198.51.100.16/28 vrf v$h3
+       ip route del 198.51.100.0/28 vrf v$h3
+
+       simple_if_fini $h3 198.51.100.34/28 2001:db8:3::2/64
+}
+
+router_create()
+{
+       ip link set dev $rp1 up
+       ip link set dev $rp2 up
+       ip link set dev $rp3 up
+
+       ip address add 198.51.100.1/28 dev $rp1
+       ip address add 198.51.100.17/28 dev $rp2
+       ip address add 198.51.100.33/28 dev $rp3
+
+       ip address add 2001:db8:1::1/64 dev $rp1
+       ip address add 2001:db8:2::1/64 dev $rp2
+       ip address add 2001:db8:3::1/64 dev $rp3
+}
+
+router_destroy()
+{
+       ip address del 2001:db8:3::1/64 dev $rp3
+       ip address del 2001:db8:2::1/64 dev $rp2
+       ip address del 2001:db8:1::1/64 dev $rp1
+
+       ip address del 198.51.100.33/28 dev $rp3
+       ip address del 198.51.100.17/28 dev $rp2
+       ip address del 198.51.100.1/28 dev $rp1
+
+       ip link set dev $rp3 down
+       ip link set dev $rp2 down
+       ip link set dev $rp1 down
+}
+
+start_mcd()
+{
+       SMCROUTEDIR="$(mktemp -d)"
+
+       for ((i = 1; i <= $NUM_NETIFS; ++i)); do
+               echo "phyint ${NETIFS[p$i]} enable" >> \
+                       $SMCROUTEDIR/$table_name.conf
+       done
+
+       $MCD -N -I $table_name -f $SMCROUTEDIR/$table_name.conf \
+               -P $SMCROUTEDIR/$table_name.pid
+}
+
+kill_mcd()
+{
+       pkill $MCD
+       rm -rf $SMCROUTEDIR
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       rp1=${NETIFS[p2]}
+
+       rp2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       rp3=${NETIFS[p5]}
+       h3=${NETIFS[p6]}
+
+       start_mcd
+
+       vrf_prepare
+
+       h1_create
+       h2_create
+       h3_create
+
+       router_create
+
+       forwarding_enable
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       forwarding_restore
+
+       router_destroy
+
+       h3_destroy
+       h2_destroy
+       h1_destroy
+
+       vrf_cleanup
+
+       kill_mcd
+}
+
+create_mcast_sg()
+{
+       local if_name=$1; shift
+       local s_addr=$1; shift
+       local mcast=$1; shift
+       local dest_ifs=${@}
+
+       $MC_CLI -I $table_name add $if_name $s_addr $mcast $dest_ifs
+}
+
+delete_mcast_sg()
+{
+       local if_name=$1; shift
+       local s_addr=$1; shift
+       local mcast=$1; shift
+       local dest_ifs=${@}
+
+        $MC_CLI -I $table_name remove $if_name $s_addr $mcast $dest_ifs
+}
+
+mcast_v4()
+{
+       # Add two interfaces to an MC group, send a packet to the MC group and
+       # verify packets are received on both. Then delete the route and verify
+       # packets are no longer received.
+
+       RET=0
+
+       tc filter add dev $h2 ingress protocol ip pref 1 handle 122 flower \
+               dst_ip 225.1.2.3 action drop
+       tc filter add dev $h3 ingress protocol ip pref 1 handle 133 flower \
+               dst_ip 225.1.2.3 action drop
+
+       create_mcast_sg $rp1 198.51.100.2 225.1.2.3 $rp2 $rp3
+
+       # Send frames with the corresponding L2 destination address.
+       $MZ $h1 -c 5 -p 128 -t udp -a 00:11:22:33:44:55 -b 01:00:5e:01:02:03 \
+               -A 198.51.100.2 -B 225.1.2.3 -q
+
+       tc_check_packets "dev $h2 ingress" 122 5
+       check_err $? "Multicast not received on first host"
+       tc_check_packets "dev $h3 ingress" 133 5
+       check_err $? "Multicast not received on second host"
+
+       delete_mcast_sg $rp1 198.51.100.2 225.1.2.3 $rp2 $rp3
+
+       $MZ $h1 -c 5 -p 128 -t udp -a 00:11:22:33:44:55 -b 01:00:5e:01:02:03 \
+               -A 198.51.100.2 -B 225.1.2.3 -q
+
+       tc_check_packets "dev $h2 ingress" 122 5
+       check_err $? "Multicast received on host although deleted"
+       tc_check_packets "dev $h3 ingress" 133 5
+       check_err $? "Multicast received on second host although deleted"
+
+       tc filter del dev $h3 ingress protocol ip pref 1 handle 133 flower
+       tc filter del dev $h2 ingress protocol ip pref 1 handle 122 flower
+
+       log_test "mcast IPv4"
+}
+
+mcast_v6()
+{
+       # Add two interfaces to an MC group, send a packet to the MC group and
+       # verify packets are received on both. Then delete the route and verify
+       # packets are no longer received.
+
+       RET=0
+
+       tc filter add dev $h2 ingress protocol ipv6 pref 1 handle 122 flower \
+               dst_ip ff0e::3 action drop
+       tc filter add dev $h3 ingress protocol ipv6 pref 1 handle 133 flower \
+               dst_ip ff0e::3 action drop
+
+       create_mcast_sg $rp1 2001:db8:1::2 ff0e::3 $rp2 $rp3
+
+       # Send frames with the corresponding L2 destination address.
+       $MZ $h1 -6 -c 5 -p 128 -t udp -a 00:11:22:33:44:55 \
+               -b 33:33:00:00:00:03 -A 2001:db8:1::2 -B ff0e::3 -q
+
+       tc_check_packets "dev $h2 ingress" 122 5
+       check_err $? "Multicast not received on first host"
+       tc_check_packets "dev $h3 ingress" 133 5
+       check_err $? "Multicast not received on second host"
+
+       delete_mcast_sg $rp1 2001:db8:1::2 ff0e::3 $rp2 $rp3
+
+       $MZ $h1 -6 -c 5 -p 128 -t udp -a 00:11:22:33:44:55 \
+               -b 33:33:00:00:00:03 -A 2001:db8:1::2 -B ff0e::3 -q
+
+       tc_check_packets "dev $h2 ingress" 122 5
+       check_err $? "Multicast received on first host although deleted"
+       tc_check_packets "dev $h3 ingress" 133 5
+       check_err $? "Multicast received on second host although deleted"
+
+       tc filter del dev $h3 ingress protocol ipv6 pref 1 handle 133 flower
+       tc filter del dev $h2 ingress protocol ipv6 pref 1 handle 122 flower
+
+       log_test "mcast IPv6"
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/router_vid_1.sh b/tools/testing/selftests/net/forwarding/router_vid_1.sh
new file mode 100755 (executable)
index 0000000..a7306c7
--- /dev/null
@@ -0,0 +1,135 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ALL_TESTS="ping_ipv4 ping_ipv6"
+NUM_NETIFS=4
+source lib.sh
+
+h1_create()
+{
+       vrf_create "vrf-h1"
+       ip link set dev vrf-h1 up
+
+       ip link set dev $h1 up
+       vlan_create $h1 1 vrf-h1 192.0.2.2/24 2001:db8:1::2/64
+
+       ip route add 198.51.100.0/24 vrf vrf-h1 nexthop via 192.0.2.1
+       ip route add 2001:db8:2::/64 vrf vrf-h1 nexthop via 2001:db8:1::1
+}
+
+h1_destroy()
+{
+       ip route del 2001:db8:2::/64 vrf vrf-h1
+       ip route del 198.51.100.0/24 vrf vrf-h1
+
+       vlan_destroy $h1 1
+       ip link set dev $h1 down
+
+       ip link set dev vrf-h1 down
+       vrf_destroy "vrf-h1"
+}
+
+h2_create()
+{
+       vrf_create "vrf-h2"
+       ip link set dev vrf-h2 up
+
+       ip link set dev $h2 up
+       vlan_create $h2 1 vrf-h2 198.51.100.2/24 2001:db8:2::2/64
+
+       ip route add 192.0.2.0/24 vrf vrf-h2 nexthop via 198.51.100.1
+       ip route add 2001:db8:1::/64 vrf vrf-h2 nexthop via 2001:db8:2::1
+}
+
+h2_destroy()
+{
+       ip route del 2001:db8:1::/64 vrf vrf-h2
+       ip route del 192.0.2.0/24 vrf vrf-h2
+
+       vlan_destroy $h2 1
+       ip link set dev $h2 down
+
+       ip link set dev vrf-h2 down
+       vrf_destroy "vrf-h2"
+}
+
+router_create()
+{
+       ip link set dev $rp1 up
+       ip link add link $rp1 name $rp1.1 up type vlan id 1
+
+       ip address add 192.0.2.1/24 dev $rp1.1
+       ip address add 2001:db8:1::1/64 dev $rp1.1
+
+       ip link set dev $rp2 up
+       ip link add link $rp2 name $rp2.1 up type vlan id 1
+
+       ip address add 198.51.100.1/24 dev $rp2.1
+       ip address add 2001:db8:2::1/64 dev $rp2.1
+}
+
+router_destroy()
+{
+       ip address del 2001:db8:2::1/64 dev $rp2.1
+       ip address del 198.51.100.1/24 dev $rp2.1
+
+       ip link del dev $rp2.1
+       ip link set dev $rp2 down
+
+       ip address del 2001:db8:1::1/64 dev $rp1.1
+       ip address del 192.0.2.1/24 dev $rp1.1
+
+       ip link del dev $rp1.1
+       ip link set dev $rp1 down
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       rp1=${NETIFS[p2]}
+
+       rp2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       vrf_prepare
+
+       h1_create
+       h2_create
+
+       router_create
+
+       forwarding_enable
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       forwarding_restore
+
+       router_destroy
+
+       h2_destroy
+       h1_destroy
+
+       vrf_cleanup
+}
+
+ping_ipv4()
+{
+       ping_test $h1.1 198.51.100.2
+}
+
+ping_ipv6()
+{
+       ping6_test $h1.1 2001:db8:2::2
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1d.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1d.sh
new file mode 100755 (executable)
index 0000000..56cef3b
--- /dev/null
@@ -0,0 +1,786 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# +--------------------+                               +----------------------+
+# | H1 (vrf)           |                               |             H2 (vrf) |
+# |    + $h1           |                               |  + $h2               |
+# |    | 192.0.2.1/28  |                               |  | 192.0.2.2/28      |
+# +----|---------------+                               +--|-------------------+
+#      |                                                  |
+# +----|--------------------------------------------------|-------------------+
+# | SW |                                                  |                   |
+# | +--|--------------------------------------------------|-----------------+ |
+# | |  + $swp1                   BR1 (802.1d)             + $swp2           | |
+# | |                                                                       | |
+# | |  + vx1 (vxlan)                                                        | |
+# | |    local 192.0.2.17                                                   | |
+# | |    remote 192.0.2.34 192.0.2.50                                       | |
+# | |    id 1000 dstport $VXPORT                                            | |
+# | +-----------------------------------------------------------------------+ |
+# |                                                                           |
+# |  192.0.2.32/28 via 192.0.2.18                                             |
+# |  192.0.2.48/28 via 192.0.2.18                                             |
+# |                                                                           |
+# |    + $rp1                                                                 |
+# |    | 192.0.2.17/28                                                        |
+# +----|----------------------------------------------------------------------+
+#      |
+# +----|--------------------------------------------------------+
+# |    |                                             VRP2 (vrf) |
+# |    + $rp2                                                   |
+# |      192.0.2.18/28                                          |
+# |                                                             |   (maybe) HW
+# =============================================================================
+# |                                                             |  (likely) SW
+# |    + v1 (veth)                             + v3 (veth)      |
+# |    | 192.0.2.33/28                         | 192.0.2.49/28  |
+# +----|---------------------------------------|----------------+
+#      |                                       |
+# +----|------------------------------+   +----|------------------------------+
+# |    + v2 (veth)        NS1 (netns) |   |    + v4 (veth)        NS2 (netns) |
+# |      192.0.2.34/28                |   |      192.0.2.50/28                |
+# |                                   |   |                                   |
+# |   192.0.2.16/28 via 192.0.2.33    |   |   192.0.2.16/28 via 192.0.2.49    |
+# |   192.0.2.50/32 via 192.0.2.33    |   |   192.0.2.34/32 via 192.0.2.49    |
+# |                                   |   |                                   |
+# | +-------------------------------+ |   | +-------------------------------+ |
+# | |                  BR2 (802.1d) | |   | |                  BR2 (802.1d) | |
+# | |  + vx2 (vxlan)                | |   | |  + vx2 (vxlan)                | |
+# | |    local 192.0.2.34           | |   | |    local 192.0.2.50           | |
+# | |    remote 192.0.2.17          | |   | |    remote 192.0.2.17          | |
+# | |    remote 192.0.2.50          | |   | |    remote 192.0.2.34          | |
+# | |    id 1000 dstport $VXPORT    | |   | |    id 1000 dstport $VXPORT    | |
+# | |                               | |   | |                               | |
+# | |  + w1 (veth)                  | |   | |  + w1 (veth)                  | |
+# | +--|----------------------------+ |   | +--|----------------------------+ |
+# |    |                              |   |    |                              |
+# | +--|----------------------------+ |   | +--|----------------------------+ |
+# | |  |                  VW2 (vrf) | |   | |  |                  VW2 (vrf) | |
+# | |  + w2 (veth)                  | |   | |  + w2 (veth)                  | |
+# | |    192.0.2.3/28               | |   | |    192.0.2.4/28               | |
+# | +-------------------------------+ |   | +-------------------------------+ |
+# +-----------------------------------+   +-----------------------------------+
+
+: ${VXPORT:=4789}
+export VXPORT
+
+: ${ALL_TESTS:="
+       ping_ipv4
+       test_flood
+       test_unicast
+       test_ttl
+       test_tos
+       test_ecn_encap
+       test_ecn_decap
+       reapply_config
+       ping_ipv4
+       test_flood
+       test_unicast
+       test_learning
+    "}
+
+NUM_NETIFS=6
+source lib.sh
+
+h1_create()
+{
+       simple_if_init $h1 192.0.2.1/28
+       tc qdisc add dev $h1 clsact
+}
+
+h1_destroy()
+{
+       tc qdisc del dev $h1 clsact
+       simple_if_fini $h1 192.0.2.1/28
+}
+
+h2_create()
+{
+       simple_if_init $h2 192.0.2.2/28
+       tc qdisc add dev $h2 clsact
+}
+
+h2_destroy()
+{
+       tc qdisc del dev $h2 clsact
+       simple_if_fini $h2 192.0.2.2/28
+}
+
+rp1_set_addr()
+{
+       ip address add dev $rp1 192.0.2.17/28
+
+       ip route add 192.0.2.32/28 nexthop via 192.0.2.18
+       ip route add 192.0.2.48/28 nexthop via 192.0.2.18
+}
+
+rp1_unset_addr()
+{
+       ip route del 192.0.2.48/28 nexthop via 192.0.2.18
+       ip route del 192.0.2.32/28 nexthop via 192.0.2.18
+
+       ip address del dev $rp1 192.0.2.17/28
+}
+
+switch_create()
+{
+       ip link add name br1 type bridge vlan_filtering 0 mcast_snooping 0
+       # Make sure the bridge uses the MAC address of the local port and not
+       # that of the VxLAN's device.
+       ip link set dev br1 address $(mac_get $swp1)
+       ip link set dev br1 up
+
+       ip link set dev $rp1 up
+       rp1_set_addr
+
+       ip link add name vx1 type vxlan id 1000         \
+               local 192.0.2.17 dstport "$VXPORT"      \
+               nolearning noudpcsum tos inherit ttl 100
+       ip link set dev vx1 up
+
+       ip link set dev vx1 master br1
+       ip link set dev $swp1 master br1
+       ip link set dev $swp1 up
+
+       ip link set dev $swp2 master br1
+       ip link set dev $swp2 up
+
+       bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
+}
+
+switch_destroy()
+{
+       rp1_unset_addr
+       ip link set dev $rp1 down
+
+       bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
+
+       ip link set dev vx1 nomaster
+       ip link set dev vx1 down
+       ip link del dev vx1
+
+       ip link set dev $swp2 down
+       ip link set dev $swp2 nomaster
+
+       ip link set dev $swp1 down
+       ip link set dev $swp1 nomaster
+
+       ip link set dev br1 down
+       ip link del dev br1
+}
+
+vrp2_create()
+{
+       simple_if_init $rp2 192.0.2.18/28
+       __simple_if_init v1 v$rp2 192.0.2.33/28
+       __simple_if_init v3 v$rp2 192.0.2.49/28
+       tc qdisc add dev v1 clsact
+}
+
+vrp2_destroy()
+{
+       tc qdisc del dev v1 clsact
+       __simple_if_fini v3 192.0.2.49/28
+       __simple_if_fini v1 192.0.2.33/28
+       simple_if_fini $rp2 192.0.2.18/28
+}
+
+ns_init_common()
+{
+       local in_if=$1; shift
+       local in_addr=$1; shift
+       local other_in_addr=$1; shift
+       local nh_addr=$1; shift
+       local host_addr=$1; shift
+
+       ip link set dev $in_if up
+       ip address add dev $in_if $in_addr/28
+       tc qdisc add dev $in_if clsact
+
+       ip link add name br2 type bridge vlan_filtering 0
+       ip link set dev br2 up
+
+       ip link add name w1 type veth peer name w2
+
+       ip link set dev w1 master br2
+       ip link set dev w1 up
+
+       ip link add name vx2 type vxlan id 1000 local $in_addr dstport "$VXPORT"
+       ip link set dev vx2 up
+       bridge fdb append dev vx2 00:00:00:00:00:00 dst 192.0.2.17 self
+       bridge fdb append dev vx2 00:00:00:00:00:00 dst $other_in_addr self
+
+       ip link set dev vx2 master br2
+       tc qdisc add dev vx2 clsact
+
+       simple_if_init w2 $host_addr/28
+
+       ip route add 192.0.2.16/28 nexthop via $nh_addr
+       ip route add $other_in_addr/32 nexthop via $nh_addr
+}
+export -f ns_init_common
+
+ns1_create()
+{
+       ip netns add ns1
+       ip link set dev v2 netns ns1
+       in_ns ns1 \
+             ns_init_common v2 192.0.2.34 192.0.2.50 192.0.2.33 192.0.2.3
+}
+
+ns1_destroy()
+{
+       ip netns exec ns1 ip link set dev v2 netns 1
+       ip netns del ns1
+}
+
+ns2_create()
+{
+       ip netns add ns2
+       ip link set dev v4 netns ns2
+       in_ns ns2 \
+             ns_init_common v4 192.0.2.50 192.0.2.34 192.0.2.49 192.0.2.4
+}
+
+ns2_destroy()
+{
+       ip netns exec ns2 ip link set dev v4 netns 1
+       ip netns del ns2
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       swp1=${NETIFS[p2]}
+
+       swp2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       rp1=${NETIFS[p5]}
+       rp2=${NETIFS[p6]}
+
+       vrf_prepare
+       forwarding_enable
+
+       h1_create
+       h2_create
+       switch_create
+
+       ip link add name v1 type veth peer name v2
+       ip link add name v3 type veth peer name v4
+       vrp2_create
+       ns1_create
+       ns2_create
+
+       r1_mac=$(in_ns ns1 mac_get w2)
+       r2_mac=$(in_ns ns2 mac_get w2)
+       h2_mac=$(mac_get $h2)
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       ns2_destroy
+       ns1_destroy
+       vrp2_destroy
+       ip link del dev v3
+       ip link del dev v1
+
+       switch_destroy
+       h2_destroy
+       h1_destroy
+
+       forwarding_restore
+       vrf_cleanup
+}
+
+# For the first round of tests, vx1 is the first device to get attached to the
+# bridge, and that at the point that the local IP is already configured. Try the
+# other scenario of attaching the device to an already-offloaded bridge, and
+# only then attach the local IP.
+reapply_config()
+{
+       echo "Reapplying configuration"
+
+       bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
+       rp1_unset_addr
+       ip link set dev vx1 nomaster
+       sleep 5
+
+       ip link set dev vx1 master br1
+       bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
+       sleep 1
+       rp1_set_addr
+       sleep 5
+}
+
+ping_ipv4()
+{
+       ping_test $h1 192.0.2.2 ": local->local"
+       ping_test $h1 192.0.2.3 ": local->remote 1"
+       ping_test $h1 192.0.2.4 ": local->remote 2"
+}
+
+maybe_in_ns()
+{
+       echo ${1:+in_ns} $1
+}
+
+__flood_counter_add_del()
+{
+       local add_del=$1; shift
+       local dev=$1; shift
+       local ns=$1; shift
+
+       # Putting the ICMP capture both to HW and to SW will end up
+       # double-counting the packets that are trapped to slow path, such as for
+       # the unicast test. Adding either skip_hw or skip_sw fixes this problem,
+       # but with skip_hw, the flooded packets are not counted at all, because
+       # those are dropped due to MAC address mismatch; and skip_sw is a no-go
+       # for veth-based topologies.
+       #
+       # So try to install with skip_sw and fall back to skip_sw if that fails.
+
+       $(maybe_in_ns $ns) __icmp_capture_add_del          \
+                          $add_del 100 "" $dev skip_sw 2>/dev/null || \
+       $(maybe_in_ns $ns) __icmp_capture_add_del          \
+                          $add_del 100 "" $dev skip_hw
+}
+
+flood_counter_install()
+{
+       __flood_counter_add_del add "$@"
+}
+
+flood_counter_uninstall()
+{
+       __flood_counter_add_del del "$@"
+}
+
+flood_fetch_stat()
+{
+       local dev=$1; shift
+       local ns=$1; shift
+
+       $(maybe_in_ns $ns) tc_rule_stats_get $dev 100 ingress
+}
+
+flood_fetch_stats()
+{
+       local counters=("${@}")
+       local counter
+
+       for counter in "${counters[@]}"; do
+               flood_fetch_stat $counter
+       done
+}
+
+vxlan_flood_test()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local -a expects=("${@}")
+
+       local -a counters=($h2 "vx2 ns1" "vx2 ns2")
+       local counter
+       local key
+
+       for counter in "${counters[@]}"; do
+               flood_counter_install $counter
+       done
+
+       local -a t0s=($(flood_fetch_stats "${counters[@]}"))
+       $MZ $h1 -c 10 -d 100msec -p 64 -b $mac -B $dst -t icmp -q
+       sleep 1
+       local -a t1s=($(flood_fetch_stats "${counters[@]}"))
+
+       for key in ${!t0s[@]}; do
+               local delta=$((t1s[$key] - t0s[$key]))
+               local expect=${expects[$key]}
+
+               ((expect == delta))
+               check_err $? "${counters[$key]}: Expected to capture $expect packets, got $delta."
+       done
+
+       for counter in "${counters[@]}"; do
+               flood_counter_uninstall $counter
+       done
+}
+
+__test_flood()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local what=$1; shift
+
+       RET=0
+
+       vxlan_flood_test $mac $dst 10 10 10
+
+       log_test "VXLAN: $what"
+}
+
+test_flood()
+{
+       __test_flood de:ad:be:ef:13:37 192.0.2.100 "flood"
+}
+
+vxlan_fdb_add_del()
+{
+       local add_del=$1; shift
+       local mac=$1; shift
+       local dev=$1; shift
+       local dst=$1; shift
+
+       bridge fdb $add_del dev $dev $mac self static permanent \
+               ${dst:+dst} $dst 2>/dev/null
+       bridge fdb $add_del dev $dev $mac master static 2>/dev/null
+}
+
+__test_unicast()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local hit_idx=$1; shift
+       local what=$1; shift
+
+       RET=0
+
+       local -a expects=(0 0 0)
+       expects[$hit_idx]=10
+
+       vxlan_flood_test $mac $dst "${expects[@]}"
+
+       log_test "VXLAN: $what"
+}
+
+test_unicast()
+{
+       local -a targets=("$h2_mac $h2"
+                         "$r1_mac vx1 192.0.2.34"
+                         "$r2_mac vx1 192.0.2.50")
+       local target
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del add $target
+       done
+
+       __test_unicast $h2_mac 192.0.2.2 0 "local MAC unicast"
+       __test_unicast $r1_mac 192.0.2.3 1 "remote MAC 1 unicast"
+       __test_unicast $r2_mac 192.0.2.4 2 "remote MAC 2 unicast"
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del del $target
+       done
+}
+
+vxlan_ping_test()
+{
+       local ping_dev=$1; shift
+       local ping_dip=$1; shift
+       local ping_args=$1; shift
+       local capture_dev=$1; shift
+       local capture_dir=$1; shift
+       local capture_pref=$1; shift
+       local expect=$1; shift
+
+       local t0=$(tc_rule_stats_get $capture_dev $capture_pref $capture_dir)
+       ping_do $ping_dev $ping_dip "$ping_args"
+       local t1=$(tc_rule_stats_get $capture_dev $capture_pref $capture_dir)
+       local delta=$((t1 - t0))
+
+       # Tolerate a couple stray extra packets.
+       ((expect <= delta && delta <= expect + 2))
+       check_err $? "$capture_dev: Expected to capture $expect packets, got $delta."
+}
+
+test_ttl()
+{
+       RET=0
+
+       tc filter add dev v1 egress pref 77 prot ip \
+               flower ip_ttl 99 action pass
+       vxlan_ping_test $h1 192.0.2.3 "" v1 egress 77 10
+       tc filter del dev v1 egress pref 77 prot ip
+
+       log_test "VXLAN: envelope TTL"
+}
+
+test_tos()
+{
+       RET=0
+
+       tc filter add dev v1 egress pref 77 prot ip \
+               flower ip_tos 0x40 action pass
+       vxlan_ping_test $h1 192.0.2.3 "-Q 0x40" v1 egress 77 10
+       vxlan_ping_test $h1 192.0.2.3 "-Q 0x30" v1 egress 77 0
+       tc filter del dev v1 egress pref 77 prot ip
+
+       log_test "VXLAN: envelope TOS inheritance"
+}
+
+__test_ecn_encap()
+{
+       local q=$1; shift
+       local tos=$1; shift
+
+       RET=0
+
+       tc filter add dev v1 egress pref 77 prot ip \
+               flower ip_tos $tos action pass
+       sleep 1
+       vxlan_ping_test $h1 192.0.2.3 "-Q $q" v1 egress 77 10
+       tc filter del dev v1 egress pref 77 prot ip
+
+       log_test "VXLAN: ECN encap: $q->$tos"
+}
+
+test_ecn_encap()
+{
+       # In accordance with INET_ECN_encapsulate()
+       __test_ecn_encap 0x00 0x00
+       __test_ecn_encap 0x01 0x01
+       __test_ecn_encap 0x02 0x02
+       __test_ecn_encap 0x03 0x02
+}
+
+vxlan_encapped_ping_do()
+{
+       local count=$1; shift
+       local dev=$1; shift
+       local next_hop_mac=$1; shift
+       local dest_ip=$1; shift
+       local dest_mac=$1; shift
+       local inner_tos=$1; shift
+       local outer_tos=$1; shift
+
+       $MZ $dev -c $count -d 100msec -q \
+               -b $next_hop_mac -B $dest_ip \
+               -t udp tos=$outer_tos,sp=23456,dp=$VXPORT,p=$(:
+                   )"08:"$(                      : VXLAN flags
+                   )"00:00:00:"$(                : VXLAN reserved
+                   )"00:03:e8:"$(                : VXLAN VNI
+                   )"00:"$(                      : VXLAN reserved
+                   )"$dest_mac:"$(               : ETH daddr
+                   )"$(mac_get w2):"$(           : ETH saddr
+                   )"08:00:"$(                   : ETH type
+                   )"45:"$(                      : IP version + IHL
+                   )"$inner_tos:"$(              : IP TOS
+                   )"00:54:"$(                   : IP total length
+                   )"99:83:"$(                   : IP identification
+                   )"40:00:"$(                   : IP flags + frag off
+                   )"40:"$(                      : IP TTL
+                   )"01:"$(                      : IP proto
+                   )"00:00:"$(                   : IP header csum
+                   )"c0:00:02:03:"$(             : IP saddr: 192.0.2.3
+                   )"c0:00:02:01:"$(             : IP daddr: 192.0.2.1
+                   )"08:"$(                      : ICMP type
+                   )"00:"$(                      : ICMP code
+                   )"8b:f2:"$(                   : ICMP csum
+                   )"1f:6a:"$(                   : ICMP request identifier
+                   )"00:01:"$(                   : ICMP request sequence number
+                   )"4f:ff:c5:5b:00:00:00:00:"$( : ICMP payload
+                   )"6d:74:0b:00:00:00:00:00:"$( :
+                   )"10:11:12:13:14:15:16:17:"$( :
+                   )"18:19:1a:1b:1c:1d:1e:1f:"$( :
+                   )"20:21:22:23:24:25:26:27:"$( :
+                   )"28:29:2a:2b:2c:2d:2e:2f:"$( :
+                   )"30:31:32:33:34:35:36:37"
+}
+export -f vxlan_encapped_ping_do
+
+vxlan_encapped_ping_test()
+{
+       local ping_dev=$1; shift
+       local nh_dev=$1; shift
+       local ping_dip=$1; shift
+       local inner_tos=$1; shift
+       local outer_tos=$1; shift
+       local stat_get=$1; shift
+       local expect=$1; shift
+
+       local t0=$($stat_get)
+
+       in_ns ns1 \
+               vxlan_encapped_ping_do 10 $ping_dev $(mac_get $nh_dev) \
+                       $ping_dip $(mac_get $h1) \
+                       $inner_tos $outer_tos
+
+       local t1=$($stat_get)
+       local delta=$((t1 - t0))
+
+       # Tolerate a couple stray extra packets.
+       ((expect <= delta && delta <= expect + 2))
+       check_err $? "Expected to capture $expect packets, got $delta."
+}
+export -f vxlan_encapped_ping_test
+
+__test_ecn_decap()
+{
+       local orig_inner_tos=$1; shift
+       local orig_outer_tos=$1; shift
+       local decapped_tos=$1; shift
+
+       RET=0
+
+       tc filter add dev $h1 ingress pref 77 prot ip \
+               flower ip_tos $decapped_tos action pass
+       sleep 1
+       vxlan_encapped_ping_test v2 v1 192.0.2.17 \
+                                $orig_inner_tos $orig_outer_tos \
+                                "tc_rule_stats_get $h1 77 ingress" 10
+       tc filter del dev $h1 ingress pref 77
+
+       log_test "VXLAN: ECN decap: $orig_outer_tos/$orig_inner_tos->$decapped_tos"
+}
+
+test_ecn_decap_error()
+{
+       local orig_inner_tos=00
+       local orig_outer_tos=03
+
+       RET=0
+
+       vxlan_encapped_ping_test v2 v1 192.0.2.17 \
+                                $orig_inner_tos $orig_outer_tos \
+                                "link_stats_rx_errors_get vx1" 10
+
+       log_test "VXLAN: ECN decap: $orig_outer_tos/$orig_inner_tos->error"
+}
+
+test_ecn_decap()
+{
+       # In accordance with INET_ECN_decapsulate()
+       __test_ecn_decap 00 00 0x00
+       __test_ecn_decap 01 01 0x01
+       __test_ecn_decap 02 01 0x02
+       __test_ecn_decap 01 03 0x03
+       __test_ecn_decap 02 03 0x03
+       test_ecn_decap_error
+}
+
+test_learning()
+{
+       local mac=de:ad:be:ef:13:37
+       local dst=192.0.2.100
+
+       # Enable learning on the VxLAN device and set ageing time to 10 seconds
+       ip link set dev br1 type bridge ageing_time 1000
+       ip link set dev vx1 type vxlan ageing 10
+       ip link set dev vx1 type vxlan learning
+       reapply_config
+
+       # Check that flooding works
+       RET=0
+
+       vxlan_flood_test $mac $dst 10 10 10
+
+       log_test "VXLAN: flood before learning"
+
+       # Send a packet with source mac set to $mac from host w2 and check that
+       # a corresponding entry is created in VxLAN device vx1
+       RET=0
+
+       in_ns ns1 $MZ w2 -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff -B $dst \
+               -t icmp -q
+       sleep 1
+
+       bridge fdb show brport vx1 | grep $mac | grep -q self
+       check_err $?
+       bridge fdb show brport vx1 | grep $mac | grep -q -v self
+       check_err $?
+
+       log_test "VXLAN: show learned FDB entry"
+
+       # Repeat first test and check that packets only reach host w2 in ns1
+       RET=0
+
+       vxlan_flood_test $mac $dst 0 10 0
+
+       log_test "VXLAN: learned FDB entry"
+
+       # Delete the learned FDB entry from the VxLAN and bridge devices and
+       # check that packets are flooded
+       RET=0
+
+       bridge fdb del dev vx1 $mac master self
+       sleep 1
+
+       vxlan_flood_test $mac $dst 10 10 10
+
+       log_test "VXLAN: deletion of learned FDB entry"
+
+       # Re-learn the first FDB entry and check that it is correctly aged-out
+       RET=0
+
+       in_ns ns1 $MZ w2 -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff -B $dst \
+               -t icmp -q
+       sleep 1
+
+       bridge fdb show brport vx1 | grep $mac | grep -q self
+       check_err $?
+       bridge fdb show brport vx1 | grep $mac | grep -q -v self
+       check_err $?
+
+       vxlan_flood_test $mac $dst 0 10 0
+
+       sleep 20
+
+       bridge fdb show brport vx1 | grep $mac | grep -q self
+       check_fail $?
+       bridge fdb show brport vx1 | grep $mac | grep -q -v self
+       check_fail $?
+
+       vxlan_flood_test $mac $dst 10 10 10
+
+       log_test "VXLAN: Ageing of learned FDB entry"
+
+       # Toggle learning on the bridge port and check that the bridge's FDB
+       # is populated only when it should
+       RET=0
+
+       ip link set dev vx1 type bridge_slave learning off
+
+       in_ns ns1 $MZ w2 -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff -B $dst \
+               -t icmp -q
+       sleep 1
+
+       bridge fdb show brport vx1 | grep $mac | grep -q -v self
+       check_fail $?
+
+       ip link set dev vx1 type bridge_slave learning on
+
+       in_ns ns1 $MZ w2 -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff -B $dst \
+               -t icmp -q
+       sleep 1
+
+       bridge fdb show brport vx1 | grep $mac | grep -q -v self
+       check_err $?
+
+       log_test "VXLAN: learning toggling on bridge port"
+
+       # Restore previous settings
+       ip link set dev vx1 type vxlan nolearning
+       ip link set dev vx1 type vxlan ageing 300
+       ip link set dev br1 type bridge ageing_time 30000
+       reapply_config
+}
+
+test_all()
+{
+       echo "Running tests with UDP port $VXPORT"
+       tests_run
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+test_all
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1d_port_8472.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1d_port_8472.sh
new file mode 100755 (executable)
index 0000000..3bf3da6
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# A wrapper to run VXLAN tests with an unusual port number.
+
+VXPORT=8472
+ALL_TESTS="
+       ping_ipv4
+"
+source vxlan_bridge_1d.sh
diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1q.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1q.sh
new file mode 100755 (executable)
index 0000000..a578972
--- /dev/null
@@ -0,0 +1,860 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# +-----------------------+                          +------------------------+
+# | H1 (vrf)              |                          | H2 (vrf)               |
+# |  + $h1.10             |                          |  + $h2.10              |
+# |  | 192.0.2.1/28       |                          |  | 192.0.2.2/28        |
+# |  |                    |                          |  |                     |
+# |  | + $h1.20           |                          |  | + $h2.20            |
+# |  \ | 198.51.100.1/24  |                          |  \ | 198.51.100.2/24   |
+# |   \|                  |                          |   \|                   |
+# |    + $h1              |                          |    + $h2               |
+# +----|------------------+                          +----|-------------------+
+#      |                                                  |
+# +----|--------------------------------------------------|-------------------+
+# | SW |                                                  |                   |
+# | +--|--------------------------------------------------|-----------------+ |
+# | |  + $swp1                   BR1 (802.1q)             + $swp2           | |
+# | |     vid 10                                             vid 10         | |
+# | |     vid 20                                             vid 20         | |
+# | |                                                                       | |
+# | |  + vx10 (vxlan)                        + vx20 (vxlan)                 | |
+# | |    local 192.0.2.17                      local 192.0.2.17             | |
+# | |    remote 192.0.2.34 192.0.2.50          remote 192.0.2.34 192.0.2.50 | |
+# | |    id 1000 dstport $VXPORT               id 2000 dstport $VXPORT      | |
+# | |    vid 10 pvid untagged                  vid 20 pvid untagged         | |
+# | +-----------------------------------------------------------------------+ |
+# |                                                                           |
+# |  192.0.2.32/28 via 192.0.2.18                                             |
+# |  192.0.2.48/28 via 192.0.2.18                                             |
+# |                                                                           |
+# |    + $rp1                                                                 |
+# |    | 192.0.2.17/28                                                        |
+# +----|----------------------------------------------------------------------+
+#      |
+# +----|--------------------------------------------------------+
+# |    |                                             VRP2 (vrf) |
+# |    + $rp2                                                   |
+# |      192.0.2.18/28                                          |
+# |                                                             |   (maybe) HW
+# =============================================================================
+# |                                                             |  (likely) SW
+# |    + v1 (veth)                             + v3 (veth)      |
+# |    | 192.0.2.33/28                         | 192.0.2.49/28  |
+# +----|---------------------------------------|----------------+
+#      |                                       |
+# +----|------------------------------+   +----|------------------------------+
+# |    + v2 (veth)        NS1 (netns) |   |    + v4 (veth)        NS2 (netns) |
+# |      192.0.2.34/28                |   |      192.0.2.50/28                |
+# |                                   |   |                                   |
+# |   192.0.2.16/28 via 192.0.2.33    |   |   192.0.2.16/28 via 192.0.2.49    |
+# |   192.0.2.50/32 via 192.0.2.33    |   |   192.0.2.34/32 via 192.0.2.49    |
+# |                                   |   |                                   |
+# | +-------------------------------+ |   | +-------------------------------+ |
+# | |                  BR2 (802.1q) | |   | |                  BR2 (802.1q) | |
+# | |  + vx10 (vxlan)               | |   | |  + vx10 (vxlan)               | |
+# | |    local 192.0.2.34           | |   | |    local 192.0.2.50           | |
+# | |    remote 192.0.2.17          | |   | |    remote 192.0.2.17          | |
+# | |    remote 192.0.2.50          | |   | |    remote 192.0.2.34          | |
+# | |    id 1000 dstport $VXPORT    | |   | |    id 1000 dstport $VXPORT    | |
+# | |    vid 10 pvid untagged       | |   | |    vid 10 pvid untagged       | |
+# | |                               | |   | |                               | |
+# | |  + vx20 (vxlan)               | |   | |  + vx20 (vxlan)               | |
+# | |    local 192.0.2.34           | |   | |    local 192.0.2.50           | |
+# | |    remote 192.0.2.17          | |   | |    remote 192.0.2.17          | |
+# | |    remote 192.0.2.50          | |   | |    remote 192.0.2.34          | |
+# | |    id 2000 dstport $VXPORT    | |   | |    id 2000 dstport $VXPORT    | |
+# | |    vid 20 pvid untagged       | |   | |    vid 20 pvid untagged       | |
+# | |                               | |   | |                               | |
+# | |  + w1 (veth)                  | |   | |  + w1 (veth)                  | |
+# | |  | vid 10                     | |   | |  | vid 10                     | |
+# | |  | vid 20                     | |   | |  | vid 20                     | |
+# | +--|----------------------------+ |   | +--|----------------------------+ |
+# |    |                              |   |    |                              |
+# | +--|----------------------------+ |   | +--|----------------------------+ |
+# | |  + w2 (veth)        VW2 (vrf) | |   | |  + w2 (veth)        VW2 (vrf) | |
+# | |  |\                           | |   | |  |\                           | |
+# | |  | + w2.10                    | |   | |  | + w2.10                    | |
+# | |  |   192.0.2.3/28             | |   | |  |   192.0.2.4/28             | |
+# | |  |                            | |   | |  |                            | |
+# | |  + w2.20                      | |   | |  + w2.20                      | |
+# | |    198.51.100.3/24            | |   | |    198.51.100.4/24            | |
+# | +-------------------------------+ |   | +-------------------------------+ |
+# +-----------------------------------+   +-----------------------------------+
+
+: ${VXPORT:=4789}
+export VXPORT
+
+: ${ALL_TESTS:="
+       ping_ipv4
+       test_flood
+       test_unicast
+       reapply_config
+       ping_ipv4
+       test_flood
+       test_unicast
+       test_learning
+       test_pvid
+    "}
+
+NUM_NETIFS=6
+source lib.sh
+
+h1_create()
+{
+       simple_if_init $h1
+       tc qdisc add dev $h1 clsact
+       vlan_create $h1 10 v$h1 192.0.2.1/28
+       vlan_create $h1 20 v$h1 198.51.100.1/24
+}
+
+h1_destroy()
+{
+       vlan_destroy $h1 20
+       vlan_destroy $h1 10
+       tc qdisc del dev $h1 clsact
+       simple_if_fini $h1
+}
+
+h2_create()
+{
+       simple_if_init $h2
+       tc qdisc add dev $h2 clsact
+       vlan_create $h2 10 v$h2 192.0.2.2/28
+       vlan_create $h2 20 v$h2 198.51.100.2/24
+}
+
+h2_destroy()
+{
+       vlan_destroy $h2 20
+       vlan_destroy $h2 10
+       tc qdisc del dev $h2 clsact
+       simple_if_fini $h2
+}
+
+rp1_set_addr()
+{
+       ip address add dev $rp1 192.0.2.17/28
+
+       ip route add 192.0.2.32/28 nexthop via 192.0.2.18
+       ip route add 192.0.2.48/28 nexthop via 192.0.2.18
+}
+
+rp1_unset_addr()
+{
+       ip route del 192.0.2.48/28 nexthop via 192.0.2.18
+       ip route del 192.0.2.32/28 nexthop via 192.0.2.18
+
+       ip address del dev $rp1 192.0.2.17/28
+}
+
+switch_create()
+{
+       ip link add name br1 type bridge vlan_filtering 1 vlan_default_pvid 0 \
+               mcast_snooping 0
+       # Make sure the bridge uses the MAC address of the local port and not
+       # that of the VxLAN's device.
+       ip link set dev br1 address $(mac_get $swp1)
+       ip link set dev br1 up
+
+       ip link set dev $rp1 up
+       rp1_set_addr
+
+       ip link add name vx10 type vxlan id 1000        \
+               local 192.0.2.17 dstport "$VXPORT"      \
+               nolearning noudpcsum tos inherit ttl 100
+       ip link set dev vx10 up
+
+       ip link set dev vx10 master br1
+       bridge vlan add vid 10 dev vx10 pvid untagged
+
+       ip link add name vx20 type vxlan id 2000        \
+               local 192.0.2.17 dstport "$VXPORT"      \
+               nolearning noudpcsum tos inherit ttl 100
+       ip link set dev vx20 up
+
+       ip link set dev vx20 master br1
+       bridge vlan add vid 20 dev vx20 pvid untagged
+
+       ip link set dev $swp1 master br1
+       ip link set dev $swp1 up
+       bridge vlan add vid 10 dev $swp1
+       bridge vlan add vid 20 dev $swp1
+
+       ip link set dev $swp2 master br1
+       ip link set dev $swp2 up
+       bridge vlan add vid 10 dev $swp2
+       bridge vlan add vid 20 dev $swp2
+
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst 192.0.2.50 self
+
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst 192.0.2.50 self
+}
+
+switch_destroy()
+{
+       bridge fdb del dev vx20 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx20 00:00:00:00:00:00 dst 192.0.2.34 self
+
+       bridge fdb del dev vx10 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx10 00:00:00:00:00:00 dst 192.0.2.34 self
+
+       bridge vlan del vid 20 dev $swp2
+       bridge vlan del vid 10 dev $swp2
+       ip link set dev $swp2 down
+       ip link set dev $swp2 nomaster
+
+       bridge vlan del vid 20 dev $swp1
+       bridge vlan del vid 10 dev $swp1
+       ip link set dev $swp1 down
+       ip link set dev $swp1 nomaster
+
+       bridge vlan del vid 20 dev vx20
+       ip link set dev vx20 nomaster
+
+       ip link set dev vx20 down
+       ip link del dev vx20
+
+       bridge vlan del vid 10 dev vx10
+       ip link set dev vx10 nomaster
+
+       ip link set dev vx10 down
+       ip link del dev vx10
+
+       rp1_unset_addr
+       ip link set dev $rp1 down
+
+       ip link set dev br1 down
+       ip link del dev br1
+}
+
+vrp2_create()
+{
+       simple_if_init $rp2 192.0.2.18/28
+       __simple_if_init v1 v$rp2 192.0.2.33/28
+       __simple_if_init v3 v$rp2 192.0.2.49/28
+       tc qdisc add dev v1 clsact
+}
+
+vrp2_destroy()
+{
+       tc qdisc del dev v1 clsact
+       __simple_if_fini v3 192.0.2.49/28
+       __simple_if_fini v1 192.0.2.33/28
+       simple_if_fini $rp2 192.0.2.18/28
+}
+
+ns_init_common()
+{
+       local in_if=$1; shift
+       local in_addr=$1; shift
+       local other_in_addr=$1; shift
+       local nh_addr=$1; shift
+       local host_addr1=$1; shift
+       local host_addr2=$1; shift
+
+       ip link set dev $in_if up
+       ip address add dev $in_if $in_addr/28
+       tc qdisc add dev $in_if clsact
+
+       ip link add name br2 type bridge vlan_filtering 1 vlan_default_pvid 0
+       ip link set dev br2 up
+
+       ip link add name w1 type veth peer name w2
+
+       ip link set dev w1 master br2
+       ip link set dev w1 up
+
+       bridge vlan add vid 10 dev w1
+       bridge vlan add vid 20 dev w1
+
+       ip link add name vx10 type vxlan id 1000 local $in_addr \
+               dstport "$VXPORT"
+       ip link set dev vx10 up
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst 192.0.2.17 self
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst $other_in_addr self
+
+       ip link set dev vx10 master br2
+       tc qdisc add dev vx10 clsact
+
+       bridge vlan add vid 10 dev vx10 pvid untagged
+
+       ip link add name vx20 type vxlan id 2000 local $in_addr \
+               dstport "$VXPORT"
+       ip link set dev vx20 up
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst 192.0.2.17 self
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst $other_in_addr self
+
+       ip link set dev vx20 master br2
+       tc qdisc add dev vx20 clsact
+
+       bridge vlan add vid 20 dev vx20 pvid untagged
+
+       simple_if_init w2
+        vlan_create w2 10 vw2 $host_addr1/28
+        vlan_create w2 20 vw2 $host_addr2/24
+
+       ip route add 192.0.2.16/28 nexthop via $nh_addr
+       ip route add $other_in_addr/32 nexthop via $nh_addr
+}
+export -f ns_init_common
+
+ns1_create()
+{
+       ip netns add ns1
+       ip link set dev v2 netns ns1
+       in_ns ns1 \
+             ns_init_common v2 192.0.2.34 192.0.2.50 192.0.2.33 192.0.2.3 \
+             198.51.100.3
+}
+
+ns1_destroy()
+{
+       ip netns exec ns1 ip link set dev v2 netns 1
+       ip netns del ns1
+}
+
+ns2_create()
+{
+       ip netns add ns2
+       ip link set dev v4 netns ns2
+       in_ns ns2 \
+             ns_init_common v4 192.0.2.50 192.0.2.34 192.0.2.49 192.0.2.4 \
+             198.51.100.4
+}
+
+ns2_destroy()
+{
+       ip netns exec ns2 ip link set dev v4 netns 1
+       ip netns del ns2
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       swp1=${NETIFS[p2]}
+
+       swp2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       rp1=${NETIFS[p5]}
+       rp2=${NETIFS[p6]}
+
+       vrf_prepare
+       forwarding_enable
+
+       h1_create
+       h2_create
+       switch_create
+
+       ip link add name v1 type veth peer name v2
+       ip link add name v3 type veth peer name v4
+       vrp2_create
+       ns1_create
+       ns2_create
+
+       r1_mac=$(in_ns ns1 mac_get w2)
+       r2_mac=$(in_ns ns2 mac_get w2)
+       h2_mac=$(mac_get $h2)
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       ns2_destroy
+       ns1_destroy
+       vrp2_destroy
+       ip link del dev v3
+       ip link del dev v1
+
+       switch_destroy
+       h2_destroy
+       h1_destroy
+
+       forwarding_restore
+       vrf_cleanup
+}
+
+# For the first round of tests, vx10 and vx20 were the first devices to get
+# attached to the bridge, and that at the point that the local IP is already
+# configured. Try the other scenario of attaching these devices to a bridge
+# that already has local ports members, and only then assign the local IP.
+reapply_config()
+{
+       log_info "Reapplying configuration"
+
+       bridge fdb del dev vx20 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx20 00:00:00:00:00:00 dst 192.0.2.34 self
+
+       bridge fdb del dev vx10 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx10 00:00:00:00:00:00 dst 192.0.2.34 self
+
+       ip link set dev vx20 nomaster
+       ip link set dev vx10 nomaster
+
+       rp1_unset_addr
+       sleep 5
+
+       ip link set dev vx10 master br1
+       bridge vlan add vid 10 dev vx10 pvid untagged
+
+       ip link set dev vx20 master br1
+       bridge vlan add vid 20 dev vx20 pvid untagged
+
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst 192.0.2.50 self
+
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst 192.0.2.50 self
+
+       rp1_set_addr
+       sleep 5
+}
+
+ping_ipv4()
+{
+       ping_test $h1.10 192.0.2.2 ": local->local vid 10"
+       ping_test $h1.20 198.51.100.2 ": local->local vid 20"
+       ping_test $h1.10 192.0.2.3 ": local->remote 1 vid 10"
+       ping_test $h1.10 192.0.2.4 ": local->remote 2 vid 10"
+       ping_test $h1.20 198.51.100.3 ": local->remote 1 vid 20"
+       ping_test $h1.20 198.51.100.4 ": local->remote 2 vid 20"
+}
+
+maybe_in_ns()
+{
+       echo ${1:+in_ns} $1
+}
+
+__flood_counter_add_del()
+{
+       local add_del=$1; shift
+       local dev=$1; shift
+       local ns=$1; shift
+
+       # Putting the ICMP capture both to HW and to SW will end up
+       # double-counting the packets that are trapped to slow path, such as for
+       # the unicast test. Adding either skip_hw or skip_sw fixes this problem,
+       # but with skip_hw, the flooded packets are not counted at all, because
+       # those are dropped due to MAC address mismatch; and skip_sw is a no-go
+       # for veth-based topologies.
+       #
+       # So try to install with skip_sw and fall back to skip_sw if that fails.
+
+       $(maybe_in_ns $ns) __icmp_capture_add_del          \
+                          $add_del 100 "" $dev skip_sw 2>/dev/null || \
+       $(maybe_in_ns $ns) __icmp_capture_add_del          \
+                          $add_del 100 "" $dev skip_hw
+}
+
+flood_counter_install()
+{
+       __flood_counter_add_del add "$@"
+}
+
+flood_counter_uninstall()
+{
+       __flood_counter_add_del del "$@"
+}
+
+flood_fetch_stat()
+{
+       local dev=$1; shift
+       local ns=$1; shift
+
+       $(maybe_in_ns $ns) tc_rule_stats_get $dev 100 ingress
+}
+
+flood_fetch_stats()
+{
+       local counters=("${@}")
+       local counter
+
+       for counter in "${counters[@]}"; do
+               flood_fetch_stat $counter
+       done
+}
+
+vxlan_flood_test()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local vid=$1; shift
+       local -a expects=("${@}")
+
+       local -a counters=($h2 "vx10 ns1" "vx20 ns1" "vx10 ns2" "vx20 ns2")
+       local counter
+       local key
+
+       # Packets reach the local host tagged whereas they reach the VxLAN
+       # devices untagged. In order to be able to use the same filter for
+       # all counters, make sure the packets also reach the local host
+       # untagged
+       bridge vlan add vid $vid dev $swp2 untagged
+       for counter in "${counters[@]}"; do
+               flood_counter_install $counter
+       done
+
+       local -a t0s=($(flood_fetch_stats "${counters[@]}"))
+       $MZ $h1 -Q $vid -c 10 -d 100msec -p 64 -b $mac -B $dst -t icmp -q
+       sleep 1
+       local -a t1s=($(flood_fetch_stats "${counters[@]}"))
+
+       for key in ${!t0s[@]}; do
+               local delta=$((t1s[$key] - t0s[$key]))
+               local expect=${expects[$key]}
+
+               ((expect == delta))
+               check_err $? "${counters[$key]}: Expected to capture $expect packets, got $delta."
+       done
+
+       for counter in "${counters[@]}"; do
+               flood_counter_uninstall $counter
+       done
+       bridge vlan add vid $vid dev $swp2
+}
+
+__test_flood()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local vid=$1; shift
+       local what=$1; shift
+       local -a expects=("${@}")
+
+       RET=0
+
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: $what"
+}
+
+test_flood()
+{
+       __test_flood de:ad:be:ef:13:37 192.0.2.100 10 "flood vlan 10" \
+               10 10 0 10 0
+       __test_flood ca:fe:be:ef:13:37 198.51.100.100 20 "flood vlan 20" \
+               10 0 10 0 10
+}
+
+vxlan_fdb_add_del()
+{
+       local add_del=$1; shift
+       local vid=$1; shift
+       local mac=$1; shift
+       local dev=$1; shift
+       local dst=$1; shift
+
+       bridge fdb $add_del dev $dev $mac self static permanent \
+               ${dst:+dst} $dst 2>/dev/null
+       bridge fdb $add_del dev $dev $mac master static vlan $vid 2>/dev/null
+}
+
+__test_unicast()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local hit_idx=$1; shift
+       local vid=$1; shift
+       local what=$1; shift
+
+       RET=0
+
+       local -a expects=(0 0 0 0 0)
+       expects[$hit_idx]=10
+
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: $what"
+}
+
+test_unicast()
+{
+       local -a targets=("$h2_mac $h2"
+                         "$r1_mac vx10 192.0.2.34"
+                         "$r2_mac vx10 192.0.2.50")
+       local target
+
+       log_info "unicast vlan 10"
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del add 10 $target
+       done
+
+       __test_unicast $h2_mac 192.0.2.2 0 10 "local MAC unicast"
+       __test_unicast $r1_mac 192.0.2.3 1 10 "remote MAC 1 unicast"
+       __test_unicast $r2_mac 192.0.2.4 3 10 "remote MAC 2 unicast"
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del del 10 $target
+       done
+
+       log_info "unicast vlan 20"
+
+       targets=("$h2_mac $h2" "$r1_mac vx20 192.0.2.34" \
+                "$r2_mac vx20 192.0.2.50")
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del add 20 $target
+       done
+
+       __test_unicast $h2_mac 198.51.100.2 0 20 "local MAC unicast"
+       __test_unicast $r1_mac 198.51.100.3 2 20 "remote MAC 1 unicast"
+       __test_unicast $r2_mac 198.51.100.4 4 20 "remote MAC 2 unicast"
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del del 20 $target
+       done
+}
+
+test_pvid()
+{
+       local -a expects=(0 0 0 0 0)
+       local mac=de:ad:be:ef:13:37
+       local dst=192.0.2.100
+       local vid=10
+
+       # Check that flooding works
+       RET=0
+
+       expects[0]=10; expects[1]=10; expects[3]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood before pvid off"
+
+       # Toggle PVID off and test that flood to remote hosts does not work
+       RET=0
+
+       bridge vlan add vid 10 dev vx10
+
+       expects[0]=10; expects[1]=0; expects[3]=0
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood after pvid off"
+
+       # Toggle PVID on and test that flood to remote hosts does work
+       RET=0
+
+       bridge vlan add vid 10 dev vx10 pvid untagged
+
+       expects[0]=10; expects[1]=10; expects[3]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood after pvid on"
+
+       # Add a new VLAN and test that it does not affect flooding
+       RET=0
+
+       bridge vlan add vid 30 dev vx10
+
+       expects[0]=10; expects[1]=10; expects[3]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       bridge vlan del vid 30 dev vx10
+
+       log_test "VXLAN: flood after vlan add"
+
+       # Remove currently mapped VLAN and test that flood to remote hosts does
+       # not work
+       RET=0
+
+       bridge vlan del vid 10 dev vx10
+
+       expects[0]=10; expects[1]=0; expects[3]=0
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood after vlan delete"
+
+       # Re-add the VLAN and test that flood to remote hosts does work
+       RET=0
+
+       bridge vlan add vid 10 dev vx10 pvid untagged
+
+       expects[0]=10; expects[1]=10; expects[3]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood after vlan re-add"
+}
+
+vxlan_ping_test()
+{
+       local ping_dev=$1; shift
+       local ping_dip=$1; shift
+       local ping_args=$1; shift
+       local capture_dev=$1; shift
+       local capture_dir=$1; shift
+       local capture_pref=$1; shift
+       local expect=$1; shift
+
+       local t0=$(tc_rule_stats_get $capture_dev $capture_pref $capture_dir)
+       ping_do $ping_dev $ping_dip "$ping_args"
+       local t1=$(tc_rule_stats_get $capture_dev $capture_pref $capture_dir)
+       local delta=$((t1 - t0))
+
+       # Tolerate a couple stray extra packets.
+       ((expect <= delta && delta <= expect + 2))
+       check_err $? "$capture_dev: Expected to capture $expect packets, got $delta."
+}
+
+__test_learning()
+{
+       local -a expects=(0 0 0 0 0)
+       local mac=$1; shift
+       local dst=$1; shift
+       local vid=$1; shift
+       local idx1=$1; shift
+       local idx2=$1; shift
+       local vx=vx$vid
+
+       # Check that flooding works
+       RET=0
+
+       expects[0]=10; expects[$idx1]=10; expects[$idx2]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood before learning"
+
+       # Send a packet with source mac set to $mac from host w2 and check that
+       # a corresponding entry is created in the VxLAN device
+       RET=0
+
+       in_ns ns1 $MZ w2 -Q $vid -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff \
+               -B $dst -t icmp -q
+       sleep 1
+
+       bridge fdb show brport $vx | grep $mac | grep -q self
+       check_err $?
+       bridge fdb show brport $vx | grep $mac | grep "vlan $vid" \
+               | grep -q -v self
+       check_err $?
+
+       log_test "VXLAN: show learned FDB entry"
+
+       # Repeat first test and check that packets only reach host w2 in ns1
+       RET=0
+
+       expects[0]=0; expects[$idx1]=10; expects[$idx2]=0
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: learned FDB entry"
+
+       # Delete the learned FDB entry from the VxLAN and bridge devices and
+       # check that packets are flooded
+       RET=0
+
+       bridge fdb del dev $vx $mac master self vlan $vid
+       sleep 1
+
+       expects[0]=10; expects[$idx1]=10; expects[$idx2]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: deletion of learned FDB entry"
+
+       # Re-learn the first FDB entry and check that it is correctly aged-out
+       RET=0
+
+       in_ns ns1 $MZ w2 -Q $vid -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff \
+               -B $dst -t icmp -q
+       sleep 1
+
+       bridge fdb show brport $vx | grep $mac | grep -q self
+       check_err $?
+       bridge fdb show brport $vx | grep $mac | grep "vlan $vid" \
+               | grep -q -v self
+       check_err $?
+
+       expects[0]=0; expects[$idx1]=10; expects[$idx2]=0
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       sleep 20
+
+       bridge fdb show brport $vx | grep $mac | grep -q self
+       check_fail $?
+       bridge fdb show brport $vx | grep $mac | grep "vlan $vid" \
+               | grep -q -v self
+       check_fail $?
+
+       expects[0]=10; expects[$idx1]=10; expects[$idx2]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: Ageing of learned FDB entry"
+
+       # Toggle learning on the bridge port and check that the bridge's FDB
+       # is populated only when it should
+       RET=0
+
+       ip link set dev $vx type bridge_slave learning off
+
+       in_ns ns1 $MZ w2 -Q $vid -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff \
+               -B $dst -t icmp -q
+       sleep 1
+
+       bridge fdb show brport $vx | grep $mac | grep "vlan $vid" \
+               | grep -q -v self
+       check_fail $?
+
+       ip link set dev $vx type bridge_slave learning on
+
+       in_ns ns1 $MZ w2 -Q $vid -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff \
+               -B $dst -t icmp -q
+       sleep 1
+
+       bridge fdb show brport $vx | grep $mac | grep "vlan $vid" \
+               | grep -q -v self
+       check_err $?
+
+       log_test "VXLAN: learning toggling on bridge port"
+}
+
+test_learning()
+{
+       local mac=de:ad:be:ef:13:37
+       local dst=192.0.2.100
+       local vid=10
+
+       # Enable learning on the VxLAN devices and set ageing time to 10 seconds
+       ip link set dev br1 type bridge ageing_time 1000
+       ip link set dev vx10 type vxlan ageing 10
+       ip link set dev vx10 type vxlan learning
+       ip link set dev vx20 type vxlan ageing 10
+       ip link set dev vx20 type vxlan learning
+       reapply_config
+
+       log_info "learning vlan 10"
+
+       __test_learning $mac $dst $vid 1 3
+
+       log_info "learning vlan 20"
+
+       mac=ca:fe:be:ef:13:37
+       dst=198.51.100.100
+       vid=20
+
+       __test_learning $mac $dst $vid 2 4
+
+       # Restore previous settings
+       ip link set dev vx20 type vxlan nolearning
+       ip link set dev vx20 type vxlan ageing 300
+       ip link set dev vx10 type vxlan nolearning
+       ip link set dev vx10 type vxlan ageing 300
+       ip link set dev br1 type bridge ageing_time 30000
+       reapply_config
+}
+
+test_all()
+{
+       log_info "Running tests with UDP port $VXPORT"
+       tests_run
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+test_all
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_port_8472.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_port_8472.sh
new file mode 100755 (executable)
index 0000000..b1b2d1a
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# A wrapper to run VXLAN tests with an unusual port number.
+
+VXPORT=8472
+ALL_TESTS="
+       ping_ipv4
+"
+source vxlan_bridge_1q.sh
index 406cc70..4b02933 100644 (file)
@@ -651,12 +651,13 @@ static void do_flush_datagram(int fd, int type)
 
 static void do_rx(int domain, int type, int protocol)
 {
+       const int cfg_receiver_wait_ms = 400;
        uint64_t tstop;
        int fd;
 
        fd = do_setup_rx(domain, type, protocol);
 
-       tstop = gettimeofday_ms() + cfg_runtime_ms;
+       tstop = gettimeofday_ms() + cfg_runtime_ms + cfg_receiver_wait_ms;
        do {
                if (type == SOCK_STREAM)
                        do_flush_tcp(fd);
index c43c6de..825ffec 100755 (executable)
@@ -25,6 +25,8 @@ readonly path_sysctl_mem="net.core.optmem_max"
 if [[ "$#" -eq "0" ]]; then
        $0 4 tcp -t 1
        $0 6 tcp -t 1
+       $0 4 udp -t 1
+       $0 6 udp -t 1
        echo "OK. All tests passed"
        exit 0
 fi
diff --git a/tools/testing/selftests/net/reuseport_addr_any.c b/tools/testing/selftests/net/reuseport_addr_any.c
new file mode 100644 (file)
index 0000000..6f54d42
--- /dev/null
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Test that sockets listening on a specific address are preferred
+ * over sockets listening on addr_any.
+ */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <error.h>
+#include <linux/dccp.h>
+#include <linux/in.h>
+#include <linux/unistd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+static const char *IP4_ADDR = "127.0.0.1";
+static const char *IP6_ADDR = "::1";
+static const char *IP4_MAPPED6 = "::ffff:127.0.0.1";
+
+static const int PORT = 8888;
+
+static void build_rcv_fd(int family, int proto, int *rcv_fds, int count,
+                        const char *addr_str)
+{
+       struct sockaddr_in  addr4 = {0};
+       struct sockaddr_in6 addr6 = {0};
+       struct sockaddr *addr;
+       int opt, i, sz;
+
+       memset(&addr, 0, sizeof(addr));
+
+       switch (family) {
+       case AF_INET:
+               addr4.sin_family = family;
+               if (!addr_str)
+                       addr4.sin_addr.s_addr = htonl(INADDR_ANY);
+               else if (!inet_pton(family, addr_str, &addr4.sin_addr.s_addr))
+                       error(1, errno, "inet_pton failed: %s", addr_str);
+               addr4.sin_port = htons(PORT);
+               sz = sizeof(addr4);
+               addr = (struct sockaddr *)&addr4;
+               break;
+       case AF_INET6:
+               addr6.sin6_family = AF_INET6;
+               if (!addr_str)
+                       addr6.sin6_addr = in6addr_any;
+               else if (!inet_pton(family, addr_str, &addr6.sin6_addr))
+                       error(1, errno, "inet_pton failed: %s", addr_str);
+               addr6.sin6_port = htons(PORT);
+               sz = sizeof(addr6);
+               addr = (struct sockaddr *)&addr6;
+               break;
+       default:
+               error(1, 0, "Unsupported family %d", family);
+       }
+
+       for (i = 0; i < count; ++i) {
+               rcv_fds[i] = socket(family, proto, 0);
+               if (rcv_fds[i] < 0)
+                       error(1, errno, "failed to create receive socket");
+
+               opt = 1;
+               if (setsockopt(rcv_fds[i], SOL_SOCKET, SO_REUSEPORT, &opt,
+                              sizeof(opt)))
+                       error(1, errno, "failed to set SO_REUSEPORT");
+
+               if (bind(rcv_fds[i], addr, sz))
+                       error(1, errno, "failed to bind receive socket");
+
+               if (proto == SOCK_STREAM && listen(rcv_fds[i], 10))
+                       error(1, errno, "tcp: failed to listen on receive port");
+               else if (proto == SOCK_DCCP) {
+                       if (setsockopt(rcv_fds[i], SOL_DCCP,
+                                       DCCP_SOCKOPT_SERVICE,
+                                       &(int) {htonl(42)}, sizeof(int)))
+                               error(1, errno, "failed to setsockopt");
+
+                       if (listen(rcv_fds[i], 10))
+                               error(1, errno, "dccp: failed to listen on receive port");
+               }
+       }
+}
+
+static int connect_and_send(int family, int proto)
+{
+       struct sockaddr_in  saddr4 = {0};
+       struct sockaddr_in  daddr4 = {0};
+       struct sockaddr_in6 saddr6 = {0};
+       struct sockaddr_in6 daddr6 = {0};
+       struct sockaddr *saddr, *daddr;
+       int fd, sz;
+
+       switch (family) {
+       case AF_INET:
+               saddr4.sin_family = AF_INET;
+               saddr4.sin_addr.s_addr = htonl(INADDR_ANY);
+               saddr4.sin_port = 0;
+
+               daddr4.sin_family = AF_INET;
+               if (!inet_pton(family, IP4_ADDR, &daddr4.sin_addr.s_addr))
+                       error(1, errno, "inet_pton failed: %s", IP4_ADDR);
+               daddr4.sin_port = htons(PORT);
+
+               sz = sizeof(saddr4);
+               saddr = (struct sockaddr *)&saddr4;
+               daddr = (struct sockaddr *)&daddr4;
+       break;
+       case AF_INET6:
+               saddr6.sin6_family = AF_INET6;
+               saddr6.sin6_addr = in6addr_any;
+
+               daddr6.sin6_family = AF_INET6;
+               if (!inet_pton(family, IP6_ADDR, &daddr6.sin6_addr))
+                       error(1, errno, "inet_pton failed: %s", IP6_ADDR);
+               daddr6.sin6_port = htons(PORT);
+
+               sz = sizeof(saddr6);
+               saddr = (struct sockaddr *)&saddr6;
+               daddr = (struct sockaddr *)&daddr6;
+       break;
+       default:
+               error(1, 0, "Unsupported family %d", family);
+       }
+
+       fd = socket(family, proto, 0);
+       if (fd < 0)
+               error(1, errno, "failed to create send socket");
+
+       if (proto == SOCK_DCCP &&
+               setsockopt(fd, SOL_DCCP, DCCP_SOCKOPT_SERVICE,
+                               &(int){htonl(42)}, sizeof(int)))
+               error(1, errno, "failed to setsockopt");
+
+       if (bind(fd, saddr, sz))
+               error(1, errno, "failed to bind send socket");
+
+       if (connect(fd, daddr, sz))
+               error(1, errno, "failed to connect send socket");
+
+       if (send(fd, "a", 1, 0) < 0)
+               error(1, errno, "failed to send message");
+
+       return fd;
+}
+
+static int receive_once(int epfd, int proto)
+{
+       struct epoll_event ev;
+       int i, fd;
+       char buf[8];
+
+       i = epoll_wait(epfd, &ev, 1, 3);
+       if (i < 0)
+               error(1, errno, "epoll_wait failed");
+
+       if (proto == SOCK_STREAM || proto == SOCK_DCCP) {
+               fd = accept(ev.data.fd, NULL, NULL);
+               if (fd < 0)
+                       error(1, errno, "failed to accept");
+               i = recv(fd, buf, sizeof(buf), 0);
+               close(fd);
+       } else {
+               i = recv(ev.data.fd, buf, sizeof(buf), 0);
+       }
+
+       if (i < 0)
+               error(1, errno, "failed to recv");
+
+       return ev.data.fd;
+}
+
+static void test(int *rcv_fds, int count, int family, int proto, int fd)
+{
+       struct epoll_event ev;
+       int epfd, i, send_fd, recv_fd;
+
+       epfd = epoll_create(1);
+       if (epfd < 0)
+               error(1, errno, "failed to create epoll");
+
+       ev.events = EPOLLIN;
+       for (i = 0; i < count; ++i) {
+               ev.data.fd = rcv_fds[i];
+               if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fds[i], &ev))
+                       error(1, errno, "failed to register sock epoll");
+       }
+
+       send_fd = connect_and_send(family, proto);
+
+       recv_fd = receive_once(epfd, proto);
+       if (recv_fd != fd)
+               error(1, 0, "received on an unexpected socket");
+
+       close(send_fd);
+       close(epfd);
+}
+
+
+static void run_one_test(int fam_send, int fam_rcv, int proto,
+                        const char *addr_str)
+{
+       /* Below we test that a socket listening on a specific address
+        * is always selected in preference over a socket listening
+        * on addr_any. Bugs where this is not the case often result
+        * in sockets created first or last to get picked. So below
+        * we make sure that there are always addr_any sockets created
+        * before and after a specific socket is created.
+        */
+       int rcv_fds[10], i;
+
+       build_rcv_fd(AF_INET, proto, rcv_fds, 2, NULL);
+       build_rcv_fd(AF_INET6, proto, rcv_fds + 2, 2, NULL);
+       build_rcv_fd(fam_rcv, proto, rcv_fds + 4, 1, addr_str);
+       build_rcv_fd(AF_INET, proto, rcv_fds + 5, 2, NULL);
+       build_rcv_fd(AF_INET6, proto, rcv_fds + 7, 2, NULL);
+       test(rcv_fds, 9, fam_send, proto, rcv_fds[4]);
+       for (i = 0; i < 9; ++i)
+               close(rcv_fds[i]);
+       fprintf(stderr, "pass\n");
+}
+
+static void test_proto(int proto, const char *proto_str)
+{
+       if (proto == SOCK_DCCP) {
+               int test_fd;
+
+               test_fd = socket(AF_INET, proto, 0);
+               if (test_fd < 0) {
+                       if (errno == ESOCKTNOSUPPORT) {
+                               fprintf(stderr, "DCCP not supported: skipping DCCP tests\n");
+                               return;
+                       } else
+                               error(1, errno, "failed to create a DCCP socket");
+               }
+               close(test_fd);
+       }
+
+       fprintf(stderr, "%s IPv4 ... ", proto_str);
+       run_one_test(AF_INET, AF_INET, proto, IP4_ADDR);
+
+       fprintf(stderr, "%s IPv6 ... ", proto_str);
+       run_one_test(AF_INET6, AF_INET6, proto, IP6_ADDR);
+
+       fprintf(stderr, "%s IPv4 mapped to IPv6 ... ", proto_str);
+       run_one_test(AF_INET, AF_INET6, proto, IP4_MAPPED6);
+}
+
+int main(void)
+{
+       test_proto(SOCK_DGRAM, "UDP");
+       test_proto(SOCK_STREAM, "TCP");
+       test_proto(SOCK_DCCP, "DCCP");
+
+       fprintf(stderr, "SUCCESS\n");
+       return 0;
+}
diff --git a/tools/testing/selftests/net/reuseport_addr_any.sh b/tools/testing/selftests/net/reuseport_addr_any.sh
new file mode 100755 (executable)
index 0000000..104592f
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+
+./in_netns.sh ./reuseport_addr_any
index e101af5..78fc593 100755 (executable)
@@ -205,6 +205,8 @@ kci_test_polrouting()
 
 kci_test_route_get()
 {
+       local hash_policy=$(sysctl -n net.ipv4.fib_multipath_hash_policy)
+
        ret=0
 
        ip route get 127.0.0.1 > /dev/null
@@ -223,6 +225,19 @@ kci_test_route_get()
        check_err $?
        ip route get 10.23.7.11 from 10.23.7.12 iif "$devdummy" > /dev/null
        check_err $?
+       ip route add 10.23.8.0/24 \
+               nexthop via 10.23.7.13 dev "$devdummy" \
+               nexthop via 10.23.7.14 dev "$devdummy"
+       check_err $?
+       sysctl -wq net.ipv4.fib_multipath_hash_policy=0
+       ip route get 10.23.8.11 > /dev/null
+       check_err $?
+       sysctl -wq net.ipv4.fib_multipath_hash_policy=1
+       ip route get 10.23.8.11 > /dev/null
+       check_err $?
+       sysctl -wq net.ipv4.fib_multipath_hash_policy="$hash_policy"
+       ip route del 10.23.8.0/24
+       check_err $?
        ip addr del dev "$devdummy" 10.23.7.11/24
        check_err $?
 
@@ -955,6 +970,111 @@ kci_test_ip6erspan()
        ip netns del "$testns"
 }
 
+kci_test_fdb_get()
+{
+       IP="ip -netns testns"
+       BRIDGE="bridge -netns testns"
+       brdev="test-br0"
+       vxlandev="vxlan10"
+       test_mac=de:ad:be:ef:13:37
+       localip="10.0.2.2"
+       dstip="10.0.2.3"
+       ret=0
+
+       bridge fdb help 2>&1 |grep -q 'bridge fdb get'
+       if [ $? -ne 0 ];then
+               echo "SKIP: fdb get tests: iproute2 too old"
+               return $ksft_skip
+       fi
+
+       ip netns add testns
+       if [ $? -ne 0 ]; then
+               echo "SKIP fdb get tests: cannot add net namespace $testns"
+               return $ksft_skip
+       fi
+
+       $IP link add "$vxlandev" type vxlan id 10 local $localip \
+                dstport 4789 2>/dev/null
+       check_err $?
+       $IP link add name "$brdev" type bridge &>/dev/null
+       check_err $?
+       $IP link set dev "$vxlandev" master "$brdev" &>/dev/null
+       check_err $?
+       $BRIDGE fdb add $test_mac dev "$vxlandev" master &>/dev/null
+       check_err $?
+       $BRIDGE fdb add $test_mac dev "$vxlandev" dst $dstip self &>/dev/null
+       check_err $?
+
+       $BRIDGE fdb get $test_mac brport "$vxlandev" 2>/dev/null | grep -q "dev $vxlandev master $brdev"
+       check_err $?
+       $BRIDGE fdb get $test_mac br "$brdev" 2>/dev/null | grep -q "dev $vxlandev master $brdev"
+       check_err $?
+       $BRIDGE fdb get $test_mac dev "$vxlandev" self 2>/dev/null | grep -q "dev $vxlandev dst $dstip"
+       check_err $?
+
+       ip netns del testns &>/dev/null
+
+       if [ $ret -ne 0 ]; then
+               echo "FAIL: bridge fdb get"
+               return 1
+       fi
+
+       echo "PASS: bridge fdb get"
+}
+
+kci_test_neigh_get()
+{
+       dstmac=de:ad:be:ef:13:37
+       dstip=10.0.2.4
+       dstip6=dead::2
+       ret=0
+
+       ip neigh help 2>&1 |grep -q 'ip neigh get'
+       if [ $? -ne 0 ];then
+               echo "SKIP: fdb get tests: iproute2 too old"
+               return $ksft_skip
+       fi
+
+       # ipv4
+       ip neigh add $dstip lladdr $dstmac dev "$devdummy"  > /dev/null
+       check_err $?
+       ip neigh get $dstip dev "$devdummy" 2> /dev/null | grep -q "$dstmac"
+       check_err $?
+       ip neigh del $dstip lladdr $dstmac dev "$devdummy"  > /dev/null
+       check_err $?
+
+       # ipv4 proxy
+       ip neigh add proxy $dstip dev "$devdummy" > /dev/null
+       check_err $?
+       ip neigh get proxy $dstip dev "$devdummy" 2>/dev/null | grep -q "$dstip"
+       check_err $?
+       ip neigh del proxy $dstip dev "$devdummy" > /dev/null
+       check_err $?
+
+       # ipv6
+       ip neigh add $dstip6 lladdr $dstmac dev "$devdummy"  > /dev/null
+       check_err $?
+       ip neigh get $dstip6 dev "$devdummy" 2> /dev/null | grep -q "$dstmac"
+       check_err $?
+       ip neigh del $dstip6 lladdr $dstmac dev "$devdummy"  > /dev/null
+       check_err $?
+
+       # ipv6 proxy
+       ip neigh add proxy $dstip6 dev "$devdummy" > /dev/null
+       check_err $?
+       ip neigh get proxy $dstip6 dev "$devdummy" 2>/dev/null | grep -q "$dstip6"
+       check_err $?
+       ip neigh del proxy $dstip6 dev "$devdummy" > /dev/null
+       check_err $?
+
+       if [ $ret -ne 0 ];then
+               echo "FAIL: neigh get"
+               return 1
+       fi
+
+       echo "PASS: neigh get"
+}
+
 kci_test_rtnl()
 {
        kci_add_dummy
@@ -979,6 +1099,8 @@ kci_test_rtnl()
        kci_test_macsec
        kci_test_ipsec
        kci_test_ipsec_offload
+       kci_test_fdb_get
+       kci_test_neigh_get
 
        kci_del_dummy
 }
index bea079e..2dc95fd 100755 (executable)
@@ -25,3 +25,13 @@ if [ $? -ne 0 ]; then
 else
        echo "[PASS]"
 fi
+
+echo "--------------------"
+echo "running txring_overwrite test"
+echo "--------------------"
+./in_netns.sh ./txring_overwrite
+if [ $? -ne 0 ]; then
+       echo "[FAIL]"
+else
+       echo "[PASS]"
+fi
diff --git a/tools/testing/selftests/net/test_vxlan_fdb_changelink.sh b/tools/testing/selftests/net/test_vxlan_fdb_changelink.sh
new file mode 100755 (executable)
index 0000000..2d442cd
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Check FDB default-remote handling across "ip link set".
+
+check_remotes()
+{
+       local what=$1; shift
+       local N=$(bridge fdb sh dev vx | grep 00:00:00:00:00:00 | wc -l)
+
+       echo -ne "expected two remotes after $what\t"
+       if [[ $N != 2 ]]; then
+               echo "[FAIL]"
+               EXIT_STATUS=1
+       else
+               echo "[ OK ]"
+       fi
+}
+
+ip link add name vx up type vxlan id 2000 dstport 4789
+bridge fdb ap dev vx 00:00:00:00:00:00 dst 192.0.2.20 self permanent
+bridge fdb ap dev vx 00:00:00:00:00:00 dst 192.0.2.30 self permanent
+check_remotes "fdb append"
+
+ip link set dev vx type vxlan remote 192.0.2.30
+check_remotes "link set"
+
+ip link del dev vx
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/test_vxlan_under_vrf.sh b/tools/testing/selftests/net/test_vxlan_under_vrf.sh
new file mode 100755 (executable)
index 0000000..09f9ed9
--- /dev/null
@@ -0,0 +1,129 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# This test is for checking VXLAN underlay in a non-default VRF.
+#
+# It simulates two hypervisors running a VM each using four network namespaces:
+# two for the HVs, two for the VMs.
+# A small VXLAN tunnel is made between the two hypervisors to have the two vms
+# in the same virtual L2:
+#
+# +-------------------+                                    +-------------------+
+# |                   |                                    |                   |
+# |    vm-1 netns     |                                    |    vm-2 netns     |
+# |                   |                                    |                   |
+# |  +-------------+  |                                    |  +-------------+  |
+# |  |   veth-hv   |  |                                    |  |   veth-hv   |  |
+# |  | 10.0.0.1/24 |  |                                    |  | 10.0.0.2/24 |  |
+# |  +-------------+  |                                    |  +-------------+  |
+# |        .          |                                    |         .         |
+# +-------------------+                                    +-------------------+
+#          .                                                         .
+#          .                                                         .
+#          .                                                         .
+# +-----------------------------------+   +------------------------------------+
+# |        .                          |   |                          .         |
+# |  +----------+                     |   |                     +----------+   |
+# |  | veth-tap |                     |   |                     | veth-tap |   |
+# |  +----+-----+                     |   |                     +----+-----+   |
+# |       |                           |   |                          |         |
+# |    +--+--+      +--------------+  |   |  +--------------+     +--+--+      |
+# |    | br0 |      | vrf-underlay |  |   |  | vrf-underlay |     | br0 |      |
+# |    +--+--+      +-------+------+  |   |  +------+-------+     +--+--+      |
+# |       |                 |         |   |         |                |         |
+# |   +---+----+    +-------+-------+ |   | +-------+-------+    +---+----+    |
+# |   | vxlan0 |....|     veth0     |.|...|.|     veth0     |....| vxlan0 |    |
+# |   +--------+    | 172.16.0.1/24 | |   | | 172.16.0.2/24 |    +--------+    |
+# |                 +---------------+ |   | +---------------+                  |
+# |                                   |   |                                    |
+# |             hv-1 netns            |   |           hv-2 netns               |
+# |                                   |   |                                    |
+# +-----------------------------------+   +------------------------------------+
+#
+# This tests both the connectivity between vm-1 and vm-2, and that the underlay
+# can be moved in and out of the vrf by unsetting and setting veth0's master.
+
+set -e
+
+cleanup() {
+    ip link del veth-hv-1 2>/dev/null || true
+    ip link del veth-tap 2>/dev/null || true
+
+    for ns in hv-1 hv-2 vm-1 vm-2; do
+        ip netns del $ns || true
+    done
+}
+
+# Clean start
+cleanup &> /dev/null
+
+[[ $1 == "clean" ]] && exit 0
+
+trap cleanup EXIT
+
+# Setup "Hypervisors" simulated with netns
+ip link add veth-hv-1 type veth peer name veth-hv-2
+setup-hv-networking() {
+    hv=$1
+
+    ip netns add hv-$hv
+    ip link set veth-hv-$hv netns hv-$hv
+    ip -netns hv-$hv link set veth-hv-$hv name veth0
+
+    ip -netns hv-$hv link add vrf-underlay type vrf table 1
+    ip -netns hv-$hv link set vrf-underlay up
+    ip -netns hv-$hv addr add 172.16.0.$hv/24 dev veth0
+    ip -netns hv-$hv link set veth0 up
+
+    ip -netns hv-$hv link add br0 type bridge
+    ip -netns hv-$hv link set br0 up
+
+    ip -netns hv-$hv link add vxlan0 type vxlan id 10 local 172.16.0.$hv dev veth0 dstport 4789
+    ip -netns hv-$hv link set vxlan0 master br0
+    ip -netns hv-$hv link set vxlan0 up
+}
+setup-hv-networking 1
+setup-hv-networking 2
+
+# Check connectivity between HVs by pinging hv-2 from hv-1
+echo -n "Checking HV connectivity                                           "
+ip netns exec hv-1 ping -c 1 -W 1 172.16.0.2 &> /dev/null || (echo "[FAIL]"; false)
+echo "[ OK ]"
+
+# Setups a "VM" simulated by a netns an a veth pair
+setup-vm() {
+    id=$1
+
+    ip netns add vm-$id
+    ip link add veth-tap type veth peer name veth-hv
+
+    ip link set veth-tap netns hv-$id
+    ip -netns hv-$id link set veth-tap master br0
+    ip -netns hv-$id link set veth-tap up
+
+    ip link set veth-hv netns vm-$id
+    ip -netns vm-$id addr add 10.0.0.$id/24 dev veth-hv
+    ip -netns vm-$id link set veth-hv up
+}
+setup-vm 1
+setup-vm 2
+
+# Setup VTEP routes to make ARP work
+bridge -netns hv-1 fdb add 00:00:00:00:00:00 dev vxlan0 dst 172.16.0.2 self permanent
+bridge -netns hv-2 fdb add 00:00:00:00:00:00 dev vxlan0 dst 172.16.0.1 self permanent
+
+echo -n "Check VM connectivity through VXLAN (underlay in the default VRF)  "
+ip netns exec vm-1 ping -c 1 -W 1 10.0.0.2 &> /dev/null || (echo "[FAIL]"; false)
+echo "[ OK ]"
+
+# Move the underlay to a non-default VRF
+ip -netns hv-1 link set veth0 vrf vrf-underlay
+ip -netns hv-1 link set veth0 down
+ip -netns hv-1 link set veth0 up
+ip -netns hv-2 link set veth0 vrf vrf-underlay
+ip -netns hv-2 link set veth0 down
+ip -netns hv-2 link set veth0 up
+
+echo -n "Check VM connectivity through VXLAN (underlay in a VRF)            "
+ip netns exec vm-1 ping -c 1 -W 1 10.0.0.2 &> /dev/null || (echo "[FAIL]"; false)
+echo "[ OK ]"
diff --git a/tools/testing/selftests/net/txring_overwrite.c b/tools/testing/selftests/net/txring_overwrite.c
new file mode 100644 (file)
index 0000000..fd8b1c6
--- /dev/null
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Verify that consecutive sends over packet tx_ring are mirrored
+ * with their original content intact.
+ */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/filter.h>
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+const int eth_off = TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
+const int cfg_frame_size = 1000;
+
+static void build_packet(void *buffer, size_t blen, char payload_char)
+{
+       struct udphdr *udph;
+       struct ethhdr *eth;
+       struct iphdr *iph;
+       size_t off = 0;
+
+       memset(buffer, 0, blen);
+
+       eth = buffer;
+       eth->h_proto = htons(ETH_P_IP);
+
+       off += sizeof(*eth);
+       iph = buffer + off;
+       iph->ttl        = 8;
+       iph->ihl        = 5;
+       iph->version    = 4;
+       iph->saddr      = htonl(INADDR_LOOPBACK);
+       iph->daddr      = htonl(INADDR_LOOPBACK + 1);
+       iph->protocol   = IPPROTO_UDP;
+       iph->tot_len    = htons(blen - off);
+       iph->check      = 0;
+
+       off += sizeof(*iph);
+       udph = buffer + off;
+       udph->dest      = htons(8000);
+       udph->source    = htons(8001);
+       udph->len       = htons(blen - off);
+       udph->check     = 0;
+
+       off += sizeof(*udph);
+       memset(buffer + off, payload_char, blen - off);
+}
+
+static int setup_rx(void)
+{
+       int fdr;
+
+       fdr = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
+       if (fdr == -1)
+               error(1, errno, "socket r");
+
+       return fdr;
+}
+
+static int setup_tx(char **ring)
+{
+       struct sockaddr_ll laddr = {};
+       struct tpacket_req req = {};
+       int fdt;
+
+       fdt = socket(PF_PACKET, SOCK_RAW, 0);
+       if (fdt == -1)
+               error(1, errno, "socket t");
+
+       laddr.sll_family = AF_PACKET;
+       laddr.sll_protocol = htons(0);
+       laddr.sll_ifindex = if_nametoindex("lo");
+       if (!laddr.sll_ifindex)
+               error(1, errno, "if_nametoindex");
+
+       if (bind(fdt, (void *)&laddr, sizeof(laddr)))
+               error(1, errno, "bind fdt");
+
+       req.tp_block_size = getpagesize();
+       req.tp_block_nr   = 1;
+       req.tp_frame_size = getpagesize();
+       req.tp_frame_nr   = 1;
+
+       if (setsockopt(fdt, SOL_PACKET, PACKET_TX_RING,
+                      (void *)&req, sizeof(req)))
+               error(1, errno, "setsockopt ring");
+
+       *ring = mmap(0, req.tp_block_size * req.tp_block_nr,
+                    PROT_READ | PROT_WRITE, MAP_SHARED, fdt, 0);
+       if (!*ring)
+               error(1, errno, "mmap");
+
+       return fdt;
+}
+
+static void send_pkt(int fdt, void *slot, char payload_char)
+{
+       struct tpacket_hdr *header = slot;
+       int ret;
+
+       while (header->tp_status != TP_STATUS_AVAILABLE)
+               usleep(1000);
+
+       build_packet(slot + eth_off, cfg_frame_size, payload_char);
+
+       header->tp_len = cfg_frame_size;
+       header->tp_status = TP_STATUS_SEND_REQUEST;
+
+       ret = sendto(fdt, NULL, 0, 0, NULL, 0);
+       if (ret == -1)
+               error(1, errno, "kick tx");
+}
+
+static int read_verify_pkt(int fdr, char payload_char)
+{
+       char buf[100];
+       int ret;
+
+       ret = read(fdr, buf, sizeof(buf));
+       if (ret != sizeof(buf))
+               error(1, errno, "read");
+
+       if (buf[60] != payload_char) {
+               printf("wrong pattern: 0x%x != 0x%x\n", buf[60], payload_char);
+               return 1;
+       }
+
+       printf("read: %c (0x%x)\n", buf[60], buf[60]);
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       const char payload_patterns[] = "ab";
+       char *ring;
+       int fdr, fdt, ret = 0;
+
+       fdr = setup_rx();
+       fdt = setup_tx(&ring);
+
+       send_pkt(fdt, ring, payload_patterns[0]);
+       send_pkt(fdt, ring, payload_patterns[1]);
+
+       ret |= read_verify_pkt(fdr, payload_patterns[0]);
+       ret |= read_verify_pkt(fdr, payload_patterns[1]);
+
+       if (close(fdt))
+               error(1, errno, "close t");
+       if (close(fdr))
+               error(1, errno, "close r");
+
+       return ret;
+}
index e94ef80..aeac53a 100755 (executable)
@@ -91,6 +91,28 @@ run_one_nat() {
        wait $(jobs -p)
 }
 
+run_one_2sock() {
+       # use 'rx' as separator between sender args and receiver args
+       local -r all="$@"
+       local -r tx_args=${all%rx*}
+       local -r rx_args=${all#*rx}
+
+       cfg_veth
+
+       ip netns exec "${PEER_NS}" ./udpgso_bench_rx ${rx_args} -p 12345 &
+       ip netns exec "${PEER_NS}" ./udpgso_bench_rx ${rx_args} && \
+               echo "ok" || \
+               echo "failed" &
+
+       # Hack: let bg programs complete the startup
+       sleep 0.1
+       ./udpgso_bench_tx ${tx_args} -p 12345
+       sleep 0.1
+       # first UDP GSO socket should be closed at this point
+       ./udpgso_bench_tx ${tx_args}
+       wait $(jobs -p)
+}
+
 run_nat_test() {
        local -r args=$@
 
@@ -98,6 +120,13 @@ run_nat_test() {
        ./in_netns.sh $0 __subprocess_nat $2 rx -r $3
 }
 
+run_2sock_test() {
+       local -r args=$@
+
+       printf " %-40s" "$1"
+       ./in_netns.sh $0 __subprocess_2sock $2 rx -G -r $3
+}
+
 run_all() {
        local -r core_args="-l 4"
        local -r ipv4_args="${core_args} -4 -D 192.168.1.1"
@@ -120,6 +149,7 @@ run_all() {
        run_test "GRO with custom segment size cmsg" "${ipv4_args} -M 1 -s 14720 -S 500 " "-4 -n 1 -l 14720 -S 500"
 
        run_nat_test "bad GRO lookup" "${ipv4_args} -M 1 -s 14720 -S 0" "-n 10 -l 1472"
+       run_2sock_test "multiple GRO socks" "${ipv4_args} -M 1 -s 14720 -S 0 " "-4 -n 1 -l 14720 -S 1472"
 
        echo "ipv6"
        run_test "no GRO" "${ipv6_args} -M 10 -s 1400" "-n 10 -l 1400"
@@ -130,6 +160,7 @@ run_all() {
        run_test "GRO with custom segment size cmsg" "${ipv6_args} -M 1 -s 14520 -S 500" "-n 1 -l 14520 -S 500"
 
        run_nat_test "bad GRO lookup" "${ipv6_args} -M 1 -s 14520 -S 0" "-n 10 -l 1452"
+       run_2sock_test "multiple GRO socks" "${ipv6_args} -M 1 -s 14520 -S 0 " "-n 1 -l 14520 -S 1452"
 }
 
 if [ ! -f ../bpf/xdp_dummy.o ]; then
@@ -145,4 +176,7 @@ elif [[ $1 == "__subprocess" ]]; then
 elif [[ $1 == "__subprocess_nat" ]]; then
        shift
        run_one_nat $@
+elif [[ $1 == "__subprocess_2sock" ]]; then
+       shift
+       run_one_2sock $@
 fi
index 0f06286..5670a9f 100755 (executable)
@@ -35,6 +35,9 @@ run_udp() {
 
        echo "udp gso"
        run_in_netns ${args} -S 0
+
+       echo "udp gso zerocopy"
+       run_in_netns ${args} -S 0 -z
 }
 
 run_tcp() {
diff --git a/tools/testing/selftests/net/xfrm_policy.sh b/tools/testing/selftests/net/xfrm_policy.sh
new file mode 100755 (executable)
index 0000000..8db35b9
--- /dev/null
@@ -0,0 +1,302 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Check xfrm policy resolution.  Topology:
+#
+# 1.2   1.1   3.1  3.10    2.1   2.2
+# eth1  eth1 veth0 veth0 eth1   eth1
+# ns1 ---- ns3 ----- ns4 ---- ns2
+#
+# ns3 and ns4 are connected via ipsec tunnel.
+# pings from ns1 to ns2 (and vice versa) are supposed to work like this:
+# ns1: ping 10.0.2.2: passes via ipsec tunnel.
+# ns2: ping 10.0.1.2: passes via ipsec tunnel.
+
+# ns1: ping 10.0.1.253: passes via ipsec tunnel (direct policy)
+# ns2: ping 10.0.2.253: passes via ipsec tunnel (direct policy)
+#
+# ns1: ping 10.0.2.254: does NOT pass via ipsec tunnel (exception)
+# ns2: ping 10.0.1.254: does NOT pass via ipsec tunnel (exception)
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+ret=0
+policy_checks_ok=1
+
+KEY_SHA=0xdeadbeef1234567890abcdefabcdefabcdefabcd
+KEY_AES=0x0123456789abcdef0123456789012345
+SPI1=0x1
+SPI2=0x2
+
+do_esp() {
+    local ns=$1
+    local me=$2
+    local remote=$3
+    local lnet=$4
+    local rnet=$5
+    local spi_out=$6
+    local spi_in=$7
+
+    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_get_check() {
+    local ns=$1
+    local lnet=$2
+    local rnet=$3
+
+    ip -net $ns xfrm policy get src $lnet dst $rnet dir out > /dev/null
+    if [ $? -ne 0 ] && [ $policy_checks_ok -eq 1 ] ;then
+        policy_checks_ok=0
+        echo "FAIL: ip -net $ns xfrm policy get src $lnet dst $rnet dir out"
+        ret=1
+    fi
+
+    ip -net $ns xfrm policy get src $rnet dst $lnet dir fwd > /dev/null
+    if [ $? -ne 0 ] && [ $policy_checks_ok -eq 1 ] ;then
+        policy_checks_ok=0
+        echo "FAIL: ip -net $ns xfrm policy get src $rnet dst $lnet dir fwd"
+        ret=1
+    fi
+}
+
+do_exception() {
+    local ns=$1
+    local me=$2
+    local remote=$3
+    local encryptip=$4
+    local plain=$5
+
+    # network $plain passes without tunnel
+    ip -net $ns xfrm policy add dst $plain dir out priority 10 action allow
+
+    # direct policy for $encryptip, use tunnel, higher prio takes precedence
+    ip -net $ns xfrm policy add dst $encryptip dir out tmpl src $me dst $remote proto esp mode tunnel priority 1 action allow
+}
+
+# policies that are not supposed to match any packets generated in this test.
+do_dummies4() {
+    local ns=$1
+
+    for i in $(seq 10 16);do
+      # dummy policy with wildcard src/dst.
+      echo netns exec $ns ip xfrm policy add src 0.0.0.0/0 dst 10.$i.99.0/30 dir out action block
+      echo netns exec $ns ip xfrm policy add src 10.$i.99.0/30 dst 0.0.0.0/0 dir out action block
+      for j in $(seq 32 64);do
+        echo netns exec $ns ip xfrm policy add src 10.$i.1.0/30 dst 10.$i.$j.0/30 dir out action block
+        # silly, as it encompasses the one above too, but its allowed:
+        echo netns exec $ns ip xfrm policy add src 10.$i.1.0/29 dst 10.$i.$j.0/29 dir out action block
+        # and yet again, even more broad one.
+        echo netns exec $ns ip xfrm policy add src 10.$i.1.0/24 dst 10.$i.$j.0/24 dir out action block
+        echo netns exec $ns ip xfrm policy add src 10.$i.$j.0/24 dst 10.$i.1.0/24 dir fwd action block
+      done
+    done | ip -batch /dev/stdin
+}
+
+do_dummies6() {
+    local ns=$1
+
+    for i in $(seq 10 16);do
+      for j in $(seq 32 64);do
+       echo netns exec $ns ip xfrm policy add src dead:$i::/64 dst dead:$i:$j::/64 dir out action block
+       echo netns exec $ns ip xfrm policy add src dead:$i:$j::/64 dst dead:$i::/24 dir fwd action block
+      done
+    done | ip -batch /dev/stdin
+}
+
+check_ipt_policy_count()
+{
+       ns=$1
+
+       ip netns exec $ns iptables-save -c |grep policy | ( read c rest
+               ip netns exec $ns iptables -Z
+               if [ x"$c" = x'[0:0]' ]; then
+                       exit 0
+               elif [ x"$c" = x ]; then
+                       echo "ERROR: No counters"
+                       ret=1
+                       exit 111
+               else
+                       exit 1
+               fi
+       )
+}
+
+check_xfrm() {
+       # 0: iptables -m policy rule count == 0
+       # 1: iptables -m policy rule count != 0
+       rval=$1
+       ip=$2
+       lret=0
+
+       ip netns exec ns1 ping -q -c 1 10.0.2.$ip > /dev/null
+
+       check_ipt_policy_count ns3
+       if [ $? -ne $rval ] ; then
+               lret=1
+       fi
+       check_ipt_policy_count ns4
+       if [ $? -ne $rval ] ; then
+               lret=1
+       fi
+
+       ip netns exec ns2 ping -q -c 1 10.0.1.$ip > /dev/null
+
+       check_ipt_policy_count ns3
+       if [ $? -ne $rval ] ; then
+               lret=1
+       fi
+       check_ipt_policy_count ns4
+       if [ $? -ne $rval ] ; then
+               lret=1
+       fi
+
+       return $lret
+}
+
+#check for needed privileges
+if [ "$(id -u)" -ne 0 ];then
+       echo "SKIP: Need root privileges"
+       exit $ksft_skip
+fi
+
+ip -Version 2>/dev/null >/dev/null
+if [ $? -ne 0 ];then
+       echo "SKIP: Could not run test without the ip tool"
+       exit $ksft_skip
+fi
+
+# needed to check if policy lookup got valid ipsec result
+iptables --version 2>/dev/null >/dev/null
+if [ $? -ne 0 ];then
+       echo "SKIP: Could not run test without iptables tool"
+       exit $ksft_skip
+fi
+
+for i in 1 2 3 4; do
+    ip netns add ns$i
+    ip -net ns$i link set lo up
+done
+
+DEV=veth0
+ip link add $DEV netns ns1 type veth peer name eth1 netns ns3
+ip link add $DEV netns ns2 type veth peer name eth1 netns ns4
+
+ip link add $DEV netns ns3 type veth peer name veth0 netns ns4
+
+DEV=veth0
+for i in 1 2; do
+    ip -net ns$i link set $DEV up
+    ip -net ns$i addr add 10.0.$i.2/24 dev $DEV
+    ip -net ns$i addr add dead:$i::2/64 dev $DEV
+
+    ip -net ns$i addr add 10.0.$i.253 dev $DEV
+    ip -net ns$i addr add 10.0.$i.254 dev $DEV
+    ip -net ns$i addr add dead:$i::fd dev $DEV
+    ip -net ns$i addr add dead:$i::fe dev $DEV
+done
+
+for i in 3 4; do
+ip -net ns$i link set eth1 up
+ip -net ns$i link set veth0 up
+done
+
+ip -net ns1 route add default via 10.0.1.1
+ip -net ns2 route add default via 10.0.2.1
+
+ip -net ns3 addr add 10.0.1.1/24 dev eth1
+ip -net ns3 addr add 10.0.3.1/24 dev veth0
+ip -net ns3 addr add 2001:1::1/64 dev eth1
+ip -net ns3 addr add 2001:3::1/64 dev veth0
+
+ip -net ns3 route add default via 10.0.3.10
+
+ip -net ns4 addr add 10.0.2.1/24 dev eth1
+ip -net ns4 addr add 10.0.3.10/24 dev veth0
+ip -net ns4 addr add 2001:2::1/64 dev eth1
+ip -net ns4 addr add 2001:3::10/64 dev veth0
+ip -net ns4 route add default via 10.0.3.1
+
+for j in 4 6; do
+       for i in 3 4;do
+               ip netns exec ns$i sysctl net.ipv$j.conf.eth1.forwarding=1 > /dev/null
+               ip netns exec ns$i sysctl net.ipv$j.conf.veth0.forwarding=1 > /dev/null
+       done
+done
+
+# abuse iptables rule counter to check if ping matches a policy
+ip netns exec ns3 iptables -p icmp -A FORWARD -m policy --dir out --pol ipsec
+ip netns exec ns4 iptables -p icmp -A FORWARD -m policy --dir out --pol ipsec
+if [ $? -ne 0 ];then
+       echo "SKIP: Could not insert iptables rule"
+       for i in 1 2 3 4;do ip netns del ns$i;done
+       exit $ksft_skip
+fi
+
+#          localip  remoteip  localnet    remotenet
+do_esp ns3 10.0.3.1 10.0.3.10 10.0.1.0/24 10.0.2.0/24 $SPI1 $SPI2
+do_esp ns3 dead:3::1 dead:3::10 dead:1::/64 dead:2::/64 $SPI1 $SPI2
+do_esp ns4 10.0.3.10 10.0.3.1 10.0.2.0/24 10.0.1.0/24 $SPI2 $SPI1
+do_esp ns4 dead:3::10 dead:3::1 dead:2::/64 dead:1::/64 $SPI2 $SPI1
+
+do_dummies4 ns3
+do_dummies6 ns4
+
+do_esp_policy_get_check ns3 10.0.1.0/24 10.0.2.0/24
+do_esp_policy_get_check ns4 10.0.2.0/24 10.0.1.0/24
+do_esp_policy_get_check ns3 dead:1::/64 dead:2::/64
+do_esp_policy_get_check ns4 dead:2::/64 dead:1::/64
+
+# ping to .254 should use ipsec, exception is not installed.
+check_xfrm 1 254
+if [ $? -ne 0 ]; then
+       echo "FAIL: expected ping to .254 to use ipsec tunnel"
+       ret=1
+else
+       echo "PASS: policy before exception matches"
+fi
+
+# installs exceptions
+#                localip  remoteip   encryptdst  plaindst
+do_exception ns3 10.0.3.1 10.0.3.10 10.0.2.253 10.0.2.240/28
+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
+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
+
+# ping to .2 should use ipsec.
+check_xfrm 1 2
+if [ $? -ne 0 ]; then
+       echo "FAIL: expected ping to .2 to use ipsec tunnel"
+       ret=1
+else
+       echo "PASS: policy matches"
+fi
+
+for i in 1 2 3 4;do ip netns del ns$i;done
+
+exit $ret
diff --git a/tools/testing/selftests/netfilter/Makefile b/tools/testing/selftests/netfilter/Makefile
new file mode 100644 (file)
index 0000000..47ed6ce
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for netfilter selftests
+
+TEST_PROGS := nft_trans_stress.sh
+
+include ../lib.mk
diff --git a/tools/testing/selftests/netfilter/config b/tools/testing/selftests/netfilter/config
new file mode 100644 (file)
index 0000000..1017313
--- /dev/null
@@ -0,0 +1,2 @@
+CONFIG_NET_NS=y
+NF_TABLES_INET=y
diff --git a/tools/testing/selftests/netfilter/nft_trans_stress.sh b/tools/testing/selftests/netfilter/nft_trans_stress.sh
new file mode 100755 (executable)
index 0000000..f1affd1
--- /dev/null
@@ -0,0 +1,78 @@
+#!/bin/bash
+#
+# This test is for stress-testing the nf_tables config plane path vs.
+# packet path processing: Make sure we never release rules that are
+# still visible to other cpus.
+#
+# set -e
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+testns=testns1
+tables="foo bar baz quux"
+
+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
+
+tmp=$(mktemp)
+
+for table in $tables; do
+       echo add table inet "$table" >> "$tmp"
+       echo flush table inet "$table" >> "$tmp"
+
+       echo "add chain inet $table INPUT { type filter hook input priority 0; }" >> "$tmp"
+       echo "add chain inet $table OUTPUT { type filter hook output priority 0; }" >> "$tmp"
+       for c in $(seq 1 400); do
+               chain=$(printf "chain%03u" "$c")
+               echo "add chain inet $table $chain" >> "$tmp"
+       done
+
+       for c in $(seq 1 400); do
+               chain=$(printf "chain%03u" "$c")
+               for BASE in INPUT OUTPUT; do
+                       echo "add rule inet $table $BASE counter jump $chain" >> "$tmp"
+               done
+               echo "add rule inet $table $chain counter return" >> "$tmp"
+       done
+done
+
+ip netns add "$testns"
+ip -netns "$testns" link set lo up
+
+lscpu | grep ^CPU\(s\): | ( read cpu cpunum ;
+cpunum=$((cpunum-1))
+for i in $(seq 0 $cpunum);do
+       mask=$(printf 0x%x $((1<<$i)))
+        ip netns exec "$testns" taskset $mask ping -4 127.0.0.1 -fq > /dev/null &
+        ip netns exec "$testns" taskset $mask ping -6 ::1 -fq > /dev/null &
+done)
+
+sleep 1
+
+for i in $(seq 1 10) ; do ip netns exec "$testns" nft -f "$tmp" & done
+
+for table in $tables;do
+       randsleep=$((RANDOM%10))
+       sleep $randsleep
+       ip netns exec "$testns" nft delete table inet $table 2>/dev/null
+done
+
+randsleep=$((RANDOM%10))
+sleep $randsleep
+
+pkill -9 ping
+
+wait
+
+rm -f "$tmp"
+ip netns del "$testns"
index 14cfcf0..6c17a93 100644 (file)
@@ -1,7 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 CFLAGS += -I../../../../../usr/include
 
-TEST_PROGS := hwtstamp_config rxtimestamp timestamping txtimestamp
+TEST_GEN_FILES := hwtstamp_config rxtimestamp timestamping txtimestamp
+TEST_PROGS := txtimestamp.sh
 
 all: $(TEST_PROGS)
 
@@ -9,4 +10,4 @@ top_srcdir = ../../../../..
 include ../../lib.mk
 
 clean:
-       rm -fr $(TEST_PROGS)
+       rm -fr $(TEST_GEN_FILES)
diff --git a/tools/testing/selftests/networking/timestamping/config b/tools/testing/selftests/networking/timestamping/config
new file mode 100644 (file)
index 0000000..a13e316
--- /dev/null
@@ -0,0 +1,2 @@
+CONFIG_IFB=y
+CONFIG_NET_SCH_NETEM=y
index 81a98a2..2e563d1 100644 (file)
@@ -39,6 +39,7 @@
 #include <inttypes.h>
 #include <linux/errqueue.h>
 #include <linux/if_ether.h>
+#include <linux/ipv6.h>
 #include <linux/net_tstamp.h>
 #include <netdb.h>
 #include <net/if.h>
@@ -69,15 +70,67 @@ static int do_ipv4 = 1;
 static int do_ipv6 = 1;
 static int cfg_payload_len = 10;
 static int cfg_poll_timeout = 100;
+static int cfg_delay_snd;
+static int cfg_delay_ack;
 static bool cfg_show_payload;
 static bool cfg_do_pktinfo;
 static bool cfg_loop_nodata;
 static bool cfg_no_delay;
+static bool cfg_use_cmsg;
+static bool cfg_use_pf_packet;
+static bool cfg_do_listen;
 static uint16_t dest_port = 9000;
 
 static struct sockaddr_in daddr;
 static struct sockaddr_in6 daddr6;
-static struct timespec ts_prev;
+static struct timespec ts_usr;
+
+static int saved_tskey = -1;
+static int saved_tskey_type = -1;
+
+static bool test_failed;
+
+static int64_t timespec_to_us64(struct timespec *ts)
+{
+       return ts->tv_sec * 1000 * 1000 + ts->tv_nsec / 1000;
+}
+
+static void validate_key(int tskey, int tstype)
+{
+       int stepsize;
+
+       /* compare key for each subsequent request
+        * must only test for one type, the first one requested
+        */
+       if (saved_tskey == -1)
+               saved_tskey_type = tstype;
+       else if (saved_tskey_type != tstype)
+               return;
+
+       stepsize = cfg_proto == SOCK_STREAM ? cfg_payload_len : 1;
+       if (tskey != saved_tskey + stepsize) {
+               fprintf(stderr, "ERROR: key %d, expected %d\n",
+                               tskey, saved_tskey + stepsize);
+               test_failed = true;
+       }
+
+       saved_tskey = tskey;
+}
+
+static void validate_timestamp(struct timespec *cur, int min_delay)
+{
+       int max_delay = min_delay + 500 /* processing time upper bound */;
+       int64_t cur64, start64;
+
+       cur64 = timespec_to_us64(cur);
+       start64 = timespec_to_us64(&ts_usr);
+
+       if (cur64 < start64 + min_delay || cur64 > start64 + max_delay) {
+               fprintf(stderr, "ERROR: delay %lu expected between %d and %d\n",
+                               cur64 - start64, min_delay, max_delay);
+               test_failed = true;
+       }
+}
 
 static void __print_timestamp(const char *name, struct timespec *cur,
                              uint32_t key, int payload_len)
@@ -89,32 +142,19 @@ static void __print_timestamp(const char *name, struct timespec *cur,
                        name, cur->tv_sec, cur->tv_nsec / 1000,
                        key, payload_len);
 
-       if ((ts_prev.tv_sec | ts_prev.tv_nsec)) {
-               int64_t cur_ms, prev_ms;
-
-               cur_ms = (long) cur->tv_sec * 1000 * 1000;
-               cur_ms += cur->tv_nsec / 1000;
-
-               prev_ms = (long) ts_prev.tv_sec * 1000 * 1000;
-               prev_ms += ts_prev.tv_nsec / 1000;
-
-               fprintf(stderr, "  (%+" PRId64 " us)", cur_ms - prev_ms);
-       }
+       if (cur != &ts_usr)
+               fprintf(stderr, "  (USR %+" PRId64 " us)",
+                       timespec_to_us64(cur) - timespec_to_us64(&ts_usr));
 
-       ts_prev = *cur;
        fprintf(stderr, "\n");
 }
 
 static void print_timestamp_usr(void)
 {
-       struct timespec ts;
-       struct timeval tv;      /* avoid dependency on -lrt */
-
-       gettimeofday(&tv, NULL);
-       ts.tv_sec = tv.tv_sec;
-       ts.tv_nsec = tv.tv_usec * 1000;
+       if (clock_gettime(CLOCK_REALTIME, &ts_usr))
+               error(1, errno, "clock_gettime");
 
-       __print_timestamp("  USR", &ts, 0, 0);
+       __print_timestamp("  USR", &ts_usr, 0, 0);
 }
 
 static void print_timestamp(struct scm_timestamping *tss, int tstype,
@@ -122,15 +162,20 @@ static void print_timestamp(struct scm_timestamping *tss, int tstype,
 {
        const char *tsname;
 
+       validate_key(tskey, tstype);
+
        switch (tstype) {
        case SCM_TSTAMP_SCHED:
                tsname = "  ENQ";
+               validate_timestamp(&tss->ts[0], 0);
                break;
        case SCM_TSTAMP_SND:
                tsname = "  SND";
+               validate_timestamp(&tss->ts[0], cfg_delay_snd);
                break;
        case SCM_TSTAMP_ACK:
                tsname = "  ACK";
+               validate_timestamp(&tss->ts[0], cfg_delay_ack);
                break;
        default:
                error(1, 0, "unknown timestamp type: %u",
@@ -194,7 +239,9 @@ static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
                } else if ((cm->cmsg_level == SOL_IP &&
                            cm->cmsg_type == IP_RECVERR) ||
                           (cm->cmsg_level == SOL_IPV6 &&
-                           cm->cmsg_type == IPV6_RECVERR)) {
+                           cm->cmsg_type == IPV6_RECVERR) ||
+                          (cm->cmsg_level = SOL_PACKET &&
+                           cm->cmsg_type == PACKET_TX_TIMESTAMP)) {
                        serr = (void *) CMSG_DATA(cm);
                        if (serr->ee_errno != ENOMSG ||
                            serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
@@ -269,32 +316,124 @@ static int recv_errmsg(int fd)
        return ret == -1;
 }
 
-static void do_test(int family, unsigned int opt)
+static uint16_t get_ip_csum(const uint16_t *start, int num_words,
+                           unsigned long sum)
+{
+       int i;
+
+       for (i = 0; i < num_words; i++)
+               sum += start[i];
+
+       while (sum >> 16)
+               sum = (sum & 0xFFFF) + (sum >> 16);
+
+       return ~sum;
+}
+
+static uint16_t get_udp_csum(const struct udphdr *udph, int alen)
+{
+       unsigned long pseudo_sum, csum_len;
+       const void *csum_start = udph;
+
+       pseudo_sum = htons(IPPROTO_UDP);
+       pseudo_sum += udph->len;
+
+       /* checksum ip(v6) addresses + udp header + payload */
+       csum_start -= alen * 2;
+       csum_len = ntohs(udph->len) + alen * 2;
+
+       return get_ip_csum(csum_start, csum_len >> 1, pseudo_sum);
+}
+
+static int fill_header_ipv4(void *p)
+{
+       struct iphdr *iph = p;
+
+       memset(iph, 0, sizeof(*iph));
+
+       iph->ihl        = 5;
+       iph->version    = 4;
+       iph->ttl        = 2;
+       iph->saddr      = daddr.sin_addr.s_addr;        /* set for udp csum calc */
+       iph->daddr      = daddr.sin_addr.s_addr;
+       iph->protocol   = IPPROTO_UDP;
+
+       /* kernel writes saddr, csum, len */
+
+       return sizeof(*iph);
+}
+
+static int fill_header_ipv6(void *p)
+{
+       struct ipv6hdr *ip6h = p;
+
+       memset(ip6h, 0, sizeof(*ip6h));
+
+       ip6h->version           = 6;
+       ip6h->payload_len       = htons(sizeof(struct udphdr) + cfg_payload_len);
+       ip6h->nexthdr           = IPPROTO_UDP;
+       ip6h->hop_limit         = 64;
+
+       ip6h->saddr             = daddr6.sin6_addr;
+       ip6h->daddr             = daddr6.sin6_addr;
+
+       /* kernel does not write saddr in case of ipv6 */
+
+       return sizeof(*ip6h);
+}
+
+static void fill_header_udp(void *p, bool is_ipv4)
 {
+       struct udphdr *udph = p;
+
+       udph->source = ntohs(dest_port + 1);    /* spoof */
+       udph->dest   = ntohs(dest_port);
+       udph->len    = ntohs(sizeof(*udph) + cfg_payload_len);
+       udph->check  = 0;
+
+       udph->check  = get_udp_csum(udph, is_ipv4 ? sizeof(struct in_addr) :
+                                                   sizeof(struct in6_addr));
+}
+
+static void do_test(int family, unsigned int report_opt)
+{
+       char control[CMSG_SPACE(sizeof(uint32_t))];
+       struct sockaddr_ll laddr;
+       unsigned int sock_opt;
+       struct cmsghdr *cmsg;
+       struct msghdr msg;
+       struct iovec iov;
        char *buf;
        int fd, i, val = 1, total_len;
 
-       if (family == AF_INET6 && cfg_proto != SOCK_STREAM) {
-               /* due to lack of checksum generation code */
-               fprintf(stderr, "test: skipping datagram over IPv6\n");
-               return;
-       }
-
        total_len = cfg_payload_len;
-       if (cfg_proto == SOCK_RAW) {
+       if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) {
                total_len += sizeof(struct udphdr);
-               if (cfg_ipproto == IPPROTO_RAW)
-                       total_len += sizeof(struct iphdr);
+               if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW)
+                       if (family == PF_INET)
+                               total_len += sizeof(struct iphdr);
+                       else
+                               total_len += sizeof(struct ipv6hdr);
+
+               /* special case, only rawv6_sendmsg:
+                * pass proto in sin6_port if not connected
+                * also see ANK comment in net/ipv4/raw.c
+                */
+               daddr6.sin6_port = htons(cfg_ipproto);
        }
 
        buf = malloc(total_len);
        if (!buf)
                error(1, 0, "malloc");
 
-       fd = socket(family, cfg_proto, cfg_ipproto);
+       fd = socket(cfg_use_pf_packet ? PF_PACKET : family,
+                   cfg_proto, cfg_ipproto);
        if (fd < 0)
                error(1, errno, "socket");
 
+       /* reset expected key on each new socket */
+       saved_tskey = -1;
+
        if (cfg_proto == SOCK_STREAM) {
                if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
                               (char*) &val, sizeof(val)))
@@ -321,54 +460,80 @@ static void do_test(int family, unsigned int opt)
                }
        }
 
-       opt |= SOF_TIMESTAMPING_SOFTWARE |
-              SOF_TIMESTAMPING_OPT_CMSG |
-              SOF_TIMESTAMPING_OPT_ID;
+       sock_opt = SOF_TIMESTAMPING_SOFTWARE |
+                  SOF_TIMESTAMPING_OPT_CMSG |
+                  SOF_TIMESTAMPING_OPT_ID;
+
+       if (!cfg_use_cmsg)
+               sock_opt |= report_opt;
+
        if (cfg_loop_nodata)
-               opt |= SOF_TIMESTAMPING_OPT_TSONLY;
+               sock_opt |= SOF_TIMESTAMPING_OPT_TSONLY;
 
        if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
-                      (char *) &opt, sizeof(opt)))
+                      (char *) &sock_opt, sizeof(sock_opt)))
                error(1, 0, "setsockopt timestamping");
 
        for (i = 0; i < cfg_num_pkts; i++) {
-               memset(&ts_prev, 0, sizeof(ts_prev));
+               memset(&msg, 0, sizeof(msg));
                memset(buf, 'a' + i, total_len);
 
-               if (cfg_proto == SOCK_RAW) {
-                       struct udphdr *udph;
+               if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) {
                        int off = 0;
 
-                       if (cfg_ipproto == IPPROTO_RAW) {
-                               struct iphdr *iph = (void *) buf;
-
-                               memset(iph, 0, sizeof(*iph));
-                               iph->ihl      = 5;
-                               iph->version  = 4;
-                               iph->ttl      = 2;
-                               iph->daddr    = daddr.sin_addr.s_addr;
-                               iph->protocol = IPPROTO_UDP;
-                               /* kernel writes saddr, csum, len */
-
-                               off = sizeof(*iph);
+                       if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW) {
+                               if (family == PF_INET)
+                                       off = fill_header_ipv4(buf);
+                               else
+                                       off = fill_header_ipv6(buf);
                        }
 
-                       udph = (void *) buf + off;
-                       udph->source = ntohs(9000);     /* random spoof */
-                       udph->dest   = ntohs(dest_port);
-                       udph->len    = ntohs(sizeof(*udph) + cfg_payload_len);
-                       udph->check  = 0;       /* not allowed for IPv6 */
+                       fill_header_udp(buf + off, family == PF_INET);
                }
 
                print_timestamp_usr();
+
+               iov.iov_base = buf;
+               iov.iov_len = total_len;
+
                if (cfg_proto != SOCK_STREAM) {
-                       if (family == PF_INET)
-                               val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr));
-                       else
-                               val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6));
-               } else {
-                       val = send(fd, buf, cfg_payload_len, 0);
+                       if (cfg_use_pf_packet) {
+                               memset(&laddr, 0, sizeof(laddr));
+
+                               laddr.sll_family        = AF_PACKET;
+                               laddr.sll_ifindex       = 1;
+                               laddr.sll_protocol      = htons(family == AF_INET ? ETH_P_IP : ETH_P_IPV6);
+                               laddr.sll_halen         = ETH_ALEN;
+
+                               msg.msg_name = (void *)&laddr;
+                               msg.msg_namelen = sizeof(laddr);
+                       } else if (family == PF_INET) {
+                               msg.msg_name = (void *)&daddr;
+                               msg.msg_namelen = sizeof(daddr);
+                       } else {
+                               msg.msg_name = (void *)&daddr6;
+                               msg.msg_namelen = sizeof(daddr6);
+                       }
+               }
+
+               msg.msg_iov = &iov;
+               msg.msg_iovlen = 1;
+
+               if (cfg_use_cmsg) {
+                       memset(control, 0, sizeof(control));
+
+                       msg.msg_control = control;
+                       msg.msg_controllen = sizeof(control);
+
+                       cmsg = CMSG_FIRSTHDR(&msg);
+                       cmsg->cmsg_level = SOL_SOCKET;
+                       cmsg->cmsg_type = SO_TIMESTAMPING;
+                       cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
+
+                       *((uint32_t *) CMSG_DATA(cmsg)) = report_opt;
                }
+
+               val = sendmsg(fd, &msg, 0);
                if (val != total_len)
                        error(1, errno, "send");
 
@@ -385,7 +550,7 @@ static void do_test(int family, unsigned int opt)
                error(1, errno, "close");
 
        free(buf);
-       usleep(400 * 1000);
+       usleep(100 * 1000);
 }
 
 static void __attribute__((noreturn)) usage(const char *filepath)
@@ -396,15 +561,20 @@ static void __attribute__((noreturn)) usage(const char *filepath)
                        "  -6:   only IPv6\n"
                        "  -h:   show this message\n"
                        "  -c N: number of packets for each test\n"
+                       "  -C:   use cmsg to set tstamp recording options\n"
                        "  -D:   no delay between packets\n"
                        "  -F:   poll() waits forever for an event\n"
                        "  -I:   request PKTINFO\n"
                        "  -l N: send N bytes at a time\n"
+                       "  -L    listen on hostname and port\n"
                        "  -n:   set no-payload option\n"
+                       "  -p N: connect to port N\n"
+                       "  -P:   use PF_PACKET\n"
                        "  -r:   use raw\n"
                        "  -R:   use raw (IP_HDRINCL)\n"
-                       "  -p N: connect to port N\n"
                        "  -u:   use udp\n"
+                       "  -v:   validate SND delay (usec)\n"
+                       "  -V:   validate ACK delay (usec)\n"
                        "  -x:   show payload (up to 70 bytes)\n",
                        filepath);
        exit(1);
@@ -413,9 +583,9 @@ static void __attribute__((noreturn)) usage(const char *filepath)
 static void parse_opt(int argc, char **argv)
 {
        int proto_count = 0;
-       char c;
+       int c;
 
-       while ((c = getopt(argc, argv, "46c:DFhIl:np:rRux")) != -1) {
+       while ((c = getopt(argc, argv, "46c:CDFhIl:Lnp:PrRuv:V:x")) != -1) {
                switch (c) {
                case '4':
                        do_ipv6 = 0;
@@ -426,6 +596,9 @@ static void parse_opt(int argc, char **argv)
                case 'c':
                        cfg_num_pkts = strtoul(optarg, NULL, 10);
                        break;
+               case 'C':
+                       cfg_use_cmsg = true;
+                       break;
                case 'D':
                        cfg_no_delay = true;
                        break;
@@ -435,9 +608,24 @@ static void parse_opt(int argc, char **argv)
                case 'I':
                        cfg_do_pktinfo = true;
                        break;
+               case 'l':
+                       cfg_payload_len = strtoul(optarg, NULL, 10);
+                       break;
+               case 'L':
+                       cfg_do_listen = true;
+                       break;
                case 'n':
                        cfg_loop_nodata = true;
                        break;
+               case 'p':
+                       dest_port = strtoul(optarg, NULL, 10);
+                       break;
+               case 'P':
+                       proto_count++;
+                       cfg_use_pf_packet = true;
+                       cfg_proto = SOCK_DGRAM;
+                       cfg_ipproto = 0;
+                       break;
                case 'r':
                        proto_count++;
                        cfg_proto = SOCK_RAW;
@@ -453,11 +641,11 @@ static void parse_opt(int argc, char **argv)
                        cfg_proto = SOCK_DGRAM;
                        cfg_ipproto = IPPROTO_UDP;
                        break;
-               case 'l':
-                       cfg_payload_len = strtoul(optarg, NULL, 10);
+               case 'v':
+                       cfg_delay_snd = strtoul(optarg, NULL, 10);
                        break;
-               case 'p':
-                       dest_port = strtoul(optarg, NULL, 10);
+               case 'V':
+                       cfg_delay_ack = strtoul(optarg, NULL, 10);
                        break;
                case 'x':
                        cfg_show_payload = true;
@@ -475,7 +663,9 @@ static void parse_opt(int argc, char **argv)
        if (!do_ipv4 && !do_ipv6)
                error(1, 0, "pass -4 or -6, not both");
        if (proto_count > 1)
-               error(1, 0, "pass -r, -R or -u, not multiple");
+               error(1, 0, "pass -P, -r, -R or -u, not multiple");
+       if (cfg_do_pktinfo && cfg_use_pf_packet)
+               error(1, 0, "cannot ask for pktinfo over pf_packet");
 
        if (optind != argc - 1)
                error(1, 0, "missing required hostname argument");
@@ -483,10 +673,12 @@ static void parse_opt(int argc, char **argv)
 
 static void resolve_hostname(const char *hostname)
 {
+       struct addrinfo hints = { .ai_family = do_ipv4 ? AF_INET : AF_INET6 };
        struct addrinfo *addrs, *cur;
        int have_ipv4 = 0, have_ipv6 = 0;
 
-       if (getaddrinfo(hostname, NULL, NULL, &addrs))
+retry:
+       if (getaddrinfo(hostname, NULL, &hints, &addrs))
                error(1, errno, "getaddrinfo");
 
        cur = addrs;
@@ -506,14 +698,41 @@ static void resolve_hostname(const char *hostname)
        if (addrs)
                freeaddrinfo(addrs);
 
+       if (do_ipv6 && hints.ai_family != AF_INET6) {
+               hints.ai_family = AF_INET6;
+               goto retry;
+       }
+
        do_ipv4 &= have_ipv4;
        do_ipv6 &= have_ipv6;
 }
 
+static void do_listen(int family, void *addr, int alen)
+{
+       int fd, type;
+
+       type = cfg_proto == SOCK_RAW ? SOCK_DGRAM : cfg_proto;
+
+       fd = socket(family, type, 0);
+       if (fd == -1)
+               error(1, errno, "socket rx");
+
+       if (bind(fd, addr, alen))
+               error(1, errno, "bind rx");
+
+       if (type == SOCK_STREAM && listen(fd, 10))
+               error(1, errno, "listen rx");
+
+       /* leave fd open, will be closed on process exit.
+        * this enables connect() to succeed and avoids icmp replies
+        */
+}
+
 static void do_main(int family)
 {
-       fprintf(stderr, "family:       %s\n",
-                       family == PF_INET ? "INET" : "INET6");
+       fprintf(stderr, "family:       %s %s\n",
+                       family == PF_INET ? "INET" : "INET6",
+                       cfg_use_pf_packet ? "(PF_PACKET)" : "");
 
        fprintf(stderr, "test SND\n");
        do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);
@@ -555,10 +774,17 @@ int main(int argc, char **argv)
        fprintf(stderr, "server port:  %u\n", dest_port);
        fprintf(stderr, "\n");
 
-       if (do_ipv4)
+       if (do_ipv4) {
+               if (cfg_do_listen)
+                       do_listen(PF_INET, &daddr, sizeof(daddr));
                do_main(PF_INET);
-       if (do_ipv6)
+       }
+
+       if (do_ipv6) {
+               if (cfg_do_listen)
+                       do_listen(PF_INET6, &daddr6, sizeof(daddr6));
                do_main(PF_INET6);
+       }
 
-       return 0;
+       return test_failed;
 }
diff --git a/tools/testing/selftests/networking/timestamping/txtimestamp.sh b/tools/testing/selftests/networking/timestamping/txtimestamp.sh
new file mode 100755 (executable)
index 0000000..df0d86c
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Send packets with transmit timestamps over loopback with netem
+# Verify that timestamps correspond to netem delay
+
+set -e
+
+setup() {
+       # set 1ms delay on lo egress
+       tc qdisc add dev lo root netem delay 1ms
+
+       # set 2ms delay on ifb0 egress
+       modprobe ifb
+       ip link add ifb_netem0 type ifb
+       ip link set dev ifb_netem0 up
+       tc qdisc add dev ifb_netem0 root netem delay 2ms
+
+       # redirect lo ingress through ifb0 egress
+       tc qdisc add dev lo handle ffff: ingress
+       tc filter add dev lo parent ffff: \
+               u32 match mark 0 0xffff \
+               action mirred egress redirect dev ifb_netem0
+}
+
+run_test_v4v6() {
+       # SND will be delayed 1000us
+       # ACK will be delayed 6000us: 1 + 2 ms round-trip
+       local -r args="$@ -v 1000 -V 6000"
+
+       ./txtimestamp ${args} -4 -L 127.0.0.1
+       ./txtimestamp ${args} -6 -L ::1
+}
+
+run_test_tcpudpraw() {
+       local -r args=$@
+
+       run_test_v4v6 ${args}           # tcp
+       run_test_v4v6 ${args} -u        # udp
+       run_test_v4v6 ${args} -r        # raw
+       run_test_v4v6 ${args} -R        # raw (IPPROTO_RAW)
+       run_test_v4v6 ${args} -P        # pf_packet
+}
+
+run_test_all() {
+       run_test_tcpudpraw              # setsockopt
+       run_test_tcpudpraw -C           # cmsg
+       run_test_tcpudpraw -n           # timestamp w/o data
+}
+
+if [[ "$(ip netns identify)" == "root" ]]; then
+       ../../net/in_netns.sh $0 $@
+else
+       setup
+       run_test_all
+       echo "OK. All tests passed"
+fi
index 1b0e9e9..f2fa101 100644 (file)
@@ -47,8 +47,9 @@ static int ok(void)
        return 0;
 }
 
-#define REG_POISON     0x5a5aUL
-#define POISONED_REG(n)        ((REG_POISON << 48) | ((n) << 32) | (REG_POISON << 16) | (n))
+#define REG_POISON     0x5a5a
+#define POISONED_REG(n)        ((((unsigned long)REG_POISON) << 48) | ((n) << 32) | \
+                        (((unsigned long)REG_POISON) << 16) | (n))
 
 static inline void poison_regs(void)
 {
@@ -105,6 +106,20 @@ static void dump_regs(void)
        }
 }
 
+#ifdef _CALL_AIXDESC
+struct opd {
+       unsigned long ip;
+       unsigned long toc;
+       unsigned long env;
+};
+static struct opd bad_opd = {
+       .ip = BAD_NIP,
+};
+#define BAD_FUNC (&bad_opd)
+#else
+#define BAD_FUNC BAD_NIP
+#endif
+
 int test_wild_bctr(void)
 {
        int (*func_ptr)(void);
@@ -133,7 +148,7 @@ int test_wild_bctr(void)
 
                poison_regs();
 
-               func_ptr = (int (*)(void))BAD_NIP;
+               func_ptr = (int (*)(void))BAD_FUNC;
                func_ptr();
 
                FAIL_IF(1); /* we didn't segv? */
index 6f1f4a6..8574442 100644 (file)
@@ -13,7 +13,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-/* Test readlink /proc/self/map_files/... with address 0. */
+/* Test readlink /proc/self/map_files/... with minimum address. */
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -47,6 +47,11 @@ static void fail(const char *fmt, unsigned long a, unsigned long b)
 int main(void)
 {
        const unsigned int PAGE_SIZE = sysconf(_SC_PAGESIZE);
+#ifdef __arm__
+       unsigned long va = 2 * PAGE_SIZE;
+#else
+       unsigned long va = 0;
+#endif
        void *p;
        int fd;
        unsigned long a, b;
@@ -55,7 +60,7 @@ int main(void)
        if (fd == -1)
                return 1;
 
-       p = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE|MAP_FILE|MAP_FIXED, fd, 0);
+       p = mmap((void *)va, PAGE_SIZE, PROT_NONE, MAP_PRIVATE|MAP_FILE|MAP_FIXED, fd, 0);
        if (p == MAP_FAILED) {
                if (errno == EPERM)
                        return 2;
index e147323..c9a2abf 100644 (file)
@@ -2731,9 +2731,14 @@ TEST(syscall_restart)
        ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
        ASSERT_EQ(true, WIFSTOPPED(status));
        ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
-       /* Verify signal delivery came from parent now. */
        ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &info));
-       EXPECT_EQ(getpid(), info.si_pid);
+       /*
+        * There is no siginfo on SIGSTOP any more, so we can't verify
+        * signal delivery came from parent now (getpid() == info.si_pid).
+        * https://lkml.kernel.org/r/CAGXu5jJaZAOzP1qFz66tYrtbuywqb+UN2SOA1VLHpCCOiYvYeg@mail.gmail.com
+        * At least verify the SIGSTOP via PTRACE_GETSIGINFO.
+        */
+       EXPECT_EQ(SIGSTOP, info.si_signo);
 
        /* Restart nanosleep with SIGCONT, which triggers restart_syscall. */
        ASSERT_EQ(0, kill(child_pid, SIGCONT));
index 3ee9a6d..1d9e279 100644 (file)
@@ -18,11 +18,12 @@ class TdcPlugin:
         if self.args.verbose > 1:
             print(' -- {}.post_suite'.format(self.sub_class))
 
-    def pre_case(self, test_ordinal, testid):
+    def pre_case(self, test_ordinal, testid, test_name):
         '''run commands before test_runner does one test'''
         if self.args.verbose > 1:
             print(' -- {}.pre_case'.format(self.sub_class))
         self.args.testid = testid
+        self.args.test_name = test_name
         self.args.test_ordinal = test_ordinal
 
     def post_case(self):
diff --git a/tools/testing/selftests/tc-testing/TdcResults.py b/tools/testing/selftests/tc-testing/TdcResults.py
new file mode 100644 (file)
index 0000000..1e4d95f
--- /dev/null
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+
+from enum import Enum
+
+class ResultState(Enum):
+    noresult = -1
+    skip = 0
+    success = 1
+    fail = 2
+
+class TestResult:
+    def __init__(self, test_id="", test_name=""):
+       self.test_id = test_id
+       self.test_name = test_name
+       self.result = ResultState.noresult
+       self.failmsg = ""
+       self.errormsg = ""
+       self.steps = []
+
+    def set_result(self, result):
+        if (isinstance(result, ResultState)):
+            self.result = result
+            return True
+        else:
+            raise TypeError('Unknown result type, must be type ResultState')
+
+    def get_result(self):
+        return self.result
+
+    def set_errormsg(self, errormsg):
+        self.errormsg = errormsg
+        return True
+
+    def append_errormsg(self, errormsg):
+        self.errormsg = '{}\n{}'.format(self.errormsg, errormsg)
+
+    def get_errormsg(self):
+        return self.errormsg
+
+    def set_failmsg(self, failmsg):
+        self.failmsg = failmsg
+        return True
+
+    def append_failmsg(self, failmsg):
+        self.failmsg = '{}\n{}'.format(self.failmsg, failmsg)
+
+    def get_failmsg(self):
+        return self.failmsg
+
+    def add_steps(self, newstep):
+        if type(newstep) == list:
+            self.steps.extend(newstep)
+        elif type(newstep) == str:
+            self.steps.append(step)
+        else:
+            raise TypeError('TdcResults.add_steps() requires a list or str')
+
+    def get_executed_steps(self):
+        return self.steps
+
+class TestSuiteReport():
+    _testsuite = []
+
+    def add_resultdata(self, result_data):
+        if isinstance(result_data, TestResult):
+            self._testsuite.append(result_data)
+            return True
+
+    def count_tests(self):
+        return len(self._testsuite)
+
+    def count_failures(self):
+        return sum(1 for t in self._testsuite if t.result == ResultState.fail)
+
+    def count_skips(self):
+        return sum(1 for t in self._testsuite if t.result == ResultState.skip)
+
+    def find_result(self, test_id):
+        return next((tr for tr in self._testsuite if tr.test_id == test_id), None)
+
+    def update_result(self, result_data):
+        orig = self.find_result(result_data.test_id)
+        if orig != None:
+            idx = self._testsuite.index(orig)
+            self._testsuite[idx] = result_data
+        else:
+            self.add_resultdata(result_data)
+
+    def format_tap(self):
+        ftap = ""
+        ftap += '1..{}\n'.format(self.count_tests())
+        index = 1
+        for t in self._testsuite:
+            if t.result == ResultState.fail:
+                ftap += 'not '
+            ftap += 'ok {} {} - {}'.format(str(index), t.test_id, t.test_name)
+            if t.result == ResultState.skip or t.result == ResultState.noresult:
+                ftap += ' # skipped - {}\n'.format(t.errormsg)
+            elif t.result == ResultState.fail:
+                if len(t.steps) > 0:
+                    ftap += '\tCommands executed in this test case:'
+                    for step in t.steps:
+                        ftap += '\n\t\t{}'.format(step)
+                ftap += '\n\t{}'.format(t.failmsg)
+            ftap += '\n'
+            index += 1
+        return ftap
+
+    def format_xunit(self):
+        from xml.sax.saxutils import escape
+        xunit = "<testsuites>\n"
+        xunit += '\t<testsuite tests=\"{}\" skips=\"{}\">\n'.format(self.count_tests(), self.count_skips())
+        for t in self._testsuite:
+            xunit += '\t\t<testcase classname=\"{}\" '.format(escape(t.test_id))
+            xunit += 'name=\"{}\">\n'.format(escape(t.test_name))
+            if t.failmsg:
+                xunit += '\t\t\t<failure>\n'
+                if len(t.steps) > 0:
+                    xunit += 'Commands executed in this test case:\n'
+                    for step in t.steps:
+                        xunit += '\t{}\n'.format(escape(step))
+                xunit += 'FAILURE: {}\n'.format(escape(t.failmsg))
+                xunit += '\t\t\t</failure>\n'
+            if t.errormsg:
+                xunit += '\t\t\t<error>\n{}\n'.format(escape(t.errormsg))
+                xunit += '\t\t\t</error>\n'
+            if t.result == ResultState.skip:
+                xunit += '\t\t\t<skipped/>\n'
+            xunit += '\t\t</testcase>\n'
+        xunit += '\t</testsuite>\n'
+        xunit += '</testsuites>\n'
+        return xunit
index 477a7bd..e00c798 100644 (file)
@@ -11,6 +11,7 @@ from string import Template
 import subprocess
 import time
 from TdcPlugin import TdcPlugin
+from TdcResults import *
 
 from tdc_config import *
 
@@ -21,6 +22,7 @@ class SubPlugin(TdcPlugin):
     def __init__(self):
         self.sub_class = 'valgrind/SubPlugin'
         self.tap = ''
+        self._tsr = TestSuiteReport()
         super().__init__()
 
     def pre_suite(self, testcount, testidlist):
@@ -34,10 +36,14 @@ class SubPlugin(TdcPlugin):
     def post_suite(self, index):
         '''run commands after test_runner goes into a test loop'''
         super().post_suite(index)
-        self._add_to_tap('\n|---\n')
         if self.args.verbose > 1:
             print('{}.post_suite'.format(self.sub_class))
-        print('{}'.format(self.tap))
+        #print('{}'.format(self.tap))
+        for xx in range(index - 1, self.testcount):
+            res = TestResult('{}-mem'.format(self.testidlist[xx]), 'Test skipped')
+            res.set_result(ResultState.skip)
+            res.set_errormsg('Skipped because of prior setup/teardown failure')
+            self._add_results(res)
         if self.args.verbose < 4:
             subprocess.check_output('rm -f vgnd-*.log', shell=True)
 
@@ -128,8 +134,17 @@ class SubPlugin(TdcPlugin):
                 nle_num = int(nle_mo.group(1))
 
         mem_results = ''
+        res = TestResult('{}-mem'.format(self.args.testid),
+              '{} memory leak check'.format(self.args.test_name))
         if (def_num > 0) or (ind_num > 0) or (pos_num > 0) or (nle_num > 0):
             mem_results += 'not '
+            res.set_result(ResultState.fail)
+            res.set_failmsg('Memory leak detected')
+            res.append_failmsg(content)
+        else:
+            res.set_result(ResultState.success)
+
+        self._add_results(res)
 
         mem_results += 'ok {} - {}-mem # {}\n'.format(
             self.args.test_ordinal, self.args.testid, 'memory leak check')
@@ -138,5 +153,8 @@ class SubPlugin(TdcPlugin):
             print('{}'.format(content))
             self._add_to_tap(content)
 
+    def _add_results(self, res):
+        self._tsr.add_resultdata(res)
+
     def _add_to_tap(self, more_tap_output):
         self.tap += more_tap_output
index 87a04a8..e6e4ce8 100755 (executable)
@@ -23,6 +23,7 @@ from tdc_config import *
 from tdc_helper import *
 
 import TdcPlugin
+from TdcResults import *
 
 
 class PluginMgrTestFail(Exception):
@@ -60,10 +61,10 @@ class PluginMgr:
         for pgn_inst in reversed(self.plugin_instances):
             pgn_inst.post_suite(index)
 
-    def call_pre_case(self, test_ordinal, testid):
+    def call_pre_case(self, test_ordinal, testid, test_name):
         for pgn_inst in self.plugin_instances:
             try:
-                pgn_inst.pre_case(test_ordinal, testid)
+                pgn_inst.pre_case(test_ordinal, testid, test_name)
             except Exception as ee:
                 print('exception {} in call to pre_case for {} plugin'.
                       format(ee, pgn_inst.__class__))
@@ -102,7 +103,6 @@ class PluginMgr:
         self.argparser = argparse.ArgumentParser(
             description='Linux TC unit tests')
 
-
 def replace_keywords(cmd):
     """
     For a given executable command, substitute any known
@@ -131,12 +131,16 @@ def exec_cmd(args, pm, stage, command):
         stdout=subprocess.PIPE,
         stderr=subprocess.PIPE,
         env=ENVIR)
-    (rawout, serr) = proc.communicate()
 
-    if proc.returncode != 0 and len(serr) > 0:
-        foutput = serr.decode("utf-8")
-    else:
-        foutput = rawout.decode("utf-8")
+    try:
+        (rawout, serr) = proc.communicate(timeout=NAMES['TIMEOUT'])
+        if proc.returncode != 0 and len(serr) > 0:
+            foutput = serr.decode("utf-8", errors="ignore")
+        else:
+            foutput = rawout.decode("utf-8", errors="ignore")
+    except subprocess.TimeoutExpired:
+        foutput = "Command \"{}\" timed out\n".format(command)
+        proc.returncode = 255
 
     proc.stdout.close()
     proc.stderr.close()
@@ -169,6 +173,8 @@ def prepare_env(args, pm, stage, prefix, cmdlist, output = None):
                   file=sys.stderr)
             print("\n{} *** Error message: \"{}\"".format(prefix, foutput),
                   file=sys.stderr)
+            print("returncode {}; expected {}".format(proc.returncode,
+                                                      exit_codes))
             print("\n{} *** Aborting test run.".format(prefix), file=sys.stderr)
             print("\n\n{} *** stdout ***".format(proc.stdout), file=sys.stderr)
             print("\n\n{} *** stderr ***".format(proc.stderr), file=sys.stderr)
@@ -181,6 +187,7 @@ def run_one_test(pm, args, index, tidx):
     result = True
     tresult = ""
     tap = ""
+    res = TestResult(tidx['id'], tidx['name'])
     if args.verbose > 0:
         print("\t====================\n=====> ", end="")
     print("Test " + tidx["id"] + ": " + tidx["name"])
@@ -188,19 +195,26 @@ def run_one_test(pm, args, index, tidx):
     # populate NAMES with TESTID for this test
     NAMES['TESTID'] = tidx['id']
 
-    pm.call_pre_case(index, tidx['id'])
+    pm.call_pre_case(index, tidx['id'], tidx['name'])
     prepare_env(args, pm, 'setup', "-----> prepare stage", tidx["setup"])
 
     if (args.verbose > 0):
         print('-----> execute stage')
     pm.call_pre_execute()
     (p, procout) = exec_cmd(args, pm, 'execute', tidx["cmdUnderTest"])
-    exit_code = p.returncode
+    if p:
+        exit_code = p.returncode
+    else:
+        exit_code = None
+
     pm.call_post_execute()
 
-    if (exit_code != int(tidx["expExitCode"])):
-        result = False
-        print("exit:", exit_code, int(tidx["expExitCode"]))
+    if (exit_code is None or exit_code != int(tidx["expExitCode"])):
+        print("exit: {!r}".format(exit_code))
+        print("exit: {}".format(int(tidx["expExitCode"])))
+        #print("exit: {!r} {}".format(exit_code, int(tidx["expExitCode"])))
+        res.set_result(ResultState.fail)
+        res.set_failmsg('Command exited with {}, expected {}\n{}'.format(exit_code, tidx["expExitCode"], procout))
         print(procout)
     else:
         if args.verbose > 0:
@@ -211,20 +225,15 @@ def run_one_test(pm, args, index, tidx):
         if procout:
             match_index = re.findall(match_pattern, procout)
             if len(match_index) != int(tidx["matchCount"]):
-                result = False
+                res.set_result(ResultState.fail)
+                res.set_failmsg('Could not match regex pattern. Verify command output:\n{}'.format(procout))
+            else:
+                res.set_result(ResultState.success)
         elif int(tidx["matchCount"]) != 0:
-            result = False
-
-    if not result:
-        tresult += 'not '
-    tresult += 'ok {} - {} # {}\n'.format(str(index), tidx['id'], tidx['name'])
-    tap += tresult
-
-    if result == False:
-        if procout:
-            tap += procout
+            res.set_result(ResultState.fail)
+            res.set_failmsg('No output generated by verify command.')
         else:
-            tap += 'No output!\n'
+            res.set_result(ResultState.success)
 
     prepare_env(args, pm, 'teardown', '-----> teardown stage', tidx['teardown'], procout)
     pm.call_post_case()
@@ -233,7 +242,7 @@ def run_one_test(pm, args, index, tidx):
 
     # remove TESTID from NAMES
     del(NAMES['TESTID'])
-    return tap
+    return res
 
 def test_runner(pm, args, filtered_tests):
     """
@@ -253,25 +262,15 @@ def test_runner(pm, args, filtered_tests):
     emergency_exit = False
     emergency_exit_message = ''
 
-    if args.notap:
-        if args.verbose:
-            tap = 'notap requested:  omitting test plan\n'
-    else:
-        tap = str(index) + ".." + str(tcount) + "\n"
+    tsr = TestSuiteReport()
+
     try:
         pm.call_pre_suite(tcount, [tidx['id'] for tidx in testlist])
     except Exception as ee:
         ex_type, ex, ex_tb = sys.exc_info()
         print('Exception {} {} (caught in pre_suite).'.
               format(ex_type, ex))
-        # when the extra print statements are uncommented,
-        # the traceback does not appear between them
-        # (it appears way earlier in the tdc.py output)
-        # so don't bother ...
-        # print('--------------------(')
-        # print('traceback')
         traceback.print_tb(ex_tb)
-        # print('--------------------)')
         emergency_exit_message = 'EMERGENCY EXIT, call_pre_suite failed with exception {} {}\n'.format(ex_type, ex)
         emergency_exit = True
         stage = 'pre-SUITE'
@@ -287,15 +286,26 @@ def test_runner(pm, args, filtered_tests):
             if args.verbose > 1:
                 print('Not executing test {} {} because DEV2 not defined'.
                       format(tidx['id'], tidx['name']))
+            res = TestResult(tidx['id'], tidx['name'])
+            res.set_result(ResultState.skip)
+            res.set_errormsg('Not executed because DEV2 is not defined')
+            tsr.add_resultdata(res)
             continue
         try:
             badtest = tidx  # in case it goes bad
-            tap += run_one_test(pm, args, index, tidx)
+            res = run_one_test(pm, args, index, tidx)
+            tsr.add_resultdata(res)
         except PluginMgrTestFail as pmtf:
             ex_type, ex, ex_tb = sys.exc_info()
             stage = pmtf.stage
             message = pmtf.message
             output = pmtf.output
+            res = TestResult(tidx['id'], tidx['name'])
+            res.set_result(ResultState.skip)
+            res.set_errormsg(pmtf.message)
+            res.set_failmsg(pmtf.output)
+            tsr.add_resultdata(res)
+            index += 1
             print(message)
             print('Exception {} {} (caught in test_runner, running test {} {} {} stage {})'.
                   format(ex_type, ex, index, tidx['id'], tidx['name'], stage))
@@ -314,16 +324,16 @@ def test_runner(pm, args, filtered_tests):
     # if we failed in setup or teardown,
     # fill in the remaining tests with ok-skipped
     count = index
-    if not args.notap:
-        tap += 'about to flush the tap output if tests need to be skipped\n'
-        if tcount + 1 != index:
-            for tidx in testlist[index - 1:]:
-                msg = 'skipped - previous {} failed'.format(stage)
-                tap += 'ok {} - {} # {} {} {}\n'.format(
-                    count, tidx['id'], msg, index, badtest.get('id', '--Unknown--'))
-                count += 1
 
-        tap += 'done flushing skipped test tap output\n'
+    if tcount + 1 != count:
+        for tidx in testlist[count - 1:]:
+            res = TestResult(tidx['id'], tidx['name'])
+            res.set_result(ResultState.skip)
+            msg = 'skipped - previous {} failed {} {}'.format(stage,
+                index, badtest.get('id', '--Unknown--'))
+            res.set_errormsg(msg)
+            tsr.add_resultdata(res)
+            count += 1
 
     if args.pause:
         print('Want to pause\nPress enter to continue ...')
@@ -332,7 +342,7 @@ def test_runner(pm, args, filtered_tests):
 
     pm.call_post_suite(index)
 
-    return tap
+    return tsr
 
 def has_blank_ids(idlist):
     """
@@ -372,6 +382,10 @@ def set_args(parser):
     """
     Set the command line arguments for tdc.
     """
+    parser.add_argument(
+        '--outfile', type=str,
+        help='Path to the file in which results should be saved. ' +
+        'Default target is the current directory.')
     parser.add_argument(
         '-p', '--path', type=str,
         help='The full path to the tc executable to use')
@@ -408,8 +422,9 @@ def set_args(parser):
         '-v', '--verbose', action='count', default=0,
         help='Show the commands that are being run')
     parser.add_argument(
-        '-N', '--notap', action='store_true',
-        help='Suppress tap results for command under test')
+        '--format', default='tap', const='tap', nargs='?',
+        choices=['none', 'xunit', 'tap'],
+        help='Specify the format for test results. (Default: TAP)')
     parser.add_argument('-d', '--device',
                         help='Execute the test case in flower category')
     parser.add_argument(
@@ -430,6 +445,8 @@ def check_default_settings(args, remaining, pm):
         NAMES['TC'] = args.path
     if args.device != None:
         NAMES['DEV2'] = args.device
+    if 'TIMEOUT' not in NAMES:
+        NAMES['TIMEOUT'] = None
     if not os.path.isfile(NAMES['TC']):
         print("The specified tc path " + NAMES['TC'] + " does not exist.")
         exit(1)
@@ -624,12 +641,30 @@ def set_operation_mode(pm, args):
 
     if len(alltests):
         catresults = test_runner(pm, args, alltests)
+        if args.format == 'none':
+            print('Test results output suppression requested\n')
+        else:
+            print('\nAll test results: \n')
+            if args.format == 'xunit':
+                suffix = 'xml'
+                res = catresults.format_xunit()
+            elif args.format == 'tap':
+                suffix = 'tap'
+                res = catresults.format_tap()
+            print(res)
+            print('\n\n')
+            if not args.outfile:
+                fname = 'test-results.{}'.format(suffix)
+            else:
+                fname = args.outfile
+            with open(fname, 'w') as fh:
+                fh.write(res)
+                fh.close()
+                if os.getenv('SUDO_UID') is not None:
+                    os.chown(fname, uid=int(os.getenv('SUDO_UID')),
+                        gid=int(os.getenv('SUDO_GID')))
     else:
-        catresults = 'No tests found\n'
-    if args.notap:
-        print('Tap output suppression requested\n')
-    else:
-        print('All test results: \n\n{}'.format(catresults))
+        print('No tests found\n')
 
 def main():
     """
index d651bc1..6d91e48 100644 (file)
@@ -15,6 +15,8 @@ NAMES = {
           'DEV1': 'v0p1',
           'DEV2': '',
           'BATCH_FILE': './batch.txt',
+          # Length of time in seconds to wait before terminating a command
+          'TIMEOUT': 12,
           # Name of the namespace to use
           'NS': 'tcut',
           # Directory containing eBPF test programs
index fb22bcc..7ef45a4 100644 (file)
 #define PAGE_MASK (~(PAGE_SIZE-1))
 #define PAGE_ALIGN(x) ((x + PAGE_SIZE - 1) & PAGE_MASK)
 
+/* generic data direction definitions */
+#define READ                    0
+#define WRITE                   1
+
 typedef unsigned long long phys_addr_t;
 typedef unsigned long long dma_addr_t;
 typedef size_t __kernel_size_t;
index 3710342..6855cce 100644 (file)
@@ -175,10 +175,14 @@ int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
 {
        struct kvm_coalesced_mmio_dev *dev, *tmp;
 
+       if (zone->pio != 1 && zone->pio != 0)
+               return -EINVAL;
+
        mutex_lock(&kvm->slots_lock);
 
        list_for_each_entry_safe(dev, tmp, &kvm->coalesced_zones, list)
-               if (coalesced_mmio_in_range(dev, zone->addr, zone->size)) {
+               if (zone->pio == dev->zone.pio &&
+                   coalesced_mmio_in_range(dev, zone->addr, zone->size)) {
                        kvm_io_bus_unregister_dev(kvm,
                                zone->pio ? KVM_PIO_BUS : KVM_MMIO_BUS, &dev->dev);
                        kvm_iodevice_destructor(&dev->dev);